summaryrefslogtreecommitdiff
path: root/chromium/components
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-04-05 14:08:31 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-04-11 07:46:53 +0000
commit6a4cabb866f66d4128a97cdc6d9d08ce074f1247 (patch)
treeab00f70a5e89278d6a0d16ff0c42578dc4d84a2d /chromium/components
parente733310db58160074f574c429d48f8308c0afe17 (diff)
downloadqtwebengine-chromium-6a4cabb866f66d4128a97cdc6d9d08ce074f1247.tar.gz
BASELINE: Update Chromium to 57.0.2987.144
Change-Id: I29db402ff696c71a04c4dbaec822c2e53efe0267 Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
Diffstat (limited to 'chromium/components')
-rw-r--r--chromium/components/BUILD.gn33
-rw-r--r--chromium/components/OWNERS3
-rw-r--r--chromium/components/app_modal/native_app_modal_dialog.h1
-rw-r--r--chromium/components/app_modal/views/javascript_app_modal_dialog_views.h4
-rw-r--r--chromium/components/arc/BUILD.gn68
-rw-r--r--chromium/components/arc/DEPS2
-rw-r--r--chromium/components/arc/arc_bridge_host_impl.cc59
-rw-r--r--chromium/components/arc/arc_bridge_host_impl.h8
-rw-r--r--chromium/components/arc/arc_bridge_service.cc86
-rw-r--r--chromium/components/arc/arc_bridge_service.h168
-rw-r--r--chromium/components/arc/arc_bridge_service_impl.cc173
-rw-r--r--chromium/components/arc/arc_bridge_service_impl.h97
-rw-r--r--chromium/components/arc/arc_bridge_service_unittest.cc214
-rw-r--r--chromium/components/arc/arc_service_manager.cc103
-rw-r--r--chromium/components/arc/arc_service_manager.h88
-rw-r--r--chromium/components/arc/arc_service_manager_unittest.cc209
-rw-r--r--chromium/components/arc/arc_session.cc129
-rw-r--r--chromium/components/arc/arc_session.h25
-rw-r--r--chromium/components/arc/arc_session_observer.cc27
-rw-r--r--chromium/components/arc/arc_session_observer.h48
-rw-r--r--chromium/components/arc/arc_session_runner.cc200
-rw-r--r--chromium/components/arc/arc_session_runner.h135
-rw-r--r--chromium/components/arc/arc_session_runner_unittest.cc246
-rw-r--r--chromium/components/arc/audio/arc_audio_bridge.cc15
-rw-r--r--chromium/components/arc/bluetooth/arc_bluetooth_bridge.cc204
-rw-r--r--chromium/components/arc/bluetooth/arc_bluetooth_bridge.h8
-rw-r--r--chromium/components/arc/bluetooth/arc_bluetooth_bridge_unittest.cc26
-rw-r--r--chromium/components/arc/bluetooth/bluetooth_struct_traits.cc1
-rw-r--r--chromium/components/arc/bluetooth/bluetooth_struct_traits.h7
-rw-r--r--chromium/components/arc/bluetooth/bluetooth_struct_traits_unittest.cc14
-rw-r--r--chromium/components/arc/bluetooth/bluetooth_type_converters.cc14
-rw-r--r--chromium/components/arc/bluetooth/bluetooth_type_converters_unittest.cc55
-rw-r--r--chromium/components/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc58
-rw-r--r--chromium/components/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h45
-rw-r--r--chromium/components/arc/clipboard/arc_clipboard_bridge.cc7
-rw-r--r--chromium/components/arc/common/app.mojom20
-rw-r--r--chromium/components/arc/common/auth.mojom29
-rw-r--r--chromium/components/arc/common/bluetooth.mojom4
-rw-r--r--chromium/components/arc/common/file_system.mojom64
-rw-r--r--chromium/components/arc/common/intent_helper.mojom9
-rw-r--r--chromium/components/arc/common/intent_helper.typemap16
-rw-r--r--chromium/components/arc/common/power.mojom11
-rw-r--r--chromium/components/arc/common/typemaps.gni2
-rw-r--r--chromium/components/arc/common/video.mojom14
-rw-r--r--chromium/components/arc/common/video_accelerator.mojom13
-rw-r--r--chromium/components/arc/common/video_accelerator.typemap12
-rw-r--r--chromium/components/arc/common/wallpaper.mojom4
-rw-r--r--chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc6
-rw-r--r--chromium/components/arc/file_system/arc_file_system_operation_runner.cc19
-rw-r--r--chromium/components/arc/file_system/arc_file_system_operation_runner.h55
-rw-r--r--chromium/components/arc/ime/arc_ime_bridge_impl.cc21
-rw-r--r--chromium/components/arc/ime/arc_ime_service.h2
-rw-r--r--chromium/components/arc/ime/arc_ime_service_unittest.cc14
-rw-r--r--chromium/components/arc/instance_holder.h26
-rw-r--r--chromium/components/arc/intent_helper/OWNERS2
-rw-r--r--chromium/components/arc/intent_helper/activity_icon_loader.cc20
-rw-r--r--chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc63
-rw-r--r--chromium/components/arc/intent_helper/arc_intent_helper_bridge.h26
-rw-r--r--chromium/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc68
-rw-r--r--chromium/components/arc/intent_helper/arc_intent_helper_observer.h19
-rw-r--r--chromium/components/arc/intent_helper/intent_filter.cc56
-rw-r--r--chromium/components/arc/intent_helper/intent_filter.h55
-rw-r--r--chromium/components/arc/intent_helper/intent_filter_struct_traits.cc55
-rw-r--r--chromium/components/arc/intent_helper/intent_filter_struct_traits.h79
-rw-r--r--chromium/components/arc/intent_helper/intent_filter_unittest.cc64
-rw-r--r--chromium/components/arc/intent_helper/link_handler_model_impl.cc17
-rw-r--r--chromium/components/arc/intent_helper/local_activity_resolver.cc6
-rw-r--r--chromium/components/arc/intent_helper/local_activity_resolver.h2
-rw-r--r--chromium/components/arc/intent_helper/local_activity_resolver_unittest.cc44
-rw-r--r--chromium/components/arc/intent_helper/page_transition_util.h1
-rw-r--r--chromium/components/arc/intent_helper/page_transition_util_unittest.cc18
-rw-r--r--chromium/components/arc/kiosk/arc_kiosk_bridge.cc12
-rw-r--r--chromium/components/arc/kiosk/arc_kiosk_bridge.h12
-rw-r--r--chromium/components/arc/metrics/arc_metrics_service.cc10
-rw-r--r--chromium/components/arc/metrics/arc_metrics_service.h5
-rw-r--r--chromium/components/arc/metrics/oom_kills_histogram.h23
-rw-r--r--chromium/components/arc/metrics/oom_kills_monitor.cc206
-rw-r--r--chromium/components/arc/metrics/oom_kills_monitor.h66
-rw-r--r--chromium/components/arc/net/DEPS1
-rw-r--r--chromium/components/arc/net/arc_net_host_impl.cc69
-rw-r--r--chromium/components/arc/net/arc_net_host_impl.h6
-rw-r--r--chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.cc2
-rw-r--r--chromium/components/arc/power/DEPS2
-rw-r--r--chromium/components/arc/power/arc_power_bridge.cc38
-rw-r--r--chromium/components/arc/power/arc_power_bridge.h11
-rw-r--r--chromium/components/arc/storage_manager/arc_storage_manager.cc17
-rw-r--r--chromium/components/arc/user_data/arc_user_data_service.cc86
-rw-r--r--chromium/components/arc/user_data/arc_user_data_service.h84
-rw-r--r--chromium/components/arc/video_accelerator/OWNERS2
-rw-r--r--chromium/components/arc/video_accelerator/video_accelerator.h17
-rw-r--r--chromium/components/arc/video_accelerator/video_accelerator_struct_traits.cc23
-rw-r--r--chromium/components/arc/video_accelerator/video_accelerator_struct_traits.h32
-rw-r--r--chromium/components/autofill/DEPS5
-rw-r--r--chromium/components/autofill/OWNERS2
-rw-r--r--chromium/components/autofill/android/BUILD.gn1
-rw-r--r--chromium/components/autofill/android/java/res/values/colors.xml11
-rw-r--r--chromium/components/autofill/android/java/res/values/dimens.xml9
-rw-r--r--chromium/components/autofill/content/browser/BUILD.gn1
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver.cc22
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver.h2
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_factory.h1
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc27
-rw-r--r--chromium/components/autofill/content/browser/risk/fingerprint.cc5
-rw-r--r--chromium/components/autofill/content/browser/risk/fingerprint.h2
-rw-r--r--chromium/components/autofill/content/common/autofill_agent.mojom2
-rw-r--r--chromium/components/autofill/content/common/autofill_driver.mojom21
-rw-r--r--chromium/components/autofill/content/common/autofill_param_traits_macros.h2
-rw-r--r--chromium/components/autofill/content/common/autofill_types.mojom13
-rw-r--r--chromium/components/autofill/content/common/autofill_types.typemap1
-rw-r--r--chromium/components/autofill/content/common/autofill_types_struct_traits.cc43
-rw-r--r--chromium/components/autofill/content/common/autofill_types_struct_traits.h9
-rw-r--r--chromium/components/autofill/content/renderer/BUILD.gn4
-rw-r--r--chromium/components/autofill/content/renderer/DEPS8
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.cc18
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.h5
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.cc16
-rw-r--r--chromium/components/autofill/content/renderer/form_cache.h3
-rw-r--r--chromium/components/autofill/content/renderer/page_click_tracker.cc3
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.cc60
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.h6
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.cc22
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.h3
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc40
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_agent.cc1
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_agent.h5
-rw-r--r--chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc3
-rw-r--r--chromium/components/autofill/core/browser/BUILD.gn6
-rw-r--r--chromium/components/autofill/core/browser/address_field_unittest.cc69
-rw-r--r--chromium/components/autofill/core/browser/autofill_assistant.cc3
-rw-r--r--chromium/components/autofill/core/browser/autofill_client.h14
-rw-r--r--chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.h4
-rw-r--r--chromium/components/autofill/core/browser/autofill_data_model.h2
-rw-r--r--chromium/components/autofill/core/browser/autofill_data_util.cc1
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_manager.cc4
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc92
-rw-r--r--chromium/components/autofill/core/browser/autofill_experiments.cc110
-rw-r--r--chromium/components/autofill/core/browser/autofill_experiments.h49
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate.cc49
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate.h11
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc25
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.cc122
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.h5
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager_unittest.cc350
-rw-r--r--chromium/components/autofill/core/browser/autofill_merge_unittest.cc4
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics.cc26
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics.h16
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics_unittest.cc344
-rw-r--r--chromium/components/autofill/core/browser/autofill_popup_delegate.h3
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile.cc12
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile.h8
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile_comparator.cc124
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile_comparator_unittest.cc69
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile_unittest.cc473
-rw-r--r--chromium/components/autofill/core/browser/autofill_scanner.cc24
-rw-r--r--chromium/components/autofill/core/browser/autofill_scanner.h12
-rw-r--r--chromium/components/autofill/core/browser/autofill_type.cc3
-rw-r--r--chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc24
-rw-r--r--chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.h19
-rw-r--r--chromium/components/autofill/core/browser/credit_card.cc22
-rw-r--r--chromium/components/autofill/core/browser/credit_card.h1
-rw-r--r--chromium/components/autofill/core/browser/credit_card_field_unittest.cc171
-rw-r--r--chromium/components/autofill/core/browser/credit_card_unittest.cc53
-rw-r--r--chromium/components/autofill/core/browser/field_types.h6
-rw-r--r--chromium/components/autofill/core/browser/form_field.cc31
-rw-r--r--chromium/components/autofill/core/browser/form_field.h2
-rw-r--r--chromium/components/autofill/core/browser/form_field_unittest.cc29
-rw-r--r--chromium/components/autofill/core/browser/form_structure.cc60
-rw-r--r--chromium/components/autofill/core/browser/form_structure.h20
-rw-r--r--chromium/components/autofill/core/browser/legal_message_line.h4
-rw-r--r--chromium/components/autofill/core/browser/name_field_unittest.cc104
-rw-r--r--chromium/components/autofill/core/browser/password_generator.cc2
-rw-r--r--chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc1
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_client.cc2
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.cc117
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.h32
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_unittest.cc303
-rw-r--r--chromium/components/autofill/core/browser/phone_field_unittest.cc67
-rw-r--r--chromium/components/autofill/core/browser/popup_item_ids.h4
-rw-r--r--chromium/components/autofill/core/browser/proto/BUILD.gn1
-rw-r--r--chromium/components/autofill/core/browser/proto/autofill_sync.proto17
-rw-r--r--chromium/components/autofill/core/browser/suggestion.cc12
-rw-r--r--chromium/components/autofill/core/browser/suggestion.h1
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_client.cc11
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_client.h15
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_external_delegate.h2
-rw-r--r--chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc42
-rw-r--r--chromium/components/autofill/core/browser/ui/card_unmask_prompt_view.h2
-rw-r--r--chromium/components/autofill/core/browser/validation.cc25
-rw-r--r--chromium/components/autofill/core/browser/validation.h8
-rw-r--r--chromium/components/autofill/core/browser/validation_unittest.cc58
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc477
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h95
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc524
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc2
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.cc22
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.h16
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_data_type_controller_unittest.cc1
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_entry.cc6
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_entry.h2
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.cc75
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.h55
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.cc25
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h17
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.cc330
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.h73
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc126
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc197
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h18
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc773
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc85
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h28
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service_unittest.cc165
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata.h14
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc31
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h20
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc22
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h6
-rw-r--r--chromium/components/autofill/core/common/autofill_regexes.cc8
-rw-r--r--chromium/components/autofill/core/common/password_form.cc5
-rw-r--r--chromium/components/autofill/core/common/password_form.h7
-rw-r--r--chromium/components/autofill/core/common/password_form_fill_data.cc4
-rw-r--r--chromium/components/autofill/core/common/save_password_progress_logger.cc14
-rw-r--r--chromium/components/autofill/core/common/save_password_progress_logger.h6
-rw-r--r--chromium/components/autofill/ios/browser/BUILD.gn1
-rw-r--r--chromium/components/autofill/ios/browser/autofill_driver_ios.h2
-rw-r--r--chromium/components/autofill/ios/browser/form_suggestion.h2
-rw-r--r--chromium/components/autofill/ios/browser/form_suggestion.mm2
-rw-r--r--chromium/components/autofill_strings.grdp64
-rw-r--r--chromium/components/bookmark_component_strings.grdp2
-rw-r--r--chromium/components/bookmarks/browser/BUILD.gn13
-rw-r--r--chromium/components/bookmarks/browser/bookmark_client.cc7
-rw-r--r--chromium/components/bookmarks/browser/bookmark_client.h24
-rw-r--r--chromium/components/bookmarks/browser/bookmark_codec.cc10
-rw-r--r--chromium/components/bookmarks/browser/bookmark_codec_unittest.cc10
-rw-r--r--chromium/components/bookmarks/browser/bookmark_index.h93
-rw-r--r--chromium/components/bookmarks/browser/bookmark_index_unittest.cc65
-rw-r--r--chromium/components/bookmarks/browser/bookmark_model.cc15
-rw-r--r--chromium/components/bookmarks/browser/bookmark_model.h10
-rw-r--r--chromium/components/bookmarks/browser/bookmark_node.cc8
-rw-r--r--chromium/components/bookmarks/browser/bookmark_node.h7
-rw-r--r--chromium/components/bookmarks/browser/bookmark_storage.cc4
-rw-r--r--chromium/components/bookmarks/browser/bookmark_storage.h10
-rw-r--r--chromium/components/bookmarks/browser/titled_url_index.cc (renamed from chromium/components/bookmarks/browser/bookmark_index.cc)148
-rw-r--r--chromium/components/bookmarks/browser/titled_url_index.h94
-rw-r--r--chromium/components/bookmarks/browser/titled_url_match.cc (renamed from chromium/components/bookmarks/browser/bookmark_match.cc)12
-rw-r--r--chromium/components/bookmarks/browser/titled_url_match.h (renamed from chromium/components/bookmarks/browser/bookmark_match.h)22
-rw-r--r--chromium/components/bookmarks/browser/titled_url_match_unittest.cc61
-rw-r--r--chromium/components/bookmarks/browser/titled_url_node.h29
-rw-r--r--chromium/components/bookmarks/browser/titled_url_node_sorter.h30
-rw-r--r--chromium/components/bookmarks/browser/typed_count_sorter.cc83
-rw-r--r--chromium/components/bookmarks/browser/typed_count_sorter.h35
-rw-r--r--chromium/components/browser_sync/BUILD.gn2
-rw-r--r--chromium/components/browser_sync/DEPS1
-rw-r--r--chromium/components/browser_sync/abstract_profile_sync_service_test.cc96
-rw-r--r--chromium/components/browser_sync/abstract_profile_sync_service_test.h4
-rw-r--r--chromium/components/browser_sync/profile_sync_components_factory_impl.cc90
-rw-r--r--chromium/components/browser_sync/profile_sync_components_factory_impl.h8
-rw-r--r--chromium/components/browser_sync/profile_sync_service.cc786
-rw-r--r--chromium/components/browser_sync/profile_sync_service.h230
-rw-r--r--chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc11
-rw-r--r--chromium/components/browser_sync/profile_sync_service_bookmark_unittest.cc1
-rw-r--r--chromium/components/browser_sync/profile_sync_service_mock.h13
-rw-r--r--chromium/components/browser_sync/profile_sync_service_startup_unittest.cc46
-rw-r--r--chromium/components/browser_sync/profile_sync_service_typed_url_unittest.cc11
-rw-r--r--chromium/components/browser_sync/profile_sync_service_unittest.cc192
-rw-r--r--chromium/components/browser_sync/profile_sync_test_util.cc10
-rw-r--r--chromium/components/browser_sync/test_profile_sync_service.cc2
-rw-r--r--chromium/components/browser_watcher/BUILD.gn209
-rw-r--r--chromium/components/browser_watcher/dump_postmortem_minidump_main_win.cc54
-rw-r--r--chromium/components/browser_watcher/postmortem_minidump_writer_win_unittest.cc4
-rw-r--r--chromium/components/browser_watcher/postmortem_report_collector.cc103
-rw-r--r--chromium/components/browser_watcher/postmortem_report_collector.h13
-rw-r--r--chromium/components/browser_watcher/postmortem_report_collector_unittest.cc156
-rw-r--r--chromium/components/browser_watcher/stability_data_names.cc19
-rw-r--r--chromium/components/browser_watcher/stability_data_names.h23
-rw-r--r--chromium/components/browser_watcher/stability_debugging.cc (renamed from chromium/components/browser_watcher/stability_debugging_win.cc)42
-rw-r--r--chromium/components/browser_watcher/stability_debugging.h (renamed from chromium/components/browser_watcher/stability_debugging_win.h)19
-rw-r--r--chromium/components/browser_watcher/stability_report.proto35
-rw-r--r--chromium/components/browser_watcher/watcher_metrics_provider_win.cc2
-rw-r--r--chromium/components/browsing_data/content/BUILD.gn2
-rw-r--r--chromium/components/browsing_data/content/conditional_cache_counting_helper.cc178
-rw-r--r--chromium/components/browsing_data/content/conditional_cache_counting_helper.h97
-rw-r--r--chromium/components/browsing_data/content/conditional_cache_deletion_helper.cc6
-rw-r--r--chromium/components/browsing_data/content/storage_partition_http_cache_data_remover.cc164
-rw-r--r--chromium/components/browsing_data/content/storage_partition_http_cache_data_remover.h27
-rw-r--r--chromium/components/browsing_data/core/browsing_data_utils.cc28
-rw-r--r--chromium/components/browsing_data/core/browsing_data_utils.h6
-rw-r--r--chromium/components/browsing_data/core/counters/autofill_counter.cc2
-rw-r--r--chromium/components/browsing_data/core/counters/autofill_counter.h5
-rw-r--r--chromium/components/browsing_data/core/counters/browsing_data_counter.cc6
-rw-r--r--chromium/components/browsing_data/core/history_notice_utils_unittest.cc6
-rw-r--r--chromium/components/cast_certificate/OWNERS3
-rw-r--r--chromium/components/cast_certificate/cast_cert_validator.cc3
-rw-r--r--chromium/components/cast_certificate/cast_crl.cc3
-rw-r--r--chromium/components/cdm/renderer/widevine_key_system_properties.cc8
-rw-r--r--chromium/components/certificate_reporting/cert_logger.proto4
-rw-r--r--chromium/components/certificate_reporting/error_report.cc8
-rw-r--r--chromium/components/certificate_reporting/error_report.h6
-rw-r--r--chromium/components/certificate_reporting/error_report_unittest.cc1
-rw-r--r--chromium/components/certificate_reporting/error_reporter.h2
-rw-r--r--chromium/components/certificate_transparency/log_proof_fetcher.h4
-rw-r--r--chromium/components/component_updater/component_updater_service.h12
-rw-r--r--chromium/components/components_google_chrome_strings.grd6
-rw-r--r--chromium/components/components_strings.grd3
-rw-r--r--chromium/components/constrained_window/BUILD.gn2
-rw-r--r--chromium/components/constrained_window/DEPS1
-rw-r--r--chromium/components/constrained_window/constrained_window_views.cc16
-rw-r--r--chromium/components/constrained_window/constrained_window_views_client.h4
-rw-r--r--chromium/components/constrained_window/constrained_window_views_unittest.cc38
-rw-r--r--chromium/components/content_settings/core/browser/BUILD.gn8
-rw-r--r--chromium/components/content_settings/core/browser/DEPS1
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_binary_value_map.cc64
-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_global_value_map.cc72
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_global_value_map.h (renamed from chromium/components/content_settings/core/browser/content_settings_binary_value_map.h)37
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_info.h10
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_policy_provider.cc20
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_pref.cc2
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_pref.h8
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_pref_provider.h1
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_provider.h1
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_registry.cc51
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_registry_unittest.cc2
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_utils.cc38
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_utils.h7
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_utils_unittest.cc83
-rw-r--r--chromium/components/content_settings/core/browser/cookie_settings.cc92
-rw-r--r--chromium/components/content_settings/core/browser/cookie_settings.h24
-rw-r--r--chromium/components/content_settings/core/browser/cookie_settings_unittest.cc9
-rw-r--r--chromium/components/content_settings/core/browser/host_content_settings_map.cc154
-rw-r--r--chromium/components/content_settings/core/browser/host_content_settings_map.h25
-rw-r--r--chromium/components/content_settings/core/browser/website_settings_info.cc2
-rw-r--r--chromium/components/content_settings/core/common/BUILD.gn9
-rw-r--r--chromium/components/content_settings/core/common/DEPS1
-rw-r--r--chromium/components/content_settings/core/common/OWNERS5
-rw-r--r--chromium/components/content_settings/core/common/content_settings.cc2
-rw-r--r--chromium/components/content_settings/core/common/content_settings.h3
-rw-r--r--chromium/components/content_settings/core/common/content_settings.mojom72
-rw-r--r--chromium/components/content_settings/core/common/content_settings.typemap27
-rw-r--r--chromium/components/content_settings/core/common/content_settings_pattern.h9
-rw-r--r--chromium/components/content_settings/core/common/content_settings_struct_traits.cc104
-rw-r--r--chromium/components/content_settings/core/common/content_settings_struct_traits.h142
-rw-r--r--chromium/components/content_settings/core/common/content_settings_types.h1
-rw-r--r--chromium/components/content_settings/core/common/pref_names.cc7
-rw-r--r--chromium/components/content_settings/core/common/pref_names.h3
-rw-r--r--chromium/components/contextual_search/browser/contextual_search_js_api_service_impl.cc1
-rw-r--r--chromium/components/contextual_search/renderer/contextual_search_wrapper.h4
-rw-r--r--chromium/components/contextual_search/renderer/overlay_js_render_frame_observer.cc1
-rw-r--r--chromium/components/crash/content/app/BUILD.gn22
-rw-r--r--chromium/components/crash/content/app/breakpad_linux.cc144
-rw-r--r--chromium/components/crash/content/app/breakpad_linux.h7
-rw-r--r--chromium/components/crash/content/app/breakpad_win.cc15
-rw-r--r--chromium/components/crash/content/app/crashpad_win.cc57
-rw-r--r--chromium/components/crash/content/app/fallback_crash_handler_launcher_win.cc131
-rw-r--r--chromium/components/crash/content/app/fallback_crash_handler_launcher_win.h68
-rw-r--r--chromium/components/crash/content/app/fallback_crash_handler_launcher_win_unittest.cc152
-rw-r--r--chromium/components/crash/content/app/fallback_crash_handler_win.cc464
-rw-r--r--chromium/components/crash/content/app/fallback_crash_handler_win.h62
-rw-r--r--chromium/components/crash/content/app/fallback_crash_handler_win_unittest.cc232
-rw-r--r--chromium/components/crash/content/app/fallback_crash_handling_win.cc119
-rw-r--r--chromium/components/crash/content/app/fallback_crash_handling_win.h43
-rw-r--r--chromium/components/crash/content/app/fallback_crash_handling_win_unittest.cc85
-rw-r--r--chromium/components/crash/content/browser/BUILD.gn4
-rw-r--r--chromium/components/crash/content/browser/crash_dump_manager_android.cc128
-rw-r--r--chromium/components/crash/content/browser/crash_dump_manager_android.h67
-rw-r--r--chromium/components/crash/content/browser/crash_dump_observer_android.cc154
-rw-r--r--chromium/components/crash/content/browser/crash_dump_observer_android.h109
-rw-r--r--chromium/components/crash/content/browser/crash_micro_dump_manager_android.cc129
-rw-r--r--chromium/components/crash/content/browser/crash_micro_dump_manager_android.h69
-rw-r--r--chromium/components/crash/core/browser/crashes_ui_util.cc11
-rw-r--r--chromium/components/crash/core/browser/resources/crashes.js21
-rw-r--r--chromium/components/crash_strings.grdp7
-rw-r--r--chromium/components/cronet/android/BUILD.gn455
-rw-r--r--chromium/components/cronet/ios/BUILD.gn8
-rw-r--r--chromium/components/cronet/ios/test/BUILD.gn1
-rw-r--r--chromium/components/cryptauth/BUILD.gn (renamed from chromium/components/proximity_auth/cryptauth/BUILD.gn)42
-rw-r--r--chromium/components/cryptauth/DEPS9
-rw-r--r--chromium/components/cryptauth/OWNERS5
-rw-r--r--chromium/components/cryptauth/README3
-rw-r--r--chromium/components/cryptauth/ble/BUILD.gn65
-rw-r--r--chromium/components/cryptauth/ble/OWNERS5
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.cc142
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h119
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder_unittest.cc295
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc639
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h316
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc1061
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_defines.h55
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.cc201
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.h74
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator_unittest.cc264
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.cc451
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.h222
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver_unittest.cc1026
-rw-r--r--chromium/components/cryptauth/ble/fake_wire_message.cc23
-rw-r--r--chromium/components/cryptauth/ble/fake_wire_message.h29
-rw-r--r--chromium/components/cryptauth/ble/remote_attribute.h22
-rw-r--r--chromium/components/cryptauth/bluetooth_throttler.h38
-rw-r--r--chromium/components/cryptauth/bluetooth_throttler_impl.cc66
-rw-r--r--chromium/components/cryptauth/bluetooth_throttler_impl.h69
-rw-r--r--chromium/components/cryptauth/bluetooth_throttler_impl_unittest.cc101
-rw-r--r--chromium/components/cryptauth/connection.cc105
-rw-r--r--chromium/components/cryptauth/connection.h117
-rw-r--r--chromium/components/cryptauth/connection_finder.h33
-rw-r--r--chromium/components/cryptauth/connection_observer.h39
-rw-r--r--chromium/components/cryptauth/connection_unittest.cc255
-rw-r--r--chromium/components/cryptauth/cryptauth_access_token_fetcher.h30
-rw-r--r--chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.cc60
-rw-r--r--chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.h59
-rw-r--r--chromium/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc76
-rw-r--r--chromium/components/cryptauth/cryptauth_api_call_flow.cc83
-rw-r--r--chromium/components/cryptauth/cryptauth_api_call_flow.h76
-rw-r--r--chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc153
-rw-r--r--chromium/components/cryptauth/cryptauth_client.h107
-rw-r--r--chromium/components/cryptauth/cryptauth_client_impl.cc203
-rw-r--r--chromium/components/cryptauth/cryptauth_client_impl.h150
-rw-r--r--chromium/components/cryptauth/cryptauth_client_impl_unittest.cc562
-rw-r--r--chromium/components/cryptauth/cryptauth_device_manager.cc539
-rw-r--r--chromium/components/cryptauth/cryptauth_device_manager.h179
-rw-r--r--chromium/components/cryptauth/cryptauth_device_manager_unittest.cc908
-rw-r--r--chromium/components/cryptauth/cryptauth_enroller.h47
-rw-r--r--chromium/components/cryptauth/cryptauth_enroller_impl.cc229
-rw-r--r--chromium/components/cryptauth/cryptauth_enroller_impl.h103
-rw-r--r--chromium/components/cryptauth/cryptauth_enroller_impl_unittest.cc359
-rw-r--r--chromium/components/cryptauth/cryptauth_enrollment_manager.cc304
-rw-r--r--chromium/components/cryptauth/cryptauth_enrollment_manager.h189
-rw-r--r--chromium/components/cryptauth/cryptauth_enrollment_manager_unittest.cc470
-rw-r--r--chromium/components/cryptauth/cryptauth_enrollment_utils.cc44
-rw-r--r--chromium/components/cryptauth/cryptauth_enrollment_utils.h23
-rw-r--r--chromium/components/cryptauth/cryptauth_gcm_manager.cc30
-rw-r--r--chromium/components/cryptauth/cryptauth_gcm_manager.h64
-rw-r--r--chromium/components/cryptauth/cryptauth_gcm_manager_impl.cc156
-rw-r--r--chromium/components/cryptauth/cryptauth_gcm_manager_impl.h81
-rw-r--r--chromium/components/cryptauth/cryptauth_gcm_manager_impl_unittest.cc198
-rw-r--r--chromium/components/cryptauth/cryptauth_test_util.cc17
-rw-r--r--chromium/components/cryptauth/cryptauth_test_util.h39
-rw-r--r--chromium/components/cryptauth/eid_generator.cc450
-rw-r--r--chromium/components/cryptauth/eid_generator.h236
-rw-r--r--chromium/components/cryptauth/eid_generator_unittest.cc770
-rw-r--r--chromium/components/cryptauth/fake_connection.cc61
-rw-r--r--chromium/components/cryptauth/fake_connection.h54
-rw-r--r--chromium/components/cryptauth/fake_cryptauth_gcm_manager.cc53
-rw-r--r--chromium/components/cryptauth/fake_cryptauth_gcm_manager.h64
-rw-r--r--chromium/components/cryptauth/fake_secure_message_delegate.cc200
-rw-r--r--chromium/components/cryptauth/fake_secure_message_delegate.h54
-rw-r--r--chromium/components/cryptauth/fake_secure_message_delegate_unittest.cc206
-rw-r--r--chromium/components/cryptauth/mock_cryptauth_client.cc46
-rw-r--r--chromium/components/cryptauth/mock_cryptauth_client.h88
-rw-r--r--chromium/components/cryptauth/mock_eid_generator.cc74
-rw-r--r--chromium/components/cryptauth/mock_eid_generator.h78
-rw-r--r--chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.cc44
-rw-r--r--chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.h41
-rw-r--r--chromium/components/cryptauth/mock_sync_scheduler.cc15
-rw-r--r--chromium/components/cryptauth/mock_sync_scheduler.h36
-rw-r--r--chromium/components/cryptauth/pref_names.cc57
-rw-r--r--chromium/components/cryptauth/pref_names.h25
-rw-r--r--chromium/components/cryptauth/proto/BUILD.gn (renamed from chromium/components/proximity_auth/cryptauth/proto/BUILD.gn)0
-rw-r--r--chromium/components/cryptauth/proto/cryptauth_api.proto513
-rw-r--r--chromium/components/cryptauth/proto/securemessage.proto116
-rw-r--r--chromium/components/cryptauth/remote_beacon_seed_fetcher.cc41
-rw-r--r--chromium/components/cryptauth/remote_beacon_seed_fetcher.h40
-rw-r--r--chromium/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc173
-rw-r--r--chromium/components/cryptauth/remote_device.cc70
-rw-r--r--chromium/components/cryptauth/remote_device.h58
-rw-r--r--chromium/components/cryptauth/remote_device_test_util.cc23
-rw-r--r--chromium/components/cryptauth/remote_device_test_util.h18
-rw-r--r--chromium/components/cryptauth/secure_message_delegate.cc30
-rw-r--r--chromium/components/cryptauth/secure_message_delegate.h102
-rw-r--r--chromium/components/cryptauth/switches.cc15
-rw-r--r--chromium/components/cryptauth/switches.h18
-rw-r--r--chromium/components/cryptauth/sync_scheduler.cc31
-rw-r--r--chromium/components/cryptauth/sync_scheduler.h98
-rw-r--r--chromium/components/cryptauth/sync_scheduler_impl.cc197
-rw-r--r--chromium/components/cryptauth/sync_scheduler_impl.h104
-rw-r--r--chromium/components/cryptauth/sync_scheduler_impl_unittest.cc292
-rw-r--r--chromium/components/cryptauth/wire_message.cc173
-rw-r--r--chromium/components/cryptauth/wire_message.h49
-rw-r--r--chromium/components/cryptauth/wire_message_unittest.cc214
-rw-r--r--chromium/components/data_reduction_proxy/OWNERS2
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/BUILD.gn3
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.cc19
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_lofi_decider_unittest.cc51
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_lofi_ui_service_unittest.cc10
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc69
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.h45
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider_unittest.cc179
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc1
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h3
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc33
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h6
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc45
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc128
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h24
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc71
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc284
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h69
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc45
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h4
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc305
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc24
-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.cc435
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc1
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h6
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc81
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h49
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc135
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc10
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h3
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc34
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc21
-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.cc24
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h2
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics_unittest.cc6
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc17
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h13
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc94
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc181
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h34
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc275
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc5
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h1
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc15
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h10
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc3
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h1
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc43
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h11
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc10
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h17
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc61
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc46
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h29
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/db_data_owner.h4
-rw-r--r--chromium/components/data_reduction_proxy/core/common/BUILD.gn3
-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_creator.h1
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.cc35
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h27
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h4
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc161
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h63
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc7
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h14
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc234
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.cc5
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h1
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_server.cc34
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_server.h44
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc9
-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.cc62
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_util.h5
-rw-r--r--chromium/components/data_reduction_proxy/core/common/resource_type_provider.h43
-rw-r--r--chromium/components/data_reduction_proxy/proto/client_config.proto14
-rw-r--r--chromium/components/data_usage/OWNERS1
-rw-r--r--chromium/components/data_use_measurement/content/BUILD.gn1
-rw-r--r--chromium/components/data_use_measurement/content/DEPS1
-rw-r--r--chromium/components/data_use_measurement/content/content_url_request_classifier.cc41
-rw-r--r--chromium/components/data_use_measurement/content/content_url_request_classifier.h4
-rw-r--r--chromium/components/data_use_measurement/core/BUILD.gn4
-rw-r--r--chromium/components/data_use_measurement/core/DEPS1
-rw-r--r--chromium/components/data_use_measurement/core/data_use.cc4
-rw-r--r--chromium/components/data_use_measurement/core/data_use.h3
-rw-r--r--chromium/components/data_use_measurement/core/data_use_ascriber.cc16
-rw-r--r--chromium/components/data_use_measurement/core/data_use_ascriber.h23
-rw-r--r--chromium/components/data_use_measurement/core/data_use_measurement.cc110
-rw-r--r--chromium/components/data_use_measurement/core/data_use_measurement.h34
-rw-r--r--chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc217
-rw-r--r--chromium/components/data_use_measurement/core/data_use_network_delegate.cc23
-rw-r--r--chromium/components/data_use_measurement/core/data_use_network_delegate.h7
-rw-r--r--chromium/components/data_use_measurement/core/data_use_network_delegate_unittest.cc34
-rw-r--r--chromium/components/data_use_measurement/core/data_use_recorder.cc17
-rw-r--r--chromium/components/data_use_measurement/core/data_use_recorder.h51
-rw-r--r--chromium/components/data_use_measurement/core/data_use_user_data.cc4
-rw-r--r--chromium/components/data_use_measurement/core/data_use_user_data.h29
-rw-r--r--chromium/components/data_use_measurement/core/url_request_classifier.h10
-rw-r--r--chromium/components/device_event_log/OWNERS2
-rw-r--r--chromium/components/discardable_memory/DEPS3
-rw-r--r--chromium/components/discardable_memory/client/BUILD.gn1
-rw-r--r--chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.cc158
-rw-r--r--chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.h44
-rw-r--r--chromium/components/discardable_memory/common/BUILD.gn1
-rw-r--r--chromium/components/discardable_memory/common/discardable_shared_memory_id.h17
-rw-r--r--chromium/components/discardable_memory/public/interfaces/BUILD.gn17
-rw-r--r--chromium/components/discardable_memory/public/interfaces/OWNERS2
-rw-r--r--chromium/components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom16
-rw-r--r--chromium/components/discardable_memory/service/BUILD.gn1
-rw-r--r--chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc92
-rw-r--r--chromium/components/discardable_memory/service/discardable_shared_memory_manager.h31
-rw-r--r--chromium/components/discardable_memory/service/discardable_shared_memory_manager_unittest.cc21
-rw-r--r--chromium/components/display_compositor/DEPS1
-rw-r--r--chromium/components/display_compositor/buffer_queue.cc31
-rw-r--r--chromium/components/display_compositor/buffer_queue.h6
-rw-r--r--chromium/components/display_compositor/buffer_queue_unittest.cc36
-rw-r--r--chromium/components/display_compositor/compositor_overlay_candidate_validator_android.cc11
-rw-r--r--chromium/components/display_compositor/gl_helper_benchmark.cc20
-rw-r--r--chromium/components/display_compositor/gl_helper_unittest.cc20
-rw-r--r--chromium/components/display_compositor/yuv_readback_unittest.cc21
-rw-r--r--chromium/components/dom_distiller/DEPS1
-rw-r--r--chromium/components/dom_distiller/content/browser/distillability_driver.cc3
-rw-r--r--chromium/components/dom_distiller/content/browser/distillability_driver.h3
-rw-r--r--chromium/components/dom_distiller/content/browser/distillable_page_utils_android.cc41
-rw-r--r--chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.cc2
-rw-r--r--chromium/components/dom_distiller/content/browser/distiller_javascript_service_impl.h1
-rw-r--r--chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.h2
-rw-r--r--chromium/components/dom_distiller/content/renderer/distillability_agent.cc1
-rw-r--r--chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.cc2
-rw-r--r--chromium/components/dom_distiller/content/renderer/distiller_js_render_frame_observer.h1
-rw-r--r--chromium/components/dom_distiller/core/BUILD.gn2
-rw-r--r--chromium/components/dom_distiller/core/distilled_page_prefs_unittests.cc6
-rw-r--r--chromium/components/dom_distiller/core/distiller_page.cc2
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc6
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_service.cc28
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_store.cc4
-rw-r--r--chromium/components/dom_distiller/ios/BUILD.gn2
-rw-r--r--chromium/components/dom_distiller/ios/DEPS2
-rw-r--r--chromium/components/dom_distiller/ios/distiller_page_factory_ios.h6
-rw-r--r--chromium/components/dom_distiller/ios/distiller_page_factory_ios.mm9
-rw-r--r--chromium/components/dom_distiller/ios/distiller_page_ios.h26
-rw-r--r--chromium/components/dom_distiller/ios/distiller_page_ios.mm121
-rw-r--r--chromium/components/dom_distiller/standalone/content_extractor_browsertest.cc6
-rw-r--r--chromium/components/dom_distiller/webui/dom_distiller_ui.cc4
-rw-r--r--chromium/components/domain_reliability/BUILD.gn4
-rw-r--r--chromium/components/domain_reliability/DEPS1
-rwxr-xr-xchromium/components/domain_reliability/bake_in_configs.py1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/c_2mdn_net.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/c_android_clients_google_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/c_bigcache_googleapis_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/c_doc-0-0-sj_sj_googleusercontent_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/c_docs_google_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/c_drive_google_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/c_googlesyndication_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/c_pack_google_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/c_play_google_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/c_youtube_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/clients2_google_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/docs_google_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/gcp_gvt2_com.json21
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/gcp_gvt6_com.json21
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/google-analytics_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/googlevideo_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/gvt1_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/gvt2_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/gvt6_com.json21
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/ssl_gstatic_com.json1
-rw-r--r--chromium/components/domain_reliability/baked_in_configs/www_google_com.json1
-rw-r--r--chromium/components/domain_reliability/beacon.cc2
-rw-r--r--chromium/components/domain_reliability/beacon.h3
-rw-r--r--chromium/components/domain_reliability/config.cc3
-rw-r--r--chromium/components/domain_reliability/config.h4
-rw-r--r--chromium/components/domain_reliability/config_unittest.cc11
-rw-r--r--chromium/components/domain_reliability/dispatcher.cc11
-rw-r--r--chromium/components/domain_reliability/dispatcher.h12
-rw-r--r--chromium/components/domain_reliability/google_configs.cc6
-rw-r--r--chromium/components/domain_reliability/header.cc9
-rw-r--r--chromium/components/domain_reliability/header_unittest.cc5
-rw-r--r--chromium/components/domain_reliability/monitor.cc27
-rw-r--r--chromium/components/domain_reliability/monitor.h9
-rw-r--r--chromium/components/domain_reliability/monitor_unittest.cc20
-rw-r--r--chromium/components/domain_reliability/quic_error_mapping.cc2
-rw-r--r--chromium/components/domain_reliability/quic_error_mapping.h2
-rw-r--r--chromium/components/domain_reliability/scheduler.h1
-rw-r--r--chromium/components/domain_reliability/service.cc39
-rw-r--r--chromium/components/domain_reliability/service.h10
-rw-r--r--chromium/components/domain_reliability/test_util.cc4
-rw-r--r--chromium/components/domain_reliability/test_util.h4
-rw-r--r--chromium/components/domain_reliability/uploader.cc38
-rw-r--r--chromium/components/domain_reliability/uploader.h9
-rw-r--r--chromium/components/domain_reliability/uploader_unittest.cc51
-rw-r--r--chromium/components/domain_reliability/util.cc18
-rw-r--r--chromium/components/domain_reliability/util.h8
-rw-r--r--chromium/components/error_page/common/BUILD.gn2
-rw-r--r--chromium/components/error_page/common/localized_error.cc40
-rw-r--r--chromium/components/error_page/renderer/net_error_helper_core.cc14
-rw-r--r--chromium/components/error_page/renderer/net_error_helper_core.h7
-rw-r--r--chromium/components/error_page/renderer/net_error_helper_core_unittest.cc2
-rw-r--r--chromium/components/error_page_strings.grdp12
-rw-r--r--chromium/components/exo/BUILD.gn20
-rw-r--r--chromium/components/exo/DEPS8
-rw-r--r--chromium/components/exo/buffer.cc130
-rw-r--r--chromium/components/exo/buffer.h53
-rw-r--r--chromium/components/exo/buffer_unittest.cc75
-rw-r--r--chromium/components/exo/compositor_frame_sink.cc72
-rw-r--r--chromium/components/exo/compositor_frame_sink.h52
-rw-r--r--chromium/components/exo/compositor_frame_sink_holder.cc131
-rw-r--r--chromium/components/exo/compositor_frame_sink_holder.h94
-rw-r--r--chromium/components/exo/display.cc28
-rw-r--r--chromium/components/exo/gamepad.cc22
-rw-r--r--chromium/components/exo/gamepad_unittest.cc3
-rw-r--r--chromium/components/exo/keyboard.cc56
-rw-r--r--chromium/components/exo/keyboard.h23
-rw-r--r--chromium/components/exo/keyboard_delegate.h2
-rw-r--r--chromium/components/exo/keyboard_device_configuration_delegate.h27
-rw-r--r--chromium/components/exo/pointer.cc30
-rw-r--r--chromium/components/exo/pointer.h1
-rw-r--r--chromium/components/exo/pointer_delegate.h1
-rw-r--r--chromium/components/exo/pointer_unittest.cc8
-rw-r--r--chromium/components/exo/shared_memory.cc10
-rw-r--r--chromium/components/exo/shell_surface.cc278
-rw-r--r--chromium/components/exo/shell_surface.h44
-rw-r--r--chromium/components/exo/shell_surface_unittest.cc189
-rw-r--r--chromium/components/exo/surface.cc287
-rw-r--r--chromium/components/exo/surface.h112
-rw-r--r--chromium/components/exo/surface_unittest.cc33
-rw-r--r--chromium/components/exo/touch_unittest.cc2
-rw-r--r--chromium/components/exo/wayland/BUILD.gn11
-rw-r--r--chromium/components/exo/wayland/DEPS1
-rw-r--r--chromium/components/exo/wayland/clients/motion_events.cc253
-rw-r--r--chromium/components/exo/wayland/server.cc380
-rw-r--r--chromium/components/exo/wm_helper.cc14
-rw-r--r--chromium/components/exo/wm_helper.h12
-rw-r--r--chromium/components/exo/wm_helper_ash.cc11
-rw-r--r--chromium/components/exo/wm_helper_ash.h7
-rw-r--r--chromium/components/exo/wm_helper_mus.cc127
-rw-r--r--chromium/components/exo/wm_helper_mus.h37
-rw-r--r--chromium/components/favicon/content/content_favicon_driver.h1
-rw-r--r--chromium/components/favicon/core/fallback_icon_client.h2
-rw-r--r--chromium/components/favicon/core/favicon_driver_impl.h2
-rw-r--r--chromium/components/favicon/core/favicon_handler.h4
-rw-r--r--chromium/components/favicon/ios/web_favicon_driver.h1
-rw-r--r--chromium/components/feedback/BUILD.gn2
-rw-r--r--chromium/components/feedback/DEPS2
-rw-r--r--chromium/components/feedback/feedback_data.h1
-rw-r--r--chromium/components/feedback/feedback_uploader_chrome.cc4
-rw-r--r--chromium/components/feedback/feedback_uploader_unittest.cc12
-rw-r--r--chromium/components/feedback/feedback_util.h6
-rw-r--r--chromium/components/feedback/tracing_manager.h1
-rw-r--r--chromium/components/filesystem/directory_impl.cc46
-rw-r--r--chromium/components/filesystem/directory_impl.h5
-rw-r--r--chromium/components/filesystem/directory_impl_unittest.cc48
-rw-r--r--chromium/components/filesystem/file_impl.cc15
-rw-r--r--chromium/components/filesystem/file_impl_unittest.cc57
-rw-r--r--chromium/components/filesystem/file_system_app.cc1
-rw-r--r--chromium/components/filesystem/file_system_app.h4
-rw-r--r--chromium/components/filesystem/file_system_impl.cc1
-rw-r--r--chromium/components/filesystem/file_system_impl.h1
-rw-r--r--chromium/components/filesystem/files_test_base.cc4
-rw-r--r--chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc10
-rw-r--r--chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.h2
-rw-r--r--chromium/components/filesystem/public/cpp/prefs/pref_service_factory.cc2
-rw-r--r--chromium/components/filesystem/public/interfaces/BUILD.gn3
-rw-r--r--chromium/components/filesystem/public/interfaces/directory.mojom5
-rw-r--r--chromium/components/filesystem/public/interfaces/file.mojom3
-rw-r--r--chromium/components/filesystem/util.cc1
-rw-r--r--chromium/components/filesystem/util.h4
-rw-r--r--chromium/components/font_service/font_service_app.cc19
-rw-r--r--chromium/components/font_service/public/cpp/font_loader.cc2
-rw-r--r--chromium/components/font_service/public/cpp/font_service_thread.cc11
-rw-r--r--chromium/components/font_service/public/cpp/font_service_thread.h2
-rw-r--r--chromium/components/font_service/public/interfaces/BUILD.gn4
-rw-r--r--chromium/components/font_service/public/interfaces/font_service.mojom4
-rw-r--r--chromium/components/google/core/browser/BUILD.gn1
-rw-r--r--chromium/components/google/core/browser/google_tld_list.h48
-rw-r--r--chromium/components/google/core/browser/google_url_tracker.h6
-rw-r--r--chromium/components/google/core/browser/google_util.cc23
-rw-r--r--chromium/components/google/core/browser/google_util_unittest.cc10
-rw-r--r--chromium/components/grpc_support/bidirectional_stream_unittest.cc3
-rw-r--r--chromium/components/guest_view/browser/guest_view_base.cc6
-rw-r--r--chromium/components/guest_view/browser/guest_view_base.h2
-rw-r--r--chromium/components/guest_view/browser/guest_view_message_filter.h5
-rw-r--r--chromium/components/guest_view/renderer/guest_view_container.h2
-rw-r--r--chromium/components/guest_view/renderer/guest_view_container_dispatcher.h1
-rw-r--r--chromium/components/handoff/handoff_manager.h12
-rw-r--r--chromium/components/handoff/handoff_manager.mm8
-rw-r--r--chromium/components/history/core/browser/BUILD.gn1
-rw-r--r--chromium/components/history/core/browser/DEPS3
-rw-r--r--chromium/components/history/core/browser/history_backend.cc9
-rw-r--r--chromium/components/history/core/browser/history_backend.h7
-rw-r--r--chromium/components/history/core/browser/history_client.h7
-rw-r--r--chromium/components/history/core/browser/history_database.h1
-rw-r--r--chromium/components/history/core/browser/history_delete_directives_data_type_controller.cc9
-rw-r--r--chromium/components/history/core/browser/history_delete_directives_data_type_controller.h6
-rw-r--r--chromium/components/history/core/browser/history_service.cc253
-rw-r--r--chromium/components/history/core/browser/history_service.h18
-rw-r--r--chromium/components/history/core/browser/history_types.h1
-rw-r--r--chromium/components/history/core/browser/in_memory_history_backend.h1
-rw-r--r--chromium/components/history/core/browser/page_usage_data.h2
-rw-r--r--chromium/components/history/core/browser/top_sites.h20
-rw-r--r--chromium/components/history/core/browser/top_sites_database.cc1
-rw-r--r--chromium/components/history/core/browser/top_sites_impl.cc214
-rw-r--r--chromium/components/history/core/browser/top_sites_impl.h7
-rw-r--r--chromium/components/history/core/browser/typed_url_data_type_controller.cc14
-rw-r--r--chromium/components/history/core/browser/typed_url_data_type_controller.h19
-rw-r--r--chromium/components/history/core/browser/typed_url_syncable_service.cc16
-rw-r--r--chromium/components/history/core/browser/typed_url_syncable_service.h8
-rw-r--r--chromium/components/history/core/browser/web_history_service.cc12
-rw-r--r--chromium/components/infobars/core/confirm_infobar_delegate.h1
-rw-r--r--chromium/components/infobars/core/infobar_container.cc6
-rw-r--r--chromium/components/infobars/core/infobar_delegate.h3
-rw-r--r--chromium/components/infobars/core/infobar_manager.cc3
-rw-r--r--chromium/components/infobars/core/infobar_manager.h13
-rw-r--r--chromium/components/invalidation/impl/BUILD.gn3
-rw-r--r--chromium/components/json_schema/json_schema_validator.cc95
-rw-r--r--chromium/components/json_schema/json_schema_validator.h1
-rw-r--r--chromium/components/json_schema/json_schema_validator_unittest_base.cc4
-rw-r--r--chromium/components/keyed_service/core/refcounted_keyed_service.cc14
-rw-r--r--chromium/components/keyed_service/core/refcounted_keyed_service.h18
-rw-r--r--chromium/components/leveldb/leveldb_database_impl.h2
-rw-r--r--chromium/components/leveldb/leveldb_mojo_proxy.cc21
-rw-r--r--chromium/components/leveldb/leveldb_service_impl.cc40
-rw-r--r--chromium/components/leveldb/leveldb_service_impl.h18
-rw-r--r--chromium/components/leveldb/leveldb_service_unittest.cc137
-rw-r--r--chromium/components/leveldb/public/interfaces/leveldb.mojom18
-rw-r--r--chromium/components/leveldb/remote_iterator_unittest.cc11
-rw-r--r--chromium/components/leveldb_proto/BUILD.gn1
-rw-r--r--chromium/components/leveldb_proto/leveldb_database.cc33
-rw-r--r--chromium/components/leveldb_proto/leveldb_database.h23
-rw-r--r--chromium/components/leveldb_proto/options.h29
-rw-r--r--chromium/components/leveldb_proto/proto_database.h18
-rw-r--r--chromium/components/leveldb_proto/proto_database_impl.h33
-rw-r--r--chromium/components/leveldb_proto/proto_database_impl_unittest.cc11
-rw-r--r--chromium/components/leveldb_proto/testing/fake_db.h16
-rw-r--r--chromium/components/login/screens/screen_context.h1
-rw-r--r--chromium/components/metrics/BUILD.gn28
-rw-r--r--chromium/components/metrics/DEPS1
-rw-r--r--chromium/components/metrics/call_stack_profile_metrics_provider.cc51
-rw-r--r--chromium/components/metrics/call_stack_profile_metrics_provider.h14
-rw-r--r--chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc785
-rw-r--r--chromium/components/metrics/child_call_stack_profile_collector_unittest.cc2
-rw-r--r--chromium/components/metrics/data_use_tracker.cc52
-rw-r--r--chromium/components/metrics/data_use_tracker.h19
-rw-r--r--chromium/components/metrics/data_use_tracker_unittest.cc6
-rw-r--r--chromium/components/metrics/drive_metrics_provider_linux.cc9
-rw-r--r--chromium/components/metrics/drive_metrics_provider_mac.mm2
-rw-r--r--chromium/components/metrics/file_metrics_provider.cc7
-rw-r--r--chromium/components/metrics/file_metrics_provider.h2
-rw-r--r--chromium/components/metrics/metrics_log.cc4
-rw-r--r--chromium/components/metrics/metrics_log.h5
-rw-r--r--chromium/components/metrics/metrics_log_manager.cc15
-rw-r--r--chromium/components/metrics/metrics_log_manager_unittest.cc7
-rw-r--r--chromium/components/metrics/metrics_pref_names.cc15
-rw-r--r--chromium/components/metrics/metrics_provider.h1
-rw-r--r--chromium/components/metrics/metrics_reporting_scheduler.cc32
-rw-r--r--chromium/components/metrics/metrics_reporting_scheduler.h16
-rw-r--r--chromium/components/metrics/metrics_rotation_scheduler.cc53
-rw-r--r--chromium/components/metrics/metrics_rotation_scheduler.h62
-rw-r--r--chromium/components/metrics/metrics_scheduler.cc64
-rw-r--r--chromium/components/metrics/metrics_scheduler.h65
-rw-r--r--chromium/components/metrics/metrics_service.cc208
-rw-r--r--chromium/components/metrics/metrics_service.h32
-rw-r--r--chromium/components/metrics/metrics_service_client.cc6
-rw-r--r--chromium/components/metrics/metrics_service_client.h9
-rw-r--r--chromium/components/metrics/metrics_upload_scheduler.cc93
-rw-r--r--chromium/components/metrics/metrics_upload_scheduler.h53
-rw-r--r--chromium/components/metrics/net/DEPS1
-rw-r--r--chromium/components/metrics/net/net_metrics_log_uploader.cc4
-rw-r--r--chromium/components/metrics/net/network_metrics_provider.cc173
-rw-r--r--chromium/components/metrics/net/network_metrics_provider.h68
-rw-r--r--chromium/components/metrics/net/network_metrics_provider_unittest.cc144
-rw-r--r--chromium/components/metrics/persisted_logs.cc85
-rw-r--r--chromium/components/metrics/persisted_logs.h20
-rw-r--r--chromium/components/metrics/persisted_logs_metrics.h35
-rw-r--r--chromium/components/metrics/persisted_logs_metrics_impl.cc35
-rw-r--r--chromium/components/metrics/persisted_logs_metrics_impl.h35
-rw-r--r--chromium/components/metrics/persisted_logs_unittest.cc7
-rw-r--r--chromium/components/metrics/profiler/profiler_metrics_provider.cc22
-rw-r--r--chromium/components/metrics/profiler/profiler_metrics_provider_unittest.cc33
-rw-r--r--chromium/components/metrics/profiler/tracking_synchronizer_observer.h4
-rw-r--r--chromium/components/metrics/profiler/tracking_synchronizer_unittest.cc12
-rw-r--r--chromium/components/metrics/proto/call_stack_profile.proto8
-rw-r--r--chromium/components/metrics/proto/execution_context.proto26
-rw-r--r--chromium/components/metrics/proto/system_profile.proto22
-rw-r--r--chromium/components/metrics/public/cpp/call_stack_profile.typemap1
-rw-r--r--chromium/components/metrics/public/cpp/call_stack_profile_struct_traits.h52
-rw-r--r--chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc42
-rw-r--r--chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom10
-rw-r--r--chromium/components/metrics/test_metrics_service_client.cc5
-rw-r--r--chromium/components/metrics/test_metrics_service_client.h3
-rw-r--r--chromium/components/metrics_services_manager/metrics_services_manager.cc12
-rw-r--r--chromium/components/metrics_services_manager/metrics_services_manager.h14
-rw-r--r--chromium/components/metrics_services_manager/metrics_services_manager_client.h5
-rw-r--r--chromium/components/minidump_uploader/BUILD.gn2
-rw-r--r--chromium/components/nacl/broker/BUILD.gn11
-rw-r--r--chromium/components/nacl/common/BUILD.gn32
-rw-r--r--chromium/components/nacl/common/nacl.mojom26
-rw-r--r--chromium/components/nacl/common/nacl.typemap15
-rw-r--r--chromium/components/nacl/loader/BUILD.gn29
-rw-r--r--chromium/components/nacl/loader/sandbox_linux/BUILD.gn5
-rw-r--r--chromium/components/nacl/renderer/plugin/BUILD.gn1
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_delegate.h2
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_throttle.h2
-rw-r--r--chromium/components/neterror/resources/neterror.js12
-rw-r--r--chromium/components/neterror/resources/offline.js5
-rw-r--r--chromium/components/network_session_configurator/network_session_configurator.cc72
-rw-r--r--chromium/components/network_session_configurator/network_session_configurator_unittest.cc18
-rw-r--r--chromium/components/network_time/network_time_tracker.cc11
-rw-r--r--chromium/components/network_time/network_time_tracker.h6
-rw-r--r--chromium/components/network_time/network_time_tracker_unittest.cc131
-rw-r--r--chromium/components/new_or_sad_tab_strings.grdp5
-rw-r--r--chromium/components/ntp_snippets/BUILD.gn68
-rw-r--r--chromium/components/ntp_snippets/OWNERS9
-rw-r--r--chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.cc157
-rw-r--r--chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.h26
-rw-r--r--chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils_unittest.cc246
-rw-r--r--chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc133
-rw-r--r--chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h12
-rw-r--r--chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider_unittest.cc146
-rw-r--r--chromium/components/ntp_snippets/category.cc25
-rw-r--r--chromium/components/ntp_snippets/category.h23
-rw-r--r--chromium/components/ntp_snippets/category_factory.cc89
-rw-r--r--chromium/components/ntp_snippets/category_factory.h61
-rw-r--r--chromium/components/ntp_snippets/category_factory_unittest.cc129
-rw-r--r--chromium/components/ntp_snippets/category_rankers/category_ranker.h46
-rw-r--r--chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc482
-rw-r--r--chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.h95
-rw-r--r--chromium/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc749
-rw-r--r--chromium/components/ntp_snippets/category_rankers/constant_category_ranker.cc99
-rw-r--r--chromium/components/ntp_snippets/category_rankers/constant_category_ranker.h45
-rw-r--r--chromium/components/ntp_snippets/category_rankers/constant_category_ranker_unittest.cc124
-rw-r--r--chromium/components/ntp_snippets/category_rankers/fake_category_ranker.cc41
-rw-r--r--chromium/components/ntp_snippets/category_rankers/fake_category_ranker.h39
-rw-r--r--chromium/components/ntp_snippets/category_rankers/mock_category_ranker.cc13
-rw-r--r--chromium/components/ntp_snippets/category_rankers/mock_category_ranker.h29
-rw-r--r--chromium/components/ntp_snippets/category_unittest.cc54
-rw-r--r--chromium/components/ntp_snippets/content_suggestion.cc5
-rw-r--r--chromium/components/ntp_snippets/content_suggestion.h31
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_metrics.cc200
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_metrics.h14
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc52
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_provider.cc8
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_provider.h20
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_service.cc171
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_service.h105
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_service_unittest.cc393
-rw-r--r--chromium/components/ntp_snippets/fake_content_suggestions_provider_observer.cc61
-rw-r--r--chromium/components/ntp_snippets/fake_content_suggestions_provider_observer.h54
-rw-r--r--chromium/components/ntp_snippets/features.cc81
-rw-r--r--chromium/components/ntp_snippets/features.h39
-rw-r--r--chromium/components/ntp_snippets/mock_content_suggestions_provider.cc68
-rw-r--r--chromium/components/ntp_snippets/mock_content_suggestions_provider.h72
-rw-r--r--chromium/components/ntp_snippets/offline_pages/offline_pages_test_utils.cc5
-rw-r--r--chromium/components/ntp_snippets/offline_pages/offline_pages_test_utils.h4
-rw-r--r--chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc103
-rw-r--r--chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h26
-rw-r--r--chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc151
-rw-r--r--chromium/components/ntp_snippets/physical_web_pages/DEPS4
-rw-r--r--chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc334
-rw-r--r--chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h78
-rw-r--r--chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc465
-rw-r--r--chromium/components/ntp_snippets/pref_names.cc30
-rw-r--r--chromium/components/ntp_snippets/pref_names.h35
-rw-r--r--chromium/components/ntp_snippets/remote/json_request.cc512
-rw-r--r--chromium/components/ntp_snippets/remote/json_request.h193
-rw-r--r--chromium/components/ntp_snippets/remote/json_request_unittest.cc335
-rw-r--r--chromium/components/ntp_snippets/remote/ntp_snippet.cc307
-rw-r--r--chromium/components/ntp_snippets/remote/ntp_snippet.h91
-rw-r--r--chromium/components/ntp_snippets/remote/ntp_snippet_unittest.cc316
-rw-r--r--chromium/components/ntp_snippets/remote/ntp_snippets_fetcher.cc880
-rw-r--r--chromium/components/ntp_snippets/remote/ntp_snippets_scheduler.h36
-rw-r--r--chromium/components/ntp_snippets/remote/ntp_snippets_status_service.cc119
-rw-r--r--chromium/components/ntp_snippets/remote/ntp_snippets_status_service.h86
-rw-r--r--chromium/components/ntp_snippets/remote/ntp_snippets_status_service_unittest.cc59
-rw-r--r--chromium/components/ntp_snippets/remote/persistent_scheduler.h47
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_database.cc49
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_database_unittest.cc39
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.cc631
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.h (renamed from chromium/components/ntp_snippets/remote/ntp_snippets_fetcher.h)217
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc (renamed from chromium/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc)712
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider.cc1306
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider.h413
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc1284
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h455
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc (renamed from chromium/components/ntp_snippets/remote/remote_suggestions_provider_unittest.cc)950
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_scheduler.h37
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_status_service.cc113
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_status_service.h85
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_status_service_unittest.cc92
-rw-r--r--chromium/components/ntp_snippets/remote/request_params.cc13
-rw-r--r--chromium/components/ntp_snippets/remote/request_params.h57
-rw-r--r--chromium/components/ntp_snippets/remote/request_throttler.cc3
-rw-r--r--chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc517
-rw-r--r--chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h195
-rw-r--r--chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc589
-rw-r--r--chromium/components/ntp_snippets/remote/test_utils.cc (renamed from chromium/components/ntp_snippets/remote/ntp_snippets_test_utils.cc)21
-rw-r--r--chromium/components/ntp_snippets/remote/test_utils.h (renamed from chromium/components/ntp_snippets/remote/ntp_snippets_test_utils.h)17
-rw-r--r--chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.cc25
-rw-r--r--chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h1
-rw-r--r--chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc10
-rw-r--r--chromium/components/ntp_snippets/status.cc8
-rw-r--r--chromium/components/ntp_snippets/status.h12
-rw-r--r--chromium/components/ntp_snippets/user_classifier.cc65
-rw-r--r--chromium/components/ntp_snippets_strings.grdp27
-rw-r--r--chromium/components/ntp_tiles/BUILD.gn40
-rw-r--r--chromium/components/ntp_tiles/DEPS2
-rw-r--r--chromium/components/ntp_tiles/field_trial.cc27
-rw-r--r--chromium/components/ntp_tiles/icon_cacher.h49
-rw-r--r--chromium/components/ntp_tiles/icon_cacher_impl.cc (renamed from chromium/components/ntp_tiles/icon_cacher.cc)28
-rw-r--r--chromium/components/ntp_tiles/icon_cacher_impl.h64
-rw-r--r--chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc (renamed from chromium/components/ntp_tiles/icon_cacher_unittest.cc)12
-rw-r--r--chromium/components/ntp_tiles/json_unsafe_parser.cc41
-rw-r--r--chromium/components/ntp_tiles/json_unsafe_parser.h40
-rw-r--r--chromium/components/ntp_tiles/metrics.cc81
-rw-r--r--chromium/components/ntp_tiles/metrics.h32
-rw-r--r--chromium/components/ntp_tiles/metrics_unittest.cc163
-rw-r--r--chromium/components/ntp_tiles/most_visited_sites.cc80
-rw-r--r--chromium/components/ntp_tiles/most_visited_sites.h52
-rw-r--r--chromium/components/ntp_tiles/most_visited_sites_unittest.cc835
-rw-r--r--chromium/components/ntp_tiles/ntp_tile.h25
-rw-r--r--chromium/components/ntp_tiles/ntp_tile_source.h28
-rw-r--r--chromium/components/ntp_tiles/popular_sites.h120
-rw-r--r--chromium/components/ntp_tiles/popular_sites_impl.cc (renamed from chromium/components/ntp_tiles/popular_sites.cc)316
-rw-r--r--chromium/components/ntp_tiles/popular_sites_impl.h111
-rw-r--r--chromium/components/ntp_tiles/popular_sites_impl_unittest.cc (renamed from chromium/components/ntp_tiles/popular_sites_unittest.cc)171
-rw-r--r--chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc166
-rw-r--r--chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h61
-rw-r--r--chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.cc14
-rw-r--r--chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.h74
-rw-r--r--chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.cc45
-rw-r--r--chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.h2
-rw-r--r--chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.css54
-rw-r--r--chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.html117
-rw-r--r--chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.js43
-rw-r--r--chromium/components/ntp_tiles/webui/resources/popular_sites_internals.html3
-rw-r--r--chromium/components/offline_pages/BUILD.gn121
-rw-r--r--chromium/components/offline_pages/background/change_requests_state_task.cc90
-rw-r--r--chromium/components/offline_pages/background/offliner_factory.h31
-rw-r--r--chromium/components/offline_pages/background/pick_request_task_factory.cc38
-rw-r--r--chromium/components/offline_pages/background/pick_request_task_factory.h49
-rw-r--r--chromium/components/offline_pages/content/background_loader/BUILD.gn46
-rw-r--r--chromium/components/offline_pages/content/background_loader/DEPS1
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents.cc122
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents.h15
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents_stub.cc27
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents_stub.h31
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc149
-rw-r--r--chromium/components/offline_pages/core/BUILD.gn84
-rw-r--r--chromium/components/offline_pages/core/DEPS (renamed from chromium/components/offline_pages/DEPS)0
-rw-r--r--chromium/components/offline_pages/core/archive_manager.cc (renamed from chromium/components/offline_pages/archive_manager.cc)2
-rw-r--r--chromium/components/offline_pages/core/archive_manager.h (renamed from chromium/components/offline_pages/archive_manager.h)6
-rw-r--r--chromium/components/offline_pages/core/archive_manager_unittest.cc (renamed from chromium/components/offline_pages/archive_manager_unittest.cc)10
-rw-r--r--chromium/components/offline_pages/core/background/BUILD.gn (renamed from chromium/components/offline_pages/background/BUILD.gn)52
-rw-r--r--chromium/components/offline_pages/core/background/DEPS (renamed from chromium/components/offline_pages/background/DEPS)0
-rw-r--r--chromium/components/offline_pages/core/background/add_request_task.cc36
-rw-r--r--chromium/components/offline_pages/core/background/add_request_task.h46
-rw-r--r--chromium/components/offline_pages/core/background/add_request_task_unittest.cc196
-rw-r--r--chromium/components/offline_pages/core/background/change_requests_state_task.cc77
-rw-r--r--chromium/components/offline_pages/core/background/change_requests_state_task.h (renamed from chromium/components/offline_pages/background/change_requests_state_task.h)24
-rw-r--r--chromium/components/offline_pages/core/background/change_requests_state_task_unittest.cc (renamed from chromium/components/offline_pages/background/change_requests_state_task_unittest.cc)83
-rw-r--r--chromium/components/offline_pages/core/background/cleanup_task.cc104
-rw-r--r--chromium/components/offline_pages/core/background/cleanup_task.h61
-rw-r--r--chromium/components/offline_pages/core/background/cleanup_task_factory.cc35
-rw-r--r--chromium/components/offline_pages/core/background/cleanup_task_factory.h42
-rw-r--r--chromium/components/offline_pages/core/background/cleanup_task_unittest.cc279
-rw-r--r--chromium/components/offline_pages/core/background/connection_notifier.cc (renamed from chromium/components/offline_pages/background/connection_notifier.cc)2
-rw-r--r--chromium/components/offline_pages/core/background/connection_notifier.h (renamed from chromium/components/offline_pages/background/connection_notifier.h)6
-rw-r--r--chromium/components/offline_pages/core/background/device_conditions.h (renamed from chromium/components/offline_pages/background/device_conditions.h)9
-rw-r--r--chromium/components/offline_pages/core/background/get_requests_task.cc36
-rw-r--r--chromium/components/offline_pages/core/background/get_requests_task.h45
-rw-r--r--chromium/components/offline_pages/core/background/get_requests_task_unittest.cc138
-rw-r--r--chromium/components/offline_pages/core/background/initialize_store_task.cc61
-rw-r--r--chromium/components/offline_pages/core/background/initialize_store_task.h58
-rw-r--r--chromium/components/offline_pages/core/background/initialize_store_task_unittest.cc107
-rw-r--r--chromium/components/offline_pages/core/background/mark_attempt_aborted_task.cc (renamed from chromium/components/offline_pages/background/mark_attempt_aborted_task.cc)2
-rw-r--r--chromium/components/offline_pages/core/background/mark_attempt_aborted_task.h (renamed from chromium/components/offline_pages/background/mark_attempt_aborted_task.h)8
-rw-r--r--chromium/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc (renamed from chromium/components/offline_pages/background/mark_attempt_aborted_task_unittest.cc)40
-rw-r--r--chromium/components/offline_pages/core/background/mark_attempt_completed_task.cc (renamed from chromium/components/offline_pages/background/mark_attempt_completed_task.cc)2
-rw-r--r--chromium/components/offline_pages/core/background/mark_attempt_completed_task.h (renamed from chromium/components/offline_pages/background/mark_attempt_completed_task.h)10
-rw-r--r--chromium/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc (renamed from chromium/components/offline_pages/background/mark_attempt_completed_task_unittest.cc)32
-rw-r--r--chromium/components/offline_pages/core/background/mark_attempt_started_task.cc (renamed from chromium/components/offline_pages/background/mark_attempt_started_task.cc)2
-rw-r--r--chromium/components/offline_pages/core/background/mark_attempt_started_task.h (renamed from chromium/components/offline_pages/background/mark_attempt_started_task.h)8
-rw-r--r--chromium/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc (renamed from chromium/components/offline_pages/background/mark_attempt_started_task_unittest.cc)34
-rw-r--r--chromium/components/offline_pages/core/background/network_quality_provider_stub.cc43
-rw-r--r--chromium/components/offline_pages/core/background/network_quality_provider_stub.h48
-rw-r--r--chromium/components/offline_pages/core/background/offliner.h (renamed from chromium/components/offline_pages/background/offliner.h)36
-rw-r--r--chromium/components/offline_pages/core/background/offliner_policy.h (renamed from chromium/components/offline_pages/background/offliner_policy.h)11
-rw-r--r--chromium/components/offline_pages/core/background/offliner_policy_utils.cc34
-rw-r--r--chromium/components/offline_pages/core/background/offliner_policy_utils.h29
-rw-r--r--chromium/components/offline_pages/core/background/offliner_stub.cc38
-rw-r--r--chromium/components/offline_pages/core/background/offliner_stub.h39
-rw-r--r--chromium/components/offline_pages/core/background/pick_request_task.cc (renamed from chromium/components/offline_pages/background/pick_request_task.cc)143
-rw-r--r--chromium/components/offline_pages/core/background/pick_request_task.h (renamed from chromium/components/offline_pages/background/pick_request_task.h)46
-rw-r--r--chromium/components/offline_pages/core/background/pick_request_task_unittest.cc (renamed from chromium/components/offline_pages/background/pick_request_task_unittest.cc)99
-rw-r--r--chromium/components/offline_pages/core/background/reconcile_task.cc75
-rw-r--r--chromium/components/offline_pages/core/background/reconcile_task.h52
-rw-r--r--chromium/components/offline_pages/core/background/reconcile_task_unittest.cc219
-rw-r--r--chromium/components/offline_pages/core/background/remove_requests_task.cc (renamed from chromium/components/offline_pages/background/remove_requests_task.cc)2
-rw-r--r--chromium/components/offline_pages/core/background/remove_requests_task.h (renamed from chromium/components/offline_pages/background/remove_requests_task.h)10
-rw-r--r--chromium/components/offline_pages/core/background/remove_requests_task_unittest.cc (renamed from chromium/components/offline_pages/background/remove_requests_task_unittest.cc)39
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator.cc (renamed from chromium/components/offline_pages/background/request_coordinator.cc)180
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator.h (renamed from chromium/components/offline_pages/background/request_coordinator.h)112
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator_event_logger.cc (renamed from chromium/components/offline_pages/background/request_coordinator_event_logger.cc)32
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator_event_logger.h (renamed from chromium/components/offline_pages/background/request_coordinator_event_logger.h)14
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator_event_logger_unittest.cc (renamed from chromium/components/offline_pages/background/request_coordinator_event_logger_unittest.cc)2
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator_unittest.cc (renamed from chromium/components/offline_pages/background/request_coordinator_unittest.cc)630
-rw-r--r--chromium/components/offline_pages/core/background/request_notifier.h (renamed from chromium/components/offline_pages/background/request_notifier.h)10
-rw-r--r--chromium/components/offline_pages/core/background/request_queue.cc (renamed from chromium/components/offline_pages/background/request_queue.cc)69
-rw-r--r--chromium/components/offline_pages/core/background/request_queue.h (renamed from chromium/components/offline_pages/background/request_queue.h)55
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_in_memory_store.cc (renamed from chromium/components/offline_pages/background/request_queue_in_memory_store.cc)38
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_in_memory_store.h (renamed from chromium/components/offline_pages/background/request_queue_in_memory_store.h)21
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_results.h (renamed from chromium/components/offline_pages/background/request_queue_results.h)4
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_store.h (renamed from chromium/components/offline_pages/background/request_queue_store.h)20
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_store_sql.cc (renamed from chromium/components/offline_pages/background/request_queue_store_sql.cc)114
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_store_sql.h (renamed from chromium/components/offline_pages/background/request_queue_store_sql.h)22
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_store_unittest.cc (renamed from chromium/components/offline_pages/background/request_queue_store_unittest.cc)99
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_unittest.cc (renamed from chromium/components/offline_pages/background/request_queue_unittest.cc)53
-rw-r--r--chromium/components/offline_pages/core/background/save_page_request.cc (renamed from chromium/components/offline_pages/background/save_page_request.cc)5
-rw-r--r--chromium/components/offline_pages/core/background/save_page_request.h (renamed from chromium/components/offline_pages/background/save_page_request.h)16
-rw-r--r--chromium/components/offline_pages/core/background/save_page_request_unittest.cc (renamed from chromium/components/offline_pages/background/save_page_request_unittest.cc)12
-rw-r--r--chromium/components/offline_pages/core/background/scheduler.h (renamed from chromium/components/offline_pages/background/scheduler.h)11
-rw-r--r--chromium/components/offline_pages/core/background/scheduler_stub.cc48
-rw-r--r--chromium/components/offline_pages/core/background/scheduler_stub.h52
-rw-r--r--chromium/components/offline_pages/core/background/update_request_task.cc (renamed from chromium/components/offline_pages/background/update_request_task.cc)2
-rw-r--r--chromium/components/offline_pages/core/background/update_request_task.h (renamed from chromium/components/offline_pages/background/update_request_task.h)8
-rw-r--r--chromium/components/offline_pages/core/client_namespace_constants.cc (renamed from chromium/components/offline_pages/client_namespace_constants.cc)2
-rw-r--r--chromium/components/offline_pages/core/client_namespace_constants.h (renamed from chromium/components/offline_pages/client_namespace_constants.h)6
-rw-r--r--chromium/components/offline_pages/core/client_policy_controller.cc (renamed from chromium/components/offline_pages/client_policy_controller.cc)4
-rw-r--r--chromium/components/offline_pages/core/client_policy_controller.h (renamed from chromium/components/offline_pages/client_policy_controller.h)8
-rw-r--r--chromium/components/offline_pages/core/client_policy_controller_unittest.cc (renamed from chromium/components/offline_pages/client_policy_controller_unittest.cc)4
-rw-r--r--chromium/components/offline_pages/core/downloads/BUILD.gn (renamed from chromium/components/offline_pages/downloads/BUILD.gn)10
-rw-r--r--chromium/components/offline_pages/core/downloads/download_notifying_observer.cc (renamed from chromium/components/offline_pages/downloads/download_notifying_observer.cc)12
-rw-r--r--chromium/components/offline_pages/core/downloads/download_notifying_observer.h (renamed from chromium/components/offline_pages/downloads/download_notifying_observer.h)10
-rw-r--r--chromium/components/offline_pages/core/downloads/download_notifying_observer_unittest.cc (renamed from chromium/components/offline_pages/downloads/download_notifying_observer_unittest.cc)12
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_adapter.cc (renamed from chromium/components/offline_pages/downloads/download_ui_adapter.cc)77
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_adapter.h (renamed from chromium/components/offline_pages/downloads/download_ui_adapter.h)20
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc (renamed from chromium/components/offline_pages/downloads/download_ui_adapter_unittest.cc)83
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_item.cc (renamed from chromium/components/offline_pages/downloads/download_ui_item.cc)13
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_item.h (renamed from chromium/components/offline_pages/downloads/download_ui_item.h)6
-rw-r--r--chromium/components/offline_pages/core/downloads/offline_page_download_notifier.h (renamed from chromium/components/offline_pages/downloads/offline_page_download_notifier.h)8
-rw-r--r--chromium/components/offline_pages/core/offline_event_logger.cc (renamed from chromium/components/offline_pages/offline_event_logger.cc)45
-rw-r--r--chromium/components/offline_pages/core/offline_event_logger.h (renamed from chromium/components/offline_pages/offline_event_logger.h)28
-rw-r--r--chromium/components/offline_pages/core/offline_event_logger_unittest.cc53
-rw-r--r--chromium/components/offline_pages/core/offline_page_archiver.h (renamed from chromium/components/offline_pages/offline_page_archiver.h)20
-rw-r--r--chromium/components/offline_pages/core/offline_page_client_policy.h (renamed from chromium/components/offline_pages/offline_page_client_policy.h)14
-rw-r--r--chromium/components/offline_pages/core/offline_page_feature.cc (renamed from chromium/components/offline_pages/offline_page_feature.cc)30
-rw-r--r--chromium/components/offline_pages/core/offline_page_feature.h (renamed from chromium/components/offline_pages/offline_page_feature.h)9
-rw-r--r--chromium/components/offline_pages/core/offline_page_item.cc (renamed from chromium/components/offline_pages/offline_page_item.cc)16
-rw-r--r--chromium/components/offline_pages/core/offline_page_item.h (renamed from chromium/components/offline_pages/offline_page_item.h)11
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store.cc (renamed from chromium/components/offline_pages/offline_page_metadata_store.cc)5
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store.h (renamed from chromium/components/offline_pages/offline_page_metadata_store.h)17
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store_impl_unittest.cc (renamed from chromium/components/offline_pages/offline_page_metadata_store_impl_unittest.cc)116
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc (renamed from chromium/components/offline_pages/offline_page_metadata_store_sql.cc)82
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store_sql.h (renamed from chromium/components/offline_pages/offline_page_metadata_store_sql.h)12
-rw-r--r--chromium/components/offline_pages/core/offline_page_model.cc (renamed from chromium/components/offline_pages/offline_page_model.cc)9
-rw-r--r--chromium/components/offline_pages/core/offline_page_model.h (renamed from chromium/components/offline_pages/offline_page_model.h)39
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_event_logger.cc (renamed from chromium/components/offline_pages/offline_page_model_event_logger.cc)2
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_event_logger.h (renamed from chromium/components/offline_pages/offline_page_model_event_logger.h)8
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_event_logger_unittest.cc (renamed from chromium/components/offline_pages/offline_page_model_event_logger_unittest.cc)4
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_impl.cc (renamed from chromium/components/offline_pages/offline_page_model_impl.cc)276
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_impl.h (renamed from chromium/components/offline_pages/offline_page_model_impl.h)61
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_impl_unittest.cc (renamed from chromium/components/offline_pages/offline_page_model_impl_unittest.cc)159
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_query.cc (renamed from chromium/components/offline_pages/offline_page_model_query.cc)16
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_query.h (renamed from chromium/components/offline_pages/offline_page_model_query.h)24
-rw-r--r--chromium/components/offline_pages/core/offline_page_model_query_unittest.cc (renamed from chromium/components/offline_pages/offline_page_model_query_unittest.cc)24
-rw-r--r--chromium/components/offline_pages/core/offline_page_storage_manager.cc (renamed from chromium/components/offline_pages/offline_page_storage_manager.cc)77
-rw-r--r--chromium/components/offline_pages/core/offline_page_storage_manager.h (renamed from chromium/components/offline_pages/offline_page_storage_manager.h)63
-rw-r--r--chromium/components/offline_pages/core/offline_page_storage_manager_unittest.cc (renamed from chromium/components/offline_pages/offline_page_storage_manager_unittest.cc)118
-rw-r--r--chromium/components/offline_pages/core/offline_page_test_archiver.cc (renamed from chromium/components/offline_pages/offline_page_test_archiver.cc)5
-rw-r--r--chromium/components/offline_pages/core/offline_page_test_archiver.h (renamed from chromium/components/offline_pages/offline_page_test_archiver.h)17
-rw-r--r--chromium/components/offline_pages/core/offline_page_test_store.cc (renamed from chromium/components/offline_pages/offline_page_test_store.cc)2
-rw-r--r--chromium/components/offline_pages/core/offline_page_test_store.h (renamed from chromium/components/offline_pages/offline_page_test_store.h)10
-rw-r--r--chromium/components/offline_pages/core/offline_page_types.h (renamed from chromium/components/offline_pages/offline_page_types.h)8
-rw-r--r--chromium/components/offline_pages/core/offline_store_types.h (renamed from chromium/components/offline_pages/offline_store_types.h)9
-rw-r--r--chromium/components/offline_pages/core/request_header/BUILD.gn (renamed from chromium/components/offline_pages/request_header/BUILD.gn)0
-rw-r--r--chromium/components/offline_pages/core/request_header/offline_page_header.cc (renamed from chromium/components/offline_pages/request_header/offline_page_header.cc)9
-rw-r--r--chromium/components/offline_pages/core/request_header/offline_page_header.h (renamed from chromium/components/offline_pages/request_header/offline_page_header.h)13
-rw-r--r--chromium/components/offline_pages/core/request_header/offline_page_header_unittest.cc (renamed from chromium/components/offline_pages/request_header/offline_page_header_unittest.cc)4
-rw-r--r--chromium/components/offline_pages/core/snapshot_controller.cc (renamed from chromium/components/offline_pages/snapshot_controller.cc)12
-rw-r--r--chromium/components/offline_pages/core/snapshot_controller.h (renamed from chromium/components/offline_pages/snapshot_controller.h)8
-rw-r--r--chromium/components/offline_pages/core/snapshot_controller_unittest.cc (renamed from chromium/components/offline_pages/snapshot_controller_unittest.cc)13
-rw-r--r--chromium/components/offline_pages/core/stub_offline_page_model.cc (renamed from chromium/components/offline_pages/stub_offline_page_model.cc)8
-rw-r--r--chromium/components/offline_pages/core/stub_offline_page_model.h (renamed from chromium/components/offline_pages/stub_offline_page_model.h)15
-rw-r--r--chromium/components/offline_pages/core/task.h6
-rw-r--r--chromium/components/offline_pages/core/task_queue.cc6
-rw-r--r--chromium/components/offline_pages/core/task_queue.h2
-rw-r--r--chromium/components/omnibox/browser/BUILD.gn6
-rw-r--r--chromium/components/onc/docs/onc_spec.html11
-rw-r--r--chromium/components/os_crypt/keychain_password_mac.h4
-rw-r--r--chromium/components/os_crypt/keychain_password_mac.mm27
-rw-r--r--chromium/components/os_crypt/libsecret_util_linux.cc2
-rw-r--r--chromium/components/ownership/owner_key_util.h2
-rw-r--r--chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.h4
-rw-r--r--chromium/components/pageinfo_strings.grdp53
-rw-r--r--chromium/components/pairing/controller_pairing_controller.h8
-rw-r--r--chromium/components/password_manager/OWNERS1
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver.cc25
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver.h9
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver_factory.h5
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver_unittest.cc23
-rw-r--r--chromium/components/password_manager/content/browser/credential_manager_impl.cc106
-rw-r--r--chromium/components/password_manager/content/browser/credential_manager_impl.h20
-rw-r--r--chromium/components/password_manager/content/browser/credential_manager_impl_unittest.cc217
-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.cc1
-rw-r--r--chromium/components/password_manager/core/browser/BUILD.gn14
-rw-r--r--chromium/components/password_manager/core/browser/DEPS1
-rw-r--r--chromium/components/password_manager/core/browser/affiliated_match_helper.h4
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_backend.cc38
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_backend.h4
-rw-r--r--chromium/components/password_manager/core/browser/affiliation_utils.h18
-rw-r--r--chromium/components/password_manager/core/browser/browser_save_password_progress_logger.cc22
-rw-r--r--chromium/components/password_manager/core/browser/browser_save_password_progress_logger.h11
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_password_form_manager.cc46
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_password_form_manager.h24
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_password_form_manager_unittest.cc77
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_pending_request_task.cc172
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_pending_request_task.h25
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.cc41
-rw-r--r--chromium/components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.h30
-rw-r--r--chromium/components/password_manager/core/browser/fake_form_fetcher.cc49
-rw-r--r--chromium/components/password_manager/core/browser/fake_form_fetcher.h75
-rw-r--r--chromium/components/password_manager/core/browser/form_fetcher.h8
-rw-r--r--chromium/components/password_manager/core/browser/form_fetcher_impl.cc132
-rw-r--r--chromium/components/password_manager/core/browser/form_fetcher_impl.h62
-rw-r--r--chromium/components/password_manager/core/browser/form_fetcher_impl_unittest.cc304
-rw-r--r--chromium/components/password_manager/core/browser/http_password_migrator.cc66
-rw-r--r--chromium/components/password_manager/core/browser/http_password_migrator.h58
-rw-r--r--chromium/components/password_manager/core/browser/http_password_migrator_unittest.cc130
-rw-r--r--chromium/components/password_manager/core/browser/login_database.cc83
-rw-r--r--chromium/components/password_manager/core/browser/login_database.h22
-rw-r--r--chromium/components/password_manager/core/browser/login_database_ios_unittest.cc6
-rw-r--r--chromium/components/password_manager/core/browser/login_database_unittest.cc60
-rw-r--r--chromium/components/password_manager/core/browser/mock_password_store.cc12
-rw-r--r--chromium/components/password_manager/core/browser/mock_password_store.h10
-rw-r--r--chromium/components/password_manager/core/browser/password_autofill_manager.cc117
-rw-r--r--chromium/components/password_manager/core/browser/password_autofill_manager.h11
-rw-r--r--chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc276
-rw-r--r--chromium/components/password_manager/core/browser/password_bubble_experiment.cc41
-rw-r--r--chromium/components/password_manager/core/browser/password_bubble_experiment.h32
-rw-r--r--chromium/components/password_manager/core/browser/password_bubble_experiment_unittest.cc246
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager.cc468
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager.h133
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager_unittest.cc1182
-rw-r--r--chromium/components/password_manager/core/browser/password_generation_manager.cc10
-rw-r--r--chromium/components/password_manager/core/browser/password_generation_manager_unittest.cc4
-rw-r--r--chromium/components/password_manager/core/browser/password_manager.cc82
-rw-r--r--chromium/components/password_manager/core/browser/password_manager.h24
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_client.cc4
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_client.h4
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_driver.h2
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_util.cc40
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_util.h26
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_test_utils.cc4
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_test_utils.h9
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_unittest.cc159
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_util.cc33
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_util.h10
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detection_manager.cc65
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detection_manager.h44
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc90
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector.cc119
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector.h82
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector_consumer.cc13
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector_consumer.h34
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc189
-rw-r--r--chromium/components/password_manager/core/browser/password_store.cc72
-rw-r--r--chromium/components/password_manager/core/browser/password_store.h60
-rw-r--r--chromium/components/password_manager/core/browser/password_store_consumer.cc2
-rw-r--r--chromium/components/password_manager/core/browser/password_store_consumer.h3
-rw-r--r--chromium/components/password_manager/core/browser/password_store_default.cc24
-rw-r--r--chromium/components/password_manager/core/browser/password_store_default.h2
-rw-r--r--chromium/components/password_manager/core/browser/password_store_default_unittest.cc1
-rw-r--r--chromium/components/password_manager/core/browser/password_store_sync.h1
-rw-r--r--chromium/components/password_manager/core/browser/password_store_unittest.cc67
-rw-r--r--chromium/components/password_manager/core/browser/psl_matching_helper.cc71
-rw-r--r--chromium/components/password_manager/core/browser/psl_matching_helper.h21
-rw-r--r--chromium/components/password_manager/core/browser/psl_matching_helper_unittest.cc185
-rw-r--r--chromium/components/password_manager/core/browser/statistics_table.cc25
-rw-r--r--chromium/components/password_manager/core/browser/statistics_table.h5
-rw-r--r--chromium/components/password_manager/core/browser/statistics_table_unittest.cc32
-rw-r--r--chromium/components/password_manager/core/browser/test_password_store.cc16
-rw-r--r--chromium/components/password_manager/core/browser/test_password_store.h2
-rw-r--r--chromium/components/password_manager/core/common/password_manager_pref_names.cc3
-rw-r--r--chromium/components/password_manager/core/common/password_manager_pref_names.h4
-rw-r--r--chromium/components/password_manager/core/common/password_manager_ui.h4
-rw-r--r--chromium/components/password_manager/sync/browser/BUILD.gn1
-rw-r--r--chromium/components/password_manager/sync/browser/password_data_type_controller.cc12
-rw-r--r--chromium/components/password_manager/sync/browser/password_data_type_controller.h13
-rw-r--r--chromium/components/password_manager/sync/browser/password_manager_setting_migrator_service_unittest.cc1
-rw-r--r--chromium/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc91
-rw-r--r--chromium/components/password_manager/sync/browser/sync_username_test_base.h4
-rw-r--r--chromium/components/payments/BUILD.gn36
-rw-r--r--chromium/components/payments/DEPS5
-rw-r--r--chromium/components/payments/android/BUILD.gn4
-rw-r--r--chromium/components/payments/android/currency_formatter_android.cc76
-rw-r--r--chromium/components/payments/android/currency_formatter_android.h51
-rw-r--r--chromium/components/payments/currency_formatter.cc136
-rw-r--r--chromium/components/payments/currency_formatter.h59
-rw-r--r--chromium/components/payments/currency_formatter_unittest.cc178
-rw-r--r--chromium/components/payments/payment_app.mojom22
-rw-r--r--chromium/components/payments/payment_details_validation.cc22
-rw-r--r--chromium/components/payments/payment_request.cc110
-rw-r--r--chromium/components/payments/payment_request.h93
-rw-r--r--chromium/components/payments/payment_request.mojom93
-rw-r--r--chromium/components/payments/payment_request_delegate.h29
-rw-r--r--chromium/components/payments/payment_request_web_contents_manager.cc45
-rw-r--r--chromium/components/payments/payment_request_web_contents_manager.h61
-rw-r--r--chromium/components/pdf/renderer/pdf_accessibility_tree.h1
-rw-r--r--chromium/components/pdf/renderer/pepper_pdf_host.cc4
-rw-r--r--chromium/components/pdf/renderer/pepper_pdf_host.h7
-rw-r--r--chromium/components/physical_web/data_source/BUILD.gn15
-rw-r--r--chromium/components/physical_web/data_source/fake_physical_web_data_source.cc123
-rw-r--r--chromium/components/physical_web/data_source/fake_physical_web_data_source.h70
-rw-r--r--chromium/components/physical_web/data_source/physical_web_data_source.cc26
-rw-r--r--chromium/components/physical_web/data_source/physical_web_data_source.h81
-rw-r--r--chromium/components/physical_web/data_source/physical_web_data_source_impl.cc10
-rw-r--r--chromium/components/physical_web/data_source/physical_web_data_source_impl.h12
-rw-r--r--chromium/components/physical_web/data_source/physical_web_data_source_impl_unittest.cc27
-rw-r--r--chromium/components/physical_web/data_source/physical_web_listener.h13
-rw-r--r--chromium/components/physical_web/webui/BUILD.gn3
-rw-r--r--chromium/components/physical_web/webui/physical_web_base_message_handler.cc82
-rw-r--r--chromium/components/physical_web/webui/physical_web_base_message_handler.h58
-rw-r--r--chromium/components/physical_web/webui/physical_web_ui_constants.cc2
-rw-r--r--chromium/components/physical_web/webui/physical_web_ui_constants.h2
-rw-r--r--chromium/components/physical_web/webui/resources/physical_web.css1
-rw-r--r--chromium/components/physical_web/webui/resources/physical_web.html6
-rw-r--r--chromium/components/plugins/renderer/BUILD.gn1
-rw-r--r--chromium/components/plugins/renderer/loadable_plugin_placeholder.cc6
-rw-r--r--chromium/components/plugins/renderer/webview_plugin.cc135
-rw-r--r--chromium/components/plugins/renderer/webview_plugin.h81
-rw-r--r--chromium/components/policy/BUILD.gn575
-rw-r--r--chromium/components/policy/android/BUILD.gn1
-rw-r--r--chromium/components/policy/core/browser/BUILD.gn162
-rw-r--r--chromium/components/policy/core/common/BUILD.gn610
-rw-r--r--chromium/components/policy/resources/policy_templates.gni58
-rw-r--r--chromium/components/policy_strings.grdp3
-rw-r--r--chromium/components/power/BUILD.gn32
-rw-r--r--chromium/components/power/DEPS4
-rw-r--r--chromium/components/power/OWNERS3
-rw-r--r--chromium/components/power/origin_power_map.cc87
-rw-r--r--chromium/components/power/origin_power_map.h69
-rw-r--r--chromium/components/power/origin_power_map_factory.cc38
-rw-r--r--chromium/components/power/origin_power_map_factory.h36
-rw-r--r--chromium/components/power/origin_power_map_unittest.cc133
-rw-r--r--chromium/components/precache/android/BUILD.gn1
-rw-r--r--chromium/components/precache/content/BUILD.gn2
-rw-r--r--chromium/components/precache/content/precache_manager.cc9
-rw-r--r--chromium/components/precache/content/precache_manager_unittest.cc12
-rw-r--r--chromium/components/precache/core/precache_fetcher.cc40
-rw-r--r--chromium/components/precache/core/precache_fetcher.h7
-rw-r--r--chromium/components/precache/core/precache_fetcher_unittest.cc38
-rw-r--r--chromium/components/precache/core/proto/precache.proto14
-rw-r--r--chromium/components/pref_registry/BUILD.gn17
-rw-r--r--chromium/components/pref_registry/pref_registry_syncable.h3
-rw-r--r--chromium/components/pref_registry/testing_pref_service_syncable.cc70
-rw-r--r--chromium/components/pref_registry/testing_pref_service_syncable.h38
-rw-r--r--chromium/components/prefs/BUILD.gn16
-rw-r--r--chromium/components/prefs/json_pref_store.cc2
-rw-r--r--chromium/components/prefs/overlay_user_pref_store_unittest.cc6
-rw-r--r--chromium/components/prefs/pref_filter.h1
-rw-r--r--chromium/components/prefs/pref_member.cc2
-rw-r--r--chromium/components/prefs/pref_registry.cc4
-rw-r--r--chromium/components/prefs/pref_service.cc11
-rw-r--r--chromium/components/prefs/pref_service.h4
-rw-r--r--chromium/components/prefs/pref_service_unittest.cc16
-rw-r--r--chromium/components/prefs/pref_value_map.cc32
-rw-r--r--chromium/components/prefs/pref_value_map.h4
-rw-r--r--chromium/components/prefs/pref_value_store_unittest.cc32
-rw-r--r--chromium/components/prefs/scoped_user_pref_update.h4
-rw-r--r--chromium/components/prefs/testing_pref_service.cc5
-rw-r--r--chromium/components/prefs/testing_pref_service.h41
-rw-r--r--chromium/components/previews/core/previews_black_list.h4
-rw-r--r--chromium/components/previews/core/previews_experiments.cc45
-rw-r--r--chromium/components/previews/core/previews_io_data.cc12
-rw-r--r--chromium/components/previews/core/previews_io_data_unittest.cc12
-rw-r--r--chromium/components/previews/core/previews_opt_out_store.h2
-rw-r--r--chromium/components/printing/browser/print_manager_utils.cc1
-rw-r--r--chromium/components/printing/common/print_messages.cc42
-rw-r--r--chromium/components/printing/common/print_messages.h12
-rw-r--r--chromium/components/printing/renderer/print_web_view_helper.cc23
-rw-r--r--chromium/components/printing/renderer/print_web_view_helper.h4
-rw-r--r--chromium/components/proximity_auth/BUILD.gn33
-rw-r--r--chromium/components/proximity_auth/ble/BUILD.gn25
-rw-r--r--chromium/components/proximity_auth/webui/BUILD.gn2
-rw-r--r--chromium/components/rappor/BUILD.gn8
-rw-r--r--chromium/components/rappor/byte_vector_utils.h2
-rw-r--r--chromium/components/rappor/log_uploader.h6
-rw-r--r--chromium/components/rappor/public/BUILD.gn (renamed from chromium/components/task_scheduler_util/BUILD.gn)8
-rw-r--r--chromium/components/rappor/public/rappor_parameters.h (renamed from chromium/components/rappor/rappor_parameters.h)118
-rw-r--r--chromium/components/rappor/public/rappor_service.h44
-rw-r--r--chromium/components/rappor/public/rappor_utils.h (renamed from chromium/components/rappor/rappor_utils.h)17
-rw-r--r--chromium/components/rappor/public/sample.h (renamed from chromium/components/rappor/sample.h)14
-rw-r--r--chromium/components/rappor/rappor_metric.cc2
-rw-r--r--chromium/components/rappor/rappor_metric.h4
-rw-r--r--chromium/components/rappor/rappor_parameters.cc2
-rw-r--r--chromium/components/rappor/rappor_prefs.cc2
-rw-r--r--chromium/components/rappor/rappor_recorder_impl.cc9
-rw-r--r--chromium/components/rappor/rappor_recorder_impl.h8
-rw-r--r--chromium/components/rappor/rappor_service_impl.cc (renamed from chromium/components/rappor/rappor_service.cc)90
-rw-r--r--chromium/components/rappor/rappor_service_impl.h (renamed from chromium/components/rappor/rappor_service.h)49
-rw-r--r--chromium/components/rappor/rappor_service_unittest.cc54
-rw-r--r--chromium/components/rappor/rappor_utils.cc22
-rw-r--r--chromium/components/rappor/rappor_utils_unittest.cc2
-rw-r--r--chromium/components/rappor/reports.cc2
-rw-r--r--chromium/components/rappor/reports.h2
-rw-r--r--chromium/components/rappor/sample.cc2
-rw-r--r--chromium/components/rappor/sampler.cc2
-rw-r--r--chromium/components/rappor/sampler.h8
-rw-r--r--chromium/components/rappor/test_rappor_service.cc46
-rw-r--r--chromium/components/rappor/test_rappor_service.h25
-rw-r--r--chromium/components/reading_list/DEPS6
-rw-r--r--chromium/components/reading_list/OWNERS2
-rw-r--r--chromium/components/reading_list/core/BUILD.gn23
-rw-r--r--chromium/components/reading_list/core/reading_list.gni9
-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/ios/BUILD.gn58
-rw-r--r--chromium/components/reading_list/ios/favicon_web_state_dispatcher.h33
-rw-r--r--chromium/components/reading_list/ios/offline_url_utils.cc48
-rw-r--r--chromium/components/reading_list/ios/offline_url_utils.h52
-rw-r--r--chromium/components/reading_list/ios/offline_url_utils_unittest.cc61
-rw-r--r--chromium/components/reading_list/ios/proto/BUILD.gn14
-rw-r--r--chromium/components/reading_list/ios/proto/reading_list.proto45
-rw-r--r--chromium/components/reading_list/ios/reading_list_entry.cc551
-rw-r--r--chromium/components/reading_list/ios/reading_list_entry.h172
-rw-r--r--chromium/components/reading_list/ios/reading_list_entry_unittest.cc363
-rw-r--r--chromium/components/reading_list/ios/reading_list_model.cc81
-rw-r--r--chromium/components/reading_list/ios/reading_list_model.h167
-rw-r--r--chromium/components/reading_list/ios/reading_list_model_bridge_observer.h81
-rw-r--r--chromium/components/reading_list/ios/reading_list_model_bridge_observer.mm104
-rw-r--r--chromium/components/reading_list/ios/reading_list_model_impl.cc505
-rw-r--r--chromium/components/reading_list/ios/reading_list_model_impl.h140
-rw-r--r--chromium/components/reading_list/ios/reading_list_model_observer.h83
-rw-r--r--chromium/components/reading_list/ios/reading_list_model_storage.cc12
-rw-r--r--chromium/components/reading_list/ios/reading_list_model_storage.h67
-rw-r--r--chromium/components/reading_list/ios/reading_list_model_unittest.mm686
-rw-r--r--chromium/components/reading_list/ios/reading_list_pref_names.cc15
-rw-r--r--chromium/components/reading_list/ios/reading_list_pref_names.h18
-rw-r--r--chromium/components/reading_list/ios/reading_list_store.cc461
-rw-r--r--chromium/components/reading_list/ios/reading_list_store.h169
-rw-r--r--chromium/components/reading_list/ios/reading_list_store_delegate.h45
-rw-r--r--chromium/components/reading_list/ios/reading_list_store_unittest.mm440
-rw-r--r--chromium/components/renderer_context_menu/BUILD.gn1
-rw-r--r--chromium/components/renderer_context_menu/DEPS2
-rw-r--r--chromium/components/renderer_context_menu/render_view_context_menu_base.cc3
-rw-r--r--chromium/components/renderer_context_menu/render_view_context_menu_base.h12
-rw-r--r--chromium/components/resources/OWNERS13
-rw-r--r--chromium/components/resources/autofill_scaled_resources.grdp27
-rw-r--r--chromium/components/resources/components_scaled_resources.grd1
-rw-r--r--chromium/components/resources/content_suggestions_scaled_resources.grdp4
-rw-r--r--chromium/components/resources/default_100_percent/autofill/amex.pngbin622 -> 1213 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/diners.pngbin0 -> 1079 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/discover.pngbin755 -> 652 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/jcb.pngbin0 -> 781 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/mastercard.pngbin595 -> 864 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/mir.pngbin0 -> 496 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/pr_amex.pngbin0 -> 1187 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/pr_dinersclub.pngbin0 -> 1394 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/pr_discover.pngbin0 -> 883 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/pr_generic.pngbin0 -> 202 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/pr_jcb.pngbin0 -> 2146 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/pr_mc.pngbin0 -> 1425 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/pr_mir.pngbin0 -> 976 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/pr_unionpay.pngbin0 -> 2269 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/pr_visa.pngbin0 -> 1159 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/visa.pngbin633 -> 878 bytes
-rw-r--r--chromium/components/resources/default_100_percent/content_suggestions/physical_web_logo_with_padding.pngbin0 -> 2900 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/amex.pngbin1580 -> 2851 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/diners.pngbin0 -> 2765 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/discover.pngbin1270 -> 1477 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/jcb.pngbin0 -> 1727 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/mastercard.pngbin1440 -> 1872 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/mir.pngbin0 -> 1225 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/pr_amex.pngbin0 -> 3344 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/pr_dinersclub.pngbin0 -> 3074 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/pr_discover.pngbin0 -> 2228 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/pr_generic.pngbin0 -> 334 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/pr_jcb.pngbin0 -> 4371 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/pr_mc.pngbin0 -> 3303 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/pr_mir.pngbin0 -> 2877 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/pr_unionpay.pngbin0 -> 7284 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/pr_visa.pngbin0 -> 2454 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/visa.pngbin1356 -> 1834 bytes
-rw-r--r--chromium/components/resources/default_200_percent/content_suggestions/physical_web_logo_with_padding.pngbin0 -> 4245 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/pr_amex.pngbin0 -> 9023 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/pr_dinersclub.pngbin0 -> 6742 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/pr_discover.pngbin0 -> 4991 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/pr_generic.pngbin0 -> 585 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/pr_jcb.pngbin0 -> 12846 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/pr_mc.pngbin0 -> 7067 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/pr_mir.pngbin0 -> 8597 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/pr_unionpay.pngbin0 -> 23032 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/pr_visa.pngbin0 -> 5699 bytes
-rw-r--r--chromium/components/resources/default_300_percent/content_suggestions/physical_web_logo_with_padding.pngbin0 -> 6660 bytes
-rw-r--r--chromium/components/resources/ntp_tiles_resources.grdp3
-rw-r--r--chromium/components/safe_browsing/BUILD.gn27
-rw-r--r--chromium/components/safe_browsing/DEPS11
-rw-r--r--chromium/components/safe_browsing/OWNERS7
-rw-r--r--chromium/components/safe_browsing/base_blocking_page.cc327
-rw-r--r--chromium/components/safe_browsing/base_blocking_page.h140
-rw-r--r--chromium/components/safe_browsing/base_resource_throttle.cc455
-rw-r--r--chromium/components/safe_browsing/base_resource_throttle.h195
-rw-r--r--chromium/components/safe_browsing/base_ui_manager.cc351
-rw-r--r--chromium/components/safe_browsing/base_ui_manager.h147
-rw-r--r--chromium/components/safe_browsing/common/BUILD.gn21
-rw-r--r--chromium/components/safe_browsing/common/DEPS4
-rw-r--r--chromium/components/safe_browsing/common/OWNERS4
-rw-r--r--chromium/components/safe_browsing/common/safebrowsing_constants.cc12
-rw-r--r--chromium/components/safe_browsing/common/safebrowsing_constants.h16
-rw-r--r--chromium/components/safe_browsing/common/safebrowsing_message_generator.cc39
-rw-r--r--chromium/components/safe_browsing/common/safebrowsing_message_generator.h7
-rw-r--r--chromium/components/safe_browsing/common/safebrowsing_messages.h66
-rw-r--r--chromium/components/safe_browsing/common/safebrowsing_switches.cc37
-rw-r--r--chromium/components/safe_browsing/common/safebrowsing_switches.h19
-rw-r--r--chromium/components/safe_browsing_db/BUILD.gn17
-rw-r--r--chromium/components/safe_browsing_db/DEPS1
-rw-r--r--chromium/components/safe_browsing_db/android/BUILD.gn23
-rw-r--r--chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.cc196
-rw-r--r--chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.h48
-rw-r--r--chromium/components/safe_browsing_db/database_manager.h1
-rw-r--r--chromium/components/safe_browsing_db/safe_browsing_api_handler_util.cc2
-rw-r--r--chromium/components/safe_browsing_db/safe_browsing_prefs.cc142
-rw-r--r--chromium/components/safe_browsing_db/safe_browsing_prefs.h32
-rw-r--r--chromium/components/safe_browsing_db/safebrowsing.proto25
-rw-r--r--chromium/components/safe_browsing_db/test_database_manager.h6
-rw-r--r--chromium/components/safe_browsing_db/v4_database.cc16
-rw-r--r--chromium/components/safe_browsing_db/v4_database.h5
-rw-r--r--chromium/components/safe_browsing_db/v4_database_unittest.cc39
-rw-r--r--chromium/components/safe_browsing_db/v4_local_database_manager.cc19
-rw-r--r--chromium/components/safe_browsing_db/v4_local_database_manager.h13
-rw-r--r--chromium/components/safe_browsing_db/v4_local_database_manager_unittest.cc101
-rw-r--r--chromium/components/safe_browsing_db/v4_store.cc5
-rw-r--r--chromium/components/safe_browsing_db/v4_store_unittest.cc8
-rw-r--r--chromium/components/safe_browsing_db/v4_update_protocol_manager.cc43
-rw-r--r--chromium/components/safe_browsing_db/v4_update_protocol_manager.h16
-rw-r--r--chromium/components/safe_browsing_db/v4_update_protocol_manager_unittest.cc36
-rw-r--r--chromium/components/safe_json/json_sanitizer.cc3
-rw-r--r--chromium/components/safe_json/json_sanitizer_unittest.cc10
-rw-r--r--chromium/components/safe_json/public/interfaces/BUILD.gn4
-rw-r--r--chromium/components/safe_json/public/interfaces/safe_json.mojom5
-rw-r--r--chromium/components/safe_json/public/interfaces/safe_json.typemap9
-rw-r--r--chromium/components/safe_json/safe_json_parser_android.h2
-rw-r--r--chromium/components/safe_json/safe_json_parser_impl.cc19
-rw-r--r--chromium/components/safe_json/safe_json_parser_impl.h3
-rw-r--r--chromium/components/safe_json/testing_json_parser_unittest.cc2
-rw-r--r--chromium/components/safe_json/utility/safe_json_parser_mojo_impl.cc7
-rw-r--r--chromium/components/search/BUILD.gn2
-rw-r--r--chromium/components/search/search.cc65
-rw-r--r--chromium/components/search/search.h20
-rw-r--r--chromium/components/search/search_android_unittest.cc5
-rw-r--r--chromium/components/search/search_switches.cc21
-rw-r--r--chromium/components/search/search_switches.h19
-rw-r--r--chromium/components/search/search_unittest.cc8
-rw-r--r--chromium/components/search_engines/BUILD.gn13
-rw-r--r--chromium/components/search_engines/OWNERS2
-rw-r--r--chromium/components/search_engines/default_search_manager.cc154
-rw-r--r--chromium/components/search_engines/default_search_manager.h2
-rw-r--r--chromium/components/search_engines/default_search_manager_unittest.cc54
-rw-r--r--chromium/components/search_engines/default_search_policy_handler.cc163
-rw-r--r--chromium/components/search_engines/default_search_policy_handler.h22
-rw-r--r--chromium/components/search_engines/default_search_policy_handler_unittest.cc72
-rw-r--r--chromium/components/search_engines/default_search_pref_migration.cc170
-rw-r--r--chromium/components/search_engines/default_search_pref_migration.h17
-rw-r--r--chromium/components/search_engines/default_search_pref_migration_unittest.cc234
-rw-r--r--chromium/components/search_engines/default_search_pref_test_util.cc56
-rw-r--r--chromium/components/search_engines/default_search_pref_test_util.h60
-rw-r--r--chromium/components/search_engines/keyword_table.cc265
-rw-r--r--chromium/components/search_engines/keyword_table.h7
-rw-r--r--chromium/components/search_engines/keyword_table_unittest.cc8
-rw-r--r--chromium/components/search_engines/prepopulated_engines.json4
-rw-r--r--chromium/components/search_engines/search_engine_data_type_controller.cc10
-rw-r--r--chromium/components/search_engines/search_engine_data_type_controller.h9
-rw-r--r--chromium/components/search_engines/search_engine_data_type_controller_unittest.cc3
-rw-r--r--chromium/components/search_engines/search_engines_pref_names.cc76
-rw-r--r--chromium/components/search_engines/search_engines_pref_names.h17
-rw-r--r--chromium/components/search_engines/search_terms_data.cc3
-rw-r--r--chromium/components/search_engines/search_terms_data.h5
-rw-r--r--chromium/components/search_engines/template_url.cc65
-rw-r--r--chromium/components/search_engines/template_url.h53
-rw-r--r--chromium/components/search_engines/template_url_data.cc56
-rw-r--r--chromium/components/search_engines/template_url_data.h39
-rw-r--r--chromium/components/search_engines/template_url_data_util.cc244
-rw-r--r--chromium/components/search_engines/template_url_data_util.h39
-rw-r--r--chromium/components/search_engines/template_url_fetcher.cc2
-rw-r--r--chromium/components/search_engines/template_url_parser.cc12
-rw-r--r--chromium/components/search_engines/template_url_parser.h1
-rw-r--r--chromium/components/search_engines/template_url_prepopulate_data.cc129
-rw-r--r--chromium/components/search_engines/template_url_prepopulate_data.h8
-rw-r--r--chromium/components/search_engines/template_url_prepopulate_data_unittest.cc18
-rw-r--r--chromium/components/search_engines/template_url_service.cc103
-rw-r--r--chromium/components/search_engines/template_url_service.h26
-rw-r--r--chromium/components/search_engines/template_url_unittest.cc22
-rw-r--r--chromium/components/search_provider_logos/logo_cache_unittest.cc2
-rw-r--r--chromium/components/security_interstitials/OWNERS5
-rw-r--r--chromium/components/security_interstitials/content/BUILD.gn30
-rw-r--r--chromium/components/security_interstitials/content/DEPS8
-rw-r--r--chromium/components/security_interstitials/content/security_interstitial_controller_client.cc102
-rw-r--r--chromium/components/security_interstitials/content/security_interstitial_controller_client.h70
-rw-r--r--chromium/components/security_interstitials/content/security_interstitial_page.cc126
-rw-r--r--chromium/components/security_interstitials/content/security_interstitial_page.h100
-rw-r--r--chromium/components/security_interstitials/content/unsafe_resource.cc79
-rw-r--r--chromium/components/security_interstitials/content/unsafe_resource.h74
-rw-r--r--chromium/components/security_interstitials/core/BUILD.gn3
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_v2.css3
-rw-r--r--chromium/components/security_interstitials/core/browser/resources/interstitial_v2.js8
-rw-r--r--chromium/components/security_interstitials/core/controller_client.cc2
-rw-r--r--chromium/components/security_interstitials/core/controller_client.h16
-rw-r--r--chromium/components/security_interstitials/core/metrics_helper.cc69
-rw-r--r--chromium/components/security_interstitials/core/metrics_helper.h41
-rw-r--r--chromium/components/security_interstitials/core/safe_browsing_error_ui.cc311
-rw-r--r--chromium/components/security_interstitials/core/safe_browsing_error_ui.h124
-rw-r--r--chromium/components/security_interstitials_strings.grdp80
-rw-r--r--chromium/components/security_state/content/content_utils.cc28
-rw-r--r--chromium/components/security_state/core/BUILD.gn2
-rw-r--r--chromium/components/security_state/core/security_state.cc65
-rw-r--r--chromium/components/security_state/core/security_state.h33
-rw-r--r--chromium/components/security_state/core/security_state_ui.cc27
-rw-r--r--chromium/components/security_state/core/security_state_ui.h21
-rw-r--r--chromium/components/security_state/core/security_state_unittest.cc36
-rw-r--r--chromium/components/security_state/core/switches.cc2
-rw-r--r--chromium/components/security_state/core/switches.h1
-rw-r--r--chromium/components/security_state_strings.grdp13
-rw-r--r--chromium/components/session_manager/BUILD.gn3
-rw-r--r--chromium/components/session_manager/core/session_manager.cc17
-rw-r--r--chromium/components/session_manager/core/session_manager.h12
-rw-r--r--chromium/components/sessions/content/content_live_tab.h2
-rw-r--r--chromium/components/sessions/core/base_session_service.cc31
-rw-r--r--chromium/components/sessions/core/base_session_service.h8
-rw-r--r--chromium/components/sessions/core/base_session_service_test_helper.cc2
-rw-r--r--chromium/components/sessions/core/base_session_service_test_helper.h7
-rw-r--r--chromium/components/sessions/core/persistent_tab_restore_service.cc14
-rw-r--r--chromium/components/sessions/core/session_backend.cc46
-rw-r--r--chromium/components/sessions/core/session_backend.h11
-rw-r--r--chromium/components/sessions/core/session_backend_unittest.cc36
-rw-r--r--chromium/components/sessions/core/session_service_commands.cc32
-rw-r--r--chromium/components/sessions/core/session_service_commands.h2
-rw-r--r--chromium/components/sessions/core/session_types.h5
-rw-r--r--chromium/components/sessions/core/tab_restore_service_client.h1
-rw-r--r--chromium/components/sessions/ios/ios_live_tab.h1
-rw-r--r--chromium/components/sessions/ios/ios_serialized_navigation_builder.h6
-rw-r--r--chromium/components/sessions/ios/ios_serialized_navigation_builder.mm12
-rw-r--r--chromium/components/signin/DEPS1
-rw-r--r--chromium/components/signin/OWNERS2
-rw-r--r--chromium/components/signin/core/account_id/account_id.cc243
-rw-r--r--chromium/components/signin/core/account_id/account_id.h46
-rw-r--r--chromium/components/signin/core/browser/BUILD.gn3
-rw-r--r--chromium/components/signin/core/browser/about_signin_internals.h2
-rw-r--r--chromium/components/signin/core/browser/account_fetcher_service.cc18
-rw-r--r--chromium/components/signin/core/browser/account_fetcher_service.h8
-rw-r--r--chromium/components/signin/core/browser/account_investigator_unittest.cc4
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor.h4
-rw-r--r--chromium/components/signin/core/browser/android/BUILD.gn1
-rw-r--r--chromium/components/signin/core/browser/fake_account_fetcher_service.h4
-rw-r--r--chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc10
-rw-r--r--chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.h8
-rw-r--r--chromium/components/signin/core/browser/gaia_cookie_manager_service.cc53
-rw-r--r--chromium/components/signin/core/browser/gaia_cookie_manager_service.h7
-rw-r--r--chromium/components/signin/core/browser/signin_client.cc4
-rw-r--r--chromium/components/signin/core/browser/signin_client.h5
-rw-r--r--chromium/components/signin/core/browser/signin_header_helper_unittest.cc4
-rw-r--r--chromium/components/signin/core/browser/signin_manager.cc2
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider.cc7
-rw-r--r--chromium/components/signin/core/browser/signin_status_metrics_provider.h2
-rw-r--r--chromium/components/signin/core/browser/webdata/token_web_data.cc9
-rw-r--r--chromium/components/signin/ios/browser/BUILD.gn2
-rw-r--r--chromium/components/signin/ios/browser/account_consistency_service.h2
-rw-r--r--chromium/components/signin/ios/browser/account_consistency_service.mm8
-rw-r--r--chromium/components/signin/ios/browser/account_consistency_service_unittest.mm20
-rw-r--r--chromium/components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.mm2
-rw-r--r--chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h1
-rw-r--r--chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm2
-rw-r--r--chromium/components/signin/public/interfaces/BUILD.gn11
-rw-r--r--chromium/components/signin/public/interfaces/OWNERS2
-rw-r--r--chromium/components/signin/public/interfaces/account_id.mojom19
-rw-r--r--chromium/components/signin/public/interfaces/account_id.typemap14
-rw-r--r--chromium/components/signin/public/interfaces/account_id_traits.h107
-rw-r--r--chromium/components/spellcheck/browser/spellcheck_platform.h4
-rw-r--r--chromium/components/spellcheck/browser/spelling_service_client.cc2
-rw-r--r--chromium/components/spellcheck/browser/spelling_service_client.h1
-rw-r--r--chromium/components/spellcheck/common/spellcheck_features.cc8
-rw-r--r--chromium/components/spellcheck/renderer/hunspell_engine.cc12
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_multilingual_unittest.cc10
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider.cc2
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider.h4
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_unittest.cc15
-rw-r--r--chromium/components/ssl_config/ssl_config_service_manager_pref.cc12
-rw-r--r--chromium/components/ssl_config/ssl_config_service_manager_pref_unittest.cc56
-rw-r--r--chromium/components/ssl_errors/error_classification_unittest.cc2
-rw-r--r--chromium/components/startup_metric_utils/OWNERS2
-rw-r--r--chromium/components/startup_metric_utils/browser/BUILD.gn1
-rw-r--r--chromium/components/startup_metric_utils/browser/DEPS1
-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_utils.cc57
-rw-r--r--chromium/components/startup_metric_utils/common/startup_metric.mojom2
-rw-r--r--chromium/components/storage_monitor/storage_monitor_linux.cc2
-rw-r--r--chromium/components/storage_monitor/storage_monitor_linux_unittest.cc22
-rw-r--r--chromium/components/storage_monitor/storage_monitor_mac.mm2
-rw-r--r--chromium/components/storage_monitor/udev_util_linux.h2
-rw-r--r--chromium/components/strings/BUILD.gn3
-rw-r--r--chromium/components/strings/components_chromium_strings_bn.xtb4
-rw-r--r--chromium/components/strings/components_google_chrome_strings_bn.xtb4
-rw-r--r--chromium/components/strings/components_strings_am.xtb76
-rw-r--r--chromium/components/strings/components_strings_ar.xtb74
-rw-r--r--chromium/components/strings/components_strings_bg.xtb72
-rw-r--r--chromium/components/strings/components_strings_bn.xtb104
-rw-r--r--chromium/components/strings/components_strings_ca.xtb74
-rw-r--r--chromium/components/strings/components_strings_cs.xtb74
-rw-r--r--chromium/components/strings/components_strings_da.xtb74
-rw-r--r--chromium/components/strings/components_strings_de.xtb74
-rw-r--r--chromium/components/strings/components_strings_el.xtb74
-rw-r--r--chromium/components/strings/components_strings_en-GB.xtb72
-rw-r--r--chromium/components/strings/components_strings_es-419.xtb74
-rw-r--r--chromium/components/strings/components_strings_es.xtb72
-rw-r--r--chromium/components/strings/components_strings_et.xtb76
-rw-r--r--chromium/components/strings/components_strings_fa.xtb74
-rw-r--r--chromium/components/strings/components_strings_fi.xtb72
-rw-r--r--chromium/components/strings/components_strings_fil.xtb78
-rw-r--r--chromium/components/strings/components_strings_fr.xtb74
-rw-r--r--chromium/components/strings/components_strings_gu.xtb74
-rw-r--r--chromium/components/strings/components_strings_hi.xtb120
-rw-r--r--chromium/components/strings/components_strings_hr.xtb72
-rw-r--r--chromium/components/strings/components_strings_hu.xtb74
-rw-r--r--chromium/components/strings/components_strings_id.xtb78
-rw-r--r--chromium/components/strings/components_strings_it.xtb72
-rw-r--r--chromium/components/strings/components_strings_iw.xtb78
-rw-r--r--chromium/components/strings/components_strings_ja.xtb76
-rw-r--r--chromium/components/strings/components_strings_kn.xtb76
-rw-r--r--chromium/components/strings/components_strings_ko.xtb74
-rw-r--r--chromium/components/strings/components_strings_lt.xtb72
-rw-r--r--chromium/components/strings/components_strings_lv.xtb72
-rw-r--r--chromium/components/strings/components_strings_ml.xtb74
-rw-r--r--chromium/components/strings/components_strings_mr.xtb88
-rw-r--r--chromium/components/strings/components_strings_ms.xtb72
-rw-r--r--chromium/components/strings/components_strings_nl.xtb74
-rw-r--r--chromium/components/strings/components_strings_no.xtb74
-rw-r--r--chromium/components/strings/components_strings_pl.xtb76
-rw-r--r--chromium/components/strings/components_strings_pt-BR.xtb72
-rw-r--r--chromium/components/strings/components_strings_pt-PT.xtb72
-rw-r--r--chromium/components/strings/components_strings_ro.xtb72
-rw-r--r--chromium/components/strings/components_strings_ru.xtb74
-rw-r--r--chromium/components/strings/components_strings_sk.xtb74
-rw-r--r--chromium/components/strings/components_strings_sl.xtb74
-rw-r--r--chromium/components/strings/components_strings_sr.xtb72
-rw-r--r--chromium/components/strings/components_strings_sv.xtb74
-rw-r--r--chromium/components/strings/components_strings_sw.xtb72
-rw-r--r--chromium/components/strings/components_strings_ta.xtb74
-rw-r--r--chromium/components/strings/components_strings_te.xtb74
-rw-r--r--chromium/components/strings/components_strings_th.xtb74
-rw-r--r--chromium/components/strings/components_strings_tr.xtb74
-rw-r--r--chromium/components/strings/components_strings_uk.xtb76
-rw-r--r--chromium/components/strings/components_strings_vi.xtb72
-rw-r--r--chromium/components/strings/components_strings_zh-CN.xtb72
-rw-r--r--chromium/components/strings/components_strings_zh-TW.xtb72
-rw-r--r--chromium/components/subresource_filter/OWNERS1
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_driver.cc6
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_driver.h3
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc233
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h71
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc511
-rw-r--r--chromium/components/subresource_filter/content/common/BUILD.gn1
-rw-r--r--chromium/components/subresource_filter/content/common/document_load_statistics.h35
-rw-r--r--chromium/components/subresource_filter/content/common/subresource_filter_messages.h28
-rw-r--r--chromium/components/subresource_filter/content/renderer/document_subresource_filter.cc33
-rw-r--r--chromium/components/subresource_filter/content/renderer/document_subresource_filter.h25
-rw-r--r--chromium/components/subresource_filter/content/renderer/document_subresource_filter_unittest.cc52
-rw-r--r--chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc84
-rw-r--r--chromium/components/subresource_filter/content/renderer/subresource_filter_agent.h9
-rw-r--r--chromium/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc328
-rw-r--r--chromium/components/subresource_filter/core/browser/BUILD.gn1
-rw-r--r--chromium/components/subresource_filter/core/browser/ruleset_service.cc5
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features.cc13
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features.h7
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc27
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.h15
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc36
-rw-r--r--chromium/components/subresource_filter/core/common/BUILD.gn8
-rw-r--r--chromium/components/subresource_filter/core/common/activation_list.cc31
-rw-r--r--chromium/components/subresource_filter/core/common/activation_list.h5
-rw-r--r--chromium/components/subresource_filter/core/common/scoped_timers.h156
-rw-r--r--chromium/components/subresource_filter/core/common/scoped_timers_unittest.cc187
-rw-r--r--chromium/components/subresource_filter/core/common/test_ruleset_creator.cc48
-rw-r--r--chromium/components/subresource_filter/core/common/test_ruleset_creator.h4
-rw-r--r--chromium/components/subresource_filter/core/common/test_ruleset_utils.cc36
-rw-r--r--chromium/components/subresource_filter/core/common/test_ruleset_utils.h21
-rw-r--r--chromium/components/subresource_filter/core/common/time_measurements.h150
-rw-r--r--chromium/components/subresource_filter/core/common/unindexed_ruleset.cc1
-rw-r--r--chromium/components/suggestions/BUILD.gn7
-rw-r--r--chromium/components/suggestions/DEPS1
-rw-r--r--chromium/components/suggestions/blacklist_store_unittest.cc10
-rw-r--r--chromium/components/suggestions/image_manager.h4
-rw-r--r--chromium/components/suggestions/proto/suggestions.proto4
-rw-r--r--chromium/components/suggestions/suggestions_service.h190
-rw-r--r--chromium/components/suggestions/suggestions_service_impl.cc (renamed from chromium/components/suggestions/suggestions_service.cc)133
-rw-r--r--chromium/components/suggestions/suggestions_service_impl.h203
-rw-r--r--chromium/components/suggestions/suggestions_service_impl_unittest.cc (renamed from chromium/components/suggestions/suggestions_service_unittest.cc)79
-rw-r--r--chromium/components/suggestions/suggestions_store_unittest.cc8
-rw-r--r--chromium/components/supervised_user_error_page/BUILD.gn12
-rw-r--r--chromium/components/supervised_user_error_page/supervised_user_error_page.cc7
-rw-r--r--chromium/components/supervised_user_error_page/supervised_user_error_page.h13
-rw-r--r--chromium/components/supervised_user_error_page_strings.grdp6
-rw-r--r--chromium/components/sync/BUILD.gn96
-rw-r--r--chromium/components/sync/android/BUILD.gn1
-rw-r--r--chromium/components/sync_bookmarks/bookmark_change_processor.h2
-rw-r--r--chromium/components/sync_preferences/BUILD.gn5
-rw-r--r--chromium/components/sync_preferences/pref_model_associator.cc26
-rw-r--r--chromium/components/sync_preferences/pref_model_associator.h1
-rw-r--r--chromium/components/sync_preferences/pref_model_associator_unittest.cc4
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable_factory.cc7
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable_factory.h4
-rw-r--r--chromium/components/sync_preferences/testing_pref_service_syncable.cc11
-rw-r--r--chromium/components/sync_preferences/testing_pref_service_syncable.h4
-rw-r--r--chromium/components/sync_sessions/revisit/bookmarks_page_revisit_observer.h1
-rw-r--r--chromium/components/sync_sessions/revisit/sessions_page_revisit_observer.h6
-rw-r--r--chromium/components/sync_sessions/session_data_type_controller.cc9
-rw-r--r--chromium/components/sync_sessions/session_data_type_controller.h6
-rw-r--r--chromium/components/sync_sessions/sessions_sync_manager.cc52
-rw-r--r--chromium/components/sync_sessions/sessions_sync_manager.h11
-rw-r--r--chromium/components/sync_sessions/synced_tab_delegate.h2
-rw-r--r--chromium/components/sync_ui_strings.grdp3
-rw-r--r--chromium/components/task_scheduler_util/browser/BUILD.gn17
-rw-r--r--chromium/components/task_scheduler_util/browser/DEPS3
-rw-r--r--chromium/components/task_scheduler_util/browser/initialization.cc89
-rw-r--r--chromium/components/task_scheduler_util/browser/initialization.h35
-rw-r--r--chromium/components/task_scheduler_util/common/BUILD.gn28
-rw-r--r--chromium/components/task_scheduler_util/common/DEPS3
-rw-r--r--chromium/components/task_scheduler_util/common/variations_util.cc180
-rw-r--r--chromium/components/task_scheduler_util/common/variations_util.h68
-rw-r--r--chromium/components/task_scheduler_util/common/variations_util_unittest.cc266
-rw-r--r--chromium/components/task_scheduler_util/initialization_util.cc153
-rw-r--r--chromium/components/task_scheduler_util/initialization_util.h22
-rw-r--r--chromium/components/task_scheduler_util/renderer/BUILD.gn17
-rw-r--r--chromium/components/task_scheduler_util/renderer/initialization.cc58
-rw-r--r--chromium/components/task_scheduler_util/renderer/initialization.h32
-rw-r--r--chromium/components/test_runner/BUILD.gn8
-rw-r--r--chromium/components/toolbar/toolbar_model_delegate.h10
-rw-r--r--chromium/components/toolbar/toolbar_model_impl.cc4
-rw-r--r--chromium/components/tracing/browser/trace_config_file.cc2
-rw-r--r--chromium/components/tracing/child/child_trace_message_filter_browsertest.cc3
-rw-r--r--chromium/components/tracing/common/process_metrics_memory_dump_provider.cc70
-rw-r--r--chromium/components/tracing/common/process_metrics_memory_dump_provider.h8
-rw-r--r--chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc49
-rw-r--r--chromium/components/translate/content/common/translate.mojom2
-rw-r--r--chromium/components/translate/core/browser/BUILD.gn3
-rw-r--r--chromium/components/translate/ios/browser/BUILD.gn4
-rw-r--r--chromium/components/typemaps.gni4
-rw-r--r--chromium/components/ui_devtools/devtools_client.cc11
-rw-r--r--chromium/components/ui_devtools/devtools_client.h7
-rw-r--r--chromium/components/ui_devtools/devtools_server.cc110
-rw-r--r--chromium/components/ui_devtools/devtools_server.h18
-rw-r--r--chromium/components/ui_devtools/protocol.json173
-rw-r--r--chromium/components/update_client/action.cc4
-rw-r--r--chromium/components/update_client/action.h1
-rw-r--r--chromium/components/update_client/action_update.cc2
-rw-r--r--chromium/components/update_client/action_update.h1
-rw-r--r--chromium/components/update_client/component_patcher.cc2
-rw-r--r--chromium/components/update_client/component_patcher_unittest.h1
-rw-r--r--chromium/components/update_client/component_unpacker.cc2
-rw-r--r--chromium/components/update_client/test_configurator.h2
-rw-r--r--chromium/components/update_client/update_checker.h2
-rw-r--r--chromium/components/update_client/update_client.h1
-rw-r--r--chromium/components/update_client/update_client_internal.h6
-rw-r--r--chromium/components/update_client/update_engine.cc2
-rw-r--r--chromium/components/update_client/update_query_params.cc5
-rw-r--r--chromium/components/update_client/url_request_post_interceptor.h4
-rw-r--r--chromium/components/update_client/utils.cc5
-rw-r--r--chromium/components/update_client/utils.h2
-rw-r--r--chromium/components/url_formatter/elide_url_unittest.cc5
-rw-r--r--chromium/components/url_matcher/url_matcher.h4
-rw-r--r--chromium/components/user_manager/BUILD.gn2
-rw-r--r--chromium/components/user_manager/DEPS1
-rw-r--r--chromium/components/user_manager/known_user.cc161
-rw-r--r--chromium/components/user_manager/known_user.h10
-rw-r--r--chromium/components/user_manager/user.cc33
-rw-r--r--chromium/components/user_manager/user.h15
-rw-r--r--chromium/components/user_manager/user_image/user_image.cc72
-rw-r--r--chromium/components/user_manager/user_image/user_image.h50
-rw-r--r--chromium/components/user_manager/user_manager.cc18
-rw-r--r--chromium/components/user_manager/user_manager.h26
-rw-r--r--chromium/components/user_manager/user_manager_base.cc38
-rw-r--r--chromium/components/user_manager/user_manager_base.h7
-rw-r--r--chromium/components/user_manager/user_type.h4
-rw-r--r--chromium/components/user_prefs/tracked/pref_hash_calculator_unittest.cc18
-rw-r--r--chromium/components/user_prefs/tracked/pref_hash_filter.cc28
-rw-r--r--chromium/components/user_prefs/tracked/pref_hash_filter.h11
-rw-r--r--chromium/components/user_prefs/tracked/tracked_preferences_migration.h1
-rw-r--r--chromium/components/variations/BUILD.gn19
-rw-r--r--chromium/components/variations/android/BUILD.gn2
-rw-r--r--chromium/components/variations/child_process_field_trial_syncer.cc66
-rw-r--r--chromium/components/variations/child_process_field_trial_syncer.h53
-rw-r--r--chromium/components/variations/child_process_field_trial_syncer_unittest.cc98
-rw-r--r--chromium/components/variations/entropy_provider.cc6
-rw-r--r--chromium/components/variations/entropy_provider.h16
-rw-r--r--chromium/components/variations/entropy_provider_unittest.cc27
-rw-r--r--chromium/components/variations/processed_study.cc24
-rw-r--r--chromium/components/variations/processed_study.h8
-rw-r--r--chromium/components/variations/proto/study.proto17
-rw-r--r--chromium/components/variations/study_filtering.cc15
-rw-r--r--chromium/components/variations/study_filtering.h3
-rw-r--r--chromium/components/variations/study_filtering_unittest.cc34
-rw-r--r--chromium/components/variations/variations_associated_data.cc78
-rw-r--r--chromium/components/variations/variations_associated_data.h64
-rw-r--r--chromium/components/variations/variations_associated_data_unittest.cc85
-rw-r--r--chromium/components/variations/variations_params_manager.cc96
-rw-r--r--chromium/components/variations/variations_params_manager.h83
-rw-r--r--chromium/components/variations/variations_seed_processor.cc8
-rw-r--r--chromium/components/variations/variations_seed_processor_unittest.cc22
-rw-r--r--chromium/components/variations/variations_seed_store.cc7
-rw-r--r--chromium/components/version_ui/resources/about_version.html18
-rw-r--r--chromium/components/version_ui/resources/about_version.js14
-rw-r--r--chromium/components/version_ui/version_ui_constants.cc3
-rw-r--r--chromium/components/version_ui/version_ui_constants.h3
-rw-r--r--chromium/components/version_ui_strings.grdp3
-rw-r--r--chromium/components/visitedlink/browser/visitedlink_delegate.h4
-rw-r--r--chromium/components/wallpaper/wallpaper_manager_base.cc43
-rw-r--r--chromium/components/wallpaper/wallpaper_manager_base.h25
-rw-r--r--chromium/components/web_cache/browser/web_cache_manager.cc114
-rw-r--r--chromium/components/web_cache/browser/web_cache_manager.h35
-rw-r--r--chromium/components/web_cache/browser/web_cache_manager_unittest.cc80
-rw-r--r--chromium/components/web_cache/public/interfaces/web_cache.mojom4
-rw-r--r--chromium/components/web_cache/renderer/web_cache_impl.cc9
-rw-r--r--chromium/components/web_cache/renderer/web_cache_impl.h4
-rw-r--r--chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc4
-rw-r--r--chromium/components/web_contents_delegate_android/web_contents_delegate_android.h2
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.h4
-rw-r--r--chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.cc1
-rw-r--r--chromium/components/web_restrictions/browser/web_restrictions_resource_throttle.cc5
-rw-r--r--chromium/components/web_restrictions/browser/web_restrictions_resource_throttle.h4
-rw-r--r--chromium/components/web_restrictions/browser/web_restrictions_resource_throttle_unittest.cc33
-rw-r--r--chromium/components/webcrypto/algorithm_dispatch.h1
-rw-r--r--chromium/components/webcrypto/algorithm_implementation.h20
-rw-r--r--chromium/components/webcrypto/algorithms/aes.cc4
-rw-r--r--chromium/components/webcrypto/algorithms/aes_cbc.cc6
-rw-r--r--chromium/components/webcrypto/algorithms/aes_ctr.cc2
-rw-r--r--chromium/components/webcrypto/algorithms/ec.cc3
-rw-r--r--chromium/components/webcrypto/algorithms/hkdf.cc4
-rw-r--r--chromium/components/webcrypto/algorithms/hmac.cc4
-rw-r--r--chromium/components/webcrypto/algorithms/pbkdf2.cc4
-rw-r--r--chromium/components/webcrypto/algorithms/rsa.cc3
-rw-r--r--chromium/components/webcrypto/algorithms/test_helpers.cc2
-rw-r--r--chromium/components/webcrypto/algorithms/util.h1
-rw-r--r--chromium/components/webdata/common/BUILD.gn4
-rw-r--r--chromium/components/webdata/common/web_data_request_manager.cc166
-rw-r--r--chromium/components/webdata/common/web_data_request_manager.h79
-rw-r--r--chromium/components/webdata/common/web_data_service_base.cc5
-rw-r--r--chromium/components/webdata/common/web_data_service_base.h8
-rw-r--r--chromium/components/webdata/common/web_database.cc4
-rw-r--r--chromium/components/webdata/common/web_database_backend.cc12
-rw-r--r--chromium/components/webdata/common/web_database_backend.h10
-rw-r--r--chromium/components/webdata/common/web_database_migration_unittest.cc187
-rw-r--r--chromium/components/webdata/common/web_database_service.cc10
-rw-r--r--chromium/components/webdata/common/web_database_service.h11
-rw-r--r--chromium/components/webdata_services/web_data_service_wrapper.cc17
-rw-r--r--chromium/components/webdata_services/web_data_service_wrapper.h1
1934 files changed, 81784 insertions, 27064 deletions
diff --git a/chromium/components/BUILD.gn b/chromium/components/BUILD.gn
index 82730a45e99..a563627bb29 100644
--- a/chromium/components/BUILD.gn
+++ b/chromium/components/BUILD.gn
@@ -6,6 +6,7 @@ import("//build/config/chrome_build.gni")
import("//build/config/features.gni")
import("//build/config/ui.gni")
import("//printing/features/features.gni")
+import("//rlz/features/features.gni")
import("//testing/test.gni")
import("//tools/grit/repack.gni")
@@ -52,6 +53,10 @@ test("components_unittests") {
]
}
+ if (is_android) {
+ enable_multidex = true
+ }
+
# Add only ":unit_tests" dependencies here. If your tests have dependencies
# (this would at least include the component itself), they should be on the
# test source set and not here.
@@ -99,11 +104,10 @@ test("components_unittests") {
"//components/network_time:unit_tests",
"//components/ntp_snippets:unit_tests",
"//components/ntp_tiles:unit_tests",
- "//components/offline_pages:unit_tests",
- "//components/offline_pages/background:unit_tests",
"//components/offline_pages/core:unit_tests",
- "//components/offline_pages/downloads:unit_tests",
- "//components/offline_pages/request_header:unit_tests",
+ "//components/offline_pages/core/background:unit_tests",
+ "//components/offline_pages/core/downloads:unit_tests",
+ "//components/offline_pages/core/request_header:unit_tests",
"//components/omnibox/browser:unit_tests",
"//components/open_from_clipboard:unit_tests",
"//components/os_crypt:unit_tests",
@@ -134,6 +138,7 @@ test("components_unittests") {
"//components/sync_bookmarks:unit_tests",
"//components/sync_preferences:unit_tests",
"//components/sync_sessions:unit_tests",
+ "//components/task_scheduler_util/common:unit_tests",
"//components/test:test_support",
"//components/translate/core/browser:unit_tests",
"//components/translate/core/common:unit_tests",
@@ -156,6 +161,7 @@ test("components_unittests") {
if (is_ios) {
deps += [
+ "//components/reading_list/ios:unit_tests",
"//components/signin/ios/browser:unit_tests",
"//components/translate/ios/browser:unit_tests",
]
@@ -189,9 +195,11 @@ test("components_unittests") {
"//components/link_header_util:unit_tests",
"//components/navigation_interception:unit_tests",
"//components/network_hints/renderer:unit_tests",
+ "//components/offline_pages/content/background_loader:unit_tests",
"//components/packed_ct_ev_whitelist:unit_tests",
"//components/password_manager/content/browser:unit_tests",
- "//components/power:unit_tests",
+ "//components/policy/core/browser:unit_tests",
+ "//components/policy/core/common:unit_tests",
"//components/precache/content:unit_tests",
"//components/safe_browsing_db:unit_tests",
"//components/safe_json:unit_tests",
@@ -230,6 +238,7 @@ test("components_unittests") {
"//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
"//components/invalidation/impl",
"//components/invalidation/impl:java",
+ "//components/policy/android:policy_java",
"//components/safe_json",
"//components/safe_json/android:safe_json_java",
"//components/signin/core/browser",
@@ -247,15 +256,12 @@ test("components_unittests") {
"//ui/gfx",
"//v8:v8_external_startup_data_assets",
]
-
- if (enable_configuration_policy) {
- deps += [ "//components/policy/android:policy_java" ]
- }
}
# Desktop-only deps.
if (!is_android && !is_ios) {
deps += [
+ "//components/cryptauth:unit_tests",
"//components/feedback:unit_tests",
"//components/proximity_auth:unit_tests",
"//components/storage_monitor:unit_tests",
@@ -278,13 +284,6 @@ test("components_unittests") {
]
}
- if (enable_configuration_policy) {
- deps += [
- "//components/policy/core/browser:unit_tests",
- "//components/policy/core/common:unit_tests",
- ]
- }
-
if (toolkit_views) {
deps += [ "//components/constrained_window:unit_tests" ]
}
@@ -468,8 +467,8 @@ if (!is_ios) {
sources += [ "dom_distiller/standalone/content_extractor_browsertest.cc" ]
deps += [
"//components/leveldb_proto",
- "//components/pref_registry:test_support",
"//components/prefs:test_support",
+ "//components/sync_preferences:test_support",
]
}
diff --git a/chromium/components/OWNERS b/chromium/components/OWNERS
index 825c24bd457..fb6112e8aee 100644
--- a/chromium/components/OWNERS
+++ b/chromium/components/OWNERS
@@ -16,10 +16,13 @@ per-file nacl_loader_unittests.isolate=file://components/nacl/OWNERS
per-file ntp_snippets_strings.grdp=file://components/ntp_snippets/OWNERS
per-file omnibox_strings.grdp=file://components/omnibox/OWNERS
per-file password_manager_strings.grdp=file://components/password_manager/OWNERS
+per-file pageinfo_strings.grdp=estark@chromium.org
+per-file pageinfo_strings.grdp=lgarron@chromium.org
per-file pdf_strings.grdp=raymes@chromium.org
per-file pdf_strings.grdp=tsergeant@chromium.org
per-file policy_strings.grdp=file://components/policy/OWNERS
per-file security_interstitials_strings.grdp=file://components/security_interstitials/OWNERS
+per-file security_state_strings.grdp=file://components/security_state/OWNERS
per-file ssl_errors_strings.grdp=file://components/ssl_errors/OWNERS
per-file supervised_user_error_page_strings.grdp=file://components/supervised_user_error_page/OWNERS
per-file sync_ui_strings.grdp=file://components/sync/OWNERS
diff --git a/chromium/components/app_modal/native_app_modal_dialog.h b/chromium/components/app_modal/native_app_modal_dialog.h
index 26025cd25c7..dd6cfd608a7 100644
--- a/chromium/components/app_modal/native_app_modal_dialog.h
+++ b/chromium/components/app_modal/native_app_modal_dialog.h
@@ -9,7 +9,6 @@
namespace app_modal {
-class JavaScriptAppModalDialog;
class NativeAppModalDialog {
public:
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 923b64af7d8..d1195fd4547 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
@@ -11,14 +11,14 @@
#include "components/app_modal/native_app_modal_dialog.h"
#include "ui/views/window/dialog_delegate.h"
-class JavaScriptAppModalDialog;
-
namespace views {
class MessageBoxView;
}
namespace app_modal {
+class JavaScriptAppModalDialog;
+
class JavaScriptAppModalDialogViews : public NativeAppModalDialog,
public views::DialogDelegate {
public:
diff --git a/chromium/components/arc/BUILD.gn b/chromium/components/arc/BUILD.gn
index 1e10a071224..9ffc7305ba3 100644
--- a/chromium/components/arc/BUILD.gn
+++ b/chromium/components/arc/BUILD.gn
@@ -7,16 +7,8 @@ import("//testing/test.gni")
static_library("arc") {
sources = [
- "arc_bridge_host_impl.cc",
- "arc_bridge_host_impl.h",
- "arc_bridge_service_impl.cc",
- "arc_bridge_service_impl.h",
- "arc_features.cc",
- "arc_features.h",
"arc_service_manager.cc",
"arc_service_manager.h",
- "arc_session.cc",
- "arc_session.h",
"audio/arc_audio_bridge.cc",
"audio/arc_audio_bridge.h",
"bluetooth/arc_bluetooth_bridge.cc",
@@ -25,12 +17,12 @@ static_library("arc") {
"bluetooth/bluetooth_struct_traits.h",
"bluetooth/bluetooth_type_converters.cc",
"bluetooth/bluetooth_type_converters.h",
- "boot_phase_monitor/arc_boot_phase_monitor_bridge.cc",
- "boot_phase_monitor/arc_boot_phase_monitor_bridge.h",
"clipboard/arc_clipboard_bridge.cc",
"clipboard/arc_clipboard_bridge.h",
"crash_collector/arc_crash_collector_bridge.cc",
"crash_collector/arc_crash_collector_bridge.h",
+ "file_system/arc_file_system_operation_runner.cc",
+ "file_system/arc_file_system_operation_runner.h",
"ime/arc_ime_bridge.h",
"ime/arc_ime_bridge_impl.cc",
"ime/arc_ime_bridge_impl.h",
@@ -56,9 +48,6 @@ static_library("arc") {
"kiosk/arc_kiosk_bridge.h",
"metrics/arc_metrics_service.cc",
"metrics/arc_metrics_service.h",
- "metrics/oom_kills_histogram.h",
- "metrics/oom_kills_monitor.cc",
- "metrics/oom_kills_monitor.h",
"net/arc_net_host_impl.cc",
"net/arc_net_host_impl.h",
"obb_mounter/arc_obb_mounter_bridge.cc",
@@ -67,8 +56,8 @@ static_library("arc") {
"power/arc_power_bridge.h",
"storage_manager/arc_storage_manager.cc",
"storage_manager/arc_storage_manager.h",
- "user_data/arc_user_data_service.cc",
- "user_data/arc_user_data_service.h",
+ "video_accelerator/video_accelerator_struct_traits.cc",
+ "video_accelerator/video_accelerator_struct_traits.h",
]
public_deps = [
@@ -86,18 +75,15 @@ static_library("arc") {
"//components/onc",
"//components/prefs",
"//components/signin/core/account_id",
- "//components/user_manager",
"//device/bluetooth",
"//google_apis",
- "//ipc:ipc",
"//mojo/edk/system",
"//skia",
"//third_party/re2:re2",
- "//ui/arc",
"//ui/aura",
"//ui/base:base",
"//ui/base/ime",
- "//ui/display:display",
+ "//ui/display/manager",
"//ui/events",
"//ui/events:dom_keycode_converter",
"//ui/keyboard:keyboard",
@@ -106,17 +92,31 @@ static_library("arc") {
}
static_library("arc_base") {
+ # TODO(hidehiko): Revisit here and move back some files to "arc"
+ # on completion to move ArcSession task to ArcSessionManager.
sources = [
+ "arc_bridge_host_impl.cc",
+ "arc_bridge_host_impl.h",
"arc_bridge_service.cc",
"arc_bridge_service.h",
+ "arc_features.cc",
+ "arc_features.h",
"arc_service.cc",
"arc_service.h",
+ "arc_session.cc",
+ "arc_session.h",
+ "arc_session_observer.cc",
+ "arc_session_observer.h",
+ "arc_session_runner.cc",
+ "arc_session_runner.h",
"instance_holder.h",
]
deps = [
"//base",
"//chromeos",
+ "//components/user_manager",
+ "//mojo/edk/system",
]
public_deps = [
@@ -124,28 +124,14 @@ static_library("arc_base") {
]
}
-# TODO(crbug.com/662510): Remove this altogether.
-mojom("arc_bindings_old_types") {
- sources = [
- "common/bitmap.mojom",
- "common/bluetooth.mojom",
- "common/net.mojom",
- ]
-
- public_deps = [
- "//mojo/common:common_custom_types",
- "//ui/gfx/geometry/mojo",
- ]
-
- use_new_wrapper_types = false
-}
-
mojom("arc_bindings") {
sources = [
"common/app.mojom",
"common/arc_bridge.mojom",
"common/audio.mojom",
"common/auth.mojom",
+ "common/bitmap.mojom",
+ "common/bluetooth.mojom",
"common/boot_phase_monitor.mojom",
"common/clipboard.mojom",
"common/crash_collector.mojom",
@@ -155,6 +141,7 @@ mojom("arc_bindings") {
"common/intent_helper.mojom",
"common/kiosk.mojom",
"common/metrics.mojom",
+ "common/net.mojom",
"common/notifications.mojom",
"common/obb_mounter.mojom",
"common/policy.mojom",
@@ -171,22 +158,24 @@ mojom("arc_bindings") {
]
public_deps = [
- ":arc_bindings_old_types",
"//mojo/common:common_custom_types",
+ "//ui/gfx/geometry/mojo",
]
}
static_library("arc_test_support") {
testonly = true
sources = [
+ "file_system/test/fake_arc_file_system_operation_runner.cc",
+ "file_system/test/fake_arc_file_system_operation_runner.h",
"test/fake_app_instance.cc",
"test/fake_app_instance.h",
- "test/fake_arc_bridge_service.cc",
- "test/fake_arc_bridge_service.h",
"test/fake_arc_session.cc",
"test/fake_arc_session.h",
"test/fake_bluetooth_instance.cc",
"test/fake_bluetooth_instance.h",
+ "test/fake_file_system_instance.cc",
+ "test/fake_file_system_instance.h",
"test/fake_intent_helper_instance.cc",
"test/fake_intent_helper_instance.h",
"test/fake_notifications_instance.cc",
@@ -208,7 +197,8 @@ static_library("arc_test_support") {
source_set("unit_tests") {
testonly = true
sources = [
- "arc_bridge_service_unittest.cc",
+ "arc_service_manager_unittest.cc",
+ "arc_session_runner_unittest.cc",
"bluetooth/arc_bluetooth_bridge_unittest.cc",
"bluetooth/bluetooth_struct_traits_unittest.cc",
"bluetooth/bluetooth_type_converters_unittest.cc",
diff --git a/chromium/components/arc/DEPS b/chromium/components/arc/DEPS
index f96c22a28d0..b0c29d218e2 100644
--- a/chromium/components/arc/DEPS
+++ b/chromium/components/arc/DEPS
@@ -6,10 +6,8 @@ include_rules = [
"+components/prefs",
"+components/signin/core/account_id",
"+components/user_manager",
- "+ipc",
"+mojo",
"+third_party/re2",
"+third_party/skia",
- "+ui/arc",
"+ui/gfx/geometry/rect.h",
]
diff --git a/chromium/components/arc/arc_bridge_host_impl.cc b/chromium/components/arc/arc_bridge_host_impl.cc
index 1fc93b50b4c..b22068e5d4e 100644
--- a/chromium/components/arc/arc_bridge_host_impl.cc
+++ b/chromium/components/arc/arc_bridge_host_impl.cc
@@ -67,8 +67,12 @@ class MojoChannelImpl : public ArcBridgeHostImpl::MojoChannel {
} // namespace
-ArcBridgeHostImpl::ArcBridgeHostImpl(mojom::ArcBridgeInstancePtr instance)
- : binding_(this), instance_(std::move(instance)) {
+ArcBridgeHostImpl::ArcBridgeHostImpl(ArcBridgeService* arc_bridge_service,
+ mojom::ArcBridgeInstancePtr instance)
+ : arc_bridge_service_(arc_bridge_service),
+ binding_(this),
+ instance_(std::move(instance)) {
+ DCHECK(arc_bridge_service_);
DCHECK(instance_.is_bound());
instance_.set_connection_error_handler(
base::Bind(&ArcBridgeHostImpl::OnClosed, base::Unretained(this)));
@@ -80,129 +84,126 @@ ArcBridgeHostImpl::~ArcBridgeHostImpl() {
}
void ArcBridgeHostImpl::OnAppInstanceReady(mojom::AppInstancePtr app_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->app(), std::move(app_ptr));
+ OnInstanceReady(arc_bridge_service_->app(), std::move(app_ptr));
}
void ArcBridgeHostImpl::OnAudioInstanceReady(
mojom::AudioInstancePtr audio_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->audio(), std::move(audio_ptr));
+ OnInstanceReady(arc_bridge_service_->audio(), std::move(audio_ptr));
}
void ArcBridgeHostImpl::OnAuthInstanceReady(mojom::AuthInstancePtr auth_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->auth(), std::move(auth_ptr));
+ OnInstanceReady(arc_bridge_service_->auth(), std::move(auth_ptr));
}
void ArcBridgeHostImpl::OnBluetoothInstanceReady(
mojom::BluetoothInstancePtr bluetooth_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->bluetooth(),
- std::move(bluetooth_ptr));
+ OnInstanceReady(arc_bridge_service_->bluetooth(), std::move(bluetooth_ptr));
}
void ArcBridgeHostImpl::OnBootPhaseMonitorInstanceReady(
mojom::BootPhaseMonitorInstancePtr boot_phase_monitor_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->boot_phase_monitor(),
+ OnInstanceReady(arc_bridge_service_->boot_phase_monitor(),
std::move(boot_phase_monitor_ptr));
}
void ArcBridgeHostImpl::OnClipboardInstanceReady(
mojom::ClipboardInstancePtr clipboard_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->clipboard(),
- std::move(clipboard_ptr));
+ OnInstanceReady(arc_bridge_service_->clipboard(), std::move(clipboard_ptr));
}
void ArcBridgeHostImpl::OnCrashCollectorInstanceReady(
mojom::CrashCollectorInstancePtr crash_collector_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->crash_collector(),
+ OnInstanceReady(arc_bridge_service_->crash_collector(),
std::move(crash_collector_ptr));
}
void ArcBridgeHostImpl::OnEnterpriseReportingInstanceReady(
mojom::EnterpriseReportingInstancePtr enterprise_reporting_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->enterprise_reporting(),
+ OnInstanceReady(arc_bridge_service_->enterprise_reporting(),
std::move(enterprise_reporting_ptr));
}
void ArcBridgeHostImpl::OnFileSystemInstanceReady(
mojom::FileSystemInstancePtr file_system_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->file_system(),
+ OnInstanceReady(arc_bridge_service_->file_system(),
std::move(file_system_ptr));
}
void ArcBridgeHostImpl::OnImeInstanceReady(mojom::ImeInstancePtr ime_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->ime(), std::move(ime_ptr));
+ OnInstanceReady(arc_bridge_service_->ime(), std::move(ime_ptr));
}
void ArcBridgeHostImpl::OnIntentHelperInstanceReady(
mojom::IntentHelperInstancePtr intent_helper_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->intent_helper(),
+ OnInstanceReady(arc_bridge_service_->intent_helper(),
std::move(intent_helper_ptr));
}
void ArcBridgeHostImpl::OnKioskInstanceReady(
mojom::KioskInstancePtr kiosk_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->kiosk(), std::move(kiosk_ptr));
+ OnInstanceReady(arc_bridge_service_->kiosk(), std::move(kiosk_ptr));
}
void ArcBridgeHostImpl::OnMetricsInstanceReady(
mojom::MetricsInstancePtr metrics_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->metrics(), std::move(metrics_ptr));
+ OnInstanceReady(arc_bridge_service_->metrics(), std::move(metrics_ptr));
}
void ArcBridgeHostImpl::OnNetInstanceReady(mojom::NetInstancePtr net_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->net(), std::move(net_ptr));
+ OnInstanceReady(arc_bridge_service_->net(), std::move(net_ptr));
}
void ArcBridgeHostImpl::OnNotificationsInstanceReady(
mojom::NotificationsInstancePtr notifications_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->notifications(),
+ OnInstanceReady(arc_bridge_service_->notifications(),
std::move(notifications_ptr));
}
void ArcBridgeHostImpl::OnObbMounterInstanceReady(
mojom::ObbMounterInstancePtr obb_mounter_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->obb_mounter(),
+ OnInstanceReady(arc_bridge_service_->obb_mounter(),
std::move(obb_mounter_ptr));
}
void ArcBridgeHostImpl::OnPolicyInstanceReady(
mojom::PolicyInstancePtr policy_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->policy(), std::move(policy_ptr));
+ OnInstanceReady(arc_bridge_service_->policy(), std::move(policy_ptr));
}
void ArcBridgeHostImpl::OnPowerInstanceReady(
mojom::PowerInstancePtr power_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->power(), std::move(power_ptr));
+ OnInstanceReady(arc_bridge_service_->power(), std::move(power_ptr));
}
void ArcBridgeHostImpl::OnPrintInstanceReady(
mojom::PrintInstancePtr print_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->print(), std::move(print_ptr));
+ OnInstanceReady(arc_bridge_service_->print(), std::move(print_ptr));
}
void ArcBridgeHostImpl::OnProcessInstanceReady(
mojom::ProcessInstancePtr process_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->process(), std::move(process_ptr));
+ OnInstanceReady(arc_bridge_service_->process(), std::move(process_ptr));
}
void ArcBridgeHostImpl::OnStorageManagerInstanceReady(
mojom::StorageManagerInstancePtr storage_manager_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->storage_manager(),
+ OnInstanceReady(arc_bridge_service_->storage_manager(),
std::move(storage_manager_ptr));
}
void ArcBridgeHostImpl::OnTtsInstanceReady(mojom::TtsInstancePtr tts_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->tts(), std::move(tts_ptr));
+ OnInstanceReady(arc_bridge_service_->tts(), std::move(tts_ptr));
}
void ArcBridgeHostImpl::OnVideoInstanceReady(
mojom::VideoInstancePtr video_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->video(), std::move(video_ptr));
+ OnInstanceReady(arc_bridge_service_->video(), std::move(video_ptr));
}
void ArcBridgeHostImpl::OnWallpaperInstanceReady(
mojom::WallpaperInstancePtr wallpaper_ptr) {
- OnInstanceReady(ArcBridgeService::Get()->wallpaper(),
- std::move(wallpaper_ptr));
+ OnInstanceReady(arc_bridge_service_->wallpaper(), std::move(wallpaper_ptr));
}
void ArcBridgeHostImpl::OnClosed() {
diff --git a/chromium/components/arc/arc_bridge_host_impl.h b/chromium/components/arc/arc_bridge_host_impl.h
index b99a4953f2f..b42072583a4 100644
--- a/chromium/components/arc/arc_bridge_host_impl.h
+++ b/chromium/components/arc/arc_bridge_host_impl.h
@@ -17,6 +17,8 @@
namespace arc {
+class ArcBridgeService;
+
// Implementation of the ArcBridgeHost.
// The lifetime of ArcBridgeHost and ArcBridgeInstance mojo channels are tied
// to this instance. Also, any ARC related Mojo channel will be closed if
@@ -31,7 +33,8 @@ class ArcBridgeHostImpl : public mojom::ArcBridgeHost {
// Interface to keep the Mojo channel InterfacePtr.
class MojoChannel;
- explicit ArcBridgeHostImpl(mojom::ArcBridgeInstancePtr instance);
+ ArcBridgeHostImpl(ArcBridgeService* arc_bridge_service,
+ mojom::ArcBridgeInstancePtr instance);
~ArcBridgeHostImpl() override;
// ArcBridgeHost overrides.
@@ -86,6 +89,9 @@ class ArcBridgeHostImpl : public mojom::ArcBridgeHost {
base::ThreadChecker thread_checker_;
+ // Owned by ArcServiceManager.
+ ArcBridgeService* const arc_bridge_service_;
+
mojo::Binding<mojom::ArcBridgeHost> binding_;
mojom::ArcBridgeInstancePtr instance_;
diff --git a/chromium/components/arc/arc_bridge_service.cc b/chromium/components/arc/arc_bridge_service.cc
index be3e44d1597..93ce0a909fe 100644
--- a/chromium/components/arc/arc_bridge_service.cc
+++ b/chromium/components/arc/arc_bridge_service.cc
@@ -11,6 +11,7 @@
#include "base/sequenced_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chromeos/chromeos_switches.h"
+#include "components/user_manager/user_manager.h"
namespace arc {
@@ -21,30 +22,9 @@ const base::Feature kArcEnabledFeature{"EnableARC",
} // namespace
-// Weak pointer. This class is owned by ArcServiceManager.
-ArcBridgeService* g_arc_bridge_service = nullptr;
+ArcBridgeService::ArcBridgeService() = default;
-ArcBridgeService::ArcBridgeService()
- : state_(State::STOPPED),
- stop_reason_(StopReason::SHUTDOWN),
- weak_factory_(this) {}
-
-ArcBridgeService::~ArcBridgeService() {
- DCHECK(CalledOnValidThread());
- DCHECK(state() == State::STOPPING || state() == State::STOPPED);
-}
-
-// static
-ArcBridgeService* ArcBridgeService::Get() {
- if (!g_arc_bridge_service) {
- // ArcBridgeService may be indirectly referenced in unit tests where
- // ArcBridgeService is optional.
- LOG(ERROR) << "ArcBridgeService is not ready.";
- return nullptr;
- }
- DCHECK(g_arc_bridge_service->CalledOnValidThread());
- return g_arc_bridge_service;
-}
+ArcBridgeService::~ArcBridgeService() = default;
// static
bool ArcBridgeService::GetEnabled(const base::CommandLine* command_line) {
@@ -53,60 +33,20 @@ bool ArcBridgeService::GetEnabled(const base::CommandLine* command_line) {
base::FeatureList::IsEnabled(kArcEnabledFeature));
}
-// static
-bool ArcBridgeService::GetAvailable(const base::CommandLine* command_line) {
- return command_line->HasSwitch(chromeos::switches::kArcAvailable);
+bool ArcBridgeService::GetKioskEnabled(const base::CommandLine* command_line) {
+ return GetEnabled(command_line) ||
+ command_line->HasSwitch(chromeos::switches::kArcAvailable);
}
-void ArcBridgeService::AddObserver(Observer* observer) {
- DCHECK(CalledOnValidThread());
- observer_list_.AddObserver(observer);
+bool ArcBridgeService::GetKioskStarted(const base::CommandLine* command_line) {
+ return user_manager::UserManager::IsInitialized() &&
+ user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp() &&
+ GetKioskEnabled(command_line);
}
-void ArcBridgeService::RemoveObserver(Observer* observer) {
- DCHECK(CalledOnValidThread());
- observer_list_.RemoveObserver(observer);
-}
-
-void ArcBridgeService::SetState(State state) {
- DCHECK(CalledOnValidThread());
- DCHECK_NE(state_, state);
- state_ = state;
- VLOG(2) << "State: " << static_cast<uint32_t>(state_);
- if (state_ == State::READY) {
- for (auto& observer : observer_list())
- observer.OnBridgeReady();
- } else if (state == State::STOPPED) {
- for (auto& observer : observer_list())
- observer.OnBridgeStopped(stop_reason_);
- }
-}
-
-void ArcBridgeService::SetStopReason(StopReason stop_reason) {
- DCHECK(CalledOnValidThread());
- stop_reason_ = stop_reason;
-}
-
-bool ArcBridgeService::CalledOnValidThread() {
- return thread_checker_.CalledOnValidThread();
-}
-
-std::ostream& operator<<(
- std::ostream& os, ArcBridgeService::StopReason reason) {
- switch (reason) {
-#define CASE_IMPL(val) \
- case ArcBridgeService::StopReason::val: \
- return os << #val
-
- CASE_IMPL(SHUTDOWN);
- CASE_IMPL(GENERIC_BOOT_FAILURE);
- CASE_IMPL(LOW_DISK_SPACE);
- CASE_IMPL(CRASH);
-#undef CASE_IMPL
- }
-
- // In case of unexpected value, output the int value.
- return os << "StopReason(" << static_cast<int>(reason) << ")";
+// static
+bool ArcBridgeService::GetAvailable(const base::CommandLine* command_line) {
+ return command_line->HasSwitch(chromeos::switches::kArcAvailable);
}
} // namespace arc
diff --git a/chromium/components/arc/arc_bridge_service.h b/chromium/components/arc/arc_bridge_service.h
index acd3c001df5..e368eae6d18 100644
--- a/chromium/components/arc/arc_bridge_service.h
+++ b/chromium/components/arc/arc_bridge_service.h
@@ -5,15 +5,10 @@
#ifndef COMPONENTS_ARC_ARC_BRIDGE_SERVICE_H_
#define COMPONENTS_ARC_ARC_BRIDGE_SERVICE_H_
-#include <iosfwd>
-#include <string>
-#include <vector>
+#include <memory>
-#include "base/files/scoped_file.h"
-#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/observer_list.h"
-#include "base/values.h"
#include "components/arc/instance_holder.h"
namespace base {
@@ -22,8 +17,6 @@ class CommandLine;
namespace arc {
-class ArcBridgeTest;
-
namespace mojom {
// Instead of including components/arc/common/arc_bridge.mojom.h, list all the
@@ -55,73 +48,24 @@ class WallpaperInstance;
} // namespace mojom
-// The Chrome-side service that handles ARC instances and ARC bridge creation.
-// This service handles the lifetime of ARC instances and sets up the
-// communication channel (the ARC bridge) used to send and receive messages.
+// Holds Mojo channels which proxy to ARC side implementation. The actual
+// instances are set/removed via ArcBridgeHostImpl.
class ArcBridgeService {
public:
- // Describes the reason the bridge is stopped.
- enum class StopReason {
- // ARC instance has been gracefully shut down.
- SHUTDOWN,
-
- // Errors occurred during the ARC instance boot. This includes any failures
- // before the instance is actually attempted to be started, and also
- // failures on bootstrapping IPC channels with Android.
- GENERIC_BOOT_FAILURE,
-
- // The device is critically low on disk space.
- LOW_DISK_SPACE,
-
- // ARC instance has crashed.
- CRASH,
- };
-
- // Notifies life cycle events of ArcBridgeService.
- class Observer {
- public:
- // Called whenever the state of the bridge has changed.
- virtual void OnBridgeReady() {}
- virtual void OnBridgeStopped(StopReason reason) {}
-
- protected:
- virtual ~Observer() {}
- };
-
- virtual ~ArcBridgeService();
-
- // Gets the global instance of the ARC Bridge Service. This can only be
- // called on the thread that this class was created on.
- static ArcBridgeService* Get();
+ ArcBridgeService();
+ ~ArcBridgeService();
- // Return true if ARC has been enabled through a commandline
- // switch.
+ // Returns true if ARC has been enabled through a commandline switch.
static bool GetEnabled(const base::CommandLine* command_line);
- // Return true if ARC is available on the current board.
- static bool GetAvailable(const base::CommandLine* command_line);
+ // Returns true if ARC Kiosk has been enabled through a commandline switch.
+ static bool GetKioskEnabled(const base::CommandLine* command_line);
- // HandleStartup() should be called upon profile startup. This will only
- // launch an instance if the instance is enabled.
- // This can only be called on the thread that this class was created on.
+ // Returns true if ARC Kiosk session is started.
+ static bool GetKioskStarted(const base::CommandLine* command_line);
- // Starts the ARC service, then it will connect the Mojo channel. When the
- // bridge becomes ready, OnBridgeReady() is called.
- virtual void RequestStart() = 0;
-
- // Stops the ARC service.
- virtual void RequestStop() = 0;
-
- // 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
- // when this function is called, MessageLoop is no longer exists.
- virtual void OnShutdown() = 0;
-
- // Adds or removes observers. This can only be called on the thread that this
- // class was created on. RemoveObserver does nothing if |observer| is not in
- // the list.
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
+ // Returns true if ARC is available on the current board.
+ static bool GetAvailable(const base::CommandLine* command_line);
InstanceHolder<mojom::AppInstance>* app() { return &app_; }
InstanceHolder<mojom::AudioInstance>* audio() { return &audio_; }
@@ -164,52 +108,7 @@ class ArcBridgeService {
InstanceHolder<mojom::VideoInstance>* video() { return &video_; }
InstanceHolder<mojom::WallpaperInstance>* wallpaper() { return &wallpaper_; }
- // Gets if ARC is currently running.
- bool ready() const { return state() == State::READY; }
-
- // Gets if ARC is currently stopped. This is not exactly !ready() since there
- // are transient states between ready() and stopped().
- bool stopped() const { return state() == State::STOPPED; }
-
- protected:
- // The possible states of the bridge. In the normal flow, the state changes
- // in the following sequence:
- //
- // STOPPED
- // PrerequisitesChanged() ->
- // CONNECTING
- // OnConnectionEstablished() ->
- // READY
- //
- // The ArcSession state machine can be thought of being substates of
- // ArcBridgeService's CONNECTING state.
- //
- // *
- // StopInstance() ->
- // STOPPING
- // OnStopped() ->
- // STOPPED
- enum class State {
- // ARC is not currently running.
- STOPPED,
-
- // The request to connect has been sent.
- CONNECTING,
-
- // The instance has started, and the bridge is fully established.
- CONNECTED,
-
- // The ARC instance has finished initializing and is now ready for user
- // interaction.
- READY,
-
- // The ARC instance has started shutting down.
- STOPPING,
- };
-
- ArcBridgeService();
-
- // Instance holders.
+ private:
InstanceHolder<mojom::AppInstance> app_;
InstanceHolder<mojom::AudioInstance> audio_;
InstanceHolder<mojom::AuthInstance> auth_;
@@ -235,50 +134,9 @@ class ArcBridgeService {
InstanceHolder<mojom::VideoInstance> video_;
InstanceHolder<mojom::WallpaperInstance> wallpaper_;
- // Gets the current state of the bridge service.
- State state() const { return state_; }
-
- // Changes the current state and notifies all observers.
- void SetState(State state);
-
- // Sets the reason the bridge is stopped. This function must be always called
- // before SetState(State::STOPPED) to report a correct reason with
- // Observer::OnBridgeStopped().
- void SetStopReason(StopReason stop_reason);
-
- base::ObserverList<Observer>& observer_list() { return observer_list_; }
-
- bool CalledOnValidThread();
-
- private:
- friend class ArcBridgeTest;
- FRIEND_TEST_ALL_PREFIXES(ArcBridgeTest, Basic);
- FRIEND_TEST_ALL_PREFIXES(ArcBridgeTest, Prerequisites);
- FRIEND_TEST_ALL_PREFIXES(ArcBridgeTest, StopMidStartup);
- FRIEND_TEST_ALL_PREFIXES(ArcBridgeTest, Restart);
- FRIEND_TEST_ALL_PREFIXES(ArcBridgeTest, OnBridgeStopped);
- FRIEND_TEST_ALL_PREFIXES(ArcBridgeTest, Shutdown);
-
- base::ObserverList<Observer> observer_list_;
-
- base::ThreadChecker thread_checker_;
-
- // The current state of the bridge.
- ArcBridgeService::State state_;
-
- // The reason the bridge is stopped.
- StopReason stop_reason_;
-
- // WeakPtrFactory to use callbacks.
- base::WeakPtrFactory<ArcBridgeService> weak_factory_;
-
DISALLOW_COPY_AND_ASSIGN(ArcBridgeService);
};
-// Defines "<<" operator for LOGging purpose.
-std::ostream& operator<<(
- std::ostream& os, ArcBridgeService::StopReason reason);
-
} // namespace arc
#endif // COMPONENTS_ARC_ARC_BRIDGE_SERVICE_H_
diff --git a/chromium/components/arc/arc_bridge_service_impl.cc b/chromium/components/arc/arc_bridge_service_impl.cc
deleted file mode 100644
index d426b6389e6..00000000000
--- a/chromium/components/arc/arc_bridge_service_impl.cc
+++ /dev/null
@@ -1,173 +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/arc/arc_bridge_service_impl.h"
-
-#include <string>
-#include <utility>
-
-#include "base/command_line.h"
-#include "base/json/json_writer.h"
-#include "base/sequenced_task_runner.h"
-#include "base/sys_info.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "chromeos/chromeos_switches.h"
-#include "chromeos/dbus/dbus_method_call_status.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-
-namespace arc {
-
-extern ArcBridgeService* g_arc_bridge_service;
-
-namespace {
-constexpr int64_t kReconnectDelayInSeconds = 5;
-} // namespace
-
-ArcBridgeServiceImpl::ArcBridgeServiceImpl(
- const scoped_refptr<base::TaskRunner>& blocking_task_runner)
- : session_started_(false),
- factory_(base::Bind(ArcSession::Create, blocking_task_runner)),
- weak_factory_(this) {
- DCHECK(!g_arc_bridge_service);
- g_arc_bridge_service = this;
-}
-
-ArcBridgeServiceImpl::~ArcBridgeServiceImpl() {
- if (arc_session_)
- arc_session_->RemoveObserver(this);
-
- DCHECK(g_arc_bridge_service == this);
- g_arc_bridge_service = nullptr;
-}
-
-void ArcBridgeServiceImpl::RequestStart() {
- DCHECK(CalledOnValidThread());
- if (session_started_)
- return;
- VLOG(1) << "Session started";
- session_started_ = true;
- PrerequisitesChanged();
-}
-
-void ArcBridgeServiceImpl::RequestStop() {
- DCHECK(CalledOnValidThread());
- if (!session_started_)
- return;
- VLOG(1) << "Session ended";
- session_started_ = false;
- PrerequisitesChanged();
-}
-
-void ArcBridgeServiceImpl::OnShutdown() {
- DCHECK(CalledOnValidThread());
- VLOG(1) << "OnShutdown";
- if (!session_started_)
- return;
- session_started_ = false;
- reconnect_ = false;
- if (arc_session_)
- arc_session_->OnShutdown();
-}
-
-void ArcBridgeServiceImpl::SetArcSessionFactoryForTesting(
- const ArcSessionFactory& factory) {
- DCHECK(!factory.is_null());
- factory_ = factory;
-}
-
-void ArcBridgeServiceImpl::DisableReconnectDelayForTesting() {
- use_delay_before_reconnecting_ = false;
-}
-
-void ArcBridgeServiceImpl::PrerequisitesChanged() {
- DCHECK(CalledOnValidThread());
- VLOG(1) << "Prerequisites changed. "
- << "state=" << static_cast<uint32_t>(state())
- << ", session_started=" << session_started_;
- if (state() == State::STOPPED) {
- if (!session_started_)
- return;
- VLOG(0) << "Prerequisites met, starting ARC";
- SetStopReason(StopReason::SHUTDOWN);
-
- if (arc_session_)
- arc_session_->RemoveObserver(this);
-
- SetState(State::CONNECTING);
- arc_session_ = factory_.Run();
- arc_session_->AddObserver(this);
- arc_session_->Start();
- } else {
- if (session_started_)
- return;
- VLOG(0) << "Prerequisites stopped being met, stopping ARC";
- StopInstance();
- }
-}
-
-void ArcBridgeServiceImpl::StopInstance() {
- DCHECK(CalledOnValidThread());
- if (state() == State::STOPPED || state() == State::STOPPING) {
- VLOG(1) << "StopInstance() called when ARC is not running";
- return;
- }
-
- // We were explicitly asked to stop, so do not reconnect.
- reconnect_ = false;
-
- VLOG(1) << "Stopping ARC";
- DCHECK(arc_session_.get());
- SetState(State::STOPPING);
-
- // Note: this can call OnStopped() internally as a callback.
- arc_session_->Stop();
-}
-
-void ArcBridgeServiceImpl::OnReady() {
- DCHECK(CalledOnValidThread());
- if (state() != State::CONNECTING) {
- VLOG(1) << "StopInstance() called while connecting";
- return;
- }
-
- // The container can be considered to have been successfully launched, so
- // restart if the connection goes down without being requested.
- reconnect_ = true;
- VLOG(0) << "ARC ready";
- SetState(State::READY);
-}
-
-void ArcBridgeServiceImpl::OnStopped(StopReason stop_reason) {
- DCHECK(CalledOnValidThread());
- VLOG(0) << "ARC stopped: " << stop_reason;
- arc_session_->RemoveObserver(this);
- arc_session_.reset();
- SetStopReason(stop_reason);
- SetState(State::STOPPED);
-
- if (reconnect_) {
- // There was a previous invocation and it crashed for some reason. Try
- // starting the container again.
- reconnect_ = false;
- VLOG(0) << "ARC reconnecting";
- if (use_delay_before_reconnecting_) {
- // Instead of immediately trying to restart the container, give it some
- // time to finish tearing down in case it is still in the process of
- // stopping.
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, base::Bind(&ArcBridgeServiceImpl::PrerequisitesChanged,
- weak_factory_.GetWeakPtr()),
- base::TimeDelta::FromSeconds(kReconnectDelayInSeconds));
- } else {
- // Restart immediately.
- PrerequisitesChanged();
- }
- }
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/arc_bridge_service_impl.h b/chromium/components/arc/arc_bridge_service_impl.h
deleted file mode 100644
index 8d89e32722f..00000000000
--- a/chromium/components/arc/arc_bridge_service_impl.h
+++ /dev/null
@@ -1,97 +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_ARC_ARC_BRIDGE_SERVICE_IMPL_H_
-#define COMPONENTS_ARC_ARC_BRIDGE_SERVICE_IMPL_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/files/scoped_file.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/task_runner.h"
-#include "components/arc/arc_bridge_service.h"
-#include "components/arc/arc_session.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace base {
-class SequencedTaskRunner;
-class SingleThreadTaskRunner;
-} // namespace base
-
-namespace arc {
-
-// Real IPC based ArcBridgeService that is used in production.
-class ArcBridgeServiceImpl : public ArcBridgeService,
- public ArcSession::Observer {
- public:
- // This is the factory interface to inject ArcSession instance
- // for testing purpose.
- using ArcSessionFactory = base::Callback<std::unique_ptr<ArcSession>()>;
-
- explicit ArcBridgeServiceImpl(
- const scoped_refptr<base::TaskRunner>& blocking_task_runner);
- ~ArcBridgeServiceImpl() override;
-
- // ArcBridgeService overrides:
- void RequestStart() override;
- void RequestStop() override;
- void OnShutdown() override;
-
- // Inject a factory to create ArcSession instance for testing purpose.
- // |factory| must not be null.
- void SetArcSessionFactoryForTesting(const ArcSessionFactory& factory);
-
- // Returns the current ArcSession instance for testing purpose.
- ArcSession* GetArcSessionForTesting() { return arc_session_.get(); }
-
- // Normally, reconnecting after connection shutdown happens after a short
- // delay. When testing, however, we'd like it to happen immediately to avoid
- // adding unnecessary delays.
- void DisableReconnectDelayForTesting();
-
- private:
- friend class ArcBridgeTest;
- FRIEND_TEST_ALL_PREFIXES(ArcBridgeTest, Restart);
- FRIEND_TEST_ALL_PREFIXES(ArcBridgeTest, OnBridgeStopped);
-
- // If all pre-requisites are true (ARC is available, it has been enabled, and
- // the session has started), and ARC is stopped, start ARC. If ARC is running
- // and the pre-requisites stop being true, stop ARC.
- void PrerequisitesChanged();
-
- // Stops the running instance.
- void StopInstance();
-
- // ArcSession::Observer:
- void OnReady() override;
- void OnStopped(StopReason reason) override;
-
- std::unique_ptr<ArcSession> arc_session_;
-
- // If the user's session has started.
- bool session_started_;
-
- // If the instance had already been started but the connection to it was
- // lost. This should make the instance restart.
- bool reconnect_ = false;
-
- // Delay the reconnection.
- bool use_delay_before_reconnecting_ = true;
-
- // Factory to inject a fake ArcSession instance for testing.
- ArcSessionFactory factory_;
-
- // WeakPtrFactory to use callbacks.
- base::WeakPtrFactory<ArcBridgeServiceImpl> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(ArcBridgeServiceImpl);
-};
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_ARC_BRIDGE_SERVICE_IMPL_H_
diff --git a/chromium/components/arc/arc_bridge_service_unittest.cc b/chromium/components/arc/arc_bridge_service_unittest.cc
deleted file mode 100644
index e0ab45ddc61..00000000000
--- a/chromium/components/arc/arc_bridge_service_unittest.cc
+++ /dev/null
@@ -1,214 +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 <memory>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "components/arc/arc_bridge_service_impl.h"
-#include "components/arc/test/fake_arc_session.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace arc {
-
-namespace {
-
-class DummyObserver : public ArcBridgeService::Observer {};
-
-} // namespace
-
-// TODO(hidehiko): ArcBridgeTest gets complicated and has stale code.
-// Simplify the code.
-class ArcBridgeTest : public testing::Test,
- public ArcBridgeService::Observer {
- public:
- ArcBridgeTest() = default;
-
- void OnBridgeReady() override {
- state_ = ArcBridgeService::State::READY;
- ready_ = true;
- }
-
- void OnBridgeStopped(ArcBridgeService::StopReason stop_reason) override {
- // The instance is already destructed in ArcBridgeServiceImpl::OnStopped().
- state_ = ArcBridgeService::State::STOPPED;
- stop_reason_ = stop_reason;
- message_loop_.task_runner()->PostTask(FROM_HERE,
- message_loop_.QuitWhenIdleClosure());
- }
-
- bool ready() const { return ready_; }
- ArcBridgeService::State state() const { return state_; }
- FakeArcSession* arc_session() const {
- return static_cast<FakeArcSession*>(service_->GetArcSessionForTesting());
- }
-
- protected:
- std::unique_ptr<ArcBridgeServiceImpl> service_;
- ArcBridgeService::StopReason stop_reason_;
-
- static std::unique_ptr<ArcSession> CreateSuspendedArcSession() {
- auto arc_session = base::MakeUnique<FakeArcSession>();
- arc_session->SuspendBoot();
- return std::move(arc_session);
- }
-
- static std::unique_ptr<ArcSession> CreateBootFailureArcSession(
- ArcBridgeService::StopReason reason) {
- auto arc_session = base::MakeUnique<FakeArcSession>();
- arc_session->EnableBootFailureEmulation(reason);
- return std::move(arc_session);
- }
-
- private:
- void SetUp() override {
- chromeos::DBusThreadManager::Initialize();
-
- ready_ = false;
- state_ = ArcBridgeService::State::STOPPED;
- stop_reason_ = ArcBridgeService::StopReason::SHUTDOWN;
-
- // We inject FakeArcSession here so we do not need task_runner.
- service_.reset(new ArcBridgeServiceImpl(nullptr));
- service_->SetArcSessionFactoryForTesting(
- base::Bind(FakeArcSession::Create));
- service_->AddObserver(this);
- }
-
- void TearDown() override {
- service_->RemoveObserver(this);
- service_.reset();
-
- chromeos::DBusThreadManager::Shutdown();
- }
-
- bool ready_ = false;
- ArcBridgeService::State state_;
- base::MessageLoopForUI message_loop_;
-
- DISALLOW_COPY_AND_ASSIGN(ArcBridgeTest);
-};
-
-// Exercises the basic functionality of the ARC Bridge Service. A message from
-// within the instance should cause the observer to be notified.
-TEST_F(ArcBridgeTest, Basic) {
- ASSERT_FALSE(ready());
- ASSERT_EQ(ArcBridgeService::State::STOPPED, state());
-
- service_->RequestStart();
- ASSERT_EQ(ArcBridgeService::State::READY, state());
-
- service_->RequestStop();
- ASSERT_EQ(ArcBridgeService::State::STOPPED, state());
-}
-
-// If the ArcBridgeService is shut down, it should be stopped, even
-// mid-startup.
-TEST_F(ArcBridgeTest, StopMidStartup) {
- ASSERT_FALSE(ready());
-
- service_->SetArcSessionFactoryForTesting(
- base::Bind(ArcBridgeTest::CreateSuspendedArcSession));
- service_->RequestStart();
- ASSERT_FALSE(service_->stopped());
- ASSERT_FALSE(service_->ready());
-
- service_->RequestStop();
- ASSERT_EQ(ArcBridgeService::State::STOPPED, state());
-}
-
-// If the boot procedure is failed, then restarting mechanism should not
-// triggered.
-TEST_F(ArcBridgeTest, BootFailure) {
- ASSERT_TRUE(service_->stopped());
-
- service_->SetArcSessionFactoryForTesting(
- base::Bind(ArcBridgeTest::CreateBootFailureArcSession,
- ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE));
- service_->RequestStart();
- EXPECT_EQ(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE, stop_reason_);
- ASSERT_TRUE(service_->stopped());
-}
-
-// If the instance is stopped, it should be re-started.
-TEST_F(ArcBridgeTest, Restart) {
- ASSERT_FALSE(ready());
-
- service_->RequestStart();
- ASSERT_EQ(ArcBridgeService::State::READY, state());
-
- // Simulate a connection loss.
- service_->DisableReconnectDelayForTesting();
- ASSERT_TRUE(arc_session());
- arc_session()->StopWithReason(ArcBridgeService::StopReason::CRASH);
- ASSERT_TRUE(service_->ready());
-
- service_->RequestStop();
- ASSERT_EQ(ArcBridgeService::State::STOPPED, state());
-}
-
-// Makes sure OnBridgeStopped is called on stop.
-TEST_F(ArcBridgeTest, OnBridgeStopped) {
- ASSERT_FALSE(ready());
-
- service_->DisableReconnectDelayForTesting();
- service_->RequestStart();
- ASSERT_EQ(ArcBridgeService::State::READY, state());
-
- // Simulate boot failure.
- ASSERT_TRUE(arc_session());
- arc_session()->StopWithReason(
- ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
- EXPECT_EQ(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE, stop_reason_);
- ASSERT_TRUE(service_->ready());
-
- // Simulate crash.
- ASSERT_TRUE(arc_session());
- arc_session()->StopWithReason(ArcBridgeService::StopReason::CRASH);
- EXPECT_EQ(ArcBridgeService::StopReason::CRASH, stop_reason_);
- ASSERT_TRUE(service_->ready());
-
- // Graceful stop.
- service_->RequestStop();
- ASSERT_EQ(ArcBridgeService::StopReason::SHUTDOWN, stop_reason_);
- ASSERT_EQ(ArcBridgeService::State::STOPPED, state());
-}
-
-TEST_F(ArcBridgeTest, Shutdown) {
- ASSERT_FALSE(ready());
-
- service_->DisableReconnectDelayForTesting();
- service_->RequestStart();
- ASSERT_EQ(ArcBridgeService::State::READY, state());
-
- // Simulate shutdown.
- service_->OnShutdown();
- ASSERT_EQ(ArcBridgeService::StopReason::SHUTDOWN, stop_reason_);
- ASSERT_EQ(ArcBridgeService::State::STOPPED, state());
-}
-
-// Removing the same observer more than once should be okay.
-TEST_F(ArcBridgeTest, RemoveObserverTwice) {
- ASSERT_FALSE(ready());
- auto dummy_observer = base::MakeUnique<DummyObserver>();
- service_->AddObserver(dummy_observer.get());
- // Call RemoveObserver() twice.
- service_->RemoveObserver(dummy_observer.get());
- service_->RemoveObserver(dummy_observer.get());
-}
-
-// Removing an unknown observer should be allowed.
-TEST_F(ArcBridgeTest, RemoveUnknownObserver) {
- ASSERT_FALSE(ready());
- auto dummy_observer = base::MakeUnique<DummyObserver>();
- service_->RemoveObserver(dummy_observer.get());
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/arc_service_manager.cc b/chromium/components/arc/arc_service_manager.cc
index 6d6a25f5fdf..00177619c7c 100644
--- a/chromium/components/arc/arc_service_manager.cc
+++ b/chromium/components/arc/arc_service_manager.cc
@@ -4,84 +4,42 @@
#include "components/arc/arc_service_manager.h"
-#include <utility>
-
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
-#include "base/sequenced_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/task_runner.h"
#include "components/arc/arc_bridge_service.h"
-#include "components/arc/arc_bridge_service_impl.h"
-#include "components/arc/audio/arc_audio_bridge.h"
-#include "components/arc/bluetooth/arc_bluetooth_bridge.h"
-#include "components/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h"
-#include "components/arc/clipboard/arc_clipboard_bridge.h"
-#include "components/arc/crash_collector/arc_crash_collector_bridge.h"
-#include "components/arc/ime/arc_ime_service.h"
-#include "components/arc/intent_helper/activity_icon_loader.h"
-#include "components/arc/kiosk/arc_kiosk_bridge.h"
-#include "components/arc/metrics/arc_metrics_service.h"
-#include "components/arc/net/arc_net_host_impl.h"
-#include "components/arc/obb_mounter/arc_obb_mounter_bridge.h"
-#include "components/arc/power/arc_power_bridge.h"
-#include "components/arc/storage_manager/arc_storage_manager.h"
-#include "components/arc/user_data/arc_user_data_service.h"
-#include "components/prefs/pref_member.h"
-#include "ui/arc/notification/arc_notification_manager.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 {
-// Weak pointer. This class is owned by ChromeBrowserMainPartsChromeos.
+// Weak pointer. This class is owned by arc::ArcServiceLauncher.
ArcServiceManager* g_arc_service_manager = nullptr;
-// This pointer is owned by ArcServiceManager.
-ArcBridgeService* g_arc_bridge_service_for_testing = nullptr;
-
} // namespace
ArcServiceManager::ArcServiceManager(
scoped_refptr<base::TaskRunner> blocking_task_runner)
: blocking_task_runner_(blocking_task_runner),
+ arc_bridge_service_(base::MakeUnique<ArcBridgeService>()),
icon_loader_(new ActivityIconLoader()),
activity_resolver_(new LocalActivityResolver()) {
DCHECK(!g_arc_service_manager);
g_arc_service_manager = this;
-
- if (g_arc_bridge_service_for_testing) {
- arc_bridge_service_.reset(g_arc_bridge_service_for_testing);
- g_arc_bridge_service_for_testing = nullptr;
- } else {
- arc_bridge_service_.reset(new ArcBridgeServiceImpl(blocking_task_runner));
- }
-
- AddService(base::MakeUnique<ArcAudioBridge>(arc_bridge_service()));
- AddService(base::MakeUnique<ArcBluetoothBridge>(arc_bridge_service()));
- AddService(base::MakeUnique<ArcBootPhaseMonitorBridge>(arc_bridge_service()));
- AddService(base::MakeUnique<ArcClipboardBridge>(arc_bridge_service()));
- AddService(base::MakeUnique<ArcCrashCollectorBridge>(arc_bridge_service(),
- blocking_task_runner_));
- AddService(base::MakeUnique<ArcImeService>(arc_bridge_service()));
- AddService(base::MakeUnique<ArcKioskBridge>(arc_bridge_service()));
- AddService(base::MakeUnique<ArcMetricsService>(arc_bridge_service()));
- AddService(base::MakeUnique<ArcNetHostImpl>(arc_bridge_service()));
- AddService(base::MakeUnique<ArcObbMounterBridge>(arc_bridge_service()));
- AddService(base::MakeUnique<ArcPowerBridge>(arc_bridge_service()));
- AddService(base::MakeUnique<ArcStorageManager>(arc_bridge_service()));
}
ArcServiceManager::~ArcServiceManager() {
DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(g_arc_service_manager == this);
+ DCHECK_EQ(g_arc_service_manager, this);
g_arc_service_manager = nullptr;
- if (g_arc_bridge_service_for_testing) {
- delete g_arc_bridge_service_for_testing;
- }
}
// static
ArcServiceManager* ArcServiceManager::Get() {
- DCHECK(g_arc_service_manager);
+ if (!g_arc_service_manager)
+ return nullptr;
DCHECK(g_arc_service_manager->thread_checker_.CalledOnValidThread());
return g_arc_service_manager;
}
@@ -91,34 +49,39 @@ ArcBridgeService* ArcServiceManager::arc_bridge_service() {
return arc_bridge_service_.get();
}
-void ArcServiceManager::AddService(std::unique_ptr<ArcService> service) {
+bool ArcServiceManager::AddServiceInternal(
+ const std::string& name,
+ std::unique_ptr<ArcService> service) {
DCHECK(thread_checker_.CalledOnValidThread());
-
- services_.emplace_back(std::move(service));
+ if (!name.empty() && services_.count(name) != 0) {
+ LOG(ERROR) << "Ignoring registration of service with duplicate name: "
+ << name;
+ return false;
+ }
+ services_.insert(std::make_pair(name, std::move(service)));
+ return true;
}
-void ArcServiceManager::OnPrimaryUserProfilePrepared(
- const AccountId& account_id,
- std::unique_ptr<BooleanPrefMember> arc_enabled_pref) {
+ArcService* ArcServiceManager::GetNamedServiceInternal(
+ const std::string& name) {
DCHECK(thread_checker_.CalledOnValidThread());
- AddService(base::MakeUnique<ArcNotificationManager>(arc_bridge_service(),
- account_id));
+ if (name.empty()) {
+ LOG(ERROR) << "kArcServiceName[] should be a fully-qualified class name.";
+ return nullptr;
+ }
+ auto service = services_.find(name);
+ if (service == services_.end()) {
+ LOG(ERROR) << "Named service " << name << " not found";
+ return nullptr;
+ }
+ return service->second.get();
}
void ArcServiceManager::Shutdown() {
+ DCHECK(thread_checker_.CalledOnValidThread());
icon_loader_ = nullptr;
activity_resolver_ = nullptr;
services_.clear();
- arc_bridge_service_->OnShutdown();
-}
-
-// static
-void ArcServiceManager::SetArcBridgeServiceForTesting(
- std::unique_ptr<ArcBridgeService> arc_bridge_service) {
- if (g_arc_bridge_service_for_testing) {
- delete g_arc_bridge_service_for_testing;
- }
- g_arc_bridge_service_for_testing = arc_bridge_service.release();
}
} // namespace arc
diff --git a/chromium/components/arc/arc_service_manager.h b/chromium/components/arc/arc_service_manager.h
index 256a1992eb9..31d2b23b592 100644
--- a/chromium/components/arc/arc_service_manager.h
+++ b/chromium/components/arc/arc_service_manager.h
@@ -6,6 +6,10 @@
#define COMPONENTS_ARC_ARC_SERVICE_MANAGER_H_
#include <memory>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
#include <vector>
#include "base/macros.h"
@@ -14,38 +18,88 @@
#include "base/threading/thread_checker.h"
#include "components/arc/intent_helper/activity_icon_loader.h"
#include "components/arc/intent_helper/local_activity_resolver.h"
-#include "components/prefs/pref_member.h"
-#include "components/signin/core/account_id/account_id.h"
namespace arc {
class ArcBridgeService;
class ArcService;
+namespace internal {
+
+// If an ArcService is declared with a name, e.g.:
+//
+// class MyArcService : public ArcService {
+// public:
+// static const char kArcServiceName[];
+// ...
+// };
+//
+// it can then be retrieved from ArcServiceManager in a type-safe way using
+// GetService<T>(). This two functions allow AddService() to get the name only
+// if it was provided, or use an empty string otherwise.
+//
+// Although the typename is always specified explicitly by the caller, the
+// parameter is required in order for SFINAE to work correctly. It is not used
+// and can be nullptr, though.
+//
+// In order to avoid collisions, kArcServiceName should be the fully-qualified
+// name of the class.
+template <typename T>
+decltype(T::kArcServiceName, std::string()) GetArcServiceName(T* unused) {
+ if (strlen(T::kArcServiceName) == 0)
+ LOG(ERROR) << "kArcServiceName[] should be a fully-qualified class name.";
+ return T::kArcServiceName;
+}
+
+template <typename T>
+std::string GetArcServiceName(...) {
+ return std::string();
+}
+
+} // namespace internal
+
// Manages creation and destruction of services that communicate with the ARC
// instance via the ArcBridgeService.
class ArcServiceManager {
public:
explicit ArcServiceManager(
scoped_refptr<base::TaskRunner> blocking_task_runner);
- virtual ~ArcServiceManager();
+ ~ArcServiceManager();
// |arc_bridge_service| can only be accessed on the thread that this
// class was created on.
ArcBridgeService* arc_bridge_service();
- // Adds a service to the managed services list.
- void AddService(std::unique_ptr<ArcService> service);
+ // Adds a service to the managed services list. Returns false if another
+ // named service with that name had already been added.
+ template <typename T>
+ bool AddService(std::unique_ptr<T> service) {
+ return AddServiceInternal(internal::GetArcServiceName<T>(nullptr),
+ std::move(service));
+ }
+
+ // Gets the named service from the managed services list. This uses SFINAE, so
+ // you can only call this function if the service specified by T provides a
+ // static member variable called kArcServiceName[] (otherwise this will not
+ // compile).
+ template <typename T>
+ T* GetService() {
+ return static_cast<T*>(GetNamedServiceInternal(T::kArcServiceName));
+ }
+
+ // Does the same as GetService(), but with the global instance. Return nullptr
+ // when the instance hasn't been created or has already been destructed.
+ template <typename T> static T* GetGlobalService() {
+ auto* service_manager = ArcServiceManager::Get();
+ if (!service_manager)
+ return nullptr;
+ return service_manager->GetService<T>();
+ }
// Gets the global instance of the ARC Service Manager. This can only be
// called on the thread that this class was created on.
static ArcServiceManager* Get();
- // Called when the main profile is initialized after user logs in.
- void OnPrimaryUserProfilePrepared(
- const AccountId& account_id,
- std::unique_ptr<BooleanPrefMember> arc_enabled_pref);
-
// Called to shut down all ARC services.
void Shutdown();
@@ -53,11 +107,6 @@ class ArcServiceManager {
return blocking_task_runner_;
}
- // Set ArcBridgeService instance for testing. Call before ArcServiceManager
- // creation. ArcServiceManager owns |arc_bridge_service|.
- static void SetArcBridgeServiceForTesting(
- std::unique_ptr<ArcBridgeService> arc_bridge_service);
-
// Returns the icon loader owned by ArcServiceManager and shared by services.
scoped_refptr<ActivityIconLoader> icon_loader() { return icon_loader_; }
@@ -67,11 +116,18 @@ class ArcServiceManager {
}
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_;
std::unique_ptr<ArcBridgeService> arc_bridge_service_;
- std::vector<std::unique_ptr<ArcService>> services_;
+ std::unordered_multimap<std::string, std::unique_ptr<ArcService>> services_;
scoped_refptr<ActivityIconLoader> icon_loader_;
scoped_refptr<LocalActivityResolver> activity_resolver_;
diff --git a/chromium/components/arc/arc_service_manager_unittest.cc b/chromium/components/arc/arc_service_manager_unittest.cc
new file mode 100644
index 00000000000..120d5ac343c
--- /dev/null
+++ b/chromium/components/arc/arc_service_manager_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <utility>
+
+#include "base/auto_reset.h"
+#include "base/memory/ptr_util.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_service_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace arc {
+
+namespace {
+
+class AnonymousService : public ArcService {
+ public:
+ AnonymousService(ArcBridgeService* arc_bridge_service, bool* alive)
+ : ArcService(arc_bridge_service), alive_(alive, true) {}
+ ~AnonymousService() override = default;
+
+ private:
+ base::AutoReset<bool> alive_;
+};
+
+class NamedService : public ArcService {
+ public:
+ static const char kArcServiceName[];
+ NamedService(ArcBridgeService* arc_bridge_service, bool* alive)
+ : ArcService(arc_bridge_service), alive_(alive, true) {}
+ ~NamedService() override = default;
+
+ private:
+ base::AutoReset<bool> alive_;
+};
+
+class DifferentNamedService : public ArcService {
+ public:
+ static const char kArcServiceName[];
+ DifferentNamedService(ArcBridgeService* arc_bridge_service, bool* alive)
+ : ArcService(arc_bridge_service), alive_(alive, true) {}
+ ~DifferentNamedService() override = default;
+
+ private:
+ base::AutoReset<bool> alive_;
+};
+
+class EmptyNamedService : public ArcService {
+ public:
+ static const char kArcServiceName[];
+ EmptyNamedService(ArcBridgeService* arc_bridge_service, bool* alive)
+ : ArcService(arc_bridge_service), alive_(alive, true) {}
+ ~EmptyNamedService() override = default;
+
+ private:
+ base::AutoReset<bool> alive_;
+};
+
+const char NamedService::kArcServiceName[] =
+ "arc::(anonymous namespace)::NamedService";
+
+const char DifferentNamedService::kArcServiceName[] =
+ "arc::(anonymous namespace)::DifferentNamedService";
+
+const char EmptyNamedService::kArcServiceName[] = "";
+
+} // namespace
+
+class ArcServiceManagerTest : public testing::Test {
+ public:
+ ArcServiceManagerTest() = default;
+
+ void SetUp() override {
+ arc_bridge_service_ = base::MakeUnique<ArcBridgeService>();
+ }
+
+ void TearDown() override { arc_bridge_service_.reset(); }
+
+ ArcBridgeService* arc_bridge_service() { return arc_bridge_service_.get(); }
+
+ private:
+ std::unique_ptr<ArcBridgeService> arc_bridge_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArcServiceManagerTest);
+};
+
+// Exercises the basic getter functionality of ArcServiceManager.
+TEST_F(ArcServiceManagerTest, BasicGetter) {
+ bool named_service_alive = false;
+
+ // ArcServiceManager is empty, GetService() should return nullptr.
+ auto manager = base::MakeUnique<ArcServiceManager>(nullptr);
+ EXPECT_EQ(nullptr, manager->GetService<NamedService>());
+
+ EXPECT_TRUE(manager->AddService(base::MakeUnique<NamedService>(
+ arc_bridge_service(), &named_service_alive)));
+ EXPECT_TRUE(named_service_alive);
+ EXPECT_NE(nullptr, manager->GetService<NamedService>());
+
+ // Finally, the service should not be alive anymore.
+ manager.reset();
+ EXPECT_FALSE(named_service_alive);
+}
+
+// There is no way to distinguish between anonymous services, so it should be
+// possible to add them twice (not that it's recommended).
+TEST_F(ArcServiceManagerTest, MultipleAnonymousServices) {
+ bool anonymous_service_alive = false;
+ bool second_anonymous_service_alive = false;
+
+ auto manager = base::MakeUnique<ArcServiceManager>(nullptr);
+
+ EXPECT_TRUE(manager->AddService(base::MakeUnique<AnonymousService>(
+ arc_bridge_service(), &anonymous_service_alive)));
+ EXPECT_TRUE(anonymous_service_alive);
+ EXPECT_TRUE(manager->AddService(base::MakeUnique<AnonymousService>(
+ arc_bridge_service(), &second_anonymous_service_alive)));
+ EXPECT_TRUE(second_anonymous_service_alive);
+
+ // Finally, the individual services should not be alive anymore.
+ manager.reset();
+ EXPECT_FALSE(anonymous_service_alive);
+ EXPECT_FALSE(second_anonymous_service_alive);
+}
+
+// Named services can only be added once, but can still be retrieved.
+TEST_F(ArcServiceManagerTest, MultipleNamedServices) {
+ bool named_service_alive = false;
+ bool second_named_service_alive = false;
+ bool different_named_service_alive = false;
+
+ auto manager = base::MakeUnique<ArcServiceManager>(nullptr);
+
+ auto named_service = base::MakeUnique<NamedService>(arc_bridge_service(),
+ &named_service_alive);
+ NamedService* raw_named_service = named_service.get();
+ EXPECT_TRUE(named_service_alive);
+ EXPECT_TRUE(manager->AddService(std::move(named_service)));
+ auto second_named_service = base::MakeUnique<NamedService>(
+ arc_bridge_service(), &second_named_service_alive);
+ EXPECT_TRUE(second_named_service_alive);
+ // This will fail and immediately destroy the service.
+ EXPECT_FALSE(manager->AddService(std::move(second_named_service)));
+ EXPECT_FALSE(second_named_service_alive);
+
+ // We should still be able to add a different-named service.
+ auto different_named_service = base::MakeUnique<DifferentNamedService>(
+ arc_bridge_service(), &different_named_service_alive);
+ DifferentNamedService* raw_different_named_service =
+ different_named_service.get();
+ EXPECT_TRUE(different_named_service_alive);
+ EXPECT_TRUE(manager->AddService(std::move(different_named_service)));
+
+ // And find both.
+ EXPECT_EQ(raw_named_service, manager->GetService<NamedService>());
+ EXPECT_EQ(raw_different_named_service,
+ manager->GetService<DifferentNamedService>());
+
+ manager.reset();
+ EXPECT_FALSE(named_service_alive);
+ EXPECT_FALSE(different_named_service_alive);
+}
+
+// Named services with an empty name are treated as anonymous services.
+// Developers shouldn't do that, though, and will trigger an error log.
+TEST_F(ArcServiceManagerTest, EmptyNamedServices) {
+ bool empty_named_service_alive = false;
+
+ auto manager = base::MakeUnique<ArcServiceManager>(nullptr);
+
+ EXPECT_TRUE(manager->AddService(base::MakeUnique<EmptyNamedService>(
+ arc_bridge_service(), &empty_named_service_alive)));
+ EXPECT_TRUE(empty_named_service_alive);
+ EXPECT_EQ(nullptr, manager->GetService<EmptyNamedService>());
+
+ manager.reset();
+ EXPECT_FALSE(empty_named_service_alive);
+}
+
+// Tests if GetGlobalService works as expected regardless of whether the
+// global pointer is null.
+TEST_F(ArcServiceManagerTest, TestGetGlobalService) {
+ // The getter should return nullptr when no global instance is available.
+ EXPECT_EQ(nullptr, ArcServiceManager::GetGlobalService<NamedService>());
+
+ // Create a manager. This will automatically be registered as a global
+ // instance.
+ auto manager = base::MakeUnique<ArcServiceManager>(nullptr);
+
+ // The getter should return nullptr when the manager doesn't know about the
+ // NamedService instance.
+ EXPECT_EQ(nullptr, ArcServiceManager::GetGlobalService<NamedService>());
+
+ // Register the instance and retry. The getter should return non-null this
+ // time.
+ bool unused;
+ EXPECT_TRUE(manager->AddService(base::MakeUnique<NamedService>(
+ arc_bridge_service(), &unused)));
+ EXPECT_NE(nullptr, ArcServiceManager::GetGlobalService<NamedService>());
+
+ // Remove the global instance. The same GetGlobalService() call should return
+ // nullptr now.
+ manager.reset();
+ EXPECT_EQ(nullptr, ArcServiceManager::GetGlobalService<NamedService>());
+}
+
+} // namespace arc
diff --git a/chromium/components/arc/arc_session.cc b/chromium/components/arc/arc_session.cc
index 7d85e56f774..c3dc19525e4 100644
--- a/chromium/components/arc/arc_session.cc
+++ b/chromium/components/arc/arc_session.cc
@@ -27,9 +27,11 @@
#include "chromeos/dbus/session_manager_client.h"
#include "components/arc/arc_bridge_host_impl.h"
#include "components/arc/arc_features.h"
+#include "components/arc/arc_session_observer.h"
#include "components/user_manager/user_manager.h"
-#include "ipc/unix_domain_socket_util.h"
#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/named_platform_handle.h"
+#include "mojo/edk/embedder/named_platform_handle_utils.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/embedder/platform_channel_utils_posix.h"
#include "mojo/edk/embedder/platform_handle_vector.h"
@@ -123,7 +125,7 @@ class ArcSessionImpl : public ArcSession,
//
// 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 Observer::OnStopped().
+ // The actual stop will be notified via ArcSessionObserver::OnStopped().
//
// When Stop() is called, it makes various behavior based on the current
// phase.
@@ -198,8 +200,8 @@ class ArcSessionImpl : public ArcSession,
STOPPED,
};
- explicit ArcSessionImpl(
- const scoped_refptr<base::TaskRunner>& blocking_task_runner);
+ ArcSessionImpl(ArcBridgeService* arc_bridge_service,
+ const scoped_refptr<base::TaskRunner>& blocking_task_runner);
~ArcSessionImpl() override;
// ArcSession overrides:
@@ -210,18 +212,19 @@ class ArcSessionImpl : public ArcSession,
private:
// Creates the UNIX socket on a worker pool and then processes its file
// descriptor.
- static base::ScopedFD CreateSocket();
- void OnSocketCreated(base::ScopedFD fd);
+ static mojo::edk::ScopedPlatformHandle CreateSocket();
+ void OnSocketCreated(mojo::edk::ScopedPlatformHandle fd);
// DBus callback for StartArcInstance().
- void OnInstanceStarted(base::ScopedFD socket_fd,
+ void OnInstanceStarted(mojo::edk::ScopedPlatformHandle socket_fd,
StartArcInstanceResult result);
// Synchronously accepts a connection on |socket_fd| and then processes the
// connected socket's file descriptor.
- static base::ScopedFD ConnectMojo(base::ScopedFD socket_fd,
- base::ScopedFD cancel_fd);
- void OnMojoConnected(base::ScopedFD fd);
+ static mojo::edk::ScopedPlatformHandle ConnectMojo(
+ mojo::edk::ScopedPlatformHandle socket_fd,
+ base::ScopedFD cancel_fd);
+ void OnMojoConnected(mojo::edk::ScopedPlatformHandle fd);
// Request to stop ARC instance via DBus.
void StopArcInstance();
@@ -230,12 +233,15 @@ class ArcSessionImpl : public ArcSession,
void ArcInstanceStopped(bool clean) override;
// Completes the termination procedure.
- void OnStopped(ArcBridgeService::StopReason reason);
+ void OnStopped(ArcSessionObserver::StopReason reason);
// Checks whether a function runs on the thread where the instance is
// created.
base::ThreadChecker thread_checker_;
+ // Owned by ArcServiceManager.
+ ArcBridgeService* const arc_bridge_service_;
+
// Task runner to run a blocking tasks.
scoped_refptr<base::TaskRunner> blocking_task_runner_;
@@ -260,8 +266,11 @@ class ArcSessionImpl : public ArcSession,
};
ArcSessionImpl::ArcSessionImpl(
+ ArcBridgeService* arc_bridge_service,
const scoped_refptr<base::TaskRunner>& blocking_task_runner)
- : blocking_task_runner_(blocking_task_runner), weak_factory_(this) {
+ : arc_bridge_service_(arc_bridge_service),
+ blocking_task_runner_(blocking_task_runner),
+ weak_factory_(this) {
chromeos::SessionManagerClient* client = GetSessionManagerClient();
if (client == nullptr)
return;
@@ -291,13 +300,13 @@ void ArcSessionImpl::Start() {
}
// static
-base::ScopedFD ArcSessionImpl::CreateSocket() {
+mojo::edk::ScopedPlatformHandle ArcSessionImpl::CreateSocket() {
base::FilePath socket_path(kArcBridgeSocketPath);
- int raw_fd = -1;
- if (!IPC::CreateServerUnixDomainSocket(socket_path, &raw_fd))
- return base::ScopedFD();
- base::ScopedFD socket_fd(raw_fd);
+ mojo::edk::ScopedPlatformHandle socket_fd = mojo::edk::CreateServerHandle(
+ mojo::edk::NamedPlatformHandle(socket_path.value()));
+ if (!socket_fd.is_valid())
+ return socket_fd;
// Change permissions on the socket.
struct group arc_bridge_group;
@@ -306,41 +315,42 @@ base::ScopedFD ArcSessionImpl::CreateSocket() {
if (HANDLE_EINTR(getgrnam_r(kArcBridgeSocketGroup, &arc_bridge_group, buf,
sizeof(buf), &arc_bridge_group_res)) < 0) {
PLOG(ERROR) << "getgrnam_r";
- return base::ScopedFD();
+ return mojo::edk::ScopedPlatformHandle();
}
if (!arc_bridge_group_res) {
LOG(ERROR) << "Group '" << kArcBridgeSocketGroup << "' not found";
- return base::ScopedFD();
+ return mojo::edk::ScopedPlatformHandle();
}
if (HANDLE_EINTR(chown(kArcBridgeSocketPath, -1, arc_bridge_group.gr_gid)) <
0) {
PLOG(ERROR) << "chown";
- return base::ScopedFD();
+ return mojo::edk::ScopedPlatformHandle();
}
if (!base::SetPosixFilePermissions(socket_path, 0660)) {
PLOG(ERROR) << "Could not set permissions: " << socket_path.value();
- return base::ScopedFD();
+ return mojo::edk::ScopedPlatformHandle();
}
return socket_fd;
}
-void ArcSessionImpl::OnSocketCreated(base::ScopedFD socket_fd) {
+void ArcSessionImpl::OnSocketCreated(
+ mojo::edk::ScopedPlatformHandle socket_fd) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(state_, State::CREATING_SOCKET);
if (stop_requested_) {
VLOG(1) << "Stop() called while connecting";
- OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
+ OnStopped(ArcSessionObserver::StopReason::SHUTDOWN);
return;
}
if (!socket_fd.is_valid()) {
LOG(ERROR) << "ARC: Error creating socket";
- OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
+ OnStopped(ArcSessionObserver::StopReason::GENERIC_BOOT_FAILURE);
return;
}
@@ -362,8 +372,9 @@ void ArcSessionImpl::OnSocketCreated(base::ScopedFD socket_fd) {
base::Passed(&socket_fd)));
}
-void ArcSessionImpl::OnInstanceStarted(base::ScopedFD socket_fd,
- StartArcInstanceResult result) {
+void ArcSessionImpl::OnInstanceStarted(
+ mojo::edk::ScopedPlatformHandle socket_fd,
+ StartArcInstanceResult result) {
DCHECK(thread_checker_.CalledOnValidThread());
if (state_ == State::STOPPED) {
// This is the case that error is notified via DBus before the
@@ -380,15 +391,15 @@ void ArcSessionImpl::OnInstanceStarted(base::ScopedFD socket_fd,
StopArcInstance();
return;
}
- OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
+ OnStopped(ArcSessionObserver::StopReason::SHUTDOWN);
return;
}
if (result != StartArcInstanceResult::SUCCESS) {
LOG(ERROR) << "Failed to start ARC instance";
OnStopped(result == StartArcInstanceResult::LOW_FREE_DISK_SPACE
- ? ArcBridgeService::StopReason::LOW_DISK_SPACE
- : ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
+ ? ArcSessionObserver::StopReason::LOW_DISK_SPACE
+ : ArcSessionObserver::StopReason::GENERIC_BOOT_FAILURE);
return;
}
@@ -399,7 +410,7 @@ void ArcSessionImpl::OnInstanceStarted(base::ScopedFD socket_fd,
// Stop().
base::ScopedFD cancel_fd;
if (!CreatePipe(&cancel_fd, &accept_cancel_pipe_)) {
- OnStopped(ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE);
+ OnStopped(ArcSessionObserver::StopReason::GENERIC_BOOT_FAILURE);
return;
}
@@ -411,18 +422,20 @@ void ArcSessionImpl::OnInstanceStarted(base::ScopedFD socket_fd,
}
// static
-base::ScopedFD ArcSessionImpl::ConnectMojo(base::ScopedFD socket_fd,
- base::ScopedFD cancel_fd) {
- if (!WaitForSocketReadable(socket_fd.get(), cancel_fd.get())) {
+mojo::edk::ScopedPlatformHandle ArcSessionImpl::ConnectMojo(
+ mojo::edk::ScopedPlatformHandle socket_fd,
+ base::ScopedFD cancel_fd) {
+ if (!WaitForSocketReadable(socket_fd.get().handle, cancel_fd.get())) {
VLOG(1) << "Mojo connection was cancelled.";
- return base::ScopedFD();
+ return mojo::edk::ScopedPlatformHandle();
}
- int raw_fd = -1;
- if (!IPC::ServerOnConnect(socket_fd.get(), &raw_fd)) {
- return base::ScopedFD();
+ mojo::edk::ScopedPlatformHandle scoped_fd;
+ if (!mojo::edk::ServerAcceptConnection(socket_fd.get(), &scoped_fd,
+ /* check_peer_user = */ false) ||
+ !scoped_fd.is_valid()) {
+ return mojo::edk::ScopedPlatformHandle();
}
- base::ScopedFD scoped_fd(raw_fd);
// Hardcode pid 0 since it is unused in mojo.
const base::ProcessHandle kUnusedChildProcessHandle = 0;
@@ -437,17 +450,16 @@ base::ScopedFD ArcSessionImpl::ConnectMojo(base::ScopedFD socket_fd,
struct iovec iov = {const_cast<char*>(""), 1};
ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles(
- mojo::edk::PlatformHandle(scoped_fd.get()), &iov, 1, handles->data(),
- handles->size());
+ scoped_fd.get(), &iov, 1, handles->data(), handles->size());
if (result == -1) {
PLOG(ERROR) << "sendmsg";
- return base::ScopedFD();
+ return mojo::edk::ScopedPlatformHandle();
}
return scoped_fd;
}
-void ArcSessionImpl::OnMojoConnected(base::ScopedFD fd) {
+void ArcSessionImpl::OnMojoConnected(mojo::edk::ScopedPlatformHandle fd) {
DCHECK(thread_checker_.CalledOnValidThread());
if (state_ == State::STOPPED) {
@@ -471,8 +483,8 @@ void ArcSessionImpl::OnMojoConnected(base::ScopedFD fd) {
return;
}
- mojo::ScopedMessagePipeHandle server_pipe = mojo::edk::CreateMessagePipe(
- mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(fd.release())));
+ mojo::ScopedMessagePipeHandle server_pipe =
+ mojo::edk::CreateMessagePipe(std::move(fd));
if (!server_pipe.is_valid()) {
LOG(ERROR) << "Invalid pipe";
StopArcInstance();
@@ -482,12 +494,13 @@ void ArcSessionImpl::OnMojoConnected(base::ScopedFD fd) {
mojom::ArcBridgeInstancePtr instance;
instance.Bind(mojo::InterfacePtrInfo<mojom::ArcBridgeInstance>(
std::move(server_pipe), 0u));
- arc_bridge_host_.reset(new ArcBridgeHostImpl(std::move(instance)));
+ arc_bridge_host_ = base::MakeUnique<ArcBridgeHostImpl>(arc_bridge_service_,
+ std::move(instance));
VLOG(2) << "Mojo is connected. ARC is running.";
state_ = State::RUNNING;
for (auto& observer : observer_list_)
- observer.OnReady();
+ observer.OnSessionReady();
}
void ArcSessionImpl::Stop() {
@@ -503,7 +516,7 @@ void ArcSessionImpl::Stop() {
arc_bridge_host_.reset();
switch (state_) {
case State::NOT_STARTED:
- OnStopped(ArcBridgeService::StopReason::SHUTDOWN);
+ OnStopped(ArcSessionObserver::StopReason::SHUTDOWN);
return;
case State::CREATING_SOCKET:
@@ -560,24 +573,24 @@ void ArcSessionImpl::ArcInstanceStopped(bool clean) {
// unlock the BlockingPool thread.
accept_cancel_pipe_.reset();
- ArcBridgeService::StopReason reason;
+ ArcSessionObserver::StopReason reason;
if (stop_requested_) {
// If the ARC instance is stopped after its explicit request,
// return SHUTDOWN.
- reason = ArcBridgeService::StopReason::SHUTDOWN;
+ reason = ArcSessionObserver::StopReason::SHUTDOWN;
} else if (clean) {
// If the ARC instance is stopped, but it is not explicitly requested,
// then this is triggered by some failure during the starting procedure.
// Return GENERIC_BOOT_FAILURE for the case.
- reason = ArcBridgeService::StopReason::GENERIC_BOOT_FAILURE;
+ reason = ArcSessionObserver::StopReason::GENERIC_BOOT_FAILURE;
} else {
// Otherwise, this is caused by CRASH occured inside of the ARC instance.
- reason = ArcBridgeService::StopReason::CRASH;
+ reason = ArcSessionObserver::StopReason::CRASH;
}
OnStopped(reason);
}
-void ArcSessionImpl::OnStopped(ArcBridgeService::StopReason reason) {
+void ArcSessionImpl::OnStopped(ArcSessionObserver::StopReason reason) {
DCHECK(thread_checker_.CalledOnValidThread());
// OnStopped() should be called once per instance.
DCHECK_NE(state_, State::STOPPED);
@@ -585,7 +598,7 @@ void ArcSessionImpl::OnStopped(ArcBridgeService::StopReason reason) {
arc_bridge_host_.reset();
state_ = State::STOPPED;
for (auto& observer : observer_list_)
- observer.OnStopped(reason);
+ observer.OnSessionStopped(reason);
}
void ArcSessionImpl::OnShutdown() {
@@ -610,7 +623,7 @@ void ArcSessionImpl::OnShutdown() {
// Directly set to the STOPPED stateby 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(ArcBridgeService::StopReason::SHUTDOWN);
+ OnStopped(ArcSessionObserver::StopReason::SHUTDOWN);
}
} // namespace
@@ -618,18 +631,20 @@ void ArcSessionImpl::OnShutdown() {
ArcSession::ArcSession() = default;
ArcSession::~ArcSession() = default;
-void ArcSession::AddObserver(Observer* observer) {
+void ArcSession::AddObserver(ArcSessionObserver* observer) {
observer_list_.AddObserver(observer);
}
-void ArcSession::RemoveObserver(Observer* observer) {
+void ArcSession::RemoveObserver(ArcSessionObserver* observer) {
observer_list_.RemoveObserver(observer);
}
// static
std::unique_ptr<ArcSession> ArcSession::Create(
+ ArcBridgeService* arc_bridge_service,
const scoped_refptr<base::TaskRunner>& blocking_task_runner) {
- return base::MakeUnique<ArcSessionImpl>(blocking_task_runner);
+ return base::MakeUnique<ArcSessionImpl>(arc_bridge_service,
+ blocking_task_runner);
}
} // namespace arc
diff --git a/chromium/components/arc/arc_session.h b/chromium/components/arc/arc_session.h
index 9d82e776578..25aeede5de5 100644
--- a/chromium/components/arc/arc_session.h
+++ b/chromium/components/arc/arc_session.h
@@ -17,6 +17,8 @@
namespace arc {
+class ArcSessionObserver;
+
// Starts the ARC instance and bootstraps the bridge connection.
// Clients should implement the Delegate to be notified upon communications
// being available.
@@ -26,24 +28,9 @@ namespace arc {
// conflict.
class ArcSession {
public:
- class Observer {
- public:
- Observer() = default;
- virtual ~Observer() = default;
-
- // Called when the connection with ARC instance has been established.
- virtual void OnReady() = 0;
-
- // Called when ARC instance is stopped. This is called exactly once
- // per instance which is Start()ed.
- virtual void OnStopped(ArcBridgeService::StopReason reason) = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Observer);
- };
-
// Creates a default instance of ArcSession.
static std::unique_ptr<ArcSession> Create(
+ ArcBridgeService* arc_bridge_service,
const scoped_refptr<base::TaskRunner>& blocking_task_runner);
virtual ~ArcSession();
@@ -60,13 +47,13 @@ class ArcSession {
// loop is already stopped, and the instance will soon be deleted.
virtual void OnShutdown() = 0;
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
+ void AddObserver(ArcSessionObserver* observer);
+ void RemoveObserver(ArcSessionObserver* observer);
protected:
ArcSession();
- base::ObserverList<Observer> observer_list_;
+ base::ObserverList<ArcSessionObserver> observer_list_;
private:
DISALLOW_COPY_AND_ASSIGN(ArcSession);
diff --git a/chromium/components/arc/arc_session_observer.cc b/chromium/components/arc/arc_session_observer.cc
new file mode 100644
index 00000000000..eca089cdf4d
--- /dev/null
+++ b/chromium/components/arc/arc_session_observer.cc
@@ -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.
+
+#include "components/arc/arc_session_observer.h"
+
+namespace arc {
+
+std::ostream& operator<<(std::ostream& os,
+ ArcSessionObserver::StopReason reason) {
+ switch (reason) {
+#define CASE_IMPL(val) \
+ case ArcSessionObserver::StopReason::val: \
+ return os << #val
+
+ CASE_IMPL(SHUTDOWN);
+ CASE_IMPL(GENERIC_BOOT_FAILURE);
+ CASE_IMPL(LOW_DISK_SPACE);
+ CASE_IMPL(CRASH);
+#undef CASE_IMPL
+ }
+
+ // In case of unexpected value, output the int value.
+ return os << "StopReason(" << static_cast<int>(reason) << ")";
+}
+
+} // namespace arc
diff --git a/chromium/components/arc/arc_session_observer.h b/chromium/components/arc/arc_session_observer.h
new file mode 100644
index 00000000000..2751e35ec7b
--- /dev/null
+++ b/chromium/components/arc/arc_session_observer.h
@@ -0,0 +1,48 @@
+// 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_ARC_SESSION_OBSERVER_H_
+#define COMPONENTS_ARC_ARC_SESSION_OBSERVER_H_
+
+#include <ostream>
+
+namespace arc {
+
+// Interface to observe the ARC instance running state.
+class ArcSessionObserver {
+ public:
+ // Describes the reason the ARC instance is stopped.
+ enum class StopReason {
+ // ARC instance has been gracefully shut down.
+ SHUTDOWN,
+
+ // Errors occurred during the ARC instance boot. This includes any failures
+ // before the instance is actually attempted to be started, and also
+ // failures on bootstrapping IPC channels with Android.
+ GENERIC_BOOT_FAILURE,
+
+ // The device is critically low on disk space.
+ LOW_DISK_SPACE,
+
+ // ARC instance has crashed.
+ CRASH,
+ };
+
+ virtual ~ArcSessionObserver() = default;
+
+ // Called when the connection with ARC instance has been established.
+ virtual void OnSessionReady() {}
+
+ // Called when ARC instance is stopped. This is called exactly once
+ // per instance which is Start()ed.
+ virtual void OnSessionStopped(StopReason reason) {}
+};
+
+// Defines "<<" operator for LOGging purpose.
+std::ostream& operator<<(std::ostream& os,
+ ArcSessionObserver::StopReason reason);
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_ARC_SESSION_OBSERVER_H_
diff --git a/chromium/components/arc/arc_session_runner.cc b/chromium/components/arc/arc_session_runner.cc
new file mode 100644
index 00000000000..571a2c5d299
--- /dev/null
+++ b/chromium/components/arc/arc_session_runner.cc
@@ -0,0 +1,200 @@
+// 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/arc/arc_session_runner.h"
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/task_runner.h"
+#include "components/arc/arc_session.h"
+
+namespace arc {
+
+namespace {
+
+constexpr base::TimeDelta kDefaultRestartDelay =
+ base::TimeDelta::FromSeconds(5);
+
+} // namespace
+
+ArcSessionRunner::ArcSessionRunner(const ArcSessionFactory& factory)
+ : restart_delay_(kDefaultRestartDelay),
+ factory_(factory),
+ weak_ptr_factory_(this) {}
+
+ArcSessionRunner::~ArcSessionRunner() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (arc_session_)
+ arc_session_->RemoveObserver(this);
+}
+
+void ArcSessionRunner::AddObserver(ArcSessionObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ observer_list_.AddObserver(observer);
+}
+
+void ArcSessionRunner::RemoveObserver(ArcSessionObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ observer_list_.RemoveObserver(observer);
+}
+
+void ArcSessionRunner::RequestStart() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Consecutive RequestStart() call. Do nothing.
+ if (run_requested_)
+ return;
+
+ VLOG(1) << "Session started";
+ run_requested_ = true;
+ // Here |run_requested_| transitions from false to true. So, |restart_timer_|
+ // must be stopped (either not even started, or has been cancelled in
+ // previous RequestStop() call).
+ DCHECK(!restart_timer_.IsRunning());
+
+ if (arc_session_) {
+ // 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);
+ StartArcSession();
+ }
+}
+
+void ArcSessionRunner::RequestStop() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Consecutive RequestStop() call. Do nothing.
+ if (!run_requested_)
+ return;
+
+ VLOG(1) << "Session ended";
+ run_requested_ = false;
+
+ if (arc_session_) {
+ // The |state_| could be either STARTING, RUNNING or STOPPING.
+ DCHECK_NE(state_, State::STOPPED);
+
+ if (state_ == State::STOPPING) {
+ // STOPPING is found in the senario of "RequestStart() -> RequestStop()
+ // -> RequestStart() -> RequestStop()" case.
+ // In the first RequestStop() call, |state_| is set to STOPPING,
+ // and in the second RequestStop() finds it (so this is the second call).
+ // In that case, ArcSession::Stop() is already called, so do nothing.
+ return;
+ }
+ state_ = State::STOPPING;
+ arc_session_->Stop();
+ } else {
+ DCHECK_EQ(state_, State::STOPPED);
+ // In case restarting is in progress, cancel it.
+ restart_timer_.Stop();
+ }
+}
+
+void ArcSessionRunner::OnShutdown() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ VLOG(1) << "OnShutdown";
+ run_requested_ = false;
+ restart_timer_.Stop();
+ if (arc_session_) {
+ DCHECK_NE(state_, State::STOPPED);
+ state_ = State::STOPPING;
+ arc_session_->OnShutdown();
+ }
+ // ArcSession::OnShutdown() invokes OnSessionStopped() synchronously.
+ // In the observer method, |arc_session_| should be destroyed.
+ DCHECK(!arc_session_);
+}
+
+bool ArcSessionRunner::IsRunning() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return state_ == State::RUNNING;
+}
+
+bool ArcSessionRunner::IsStopped() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ 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(!restart_timer_.IsRunning());
+
+ VLOG(1) << "Starting ARC instance";
+ arc_session_ = factory_.Run();
+ arc_session_->AddObserver(this);
+ state_ = State::STARTING;
+ arc_session_->Start();
+}
+
+void ArcSessionRunner::OnSessionReady() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(state_, State::STARTING);
+ DCHECK(arc_session_);
+ DCHECK(!restart_timer_.IsRunning());
+
+ VLOG(0) << "ARC ready";
+ state_ = State::RUNNING;
+
+ for (auto& observer : observer_list_)
+ observer.OnSessionReady();
+}
+
+void ArcSessionRunner::OnSessionStopped(StopReason stop_reason) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_NE(state_, State::STOPPED);
+ DCHECK(arc_session_);
+ DCHECK(!restart_timer_.IsRunning());
+
+ VLOG(0) << "ARC stopped: " << stop_reason;
+ arc_session_->RemoveObserver(this);
+ arc_session_.reset();
+
+ // If RUNNING, ARC instance unexpectedly crashed so we need to restart it
+ // automatically.
+ // If STOPPING, at least once RequestStop() is called. If |session_started_|
+ // is true, RequestStart() is following so schedule to restart ARC session.
+ // Otherwise, do nothing.
+ // If STARTING, ARC instance has not been booted properly, so do not
+ // restart it automatically.
+ if (state_ == State::RUNNING ||
+ (state_ == State::STOPPING && run_requested_)) {
+ // This check is for RUNNING case. In RUNNING case |run_requested_| should
+ // be always true, because if once RequestStop() is called, the state_
+ // will be set to STOPPING.
+ DCHECK(run_requested_);
+
+ // There was a previous invocation and it crashed for some reason. Try
+ // starting ARC instance later again.
+ // Note that even |restart_delay_| is 0 (for testing), it needs to
+ // PostTask, because observer callback may call RequestStart()/Stop().
+ VLOG(0) << "ARC restarting";
+ restart_timer_.Start(FROM_HERE, restart_delay_,
+ base::Bind(&ArcSessionRunner::StartArcSession,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ // TODO(hidehiko): Consider to let observers know whether there is scheduled
+ // restarting event, or not.
+ state_ = State::STOPPED;
+ for (auto& observer : observer_list_)
+ observer.OnSessionStopped(stop_reason);
+}
+
+} // namespace arc
diff --git a/chromium/components/arc/arc_session_runner.h b/chromium/components/arc/arc_session_runner.h
new file mode 100644
index 00000000000..8eeeabd14dc
--- /dev/null
+++ b/chromium/components/arc/arc_session_runner.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_ARC_ARC_SESSION_RUNNER_H_
+#define COMPONENTS_ARC_ARC_SESSION_RUNNER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_session_observer.h"
+
+namespace arc {
+
+class ArcSession;
+
+// Accept requests to start/stop ARC instance. Also supports automatic
+// restarting on unexpected ARC instance crash.
+// TODO(hidehiko): Get rid of ArcBridgeService inheritance.
+class ArcSessionRunner : public ArcSessionObserver {
+ public:
+ // This is the factory interface to inject ArcSession instance
+ // for testing purpose.
+ using ArcSessionFactory = base::Callback<std::unique_ptr<ArcSession>()>;
+
+ explicit ArcSessionRunner(const ArcSessionFactory& factory);
+ ~ArcSessionRunner() override;
+
+ // Add/Remove an observer.
+ void AddObserver(ArcSessionObserver* observer);
+ void RemoveObserver(ArcSessionObserver* observer);
+
+ // Starts the ARC service, then it will connect the Mojo channel. When the
+ // bridge becomes ready, registered Observer's OnSessionReady() is called.
+ void RequestStart();
+
+ // Stops the ARC service.
+ void RequestStop();
+
+ // 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
+ // when this function is called, MessageLoop is no longer exists.
+ void OnShutdown();
+
+ // Returns whether currently ARC instance is running or stopped respectively.
+ // Note that, both can return false at same time when, e.g., starting
+ // or stopping ARC instance.
+ bool IsRunning() const;
+ bool IsStopped() const;
+
+ // Returns the current ArcSession instance for testing purpose.
+ ArcSession* GetArcSessionForTesting() { return arc_session_.get(); }
+
+ // Normally, automatic restarting happens after a short delay. When testing,
+ // however, we'd like it to happen immediately to avoid adding unnecessary
+ // delays.
+ void SetRestartDelayForTesting(const base::TimeDelta& restart_delay);
+
+ private:
+ // The possible states. In the normal flow, the state changes in the
+ // following sequence:
+ //
+ // STOPPED
+ // RequestStart() ->
+ // STARTING
+ // OnSessionReady() ->
+ // READY
+ //
+ // The ArcSession state machine can be thought of being substates of
+ // ArcBridgeService's STARTING state.
+ // ArcBridgeService's state machine can be stopped at any phase.
+ //
+ // *
+ // RequestStop() ->
+ // STOPPING
+ // OnSessionStopped() ->
+ // STOPPED
+ enum class State {
+ // ARC instance is not currently running.
+ STOPPED,
+
+ // Request to start ARC instance is received. Starting an ARC instance.
+ STARTING,
+
+ // ARC instance has finished initializing, and is now ready for interaction
+ // with other services.
+ RUNNING,
+
+ // Request to stop ARC instance is recieved. Stopping the ARC instance.
+ STOPPING,
+ };
+
+ // Starts to run an ARC instance.
+ void StartArcSession();
+
+ // ArcSessionObserver:
+ void OnSessionReady() override;
+ void OnSessionStopped(StopReason reason) override;
+
+ base::ThreadChecker thread_checker_;
+
+ // Observers for the ARC instance state change events.
+ base::ObserverList<ArcSessionObserver> observer_list_;
+
+ // Whether a client requests to run session or not.
+ bool run_requested_ = false;
+
+ // Instead of immediately trying to restart the container, give it some time
+ // to finish tearing down in case it is still in the process of stopping.
+ base::TimeDelta restart_delay_;
+ base::OneShotTimer restart_timer_;
+
+ // Factory to inject a fake ArcSession instance for testing.
+ ArcSessionFactory factory_;
+
+ // Current runner's state.
+ State state_ = State::STOPPED;
+
+ // ArcSession object for currently running ARC instance. This should be
+ // nullptr if the state is STOPPED, otherwise non-nullptr.
+ std::unique_ptr<ArcSession> arc_session_;
+
+ // WeakPtrFactory to use callbacks.
+ base::WeakPtrFactory<ArcSessionRunner> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArcSessionRunner);
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_ARC_SESSION_RUNNER_H_
diff --git a/chromium/components/arc/arc_session_runner_unittest.cc b/chromium/components/arc/arc_session_runner_unittest.cc
new file mode 100644
index 00000000000..13b9ff8817f
--- /dev/null
+++ b/chromium/components/arc/arc_session_runner_unittest.cc
@@ -0,0 +1,246 @@
+// 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 <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "components/arc/arc_session_runner.h"
+#include "components/arc/test/fake_arc_session.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace arc {
+
+namespace {
+
+class DummyObserver : public ArcSessionObserver {};
+
+} // namespace
+
+class ArcSessionRunnerTest : public testing::Test, public ArcSessionObserver {
+ public:
+ ArcSessionRunnerTest() = default;
+
+ void SetUp() override {
+ chromeos::DBusThreadManager::Initialize();
+
+ stop_reason_ = StopReason::SHUTDOWN;
+
+ // We inject FakeArcSession here so we do not need task_runner.
+ arc_session_runner_ =
+ base::MakeUnique<ArcSessionRunner>(base::Bind(FakeArcSession::Create));
+ arc_session_runner_->AddObserver(this);
+ }
+
+ void TearDown() override {
+ arc_session_runner_->RemoveObserver(this);
+ arc_session_runner_.reset();
+
+ chromeos::DBusThreadManager::Shutdown();
+ }
+
+ ArcSessionRunner* arc_session_runner() { return arc_session_runner_.get(); }
+
+ FakeArcSession* arc_session() {
+ return static_cast<FakeArcSession*>(
+ arc_session_runner_->GetArcSessionForTesting());
+ }
+
+ StopReason stop_reason() { return stop_reason_; }
+
+ void ResetArcSessionFactory(
+ const ArcSessionRunner::ArcSessionFactory& factory) {
+ arc_session_runner_->RemoveObserver(this);
+ arc_session_runner_ = base::MakeUnique<ArcSessionRunner>(factory);
+ arc_session_runner_->AddObserver(this);
+ }
+
+ static std::unique_ptr<ArcSession> CreateSuspendedArcSession() {
+ auto arc_session = base::MakeUnique<FakeArcSession>();
+ arc_session->SuspendBoot();
+ return std::move(arc_session);
+ }
+
+ static std::unique_ptr<ArcSession> CreateBootFailureArcSession(
+ StopReason reason) {
+ auto arc_session = base::MakeUnique<FakeArcSession>();
+ arc_session->EnableBootFailureEmulation(reason);
+ return std::move(arc_session);
+ }
+
+ private:
+ // ArcSessionObserver:
+ void OnSessionStopped(StopReason stop_reason) override {
+ // The instance is already destructed in
+ // ArcSessionRunner::OnSessionStopped().
+ stop_reason_ = stop_reason;
+ }
+
+ StopReason stop_reason_;
+ std::unique_ptr<ArcSessionRunner> arc_session_runner_;
+ base::MessageLoopForUI message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArcSessionRunnerTest);
+};
+
+// Exercises the basic functionality of the ArcSessionRunner. Observer should
+// be notified.
+TEST_F(ArcSessionRunnerTest, Basic) {
+ class Observer : public ArcSessionObserver {
+ public:
+ Observer() = default;
+
+ bool ready_called() const { return ready_called_; }
+ bool stopped_called() const { return stopped_called_; }
+
+ // ArcSessionObserver:
+ void OnSessionReady() override { ready_called_ = true; }
+ void OnSessionStopped(StopReason reason) override {
+ stopped_called_ = true;
+ }
+
+ private:
+ bool ready_called_ = false;
+ bool stopped_called_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(Observer);
+ };
+
+ Observer observer;
+ arc_session_runner()->AddObserver(&observer);
+ base::ScopedClosureRunner teardown(base::Bind(
+ [](ArcSessionRunner* arc_session_runner, Observer* observer) {
+ arc_session_runner->RemoveObserver(observer);
+ },
+ arc_session_runner(), &observer));
+
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+ arc_session_runner()->RequestStart();
+ EXPECT_TRUE(arc_session_runner()->IsRunning());
+ EXPECT_TRUE(observer.ready_called());
+
+ arc_session_runner()->RequestStop();
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+ EXPECT_TRUE(observer.stopped_called());
+}
+
+// If the ArcSessionRunner accepts a request to stop ARC instance, it should
+// stop it, even mid-startup.
+TEST_F(ArcSessionRunnerTest, StopMidStartup) {
+ ResetArcSessionFactory(
+ base::Bind(&ArcSessionRunnerTest::CreateSuspendedArcSession));
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+ arc_session_runner()->RequestStart();
+ EXPECT_FALSE(arc_session_runner()->IsStopped());
+ EXPECT_FALSE(arc_session_runner()->IsRunning());
+
+ arc_session_runner()->RequestStop();
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+}
+
+// If the boot procedure is failed, then restarting mechanism should not
+// triggered.
+TEST_F(ArcSessionRunnerTest, BootFailure) {
+ ResetArcSessionFactory(
+ base::Bind(&ArcSessionRunnerTest::CreateBootFailureArcSession,
+ StopReason::GENERIC_BOOT_FAILURE));
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+ arc_session_runner()->RequestStart();
+ EXPECT_EQ(StopReason::GENERIC_BOOT_FAILURE, stop_reason());
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+}
+
+// If the instance is stopped, it should be re-started.
+TEST_F(ArcSessionRunnerTest, Restart) {
+ arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta());
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+ arc_session_runner()->RequestStart();
+ EXPECT_TRUE(arc_session_runner()->IsRunning());
+
+ // Simulate a connection loss.
+ ASSERT_TRUE(arc_session());
+ arc_session()->StopWithReason(StopReason::CRASH);
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(arc_session_runner()->IsRunning());
+
+ arc_session_runner()->RequestStop();
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+}
+
+// Makes sure OnSessionStopped is called on stop.
+TEST_F(ArcSessionRunnerTest, OnSessionStopped) {
+ arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta());
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+ arc_session_runner()->RequestStart();
+ EXPECT_TRUE(arc_session_runner()->IsRunning());
+
+ // Simulate boot failure.
+ ASSERT_TRUE(arc_session());
+ arc_session()->StopWithReason(StopReason::GENERIC_BOOT_FAILURE);
+ EXPECT_EQ(StopReason::GENERIC_BOOT_FAILURE, stop_reason());
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(arc_session_runner()->IsRunning());
+
+ // Simulate crash.
+ ASSERT_TRUE(arc_session());
+ arc_session()->StopWithReason(StopReason::CRASH);
+ EXPECT_EQ(StopReason::CRASH, stop_reason());
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(arc_session_runner()->IsRunning());
+
+ // Graceful stop.
+ arc_session_runner()->RequestStop();
+ EXPECT_EQ(StopReason::SHUTDOWN, stop_reason());
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+}
+
+TEST_F(ArcSessionRunnerTest, Shutdown) {
+ arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta());
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+ arc_session_runner()->RequestStart();
+ EXPECT_TRUE(arc_session_runner()->IsRunning());
+
+ // Simulate shutdown.
+ arc_session_runner()->OnShutdown();
+ EXPECT_EQ(StopReason::SHUTDOWN, stop_reason());
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+}
+
+// Removing the same observer more than once should be okay.
+TEST_F(ArcSessionRunnerTest, RemoveObserverTwice) {
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+ DummyObserver dummy_observer;
+ arc_session_runner()->AddObserver(&dummy_observer);
+ // Call RemoveObserver() twice.
+ arc_session_runner()->RemoveObserver(&dummy_observer);
+ arc_session_runner()->RemoveObserver(&dummy_observer);
+}
+
+// Removing an unknown observer should be allowed.
+TEST_F(ArcSessionRunnerTest, RemoveUnknownObserver) {
+ EXPECT_TRUE(arc_session_runner()->IsStopped());
+
+ DummyObserver dummy_observer;
+ arc_session_runner()->RemoveObserver(&dummy_observer);
+}
+
+} // namespace arc
diff --git a/chromium/components/arc/audio/arc_audio_bridge.cc b/chromium/components/arc/audio/arc_audio_bridge.cc
index dc037cb965e..1a489e9f6d9 100644
--- a/chromium/components/arc/audio/arc_audio_bridge.cc
+++ b/chromium/components/arc/audio/arc_audio_bridge.cc
@@ -11,14 +11,6 @@
namespace arc {
-namespace {
-
-// Note: unlike most of our mojom definitions, AudioInstance::Init's minimum
-// version is not zero.
-constexpr uint32_t kMinInstanceVersionForInit = 1;
-
-} // namespace
-
ArcAudioBridge::ArcAudioBridge(ArcBridgeService* bridge_service)
: ArcService(bridge_service), binding_(this) {
arc_bridge_service()->audio()->AddObserver(this);
@@ -37,8 +29,7 @@ ArcAudioBridge::~ArcAudioBridge() {
void ArcAudioBridge::OnInstanceReady() {
mojom::AudioInstance* audio_instance =
- arc_bridge_service()->audio()->GetInstanceForMethod(
- "Init", kMinInstanceVersionForInit);
+ 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());
}
@@ -81,8 +72,8 @@ void ArcAudioBridge::SendSwitchState(bool headphone_inserted,
}
VLOG(1) << "Send switch state " << switch_state;
- mojom::AudioInstance* audio_instance =
- arc_bridge_service()->audio()->GetInstanceForMethod("NotifySwitchState");
+ mojom::AudioInstance* audio_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->audio(), NotifySwitchState);
if (audio_instance)
audio_instance->NotifySwitchState(switch_state);
}
diff --git a/chromium/components/arc/bluetooth/arc_bluetooth_bridge.cc b/chromium/components/arc/bluetooth/arc_bluetooth_bridge.cc
index 3090f785a1e..7cb139a384f 100644
--- a/chromium/components/arc/bluetooth/arc_bluetooth_bridge.cc
+++ b/chromium/components/arc/bluetooth/arc_bluetooth_bridge.cc
@@ -15,6 +15,7 @@
#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"
@@ -53,11 +54,6 @@ using device::BluetoothTransport;
using device::BluetoothUUID;
namespace {
-constexpr uint32_t kMinBtleVersion = 1;
-constexpr uint32_t kMinBtleNotifyVersion = 2;
-constexpr uint32_t kMinGattServerVersion = 3;
-constexpr uint32_t kMinAddrChangeVersion = 4;
-constexpr uint32_t kMinSdpSupportVersion = 5;
constexpr uint32_t kGattReadPermission =
BluetoothGattCharacteristic::Permission::PERMISSION_READ |
BluetoothGattCharacteristic::Permission::PERMISSION_READ_ENCRYPTED |
@@ -152,7 +148,7 @@ void OnGattReadDone(const GattReadCallback& callback,
arc::mojom::BluetoothGattValuePtr gattValue =
arc::mojom::BluetoothGattValue::New();
gattValue->status = arc::mojom::BluetoothGattStatus::GATT_SUCCESS;
- gattValue->value = mojo::Array<uint8_t>::From(result);
+ gattValue->value = result;
callback.Run(std::move(gattValue));
}
@@ -163,7 +159,6 @@ void OnGattReadError(const GattReadCallback& callback,
arc::mojom::BluetoothGattValue::New();
gattValue->status =
mojo::ConvertTo<arc::mojom::BluetoothGattStatus>(error_code);
- gattValue->value = nullptr;
callback.Run(std::move(gattValue));
}
@@ -172,9 +167,9 @@ void OnGattServerRead(
const BluetoothLocalGattService::Delegate::ValueCallback& success_callback,
const BluetoothLocalGattService::Delegate::ErrorCallback& error_callback,
arc::mojom::BluetoothGattStatus status,
- mojo::Array<uint8_t> value) {
+ const std::vector<uint8_t>& value) {
if (status == arc::mojom::BluetoothGattStatus::GATT_SUCCESS)
- success_callback.Run(value.To<std::vector<uint8_t>>());
+ success_callback.Run(value);
else
error_callback.Run();
}
@@ -295,7 +290,7 @@ void ArcBluetoothBridge::OnAdapterInitialized(
void ArcBluetoothBridge::OnInstanceReady() {
mojom::BluetoothInstance* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod("Init");
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->bluetooth(), Init);
DCHECK(bluetooth_instance);
bluetooth_instance->Init(binding_.CreateInterfacePtrAndBind());
@@ -313,12 +308,12 @@ void ArcBluetoothBridge::OnInstanceClosed() {
}
void ArcBluetoothBridge::SendDevice(const BluetoothDevice* device) const {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod("OnDeviceFound");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnDeviceFound);
if (!bluetooth_instance)
return;
- mojo::Array<mojom::BluetoothPropertyPtr> properties =
+ std::vector<mojom::BluetoothPropertyPtr> properties =
GetDeviceProperties(mojom::BluetoothPropertyType::ALL, device);
bluetooth_instance->OnDeviceFound(std::move(properties));
@@ -335,12 +330,11 @@ void ArcBluetoothBridge::SendDevice(const BluetoothDevice* device) const {
// 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_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnLEDeviceFound", kMinBtleVersion);
+ auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnLEDeviceFound);
if (!btle_instance)
return;
- mojo::Array<mojom::BluetoothAdvertisingDataPtr> adv_data =
+ std::vector<mojom::BluetoothAdvertisingDataPtr> adv_data =
GetAdvertisingData(device);
addr = mojom::BluetoothAddress::From(device->GetAddress());
btle_instance->OnLEDeviceFound(std::move(addr), rssi.value(),
@@ -399,8 +393,8 @@ void ArcBluetoothBridge::DeviceAddressChanged(BluetoothAdapter* adapter,
gatt_connection_cache_.erase(it);
gatt_connection_cache_.insert(device->GetAddress());
- auto* btle_instance = arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnLEDeviceAddressChange", kMinAddrChangeVersion);
+ auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnLEDeviceAddressChange);
if (!btle_instance)
return;
@@ -466,8 +460,8 @@ void ArcBluetoothBridge::GattServiceRemoved(
void ArcBluetoothBridge::GattServicesDiscovered(BluetoothAdapter* adapter,
BluetoothDevice* device) {
- auto* btle_instance = arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnSearchComplete", kMinBtleVersion);
+ auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnSearchComplete);
if (!btle_instance)
return;
@@ -518,8 +512,8 @@ void ArcBluetoothBridge::GattCharacteristicValueChanged(
BluetoothAdapter* adapter,
BluetoothRemoteGattCharacteristic* characteristic,
const std::vector<uint8_t>& value) {
- auto* btle_instance = arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnGattNotify", kMinBtleNotifyVersion);
+ auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnGattNotify);
if (!btle_instance)
return;
@@ -539,8 +533,7 @@ void ArcBluetoothBridge::GattCharacteristicValueChanged(
char_id->uuid = characteristic->GetUUID();
btle_instance->OnGattNotify(std::move(address), std::move(service_id),
- std::move(char_id), true /* is_notify */,
- mojo::Array<uint8_t>::From(value));
+ std::move(char_id), true /* is_notify */, value);
}
void ArcBluetoothBridge::GattDescriptorValueChanged(
@@ -558,9 +551,8 @@ void ArcBluetoothBridge::OnGattAttributeReadRequest(
const ValueCallback& success_callback,
const ErrorCallback& error_callback) {
DCHECK(CalledOnValidThread());
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "RequestGattRead", kMinGattServerVersion);
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), RequestGattRead);
if (!bluetooth_instance || !IsGattOffsetValid(offset)) {
error_callback.Run();
return;
@@ -583,9 +575,8 @@ void ArcBluetoothBridge::OnGattAttributeWriteRequest(
const base::Closure& success_callback,
const ErrorCallback& error_callback) {
DCHECK(CalledOnValidThread());
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "RequestGattWrite", kMinGattServerVersion);
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), RequestGattWrite);
if (!bluetooth_instance || !IsGattOffsetValid(offset)) {
error_callback.Run();
return;
@@ -595,8 +586,7 @@ void ArcBluetoothBridge::OnGattAttributeWriteRequest(
bluetooth_instance->RequestGattWrite(
mojom::BluetoothAddress::From(device->GetAddress()),
- gatt_handle_[attribute->GetIdentifier()], offset,
- mojo::Array<uint8_t>::From(value),
+ gatt_handle_[attribute->GetIdentifier()], offset, value,
base::Bind(&OnGattServerWrite, success_callback, error_callback));
}
@@ -676,13 +666,12 @@ void ArcBluetoothBridge::DisableAdapter(
void ArcBluetoothBridge::GetAdapterProperty(mojom::BluetoothPropertyType type) {
DCHECK(bluetooth_adapter_);
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnAdapterProperties");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnAdapterProperties);
if (!bluetooth_instance)
return;
- mojo::Array<mojom::BluetoothPropertyPtr> properties =
+ std::vector<mojom::BluetoothPropertyPtr> properties =
GetAdapterProperties(type);
bluetooth_instance->OnAdapterProperties(mojom::BluetoothStatus::SUCCESS,
@@ -742,12 +731,11 @@ void ArcBluetoothBridge::SetDiscoverable(bool discoverable, uint32_t timeout) {
void ArcBluetoothBridge::OnSetAdapterProperty(
mojom::BluetoothStatus status,
mojom::BluetoothPropertyPtr property) {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnAdapterProperties");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnAdapterProperties);
DCHECK(bluetooth_instance);
- auto properties = mojo::Array<arc::mojom::BluetoothPropertyPtr>::New(0);
+ std::vector<arc::mojom::BluetoothPropertyPtr> properties;
properties.push_back(std::move(property));
bluetooth_instance->OnAdapterProperties(status, std::move(properties));
@@ -796,16 +784,15 @@ void ArcBluetoothBridge::GetRemoteDeviceProperty(
mojom::BluetoothAddressPtr remote_addr,
mojom::BluetoothPropertyType type) {
DCHECK(bluetooth_adapter_);
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnRemoteDeviceProperties");
+ 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);
- mojo::Array<mojom::BluetoothPropertyPtr> properties =
+ std::vector<mojom::BluetoothPropertyPtr> properties =
GetDeviceProperties(type, device);
mojom::BluetoothStatus status = mojom::BluetoothStatus::SUCCESS;
@@ -822,9 +809,8 @@ void ArcBluetoothBridge::SetRemoteDeviceProperty(
mojom::BluetoothAddressPtr remote_addr,
mojom::BluetoothPropertyPtr property) {
DCHECK(bluetooth_adapter_);
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnRemoteDeviceProperties");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnRemoteDeviceProperties);
if (!bluetooth_instance)
return;
@@ -832,7 +818,7 @@ void ArcBluetoothBridge::SetRemoteDeviceProperty(
// And only Android Settings App / Android TV / NFC used that.
bluetooth_instance->OnRemoteDeviceProperties(
mojom::BluetoothStatus::UNSUPPORTED, std::move(remote_addr),
- mojo::Array<mojom::BluetoothPropertyPtr>::New(0));
+ std::vector<mojom::BluetoothPropertyPtr>());
}
void ArcBluetoothBridge::GetRemoteServiceRecord(
@@ -901,9 +887,8 @@ void ArcBluetoothBridge::OnDiscoveryStarted(
std::unique_ptr<BluetoothDiscoverySession> session) {
DCHECK(CalledOnValidThread());
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnDiscoveryStateChanged");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnDiscoveryStateChanged);
if (!bluetooth_instance)
return;
@@ -923,9 +908,8 @@ void ArcBluetoothBridge::OnDiscoveryStarted(
}
void ArcBluetoothBridge::OnDiscoveryStopped() {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnDiscoveryStateChanged");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnDiscoveryStateChanged);
if (!bluetooth_instance)
return;
@@ -1038,8 +1022,8 @@ void ArcBluetoothBridge::StopLEScan() {
void ArcBluetoothBridge::OnGattConnectStateChanged(
mojom::BluetoothAddressPtr addr,
bool connected) const {
- auto* btle_instance = arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnLEConnectionStateChange", kMinBtleVersion);
+ auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnLEConnectionStateChange);
if (!btle_instance)
return;
@@ -1078,9 +1062,8 @@ void ArcBluetoothBridge::OnGattDisconnected(
void ArcBluetoothBridge::ConnectLEDevice(
mojom::BluetoothAddressPtr remote_addr) {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnLEConnectionStateChange", kMinBtleVersion);
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnLEConnectionStateChange);
if (!bluetooth_instance)
return;
@@ -1105,9 +1088,8 @@ void ArcBluetoothBridge::ConnectLEDevice(
void ArcBluetoothBridge::DisconnectLEDevice(
mojom::BluetoothAddressPtr remote_addr) {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnLEConnectionStateChange", kMinBtleVersion);
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnLEConnectionStateChange);
if (!bluetooth_instance)
return;
@@ -1130,9 +1112,8 @@ void ArcBluetoothBridge::DisconnectLEDevice(
}
void ArcBluetoothBridge::SearchService(mojom::BluetoothAddressPtr remote_addr) {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnSearchComplete", kMinBtleVersion);
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnSearchComplete);
if (!bluetooth_instance)
return;
@@ -1208,15 +1189,14 @@ void ArcBluetoothBridge::StopLEListen(const StopLEListenCallback& callback) {
}
void ArcBluetoothBridge::GetGattDB(mojom::BluetoothAddressPtr remote_addr) {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod("OnGetGattDB",
- kMinBtleVersion);
+ 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>());
- mojo::Array<mojom::BluetoothGattDBElementPtr> db;
+ std::vector<mojom::BluetoothGattDBElementPtr> db;
for (auto* service : device->GetGattServices()) {
mojom::BluetoothGattDBElementPtr service_element = CreateGattDBElement(
service->IsPrimary()
@@ -1316,8 +1296,7 @@ void ArcBluetoothBridge::WriteGattCharacteristic(
DCHECK(characteristic->GetPermissions() & kGattWritePermission);
characteristic->WriteRemoteCharacteristic(
- value->value.To<std::vector<uint8_t>>(),
- base::Bind(&OnGattOperationDone, callback),
+ value->value, base::Bind(&OnGattOperationDone, callback),
base::Bind(&OnGattOperationError, callback));
}
@@ -1364,8 +1343,7 @@ void ArcBluetoothBridge::WriteGattDescriptor(
}
descriptor->WriteRemoteDescriptor(
- value->value.To<std::vector<uint8_t>>(),
- base::Bind(&OnGattOperationDone, callback),
+ value->value, base::Bind(&OnGattOperationDone, callback),
base::Bind(&OnGattOperationError, callback));
}
@@ -1595,7 +1573,7 @@ void ArcBluetoothBridge::SendIndication(
int32_t attribute_handle,
mojom::BluetoothAddressPtr address,
bool confirm,
- mojo::Array<uint8_t> value,
+ const std::vector<uint8_t>& value,
const SendIndicationCallback& callback) {}
void ArcBluetoothBridge::GetSdpRecords(mojom::BluetoothAddressPtr remote_addr,
@@ -1778,9 +1756,8 @@ void ArcBluetoothBridge::OnDiscoveryError() {
}
void ArcBluetoothBridge::OnPairing(mojom::BluetoothAddressPtr addr) const {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnBondStateChanged");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnBondStateChanged);
if (!bluetooth_instance)
return;
@@ -1790,9 +1767,8 @@ void ArcBluetoothBridge::OnPairing(mojom::BluetoothAddressPtr addr) const {
}
void ArcBluetoothBridge::OnPairedDone(mojom::BluetoothAddressPtr addr) const {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnBondStateChanged");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnBondStateChanged);
if (!bluetooth_instance)
return;
@@ -1804,9 +1780,8 @@ void ArcBluetoothBridge::OnPairedDone(mojom::BluetoothAddressPtr addr) const {
void ArcBluetoothBridge::OnPairedError(
mojom::BluetoothAddressPtr addr,
BluetoothDevice::ConnectErrorCode error_code) const {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnBondStateChanged");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnBondStateChanged);
if (!bluetooth_instance)
return;
@@ -1816,9 +1791,8 @@ void ArcBluetoothBridge::OnPairedError(
}
void ArcBluetoothBridge::OnForgetDone(mojom::BluetoothAddressPtr addr) const {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnBondStateChanged");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnBondStateChanged);
if (!bluetooth_instance)
return;
@@ -1828,9 +1802,8 @@ void ArcBluetoothBridge::OnForgetDone(mojom::BluetoothAddressPtr addr) const {
}
void ArcBluetoothBridge::OnForgetError(mojom::BluetoothAddressPtr addr) const {
- auto* bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnBondStateChanged");
+ auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnBondStateChanged);
if (!bluetooth_instance)
return;
@@ -1844,10 +1817,10 @@ void ArcBluetoothBridge::OnForgetError(mojom::BluetoothAddressPtr addr) const {
std::move(addr), bond_state);
}
-mojo::Array<mojom::BluetoothPropertyPtr>
+std::vector<mojom::BluetoothPropertyPtr>
ArcBluetoothBridge::GetDeviceProperties(mojom::BluetoothPropertyType type,
const BluetoothDevice* device) const {
- mojo::Array<mojom::BluetoothPropertyPtr> properties;
+ std::vector<mojom::BluetoothPropertyPtr> properties;
if (!device) {
return properties;
@@ -1890,7 +1863,7 @@ ArcBluetoothBridge::GetDeviceProperties(mojom::BluetoothPropertyType type,
type == mojom::BluetoothPropertyType::REMOTE_FRIENDLY_NAME) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_remote_friendly_name(
- mojo::String::From(base::UTF16ToUTF8(device->GetNameForDisplay())));
+ base::UTF16ToUTF8(device->GetNameForDisplay()));
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
@@ -1907,16 +1880,16 @@ ArcBluetoothBridge::GetDeviceProperties(mojom::BluetoothPropertyType type,
return properties;
}
-mojo::Array<mojom::BluetoothPropertyPtr>
+std::vector<mojom::BluetoothPropertyPtr>
ArcBluetoothBridge::GetAdapterProperties(
mojom::BluetoothPropertyType type) const {
- mojo::Array<mojom::BluetoothPropertyPtr> properties;
+ 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(mojo::String(name));
+ btp->set_bdname(name);
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
@@ -1929,8 +1902,7 @@ ArcBluetoothBridge::GetAdapterProperties(
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::UUIDS) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
- btp->set_uuids(
- mojo::Array<BluetoothUUID>::From(bluetooth_adapter_->GetUUIDs()));
+ btp->set_uuids(bluetooth_adapter_->GetUUIDs());
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
@@ -1962,8 +1934,7 @@ ArcBluetoothBridge::GetAdapterProperties(
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
BluetoothAdapter::DeviceList devices = bluetooth_adapter_->GetDevices();
- mojo::Array<mojom::BluetoothAddressPtr> bonded_devices =
- mojo::Array<mojom::BluetoothAddressPtr>::New(0);
+ std::vector<mojom::BluetoothAddressPtr> bonded_devices;
for (auto* device : devices) {
if (device->IsPaired())
@@ -2011,9 +1982,9 @@ ArcBluetoothBridge::GetAdapterProperties(
// 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.
-mojo::Array<mojom::BluetoothAdvertisingDataPtr>
+std::vector<mojom::BluetoothAdvertisingDataPtr>
ArcBluetoothBridge::GetAdvertisingData(const BluetoothDevice* device) const {
- mojo::Array<mojom::BluetoothAdvertisingDataPtr> advertising_data;
+ std::vector<mojom::BluetoothAdvertisingDataPtr> advertising_data;
// Advertising Data Flags
if (device->GetAdvertisingDataFlags().has_value()) {
@@ -2035,8 +2006,8 @@ ArcBluetoothBridge::GetAdvertisingData(const BluetoothDevice* device) const {
if (uuid_set.size() > 0) {
mojom::BluetoothAdvertisingDataPtr service_uuids =
mojom::BluetoothAdvertisingData::New();
- service_uuids->set_service_uuids(mojo::Array<BluetoothUUID>::From(
- std::vector<BluetoothUUID>(uuid_set.begin(), uuid_set.end())));
+ service_uuids->set_service_uuids(
+ std::vector<BluetoothUUID>(uuid_set.begin(), uuid_set.end()));
advertising_data.push_back(std::move(service_uuids));
}
@@ -2062,8 +2033,7 @@ ArcBluetoothBridge::GetAdvertisingData(const BluetoothDevice* device) const {
const std::vector<uint8_t>* data = device->GetServiceDataForUUID(uuid);
DCHECK(data != nullptr);
- std::vector<uint8_t> data_copy = *data;
- service_data->data.Swap(&data_copy);
+ service_data->data = *data;
service_data_element->set_service_data(std::move(service_data));
advertising_data.push_back(std::move(service_data_element));
@@ -2127,24 +2097,26 @@ void ArcBluetoothBridge::OnGetServiceRecordsDone(
mojom::BluetoothAddressPtr remote_addr,
const BluetoothUUID& target_uuid,
const std::vector<bluez::BluetoothServiceRecordBlueZ>& records_bluez) {
- auto* sdp_bluetooth_instance =
- arc_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnGetSdpRecords", kMinSdpSupportVersion);
+ auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnGetSdpRecords);
if (!sdp_bluetooth_instance)
return;
- sdp_bluetooth_instance->OnGetSdpRecords(
- mojom::BluetoothStatus::SUCCESS, std::move(remote_addr), target_uuid,
- mojo::Array<mojom::BluetoothSdpRecordPtr>::From(records_bluez));
+ 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_bridge_service()->bluetooth()->GetInstanceForMethod(
- "OnGetSdpRecords", kMinSdpSupportVersion);
+ auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->bluetooth(), OnGetSdpRecords);
if (!sdp_bluetooth_instance)
return;
@@ -2165,7 +2137,7 @@ void ArcBluetoothBridge::OnGetServiceRecordsError(
sdp_bluetooth_instance->OnGetSdpRecords(
status, std::move(remote_addr), target_uuid,
- mojo::Array<mojom::BluetoothSdpRecordPtr>::New(0));
+ std::vector<mojom::BluetoothSdpRecordPtr>());
}
bool ArcBluetoothBridge::CalledOnValidThread() {
diff --git a/chromium/components/arc/bluetooth/arc_bluetooth_bridge.h b/chromium/components/arc/bluetooth/arc_bluetooth_bridge.h
index 416c93bd110..a98fa52ee37 100644
--- a/chromium/components/arc/bluetooth/arc_bluetooth_bridge.h
+++ b/chromium/components/arc/bluetooth/arc_bluetooth_bridge.h
@@ -263,7 +263,7 @@ class ArcBluetoothBridge
void SendIndication(int32_t attribute_handle,
mojom::BluetoothAddressPtr address,
bool confirm,
- mojo::Array<uint8_t> value,
+ const std::vector<uint8_t>& value,
const SendIndicationCallback& callback) override;
// Bluetooth Mojo host interface - Bluetooth SDP functions
@@ -331,12 +331,12 @@ class ArcBluetoothBridge
std::unique_ptr<device::BluetoothGattNotifySession> notify_session);
private:
- mojo::Array<mojom::BluetoothPropertyPtr> GetDeviceProperties(
+ std::vector<mojom::BluetoothPropertyPtr> GetDeviceProperties(
mojom::BluetoothPropertyType type,
const device::BluetoothDevice* device) const;
- mojo::Array<mojom::BluetoothPropertyPtr> GetAdapterProperties(
+ std::vector<mojom::BluetoothPropertyPtr> GetAdapterProperties(
mojom::BluetoothPropertyType type) const;
- mojo::Array<mojom::BluetoothAdvertisingDataPtr> GetAdvertisingData(
+ std::vector<mojom::BluetoothAdvertisingDataPtr> GetAdvertisingData(
const device::BluetoothDevice* device) const;
void SendCachedDevicesFound() const;
diff --git a/chromium/components/arc/bluetooth/arc_bluetooth_bridge_unittest.cc b/chromium/components/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
index 31c2003f11b..400eaa860b2 100644
--- a/chromium/components/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
+++ b/chromium/components/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
@@ -8,12 +8,13 @@
#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_arc_bridge_service.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"
@@ -22,7 +23,6 @@
#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 "mojo/public/cpp/bindings/array.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -89,12 +89,12 @@ class ArcBluetoothBridgeTest : public testing::Test {
dbus_setter->SetBluetoothGattDescriptorClient(
base::MakeUnique<bluez::FakeBluetoothGattDescriptorClient>());
- fake_arc_bridge_service_.reset(new FakeArcBridgeService());
- fake_bluetooth_instance_.reset(new FakeBluetoothInstance());
- fake_arc_bridge_service_->bluetooth()->SetInstance(
+ 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_.reset(
- new ArcBluetoothBridge(fake_arc_bridge_service_.get()));
+ arc_bluetooth_bridge_ =
+ base::MakeUnique<ArcBluetoothBridge>(arc_bridge_service_.get());
device::BluetoothAdapterFactory::GetAdapter(base::Bind(
&ArcBluetoothBridgeTest::OnAdapterInitialized, base::Unretained(this)));
@@ -166,7 +166,7 @@ class ArcBluetoothBridgeTest : public testing::Test {
int last_adv_handle_;
mojom::BluetoothGattStatus last_status_;
- std::unique_ptr<FakeArcBridgeService> fake_arc_bridge_service_;
+ 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_;
@@ -181,7 +181,7 @@ 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 mojo::Array<mojom::BluetoothPropertyPtr>& prop =
+ const std::vector<mojom::BluetoothPropertyPtr>& prop =
fake_bluetooth_instance_->device_found_data().back();
EXPECT_EQ(7u, prop.size());
@@ -208,7 +208,7 @@ TEST_F(ArcBluetoothBridgeTest, DeviceFound) {
ChangeTestDeviceRssi(kTestRssi2);
EXPECT_EQ(3u, fake_bluetooth_instance_->device_found_data().size());
- const mojo::Array<mojom::BluetoothPropertyPtr>& prop2 =
+ 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());
@@ -225,7 +225,7 @@ TEST_F(ArcBluetoothBridgeTest, LEDeviceFound) {
const auto& le_device_found_data =
fake_bluetooth_instance_->le_device_found_data().back();
const mojom::BluetoothAddressPtr& addr = le_device_found_data->addr();
- const mojo::Array<mojom::BluetoothAdvertisingDataPtr>& adv_data =
+ const std::vector<mojom::BluetoothAdvertisingDataPtr>& adv_data =
le_device_found_data->adv_data();
EXPECT_EQ(std::string(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress),
@@ -234,7 +234,7 @@ TEST_F(ArcBluetoothBridgeTest, LEDeviceFound) {
EXPECT_TRUE(adv_data[0]->is_local_name());
EXPECT_EQ(std::string(bluez::FakeBluetoothDeviceClient::kLowEnergyName),
- adv_data[0]->get_local_name().To<std::string>());
+ adv_data[0]->get_local_name());
EXPECT_TRUE(adv_data[1]->is_service_uuids());
EXPECT_EQ(1u, adv_data[1]->get_service_uuids().size());
@@ -268,7 +268,7 @@ TEST_F(ArcBluetoothBridgeTest, GetGattDB) {
// Descriptor: ClientCharacteristicConfiguration
// Characteristic: BodySensorLocation
// Characteristic: HeartRateControlPoint
- const mojo::Array<mojom::BluetoothGattDBElementPtr>& db =
+ const std::vector<mojom::BluetoothGattDBElementPtr>& db =
fake_bluetooth_instance_->gatt_db_result().back()->db();
EXPECT_EQ(5u, db.size());
diff --git a/chromium/components/arc/bluetooth/bluetooth_struct_traits.cc b/chromium/components/arc/bluetooth/bluetooth_struct_traits.cc
index 716feca08fd..6990e4d5dc6 100644
--- a/chromium/components/arc/bluetooth/bluetooth_struct_traits.cc
+++ b/chromium/components/arc/bluetooth/bluetooth_struct_traits.cc
@@ -7,6 +7,7 @@
#include <algorithm>
#include <vector>
+#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "device/bluetooth/bluetooth_advertisement.h"
diff --git a/chromium/components/arc/bluetooth/bluetooth_struct_traits.h b/chromium/components/arc/bluetooth/bluetooth_struct_traits.h
index 26ca434aefc..2290d7ad930 100644
--- a/chromium/components/arc/bluetooth/bluetooth_struct_traits.h
+++ b/chromium/components/arc/bluetooth/bluetooth_struct_traits.h
@@ -5,6 +5,9 @@
#ifndef COMPONENTS_ARC_BLUETOOTH_BLUETOOTH_STRUCT_TRAITS_H_
#define COMPONENTS_ARC_BLUETOOTH_BLUETOOTH_STRUCT_TRAITS_H_
+#include <memory>
+#include <vector>
+
#include "components/arc/common/bluetooth.mojom.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/bluetooth_common.h"
@@ -122,10 +125,10 @@ struct StructTraits<arc::mojom::BluetoothAdvertisementDataView,
return false;
}
- static mojo::Array<arc::mojom::BluetoothAdvertisingDataPtr> data(
+ static std::vector<arc::mojom::BluetoothAdvertisingDataPtr> data(
std::unique_ptr<device::BluetoothAdvertisement::Data>& input) {
NOTREACHED();
- return mojo::Array<arc::mojom::BluetoothAdvertisingDataPtr>();
+ return std::vector<arc::mojom::BluetoothAdvertisingDataPtr>();
}
};
diff --git a/chromium/components/arc/bluetooth/bluetooth_struct_traits_unittest.cc b/chromium/components/arc/bluetooth/bluetooth_struct_traits_unittest.cc
index 94adbbb734f..5c5076d7e8f 100644
--- a/chromium/components/arc/bluetooth/bluetooth_struct_traits_unittest.cc
+++ b/chromium/components/arc/bluetooth/bluetooth_struct_traits_unittest.cc
@@ -4,9 +4,11 @@
#include "components/arc/bluetooth/bluetooth_struct_traits.h"
+#include <string>
+#include <utility>
+
#include "base/macros.h"
#include "device/bluetooth/bluetooth_uuid.h"
-#include "mojo/public/cpp/bindings/array.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -24,7 +26,7 @@ constexpr uint8_t kManufacturerData[] = {0x00, 0xe0};
template <typename MojoType, typename UserType>
mojo::StructPtr<MojoType> ConvertToMojo(UserType& input) {
- mojo::Array<uint8_t> data = MojoType::Serialize(&input);
+ std::vector<uint8_t> data = MojoType::Serialize(&input);
mojo::StructPtr<MojoType> output;
MojoType::Deserialize(std::move(data), &output);
return output;
@@ -32,7 +34,7 @@ mojo::StructPtr<MojoType> ConvertToMojo(UserType& input) {
template <typename MojoType, typename UserType>
bool ConvertFromMojo(mojo::StructPtr<MojoType> input, UserType* output) {
- mojo::Array<uint8_t> data = MojoType::Serialize(&input);
+ std::vector<uint8_t> data = MojoType::Serialize(&input);
return MojoType::Deserialize(std::move(data), output);
}
@@ -70,7 +72,7 @@ TEST(BluetoothStructTraitsTest, DeserializeBluetoothUUID) {
TEST(BluetoothStructTraitsTest, DeserializeBluetoothAdvertisement) {
arc::mojom::BluetoothAdvertisementPtr advertisement_mojo =
arc::mojom::BluetoothAdvertisement::New();
- mojo::Array<arc::mojom::BluetoothAdvertisingDataPtr> adv_data;
+ std::vector<arc::mojom::BluetoothAdvertisingDataPtr> adv_data;
// Create service UUIDs.
arc::mojom::BluetoothAdvertisingDataPtr data =
@@ -133,13 +135,13 @@ TEST(BluetoothStructTraitsTest, DeserializeBluetoothAdvertisement) {
TEST(BluetoothStructTraitsTest, DeserializeBluetoothAdvertisementFailure) {
arc::mojom::BluetoothAdvertisementPtr advertisement_mojo =
arc::mojom::BluetoothAdvertisement::New();
- mojo::Array<arc::mojom::BluetoothAdvertisingDataPtr> adv_data;
+ std::vector<arc::mojom::BluetoothAdvertisingDataPtr> adv_data;
// Create empty manufacturer data. Manufacturer data must include the CIC
// which is 2 bytes long.
arc::mojom::BluetoothAdvertisingDataPtr data =
arc::mojom::BluetoothAdvertisingData::New();
- data->set_manufacturer_data((mojo::Array<uint8_t>()));
+ data->set_manufacturer_data(std::vector<uint8_t>());
adv_data.push_back(std::move(data));
advertisement_mojo->type =
diff --git a/chromium/components/arc/bluetooth/bluetooth_type_converters.cc b/chromium/components/arc/bluetooth/bluetooth_type_converters.cc
index 6acc8c40457..33b1a9f3569 100644
--- a/chromium/components/arc/bluetooth/bluetooth_type_converters.cc
+++ b/chromium/components/arc/bluetooth/bluetooth_type_converters.cc
@@ -11,12 +11,12 @@
#include <string>
#include <vector>
+#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "components/arc/bluetooth/bluetooth_type_converters.h"
#include "device/bluetooth/bluetooth_gatt_service.h"
#include "device/bluetooth/bluetooth_uuid.h"
-#include "mojo/public/cpp/bindings/array.h"
namespace {
@@ -50,14 +50,10 @@ namespace mojo {
arc::mojom::BluetoothAddressPtr
TypeConverter<arc::mojom::BluetoothAddressPtr, std::string>::Convert(
const std::string& address) {
- std::string stripped = StripNonHex(address);
-
- std::vector<uint8_t> address_bytes;
- base::HexStringToBytes(stripped, &address_bytes);
arc::mojom::BluetoothAddressPtr mojo_addr =
arc::mojom::BluetoothAddress::New();
- mojo_addr->address = mojo::Array<uint8_t>::From(address_bytes);
+ base::HexStringToBytes(StripNonHex(address), &mojo_addr->address);
return mojo_addr;
}
@@ -68,7 +64,7 @@ std::string TypeConverter<std::string, arc::mojom::BluetoothAddress>::Convert(
std::ostringstream addr_stream;
addr_stream << std::setfill('0') << std::hex << std::uppercase;
- const mojo::Array<uint8_t>& bytes = address.address;
+ const std::vector<uint8_t>& bytes = address.address;
if (address.address.size() != kAddressSize)
return std::string(kInvalidAddress);
@@ -141,16 +137,12 @@ TypeConverter<arc::mojom::BluetoothSdpAttributePtr,
case bluez::BluetoothServiceAttributeValueBlueZ::BOOL:
result->type_size = attr_bluez.size();
result->value.Append(attr_bluez.value().CreateDeepCopy());
- result->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
break;
case bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE:
if (depth + 1 >= arc::kBluetoothSDPMaxDepth) {
result->type = bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE;
result->type_size = 0;
result->value.Append(base::Value::CreateNullValue());
- result->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
return result;
}
for (const auto& child : attr_bluez.sequence()) {
diff --git a/chromium/components/arc/bluetooth/bluetooth_type_converters_unittest.cc b/chromium/components/arc/bluetooth/bluetooth_type_converters_unittest.cc
index 6d379839ca3..98c74ec6b69 100644
--- a/chromium/components/arc/bluetooth/bluetooth_type_converters_unittest.cc
+++ b/chromium/components/arc/bluetooth/bluetooth_type_converters_unittest.cc
@@ -9,11 +9,11 @@
#include <string>
#include <vector>
+#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "device/bluetooth/bluetooth_gatt_service.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h"
-#include "mojo/public/cpp/bindings/array.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -38,7 +38,6 @@ arc::mojom::BluetoothSdpAttributePtr CreateDeepMojoSequenceAttribute(
value->type = bluez::BluetoothServiceAttributeValueBlueZ::UINT;
value->type_size = static_cast<uint32_t>(sizeof(data));
value->value.AppendInteger(static_cast<int>(data));
- value->sequence = mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
}
return value;
}
@@ -116,8 +115,6 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE;
nulltypeAttributeMojo->type_size = 0;
nulltypeAttributeMojo->value.Append(base::Value::CreateNullValue());
- nulltypeAttributeMojo->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
auto nulltypeAttributeBlueZ =
nulltypeAttributeMojo.To<bluez::BluetoothServiceAttributeValueBlueZ>();
@@ -125,7 +122,7 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE,
nulltypeAttributeBlueZ.type());
EXPECT_EQ(0u, nulltypeAttributeBlueZ.size());
- EXPECT_EQ(base::Value::TYPE_NULL, nulltypeAttributeBlueZ.value().GetType());
+ EXPECT_EQ(base::Value::Type::NONE, nulltypeAttributeBlueZ.value().GetType());
// Construct Mojo attribute with TYPE_BOOLEAN value.
bool valueBool = true;
@@ -133,16 +130,13 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
boolAttributeMojo->type = bluez::BluetoothServiceAttributeValueBlueZ::BOOL;
boolAttributeMojo->type_size = static_cast<uint32_t>(sizeof(valueBool));
boolAttributeMojo->value.AppendBoolean(valueBool);
- boolAttributeMojo->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
-
auto boolAttributeBlueZ =
boolAttributeMojo.To<bluez::BluetoothServiceAttributeValueBlueZ>();
EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::BOOL,
boolAttributeBlueZ.type());
EXPECT_EQ(sizeof(valueBool), boolAttributeBlueZ.size());
- EXPECT_EQ(base::Value::TYPE_BOOLEAN, boolAttributeBlueZ.value().GetType());
+ EXPECT_EQ(base::Value::Type::BOOLEAN, boolAttributeBlueZ.value().GetType());
EXPECT_TRUE(boolAttributeBlueZ.value().GetAsBoolean(&valueBool));
EXPECT_TRUE(valueBool);
@@ -153,8 +147,6 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
uintAttributeMojo->type = bluez::BluetoothServiceAttributeValueBlueZ::UINT;
uintAttributeMojo->type_size = static_cast<uint32_t>(sizeof(valueUint16));
uintAttributeMojo->value.AppendInteger(static_cast<int>(valueUint16));
- uintAttributeMojo->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
auto uintAttributeBlueZ =
uintAttributeMojo.To<bluez::BluetoothServiceAttributeValueBlueZ>();
@@ -162,7 +154,7 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::UINT,
uintAttributeBlueZ.type());
EXPECT_EQ(sizeof(valueUint16), uintAttributeBlueZ.size());
- EXPECT_EQ(base::Value::TYPE_INTEGER, uintAttributeBlueZ.value().GetType());
+ EXPECT_EQ(base::Value::Type::INTEGER, uintAttributeBlueZ.value().GetType());
EXPECT_TRUE(uintAttributeBlueZ.value().GetAsInteger(&valueUint16AsInt));
EXPECT_EQ(valueUint16, static_cast<uint16_t>(valueUint16AsInt));
@@ -173,8 +165,6 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
intAttributeMojo->type = bluez::BluetoothServiceAttributeValueBlueZ::INT;
intAttributeMojo->type_size = static_cast<uint32_t>(sizeof(valueInt16));
intAttributeMojo->value.AppendInteger(static_cast<int>(valueInt16));
- intAttributeMojo->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
auto intAttributeBlueZ =
intAttributeMojo.To<bluez::BluetoothServiceAttributeValueBlueZ>();
@@ -182,7 +172,7 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::INT,
intAttributeBlueZ.type());
EXPECT_EQ(sizeof(valueInt16), intAttributeBlueZ.size());
- EXPECT_EQ(base::Value::TYPE_INTEGER, intAttributeBlueZ.value().GetType());
+ EXPECT_EQ(base::Value::Type::INTEGER, intAttributeBlueZ.value().GetType());
EXPECT_TRUE(intAttributeBlueZ.value().GetAsInteger(&valueInt16AsInt));
EXPECT_EQ(valueInt16, static_cast<int16_t>(valueInt16AsInt));
@@ -195,8 +185,6 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
// UUID16, UUID32 and UUID128.
uuidAttributeMojo->type_size = static_cast<uint32_t>(sizeof(uint16_t));
uuidAttributeMojo->value.AppendString(expectedUUID);
- uuidAttributeMojo->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
auto uuidAttributeBlueZ =
uuidAttributeMojo.To<bluez::BluetoothServiceAttributeValueBlueZ>();
@@ -204,7 +192,7 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::UUID,
uuidAttributeBlueZ.type());
EXPECT_EQ(sizeof(uint16_t), uuidAttributeBlueZ.size());
- EXPECT_EQ(base::Value::TYPE_STRING, uuidAttributeBlueZ.value().GetType());
+ EXPECT_EQ(base::Value::Type::STRING, uuidAttributeBlueZ.value().GetType());
EXPECT_TRUE(uuidAttributeBlueZ.value().GetAsString(&actualUUID));
EXPECT_EQ(expectedUUID, actualUUID);
@@ -218,8 +206,6 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
stringAttributeMojo->type_size =
static_cast<uint32_t>(expectedString.length());
stringAttributeMojo->value.AppendString(expectedString);
- stringAttributeMojo->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
auto stringAttributeBlueZ =
stringAttributeMojo.To<bluez::BluetoothServiceAttributeValueBlueZ>();
@@ -227,7 +213,7 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoValueAttributeToBlueZAttribute) {
EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::STRING,
stringAttributeBlueZ.type());
EXPECT_EQ(expectedString.length(), stringAttributeBlueZ.size());
- EXPECT_EQ(base::Value::TYPE_STRING, stringAttributeBlueZ.value().GetType());
+ EXPECT_EQ(base::Value::Type::STRING, stringAttributeBlueZ.value().GetType());
EXPECT_TRUE(stringAttributeBlueZ.value().GetAsString(&actualString));
EXPECT_EQ(expectedString, actualString);
}
@@ -239,8 +225,6 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoSequenceAttributeToBlueZAttribute) {
valueUUID->type = bluez::BluetoothServiceAttributeValueBlueZ::UUID;
valueUUID->type_size = static_cast<uint32_t>(sizeof(uint16_t));
valueUUID->value.AppendString(l2capUUID);
- valueUUID->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
// Create an UINT value.
uint16_t l2capChannel = 3;
@@ -248,8 +232,6 @@ TEST(BluetoothTypeConvertorTest, ConvertMojoSequenceAttributeToBlueZAttribute) {
valueUint16->type = bluez::BluetoothServiceAttributeValueBlueZ::UINT;
valueUint16->type_size = static_cast<uint32_t>(sizeof(l2capChannel));
valueUint16->value.AppendInteger(static_cast<int>(l2capChannel));
- valueUint16->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
// Create a sequence with the above two values.
auto sequenceMojo = arc::mojom::BluetoothSdpAttribute::New();
@@ -289,8 +271,6 @@ TEST(BluetoothTypeConvertorTest,
valueNoData->type = bluez::BluetoothServiceAttributeValueBlueZ::UINT;
valueNoData->type_size = static_cast<uint32_t>(sizeof(uint32_t));
valueNoData->value.Clear();
- valueNoData->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
auto valueNoDataBlueZ =
valueNoData.To<bluez::BluetoothServiceAttributeValueBlueZ>();
@@ -298,7 +278,7 @@ TEST(BluetoothTypeConvertorTest,
EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE,
valueNoDataBlueZ.type());
EXPECT_EQ(0u, valueNoDataBlueZ.size());
- EXPECT_EQ(base::Value::TYPE_NULL, valueNoDataBlueZ.value().GetType());
+ EXPECT_EQ(base::Value::Type::NONE, valueNoDataBlueZ.value().GetType());
}
TEST(BluetoothTypeConvertorTest,
@@ -308,8 +288,6 @@ TEST(BluetoothTypeConvertorTest,
sequenceNoData->type = bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE;
sequenceNoData->type_size = 0;
sequenceNoData->value.Append(base::Value::CreateNullValue());
- sequenceNoData->sequence =
- mojo::Array<arc::mojom::BluetoothSdpAttributePtr>::New(0);
auto sequenceNoDataBlueZ =
sequenceNoData.To<bluez::BluetoothServiceAttributeValueBlueZ>();
@@ -317,7 +295,7 @@ TEST(BluetoothTypeConvertorTest,
EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE,
sequenceNoDataBlueZ.type());
EXPECT_EQ(0u, sequenceNoDataBlueZ.size());
- EXPECT_EQ(base::Value::TYPE_NULL, sequenceNoDataBlueZ.value().GetType());
+ EXPECT_EQ(base::Value::Type::NONE, sequenceNoDataBlueZ.value().GetType());
// Create a Mojo attribute with the depth = arc::kBluetoothSDPMaxDepth + 3.
auto sequenceTooDeepMojo =
@@ -344,15 +322,14 @@ TEST(BluetoothTypeConvertorTest, ConvertBlueZValueAttributeToMojoAttribute) {
nulltypeAttributeMojo->type);
EXPECT_EQ(0u, nulltypeAttributeMojo->type_size);
EXPECT_TRUE(nulltypeAttributeMojo->value.Get(0, &actualValue));
- EXPECT_EQ(base::Value::TYPE_NULL, actualValue->GetType());
+ EXPECT_EQ(base::Value::Type::NONE, actualValue->GetType());
// Check integer types (INT, UINT).
uint16_t valueUint16 = 10;
int valueUint16AsInt;
auto uintAttributeBlueZ = bluez::BluetoothServiceAttributeValueBlueZ(
bluez::BluetoothServiceAttributeValueBlueZ::UINT, sizeof(valueUint16),
- base::MakeUnique<base::FundamentalValue>(
- base::FundamentalValue(static_cast<int>(valueUint16))));
+ base::MakeUnique<base::FundamentalValue>(static_cast<int>(valueUint16)));
auto uintAttributeMojo =
ConvertTo<arc::mojom::BluetoothSdpAttributePtr>(uintAttributeBlueZ);
@@ -368,8 +345,7 @@ TEST(BluetoothTypeConvertorTest, ConvertBlueZValueAttributeToMojoAttribute) {
bool actualBool = true;
auto boolAttributeBlueZ = bluez::BluetoothServiceAttributeValueBlueZ(
bluez::BluetoothServiceAttributeValueBlueZ::BOOL, sizeof(bool),
- base::MakeUnique<base::FundamentalValue>(
- base::FundamentalValue(valueBool)));
+ base::MakeUnique<base::FundamentalValue>(valueBool));
auto boolAttributeMojo =
ConvertTo<arc::mojom::BluetoothSdpAttributePtr>(boolAttributeBlueZ);
@@ -386,7 +362,7 @@ TEST(BluetoothTypeConvertorTest, ConvertBlueZValueAttributeToMojoAttribute) {
std::string actualUUID;
auto uuidAttributeBlueZ = bluez::BluetoothServiceAttributeValueBlueZ(
bluez::BluetoothServiceAttributeValueBlueZ::UUID, sizeof(uint16_t),
- base::MakeUnique<base::StringValue>(base::StringValue(valueUUID)));
+ base::MakeUnique<base::StringValue>(valueUUID));
auto uuidAttributeMojo =
ConvertTo<arc::mojom::BluetoothSdpAttributePtr>(uuidAttributeBlueZ);
@@ -424,11 +400,10 @@ TEST(BluetoothTypeConvertorTest, ConvertBlueZSequenceAttributeToMojoAttribute) {
sequence(new bluez::BluetoothServiceAttributeValueBlueZ::Sequence());
sequence->push_back(bluez::BluetoothServiceAttributeValueBlueZ(
bluez::BluetoothServiceAttributeValueBlueZ::UUID, sizeof(uint16_t),
- base::MakeUnique<base::StringValue>(base::StringValue(l2capUUID))));
+ base::MakeUnique<base::StringValue>(l2capUUID)));
sequence->push_back(bluez::BluetoothServiceAttributeValueBlueZ(
bluez::BluetoothServiceAttributeValueBlueZ::UINT, sizeof(uint16_t),
- base::MakeUnique<base::FundamentalValue>(
- base::FundamentalValue(l2capChannel))));
+ base::MakeUnique<base::FundamentalValue>(l2capChannel)));
auto sequenceBlueZ =
bluez::BluetoothServiceAttributeValueBlueZ(std::move(sequence));
diff --git a/chromium/components/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc b/chromium/components/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc
deleted file mode 100644
index e4af76afec3..00000000000
--- a/chromium/components/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc
+++ /dev/null
@@ -1,58 +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/boot_phase_monitor/arc_boot_phase_monitor_bridge.h"
-
-#include "base/logging.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/session_manager_client.h"
-#include "components/arc/arc_bridge_service.h"
-
-namespace arc {
-
-namespace {
-
-void PrioritizeArcInstanceCallback(bool success) {
- VLOG(2) << "Finished prioritizing the instance: result=" << success;
- if (!success)
- LOG(WARNING) << "Failed to prioritize ARC";
-}
-
-} // namespace
-
-ArcBootPhaseMonitorBridge::ArcBootPhaseMonitorBridge(
- ArcBridgeService* bridge_service)
- : ArcService(bridge_service), binding_(this) {
- DCHECK(thread_checker_.CalledOnValidThread());
- arc_bridge_service()->boot_phase_monitor()->AddObserver(this);
-}
-
-ArcBootPhaseMonitorBridge::~ArcBootPhaseMonitorBridge() {
- DCHECK(thread_checker_.CalledOnValidThread());
- arc_bridge_service()->boot_phase_monitor()->RemoveObserver(this);
-}
-
-void ArcBootPhaseMonitorBridge::OnInstanceReady() {
- DCHECK(thread_checker_.CalledOnValidThread());
- auto* instance =
- arc_bridge_service()->boot_phase_monitor()->GetInstanceForMethod("Init");
- DCHECK(instance);
- instance->Init(binding_.CreateInterfacePtrAndBind());
-}
-
-void ArcBootPhaseMonitorBridge::OnInstanceClosed() {
- DCHECK(thread_checker_.CalledOnValidThread());
-}
-
-void ArcBootPhaseMonitorBridge::OnBootCompleted() {
- DCHECK(thread_checker_.CalledOnValidThread());
- VLOG(2) << "OnBootCompleted";
- chromeos::SessionManagerClient* session_manager_client =
- chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
- session_manager_client->PrioritizeArcInstance(
- base::Bind(PrioritizeArcInstanceCallback));
- session_manager_client->EmitArcBooted();
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h b/chromium/components/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h
deleted file mode 100644
index b42137dbdbf..00000000000
--- a/chromium/components/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_ARC_BOOT_PHASE_MONITOR_ARC_BOOT_PHASE_MONITOR_BRIDGE_H_
-#define COMPONENTS_ARC_BOOT_PHASE_MONITOR_ARC_BOOT_PHASE_MONITOR_BRIDGE_H_
-
-#include "base/macros.h"
-#include "base/threading/thread_checker.h"
-#include "components/arc/arc_service.h"
-#include "components/arc/common/boot_phase_monitor.mojom.h"
-#include "components/arc/instance_holder.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace arc {
-
-class ArcBridgeService;
-
-// Receives boot phase notifications from ARC.
-class ArcBootPhaseMonitorBridge
- : public ArcService,
- public InstanceHolder<mojom::BootPhaseMonitorInstance>::Observer,
- public mojom::BootPhaseMonitorHost {
- public:
- explicit ArcBootPhaseMonitorBridge(ArcBridgeService* bridge_service);
- ~ArcBootPhaseMonitorBridge() override;
-
- // InstanceHolder<mojom::BootPhaseMonitorInstance>::Observer
- void OnInstanceReady() override;
- void OnInstanceClosed() override;
-
- // mojom::BootPhaseMonitorHost
- void OnBootCompleted() override;
-
- private:
- mojo::Binding<mojom::BootPhaseMonitorHost> binding_;
-
- base::ThreadChecker thread_checker_;
-
- DISALLOW_COPY_AND_ASSIGN(ArcBootPhaseMonitorBridge);
-};
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_BOOT_PHASE_MONITOR_ARC_BOOT_PHASE_MONITOR_BRIDGE_H_
diff --git a/chromium/components/arc/clipboard/arc_clipboard_bridge.cc b/chromium/components/arc/clipboard/arc_clipboard_bridge.cc
index 517a71ea572..0e6f1e686c9 100644
--- a/chromium/components/arc/clipboard/arc_clipboard_bridge.cc
+++ b/chromium/components/arc/clipboard/arc_clipboard_bridge.cc
@@ -26,7 +26,7 @@ ArcClipboardBridge::~ArcClipboardBridge() {
void ArcClipboardBridge::OnInstanceReady() {
mojom::ClipboardInstance* clipboard_instance =
- arc_bridge_service()->clipboard()->GetInstanceForMethod("Init");
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->clipboard(), Init);
DCHECK(clipboard_instance);
clipboard_instance->Init(binding_.CreateInterfacePtrAndBind());
}
@@ -44,9 +44,8 @@ void ArcClipboardBridge::GetTextContent() {
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &text);
- mojom::ClipboardInstance* clipboard_instance =
- arc_bridge_service()->clipboard()->GetInstanceForMethod(
- "OnGetTextContent");
+ mojom::ClipboardInstance* clipboard_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->clipboard(), OnGetTextContent);
if (!clipboard_instance)
return;
clipboard_instance->OnGetTextContent(base::UTF16ToUTF8(text));
diff --git a/chromium/components/arc/common/app.mojom b/chromium/components/arc/common/app.mojom
index 2cad0d419ed..c255ea85544 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: 16
+// Next MinVersion: 18
module arc.mojom;
@@ -31,6 +31,12 @@ enum OrientationLock {
LANDSCAPE_SECONDARY = 7,
};
+// Describes installation result.
+struct InstallationResult {
+ string package_name;
+ bool success; // true if app was installed successfully.
+};
+
// Describes ARC app.
struct AppInfo {
string name;
@@ -69,7 +75,7 @@ enum ShowPackageInfoPage {
MANAGE_LINKS = 1,
};
-// Next method ID: 14
+// Next method ID: 16
interface AppHost {
// Sends newly added ARC app to Chrome. This message is sent when ARC receives
// package added notification. Multiple apps may be added in the one package.
@@ -129,6 +135,16 @@ interface AppHost {
// Notifies that an application shortcut needs to be created.
[MinVersion=9] OnInstallShortcut@11(ShortcutInfo shortcut);
+ // Notifies that Play Store installation has been started. |package_name|
+ // specifies installation package
+ [MinVersion=16] OnInstallationStarted@14(
+ [MinVersion=17] string? package_name@0);
+
+ // Notifies that Play Store installation is finished. |result| contains
+ // details of installation result.
+ [MinVersion=16] OnInstallationFinished@15(
+ [MinVersion=17] InstallationResult? result@1);
+
// Notifies that task requested orientation lock.
[MinVersion=12] OnTaskOrientationLockRequested@12(int32 task_id,
OrientationLock lock);
diff --git a/chromium/components/arc/common/auth.mojom b/chromium/components/arc/common/auth.mojom
index 8a6ba3c0cf7..11409cda466 100644
--- a/chromium/components/arc/common/auth.mojom
+++ b/chromium/components/arc/common/auth.mojom
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Next MinVersion: 6
+// Next MinVersion: 9
module arc.mojom;
@@ -12,7 +12,7 @@ enum ArcSignInFailureReason {
// Negative values are reserved for internal use.
// The values are shuffled to keep the backward compatibility and don't match
// ProvisioningResult in arc_optin_uma.h
- // Next value: 15.
+ // Next value: 16.
UNKNOWN_ERROR = 0,
// Mojo errors:
@@ -61,6 +61,9 @@ enum ArcSignInFailureReason {
CLOUD_PROVISION_FLOW_FAILED = 5,
CLOUD_PROVISION_FLOW_TIMEOUT = 13,
CLOUD_PROVISION_FLOW_INTERNAL_ERROR = 14,
+
+ // Network connection is unavailable.
+ [MinVersion=8] NO_NETWORK_CONNECTION = 15,
};
// These values describe the type of the Chrome account to provision.
@@ -76,6 +79,23 @@ enum ChromeAccountType {
ROBOT_ACCOUNT = 2,
};
+// These values describe the type of the metrics to report.
+[Extensible]
+enum MetricsType {
+ // Duration of waiting for network connection in milliseconds.
+ NETWORK_WAITING_TIME_MILLISECONDS = 0,
+
+ // Number of attempts to wait for Checkin task completed. 0 indicates that
+ // Checkin task was already completed prior to GMS SignIn.
+ CHECKIN_ATTEMPTS = 1,
+
+ // Duration of waiting for Checkin task completed in milliseconds.
+ CHECKIN_TIME_MILLISECONDS = 2,
+
+ // Duration of waiting for sign-in completed in milliseconds.
+ SIGNIN_TIME_MILLISECONDS = 3,
+};
+
// The necessary information for Android to sign in and provision itself.
struct AccountInfo {
// The authorization code that can be used in Android to sign in. If it is
@@ -89,7 +109,7 @@ struct AccountInfo {
bool is_managed;
};
-// Next Method ID: 7.
+// Next Method ID: 9.
interface AuthHost {
// Notifies Chrome that the sign-in is completed successfully.
[MinVersion=2] OnSignInComplete@2();
@@ -100,6 +120,9 @@ interface AuthHost {
// information.
[MinVersion=5] RequestAccountInfo@7();
+ // Reports metrics to Chrome to be recorded in UMA.
+ [MinVersion=7] ReportMetrics@8(MetricsType metrics_type, int32 value);
+
// Deprecated methods:
// Returns an authorization code, which can be used to sign in.
diff --git a/chromium/components/arc/common/bluetooth.mojom b/chromium/components/arc/common/bluetooth.mojom
index a1f5a0d3d91..b687ec97ffc 100644
--- a/chromium/components/arc/common/bluetooth.mojom
+++ b/chromium/components/arc/common/bluetooth.mojom
@@ -6,7 +6,7 @@
module arc.mojom;
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/values.mojom";
[Extensible]
enum BluetoothAdapterState {
@@ -272,7 +272,7 @@ enum BluetoothSdpAttributeType {
struct BluetoothSdpAttribute {
BluetoothSdpAttributeType type;
uint32 type_size;
- mojo.common.mojom.ListValue value;
+ mojo.common.mojom.LegacyListValue value;
array<BluetoothSdpAttribute> sequence;
};
diff --git a/chromium/components/arc/common/file_system.mojom b/chromium/components/arc/common/file_system.mojom
index 7cda8973b98..de13a030d00 100644
--- a/chromium/components/arc/common/file_system.mojom
+++ b/chromium/components/arc/common/file_system.mojom
@@ -1,10 +1,74 @@
// 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.
+//
+// Next MinVersion: 3
module arc.mojom;
+// Represents a document in Android DocumentsProvider.
+// See Android docs of DocumentsContract.Document for details.
+struct Document {
+ // Opaque ID of the document.
+ string document_id;
+
+ // Display name of the document.
+ string display_name;
+
+ // MIME type of the document.
+ // A directory is represented by a document having MIME_TYPE_DIR MIME type.
+ string mime_type;
+
+ // Size of the document in bytes. If the size is unknown, -1 is set.
+ int64 size;
+
+ // Timestamp when the document was modified last time, in milliseconds
+ // since UNIX epoch.
+ // 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;
+};
+
+// Next method ID: 5
interface FileSystemInstance {
+ // Notes about Android Documents Provider:
+ //
+ // In Android Storage Access Framework, a document is uniquely identified by
+ // a pair of "authority" and "document ID".
+ //
+ // - An authority specifies a Documents Provider that serves a document.
+ // 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 document ID is an opaque string that specifies a particular document
+ // in a documents provider. Its format varies by providers.
+ //
+ // See the following documents for details about Documents Provider:
+ // https://developer.android.com/guide/topics/providers/document-provider.html
+ // https://developer.android.com/reference/android/provider/DocumentsContract.html
+
+ // Queries child documents of the directory specified by |authority| and
+ // |parent_document_id| in Documents Provider.
+ // If such a directory does not exist, null is returned.
+ [MinVersion=2] GetChildDocuments@4(string authority,
+ string parent_document_id) =>
+ (array<Document>? documents);
+
+ // Queries the document specified by |authority| and |document_id| in
+ // Documents Provider.
+ // If such a document does not exist, null is returned.
+ [MinVersion=2] GetDocument@3(string authority, string document_id) =>
+ (Document? document);
+
+ // Asks the ContentResolver for the size of the file specified by the URL.
+ // If the file does not exist or the size is unknown (e.g. directories and
+ // streams), -1 is returned.
+ [MinVersion=1] GetFileSize@1(string url) => (int64 size);
+
+ // Asks the ContentResolver to get a FD to read the file specified by the
+ // URL.
+ [MinVersion=1] OpenFileToRead@2(string url) => (handle? fd);
+
// Requests MediaProvider to scan specified files.
// When the specified file does not exist, the corresponding entry in
// MediaProvider is removed.
diff --git a/chromium/components/arc/common/intent_helper.mojom b/chromium/components/arc/common/intent_helper.mojom
index 1bc7c4558f9..179d5ea2b64 100644
--- a/chromium/components/arc/common/intent_helper.mojom
+++ b/chromium/components/arc/common/intent_helper.mojom
@@ -116,8 +116,8 @@ interface IntentHelperInstance {
// When multiple packages are set as preferred, the most recent setting wins.
[MinVersion=7] AddPreferredPackage@8(string package_name);
- // Asks the ContentResolver for the size of the file specified by the URL.
- [MinVersion=15] GetFileSize@11(string url) => (int64 size);
+ // DEPRECATED. Use FileSystemInstance.GetFileSize() instead.
+ [MinVersion=15] GetFileSizeDeprecated@11(string url) => (int64 size);
// Passes an intent to an activity.
[MinVersion=12] HandleIntent@10(IntentInfo intent, ActivityName activity);
@@ -134,9 +134,8 @@ interface IntentHelperInstance {
// Establishes full-duplex communication with the host.
Init@0(IntentHelperHost host_ptr);
- // Asks the ContentResolver to get an FD to read the file specified by the
- // URL.
- [MinVersion=15] OpenFileToRead@12(string url) => (handle? fd);
+ // DEPRECATED. Use FileSystemInstance.OpenFileToRead() instead.
+ [MinVersion=15] OpenFileToReadDeprecated@12(string url) => (handle? fd);
// Requests 48dp * 48dp icons of the |activities| suitable for the
// |scale_factor|. An array of icon data will be returned.
diff --git a/chromium/components/arc/common/intent_helper.typemap b/chromium/components/arc/common/intent_helper.typemap
new file mode 100644
index 00000000000..5b1a17ceb56
--- /dev/null
+++ b/chromium/components/arc/common/intent_helper.typemap
@@ -0,0 +1,16 @@
+# 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/intent_helper.mojom"
+public_headers = [ "//components/arc/intent_helper/intent_filter.h" ]
+traits_headers =
+ [ "//components/arc/intent_helper/intent_filter_struct_traits.h" ]
+sources = [
+ "//components/arc/intent_helper/intent_filter_struct_traits.cc",
+]
+type_mappings = [
+ "arc.mojom.IntentFilter=::arc::IntentFilter[move_only]",
+ "arc.mojom.AuthorityEntry=::arc::IntentFilter::AuthorityEntry[move_only]",
+ "arc.mojom.PatternMatcher=::arc::IntentFilter::PatternMatcher[move_only]",
+]
diff --git a/chromium/components/arc/common/power.mojom b/chromium/components/arc/common/power.mojom
index 0b000c20e87..195c7c828e3 100644
--- a/chromium/components/arc/common/power.mojom
+++ b/chromium/components/arc/common/power.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 min version: 3
+
module arc.mojom;
// Enumerates the types of wake lock the ARC instance can request from the
@@ -17,6 +19,7 @@ enum DisplayWakeLockType {
DIM = 1
};
+// Next method ID: 3
interface PowerHost {
// Acquire and release wake locks on the host side.
OnAcquireDisplayWakeLock@0(DisplayWakeLockType type);
@@ -26,10 +29,18 @@ interface PowerHost {
[MinVersion=1] IsDisplayOn@2() => (bool is_on);
};
+// Next method ID: 4
interface PowerInstance {
// Establishes full-duplex communication with the host.
Init@0(PowerHost host_ptr);
// Alerts the instance to a change in interactive state.
[MinVersion=1] SetInteractive@1(bool enabled);
+
+ // Called when the system is about to suspend. The callback will be invoked
+ // when pre-suspend work is complete.
+ [MinVersion=2] Suspend@2() => ();
+
+ // Called when the system has just resumed.
+ [MinVersion=2] Resume@3();
};
diff --git a/chromium/components/arc/common/typemaps.gni b/chromium/components/arc/common/typemaps.gni
index 4359362fc8c..ca0c1675e6b 100644
--- a/chromium/components/arc/common/typemaps.gni
+++ b/chromium/components/arc/common/typemaps.gni
@@ -6,4 +6,6 @@ typemaps = [
"//components/arc/common/app.typemap",
"//components/arc/common/bitmap.typemap",
"//components/arc/common/bluetooth.typemap",
+ "//components/arc/common/intent_helper.typemap",
+ "//components/arc/common/video_accelerator.typemap",
]
diff --git a/chromium/components/arc/common/video.mojom b/chromium/components/arc/common/video.mojom
index 8b859afba1e..f8c95829911 100644
--- a/chromium/components/arc/common/video.mojom
+++ b/chromium/components/arc/common/video.mojom
@@ -2,24 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Next MinVersion: 5
-
module arc.mojom;
import "video_accelerator.mojom";
+// Next MinVersion: 5
+// Deprecated method IDs: 0
// Next method ID: 2
interface VideoHost {
- // Requests an ipc channel from Chrome to be used in the video accelerator.
- // Replies with |channel_handle| of mojo initial message pipe and |token| for
- // creating the client end of VideoAcceleratorService message pipe.
- // Replys invalid handle and empty token if any failure.
- // TODO(owenlin): Remove this deprecated function after both Android and
- // Chromium have been updated to this version.
- [MinVersion=2]
- DeprecatedOnRequestArcVideoAcceleratorChannel@0() => (handle channel_handle,
- string token);
-
// Requests an IPC channel from Chrome's browser process to bootstrap a new
// mojo child process and a token which can be used to create a message pipe
// connected to a new VideoAcceleratorFactory.
diff --git a/chromium/components/arc/common/video_accelerator.mojom b/chromium/components/arc/common/video_accelerator.mojom
index 2cb1abdfe42..a329c335490 100644
--- a/chromium/components/arc/common/video_accelerator.mojom
+++ b/chromium/components/arc/common/video_accelerator.mojom
@@ -54,6 +54,9 @@ struct ArcVideoAcceleratorDmabufPlane {
int32 stride;
};
+// Next MinVersion: 4
+// Deprecated method IDs: 2, 7
+// Next method ID: 10
interface VideoAcceleratorService {
enum Result {
SUCCESS = 0,
@@ -68,15 +71,9 @@ interface VideoAcceleratorService {
Initialize@8(ArcVideoAcceleratorConfig config,
VideoAcceleratorServiceClient client) => (Result result);
- [MinVersion=1]
- DeprecatedInitialize@7(ArcVideoAcceleratorConfig config) => (Result result);
-
BindSharedMemory@1(PortType port, uint32 index, handle ashmem_fd,
uint32 offset, uint32 length);
- DeprecatedBindDmabuf@2(PortType port, uint32 index, handle dmabuf_fd,
- int32 stride);
-
[MinVersion=3]
BindDmabuf@9(PortType port, uint32 index, handle dmabuf_fd,
array<ArcVideoAcceleratorDmabufPlane> dmabuf_planes);
@@ -90,9 +87,9 @@ interface VideoAcceleratorService {
Flush@6();
};
+// Deprecated method IDs: 0
+// Next method ID: 6
interface VideoAcceleratorServiceClient {
- DeprecatedInit@0(VideoAcceleratorService service_ptr);
-
OnError@1(VideoAcceleratorService.Result error);
OnBufferDone@2(PortType port, uint32 index, BufferMetadata metadata);
diff --git a/chromium/components/arc/common/video_accelerator.typemap b/chromium/components/arc/common/video_accelerator.typemap
new file mode 100644
index 00000000000..3461d8658d1
--- /dev/null
+++ b/chromium/components/arc/common/video_accelerator.typemap
@@ -0,0 +1,12 @@
+# 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/wallpaper.mojom b/chromium/components/arc/common/wallpaper.mojom
index 6f400742a4b..4feb1f428bf 100644
--- a/chromium/components/arc/common/wallpaper.mojom
+++ b/chromium/components/arc/common/wallpaper.mojom
@@ -13,8 +13,8 @@ interface WallpaperHost {
GetWallpaper@0() => (array<uint8> wallpaper);
// Sets an image from ARC as the wallpaper.
- // |png_data| is a PNG encoded wallpaper image.
- SetWallpaper@1(array<uint8> png_data);
+ // |data| is a byte array of the wallpaper image.
+ SetWallpaper@1(array<uint8> data);
};
// Connects with container side to publish wallpaper related intents.
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 34e81752721..2ca3da4f26a 100644
--- a/chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc
+++ b/chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc
@@ -63,9 +63,9 @@ ArcCrashCollectorBridge::~ArcCrashCollectorBridge() {
void ArcCrashCollectorBridge::OnInstanceReady() {
mojom::CrashCollectorHostPtr host_ptr;
- binding_.Bind(mojo::GetProxy(&host_ptr));
- auto* instance =
- arc_bridge_service()->crash_collector()->GetInstanceForMethod("Init");
+ binding_.Bind(mojo::MakeRequest(&host_ptr));
+ auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->crash_collector(), Init);
DCHECK(instance);
instance->Init(std::move(host_ptr));
}
diff --git a/chromium/components/arc/file_system/arc_file_system_operation_runner.cc b/chromium/components/arc/file_system/arc_file_system_operation_runner.cc
new file mode 100644
index 00000000000..ac9662effcb
--- /dev/null
+++ b/chromium/components/arc/file_system/arc_file_system_operation_runner.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/arc/file_system/arc_file_system_operation_runner.h"
+
+namespace arc {
+
+// static
+const char ArcFileSystemOperationRunner::kArcServiceName[] =
+ "arc::ArcFileSystemOperationRunner";
+
+ArcFileSystemOperationRunner::ArcFileSystemOperationRunner(
+ ArcBridgeService* bridge_service)
+ : ArcService(bridge_service) {}
+
+ArcFileSystemOperationRunner::~ArcFileSystemOperationRunner() = default;
+
+} // namespace arc
diff --git a/chromium/components/arc/file_system/arc_file_system_operation_runner.h b/chromium/components/arc/file_system/arc_file_system_operation_runner.h
new file mode 100644
index 00000000000..985ce0da753
--- /dev/null
+++ b/chromium/components/arc/file_system/arc_file_system_operation_runner.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_ARC_FILE_SYSTEM_ARC_FILE_SYSTEM_OPERATION_RUNNER_H_
+#define COMPONENTS_ARC_FILE_SYSTEM_ARC_FILE_SYSTEM_OPERATION_RUNNER_H_
+
+#include <string>
+
+#include "components/arc/arc_service.h"
+#include "components/arc/common/file_system.mojom.h"
+
+class GURL;
+
+namespace arc {
+
+// An interface to run ARC file system operations.
+//
+// This is an abstraction layer on top of mojom::FileSystemInstance.
+// In production, ArcDeferredFileSystemOperationRunner is always used which
+// defers file system operations while ARC is booting.
+// In unit tests, fake implementations will be injected.
+//
+// All member functions must be called on the UI thread.
+class ArcFileSystemOperationRunner : public ArcService {
+ public:
+ // For supporting ArcServiceManager::GetService<T>().
+ static const char kArcServiceName[];
+
+ explicit ArcFileSystemOperationRunner(ArcBridgeService* bridge_service);
+ ~ArcFileSystemOperationRunner() override;
+
+ using GetFileSizeCallback = mojom::FileSystemInstance::GetFileSizeCallback;
+ using OpenFileToReadCallback =
+ mojom::FileSystemInstance::OpenFileToReadCallback;
+ using GetDocumentCallback = mojom::FileSystemInstance::GetDocumentCallback;
+ using GetChildDocumentsCallback =
+ mojom::FileSystemInstance::GetChildDocumentsCallback;
+
+ // Runs file system operations. See file_system.mojom for documentation.
+ virtual void GetFileSize(const GURL& url,
+ const GetFileSizeCallback& callback) = 0;
+ virtual void OpenFileToRead(const GURL& url,
+ const OpenFileToReadCallback& callback) = 0;
+ virtual void GetDocument(const std::string& authority,
+ const std::string& document_id,
+ const GetDocumentCallback& callback) = 0;
+ virtual void GetChildDocuments(const std::string& authority,
+ const std::string& parent_document_id,
+ const GetChildDocumentsCallback& callback) = 0;
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_FILE_SYSTEM_ARC_FILE_SYSTEM_OPERATION_RUNNER_H_
diff --git a/chromium/components/arc/ime/arc_ime_bridge_impl.cc b/chromium/components/arc/ime/arc_ime_bridge_impl.cc
index aa36d4f88b6..13d787ef5ec 100644
--- a/chromium/components/arc/ime/arc_ime_bridge_impl.cc
+++ b/chromium/components/arc/ime/arc_ime_bridge_impl.cc
@@ -17,9 +17,6 @@
namespace arc {
namespace {
-constexpr uint32_t kMinVersionForOnKeyboardsBoundsChanging = 3;
-constexpr uint32_t kMinVersionForExtendSelectionAndDelete = 4;
-
ui::TextInputType ConvertTextInputType(mojom::TextInputType ipc_type) {
// The two enum types are similar, but intentionally made not identical.
// We cannot force them to be in sync. If we do, updates in ui::TextInputType
@@ -86,7 +83,7 @@ ArcImeBridgeImpl::~ArcImeBridgeImpl() {
}
void ArcImeBridgeImpl::OnInstanceReady() {
- auto* instance = bridge_service_->ime()->GetInstanceForMethod("Init");
+ auto* instance = ARC_GET_INSTANCE_FOR_METHOD(bridge_service_->ime(), Init);
DCHECK(instance);
instance->Init(binding_.CreateInterfacePtrAndBind());
}
@@ -94,7 +91,7 @@ void ArcImeBridgeImpl::OnInstanceReady() {
void ArcImeBridgeImpl::SendSetCompositionText(
const ui::CompositionText& composition) {
auto* ime_instance =
- bridge_service_->ime()->GetInstanceForMethod("SetCompositionText");
+ ARC_GET_INSTANCE_FOR_METHOD(bridge_service_->ime(), SetCompositionText);
if (!ime_instance)
return;
@@ -103,8 +100,8 @@ void ArcImeBridgeImpl::SendSetCompositionText(
}
void ArcImeBridgeImpl::SendConfirmCompositionText() {
- auto* ime_instance =
- bridge_service_->ime()->GetInstanceForMethod("ConfirmCompositionText");
+ auto* ime_instance = ARC_GET_INSTANCE_FOR_METHOD(bridge_service_->ime(),
+ ConfirmCompositionText);
if (!ime_instance)
return;
@@ -113,7 +110,7 @@ void ArcImeBridgeImpl::SendConfirmCompositionText() {
void ArcImeBridgeImpl::SendInsertText(const base::string16& text) {
auto* ime_instance =
- bridge_service_->ime()->GetInstanceForMethod("InsertText");
+ ARC_GET_INSTANCE_FOR_METHOD(bridge_service_->ime(), InsertText);
if (!ime_instance)
return;
@@ -122,8 +119,8 @@ void ArcImeBridgeImpl::SendInsertText(const base::string16& text) {
void ArcImeBridgeImpl::SendOnKeyboardBoundsChanging(
const gfx::Rect& new_bounds) {
- auto* ime_instance = bridge_service_->ime()->GetInstanceForMethod(
- "OnKeyboardBoundsChanging", kMinVersionForOnKeyboardsBoundsChanging);
+ auto* ime_instance = ARC_GET_INSTANCE_FOR_METHOD(bridge_service_->ime(),
+ OnKeyboardBoundsChanging);
if (!ime_instance)
return;
@@ -132,8 +129,8 @@ void ArcImeBridgeImpl::SendOnKeyboardBoundsChanging(
void ArcImeBridgeImpl::SendExtendSelectionAndDelete(
size_t before, size_t after) {
- auto* ime_instance = bridge_service_->ime()->GetInstanceForMethod(
- "ExtendSelectionAndDelete", kMinVersionForExtendSelectionAndDelete);
+ auto* ime_instance = ARC_GET_INSTANCE_FOR_METHOD(bridge_service_->ime(),
+ ExtendSelectionAndDelete);
if (!ime_instance)
return;
diff --git a/chromium/components/arc/ime/arc_ime_service.h b/chromium/components/arc/ime/arc_ime_service.h
index a88e2cb3f8a..12610a8bc36 100644
--- a/chromium/components/arc/ime/arc_ime_service.h
+++ b/chromium/components/arc/ime/arc_ime_service.h
@@ -109,7 +109,7 @@ class ArcImeService : public ArcService,
bool ChangeTextDirectionAndLayoutAlignment(
base::i18n::TextDirection direction) override;
void ExtendSelectionAndDelete(size_t before, size_t after) override;
- void EnsureCaretInRect(const gfx::Rect& rect) override {}
+ void EnsureCaretNotInRect(const gfx::Rect& rect) override {}
bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override {
}
diff --git a/chromium/components/arc/ime/arc_ime_service_unittest.cc b/chromium/components/arc/ime/arc_ime_service_unittest.cc
index 3c9af137004..086bea93c93 100644
--- a/chromium/components/arc/ime/arc_ime_service_unittest.cc
+++ b/chromium/components/arc/ime/arc_ime_service_unittest.cc
@@ -11,7 +11,7 @@
#include "base/memory/ptr_util.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
-#include "components/arc/test/fake_arc_bridge_service.h"
+#include "components/arc/arc_bridge_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/test/test_windows.h"
@@ -133,7 +133,7 @@ class ArcImeServiceTest : public testing::Test {
ArcImeServiceTest() {}
protected:
- std::unique_ptr<FakeArcBridgeService> fake_arc_bridge_service_;
+ std::unique_ptr<ArcBridgeService> arc_bridge_service_;
std::unique_ptr<FakeInputMethod> fake_input_method_;
std::unique_ptr<ArcImeService> instance_;
FakeArcImeBridge* fake_arc_ime_bridge_; // Owned by |instance_|
@@ -143,12 +143,12 @@ class ArcImeServiceTest : public testing::Test {
private:
void SetUp() override {
- fake_arc_bridge_service_.reset(new FakeArcBridgeService);
- instance_.reset(new ArcImeService(fake_arc_bridge_service_.get()));
- fake_arc_ime_bridge_ = new FakeArcImeBridge;
+ arc_bridge_service_ = base::MakeUnique<ArcBridgeService>();
+ instance_ = base::MakeUnique<ArcImeService>(arc_bridge_service_.get());
+ fake_arc_ime_bridge_ = new FakeArcImeBridge();
instance_->SetImeBridgeForTesting(base::WrapUnique(fake_arc_ime_bridge_));
- fake_input_method_.reset(new FakeInputMethod);
+ fake_input_method_ = base::MakeUnique<FakeInputMethod>();
instance_->SetInputMethodForTesting(fake_input_method_.get());
fake_window_detector_ = new FakeArcWindowDetector();
@@ -162,7 +162,7 @@ class ArcImeServiceTest : public testing::Test {
fake_window_detector_ = nullptr;
fake_arc_ime_bridge_ = nullptr;
instance_.reset();
- fake_arc_bridge_service_.reset();
+ arc_bridge_service_.reset();
}
};
diff --git a/chromium/components/arc/instance_holder.h b/chromium/components/arc/instance_holder.h
index 8455814581c..fda056e6461 100644
--- a/chromium/components/arc/instance_holder.h
+++ b/chromium/components/arc/instance_holder.h
@@ -6,6 +6,7 @@
#define COMPONENTS_ARC_INSTANCE_HOLDER_H_
#include <string>
+#include <type_traits>
#include <utility>
#include "base/bind.h"
@@ -13,6 +14,16 @@
#include "base/observer_list.h"
#include "base/threading/thread_checker.h"
+// A macro to call InstanceHolder<T>::GetInstanceForVersionDoNotCallDirectly().
+// In order to avoid exposing method names from within the Mojo bindings, we
+// will rely on stringification and the fact that the method min versions have a
+// consistent naming scheme.
+#define ARC_GET_INSTANCE_FOR_METHOD(holder, method_name) \
+ (holder)->GetInstanceForVersionDoNotCallDirectly( \
+ std::remove_pointer<decltype( \
+ holder)>::type::Instance::k##method_name##MinVersion, \
+ #method_name)
+
namespace arc {
// Holds a Mojo instance+version pair. This also allows for listening for state
@@ -34,6 +45,8 @@ class InstanceHolder {
virtual ~Observer() = default;
};
+ using Instance = T;
+
InstanceHolder() = default;
// Returns true if the Mojo interface is ready at least for its version 0
@@ -45,9 +58,11 @@ class InstanceHolder {
// |method_name_for_logging|, but only if its reported version is at least
// |min_version|. Returns nullptr if the instance is either not ready or does
// not have the requested version, and logs appropriately.
- // TODO(lhchavez): Improve the API. (crbug.com/649782)
- T* GetInstanceForMethod(const std::string& method_name_for_logging,
- uint32_t min_version) {
+ // This function should not be called directly. Instead, use the
+ // ARC_GET_INSTANCE_FOR_METHOD() macro.
+ T* GetInstanceForVersionDoNotCallDirectly(
+ uint32_t min_version,
+ const char method_name_for_logging[]) {
if (!instance_) {
VLOG(1) << "Instance for " << T::Name_ << "::" << method_name_for_logging
<< " not available.";
@@ -63,11 +78,6 @@ class InstanceHolder {
return instance_;
}
- // Same as the above, but for the version zero.
- T* GetInstanceForMethod(const std::string& method_name_for_logging) {
- return GetInstanceForMethod(method_name_for_logging, 0u);
- }
-
// Adds or removes observers. This can only be called on the thread that this
// class was created on. RemoveObserver does nothing if |observer| is not in
// the list.
diff --git a/chromium/components/arc/intent_helper/OWNERS b/chromium/components/arc/intent_helper/OWNERS
new file mode 100644
index 00000000000..bb6511619b7
--- /dev/null
+++ b/chromium/components/arc/intent_helper/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/intent_helper/activity_icon_loader.cc b/chromium/components/arc/intent_helper/activity_icon_loader.cc
index a0d74f5158f..d83b6a1da1b 100644
--- a/chromium/components/arc/intent_helper/activity_icon_loader.cc
+++ b/chromium/components/arc/intent_helper/activity_icon_loader.cc
@@ -28,14 +28,27 @@ constexpr size_t kLargeIconSizeInDip = 20;
constexpr size_t kMaxIconSizeInPx = 200;
constexpr char kPngDataUrlPrefix[] = "data:image/png;base64,";
-constexpr int kMinInstanceVersion = 3; // see intent_helper.mojom
-
ui::ScaleFactor GetSupportedScaleFactor() {
std::vector<ui::ScaleFactor> scale_factors = ui::GetSupportedScaleFactors();
DCHECK(!scale_factors.empty());
return scale_factors.back();
}
+// Returns an instance for calling RequestActivityIcons().
+mojom::IntentHelperInstance* GetInstanceForRequestActivityIcons(
+ ArcIntentHelperBridge::GetResult* out_error_code) {
+ if (!ArcIntentHelperBridge::IsIntentHelperAvailable(out_error_code))
+ return nullptr;
+ auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
+ ArcServiceManager::Get()->arc_bridge_service()->intent_helper(),
+ RequestActivityIcons);
+ if (!instance && out_error_code) {
+ *out_error_code =
+ ArcIntentHelperBridge::GetResult::FAILED_ARC_NOT_SUPPORTED;
+ }
+ return instance;
+}
+
} // namespace
ActivityIconLoader::Icons::Icons(
@@ -97,8 +110,7 @@ ActivityIconLoader::GetResult ActivityIconLoader::GetActivityIcons(
}
ArcIntentHelperBridge::GetResult error_code;
- auto* instance = ArcIntentHelperBridge::GetIntentHelperInstanceWithErrorCode(
- "RequestActivityIcons", kMinInstanceVersion, &error_code);
+ auto* instance = GetInstanceForRequestActivityIcons(&error_code);
if (!instance) {
// The mojo channel is not yet ready (or not supported at all). Run the
// callback with |result| that could be empty.
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 7dcb8be6a9a..deb7ea30688 100644
--- a/chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc
+++ b/chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -6,14 +6,15 @@
#include <utility>
+#include "ash/common/new_window_controller.h"
#include "ash/common/shell_delegate.h"
#include "ash/common/wallpaper/wallpaper_controller.h"
#include "ash/common/wm_shell.h"
-#include "ash/public/interfaces/new_window.mojom.h"
#include "ash/shell.h"
#include "base/command_line.h"
#include "base/memory/weak_ptr.h"
#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_service_manager.h"
#include "components/arc/intent_helper/activity_icon_loader.h"
#include "components/arc/intent_helper/link_handler_model_impl.h"
#include "components/arc/intent_helper/local_activity_resolver.h"
@@ -23,6 +24,10 @@
namespace arc {
// static
+const char ArcIntentHelperBridge::kArcServiceName[] =
+ "arc::ArcIntentHelperBridge";
+
+// static
const char ArcIntentHelperBridge::kArcIntentHelperPackageName[] =
"org.chromium.arc.intent_helper";
@@ -47,7 +52,7 @@ void ArcIntentHelperBridge::OnInstanceReady() {
DCHECK(thread_checker_.CalledOnValidThread());
ash::Shell::GetInstance()->set_link_handler_model_factory(this);
auto* instance =
- arc_bridge_service()->intent_helper()->GetInstanceForMethod("Init");
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->intent_helper(), Init);
DCHECK(instance);
instance->Init(binding_.CreateInterfacePtrAndBind());
}
@@ -68,7 +73,7 @@ void ArcIntentHelperBridge::OnOpenDownloads() {
// 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
// downloads, which is probably not ideal.
- ash::WmShell::Get()->new_window_client()->OpenFileManager();
+ ash::WmShell::Get()->new_window_controller()->OpenFileManager();
}
void ArcIntentHelperBridge::OnOpenUrl(const std::string& url) {
@@ -87,6 +92,14 @@ void ArcIntentHelperBridge::SetWallpaperDeprecated(
LOG(ERROR) << "IntentHelper.SetWallpaper is deprecated";
}
+void ArcIntentHelperBridge::AddObserver(ArcIntentHelperObserver* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void ArcIntentHelperBridge::RemoveObserver(ArcIntentHelperObserver* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
std::unique_ptr<ash::LinkHandlerModel> ArcIntentHelperBridge::CreateModel(
const GURL& url) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -117,14 +130,12 @@ ArcIntentHelperBridge::FilterOutIntentHelper(
}
// static
-mojom::IntentHelperInstance*
-ArcIntentHelperBridge::GetIntentHelperInstanceWithErrorCode(
- const std::string& method_name_for_logging,
- uint32_t min_instance_version,
- GetResult* out_error_code) {
- ArcBridgeService* bridge_service = ArcBridgeService::Get();
- if (!bridge_service) {
- if (!ArcBridgeService::GetEnabled(base::CommandLine::ForCurrentProcess())) {
+bool ArcIntentHelperBridge::IsIntentHelperAvailable(GetResult* out_error_code) {
+ auto* arc_service_manager = ArcServiceManager::Get();
+ if (!arc_service_manager) {
+ if (!ArcBridgeService::GetEnabled(base::CommandLine::ForCurrentProcess()) &&
+ !ArcBridgeService::GetKioskStarted(
+ base::CommandLine::ForCurrentProcess())) {
VLOG(2) << "ARC bridge is not supported.";
if (out_error_code)
*out_error_code = GetResult::FAILED_ARC_NOT_SUPPORTED;
@@ -133,38 +144,28 @@ ArcIntentHelperBridge::GetIntentHelperInstanceWithErrorCode(
if (out_error_code)
*out_error_code = GetResult::FAILED_ARC_NOT_READY;
}
- return nullptr;
+ return false;
}
- if (!bridge_service->intent_helper()->has_instance()) {
+ auto* intent_helper_holder =
+ arc_service_manager->arc_bridge_service()->intent_helper();
+ if (!intent_helper_holder->has_instance()) {
VLOG(2) << "ARC intent helper instance is not ready.";
if (out_error_code)
*out_error_code = GetResult::FAILED_ARC_NOT_READY;
- return nullptr;
+ return false;
}
- auto* instance = bridge_service->intent_helper()->GetInstanceForMethod(
- method_name_for_logging, min_instance_version);
- if (!instance) {
- if (out_error_code)
- *out_error_code = GetResult::FAILED_ARC_NOT_SUPPORTED;
- return nullptr;
- }
- return instance;
-}
-
-// static
-mojom::IntentHelperInstance* ArcIntentHelperBridge::GetIntentHelperInstance(
- const std::string& method_name_for_logging,
- uint32_t min_instance_version) {
- return GetIntentHelperInstanceWithErrorCode(method_name_for_logging,
- min_instance_version, nullptr);
+ return true;
}
void ArcIntentHelperBridge::OnIntentFiltersUpdated(
- std::vector<mojom::IntentFilterPtr> filters) {
+ std::vector<IntentFilter> filters) {
DCHECK(thread_checker_.CalledOnValidThread());
activity_resolver_->UpdateIntentFilters(std::move(filters));
+
+ for (auto& observer : observer_list_)
+ observer.OnIntentFiltersUpdated();
}
} // namespace arc
diff --git a/chromium/components/arc/intent_helper/arc_intent_helper_bridge.h b/chromium/components/arc/intent_helper/arc_intent_helper_bridge.h
index eba6e10fe7e..2a3a5676dd6 100644
--- a/chromium/components/arc/intent_helper/arc_intent_helper_bridge.h
+++ b/chromium/components/arc/intent_helper/arc_intent_helper_bridge.h
@@ -12,10 +12,12 @@
#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/arc_intent_helper_observer.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace ash {
@@ -28,8 +30,8 @@ namespace arc {
class ActivityIconLoader;
class ArcBridgeService;
+class IntentFilter;
class LocalActivityResolver;
-class SetWallpaperDelegate;
// Receives intents from ARC.
class ArcIntentHelperBridge
@@ -53,6 +55,9 @@ class ArcIntentHelperBridge
const scoped_refptr<LocalActivityResolver>& activity_resolver);
~ArcIntentHelperBridge() override;
+ void AddObserver(ArcIntentHelperObserver* observer);
+ void RemoveObserver(ArcIntentHelperObserver* observer);
+
// InstanceHolder<mojom::IntentHelperInstance>::Observer
void OnInstanceReady() override;
void OnInstanceClosed() override;
@@ -60,7 +65,7 @@ class ArcIntentHelperBridge
// mojom::IntentHelperHost
void OnIconInvalidated(const std::string& package_name) override;
void OnIntentFiltersUpdated(
- std::vector<mojom::IntentFilterPtr> intent_filters) override;
+ std::vector<IntentFilter> intent_filters) override;
void OnOpenDownloads() override;
void OnOpenUrl(const std::string& url) override;
void OpenWallpaperPicker() override;
@@ -77,17 +82,12 @@ class ArcIntentHelperBridge
static std::vector<mojom::IntentHandlerInfoPtr> FilterOutIntentHelper(
std::vector<mojom::IntentHandlerInfoPtr> handlers);
- // Gets the mojo instance if it's available. On failure, returns nullptr and
- // updates |out_error_code| if it's not nullptr.
- static mojom::IntentHelperInstance* GetIntentHelperInstanceWithErrorCode(
- const std::string& method_name_for_logging,
- uint32_t min_instance_version,
- GetResult* out_error_code);
+ // Checks if the intent helper interface is available. When it is not, returns
+ // false and updates |out_error_code| if it's not nullptr.
+ static bool IsIntentHelperAvailable(GetResult* out_error_code);
- // Does the same as above without asking for the error code.
- static mojom::IntentHelperInstance* GetIntentHelperInstance(
- const std::string& method_name_for_logging,
- uint32_t min_instance_version);
+ // For supporting ArcServiceManager::GetService<T>().
+ static const char kArcServiceName[];
static const char kArcIntentHelperPackageName[];
@@ -98,6 +98,8 @@ class ArcIntentHelperBridge
base::ThreadChecker thread_checker_;
+ base::ObserverList<ArcIntentHelperObserver> observer_list_;
+
DISALLOW_COPY_AND_ASSIGN(ArcIntentHelperBridge);
};
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 07ed13cbe76..d849fe0b519 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
@@ -6,14 +6,51 @@
#include <utility>
+#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/activity_icon_loader.h"
+#include "components/arc/intent_helper/local_activity_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace arc {
+namespace {
+
+class ArcIntentHelperTest : public testing::Test {
+ public:
+ ArcIntentHelperTest() = default;
+
+ protected:
+ std::unique_ptr<ArcBridgeService> arc_bridge_service_;
+ scoped_refptr<ActivityIconLoader> icon_loader_;
+ scoped_refptr<LocalActivityResolver> activity_resolver_;
+ std::unique_ptr<ArcIntentHelperBridge> instance_;
+
+ private:
+ void SetUp() override {
+ arc_bridge_service_ = base::MakeUnique<ArcBridgeService>();
+ icon_loader_ = new ActivityIconLoader();
+ activity_resolver_ = new LocalActivityResolver();
+ instance_ = base::MakeUnique<ArcIntentHelperBridge>(
+ arc_bridge_service_.get(), icon_loader_, activity_resolver_);
+ }
+
+ void TearDown() override {
+ instance_.reset();
+ activity_resolver_ = nullptr;
+ icon_loader_ = nullptr;
+ arc_bridge_service_.reset();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ArcIntentHelperTest);
+};
+
+} // namespace
+
// Tests if IsIntentHelperPackage works as expected. Probably too trivial
// to test but just in case.
-TEST(ArcIntentHelperTest, TestIsIntentHelperPackage) {
+TEST_F(ArcIntentHelperTest, TestIsIntentHelperPackage) {
EXPECT_FALSE(ArcIntentHelperBridge::IsIntentHelperPackage(""));
EXPECT_FALSE(ArcIntentHelperBridge::IsIntentHelperPackage(
ArcIntentHelperBridge::kArcIntentHelperPackageName + std::string("a")));
@@ -25,7 +62,7 @@ TEST(ArcIntentHelperTest, TestIsIntentHelperPackage) {
}
// Tests if FilterOutIntentHelper removes handlers as expected.
-TEST(ArcIntentHelperTest, TestFilterOutIntentHelper) {
+TEST_F(ArcIntentHelperTest, TestFilterOutIntentHelper) {
{
std::vector<mojom::IntentHandlerInfoPtr> orig;
std::vector<mojom::IntentHandlerInfoPtr> filtered =
@@ -101,4 +138,31 @@ TEST(ArcIntentHelperTest, TestFilterOutIntentHelper) {
}
}
+// Tests if observer works as expected.
+TEST_F(ArcIntentHelperTest, TestObserver) {
+ class FakeObserver : public ArcIntentHelperObserver {
+ public:
+ FakeObserver() = default;
+ void OnIntentFiltersUpdated() override { updated_ = true; }
+ bool IsUpdated() { return updated_; }
+ void Reset() { updated_ = false; }
+
+ private:
+ bool updated_ = false;
+ };
+
+ // Observer should be called when intent filter is updated.
+ auto observer = base::MakeUnique<FakeObserver>();
+ instance_->AddObserver(observer.get());
+ EXPECT_FALSE(observer->IsUpdated());
+ instance_->OnIntentFiltersUpdated(std::vector<IntentFilter>());
+ EXPECT_TRUE(observer->IsUpdated());
+
+ // Observer should not be called after it's removed.
+ observer->Reset();
+ instance_->RemoveObserver(observer.get());
+ instance_->OnIntentFiltersUpdated(std::vector<IntentFilter>());
+ EXPECT_FALSE(observer->IsUpdated());
+}
+
} // namespace arc
diff --git a/chromium/components/arc/intent_helper/arc_intent_helper_observer.h b/chromium/components/arc/intent_helper/arc_intent_helper_observer.h
new file mode 100644
index 00000000000..d6d41e3e022
--- /dev/null
+++ b/chromium/components/arc/intent_helper/arc_intent_helper_observer.h
@@ -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.
+
+#ifndef COMPONENTS_ARC_INTENT_HELPER_ARC_INTENT_HELPER_OBSERVER_H_
+#define COMPONENTS_ARC_INTENT_HELPER_ARC_INTENT_HELPER_OBSERVER_H_
+
+namespace arc {
+
+class ArcIntentHelperObserver {
+ public:
+ virtual ~ArcIntentHelperObserver() = default;
+ // Called when intent filters are added or removed.
+ virtual void OnIntentFiltersUpdated() = 0;
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_INTENT_HELPER_ARC_INTENT_HELPER_OBSERVER_H_
diff --git a/chromium/components/arc/intent_helper/intent_filter.cc b/chromium/components/arc/intent_helper/intent_filter.cc
index 7dea60f1ed0..85f30b4f204 100644
--- a/chromium/components/arc/intent_helper/intent_filter.cc
+++ b/chromium/components/arc/intent_helper/intent_filter.cc
@@ -6,30 +6,27 @@
#include "base/compiler_specific.h"
#include "base/strings/string_util.h"
+#include "components/arc/common/intent_helper.mojom.h"
#include "url/gurl.h"
namespace arc {
-IntentFilter::IntentFilter(const mojom::IntentFilterPtr& mojo_intent_filter) {
- // TODO(yusukes): Use mojo typemaps to simplify the constructor.
- if (mojo_intent_filter->data_authorities.has_value()) {
- for (const mojom::AuthorityEntryPtr& authorityptr :
- *mojo_intent_filter->data_authorities) {
- authorities_.emplace_back(authorityptr);
- }
- }
- if (mojo_intent_filter->data_paths.has_value()) {
- for (const mojom::PatternMatcherPtr& pattern :
- *mojo_intent_filter->data_paths) {
- paths_.emplace_back(pattern);
- }
- }
-}
+IntentFilter::IntentFilter() = default;
+IntentFilter::IntentFilter(IntentFilter&& other) = default;
-IntentFilter::IntentFilter(const IntentFilter& other) = default;
+IntentFilter::IntentFilter(
+ std::vector<IntentFilter::AuthorityEntry> authorities,
+ std::vector<IntentFilter::PatternMatcher> paths)
+ : authorities_(std::move(authorities)) {
+ // In order to register a path we need to have at least one authority.
+ if (!authorities_.empty())
+ paths_ = std::move(paths);
+}
IntentFilter::~IntentFilter() = default;
+IntentFilter& IntentFilter::operator=(IntentFilter&& other) = default;
+
// Logically, this maps to IntentFilter#match, but this code only deals with
// view intents for http/https URLs and so it really only implements the
// #matchData part of the match code.
@@ -41,12 +38,14 @@ bool IntentFilter::Match(const GURL& url) const {
return false;
}
- // Match the authority and the path (if any).
+ // Match the authority and the path. If there are no authorities for this
+ // filter, we can treat this as a match, since we already know this filter
+ // has a http(s) scheme and it doesn't corresponds to a MIME type.
if (!authorities_.empty()) {
return MatchDataAuthority(url) && (paths_.empty() || HasDataPath(url));
}
- return false;
+ return true;
}
// Transcribed from android's IntentFilter#hasDataPath.
@@ -70,9 +69,15 @@ bool IntentFilter::MatchDataAuthority(const GURL& url) const {
return false;
}
+IntentFilter::AuthorityEntry::AuthorityEntry() = default;
IntentFilter::AuthorityEntry::AuthorityEntry(
- const mojom::AuthorityEntryPtr& entry)
- : host_(entry->host), port_(entry->port) {
+ IntentFilter::AuthorityEntry&& other) = default;
+
+IntentFilter::AuthorityEntry& IntentFilter::AuthorityEntry::operator=(
+ IntentFilter::AuthorityEntry&& other) = default;
+
+IntentFilter::AuthorityEntry::AuthorityEntry(const std::string& host, int port)
+ : host_(host), port_(port) {
// Wildcards are only allowed at the front of the host string.
wild_ = !host_.empty() && host_[0] == '*';
if (wild_) {
@@ -115,9 +120,16 @@ bool IntentFilter::AuthorityEntry::Match(const GURL& url) const {
}
}
+IntentFilter::PatternMatcher::PatternMatcher() = default;
IntentFilter::PatternMatcher::PatternMatcher(
- const mojom::PatternMatcherPtr& pattern)
- : pattern_(pattern->pattern), match_type_(pattern->type) {}
+ IntentFilter::PatternMatcher&& other) = default;
+
+IntentFilter::PatternMatcher::PatternMatcher(const std::string& pattern,
+ mojom::PatternType match_type)
+ : pattern_(pattern), match_type_(match_type) {}
+
+IntentFilter::PatternMatcher& IntentFilter::PatternMatcher::operator=(
+ IntentFilter::PatternMatcher&& other) = default;
// Transcribed from android's PatternMatcher#matchPattern.
bool IntentFilter::PatternMatcher::Match(const std::string& str) const {
diff --git a/chromium/components/arc/intent_helper/intent_filter.h b/chromium/components/arc/intent_helper/intent_filter.h
index eb07523432d..e6c447f5873 100644
--- a/chromium/components/arc/intent_helper/intent_filter.h
+++ b/chromium/components/arc/intent_helper/intent_filter.h
@@ -8,55 +8,90 @@
#include <string>
#include <vector>
-#include "components/arc/common/intent_helper.mojom.h"
+#include "base/macros.h"
class GURL;
namespace arc {
+namespace mojom {
+enum class PatternType;
+} // namespace mojom
+
// A chrome-side implementation of Android's IntentFilter class. This is used
// to approximate the intent filtering and determine whether a given URL is
// likely to be handled by any android-side apps, prior to making expensive IPC
// calls.
class IntentFilter {
public:
- explicit IntentFilter(const mojom::IntentFilterPtr& mojo_intent_filter);
- IntentFilter(const IntentFilter& other);
- ~IntentFilter();
-
- bool Match(const GURL& url) const;
-
- private:
// A helper class for handling matching of the host part of the URL.
class AuthorityEntry {
public:
- explicit AuthorityEntry(const mojom::AuthorityEntryPtr& entry);
+ AuthorityEntry();
+ AuthorityEntry(AuthorityEntry&& other);
+ AuthorityEntry(const std::string& host, int port);
+
+ AuthorityEntry& operator=(AuthorityEntry&& other);
+
bool Match(const GURL& url) const;
+ const std::string& host() const { return host_; }
+ int port() const { return port_; }
+
private:
std::string host_;
bool wild_;
int port_;
+
+ DISALLOW_COPY_AND_ASSIGN(AuthorityEntry);
};
// A helper class for handling matching of various patterns in the URL.
class PatternMatcher {
public:
- explicit PatternMatcher(const mojom::PatternMatcherPtr& pattern);
+ PatternMatcher();
+ PatternMatcher(PatternMatcher&& other);
+ PatternMatcher(const std::string& pattern, mojom::PatternType match_type);
+
+ PatternMatcher& operator=(PatternMatcher&& other);
+
bool Match(const std::string& match) const;
+ const std::string& pattern() const { return pattern_; }
+ mojom::PatternType match_type() const { return match_type_; }
+
private:
bool MatchGlob(const std::string& match) const;
std::string pattern_;
mojom::PatternType match_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(PatternMatcher);
};
+ IntentFilter();
+ IntentFilter(IntentFilter&& other);
+ IntentFilter(std::vector<AuthorityEntry> authorities,
+ std::vector<PatternMatcher> paths);
+ ~IntentFilter();
+
+ IntentFilter& operator=(IntentFilter&& other);
+
+ bool Match(const GURL& url) const;
+
+ const std::vector<AuthorityEntry>& authorities() const {
+ return authorities_;
+ }
+ const std::vector<PatternMatcher>& paths() const { return paths_; }
+
+ private:
bool MatchDataAuthority(const GURL& url) const;
bool HasDataPath(const GURL& url) const;
std::vector<AuthorityEntry> authorities_;
std::vector<PatternMatcher> paths_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntentFilter);
};
} // namespace arc
diff --git a/chromium/components/arc/intent_helper/intent_filter_struct_traits.cc b/chromium/components/arc/intent_helper/intent_filter_struct_traits.cc
new file mode 100644
index 00000000000..2ef34534c7f
--- /dev/null
+++ b/chromium/components/arc/intent_helper/intent_filter_struct_traits.cc
@@ -0,0 +1,55 @@
+// 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/intent_filter_struct_traits.h"
+
+#include "base/strings/string_util.h"
+
+namespace mojo {
+
+bool StructTraits<arc::mojom::IntentFilterDataView, arc::IntentFilter>::
+ Read(arc::mojom::IntentFilterDataView data,
+ arc::IntentFilter* out) {
+ std::vector<arc::IntentFilter::AuthorityEntry> authorities;
+ if (!data.ReadDataAuthorities(&authorities))
+ return false;
+
+ std::vector<arc::IntentFilter::PatternMatcher> paths;
+ if (!data.ReadDataPaths(&paths))
+ return false;
+
+ *out = arc::IntentFilter(std::move(authorities), std::move(paths));
+ return true;
+}
+
+bool StructTraits<arc::mojom::AuthorityEntryDataView,
+ arc::IntentFilter::AuthorityEntry>::
+ Read(arc::mojom::AuthorityEntryDataView data,
+ arc::IntentFilter::AuthorityEntry* out) {
+ std::string host;
+ bool result = data.ReadHost(&host);
+ if (!result)
+ return false;
+
+ *out = arc::IntentFilter::AuthorityEntry(std::move(host), data.port());
+ return true;
+}
+
+bool StructTraits<arc::mojom::PatternMatcherDataView,
+ arc::IntentFilter::PatternMatcher>::
+ Read(arc::mojom::PatternMatcherDataView data,
+ arc::IntentFilter::PatternMatcher* out) {
+ std::string pattern;
+ if (!data.ReadPattern(&pattern))
+ return false;
+
+ arc::mojom::PatternType type;
+ if (!data.ReadType(&type))
+ return false;
+
+ *out = arc::IntentFilter::PatternMatcher(std::move(pattern), type);
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/components/arc/intent_helper/intent_filter_struct_traits.h b/chromium/components/arc/intent_helper/intent_filter_struct_traits.h
new file mode 100644
index 00000000000..ca9ba9c1dc9
--- /dev/null
+++ b/chromium/components/arc/intent_helper/intent_filter_struct_traits.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 COMPONENT_ARC_INTENT_FILTER_INTENT_FILTER_STRUCT_TRAITS_H_
+#define COMPONENT_ARC_INTENT_FILTER_INTENT_FILTER_STRUCT_TRAITS_H_
+
+#include "components/arc/common/intent_helper.mojom.h"
+#include "components/arc/intent_helper/intent_filter.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<arc::mojom::IntentFilterDataView, arc::IntentFilter> {
+ static const mojo::CArray<std::string> actions(const arc::IntentFilter& r) {
+ // Returns an empty array.
+ return mojo::CArray<std::string>();
+ }
+ static const mojo::CArray<std::string> categories(
+ const arc::IntentFilter& r) {
+ // Returns an empty array.
+ return mojo::CArray<std::string>();
+ }
+ static const mojo::CArray<std::string> data_schemes(
+ const arc::IntentFilter& r) {
+ // Returns an empty array.
+ return mojo::CArray<std::string>();
+ }
+ static const std::vector<arc::IntentFilter::AuthorityEntry>& data_authorities(
+ const arc::IntentFilter& r) {
+ return r.authorities();
+ }
+ static const std::vector<arc::IntentFilter::PatternMatcher>& data_paths(
+ const arc::IntentFilter& r) {
+ return r.paths();
+ }
+ static const mojo::CArray<arc::IntentFilter::PatternMatcher>
+ deprecated_data_scheme_specific_parts(const arc::IntentFilter& r) {
+ // Returns an empty array.
+ return mojo::CArray<arc::IntentFilter::PatternMatcher>();
+ }
+
+ static bool Read(arc::mojom::IntentFilterDataView data,
+ arc::IntentFilter* out);
+};
+
+template <>
+struct StructTraits<arc::mojom::AuthorityEntryDataView,
+ arc::IntentFilter::AuthorityEntry> {
+ static const std::string& host(const arc::IntentFilter::AuthorityEntry& r) {
+ return r.host();
+ }
+ static int32_t port(const arc::IntentFilter::AuthorityEntry& r) {
+ return r.port();
+ }
+
+ static bool Read(arc::mojom::AuthorityEntryDataView data,
+ arc::IntentFilter::AuthorityEntry* out);
+};
+
+template <>
+struct StructTraits<arc::mojom::PatternMatcherDataView,
+ arc::IntentFilter::PatternMatcher> {
+ static const std::string& pattern(
+ const arc::IntentFilter::PatternMatcher& r) {
+ return r.pattern();
+ }
+ static arc::mojom::PatternType type(
+ const arc::IntentFilter::PatternMatcher& r) {
+ return r.match_type();
+ }
+
+ static bool Read(arc::mojom::PatternMatcherDataView data,
+ arc::IntentFilter::PatternMatcher* out);
+};
+
+} // namespace mojo
+
+#endif // COMPONENT_ARC_INTENT_FILTER_INTENT_FILTER_STRUCT_TRAITS_H_
diff --git a/chromium/components/arc/intent_helper/intent_filter_unittest.cc b/chromium/components/arc/intent_helper/intent_filter_unittest.cc
index 10881fae85f..1205c2e529c 100644
--- a/chromium/components/arc/intent_helper/intent_filter_unittest.cc
+++ b/chromium/components/arc/intent_helper/intent_filter_unittest.cc
@@ -20,41 +20,32 @@ namespace {
class IntentFilterBuilder {
public:
- IntentFilterBuilder():
- filter_spec_(mojom::IntentFilter::New()) {
- }
+ IntentFilterBuilder() = default;
IntentFilterBuilder& authority(const std::string& host) {
return authority(host, -1);
}
IntentFilterBuilder& authority(const std::string& host, int port) {
- mojom::AuthorityEntryPtr ae = mojom::AuthorityEntry::New();
- ae->host = host;
- ae->port = port;
- if (!filter_spec_->data_authorities.has_value())
- filter_spec_->data_authorities = std::vector<mojom::AuthorityEntryPtr>();
- filter_spec_->data_authorities->push_back(std::move(ae));
+ authorities_.emplace_back(host, port);
return *this;
}
IntentFilterBuilder& path(const std::string& path,
const mojom::PatternType& type) {
- mojom::PatternMatcherPtr p = mojom::PatternMatcher::New();
- p->pattern = path;
- p->type = type;
- if (!filter_spec_->data_paths.has_value())
- filter_spec_->data_paths = std::vector<mojom::PatternMatcherPtr>();
- filter_spec_->data_paths->push_back(std::move(p));
+ paths_.emplace_back(path, type);
return *this;
}
- operator IntentFilter() const {
- return std::move(IntentFilter(filter_spec_));
+ operator IntentFilter() {
+ return IntentFilter(std::move(authorities_), std::move(paths_));
}
private:
- mojom::IntentFilterPtr filter_spec_;
+ std::vector<IntentFilter::AuthorityEntry> authorities_;
+ std::vector<IntentFilter::PatternMatcher> paths_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntentFilterBuilder);
};
} // namespace
@@ -85,6 +76,19 @@ TEST(IntentFilterTest, TestAuthorityEntry_simple) {
EXPECT_TRUE(filter.Match(GURL("https://authority1")));
}
+TEST(IntentFilterTest, TestNoAuthorityEntry_simple) {
+ // An empty authority will act as a wildcard, so any http(s) URL will match.
+ IntentFilter filter = IntentFilterBuilder();
+
+ EXPECT_TRUE(filter.Match(GURL("http://validscheme1")));
+ EXPECT_TRUE(filter.Match(GURL("http://validscheme1/path")));
+ EXPECT_TRUE(filter.Match(GURL("https://validscheme2")));
+ EXPECT_TRUE(filter.Match(GURL("https://validscheme2/path")));
+
+ EXPECT_FALSE(filter.Match(GURL("ftp://wedontsupportallschemes")));
+ EXPECT_FALSE(filter.Match(GURL("ftp://wedontsupportallschemes/path")));
+}
+
TEST(IntentFilterTest, TestAuthorityEntry_no_port) {
// A filter with no port should accept matching authority URLs with any port.
IntentFilter filter_no_port = IntentFilterBuilder()
@@ -100,6 +104,19 @@ TEST(IntentFilterTest, TestAuthorityEntry_no_port) {
EXPECT_TRUE(filter_no_port.Match(GURL("https://authority1:65535")));
}
+TEST(IntentFilterTest, TestNoAuthorityEntry_no_port) {
+ // A filter with no port and no authority is still considered a wildcard.
+ IntentFilter filter = IntentFilterBuilder();
+
+ EXPECT_TRUE(filter.Match(GURL("http://validscheme1:0")));
+ EXPECT_TRUE(filter.Match(GURL("http://validscheme1:0/path")));
+ EXPECT_TRUE(filter.Match(GURL("https://validscheme2:420")));
+ EXPECT_TRUE(filter.Match(GURL("https://validscheme2:420/path")));
+
+ EXPECT_FALSE(filter.Match(GURL("custom-scheme://unvalidscheme:0")));
+ EXPECT_FALSE(filter.Match(GURL("custom-scheme://unvalidscheme:0/path")));
+}
+
TEST(IntentFilterTest, TestAuthorityEntry_with_port) {
// A filter with a specified port should only match URLs with that port.
IntentFilter filter_port_100 = IntentFilterBuilder()
@@ -187,6 +204,17 @@ TEST(IntentFilterTest, TestDataPath_literal) {
EXPECT_TRUE(filter.Match(GURL("http://host.com/path1")));
}
+TEST(IntentFilterTest, TestNoAuthorityDataPath_literal) {
+ IntentFilter filter =
+ IntentFilterBuilder().path("/path", mojom::PatternType::PATTERN_LITERAL);
+
+ // A filter with no authority and a custom path must still match our URL.
+ EXPECT_TRUE(filter.Match(GURL("http://validscheme1")));
+ EXPECT_TRUE(filter.Match(GURL("http://validscheme1:0")));
+ EXPECT_TRUE(filter.Match(GURL("http://validscheme1:0/path")));
+ EXPECT_TRUE(filter.Match(GURL("http://validscheme1:10/other/path")));
+}
+
TEST(IntentFilterTest, TestDataPath_prefix) {
IntentFilter filter = IntentFilterBuilder()
.authority("host.com")
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 5bf0193bb57..19485e179ff 100644
--- a/chromium/components/arc/intent_helper/link_handler_model_impl.cc
+++ b/chromium/components/arc/intent_helper/link_handler_model_impl.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_service_manager.h"
#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
#include "components/google/core/browser/google_util.h"
#include "url/gurl.h"
@@ -18,7 +19,6 @@ namespace arc {
namespace {
-constexpr int kMinInstanceVersion = 2; // see intent_helper.mojom
constexpr int kMaxValueLen = 2048;
bool GetQueryValue(const GURL& url,
@@ -55,8 +55,12 @@ LinkHandlerModelImpl::LinkHandlerModelImpl(
LinkHandlerModelImpl::~LinkHandlerModelImpl() {}
bool LinkHandlerModelImpl::Init(const GURL& url) {
- auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
- "RequestUrlHandlerList", kMinInstanceVersion);
+ auto* arc_service_manager = ArcServiceManager::Get();
+ if (!arc_service_manager)
+ return false;
+ auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_service_manager->arc_bridge_service()->intent_helper(),
+ RequestUrlHandlerList);
if (!instance)
return false;
@@ -77,8 +81,11 @@ void LinkHandlerModelImpl::AddObserver(Observer* observer) {
void LinkHandlerModelImpl::OpenLinkWithHandler(const GURL& url,
uint32_t handler_id) {
- auto* instance = ArcIntentHelperBridge::GetIntentHelperInstance(
- "HandleUrl", kMinInstanceVersion);
+ auto* arc_service_manager = ArcServiceManager::Get();
+ if (!arc_service_manager)
+ return;
+ auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_service_manager->arc_bridge_service()->intent_helper(), HandleUrl);
if (!instance)
return;
if (handler_id >= handlers_.size())
diff --git a/chromium/components/arc/intent_helper/local_activity_resolver.cc b/chromium/components/arc/intent_helper/local_activity_resolver.cc
index b9939d423ce..8ea0c55bf4b 100644
--- a/chromium/components/arc/intent_helper/local_activity_resolver.cc
+++ b/chromium/components/arc/intent_helper/local_activity_resolver.cc
@@ -28,10 +28,8 @@ bool LocalActivityResolver::ShouldChromeHandleUrl(const GURL& url) {
}
void LocalActivityResolver::UpdateIntentFilters(
- std::vector<mojom::IntentFilterPtr> mojo_intent_filters) {
- intent_filters_.clear();
- for (mojom::IntentFilterPtr& mojo_filter : mojo_intent_filters)
- intent_filters_.emplace_back(mojo_filter);
+ 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
index ff3715659aa..353d31c9ed2 100644
--- a/chromium/components/arc/intent_helper/local_activity_resolver.h
+++ b/chromium/components/arc/intent_helper/local_activity_resolver.h
@@ -29,7 +29,7 @@ class LocalActivityResolver : public base::RefCounted<LocalActivityResolver> {
bool ShouldChromeHandleUrl(const GURL& url);
// Called when the list of intent filters on ARC side is updated.
- void UpdateIntentFilters(std::vector<mojom::IntentFilterPtr> intent_filters);
+ void UpdateIntentFilters(std::vector<IntentFilter> intent_filters);
private:
friend class base::RefCounted<LocalActivityResolver>;
diff --git a/chromium/components/arc/intent_helper/local_activity_resolver_unittest.cc b/chromium/components/arc/intent_helper/local_activity_resolver_unittest.cc
index fff6076e4e8..09bf44588a9 100644
--- a/chromium/components/arc/intent_helper/local_activity_resolver_unittest.cc
+++ b/chromium/components/arc/intent_helper/local_activity_resolver_unittest.cc
@@ -15,14 +15,12 @@ namespace arc {
namespace {
-mojom::IntentFilterPtr GetIntentFilter(const std::string& host) {
- mojom::IntentFilterPtr filter = mojom::IntentFilter::New();
- mojom::AuthorityEntryPtr authority_entry = mojom::AuthorityEntry::New();
- authority_entry->host = host;
- authority_entry->port = -1;
- filter->data_authorities = std::vector<mojom::AuthorityEntryPtr>();
- filter->data_authorities->push_back(std::move(authority_entry));
- return filter;
+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
@@ -41,8 +39,8 @@ TEST(LocalActivityResolverTest, TestDefault) {
TEST(LocalActivityResolverTest, TestSingleFilter) {
scoped_refptr<LocalActivityResolver> resolver(new LocalActivityResolver());
- std::vector<mojom::IntentFilterPtr> array;
- array.push_back(GetIntentFilter("www.google.com"));
+ 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")));
@@ -56,10 +54,10 @@ TEST(LocalActivityResolverTest, TestSingleFilter) {
TEST(LocalActivityResolverTest, TestMultipleFilters) {
scoped_refptr<LocalActivityResolver> resolver(new LocalActivityResolver());
- std::vector<mojom::IntentFilterPtr> array;
- array.push_back(GetIntentFilter("www.google.com"));
- array.push_back(GetIntentFilter("www.google.co.uk"));
- array.push_back(GetIntentFilter("dev.chromium.org"));
+ 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")));
@@ -80,8 +78,8 @@ TEST(LocalActivityResolverTest, TestMultipleFilters) {
TEST(LocalActivityResolverTest, TestNonHttp) {
scoped_refptr<LocalActivityResolver> resolver(new LocalActivityResolver());
- std::vector<mojom::IntentFilterPtr> array;
- array.push_back(GetIntentFilter("www.google.com"));
+ 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")));
@@ -93,15 +91,15 @@ TEST(LocalActivityResolverTest, TestNonHttp) {
TEST(LocalActivityResolverTest, TestMultipleUpdate) {
scoped_refptr<LocalActivityResolver> resolver(new LocalActivityResolver());
- std::vector<mojom::IntentFilterPtr> array;
- array.push_back(GetIntentFilter("www.google.com"));
- array.push_back(GetIntentFilter("dev.chromium.org"));
+ 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<mojom::IntentFilterPtr> array2;
- array2.push_back(GetIntentFilter("www.google.co.uk"));
- array2.push_back(GetIntentFilter("dev.chromium.org"));
- array2.push_back(GetIntentFilter("www.android.com"));
+ 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")));
diff --git a/chromium/components/arc/intent_helper/page_transition_util.h b/chromium/components/arc/intent_helper/page_transition_util.h
index d2834af8ee2..20b66fa1ecc 100644
--- a/chromium/components/arc/intent_helper/page_transition_util.h
+++ b/chromium/components/arc/intent_helper/page_transition_util.h
@@ -15,7 +15,6 @@ bool ShouldIgnoreNavigation(ui::PageTransition page_transition,
bool allow_client_redirect);
// Removes |mask| bits from |page_transition|.
-// TODO(djacobo): Move this to ui/base/page_transition_types.cc.
ui::PageTransition MaskOutPageTransition(ui::PageTransition page_transition,
ui::PageTransition mask);
diff --git a/chromium/components/arc/intent_helper/page_transition_util_unittest.cc b/chromium/components/arc/intent_helper/page_transition_util_unittest.cc
index 79f1e785d44..cc6d5d3619f 100644
--- a/chromium/components/arc/intent_helper/page_transition_util_unittest.cc
+++ b/chromium/components/arc/intent_helper/page_transition_util_unittest.cc
@@ -49,9 +49,9 @@ TEST(PageTransitionUtilTest, TestShouldIgnoreNavigationWithCoreTypes) {
EXPECT_TRUE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_KEYWORD_GENERATED,
true, true));
- static_assert(
- ui::PAGE_TRANSITION_KEYWORD_GENERATED == ui::PAGE_TRANSITION_LAST_CORE,
- "Not all core transition types are covered here");
+ static_assert(static_cast<int32_t>(ui::PAGE_TRANSITION_KEYWORD_GENERATED) ==
+ static_cast<int32_t>(ui::PAGE_TRANSITION_LAST_CORE),
+ "Not all core transition types are covered here");
}
// Tests that ShouldIgnoreNavigation returns true when no qualifiers except
@@ -173,15 +173,15 @@ TEST(PageTransitionUtilTest, TestShouldIgnoreNavigationWithClientRedirect) {
TEST(PageTransitionUtilTest, TestMaskOutPageTransition) {
ui::PageTransition page_transition = ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT);
- EXPECT_EQ(ui::PAGE_TRANSITION_LINK,
- MaskOutPageTransition(page_transition,
- ui::PAGE_TRANSITION_CLIENT_REDIRECT));
+ EXPECT_EQ(static_cast<int>(ui::PAGE_TRANSITION_LINK),
+ static_cast<int>(MaskOutPageTransition(
+ page_transition, ui::PAGE_TRANSITION_CLIENT_REDIRECT)));
page_transition = ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_SERVER_REDIRECT);
- EXPECT_EQ(ui::PAGE_TRANSITION_LINK,
- MaskOutPageTransition(page_transition,
- ui::PAGE_TRANSITION_SERVER_REDIRECT));
+ EXPECT_EQ(static_cast<int>(ui::PAGE_TRANSITION_LINK),
+ static_cast<int>(MaskOutPageTransition(
+ page_transition, ui::PAGE_TRANSITION_SERVER_REDIRECT)));
}
// Test mixed variants between |allow_form_submit| and |allow_client_redirect|.
diff --git a/chromium/components/arc/kiosk/arc_kiosk_bridge.cc b/chromium/components/arc/kiosk/arc_kiosk_bridge.cc
index bb915bb984c..a928f67cc5e 100644
--- a/chromium/components/arc/kiosk/arc_kiosk_bridge.cc
+++ b/chromium/components/arc/kiosk/arc_kiosk_bridge.cc
@@ -5,12 +5,13 @@
#include "components/arc/kiosk/arc_kiosk_bridge.h"
#include "components/arc/arc_bridge_service.h"
-#include "components/arc/arc_service_manager.h"
namespace arc {
-ArcKioskBridge::ArcKioskBridge(ArcBridgeService* bridge_service)
- : ArcService(bridge_service), binding_(this) {
+ArcKioskBridge::ArcKioskBridge(ArcBridgeService* bridge_service,
+ Delegate* delegate)
+ : ArcService(bridge_service), binding_(this), delegate_(delegate) {
+ DCHECK(delegate_);
arc_bridge_service()->kiosk()->AddObserver(this);
}
@@ -20,18 +21,19 @@ ArcKioskBridge::~ArcKioskBridge() {
void ArcKioskBridge::OnInstanceReady() {
mojom::KioskInstance* kiosk_instance =
- arc_bridge_service()->kiosk()->GetInstanceForMethod("Init");
+ 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) {
+ delegate_->OnMaintenanceSessionCreated();
// TODO(poromov@) Show appropriate splash screen.
}
void ArcKioskBridge::OnMaintenanceSessionFinished(int32_t session_id,
bool success) {
- // TODO(poromov@) Start kiosk app.
+ delegate_->OnMaintenanceSessionFinished();
}
} // namespace arc
diff --git a/chromium/components/arc/kiosk/arc_kiosk_bridge.h b/chromium/components/arc/kiosk/arc_kiosk_bridge.h
index ab0323f971a..fa04fe9b27b 100644
--- a/chromium/components/arc/kiosk/arc_kiosk_bridge.h
+++ b/chromium/components/arc/kiosk/arc_kiosk_bridge.h
@@ -21,7 +21,16 @@ class ArcKioskBridge : public ArcService,
public InstanceHolder<mojom::KioskInstance>::Observer,
public mojom::KioskHost {
public:
- explicit ArcKioskBridge(ArcBridgeService* bridge_service);
+ // 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.
@@ -33,6 +42,7 @@ class ArcKioskBridge : public ArcService,
private:
mojo::Binding<mojom::KioskHost> binding_;
+ Delegate* const delegate_;
DISALLOW_COPY_AND_ASSIGN(ArcKioskBridge);
};
diff --git a/chromium/components/arc/metrics/arc_metrics_service.cc b/chromium/components/arc/metrics/arc_metrics_service.cc
index 9914eae04bf..2f391c59838 100644
--- a/chromium/components/arc/metrics/arc_metrics_service.cc
+++ b/chromium/components/arc/metrics/arc_metrics_service.cc
@@ -29,7 +29,6 @@ ArcMetricsService::ArcMetricsService(ArcBridgeService* bridge_service)
: ArcService(bridge_service),
binding_(this),
process_observer_(this),
- oom_kills_monitor_handle_(OomKillsMonitor::StartMonitoring()),
weak_ptr_factory_(this) {
arc_bridge_service()->metrics()->AddObserver(this);
arc_bridge_service()->process()->AddObserver(&process_observer_);
@@ -76,9 +75,8 @@ void ArcMetricsService::OnProcessInstanceClosed() {
}
void ArcMetricsService::RequestProcessList() {
- mojom::ProcessInstance* process_instance =
- arc_bridge_service()->process()->GetInstanceForMethod(
- "RequestProcessList");
+ mojom::ProcessInstance* process_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->process(), RequestProcessList);
if (!process_instance)
return;
VLOG(2) << "RequestProcessList";
@@ -121,7 +119,7 @@ void ArcMetricsService::OnArcStartTimeRetrieved(
return;
}
auto* instance =
- arc_bridge_service()->metrics()->GetInstanceForMethod("Init");
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->metrics(), Init);
if (!instance)
return;
@@ -130,7 +128,7 @@ void ArcMetricsService::OnArcStartTimeRetrieved(
// time availability in ReportBootProgress().
if (!binding_.is_bound()) {
mojom::MetricsHostPtr host_ptr;
- binding_.Bind(mojo::GetProxy(&host_ptr));
+ binding_.Bind(mojo::MakeRequest(&host_ptr));
instance->Init(std::move(host_ptr));
}
arc_start_time_ = arc_start_time;
diff --git a/chromium/components/arc/metrics/arc_metrics_service.h b/chromium/components/arc/metrics/arc_metrics_service.h
index c3d256b4e1e..df591222c82 100644
--- a/chromium/components/arc/metrics/arc_metrics_service.h
+++ b/chromium/components/arc/metrics/arc_metrics_service.h
@@ -15,7 +15,6 @@
#include "components/arc/common/metrics.mojom.h"
#include "components/arc/common/process.mojom.h"
#include "components/arc/instance_holder.h"
-#include "components/arc/metrics/oom_kills_monitor.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace arc {
@@ -65,6 +64,8 @@ class ArcMetricsService
void OnInstanceClosed() override;
ArcMetricsService* arc_metrics_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessObserver);
};
mojo::Binding<mojom::MetricsHost> binding_;
@@ -73,8 +74,6 @@ class ArcMetricsService
base::ThreadChecker thread_checker_;
base::RepeatingTimer timer_;
- OomKillsMonitor::Handle oom_kills_monitor_handle_;
-
base::TimeTicks arc_start_time_;
// Always keep this the last member of this class to make sure it's the
diff --git a/chromium/components/arc/metrics/oom_kills_histogram.h b/chromium/components/arc/metrics/oom_kills_histogram.h
deleted file mode 100644
index 2a9bf562fa4..00000000000
--- a/chromium/components/arc/metrics/oom_kills_histogram.h
+++ /dev/null
@@ -1,23 +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_METRICS_OOM_KILLS_HISTOGRAM_H_
-#define COMPONENTS_ARC_METRICS_OOM_KILLS_HISTOGRAM_H_
-
-#include "base/metrics/histogram.h"
-
-namespace arc {
-
-const int kMaxOomMemoryKillTimeDeltaSecs = 30;
-
-} // namespace arc
-
-// Use this macro to report elapsed time since last OOM kill event.
-// Must be a macro as the underlying HISTOGRAM macro creates static variables.
-#define UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(name, sample) \
- UMA_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(1), \
- base::TimeDelta::FromSeconds(::arc::kMaxOomMemoryKillTimeDeltaSecs), 50)
-
-#endif // COMPONENTS_ARC_METRICS_OOM_KILLS_HISTOGRAM_H_
diff --git a/chromium/components/arc/metrics/oom_kills_monitor.cc b/chromium/components/arc/metrics/oom_kills_monitor.cc
deleted file mode 100644
index 719b778eaf9..00000000000
--- a/chromium/components/arc/metrics/oom_kills_monitor.cc
+++ /dev/null
@@ -1,206 +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/metrics/oom_kills_monitor.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/debug/leak_annotations.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/posix/safe_strerror.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
-#include "base/time/time.h"
-#include "components/arc/metrics/oom_kills_histogram.h"
-#include "third_party/re2/src/re2/re2.h"
-#include "third_party/re2/src/re2/stringpiece.h"
-
-namespace arc {
-
-using base::StringPiece;
-
-using base::SequencedWorkerPool;
-using base::TimeDelta;
-
-namespace {
-
-int64_t GetTimestamp(const StringPiece& line) {
- std::vector<StringPiece> fields = base::SplitStringPiece(
- line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
- int64_t timestamp = -1;
- // Timestamp is the third field in a line of /dev/kmsg.
- if (fields.size() < 3 || !base::StringToInt64(fields[2], &timestamp))
- return -1;
- return timestamp;
-}
-
-bool GetTimeDelta(
- const StringPiece& line, int64_t* last, TimeDelta* time_delta) {
- int64_t now = GetTimestamp(line);
- if (now < 0)
- return false;
-
- // Sets to |kMaxOomMemoryKillTimeSecs| for the first kill event.
- if (*last < 0)
- *time_delta = TimeDelta::FromSeconds(kMaxOomMemoryKillTimeDeltaSecs);
- else
- *time_delta = TimeDelta::FromMicroseconds(now - *last);
-
- *last = now;
-
- return true;
-}
-
-void LogOOMKill(const StringPiece& line) {
- static int64_t last_timestamp = -1;
- static int oom_kills = 0;
-
- // Sample log line:
- // 3,1362,97646497541,-;Out of memory: Kill process 29582 (android.vending)
- // score 961 or sacrifice child.
- int oom_badness;
- TimeDelta time_delta;
- if (RE2::PartialMatch(re2::StringPiece(line.data(), line.size()),
- "Out of memory: Kill process .* score (\\d+)",
- &oom_badness)) {
- ++oom_kills;
- // Report the cumulative count of killed process in one login session.
- // For example if there are 3 processes killed, it would report 1 for the
- // first kill, 2 for the second kill, then 3 for the final kill.
- // It doesn't report a final count at the end of a user session because
- // the code runs in a dedicated thread and never ends until browser shutdown
- // (or logout on Chrome OS). And on browser shutdown the thread may be
- // terminated brutally so there's no chance to execute a "final" block.
- // More specifically, code outside the main loop of OomKillsMonitor::Run()
- // are not guaranteed to be executed.
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Arc.OOMKills.Count", oom_kills, 1, 1000, 1001);
-
- // In practice most process has oom_badness < 1000, but
- // strictly speaking the number could be [1, 2000]. What it really
- // means is the baseline, proportion of memory used (normalized to
- // [0, 1000]), plus an adjustment score oom_score_adj [-1000, 1000],
- // truncated to 1 if negative (0 means never kill).
- // Ref: https://lwn.net/Articles/396552/
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Arc.OOMKills.Score", oom_badness, 1, 2000, 2001);
-
- if (GetTimeDelta(line, &last_timestamp, &time_delta)) {
- UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(
- "Arc.OOMKills.TimeDelta", time_delta);
- }
- }
-}
-
-void LogLowMemoryKill(const StringPiece& line) {
- static int64_t last_timestamp = -1;
- static int low_memory_kills = 0;
-
- int freed_size;
- TimeDelta time_delta;
- // Sample log line:
- // 6,2302,533604004,-;lowmemorykiller: Killing 'externalstorage' (21742),
- // adj 1000,\x0a to free 27320kB on behalf of 'kswapd0' (47) because\x0a
- // cache 181920kB is below limit 184320kB for oom_score_adj 1000\x0a
- // Free memory is 1228kB above reserved
- if (RE2::PartialMatch(re2::StringPiece(line.data(), line.size()),
- "lowmemorykiller: .* to free (\\d+)kB",
- &freed_size)) {
- // Report the count for each lowmemorykill event. See comments in
- // LogOOMKill().
- ++low_memory_kills;
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Arc.LowMemoryKiller.Count", low_memory_kills, 1, 1000, 1001);
-
- UMA_HISTOGRAM_MEMORY_KB("Arc.LowMemoryKiller.FreedSize", freed_size);
-
- if (GetTimeDelta(line, &last_timestamp, &time_delta)) {
- UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(
- "Arc.LowMemoryKiller.TimeDelta", time_delta);
- }
- }
-}
-
-} // namespace
-
-OomKillsMonitor::Handle::Handle(OomKillsMonitor* outer) : outer_(outer) {
- DCHECK(outer_);
-}
-
-OomKillsMonitor::Handle::~Handle() {
- outer_->is_shutting_down_.Set();
-}
-
-OomKillsMonitor::OomKillsMonitor() {
- base::SimpleThread::Options non_joinable_options;
- non_joinable_options.joinable = false;
- non_joinable_worker_thread_ = base::MakeUnique<base::DelegateSimpleThread>(
- this, "oom_kills_monitor", non_joinable_options);
- non_joinable_worker_thread_->Start();
-}
-
-OomKillsMonitor::~OomKillsMonitor() {
- // The instance has to be leaked on shutdown as it is referred to by a
- // non-joinable thread but ~OomKillsMonitor() can't be explicitly deleted as
- // it overrides ~SimpleThread(), it should nevertheless never be invoked.
- NOTREACHED();
-}
-
-// static
-OomKillsMonitor::Handle OomKillsMonitor::StartMonitoring() {
-#if DCHECK_IS_ON()
- static volatile bool monitoring_active = false;
- DCHECK(!monitoring_active);
- monitoring_active = true;
-#endif
-
- // Instantiate the OomKillsMonitor and its underlying thread. The
- // OomKillsMonitor itself has to be leaked on shutdown per having a
- // non-joinable thread associated to its state. The OomKillsMonitor::Handle
- // will notify the OomKillsMonitor when it is destroyed so that the underlying
- // thread can at a minimum not do extra work during shutdown.
- OomKillsMonitor* instance = new OomKillsMonitor;
- ANNOTATE_LEAKING_OBJECT_PTR(instance);
- return Handle(instance);
-}
-
-void OomKillsMonitor::Run() {
- base::ScopedFILE kmsg_handle(
- base::OpenFile(base::FilePath("/dev/kmsg"), "r"));
- if (!kmsg_handle) {
- LOG(WARNING) << "Open /dev/kmsg failed: " << base::safe_strerror(errno);
- return;
- }
- // Skip kernel messages prior to the instantiation of this object to avoid
- // double reporting.
- fseek(kmsg_handle.get(), 0, SEEK_END);
-
- static const int kMaxBufSize = 512;
- char buf[kMaxBufSize];
-
- while (fgets(buf, kMaxBufSize, kmsg_handle.get())) {
- if (is_shutting_down_.IsSet()) {
- DVLOG(1) << "Chrome is shutting down, exit now.";
- break;
- }
- const StringPiece buf_string(buf);
- LogOOMKill(buf_string);
- LogLowMemoryKill(buf_string);
- }
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/metrics/oom_kills_monitor.h b/chromium/components/arc/metrics/oom_kills_monitor.h
deleted file mode 100644
index 63e249efe2b..00000000000
--- a/chromium/components/arc/metrics/oom_kills_monitor.h
+++ /dev/null
@@ -1,66 +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_METRICS_OOM_KILLS_MONITOR_H_
-#define COMPONENTS_ARC_METRICS_OOM_KILLS_MONITOR_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/atomic_flag.h"
-#include "base/threading/simple_thread.h"
-
-namespace arc {
-
-// Traces kernel OOM kill events and lowmemorykiller (if enabled) events.
-//
-// OomKillsMonitor listens to kernel messages for both OOM kills and
-// lowmemorykiller kills, then reports to UMA. It uses a non-joinable thread
-// in order to avoid blocking shutdown.
-//
-// Note: There should be only one OomKillsMonitor instance globally at any given
-// time, otherwise UMA would receive duplicate events.
-class OomKillsMonitor : public base::DelegateSimpleThread::Delegate {
- public:
- // A handle representing the OomKillsMonitor's lifetime (the monitor itself
- // can't be destroyed per being a non-joinable Thread).
- class Handle {
- public:
- // Constructs a handle that will flag |outer| as shutting down on
- // destruction.
- explicit Handle(OomKillsMonitor* outer);
-
- ~Handle();
-
- private:
- OomKillsMonitor* const outer_;
- };
-
- // Instantiates the OomKillsMonitor instance and starts it. This must only
- // be invoked once per process.
- static Handle StartMonitoring();
-
- private:
- OomKillsMonitor();
- ~OomKillsMonitor() override;
-
- // Overridden from base::DelegateSimpleThread::Delegate:
- void Run() override;
-
- // A flag set when OomKillsMonitor is shutdown so that its thread can poll
- // it and attempt to wind down from that point (to avoid unnecessary work, not
- // because it blocks shutdown).
- base::AtomicFlag is_shutting_down_;
-
- // The underlying worker thread which is non-joinable to avoid blocking
- // shutdown.
- std::unique_ptr<base::DelegateSimpleThread> non_joinable_worker_thread_;
-
- DISALLOW_COPY_AND_ASSIGN(OomKillsMonitor);
-};
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_METRICS_OOM_KILLS_MONITOR_H_
diff --git a/chromium/components/arc/net/DEPS b/chromium/components/arc/net/DEPS
index 5e505cc417b..c8c654c663d 100644
--- a/chromium/components/arc/net/DEPS
+++ b/chromium/components/arc/net/DEPS
@@ -2,4 +2,5 @@ include_rules = [
"+chromeos/login",
"+chromeos/network",
"+components/onc",
+ "+components/user_manager",
]
diff --git a/chromium/components/arc/net/arc_net_host_impl.cc b/chromium/components/arc/net/arc_net_host_impl.cc
index 938a824ddd5..05cb78d126c 100644
--- a/chromium/components/arc/net/arc_net_host_impl.cc
+++ b/chromium/components/arc/net/arc_net_host_impl.cc
@@ -24,13 +24,11 @@
#include "chromeos/network/network_util.h"
#include "chromeos/network/onc/onc_utils.h"
#include "components/arc/arc_bridge_service.h"
+#include "components/user_manager/user_manager.h"
namespace {
constexpr int kGetNetworksListLimit = 100;
-constexpr uint32_t kScanCompletedMinInstanceVersion = 1;
-constexpr uint32_t kDefaultNetworkChangedMinInstanceVersion = 2;
-constexpr uint32_t kWifiEnabledStateChanged = 3;
chromeos::NetworkStateHandler* GetStateHandler() {
return chromeos::NetworkHandler::Get()->network_state_handler();
@@ -46,10 +44,12 @@ chromeos::NetworkConnectionHandler* GetNetworkConnectionHandler() {
}
bool IsDeviceOwner() {
- // Check whether the logged-in Chrome OS user is allowed to add or
- // remove WiFi networks.
- return chromeos::LoginState::Get()->GetLoggedInUserType() ==
- chromeos::LoginState::LOGGED_IN_USER_OWNER;
+ // Check whether the logged-in Chrome OS user is allowed to add or remove WiFi
+ // networks. The user account state changes immediately after boot. There is a
+ // small window when this may return an incorrect state. However, after things
+ // settle down this is guranteed to reflect the correct user account state.
+ return user_manager::UserManager::Get()->GetActiveUser()->GetAccountId() ==
+ user_manager::UserManager::Get()->GetOwnerAccountId();
}
std::string GetStringFromOncDictionary(const base::DictionaryValue* dict,
@@ -58,7 +58,7 @@ std::string GetStringFromOncDictionary(const base::DictionaryValue* dict,
std::string value;
dict->GetString(key, &value);
if (required && value.empty())
- NOTREACHED();
+ VLOG(1) << "Required parameter " << key << " was not found.";
return value;
}
@@ -100,23 +100,22 @@ arc::mojom::WiFiPtr TranslateONCWifi(const base::DictionaryValue* dict) {
return wifi;
}
-mojo::Array<mojo::String> TranslateStringArray(const base::ListValue* list) {
- mojo::Array<mojo::String> strings = mojo::Array<mojo::String>::New(0);
+std::vector<std::string> TranslateStringArray(const base::ListValue* list) {
+ std::vector<std::string> strings;
for (size_t i = 0; i < list->GetSize(); i++) {
std::string value;
list->GetString(i, &value);
DCHECK(!value.empty());
- strings.push_back(static_cast<mojo::String>(value));
+ strings.push_back(value);
}
return strings;
}
-mojo::Array<arc::mojom::IPConfigurationPtr> TranslateONCIPConfigs(
+std::vector<arc::mojom::IPConfigurationPtr> TranslateONCIPConfigs(
const base::ListValue* list) {
- mojo::Array<arc::mojom::IPConfigurationPtr> configs =
- mojo::Array<arc::mojom::IPConfigurationPtr>::New(0);
+ std::vector<arc::mojom::IPConfigurationPtr> configs;
for (size_t i = 0; i < list->GetSize(); i++) {
const base::DictionaryValue* ip_dict = nullptr;
@@ -307,8 +306,9 @@ void ArcNetHostImpl::OnInstanceReady() {
DCHECK(thread_checker_.CalledOnValidThread());
mojom::NetHostPtr host;
- binding_.Bind(GetProxy(&host));
- auto* instance = arc_bridge_service()->net()->GetInstanceForMethod("Init");
+ binding_.Bind(MakeRequest(&host));
+ auto* instance =
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->net(), Init);
DCHECK(instance);
instance->Init(std::move(host));
@@ -370,8 +370,7 @@ void ArcNetHostImpl::GetNetworks(mojom::GetNetworksRequestType type,
// Even if there's no WiFi, an empty (size=0) list must be returned and not a
// null one. The explicitly sized New() constructor ensures the non-null
// property.
- mojo::Array<mojom::WifiConfigurationPtr> networks =
- mojo::Array<mojom::WifiConfigurationPtr>::New(0);
+ std::vector<mojom::WifiConfigurationPtr> networks;
for (const auto& value : *network_properties_list) {
mojom::WifiConfigurationPtr wc = mojom::WifiConfiguration::New();
@@ -454,7 +453,7 @@ void ArcNetHostImpl::CreateNetwork(mojom::WifiConfigurationPtr cfg,
std::unique_ptr<base::DictionaryValue> properties(new base::DictionaryValue);
std::unique_ptr<base::DictionaryValue> wifi_dict(new base::DictionaryValue);
- if (cfg->hexssid.is_null() || !cfg->details) {
+ if (!cfg->hexssid.has_value() || !cfg->details) {
callback.Run("");
return;
}
@@ -468,18 +467,18 @@ void ArcNetHostImpl::CreateNetwork(mojom::WifiConfigurationPtr cfg,
properties->SetStringWithoutPathExpansion(onc::network_config::kType,
onc::network_config::kWiFi);
wifi_dict->SetStringWithoutPathExpansion(onc::wifi::kHexSSID,
- cfg->hexssid.get());
+ cfg->hexssid.value());
wifi_dict->SetBooleanWithoutPathExpansion(onc::wifi::kAutoConnect,
details->autoconnect);
- if (cfg->security.get().empty()) {
+ if (cfg->security.empty()) {
wifi_dict->SetStringWithoutPathExpansion(onc::wifi::kSecurity,
onc::wifi::kSecurityNone);
} else {
wifi_dict->SetStringWithoutPathExpansion(onc::wifi::kSecurity,
- cfg->security.get());
- if (!details->passphrase.is_null()) {
+ cfg->security);
+ if (details->passphrase.has_value()) {
wifi_dict->SetStringWithoutPathExpansion(onc::wifi::kPassphrase,
- details->passphrase.get());
+ details->passphrase.value());
}
}
properties->SetWithoutPathExpansion(onc::network_config::kWiFi,
@@ -511,7 +510,7 @@ bool ArcNetHostImpl::GetNetworkPathFromGuid(const std::string& guid,
}
}
-void ArcNetHostImpl::ForgetNetwork(const mojo::String& guid,
+void ArcNetHostImpl::ForgetNetwork(const std::string& guid,
const ForgetNetworkCallback& callback) {
if (!IsDeviceOwner()) {
callback.Run(mojom::NetworkResult::FAILURE);
@@ -530,7 +529,7 @@ void ArcNetHostImpl::ForgetNetwork(const mojo::String& guid,
base::Bind(&ForgetNetworkFailureCallback, callback));
}
-void ArcNetHostImpl::StartConnect(const mojo::String& guid,
+void ArcNetHostImpl::StartConnect(const std::string& guid,
const StartConnectCallback& callback) {
std::string path;
if (!GetNetworkPathFromGuid(guid, &path)) {
@@ -543,7 +542,7 @@ void ArcNetHostImpl::StartConnect(const mojo::String& guid,
base::Bind(&StartConnectFailureCallback, callback), false);
}
-void ArcNetHostImpl::StartDisconnect(const mojo::String& guid,
+void ArcNetHostImpl::StartDisconnect(const std::string& guid,
const StartDisconnectCallback& callback) {
std::string path;
if (!GetNetworkPathFromGuid(guid, &path)) {
@@ -589,8 +588,8 @@ void ArcNetHostImpl::StartScan() {
}
void ArcNetHostImpl::ScanCompleted(const chromeos::DeviceState* /*unused*/) {
- auto* net_instance = arc_bridge_service()->net()->GetInstanceForMethod(
- "ScanCompleted", kScanCompletedMinInstanceVersion);
+ auto* net_instance =
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->net(), ScanCompleted);
if (!net_instance)
return;
@@ -618,8 +617,8 @@ void ArcNetHostImpl::GetDefaultNetwork(
void ArcNetHostImpl::DefaultNetworkSuccessCallback(
const std::string& service_path,
const base::DictionaryValue& dictionary) {
- auto* net_instance = arc_bridge_service()->net()->GetInstanceForMethod(
- "DefaultNetworkChanged", kDefaultNetworkChangedMinInstanceVersion);
+ auto* net_instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->net(),
+ DefaultNetworkChanged);
if (!net_instance)
return;
@@ -631,8 +630,8 @@ void ArcNetHostImpl::DefaultNetworkChanged(
const chromeos::NetworkState* network) {
if (!network) {
VLOG(1) << "No default network";
- auto* net_instance = arc_bridge_service()->net()->GetInstanceForMethod(
- "DefaultNetworkChanged", kDefaultNetworkChangedMinInstanceVersion);
+ auto* net_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->net(), DefaultNetworkChanged);
if (net_instance)
net_instance->DefaultNetworkChanged(nullptr, nullptr);
return;
@@ -648,8 +647,8 @@ void ArcNetHostImpl::DefaultNetworkChanged(
}
void ArcNetHostImpl::DeviceListChanged() {
- auto* net_instance = arc_bridge_service()->net()->GetInstanceForMethod(
- "WifiEnabledStateChanged", kWifiEnabledStateChanged);
+ 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 c0114a99002..3c56bbf74d0 100644
--- a/chromium/components/arc/net/arc_net_host_impl.h
+++ b/chromium/components/arc/net/arc_net_host_impl.h
@@ -62,13 +62,13 @@ class ArcNetHostImpl : public ArcService,
void CreateNetwork(mojom::WifiConfigurationPtr cfg,
const CreateNetworkCallback& callback) override;
- void ForgetNetwork(const mojo::String& guid,
+ void ForgetNetwork(const std::string& guid,
const ForgetNetworkCallback& callback) override;
- void StartConnect(const mojo::String& guid,
+ void StartConnect(const std::string& guid,
const StartConnectCallback& callback) override;
- void StartDisconnect(const mojo::String& guid,
+ void StartDisconnect(const std::string& guid,
const StartDisconnectCallback& callback) override;
// Overriden from chromeos::NetworkStateHandlerObserver.
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 dc160a46b16..54fc0af9d07 100644
--- a/chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.cc
+++ b/chromium/components/arc/obb_mounter/arc_obb_mounter_bridge.cc
@@ -32,7 +32,7 @@ ArcObbMounterBridge::~ArcObbMounterBridge() {
void ArcObbMounterBridge::OnInstanceReady() {
mojom::ObbMounterInstance* obb_mounter_instance =
- arc_bridge_service()->obb_mounter()->GetInstanceForMethod("Init");
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->obb_mounter(), Init);
DCHECK(obb_mounter_instance);
obb_mounter_instance->Init(binding_.CreateInterfacePtrAndBind());
}
diff --git a/chromium/components/arc/power/DEPS b/chromium/components/arc/power/DEPS
index f9e970af95a..aeeb7315233 100644
--- a/chromium/components/arc/power/DEPS
+++ b/chromium/components/arc/power/DEPS
@@ -1,4 +1,4 @@
include_rules = [
"+ash",
- "+ui/display/chromeos",
+ "+ui/display/manager/chromeos",
]
diff --git a/chromium/components/arc/power/arc_power_bridge.cc b/chromium/components/arc/power/arc_power_bridge.cc
index c49d0b5fc46..a9a47f52230 100644
--- a/chromium/components/arc/power/arc_power_bridge.cc
+++ b/chromium/components/arc/power/arc_power_bridge.cc
@@ -9,18 +9,13 @@
#include "ash/shell.h"
#include "base/logging.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_service_manager.h"
namespace arc {
-namespace {
-
-constexpr uint32_t kMinVersionForSetInteractive = 1;
-
-} // namespace
-
ArcPowerBridge::ArcPowerBridge(ArcBridgeService* bridge_service)
: ArcService(bridge_service), binding_(this) {
arc_bridge_service()->power()->AddObserver(this);
@@ -33,22 +28,45 @@ ArcPowerBridge::~ArcPowerBridge() {
void ArcPowerBridge::OnInstanceReady() {
mojom::PowerInstance* power_instance =
- arc_bridge_service()->power()->GetInstanceForMethod("Init");
+ ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->power(), Init);
DCHECK(power_instance);
power_instance->Init(binding_.CreateInterfacePtrAndBind());
ash::Shell::GetInstance()->display_configurator()->AddObserver(this);
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
+ AddObserver(this);
}
void ArcPowerBridge::OnInstanceClosed() {
ash::Shell::GetInstance()->display_configurator()->RemoveObserver(this);
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
+ RemoveObserver(this);
ReleaseAllDisplayWakeLocks();
}
+void ArcPowerBridge::SuspendImminent() {
+ mojom::PowerInstance* power_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->power(), Suspend);
+ if (!power_instance)
+ return;
+
+ power_instance->Suspend(
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
+ GetSuspendReadinessCallback());
+}
+
+void ArcPowerBridge::SuspendDone(const base::TimeDelta& sleep_duration) {
+ mojom::PowerInstance* power_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->power(), Resume);
+ if (!power_instance)
+ return;
+
+ power_instance->Resume();
+}
+
void ArcPowerBridge::OnPowerStateChanged(
chromeos::DisplayPowerState power_state) {
- mojom::PowerInstance* power_instance =
- arc_bridge_service()->power()->GetInstanceForMethod(
- "SetInteractive", kMinVersionForSetInteractive);
+ mojom::PowerInstance* power_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->power(), SetInteractive);
if (!power_instance)
return;
diff --git a/chromium/components/arc/power/arc_power_bridge.h b/chromium/components/arc/power/arc_power_bridge.h
index 5bcffff0039..c459cffbb07 100644
--- a/chromium/components/arc/power/arc_power_bridge.h
+++ b/chromium/components/arc/power/arc_power_bridge.h
@@ -8,11 +8,12 @@
#include <map>
#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 "mojo/public/cpp/bindings/binding.h"
-#include "ui/display/chromeos/display_configurator.h"
+#include "ui/display/manager/chromeos/display_configurator.h"
namespace arc {
@@ -22,7 +23,8 @@ class ArcBridgeService;
// ARC instances.
class ArcPowerBridge : public ArcService,
public InstanceHolder<mojom::PowerInstance>::Observer,
- public ui::DisplayConfigurator::Observer,
+ public chromeos::PowerManagerClient::Observer,
+ public display::DisplayConfigurator::Observer,
public mojom::PowerHost {
public:
explicit ArcPowerBridge(ArcBridgeService* bridge_service);
@@ -32,13 +34,16 @@ class ArcPowerBridge : public ArcService,
void OnInstanceReady() override;
void OnInstanceClosed() override;
+ // chromeos::PowerManagerClient::Observer overrides.
+ void SuspendImminent() override;
+ void SuspendDone(const base::TimeDelta& sleep_duration) override;
+
// DisplayConfigurator::Observer overrides.
void OnPowerStateChanged(chromeos::DisplayPowerState power_state) override;
// mojom::PowerHost overrides.
void OnAcquireDisplayWakeLock(mojom::DisplayWakeLockType type) override;
void OnReleaseDisplayWakeLock(mojom::DisplayWakeLockType type) override;
-
void IsDisplayOn(const IsDisplayOnCallback& callback) override;
private:
diff --git a/chromium/components/arc/storage_manager/arc_storage_manager.cc b/chromium/components/arc/storage_manager/arc_storage_manager.cc
index f8cae30155e..c87a065f91b 100644
--- a/chromium/components/arc/storage_manager/arc_storage_manager.cc
+++ b/chromium/components/arc/storage_manager/arc_storage_manager.cc
@@ -14,8 +14,6 @@ namespace arc {
namespace {
-const int kMinInstanceVersion = 1; // See storage_manager.mojom.
-
// 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;
@@ -40,9 +38,8 @@ ArcStorageManager* ArcStorageManager::Get() {
}
bool ArcStorageManager::OpenPrivateVolumeSettings() {
- auto* storage_manager_instance =
- arc_bridge_service()->storage_manager()->GetInstanceForMethod(
- "OpenPrivateVolumeSettings", kMinInstanceVersion);
+ auto* storage_manager_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->storage_manager(), OpenPrivateVolumeSettings);
if (!storage_manager_instance)
return false;
storage_manager_instance->OpenPrivateVolumeSettings();
@@ -51,9 +48,8 @@ bool ArcStorageManager::OpenPrivateVolumeSettings() {
bool ArcStorageManager::GetApplicationsSize(
const GetApplicationsSizeCallback& callback) {
- auto* storage_manager_instance =
- arc_bridge_service()->storage_manager()->GetInstanceForMethod(
- "GetApplicationsSize", kMinInstanceVersion);
+ auto* storage_manager_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->storage_manager(), GetApplicationsSize);
if (!storage_manager_instance)
return false;
storage_manager_instance->GetApplicationsSize(callback);
@@ -62,9 +58,8 @@ bool ArcStorageManager::GetApplicationsSize(
bool ArcStorageManager::DeleteApplicationsCache(
const base::Callback<void()>& callback) {
- auto* storage_manager_instance =
- arc_bridge_service()->storage_manager()->GetInstanceForMethod(
- "DeleteApplicationsCache", kMinInstanceVersion);
+ auto* storage_manager_instance = ARC_GET_INSTANCE_FOR_METHOD(
+ arc_bridge_service()->storage_manager(), DeleteApplicationsCache);
if (!storage_manager_instance)
return false;
storage_manager_instance->DeleteApplicationsCache(callback);
diff --git a/chromium/components/arc/user_data/arc_user_data_service.cc b/chromium/components/arc/user_data/arc_user_data_service.cc
deleted file mode 100644
index b9b3082f9da..00000000000
--- a/chromium/components/arc/user_data/arc_user_data_service.cc
+++ /dev/null
@@ -1,86 +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/user_data/arc_user_data_service.h"
-
-#include <utility>
-
-#include "base/command_line.h"
-#include "chromeos/chromeos_switches.h"
-#include "chromeos/cryptohome/cryptohome_parameters.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "components/prefs/pref_member.h"
-#include "components/signin/core/account_id/account_id.h"
-#include "components/user_manager/user_manager.h"
-
-namespace arc {
-
-ArcUserDataService::ArcUserDataService(
- ArcBridgeService* bridge_service,
- std::unique_ptr<BooleanPrefMember> arc_enabled_pref,
- const AccountId& account_id)
- : ArcService(bridge_service),
- arc_enabled_pref_(std::move(arc_enabled_pref)),
- primary_user_account_id_(account_id),
- weak_ptr_factory_(this) {
- arc_bridge_service()->AddObserver(this);
- pref_change_registrar_.Init(arc_enabled_pref_->prefs());
- pref_change_registrar_.Add(
- arc_enabled_pref_->GetPrefName(),
- base::Bind(&ArcUserDataService::OnOptInPreferenceChanged,
- weak_ptr_factory_.GetWeakPtr()));
- WipeIfRequired();
-}
-
-ArcUserDataService::~ArcUserDataService() {
- DCHECK(thread_checker_.CalledOnValidThread());
- arc_bridge_service()->RemoveObserver(this);
-}
-
-void ArcUserDataService::OnBridgeStopped(ArcBridgeService::StopReason reason) {
- DCHECK(thread_checker_.CalledOnValidThread());
- const AccountId& account_id =
- user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId();
- if (account_id != primary_user_account_id_) {
- LOG(ERROR) << "User preferences not loaded for "
- << primary_user_account_id_.GetUserEmail()
- << ", but current primary user is " << account_id.GetUserEmail();
- primary_user_account_id_ = EmptyAccountId();
- return;
- }
- WipeIfRequired();
-}
-
-void ArcUserDataService::RequireUserDataWiped(const ArcDataCallback& callback) {
- VLOG(1) << "Require ARC user data to be wiped.";
- arc_user_data_wipe_required_ = true;
- callback_ = callback;
-}
-
-void ArcUserDataService::WipeIfRequired() {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (!arc_bridge_service()->stopped()) {
- LOG(ERROR) << "ARC instance not stopped, user data can't be wiped";
- return;
- }
- if ((arc_enabled_pref_->GetValue() && !arc_user_data_wipe_required_) ||
- base::CommandLine::ForCurrentProcess()->HasSwitch(
- chromeos::switches::kDisableArcDataWipe)) {
- return;
- }
- VLOG(1) << "Wipe ARC user data.";
- arc_user_data_wipe_required_ = false;
- const cryptohome::Identification cryptohome_id(primary_user_account_id_);
- chromeos::SessionManagerClient* session_manager_client =
- chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
- session_manager_client->RemoveArcData(cryptohome_id, callback_);
- callback_.Reset();
-}
-
-void ArcUserDataService::OnOptInPreferenceChanged() {
- if (!arc_enabled_pref_->GetValue())
- arc_user_data_wipe_required_ = true;
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/user_data/arc_user_data_service.h b/chromium/components/arc/user_data/arc_user_data_service.h
deleted file mode 100644
index 1ac89a2be75..00000000000
--- a/chromium/components/arc/user_data/arc_user_data_service.h
+++ /dev/null
@@ -1,84 +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_USER_DATA_ARC_USER_DATA_SERVICE_H_
-#define COMPONENTS_ARC_USER_DATA_ARC_USER_DATA_SERVICE_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "chromeos/dbus/session_manager_client.h"
-#include "components/arc/arc_bridge_service.h"
-#include "components/arc/arc_service.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/prefs/pref_member.h"
-#include "components/signin/core/account_id/account_id.h"
-
-namespace arc {
-
-class ArcBridgeService;
-
-// This class controls the lifecycle of ARC user data, removing it when
-// necessary.
-class ArcUserDataService : public ArcService,
- public ArcBridgeService::Observer {
- public:
- explicit ArcUserDataService(
- ArcBridgeService* arc_bridge_service,
- std::unique_ptr<BooleanPrefMember> arc_enabled_pref,
- const AccountId& account_id);
- ~ArcUserDataService() override;
-
- using ArcDataCallback = chromeos::SessionManagerClient::ArcCallback;
-
- // ArcBridgeService::Observer:
- // Called whenever the arc bridge is stopped to potentially wipe data if
- // the user has not opted in or it is required.
- void OnBridgeStopped(ArcBridgeService::StopReason reason) override;
-
- // Requires to wipe ARC user data after the next ARC bridge shutdown and call
- // |callback| with an operation result.
- void RequireUserDataWiped(const ArcDataCallback& callback);
-
- private:
- base::ThreadChecker thread_checker_;
-
- // Checks if ARC is both stopped and disabled (not opt-in) or data wipe is
- // required and triggers removal of user data.
- void WipeIfRequired();
-
- // Callback when the kArcEnabled preference changes. It watches for instances
- // where the preference is disabled and remembers this so that it can wipe
- // user data once the bridge has stopped.
- void OnOptInPreferenceChanged();
-
- const std::unique_ptr<BooleanPrefMember> arc_enabled_pref_;
-
- // Account ID for the account for which we currently have opt-in information.
- AccountId primary_user_account_id_;
-
- // Registrar used to monitor ARC enabled state.
- PrefChangeRegistrar pref_change_registrar_;
-
- // Set to true when kArcEnabled goes from true to false or user data wipe is
- // required and set to false again after user data has been wiped.
- // This ensures data is wiped even if the user tries to enable ARC before the
- // bridge has shut down.
- bool arc_user_data_wipe_required_ = false;
-
- // Callback object that is passed to RemoveArcData to be invoked with a
- // result of ARC user data wipe.
- // Set when ARC user data wipe is required by RequireUserDataWiped.
- ArcDataCallback callback_;
-
- base::WeakPtrFactory<ArcUserDataService> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(ArcUserDataService);
-};
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_USER_DATA_ARC_USER_DATA_SERVICE_H_
diff --git a/chromium/components/arc/video_accelerator/OWNERS b/chromium/components/arc/video_accelerator/OWNERS
new file mode 100644
index 00000000000..bb6511619b7
--- /dev/null
+++ b/chromium/components/arc/video_accelerator/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/video_accelerator/video_accelerator.h b/chromium/components/arc/video_accelerator/video_accelerator.h
new file mode 100644
index 00000000000..bdbf6d5466f
--- /dev/null
+++ b/chromium/components/arc/video_accelerator/video_accelerator.h
@@ -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.
+
+#ifndef COMPONENTS_ARC_VIDEO_ACCELERATOR_VIDEO_ACCELERATOR_H_
+#define COMPONENTS_ARC_VIDEO_ACCELERATOR_VIDEO_ACCELERATOR_H_
+
+namespace arc {
+
+struct ArcVideoAcceleratorDmabufPlane {
+ int32_t offset; // in bytes
+ int32_t stride; // in bytes
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_VIDEO_ACCELERATOR_VIDEO_ACCELERATOR_H_
diff --git a/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.cc b/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.cc
new file mode 100644
index 00000000000..3feb966d8af
--- /dev/null
+++ b/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.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/arc/video_accelerator/video_accelerator_struct_traits.h"
+
+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) {
+ return false;
+ }
+
+ out->offset = data.offset();
+ out->stride = data.stride();
+ 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
new file mode 100644
index 00000000000..68e3befd80f
--- /dev/null
+++ b/chromium/components/arc/video_accelerator/video_accelerator_struct_traits.h
@@ -0,0 +1,32 @@
+// 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_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/video_accelerator/video_accelerator.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<arc::mojom::ArcVideoAcceleratorDmabufPlaneDataView,
+ arc::ArcVideoAcceleratorDmabufPlane> {
+ static uint32_t offset(const arc::ArcVideoAcceleratorDmabufPlane& r) {
+ DCHECK(r.offset >= 0);
+ return r.offset;
+ }
+
+ static uint32_t stride(const arc::ArcVideoAcceleratorDmabufPlane& r) {
+ DCHECK(r.stride >= 0);
+ return r.stride;
+ }
+
+ static bool Read(arc::mojom::ArcVideoAcceleratorDmabufPlaneDataView data,
+ arc::ArcVideoAcceleratorDmabufPlane* out);
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_ARC_VIDEO_ACCELERATOR_VIDEO_ACCELERATOR_STRUCT_TRAITS_H_
diff --git a/chromium/components/autofill/DEPS b/chromium/components/autofill/DEPS
index bed4f0afd3c..6b6ca5cf0be 100644
--- a/chromium/components/autofill/DEPS
+++ b/chromium/components/autofill/DEPS
@@ -4,9 +4,14 @@ include_rules = [
"+grit", # For generated headers
"+jni",
"+net",
+ "+third_party/skia",
"+third_party/zlib/google",
"+ui",
+
# Autofill is a layered component; subdirectories must explicitly introduce
# the ability to use the content layer as appropriate.
"-components/autofill/content",
+
+ # This directory contains build flags and does not pull all of PPAPI in.
+ "+ppapi/features",
]
diff --git a/chromium/components/autofill/OWNERS b/chromium/components/autofill/OWNERS
index be52fd5be44..d52021494e9 100644
--- a/chromium/components/autofill/OWNERS
+++ b/chromium/components/autofill/OWNERS
@@ -1,6 +1,6 @@
estade@chromium.org
-isherman@chromium.org
mathp@chromium.org
+sebsg@chromium.org
vabr@chromium.org
# Owners for password autofill/generation only.
diff --git a/chromium/components/autofill/android/BUILD.gn b/chromium/components/autofill/android/BUILD.gn
index 54889a8605d..dab9ec22618 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",
+ "//third_party/android_tools:android_support_v7_appcompat_java",
"//ui/android:ui_java",
]
java_files = [
diff --git a/chromium/components/autofill/android/java/res/values/colors.xml b/chromium/components/autofill/android/java/res/values/colors.xml
new file mode 100644
index 00000000000..8b10dd3af7b
--- /dev/null
+++ b/chromium/components/autofill/android/java/res/values/colors.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <!-- Text colors of warning messages for credit card and password forms. -->
+ <color name="http_bad_warning_message_text">#C53929</color>
+ <color name="insecure_context_payment_disabled_message_text">#646464</color>
+</resources>
diff --git a/chromium/components/autofill/android/java/res/values/dimens.xml b/chromium/components/autofill/android/java/res/values/dimens.xml
index ee949e47a0f..82d8c5e07c2 100644
--- a/chromium/components/autofill/android/java/res/values/dimens.xml
+++ b/chromium/components/autofill/android/java/res/values/dimens.xml
@@ -15,4 +15,13 @@
<dimen name="keyboard_accessory_height">48dp</dimen>
<dimen name="keyboard_accessory_padding">8dp</dimen>
<dimen name="keyboard_accessory_text_size">14sp</dimen>
+
+ <!--
+ Larger label and icon sizes for Form-Not-Secure experiment
+ (warning messages on credit card and password forms when users
+ are on HTTP sites).
+ -->
+ <dimen name="dropdown_item_larger_sublabel_font_size">18sp</dimen>
+ <dimen name="dropdown_large_icon_size">24dp</dimen>
+ <dimen name="dropdown_large_icon_margin">10dp</dimen>
</resources>
diff --git a/chromium/components/autofill/content/browser/BUILD.gn b/chromium/components/autofill/content/browser/BUILD.gn
index d8819209383..88576886284 100644
--- a/chromium/components/autofill/content/browser/BUILD.gn
+++ b/chromium/components/autofill/content/browser/BUILD.gn
@@ -37,6 +37,7 @@ static_library("browser") {
"//gpu/config",
"//mojo/common:common_base",
"//net",
+ "//ppapi/features",
"//services/service_manager/public/cpp",
"//sql",
"//third_party/icu",
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc
index c0648785994..8eb8de071ae 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc
@@ -159,15 +159,15 @@ void ContentAutofillDriver::PopupHidden() {
gfx::RectF ContentAutofillDriver::TransformBoundingBoxToViewportCoordinates(
const gfx::RectF& bounding_box) {
- gfx::Point orig_point(bounding_box.x(), bounding_box.y());
- gfx::Point transformed_point;
- transformed_point =
- render_frame_host_->GetView()->TransformPointToRootCoordSpace(orig_point);
+ content::RenderWidgetHostView* view = render_frame_host_->GetView();
+ if (!view)
+ return bounding_box;
- gfx::RectF new_box;
- new_box.SetRect(transformed_point.x(), transformed_point.y(),
- bounding_box.width(), bounding_box.height());
- return new_box;
+ gfx::Point orig_point(bounding_box.x(), bounding_box.y());
+ gfx::Point transformed_point =
+ view->TransformPointToRootCoordSpace(orig_point);
+ return gfx::RectF(transformed_point.x(), transformed_point.y(),
+ bounding_box.width(), bounding_box.height());
}
void ContentAutofillDriver::DidInteractWithCreditCardForm() {
@@ -216,10 +216,6 @@ void ContentAutofillDriver::HidePopup() {
autofill_manager_->OnHidePopup();
}
-void ContentAutofillDriver::PingAck() {
- autofill_external_delegate_.OnPingAck();
-}
-
void ContentAutofillDriver::FocusNoLongerOnForm() {
autofill_manager_->OnFocusNoLongerOnForm();
}
@@ -264,7 +260,7 @@ const mojom::AutofillAgentPtr& ContentAutofillDriver::GetAutofillAgent() {
// Here is a lazy binding, and will not reconnect after connection error.
if (!autofill_agent_) {
render_frame_host_->GetRemoteInterfaces()->GetInterface(
- mojo::GetProxy(&autofill_agent_));
+ mojo::MakeRequest(&autofill_agent_));
}
return autofill_agent_;
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h
index e5740f57624..2fd226efc69 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver.h
+++ b/chromium/components/autofill/content/browser/content_autofill_driver.h
@@ -17,7 +17,6 @@
#include "mojo/public/cpp/bindings/binding.h"
namespace content {
-class BrowserContext;
class RenderFrameHost;
struct FrameNavigateParams;
struct LoadCommittedDetails;
@@ -84,7 +83,6 @@ class ContentAutofillDriver : public AutofillDriver,
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
void HidePopup() override;
- void PingAck() override;
void FocusNoLongerOnForm() override;
void DidFillAutofillFormData(const FormData& form,
base::TimeTicks timestamp) override;
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 8bec3e5a051..2781ee4c1cf 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h
@@ -20,7 +20,6 @@ class RenderFrameHost;
namespace autofill {
-class AutofillDriver;
class ContentAutofillDriver;
// Manages lifetime of ContentAutofillDriver. One Factory per WebContents
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 82dd2f6254a..3dbbd572f3c 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc
@@ -137,6 +137,9 @@ class FakeAutofillAgent : public mojom::AutofillAgent {
return true;
}
+ // Mocked mojom::AutofillAgent methods:
+ MOCK_METHOD0(FirstUserGestureObservedInTab, void());
+
private:
void CallDone() {
if (!quit_closure_.is_null()) {
@@ -145,9 +148,7 @@ class FakeAutofillAgent : public mojom::AutofillAgent {
}
}
- // mojom::AutofillAgent methods:
- void FirstUserGestureObservedInTab() override {}
-
+ // mojom::AutofillAgent:
void FillForm(int32_t id, const FormData& form) override {
fill_form_id_ = id;
fill_form_form_ = form;
@@ -236,6 +237,11 @@ class MockAutofillManager : public AutofillManager {
MOCK_METHOD0(Reset, void());
};
+class MockAutofillClient : public TestAutofillClient {
+ public:
+ MOCK_METHOD0(OnFirstUserGestureObserved, void());
+};
+
class TestContentAutofillDriver : public ContentAutofillDriver {
public:
TestContentAutofillDriver(content::RenderFrameHost* rfh,
@@ -259,7 +265,7 @@ class ContentAutofillDriverTest : public content::RenderViewHostTestHarness {
void SetUp() override {
content::RenderViewHostTestHarness::SetUp();
- test_autofill_client_.reset(new TestAutofillClient());
+ test_autofill_client_.reset(new MockAutofillClient());
driver_.reset(new TestContentAutofillDriver(web_contents()->GetMainFrame(),
test_autofill_client_.get()));
@@ -279,7 +285,7 @@ class ContentAutofillDriverTest : public content::RenderViewHostTestHarness {
}
protected:
- std::unique_ptr<TestAutofillClient> test_autofill_client_;
+ std::unique_ptr<MockAutofillClient> test_autofill_client_;
std::unique_ptr<TestContentAutofillDriver> driver_;
FakeAutofillAgent fake_agent_;
@@ -479,4 +485,15 @@ TEST_F(ContentAutofillDriverTest, CreditCardFormInteractionOnHTTPS) {
content::SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP));
}
+TEST_F(ContentAutofillDriverTest, NotifyFirstUserGestureObservedInTab) {
+ driver_->NotifyFirstUserGestureObservedInTab();
+ EXPECT_CALL(fake_agent_, FirstUserGestureObservedInTab());
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(ContentAutofillDriverTest, FirstUserGestureObserved) {
+ EXPECT_CALL(*test_autofill_client_, OnFirstUserGestureObserved());
+ driver_->FirstUserGestureObserved();
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.cc b/chromium/components/autofill/content/browser/risk/fingerprint.cc
index acac2f15ad8..09041403ccc 100644
--- a/chromium/components/autofill/content/browser/risk/fingerprint.cc
+++ b/chromium/components/autofill/content/browser/risk/fingerprint.cc
@@ -41,6 +41,7 @@
#include "device/geolocation/geolocation_provider.h"
#include "device/geolocation/geoposition.h"
#include "gpu/config/gpu_info.h"
+#include "ppapi/features/features.h"
#include "third_party/WebKit/public/platform/WebRect.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
@@ -293,7 +294,7 @@ FingerprintDataLoader::FingerprintDataLoader(
gpu_data_manager_->RequestCompleteGpuInfoIfNeeded();
}
-#if defined(ENABLE_PLUGINS)
+#if BUILDFLAG(ENABLE_PLUGINS)
// Load plugin data.
content::PluginService::GetInstance()->GetPlugins(
base::Bind(&FingerprintDataLoader::OnGotPlugins,
@@ -325,7 +326,7 @@ void FingerprintDataLoader::OnGpuInfoUpdate() {
void FingerprintDataLoader::OnGotFonts(std::unique_ptr<base::ListValue> fonts) {
DCHECK(!fonts_);
- fonts_.reset(fonts.release());
+ fonts_ = std::move(fonts);
MaybeFillFingerprint();
}
diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.h b/chromium/components/autofill/content/browser/risk/fingerprint.h
index ae1b7857a7a..870743d1a55 100644
--- a/chromium/components/autofill/content/browser/risk/fingerprint.h
+++ b/chromium/components/autofill/content/browser/risk/fingerprint.h
@@ -20,8 +20,6 @@
#include "base/callback_forward.h"
#include "components/autofill/core/browser/autofill_client.h"
-class PrefService;
-
namespace base {
class Time;
}
diff --git a/chromium/components/autofill/content/common/autofill_agent.mojom b/chromium/components/autofill/content/common/autofill_agent.mojom
index a2f40a9da67..7870cafc8b4 100644
--- a/chromium/components/autofill/content/common/autofill_agent.mojom
+++ b/chromium/components/autofill/content/common/autofill_agent.mojom
@@ -5,7 +5,7 @@
module autofill.mojom;
import "components/autofill/content/common/autofill_types.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/string16.mojom";
// There is one instance of this interface per render frame in the render
// process.
diff --git a/chromium/components/autofill/content/common/autofill_driver.mojom b/chromium/components/autofill/content/common/autofill_driver.mojom
index 51278195f4b..ce08780300c 100644
--- a/chromium/components/autofill/content/common/autofill_driver.mojom
+++ b/chromium/components/autofill/content/common/autofill_driver.mojom
@@ -5,7 +5,9 @@
module autofill.mojom;
import "components/autofill/content/common/autofill_types.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/string16.mojom";
+import "mojo/common/text_direction.mojom";
+import "mojo/common/time.mojom";
import "ui/gfx/geometry/mojo/geometry.mojom";
// There is one instance of this interface per render frame host in the browser
@@ -39,9 +41,6 @@ interface AutofillDriver {
// Instructs the browser to hide the Autofill popup if it is open.
HidePopup();
- // Sent immediately after the renderer receives a ping IPC.
- PingAck();
-
// Sent when the current form is no longer focused.
FocusNoLongerOnForm();
@@ -62,11 +61,6 @@ interface AutofillDriver {
// There is one instance of this interface per render frame host in the browser
// process.
interface PasswordManagerDriver {
- // A ping to the browser that PasswordAutofillAgent was constructed. As a
- // consequence, the browser sends SetLoggingState with the current
- // state of the logging activity.
- PasswordAutofillAgentConstructed();
-
// Notification that password forms have been seen that are candidates for
// filling/submitting by the password manager.
PasswordFormsParsed(array<PasswordForm> forms);
@@ -93,10 +87,17 @@ interface PasswordManagerDriver {
// Instructs the browser to show a popup with suggestions filled from data
// associated with |key|. The popup will use |text_direction| for displaying
// text.
- ShowPasswordSuggestions(int32 key, TextDirection text_direction,
+ ShowPasswordSuggestions(int32 key,
+ mojo.common.mojom.TextDirection text_direction,
mojo.common.mojom.String16 typed_username,
int32 options, gfx.mojom.RectF bounds);
+ // Instructs the browser to show a popup with a warning that the form
+ // is not secure. The popup will use |text_direction| for displaying
+ // text. This popup is shown when a password form on a non-secure page is
+ // autofilled on page load.
+ ShowNotSecureWarning(mojo.common.mojom.TextDirection text_direction,
+ gfx.mojom.RectF bounds);
// Instructs the browser to presave the form with generated password.
PresaveGeneratedPassword(PasswordForm password_form);
diff --git a/chromium/components/autofill/content/common/autofill_param_traits_macros.h b/chromium/components/autofill/content/common/autofill_param_traits_macros.h
index 1bced6ec0ab..edd3fcb8c05 100644
--- a/chromium/components/autofill/content/common/autofill_param_traits_macros.h
+++ b/chromium/components/autofill/content/common/autofill_param_traits_macros.h
@@ -21,7 +21,7 @@ IPC_ENUM_TRAITS_MAX_VALUE(autofill::FormFieldData::RoleAttribute,
autofill::FormFieldData::ROLE_ATTRIBUTE_OTHER)
IPC_ENUM_TRAITS_MAX_VALUE(base::i18n::TextDirection,
- base::i18n::TEXT_DIRECTION_NUM_DIRECTIONS - 1)
+ base::i18n::TEXT_DIRECTION_MAX)
IPC_STRUCT_TRAITS_BEGIN(autofill::FormFieldData)
IPC_STRUCT_TRAITS_MEMBER(label)
diff --git a/chromium/components/autofill/content/common/autofill_types.mojom b/chromium/components/autofill/content/common/autofill_types.mojom
index 91905f2d84d..ec56870ac77 100644
--- a/chromium/components/autofill/content/common/autofill_types.mojom
+++ b/chromium/components/autofill/content/common/autofill_types.mojom
@@ -4,7 +4,8 @@
module autofill.mojom;
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/text_direction.mojom";
+import "mojo/common/time.mojom";
import "url/mojo/origin.mojom";
import "url/mojo/url.mojom";
@@ -21,14 +22,6 @@ enum RoleAttribute {
ROLE_ATTRIBUTE_OTHER
};
-// base::i18n::TextDirection
-enum TextDirection {
- UNKNOWN_DIRECTION = 0,
- RIGHT_TO_LEFT = 1,
- LEFT_TO_RIGHT = 2,
- TEXT_DIRECTION_NUM_DIRECTIONS = 3
-};
-
// autofill::PasswordForm::GenerationUploadStatus
enum GenerationUploadStatus {
NO_SIGNAL_SENT,
@@ -84,7 +77,7 @@ struct FormFieldData {
bool is_focusable;
bool should_autocomplete;
RoleAttribute role;
- TextDirection text_direction;
+ mojo.common.mojom.TextDirection text_direction;
array<string> option_values;
array<string> option_contents;
diff --git a/chromium/components/autofill/content/common/autofill_types.typemap b/chromium/components/autofill/content/common/autofill_types.typemap
index 4511910b355..7072d03c967 100644
--- a/chromium/components/autofill/content/common/autofill_types.typemap
+++ b/chromium/components/autofill/content/common/autofill_types.typemap
@@ -42,6 +42,5 @@ type_mappings = [
"autofill.mojom.PasswordFormScheme=autofill::PasswordForm::Scheme",
"autofill.mojom.PasswordFormType=autofill::PasswordForm::Type",
"autofill.mojom.RoleAttribute=autofill::FormFieldData::RoleAttribute",
- "autofill.mojom.TextDirection=base::i18n::TextDirection",
"autofill.mojom.UsernamesCollectionKey=autofill::UsernamesCollectionKey",
]
diff --git a/chromium/components/autofill/content/common/autofill_types_struct_traits.cc b/chromium/components/autofill/content/common/autofill_types_struct_traits.cc
index 81e2fb478c6..c536b4abd48 100644
--- a/chromium/components/autofill/content/common/autofill_types_struct_traits.cc
+++ b/chromium/components/autofill/content/common/autofill_types_struct_traits.cc
@@ -6,6 +6,7 @@
#include "base/i18n/rtl.h"
#include "ipc/ipc_message_utils.h"
+#include "mojo/common/common_custom_types_struct_traits.h"
#include "url/mojo/origin_struct_traits.h"
#include "url/mojo/url_gurl_struct_traits.h"
@@ -83,48 +84,6 @@ bool EnumTraits<mojom::RoleAttribute, FormFieldData::RoleAttribute>::FromMojom(
}
// static
-mojom::TextDirection
-EnumTraits<mojom::TextDirection, base::i18n::TextDirection>::ToMojom(
- base::i18n::TextDirection input) {
- switch (input) {
- case base::i18n::TextDirection::UNKNOWN_DIRECTION:
- return mojom::TextDirection::UNKNOWN_DIRECTION;
- case base::i18n::TextDirection::RIGHT_TO_LEFT:
- return mojom::TextDirection::RIGHT_TO_LEFT;
- case base::i18n::TextDirection::LEFT_TO_RIGHT:
- return mojom::TextDirection::LEFT_TO_RIGHT;
- case base::i18n::TextDirection::TEXT_DIRECTION_NUM_DIRECTIONS:
- return mojom::TextDirection::TEXT_DIRECTION_NUM_DIRECTIONS;
- }
-
- NOTREACHED();
- return mojom::TextDirection::UNKNOWN_DIRECTION;
-}
-
-// static
-bool EnumTraits<mojom::TextDirection, base::i18n::TextDirection>::FromMojom(
- mojom::TextDirection input,
- base::i18n::TextDirection* output) {
- switch (input) {
- case mojom::TextDirection::UNKNOWN_DIRECTION:
- *output = base::i18n::TextDirection::UNKNOWN_DIRECTION;
- return true;
- case mojom::TextDirection::RIGHT_TO_LEFT:
- *output = base::i18n::TextDirection::RIGHT_TO_LEFT;
- return true;
- case mojom::TextDirection::LEFT_TO_RIGHT:
- *output = base::i18n::TextDirection::LEFT_TO_RIGHT;
- return true;
- case mojom::TextDirection::TEXT_DIRECTION_NUM_DIRECTIONS:
- *output = base::i18n::TextDirection::TEXT_DIRECTION_NUM_DIRECTIONS;
- return true;
- }
-
- NOTREACHED();
- return false;
-}
-
-// static
mojom::GenerationUploadStatus EnumTraits<mojom::GenerationUploadStatus,
PasswordForm::GenerationUploadStatus>::
ToMojom(PasswordForm::GenerationUploadStatus input) {
diff --git a/chromium/components/autofill/content/common/autofill_types_struct_traits.h b/chromium/components/autofill/content/common/autofill_types_struct_traits.h
index 8e2f74031ab..c623aa50b30 100644
--- a/chromium/components/autofill/content/common/autofill_types_struct_traits.h
+++ b/chromium/components/autofill/content/common/autofill_types_struct_traits.h
@@ -7,6 +7,7 @@
#include <utility>
+#include "base/i18n/rtl.h"
#include "base/strings/string16.h"
#include "components/autofill/content/common/autofill_types.mojom.h"
#include "components/autofill/core/common/form_data.h"
@@ -40,14 +41,6 @@ struct EnumTraits<autofill::mojom::RoleAttribute,
};
template <>
-struct EnumTraits<autofill::mojom::TextDirection, base::i18n::TextDirection> {
- static autofill::mojom::TextDirection ToMojom(
- base::i18n::TextDirection input);
- static bool FromMojom(autofill::mojom::TextDirection input,
- base::i18n::TextDirection* output);
-};
-
-template <>
struct EnumTraits<autofill::mojom::GenerationUploadStatus,
autofill::PasswordForm::GenerationUploadStatus> {
static autofill::mojom::GenerationUploadStatus ToMojom(
diff --git a/chromium/components/autofill/content/renderer/BUILD.gn b/chromium/components/autofill/content/renderer/BUILD.gn
index 5dd8086ea43..2ddd7715dc2 100644
--- a/chromium/components/autofill/content/renderer/BUILD.gn
+++ b/chromium/components/autofill/content/renderer/BUILD.gn
@@ -30,6 +30,10 @@ static_library("renderer") {
"//base:i18n",
"//components/autofill/content/common:mojo_interfaces",
"//components/autofill/core/common",
+
+ # TODO(elawrence): remove security_state/core when the Form-Not-Secure
+ # feature is fully launched. https://crbug.com/677295
+ "//components/security_state/core",
"//components/strings",
"//content/public/common",
"//content/public/renderer",
diff --git a/chromium/components/autofill/content/renderer/DEPS b/chromium/components/autofill/content/renderer/DEPS
index 0885896b9c9..83ce085fa2a 100644
--- a/chromium/components/autofill/content/renderer/DEPS
+++ b/chromium/components/autofill/content/renderer/DEPS
@@ -3,3 +3,11 @@ include_rules = [
"+content/public/renderer",
"+third_party/re2",
]
+
+specific_include_rules = {
+ # TODO(elawrence): remove this when the Form-Not-Secure feature is fully
+ # launched. https://crbug.com/677295
+ "password_autofill_agent\.cc" : [
+ "+components/security_state/core",
+ ],
+}
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc
index 49cf6727451..0aa8a423a75 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/autofill_agent.cc
@@ -300,10 +300,6 @@ void AutofillAgent::FocusChangeComplete() {
}
}
-void AutofillAgent::setIgnoreTextChanges(bool ignore) {
- ignore_text_changes_ = ignore;
-}
-
void AutofillAgent::FormControlElementClicked(
const WebFormControlElement& element,
bool was_focused) {
@@ -487,10 +483,6 @@ void AutofillAgent::PreviewForm(int32_t id, const FormData& form) {
GetAutofillDriver()->DidPreviewAutofillFormData();
}
-void AutofillAgent::OnPing() {
- GetAutofillDriver()->PingAck();
-}
-
void AutofillAgent::FieldTypePredictionsAvailable(
const std::vector<FormDataPredictions>& forms) {
for (const auto& form : forms) {
@@ -577,6 +569,14 @@ void AutofillAgent::ShowInitialPasswordAccountSuggestions(
ShowSuggestions(element, options);
}
+void AutofillAgent::ShowNotSecureWarning(
+ const blink::WebInputElement& element) {
+ if (is_generation_popup_possibly_visible_)
+ return;
+ password_autofill_agent_->ShowNotSecureWarning(element);
+ is_popup_possibly_visible_ = true;
+}
+
void AutofillAgent::OnSamePageNavigationCompleted() {
if (last_interacted_form_.isNull()) {
// If no last interacted form is available (i.e., there is no form tag),
@@ -783,7 +783,7 @@ void AutofillAgent::ajaxSucceeded() {
const mojom::AutofillDriverPtr& AutofillAgent::GetAutofillDriver() {
if (!autofill_driver_) {
render_frame()->GetRemoteInterfaces()->GetInterface(
- mojo::GetProxy(&autofill_driver_));
+ mojo::MakeRequest(&autofill_driver_));
}
return autofill_driver_;
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h
index e464af9575b..698e5c9fc70 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/autofill_agent.h
@@ -83,6 +83,8 @@ class AutofillAgent : public content::RenderFrameObserver,
int32_t key,
const PasswordFormFillData& form_data) override;
+ void ShowNotSecureWarning(const blink::WebInputElement& element);
+
protected:
// blink::WebAutofillClient:
void didAssociateFormControlsDynamically() override;
@@ -182,14 +184,11 @@ class AutofillAgent : public content::RenderFrameObserver,
void textFieldDidReceiveKeyDown(
const blink::WebInputElement& element,
const blink::WebKeyboardEvent& event) override;
- void setIgnoreTextChanges(bool ignore) override;
void openTextDataListChooser(const blink::WebInputElement& element) override;
void dataListOptionsChanged(const blink::WebInputElement& element) override;
void firstUserGestureObserved() override;
void ajaxSucceeded() override;
- void OnPing();
-
// Called when a same-page navigation is detected.
void OnSamePageNavigationCompleted();
// Helper method which collects unowned elements (i.e., those not inside a
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc
index 2c5dc0975a7..8d690c2446a 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.cc
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc
@@ -5,11 +5,13 @@
#include "components/autofill/content/renderer/form_autofill_util.h"
#include <map>
+#include <memory>
#include <set>
+#include <vector>
#include "base/command_line.h"
#include "base/logging.h"
-#include "base/memory/scoped_vector.h"
+#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -963,7 +965,7 @@ 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) {
DCHECK(form_fields->empty());
@@ -981,7 +983,7 @@ bool ExtractFieldsFromControlElements(
WebFormControlElementToFormField(control_element,
field_value_and_properties_map,
extract_mask, form_field);
- form_fields->push_back(form_field);
+ form_fields->push_back(base::WrapUnique(form_field));
(*element_map)[control_element] = form_field;
(*fields_extracted)[i] = true;
@@ -1083,7 +1085,7 @@ bool FormOrFieldsetsToFormData(
// The extracted FormFields. We use pointers so we can store them in
// |element_map|.
- ScopedVector<FormFieldData> form_fields;
+ std::vector<std::unique_ptr<FormFieldData>> form_fields;
// A vector of bools that indicate whether each field in the form meets the
// requirements and thus will be in the resulting |form|.
@@ -1152,8 +1154,8 @@ bool FormOrFieldsetsToFormData(
}
// Copy the created FormFields into the resulting FormData object.
- for (const auto* iter : form_fields)
- form->fields.push_back(*iter);
+ for (const auto& field : form_fields)
+ form->fields.push_back(*field);
return true;
}
@@ -1617,6 +1619,8 @@ bool UnownedPasswordFormElementsAndFieldSetsToFormData(
bool FindFormAndFieldForFormControlElement(const WebFormControlElement& element,
FormData* form,
FormFieldData* field) {
+ DCHECK(!element.isNull());
+
if (!IsAutofillableElement(element))
return false;
diff --git a/chromium/components/autofill/content/renderer/form_cache.h b/chromium/components/autofill/content/renderer/form_cache.h
index 78bef2e6447..4afb3a66f9e 100644
--- a/chromium/components/autofill/content/renderer/form_cache.h
+++ b/chromium/components/autofill/content/renderer/form_cache.h
@@ -16,9 +16,6 @@
#include "components/autofill/core/common/form_data.h"
namespace blink {
-class WebDocument;
-class WebElement;
-class WebElementCollection;
class WebFormControlElement;
class WebFrame;
class WebInputElement;
diff --git a/chromium/components/autofill/content/renderer/page_click_tracker.cc b/chromium/components/autofill/content/renderer/page_click_tracker.cc
index da70ec2e851..0af746045e8 100644
--- a/chromium/components/autofill/content/renderer/page_click_tracker.cc
+++ b/chromium/components/autofill/content/renderer/page_click_tracker.cc
@@ -10,7 +10,6 @@
#include "components/autofill/core/common/autofill_util.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h"
-#include "third_party/WebKit/public/platform/WebInputEvent.h"
#include "third_party/WebKit/public/platform/WebPoint.h"
#include "third_party/WebKit/public/platform/WebSize.h"
#include "third_party/WebKit/public/web/WebDocument.h"
@@ -25,8 +24,6 @@ using blink::WebElement;
using blink::WebFormControlElement;
using blink::WebGestureEvent;
using blink::WebInputElement;
-using blink::WebInputEvent;
-using blink::WebMouseEvent;
using blink::WebNode;
using blink::WebPoint;
using blink::WebSize;
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
index 25edf2c8888..b17f17f0de1 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
@@ -9,16 +9,17 @@
#include <memory>
#include <string>
#include <utility>
+#include <vector>
#include "base/bind.h"
#include "base/i18n/case_conversion.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/password_form_conversion_utils.h"
@@ -27,6 +28,8 @@
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/password_form_fill_data.h"
+#include "components/security_state/core/security_state.h"
+#include "content/public/common/origin_util.h"
#include "content/public/renderer/document_state.h"
#include "content/public/renderer/navigation_state.h"
#include "content/public/renderer/render_frame.h"
@@ -177,7 +180,8 @@ bool FindFormInputElement(
// fields.
const blink::WebInputElement input_element =
control_element.toConst<blink::WebInputElement>();
- if (input_element.isPasswordField() != is_password_field)
+ if (!input_element.isTextField() ||
+ input_element.isPasswordField() != is_password_field)
continue;
// For change password form with ambiguous or empty names keep only the
@@ -243,8 +247,6 @@ void FindFormElements(content::RenderFrame* render_frame,
DCHECK(results);
blink::WebDocument doc = render_frame->GetWebFrame()->document();
- if (!doc.isHTMLDocument())
- return;
if (data.origin != form_util::GetCanonicalOriginForDocument(doc))
return;
@@ -559,19 +561,6 @@ bool FillFormOnPasswordReceived(
field_value_and_properties_map, registration_callback, logger);
}
-// Takes a |map| with pointers as keys and linked_ptr as values, and returns
-// true if |key| is not NULL and |map| contains a non-NULL entry for |key|.
-// Makes sure not to create an entry as a side effect of using the operator [].
-template <class Key, class Value>
-bool ContainsNonNullEntryForNonNullKey(
- const std::map<Key*, linked_ptr<Value>>& map,
- Key* key) {
- if (!key)
- return false;
- auto it = map.find(key);
- return it != map.end() && it->second.get();
-}
-
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -586,7 +575,6 @@ PasswordAutofillAgent::PasswordAutofillAgent(content::RenderFrame* render_frame)
// PasswordAutofillAgent is guaranteed to outlive |render_frame|.
render_frame->GetInterfaceRegistry()->AddInterface(
base::Bind(&PasswordAutofillAgent::BindRequest, base::Unretained(this)));
- GetPasswordManagerDriver()->PasswordAutofillAgentConstructed();
}
PasswordAutofillAgent::~PasswordAutofillAgent() {
@@ -855,9 +843,23 @@ bool PasswordAutofillAgent::ShowSuggestions(
blink::WebInputElement username_element;
blink::WebInputElement password_element;
PasswordInfo* password_info;
+
if (!FindPasswordInfoForElement(element, &username_element, &password_element,
- &password_info))
+ &password_info)) {
+ // If we don't have a password stored, but the form is non-secure, warn
+ // the user about the non-secure form.
+ if ((element.isPasswordField() ||
+ HasAutocompleteAttributeValue(element, "username")) &&
+ security_state::IsHttpWarningInFormEnabled() &&
+ !content::IsOriginSecure(
+ url::Origin(
+ render_frame()->GetWebFrame()->top()->getSecurityOrigin())
+ .GetURL())) {
+ autofill_agent_->ShowNotSecureWarning(element);
+ return true;
+ }
return false;
+ }
// If autocomplete='off' is set on the form elements, no suggestion dialog
// should be shown. However, return |true| to indicate that this is a known
@@ -899,6 +901,16 @@ bool PasswordAutofillAgent::ShowSuggestions(
element.isPasswordField());
}
+void PasswordAutofillAgent::ShowNotSecureWarning(
+ const blink::WebInputElement& element) {
+ FormData form;
+ FormFieldData field;
+ form_util::FindFormAndFieldForFormControlElement(element, &form, &field);
+ GetPasswordManagerDriver()->ShowNotSecureWarning(
+ field.text_direction,
+ render_frame()->GetRenderView()->ElementBoundsInWindow(element));
+}
+
bool PasswordAutofillAgent::OriginCanAccessPasswordManager(
const blink::WebSecurityOrigin& origin) {
return origin.canAccessPasswordManager();
@@ -947,10 +959,6 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) {
}
blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
- // RenderFrameObserver::DidFinishLoad() can fire when Frame is
- // detaching. crbug.com/654654
- if (frame->isFrameDetachedForSpecialOneOffStopTheCrashingHackBug561873())
- return;
// Make sure that this security origin is allowed to use password manager.
blink::WebSecurityOrigin origin = frame->document().getSecurityOrigin();
@@ -1195,7 +1203,7 @@ void PasswordAutofillAgent::DidStartProvisionalLoad() {
*provisionally_saved_form_);
provisionally_saved_form_.reset();
} else {
- ScopedVector<PasswordForm> possible_submitted_forms;
+ std::vector<std::unique_ptr<PasswordForm>> possible_submitted_forms;
// Loop through the forms on the page looking for one that has been
// filled out. If one exists, try and save the credentials.
blink::WebVector<blink::WebFormElement> forms;
@@ -1218,7 +1226,7 @@ void PasswordAutofillAgent::DidStartProvisionalLoad() {
*render_frame()->GetWebFrame(), &field_value_and_properties_map_,
&form_predictions_));
- for (const PasswordForm* password_form : possible_submitted_forms) {
+ for (const auto& password_form : possible_submitted_forms) {
if (password_form && !password_form->username_value.empty() &&
FormContainsNonDefaultPasswordValue(*password_form)) {
password_forms_found = true;
@@ -1506,7 +1514,7 @@ const mojom::PasswordManagerDriverPtr&
PasswordAutofillAgent::GetPasswordManagerDriver() {
if (!password_manager_driver_) {
render_frame()->GetRemoteInterfaces()->GetInterface(
- mojo::GetProxy(&password_manager_driver_));
+ mojo::MakeRequest(&password_manager_driver_));
}
return password_manager_driver_;
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h
index 48f61a493f5..ce63053cce8 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h
@@ -25,7 +25,6 @@
namespace blink {
class WebInputElement;
-class WebKeyboardEvent;
class WebSecurityOrigin;
}
@@ -95,6 +94,11 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
bool show_all,
bool generation_popup_showing);
+ // Shows an Autofill-style popup with a warning that the form is not secure.
+ // This UI is shown when a username or password field is autofilled or edited
+ // on a non-secure page.
+ void ShowNotSecureWarning(const blink::WebInputElement& element);
+
// Called when new form controls are inserted.
void OnDynamicFormsSeen();
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 d41ac97fbe3..3465657dd93 100644
--- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -154,12 +154,16 @@ void ExcludeUsernameFromOtherUsernamesList(
}
// Helper to determine which password is the main (current) one, and which is
-// the new password (e.g., on a sign-up or change password form), if any.
+// the new password (e.g., on a sign-up or change password form), if any. If the
+// new password is found and there is another password field with the same user
+// input, the function also sets |confirmation_password| to this field.
bool LocateSpecificPasswords(std::vector<WebInputElement> passwords,
WebInputElement* current_password,
- WebInputElement* new_password) {
+ WebInputElement* new_password,
+ WebInputElement* confirmation_password) {
DCHECK(current_password && current_password->isNull());
DCHECK(new_password && new_password->isNull());
+ DCHECK(confirmation_password && confirmation_password->isNull());
// First, look for elements marked with either autocomplete='current-password'
// or 'new-password' -- if we find any, take the hint, and treat the first of
@@ -171,6 +175,9 @@ bool LocateSpecificPasswords(std::vector<WebInputElement> passwords,
} else if (HasAutocompleteAttributeValue(it, kAutocompleteNewPassword) &&
new_password->isNull()) {
*new_password = it;
+ } else if (!new_password->isNull() &&
+ (new_password->value() == it.value())) {
+ *confirmation_password = it;
}
}
@@ -197,6 +204,7 @@ bool LocateSpecificPasswords(std::vector<WebInputElement> passwords,
// password with a confirmation. This can be either a sign-up form or a
// password change form that does not ask for the old password.
*new_password = passwords[0];
+ *confirmation_password = passwords[1];
} else {
// Assume first is old password, second is new (no choice but to guess).
// This case also includes empty passwords in order to allow filling of
@@ -218,12 +226,14 @@ bool LocateSpecificPasswords(std::vector<WebInputElement> passwords,
// with 3 password fields, in which case we will assume this layout.
*current_password = passwords[0];
*new_password = passwords[1];
+ *confirmation_password = passwords[2];
} else if (passwords[0].value() == passwords[1].value()) {
// It is strange that the new password comes first, but trust more which
// fields are duplicated than the ordering of fields. Assume that
// any password fields after the new password contain sensitive
// information that isn't actually a password (security hint, SSN, etc.)
*new_password = passwords[0];
+ *confirmation_password = passwords[1];
} else {
// Three different passwords, or first and last match with middle
// different. No idea which is which, so no luck.
@@ -505,7 +515,9 @@ bool GetPasswordForm(
WebInputElement password;
WebInputElement new_password;
- if (!LocateSpecificPasswords(passwords, &password, &new_password))
+ WebInputElement confirmation_password;
+ if (!LocateSpecificPasswords(passwords, &password, &new_password,
+ &confirmation_password))
return false;
DCHECK_EQ(passwords.size(), last_text_input_before_password.size());
@@ -584,6 +596,10 @@ bool GetPasswordForm(
new_password.getAttribute("value") == new_password.value();
if (HasAutocompleteAttributeValue(new_password, kAutocompleteNewPassword))
password_form->new_password_marked_by_site = true;
+ if (!confirmation_password.isNull()) {
+ password_form->confirmation_password_element =
+ FieldName(confirmation_password, "anonymous_confirmation_password");
+ }
}
if (username_element.isNull()) {
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 4101dcb9460..4c8cd645e46 100644
--- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
@@ -15,7 +15,6 @@
#include "url/gurl.h"
namespace blink {
-class WebDocument;
class WebFormElement;
class WebFormControlElement;
class WebFrame;
@@ -24,8 +23,6 @@ class WebInputElement;
namespace autofill {
-struct FormData;
-struct FormFieldData;
struct PasswordForm;
// Tests whether the given form is a GAIA reauthentication form. The form is
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 5169e0bc376..08b1988e158 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
@@ -213,9 +213,8 @@ class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest {
FormStructure form_structure(form_data);
int field_index = 0;
- for (std::vector<AutofillField *>::const_iterator
- field = form_structure.begin();
- field != form_structure.end(); ++field, ++field_index) {
+ for (auto field = form_structure.begin(); field != form_structure.end();
+ ++field, ++field_index) {
if (predictions_positions.find(field_index) !=
predictions_positions.end()) {
(*predictions)[form_data][*(*field)] =
@@ -384,19 +383,20 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) {
const char* expected_password_value;
const char* expected_new_password_element;
const char* expected_new_password_value;
+ const char* expected_confirmation_element;
} cases[] = {
// Two non-empty fields with the same value should be treated as a new
// password field plus a confirmation field for the new password.
- {{"alpha", "alpha"}, "", "", "password1", "alpha"},
+ {{"alpha", "alpha"}, "", "", "password1", "alpha", "password2"},
// The same goes if the fields are yet empty: we speculate that we will
// identify them as new password fields once they are filled out, and we
// want to keep our abstract interpretation of the form less flaky.
- {{"", ""}, "password1", "", "password2", ""},
+ {{"", ""}, "password1", "", "password2", "", ""},
// Two different values should be treated as a password change form, one
// that also asks for the current password, but only once for the new.
- {{"alpha", ""}, "password1", "alpha", "password2", ""},
- {{"", "beta"}, "password1", "", "password2", "beta"},
- {{"alpha", "beta"}, "password1", "alpha", "password2", "beta"}};
+ {{"alpha", ""}, "password1", "alpha", "password2", "", ""},
+ {{"", "beta"}, "password1", "", "password2", "beta", ""},
+ {{"alpha", "beta"}, "password1", "alpha", "password2", "beta", ""}};
for (size_t i = 0; i < arraysize(cases); ++i) {
SCOPED_TRACE(testing::Message() << "Iteration " << i);
@@ -421,6 +421,8 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) {
password_form->new_password_element);
EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
password_form->new_password_value);
+ EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_confirmation_element),
+ password_form->confirmation_password_element);
// Do a basic sanity check that we are still selecting the right username.
EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
@@ -439,24 +441,30 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) {
const char* expected_password_value;
const char* expected_new_password_element;
const char* expected_new_password_value;
+ const char* expected_confirmation_element;
} cases[] = {
// Two fields with the same value, and one different: we should treat this
// as a password change form with confirmation for the new password. Note
// that we only recognize (current + new + new) and (new + new + current)
// without autocomplete attributes.
- {{"alpha", "", ""}, "password1", "alpha", "password2", ""},
- {{"", "beta", "beta"}, "password1", "", "password2", "beta"},
- {{"alpha", "beta", "beta"}, "password1", "alpha", "password2", "beta"},
+ {{"alpha", "", ""}, "password1", "alpha", "password2", "", "password3"},
+ {{"", "beta", "beta"}, "password1", "", "password2", "beta", "password3"},
+ {{"alpha", "beta", "beta"},
+ "password1",
+ "alpha",
+ "password2",
+ "beta",
+ "password3"},
// If confirmed password comes first, assume that the third password
// field is related to security question, SSN, or credit card and ignore
// it.
- {{"beta", "beta", "alpha"}, "", "", "password1", "beta"},
+ {{"beta", "beta", "alpha"}, "", "", "password1", "beta", "password2"},
// If the fields are yet empty, we speculate that we will identify them as
// (current + new + new) once they are filled out, so we should classify
// them the same for now to keep our abstract interpretation less flaky.
- {{"", "", ""}, "password1", "", "password2", ""}};
- // Note: In all other cases, we give up and consider the form invalid.
- // This is tested in InvalidFormDueToConfusingPasswordFields.
+ {{"", "", ""}, "password1", "", "password2", "", "password3"}};
+ // Note: In all other cases, we give up and consider the form invalid.
+ // This is tested in InvalidFormDueToConfusingPasswordFields.
for (size_t i = 0; i < arraysize(cases); ++i) {
SCOPED_TRACE(testing::Message() << "Iteration " << i);
@@ -482,6 +490,8 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) {
password_form->new_password_element);
EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
password_form->new_password_value);
+ EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_confirmation_element),
+ password_form->confirmation_password_element);
// Do a basic sanity check that we are still selecting the right username.
EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc
index a0a52834799..183b5f068c2 100644
--- a/chromium/components/autofill/content/renderer/password_generation_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc
@@ -8,6 +8,7 @@
#include "base/command_line.h"
#include "base/logging.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"
#include "components/autofill/content/renderer/password_autofill_agent.h"
diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.h b/chromium/components/autofill/content/renderer/password_generation_agent.h
index 2b73a4dc01d..694c5fc990c 100644
--- a/chromium/components/autofill/content/renderer/password_generation_agent.h
+++ b/chromium/components/autofill/content/renderer/password_generation_agent.h
@@ -21,13 +21,8 @@
#include "third_party/WebKit/public/web/WebInputElement.h"
#include "url/gurl.h"
-namespace blink {
-class WebDocument;
-}
-
namespace autofill {
-struct FormData;
struct PasswordForm;
struct PasswordFormGenerationData;
class PasswordAutofillAgent;
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 7c0c27e21c9..85958d4f696 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
@@ -61,7 +61,8 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver {
int options,
const gfx::RectF& bounds) override {}
- void PasswordAutofillAgentConstructed() override {}
+ void ShowNotSecureWarning(base::i18n::TextDirection text_direction,
+ const gfx::RectF& bounds) override {}
void RecordSavePasswordProgress(const std::string& log) override {
called_record_save_ = true;
diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn
index 4e919e7d359..5281b7de472 100644
--- a/chromium/components/autofill/core/browser/BUILD.gn
+++ b/chromium/components/autofill/core/browser/BUILD.gn
@@ -117,6 +117,8 @@ static_library("browser") {
"ui/card_unmask_prompt_view.h",
"validation.cc",
"validation.h",
+ "webdata/autocomplete_sync_bridge.cc",
+ "webdata/autocomplete_sync_bridge.h",
"webdata/autocomplete_syncable_service.cc",
"webdata/autocomplete_syncable_service.h",
"webdata/autofill_change.cc",
@@ -125,6 +127,8 @@ static_library("browser") {
"webdata/autofill_data_type_controller.h",
"webdata/autofill_entry.cc",
"webdata/autofill_entry.h",
+ "webdata/autofill_metadata_change_list.cc",
+ "webdata/autofill_metadata_change_list.h",
"webdata/autofill_profile_data_type_controller.cc",
"webdata/autofill_profile_data_type_controller.h",
"webdata/autofill_profile_syncable_service.cc",
@@ -316,10 +320,12 @@ source_set("unit_tests") {
"phone_number_unittest.cc",
"ui/card_unmask_prompt_controller_impl_unittest.cc",
"validation_unittest.cc",
+ "webdata/autocomplete_sync_bridge_unittest.cc",
"webdata/autofill_data_type_controller_unittest.cc",
"webdata/autofill_profile_syncable_service_unittest.cc",
"webdata/autofill_table_unittest.cc",
"webdata/autofill_wallet_metadata_syncable_service_unittest.cc",
+ "webdata/autofill_wallet_syncable_service_unittest.cc",
"webdata/web_data_service_unittest.cc",
]
diff --git a/chromium/components/autofill/core/browser/address_field_unittest.cc b/chromium/components/autofill/core/browser/address_field_unittest.cc
index b3f4f92ca18..d959e8d7740 100644
--- a/chromium/components/autofill/core/browser/address_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/address_field_unittest.cc
@@ -5,10 +5,10 @@
#include "components/autofill/core/browser/address_field.h"
#include <memory>
+#include <vector>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_field.h"
@@ -25,7 +25,7 @@ class AddressFieldTest : public testing::Test {
AddressFieldTest() {}
protected:
- ScopedVector<AutofillField> list_;
+ std::vector<std::unique_ptr<AutofillField>> list_;
std::unique_ptr<AddressField> field_;
FieldCandidatesMap field_candidates_map_;
@@ -40,14 +40,14 @@ class AddressFieldTest : public testing::Test {
};
TEST_F(AddressFieldTest, Empty) {
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_EQ(nullptr, field_.get());
}
TEST_F(AddressFieldTest, NonParse) {
- list_.push_back(new AutofillField);
- AutofillScanner scanner(list_.get());
+ list_.push_back(base::MakeUnique<AutofillField>());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_EQ(nullptr, field_.get());
}
@@ -58,9 +58,10 @@ TEST_F(AddressFieldTest, ParseOneLineAddress) {
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("address");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr1")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -76,13 +77,15 @@ TEST_F(AddressFieldTest, ParseTwoLineAddress) {
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("address");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr1")));
field.label = base::string16();
field.name = ASCIIToUTF16("address2");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("addr2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr2")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -102,17 +105,20 @@ TEST_F(AddressFieldTest, ParseThreeLineAddress) {
field.label = ASCIIToUTF16("Address Line1");
field.name = ASCIIToUTF16("Address1");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr1")));
field.label = ASCIIToUTF16("Address Line2");
field.name = ASCIIToUTF16("Address2");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("addr2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr2")));
field.label = ASCIIToUTF16("Address Line3");
field.name = ASCIIToUTF16("Address3");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("addr3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr3")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -136,9 +142,9 @@ TEST_F(AddressFieldTest, ParseStreetAddressFromTextArea) {
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("address");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("addr")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -154,9 +160,10 @@ TEST_F(AddressFieldTest, ParseCity) {
field.label = ASCIIToUTF16("City");
field.name = ASCIIToUTF16("city");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("city1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("city1")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -172,9 +179,10 @@ TEST_F(AddressFieldTest, ParseState) {
field.label = ASCIIToUTF16("State");
field.name = ASCIIToUTF16("state");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("state1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("state1")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -190,9 +198,9 @@ TEST_F(AddressFieldTest, ParseZip) {
field.label = ASCIIToUTF16("Zip");
field.name = ASCIIToUTF16("zip");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("zip1")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("zip1")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -208,13 +216,14 @@ TEST_F(AddressFieldTest, ParseStateAndZipOneLabel) {
field.label = ASCIIToUTF16("State/Province, Zip/Postal Code");
field.name = ASCIIToUTF16("state");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("state")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("state")));
field.label = ASCIIToUTF16("State/Province, Zip/Postal Code");
field.name = ASCIIToUTF16("zip");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("zip")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("zip")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -234,9 +243,10 @@ TEST_F(AddressFieldTest, ParseCountry) {
field.label = ASCIIToUTF16("Country");
field.name = ASCIIToUTF16("country");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("country1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("country1")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -253,9 +263,10 @@ TEST_F(AddressFieldTest, ParseCompany) {
field.label = ASCIIToUTF16("Company");
field.name = ASCIIToUTF16("company");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("company1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("company1")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
diff --git a/chromium/components/autofill/core/browser/autofill_assistant.cc b/chromium/components/autofill/core/browser/autofill_assistant.cc
index 4fccc2a7997..3e463b614bc 100644
--- a/chromium/components/autofill/core/browser/autofill_assistant.cc
+++ b/chromium/components/autofill/core/browser/autofill_assistant.cc
@@ -31,8 +31,7 @@ bool AutofillAssistant::CanShowCreditCardAssist(
!IsAutofillCreditCardAssistEnabled() ||
// Context of the page is not secure or target URL is valid but not
// secure.
- !(autofill_manager_->client()->IsContextSecure(
- form_structures.front()->source_url()) &&
+ !(autofill_manager_->client()->IsContextSecure() &&
(!form_structures.front()->target_url().is_valid() ||
!form_structures.front()->target_url().SchemeIs("http")))) {
return false;
diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h
index 61b10177c41..3960f44fdbb 100644
--- a/chromium/components/autofill/core/browser/autofill_client.h
+++ b/chromium/components/autofill/core/browser/autofill_client.h
@@ -17,7 +17,6 @@
#include "ui/base/window_open_disposition.h"
#include "url/gurl.h"
-class GURL;
class IdentityProvider;
class PrefService;
@@ -26,12 +25,11 @@ class RenderFrameHost;
}
namespace gfx {
-class Rect;
class RectF;
}
namespace rappor {
-class RapporService;
+class RapporServiceImpl;
}
namespace syncer {
@@ -46,7 +44,6 @@ class CardUnmaskDelegate;
class CreditCard;
class FormStructure;
class PersonalDataManager;
-struct FormData;
struct Suggestion;
// A client interface that needs to be supplied to the Autofill component by the
@@ -105,8 +102,8 @@ class AutofillClient {
// Gets the IdentityProvider associated with the client (for OAuth2).
virtual IdentityProvider* GetIdentityProvider() = 0;
- // Gets the RapporService associated with the client (for metrics).
- virtual rappor::RapporService* GetRapporService() = 0;
+ // Gets the RapporServiceImpl associated with the client (for metrics).
+ virtual rappor::RapporServiceImpl* GetRapporServiceImpl() = 0;
// Causes the Autofill settings UI to be shown.
virtual void ShowAutofillSettings() = 0;
@@ -183,7 +180,7 @@ class AutofillClient {
virtual void OnFirstUserGestureObserved() = 0;
// If the context is secure.
- virtual bool IsContextSecure(const GURL& form_origin) = 0;
+ virtual bool IsContextSecure() = 0;
// Whether it is appropriate to show a signin promo for this user.
virtual bool ShouldShowSigninPromo() = 0;
@@ -191,6 +188,9 @@ class AutofillClient {
// Starts the signin flow. Should not be called if ShouldShowSigninPromo()
// returns false.
virtual void StartSigninFlow() = 0;
+
+ // Shows the explanation of http not secure warning message.
+ virtual void ShowHttpNotSecureExplanation() = 0;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.h b/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.h
index fe1cc199e0e..04e030694d5 100644
--- a/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.h
+++ b/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.h
@@ -13,10 +13,6 @@
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/infobars/core/confirm_infobar_delegate.h"
-namespace infobars {
-class InfoBarManager;
-}
-
namespace autofill {
class CreditCard;
diff --git a/chromium/components/autofill/core/browser/autofill_data_model.h b/chromium/components/autofill/core/browser/autofill_data_model.h
index ee055ff49b3..4cc373b7177 100644
--- a/chromium/components/autofill/core/browser/autofill_data_model.h
+++ b/chromium/components/autofill/core/browser/autofill_data_model.h
@@ -15,8 +15,6 @@
namespace autofill {
-class AutofillType;
-
// This class is an interface for the primary data models that back Autofill.
// The information in objects of this class is managed by the
// PersonalDataManager.
diff --git a/chromium/components/autofill/core/browser/autofill_data_util.cc b/chromium/components/autofill/core/browser/autofill_data_util.cc
index 5a09665034c..d931d7b0888 100644
--- a/chromium/components/autofill/core/browser/autofill_data_util.cc
+++ b/chromium/components/autofill/core/browser/autofill_data_util.cc
@@ -27,6 +27,7 @@ const PaymentRequestData kPaymentRequestData[]{
{"discoverCC", "discover", IDR_AUTOFILL_PR_DISCOVER},
{"jcbCC", "jcb", IDR_AUTOFILL_PR_JCB},
{"masterCardCC", "mastercard", IDR_AUTOFILL_PR_MASTERCARD},
+ {"mirCC", "mir", IDR_AUTOFILL_PR_MIR},
{"unionPayCC", "unionpay", IDR_AUTOFILL_PR_UNIONPAY},
{"visaCC", "visa", IDR_AUTOFILL_PR_VISA},
};
diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.cc b/chromium/components/autofill/core/browser/autofill_download_manager.cc
index 331de998450..07084c52be4 100644
--- a/chromium/components/autofill/core/browser/autofill_download_manager.cc
+++ b/chromium/components/autofill/core/browser/autofill_download_manager.cc
@@ -247,8 +247,8 @@ bool AutofillDownloadManager::StartRequest(
net::LOAD_DO_NOT_SEND_COOKIES);
// Add Chrome experiment state to the request headers.
net::HttpRequestHeaders headers;
- // Note: It's fine to pass in |is_signed_in| false, which does not affect
- // transmission of experiment ids coming from the variations server.
+ // 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(fetcher->GetOriginalURL(),
driver_->IsOffTheRecord(), false,
diff --git a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc
index e9bfcb30cc9..73768ad2ace 100644
--- a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc
@@ -9,7 +9,9 @@
#include <list>
#include <memory>
#include <utility>
+#include <vector>
+#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -49,6 +51,14 @@ void FakeOnURLFetchComplete(net::TestURLFetcher* fetcher,
fetcher->delegate()->OnURLFetchComplete(fetcher);
}
+std::vector<FormStructure*> ToRawPointerVector(
+ const std::vector<std::unique_ptr<FormStructure>>& list) {
+ std::vector<FormStructure*> result;
+ for (const auto& item : list)
+ result.push_back(item.get());
+ return result;
+}
+
} // namespace
// This tests AutofillDownloadManager. AutofillDownloadManagerTest implements
@@ -166,9 +176,8 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) {
field.form_control_type = "submit";
form.fields.push_back(field);
- FormStructure* form_structure = new FormStructure(form);
- ScopedVector<FormStructure> form_structures;
- form_structures.push_back(form_structure);
+ std::vector<std::unique_ptr<FormStructure>> form_structures;
+ form_structures.push_back(base::MakeUnique<FormStructure>(form));
form.fields.clear();
@@ -192,8 +201,7 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) {
field.form_control_type = "submit";
form.fields.push_back(field);
- form_structure = new FormStructure(form);
- form_structures.push_back(form_structure);
+ form_structures.push_back(base::MakeUnique<FormStructure>(form));
form.fields.clear();
@@ -212,12 +220,12 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) {
field.form_control_type = "submit";
form.fields.push_back(field);
- form_structure = new FormStructure(form);
- form_structures.push_back(form_structure);
+ form_structures.push_back(base::MakeUnique<FormStructure>(form));
// Request with id 0.
base::HistogramTester histogram;
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures.get()));
+ EXPECT_TRUE(
+ download_manager_.StartQueryRequest(ToRawPointerVector(form_structures)));
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 1);
@@ -296,11 +304,11 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) {
field.name = ASCIIToUTF16("address2");
field.form_control_type = "text";
form.fields.push_back(field);
- form_structure = new FormStructure(form);
- form_structures.push_back(form_structure);
+ form_structures.push_back(base::MakeUnique<FormStructure>(form));
// Request with id 4, not successful.
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures.get()));
+ EXPECT_TRUE(
+ download_manager_.StartQueryRequest(ToRawPointerVector(form_structures)));
fetcher = factory.GetFetcherByID(4);
ASSERT_TRUE(fetcher);
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
@@ -343,13 +351,13 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Query) {
field.form_control_type = "submit";
form.fields.push_back(field);
- FormStructure* form_structure = new FormStructure(form);
- ScopedVector<FormStructure> form_structures;
- form_structures.push_back(form_structure);
+ std::vector<std::unique_ptr<FormStructure>> form_structures;
+ form_structures.push_back(base::MakeUnique<FormStructure>(form));
// Request with id 0.
base::HistogramTester histogram;
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures.get()));
+ EXPECT_TRUE(
+ download_manager_.StartQueryRequest(ToRawPointerVector(form_structures)));
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 1);
@@ -403,7 +411,7 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Upload) {
field.form_control_type = "submit";
form.fields.push_back(field);
- std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
+ auto form_structure = base::MakeUnique<FormStructure>(form);
// Request with id 0.
EXPECT_TRUE(download_manager_.StartUploadRequest(
@@ -452,7 +460,7 @@ TEST_F(AutofillDownloadManagerTest, QueryTooManyFieldsTest) {
// Create a query that contains too many fields for the server.
std::vector<FormData> forms(21);
- ScopedVector<FormStructure> form_structures;
+ std::vector<std::unique_ptr<FormStructure>> form_structures;
for (auto& form : forms) {
for (size_t i = 0; i < 5; ++i) {
FormFieldData field;
@@ -461,12 +469,12 @@ TEST_F(AutofillDownloadManagerTest, QueryTooManyFieldsTest) {
field.form_control_type = "text";
form.fields.push_back(field);
}
- FormStructure* form_structure = new FormStructure(form);
- form_structures.push_back(form_structure);
+ form_structures.push_back(base::MakeUnique<FormStructure>(form));
}
// Check whether the query is aborted.
- EXPECT_FALSE(download_manager_.StartQueryRequest(form_structures.get()));
+ EXPECT_FALSE(
+ download_manager_.StartQueryRequest(ToRawPointerVector(form_structures)));
}
TEST_F(AutofillDownloadManagerTest, QueryNotTooManyFieldsTest) {
@@ -476,7 +484,7 @@ TEST_F(AutofillDownloadManagerTest, QueryNotTooManyFieldsTest) {
// Create a query that contains a lot of fields, but not too many for the
// server.
std::vector<FormData> forms(25);
- ScopedVector<FormStructure> form_structures;
+ std::vector<std::unique_ptr<FormStructure>> form_structures;
for (auto& form : forms) {
for (size_t i = 0; i < 4; ++i) {
FormFieldData field;
@@ -485,12 +493,12 @@ TEST_F(AutofillDownloadManagerTest, QueryNotTooManyFieldsTest) {
field.form_control_type = "text";
form.fields.push_back(field);
}
- FormStructure* form_structure = new FormStructure(form);
- form_structures.push_back(form_structure);
+ form_structures.push_back(base::MakeUnique<FormStructure>(form));
}
// Check that the query is not aborted.
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures.get()));
+ EXPECT_TRUE(
+ download_manager_.StartQueryRequest(ToRawPointerVector(form_structures)));
}
TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
@@ -514,26 +522,23 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
field.name = ASCIIToUTF16("lastname");
form.fields.push_back(field);
- FormStructure* form_structure = new FormStructure(form);
- ScopedVector<FormStructure> form_structures0;
- form_structures0.push_back(form_structure);
+ std::vector<std::unique_ptr<FormStructure>> form_structures0;
+ form_structures0.push_back(base::MakeUnique<FormStructure>(form));
// Add a slightly different form, which should result in a different request.
field.label = ASCIIToUTF16("email");
field.name = ASCIIToUTF16("email");
form.fields.push_back(field);
- form_structure = new FormStructure(form);
- ScopedVector<FormStructure> form_structures1;
- form_structures1.push_back(form_structure);
+ std::vector<std::unique_ptr<FormStructure>> form_structures1;
+ form_structures1.push_back(base::MakeUnique<FormStructure>(form));
// Add another slightly different form, which should also result in a
// different request.
field.label = ASCIIToUTF16("email2");
field.name = ASCIIToUTF16("email2");
form.fields.push_back(field);
- form_structure = new FormStructure(form);
- ScopedVector<FormStructure> form_structures2;
- form_structures2.push_back(form_structure);
+ std::vector<std::unique_ptr<FormStructure>> form_structures2;
+ form_structures2.push_back(base::MakeUnique<FormStructure>(form));
// Limit cache to two forms.
LimitCache(2);
@@ -561,7 +566,8 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
base::HistogramTester histogram;
// Request with id 0.
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures0.get()));
+ EXPECT_TRUE(download_manager_.StartQueryRequest(
+ ToRawPointerVector(form_structures0)));
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 1);
@@ -577,7 +583,8 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
responses_.clear();
// No actual request - should be a cache hit.
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures0.get()));
+ EXPECT_TRUE(download_manager_.StartQueryRequest(
+ ToRawPointerVector(form_structures0)));
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 2);
// Data is available immediately from cache - no over-the-wire trip.
@@ -586,7 +593,8 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
responses_.clear();
// Request with id 1.
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures1.get()));
+ EXPECT_TRUE(download_manager_.StartQueryRequest(
+ ToRawPointerVector(form_structures1)));
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 3);
// No responses yet
@@ -601,7 +609,8 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
responses_.clear();
// Request with id 2.
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures2.get()));
+ EXPECT_TRUE(download_manager_.StartQueryRequest(
+ ToRawPointerVector(form_structures2)));
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 4);
@@ -614,11 +623,13 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
responses_.clear();
// No actual requests - should be a cache hit.
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures1.get()));
+ EXPECT_TRUE(download_manager_.StartQueryRequest(
+ ToRawPointerVector(form_structures1)));
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 5);
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures2.get()));
+ EXPECT_TRUE(download_manager_.StartQueryRequest(
+ ToRawPointerVector(form_structures2)));
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 6);
@@ -629,7 +640,8 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
// The first structure should've expired.
// Request with id 3.
- EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures0.get()));
+ EXPECT_TRUE(download_manager_.StartQueryRequest(
+ ToRawPointerVector(form_structures0)));
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 7);
// No responses yet
diff --git a/chromium/components/autofill/core/browser/autofill_experiments.cc b/chromium/components/autofill/core/browser/autofill_experiments.cc
index e066d854f55..39472262445 100644
--- a/chromium/components/autofill/core/browser/autofill_experiments.cc
+++ b/chromium/components/autofill/core/browser/autofill_experiments.cc
@@ -7,16 +7,20 @@
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial.h"
+#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
+#include "components/autofill/core/browser/suggestion.h"
#include "components/autofill/core/common/autofill_pref_names.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/prefs/pref_service.h"
-#include "components/security_state/core/switches.h"
#include "components/sync/driver/sync_service.h"
#include "components/variations/variations_associated_data.h"
#include "google_apis/gaia/gaia_auth_util.h"
+#include "grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
namespace autofill {
@@ -24,11 +28,37 @@ const base::Feature kAutofillCreditCardAssist{
"AutofillCreditCardAssist", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillCreditCardSigninPromo{
"AutofillCreditCardSigninPromo", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kAutofillProfileCleanup{"AutofillProfileCleanup",
- base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillScanCardholderName{
"AutofillScanCardholderName", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAutofillCreditCardPopupLayout{
+ "AutofillCreditCardPopupLayout", base::FEATURE_DISABLED_BY_DEFAULT};
const char kCreditCardSigninPromoImpressionLimitParamKey[] = "impression_limit";
+const char kAutofillCreditCardPopupBackgroundColorKey[] = "background_color";
+const char kAutofillCreditCardPopupDividerColorKey[] = "dropdown_divider_color";
+const char kAutofillCreditCardPopupValueBoldKey[] = "is_value_bold";
+const char kAutofillCreditCardPopupIsValueAndLabelInSingleLineKey[] =
+ "is_value_and_label_in_single_line";
+const char kAutofillPopupDropdownItemHeightKey[] =
+ "dropdown_item_height";
+const char kAutofillCreditCardPopupIsIconAtStartKey[] =
+ "is_credit_card_icon_at_start";
+const char kAutofillPopupMarginKey[] = "margin";
+
+namespace {
+
+// Returns parameter value in |kAutofillCreditCardPopupLayout| feature, or 0 if
+// parameter is not specified.
+unsigned int GetCreditCardPopupParameterUintValue(
+ const std::string& param_name) {
+ unsigned int value;
+ const std::string param_value = variations::GetVariationParamValueByFeature(
+ kAutofillCreditCardPopupLayout, param_name);
+ if (!param_value.empty() && base::StringToUint(param_value, &value))
+ return value;
+ return 0;
+}
+
+} // namespace
bool IsAutofillEnabled(const PrefService* pref_service) {
return pref_service->GetBoolean(prefs::kAutofillEnabled);
@@ -40,10 +70,6 @@ bool IsInAutofillSuggestionsDisabledExperiment() {
return group_name == "Disabled";
}
-bool IsAutofillProfileCleanupEnabled() {
- return base::FeatureList::IsEnabled(kAutofillProfileCleanup);
-}
-
bool IsAutofillCreditCardSigninPromoEnabled() {
return base::FeatureList::IsEnabled(kAutofillCreditCardSigninPromo);
}
@@ -67,6 +93,64 @@ int GetCreditCardSigninPromoImpressionLimit() {
return 0;
}
+bool IsAutofillCreditCardPopupLayoutExperimentEnabled() {
+ return base::FeatureList::IsEnabled(kAutofillCreditCardPopupLayout);
+}
+
+// |GetCreditCardPopupParameterUintValue| returns 0 if experiment parameter is
+// not specified. 0 == |SK_ColorTRANSPARENT|.
+SkColor GetCreditCardPopupBackgroundColor() {
+ return GetCreditCardPopupParameterUintValue(
+ kAutofillCreditCardPopupBackgroundColorKey);
+}
+
+SkColor GetCreditCardPopupDividerColor() {
+ return GetCreditCardPopupParameterUintValue(
+ kAutofillCreditCardPopupDividerColorKey);
+}
+
+bool IsCreditCardPopupValueBold() {
+ const std::string param_value = variations::GetVariationParamValueByFeature(
+ kAutofillCreditCardPopupLayout, kAutofillCreditCardPopupValueBoldKey);
+ return param_value == "true";
+}
+
+unsigned int GetPopupDropdownItemHeight() {
+ return GetCreditCardPopupParameterUintValue(
+ kAutofillPopupDropdownItemHeightKey);
+}
+
+bool IsIconInCreditCardPopupAtStart() {
+ const std::string param_value = variations::GetVariationParamValueByFeature(
+ kAutofillCreditCardPopupLayout, kAutofillCreditCardPopupIsIconAtStartKey);
+ return param_value == "true";
+}
+
+// Modifies |suggestion| as follows if experiment to display value and label in
+// a single line is enabled.
+// Say, |value| is 'Visa ....1111' and |label| is '01/18' (expiration date).
+// Modifies |value| to 'Visa ....1111, exp 01/18' and clears |label|.
+void ModifyAutofillCreditCardSuggestion(Suggestion* suggestion) {
+ DCHECK(IsAutofillCreditCardPopupLayoutExperimentEnabled());
+ const std::string param_value = variations::GetVariationParamValueByFeature(
+ kAutofillCreditCardPopupLayout,
+ kAutofillCreditCardPopupIsValueAndLabelInSingleLineKey);
+ if (param_value == "true") {
+ const base::string16 format_string = l10n_util::GetStringUTF16(
+ IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_LABEL_AND_ABBR);
+ if (!format_string.empty()) {
+ suggestion->value.append(l10n_util::GetStringFUTF16(
+ IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_LABEL_AND_ABBR,
+ suggestion->label));
+ }
+ suggestion->label.clear();
+ }
+}
+
+unsigned int GetPopupMargin() {
+ return GetCreditCardPopupParameterUintValue(kAutofillPopupMarginKey);
+}
+
bool OfferStoreUnmaskedCards() {
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
// The checkbox can be forced on with a flag, but by default we don't store
@@ -108,9 +192,9 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service,
// Users who have enabled a passphrase have chosen to not make their sync
// information accessible to Google. Since upload makes credit card data
// available to other Google systems, disable it for passphrase users.
- // We can't determine the passphrase state until the sync backend is
+ // We can't determine the passphrase state until the sync engine is
// initialized so disable upload if sync is not yet available.
- if (!sync_service->IsBackendInitialized() ||
+ if (!sync_service->IsEngineInitialized() ||
sync_service->IsUsingSecondaryPassphrase()) {
return false;
}
@@ -142,12 +226,4 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service,
return !group_name.empty() && group_name != "Disabled";
}
-bool IsCreditCardAutofillHttpWarningEnabled() {
- std::string choice =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- security_state::switches::kMarkHttpAs);
- return choice == security_state::switches::
- kMarkHttpWithPasswordsOrCcWithChipAndFormWarning;
-}
-
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_experiments.h b/chromium/components/autofill/core/browser/autofill_experiments.h
index 9fe4fc0b556..4467e38d9b3 100644
--- a/chromium/components/autofill/core/browser/autofill_experiments.h
+++ b/chromium/components/autofill/core/browser/autofill_experiments.h
@@ -7,6 +7,9 @@
#include <string>
+#include "base/strings/string16.h"
+#include "third_party/skia/include/core/SkColor.h"
+
class PrefService;
namespace base {
@@ -19,11 +22,14 @@ class SyncService;
namespace autofill {
+struct Suggestion;
+
extern const base::Feature kAutofillCreditCardAssist;
extern const base::Feature kAutofillCreditCardSigninPromo;
-extern const base::Feature kAutofillProfileCleanup;
extern const base::Feature kAutofillScanCardholderName;
+extern const base::Feature kAutofillCreditCardPopupLayout;
extern const char kCreditCardSigninPromoImpressionLimitParamKey[];
+extern const char kAutofillCreditCardPopupSettingsSuggestionValueKey[];
// Returns true if autofill should be enabled. See also
// IsInAutofillSuggestionsDisabledExperiment below.
@@ -35,9 +41,6 @@ bool IsAutofillEnabled(const PrefService* pref_service);
// disables providing suggestions.
bool IsInAutofillSuggestionsDisabledExperiment();
-// Returns whether the Autofill profile cleanup feature is enabled.
-bool IsAutofillProfileCleanupEnabled();
-
// Returns whether the Autofill credit card signin promo should be shown.
bool IsAutofillCreditCardSigninPromoEnabled();
@@ -60,9 +63,41 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service,
const syncer::SyncService* sync_service,
const std::string& user_email);
-// Returns true if the http warning switch is on, which will display a warning
-// in the autofill dropdown when credit card fields are on an HTTP page.
-bool IsCreditCardAutofillHttpWarningEnabled();
+// Returns whether the new Autofill credit card popup layout experiment is
+// enabled.
+bool IsAutofillCreditCardPopupLayoutExperimentEnabled();
+
+// Returns the background color for credit card autofill popup, or
+// |SK_ColorTRANSPARENT| if the new credit card autofill popup layout experiment
+// is not enabled.
+SkColor GetCreditCardPopupBackgroundColor();
+
+// Returns the divider color for credit card autofill popup, or
+// |SK_ColorTRANSPARENT| if the new credit card autofill popup layout experiment
+// is not enabled.
+SkColor GetCreditCardPopupDividerColor();
+
+// Returns true if the credit card autofill popup suggestion value is displayed
+// in bold type face.
+bool IsCreditCardPopupValueBold();
+
+// Returns the dropdown item height for autofill popup, returning 0 if the
+// dropdown item height isn't configured in an experiment to tweak autofill
+// popup layout.
+unsigned int GetPopupDropdownItemHeight();
+
+// Returns true if the icon in the credit card autofill popup must be displayed
+// before the credit card value or any other suggestion text.
+bool IsIconInCreditCardPopupAtStart();
+
+// Modifies the suggestion value and label if the new credit card autofill popup
+// experiment is enabled to tweak the display of the value and label.
+void ModifyAutofillCreditCardSuggestion(struct Suggestion* suggestion);
+
+// Returns the margin for the icon, label and between icon and label. Returns 0
+// if the margin isn't configured in an experiment to tweak autofill popup
+// layout.
+unsigned int GetPopupMargin();
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc
index 96a96c34879..e80c8ac5461 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc
@@ -19,6 +19,7 @@
#include "build/build_config.h"
#include "components/autofill/core/browser/autocomplete_history_manager.h"
#include "components/autofill/core/browser/autofill_driver.h"
+#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/popup_item_ids.h"
@@ -31,9 +32,11 @@ namespace autofill {
namespace {
// Returns true if the suggestion entry is an Autofill warning message.
-// Warning message should display on top of suggestion list.
+// Warning messages should display on top of suggestion list.
bool IsAutofillWarningEntry(int frontend_id) {
- return frontend_id == POPUP_ITEM_ID_WARNING_MESSAGE;
+ return frontend_id ==
+ POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE ||
+ frontend_id == POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE;
}
} // anonymous namespace
@@ -46,6 +49,7 @@ AutofillExternalDelegate::AutofillExternalDelegate(AutofillManager* manager,
has_autofill_suggestions_(false),
has_shown_popup_for_current_edit_(false),
should_show_scan_credit_card_(false),
+ is_credit_card_popup_(false),
should_show_cc_signin_promo_(false),
has_shown_address_book_prompt(false),
weak_ptr_factory_(this) {
@@ -67,6 +71,8 @@ void AutofillExternalDelegate::OnQuery(int query_id,
element_bounds_ = element_bounds;
should_show_scan_credit_card_ =
manager_->ShouldShowScanCreditCard(query_form_, query_field_);
+ is_credit_card_popup_ =
+ manager_->IsCreditCardPopup(query_form_, query_field_);
should_show_cc_signin_promo_ =
manager_->ShouldShowCreditCardSigninPromo(query_form_, query_field_);
}
@@ -210,7 +216,8 @@ void AutofillExternalDelegate::DidAcceptSuggestion(const base::string16& value,
} else if (identifier == POPUP_ITEM_ID_CLEAR_FORM) {
// User selected 'Clear form'.
driver_->RendererShouldClearFilledForm();
- } else if (identifier == POPUP_ITEM_ID_PASSWORD_ENTRY) {
+ } else if (identifier == POPUP_ITEM_ID_PASSWORD_ENTRY ||
+ identifier == POPUP_ITEM_ID_USERNAME_ENTRY) {
NOTREACHED(); // Should be handled elsewhere.
} else if (identifier == POPUP_ITEM_ID_DATALIST_ENTRY) {
driver_->RendererShouldAcceptDataListSuggestion(value);
@@ -223,6 +230,9 @@ void AutofillExternalDelegate::DidAcceptSuggestion(const base::string16& value,
&AutofillExternalDelegate::OnCreditCardScanned, GetWeakPtr()));
} else if (identifier == POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO) {
manager_->client()->StartSigninFlow();
+ } else if (identifier == POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE) {
+ AutofillMetrics::LogShowedHttpNotSecureExplanation();
+ manager_->client()->ShowHttpNotSecureExplanation();
} else {
if (identifier > 0) // Denotes an Autofill suggestion.
AutofillMetrics::LogAutofillSuggestionAcceptedIndex(position);
@@ -271,14 +281,12 @@ void AutofillExternalDelegate::ClearPreviewedForm() {
driver_->RendererShouldClearPreviewedForm();
}
-void AutofillExternalDelegate::Reset() {
- manager_->client()->HideAutofillPopup();
+bool AutofillExternalDelegate::IsCreditCardPopup() {
+ return is_credit_card_popup_;
}
-void AutofillExternalDelegate::OnPingAck() {
- // Reissue the most recent query, which will reopen the Autofill popup.
- manager_->OnQueryFormFieldAutofill(query_id_, query_form_, query_field_,
- element_bounds_);
+void AutofillExternalDelegate::Reset() {
+ manager_->client()->HideAutofillPopup();
}
base::WeakPtr<AutofillExternalDelegate> AutofillExternalDelegate::GetWeakPtr() {
@@ -293,7 +301,7 @@ void AutofillExternalDelegate::OnCreditCardScanned(const CreditCard& card) {
void AutofillExternalDelegate::FillAutofillFormData(int unique_id,
bool is_preview) {
// If the selected element is a warning we don't want to do anything.
- if (unique_id == POPUP_ITEM_ID_WARNING_MESSAGE)
+ if (IsAutofillWarningEntry(unique_id))
return;
AutofillDriver::RendererFormDataAction renderer_action = is_preview ?
@@ -327,8 +335,6 @@ void AutofillExternalDelegate::ApplyAutofillOptions(
if (query_field_.is_autofilled) {
base::string16 value =
l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM);
- // TODO(rouslan): Remove manual upper-casing when keyboard accessory becomes
- // default on Android.
if (IsKeyboardAccessoryEnabled())
value = base::i18n::ToUpper(value);
@@ -336,12 +342,9 @@ void AutofillExternalDelegate::ApplyAutofillOptions(
suggestions->back().frontend_id = POPUP_ITEM_ID_CLEAR_FORM;
}
- // Append the 'Chrome Autofill options' menu item;
- // TODO(rouslan): Switch on the platform in the GRD file when keyboard
- // accessory becomes default on Android.
- suggestions->push_back(Suggestion(l10n_util::GetStringUTF16(
- IsKeyboardAccessoryEnabled() ? IDS_AUTOFILL_OPTIONS_CONTENT_DESCRIPTION
- : IDS_AUTOFILL_OPTIONS_POPUP)));
+ // Append the 'Chrome Autofill options' menu item, or the menu item specified
+ // in the popup layout experiment.
+ suggestions->push_back(Suggestion(GetSettingsSuggestionValue()));
suggestions->back().frontend_id = POPUP_ITEM_ID_AUTOFILL_OPTIONS;
if (IsKeyboardAccessoryEnabled())
suggestions->back().icon = base::ASCIIToUTF16("settings");
@@ -384,4 +387,14 @@ void AutofillExternalDelegate::InsertDataListValues(
}
}
+base::string16 AutofillExternalDelegate::GetSettingsSuggestionValue()
+ const {
+ if (IsKeyboardAccessoryEnabled()) {
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_CONTENT_DESCRIPTION);
+ }
+ return l10n_util::GetStringUTF16(is_credit_card_popup_ ?
+ IDS_AUTOFILL_CREDIT_CARD_OPTIONS_POPUP :
+ IDS_AUTOFILL_OPTIONS_POPUP);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.h b/chromium/components/autofill/core/browser/autofill_external_delegate.h
index 5431b31fe6c..6a9807a0505 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate.h
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate.h
@@ -51,6 +51,9 @@ class AutofillExternalDelegate : public AutofillPopupDelegate {
base::string16* body) override;
bool RemoveSuggestion(const base::string16& value, int identifier) override;
void ClearPreviewedForm() override;
+ // Returns false for all popups prior to |onQuery|, true for credit card
+ // popups after call to |onQuery|.
+ bool IsCreditCardPopup() override;
// Records and associates a query_id with web form data. Called
// when the renderer posts an Autofill query to the browser. |bounds|
@@ -81,9 +84,6 @@ class AutofillExternalDelegate : public AutofillPopupDelegate {
// values or settings.
void Reset();
- // The renderer sent an IPC acknowledging an earlier ping IPC.
- void OnPingAck();
-
protected:
base::WeakPtr<AutofillExternalDelegate> GetWeakPtr();
@@ -118,6 +118,9 @@ class AutofillExternalDelegate : public AutofillPopupDelegate {
// version.
void InsertDataListValues(std::vector<Suggestion>* suggestions);
+ // Returns the text (i.e. |Suggestion| value) for Chrome autofill options.
+ base::string16 GetSettingsSuggestionValue() const;
+
AutofillManager* manager_; // weak.
// Provides driver-level context to the shared code of the component. Must
@@ -142,8 +145,8 @@ class AutofillExternalDelegate : public AutofillPopupDelegate {
// currently editing? Used to keep track of state for metrics logging.
bool has_shown_popup_for_current_edit_;
- // FIXME
bool should_show_scan_credit_card_;
+ bool is_credit_card_popup_;
// Whether the credit card signin promo should be shown to the user.
bool should_show_cc_signin_promo_;
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index 709c73444dd..97bd64b385c 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -80,6 +80,8 @@ class MockAutofillClient : public TestAutofillClient {
MOCK_METHOD0(StartSigninFlow, void());
+ MOCK_METHOD0(ShowHttpNotSecureExplanation, void());
+
private:
DISALLOW_COPY_AND_ASSIGN(MockAutofillClient);
};
@@ -463,16 +465,15 @@ TEST_F(AutofillExternalDelegateUnitTest, AutofillWarnings) {
EXPECT_CALL(
autofill_client_,
ShowAutofillPopup(
- _,
- _,
- SuggestionVectorIdsAre(testing::ElementsAre(
- static_cast<int>(POPUP_ITEM_ID_WARNING_MESSAGE))),
+ _, _, SuggestionVectorIdsAre(testing::ElementsAre(static_cast<int>(
+ POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE))),
_));
// This should call ShowAutofillPopup.
std::vector<Suggestion> autofill_item;
autofill_item.push_back(Suggestion());
- autofill_item[0].frontend_id = POPUP_ITEM_ID_WARNING_MESSAGE;
+ autofill_item[0].frontend_id =
+ POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item);
}
@@ -493,9 +494,10 @@ TEST_F(AutofillExternalDelegateUnitTest,
// This should call ShowAutofillPopup.
std::vector<Suggestion> suggestions;
suggestions.push_back(Suggestion());
- suggestions[0].frontend_id = POPUP_ITEM_ID_WARNING_MESSAGE;
+ suggestions[0].frontend_id = POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE;
suggestions.push_back(Suggestion());
- suggestions[1].frontend_id = POPUP_ITEM_ID_WARNING_MESSAGE;
+ suggestions[1].frontend_id =
+ POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
suggestions.push_back(Suggestion());
suggestions[2].value = ASCIIToUTF16("Rick");
suggestions[2].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY;
@@ -671,6 +673,15 @@ TEST_F(AutofillExternalDelegateUnitTest, SigninPromoMenuItem) {
base::string16(), POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO, 0);
}
+// Test that autofill client will open the security indicator help center url
+// after the user accepted the http warning message suggestion item.
+TEST_F(AutofillExternalDelegateUnitTest, HttpWarningMessageItem) {
+ EXPECT_CALL(autofill_client_, ShowHttpNotSecureExplanation());
+ EXPECT_CALL(autofill_client_, HideAutofillPopup());
+ external_delegate_->DidAcceptSuggestion(
+ base::string16(), POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE, 0);
+}
+
MATCHER_P(CreditCardMatches, card, "") {
return !arg.Compare(card);
}
diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc
index 7923e0bc9d5..977e67ae874 100644
--- a/chromium/components/autofill/core/browser/autofill_manager.cc
+++ b/chromium/components/autofill/core/browser/autofill_manager.cc
@@ -50,6 +50,7 @@
#include "components/autofill/core/browser/phone_number.h"
#include "components/autofill/core/browser/phone_number_i18n.h"
#include "components/autofill/core/browser/popup_item_ids.h"
+#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_data_validation.h"
#include "components/autofill/core/common/autofill_pref_names.h"
@@ -60,7 +61,9 @@
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
+#include "components/security_state/core/security_state.h"
#include "google_apis/gaia/identity_provider.h"
#include "grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
@@ -183,6 +186,23 @@ bool IsCreditCardExpirationType(ServerFieldType type) {
type == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR;
}
+// Create http bad warning message at the top of autofill popup list showing
+// "Payment not secure" when users are on http sites or broken https sites.
+Suggestion CreateHttpWarningMessageSuggestionItem(const GURL& source_url) {
+ Suggestion cc_field_http_warning_suggestion(
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_CREDIT_CARD_HTTP_WARNING_MESSAGE));
+ cc_field_http_warning_suggestion.frontend_id =
+ POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE;
+ cc_field_http_warning_suggestion.label =
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_HTTP_WARNING_LEARN_MORE);
+ cc_field_http_warning_suggestion.icon =
+ (source_url.is_valid() && source_url.SchemeIs("http"))
+ ? base::ASCIIToUTF16("httpWarning")
+ : base::ASCIIToUTF16("httpsInvalid");
+
+ return cc_field_http_warning_suggestion;
+}
+
} // namespace
AutofillManager::AutofillManager(
@@ -290,6 +310,12 @@ bool AutofillManager::ShouldShowScanCreditCard(const FormData& form,
return field.value.size() <= kShowScanCreditCardMaxValueLength;
}
+bool AutofillManager::IsCreditCardPopup(const FormData& form,
+ const FormFieldData& field) {
+ AutofillField* autofill_field = GetAutofillField(form, field);
+ return autofill_field && autofill_field->Type().group() == CREDIT_CARD;
+}
+
bool AutofillManager::ShouldShowCreditCardSigninPromo(
const FormData& form,
const FormFieldData& field) {
@@ -510,8 +536,6 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id,
if (!IsValidFormData(form) || !IsValidFormFieldData(field))
return;
- std::vector<Suggestion> suggestions;
-
gfx::RectF transformed_box =
driver_->TransformBoundingBoxToViewportCoordinates(bounding_box);
external_delegate_->OnQuery(query_id, form, field, transformed_box);
@@ -526,9 +550,12 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id,
// Don't send suggestions or track forms that should not be parsed.
form_structure->ShouldBeParsed();
- // Logging interactions of forms that are autofillable.
+ bool is_filling_credit_card = false;
+
+ // Log interactions of forms that are autofillable.
if (got_autofillable_form) {
if (autofill_field->Type().group() == CREDIT_CARD) {
+ is_filling_credit_card = true;
driver_->DidInteractWithCreditCardForm();
credit_card_form_event_logger_->OnDidInteractWithAutofillableForm();
} else {
@@ -536,11 +563,18 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id,
}
}
+ std::vector<Suggestion> suggestions;
+ const bool is_context_secure =
+ !form_structure ||
+ (client_->IsContextSecure() &&
+ (!form_structure->target_url().is_valid() ||
+ !form_structure->target_url().SchemeIs("http")));
+ const bool is_http_warning_enabled =
+ security_state::IsHttpWarningInFormEnabled();
+
if (is_autofill_possible &&
driver_->RendererIsAvailable() &&
got_autofillable_form) {
- AutofillType type = autofill_field->Type();
- bool is_filling_credit_card = (type.group() == CREDIT_CARD);
// On desktop, don't return non credit card related suggestions for forms or
// fields that have the "autocomplete" attribute set to off.
if (IsDesktopPlatform() && !is_filling_credit_card &&
@@ -548,16 +582,13 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id,
return;
}
if (is_filling_credit_card) {
- suggestions = GetCreditCardSuggestions(field, type);
+ suggestions = GetCreditCardSuggestions(field, autofill_field->Type());
} else {
suggestions =
GetProfileSuggestions(*form_structure, field, *autofill_field);
}
+
if (!suggestions.empty()) {
- bool is_context_secure =
- client_->IsContextSecure(form_structure->source_url()) &&
- (!form_structure->target_url().is_valid() ||
- !form_structure->target_url().SchemeIs("http"));
if (is_filling_credit_card)
AutofillMetrics::LogIsQueriedCreditCardFormSecure(is_context_secure);
@@ -566,26 +597,18 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id,
// IsContextSecure).
if (is_filling_credit_card && !is_context_secure) {
// Replace the suggestion content with a warning message explaining why
- // Autofill is disabled for a website.
+ // Autofill is disabled for a website. The string is different if the
+ // credit card autofill HTTP warning experiment is enabled.
Suggestion warning_suggestion(l10n_util::GetStringUTF16(
- IDS_AUTOFILL_WARNING_INSECURE_CONNECTION));
- warning_suggestion.frontend_id = POPUP_ITEM_ID_WARNING_MESSAGE;
+ is_http_warning_enabled
+ ? IDS_AUTOFILL_WARNING_PAYMENT_DISABLED
+ : IDS_AUTOFILL_WARNING_INSECURE_CONNECTION));
+ warning_suggestion.frontend_id =
+ POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
suggestions.assign(1, warning_suggestion);
-
- // On top of the explanation message, first show a "Payment not secure"
- // message.
- if (IsCreditCardAutofillHttpWarningEnabled()) {
- Suggestion cc_field_http_warning_suggestion(l10n_util::GetStringUTF16(
- IDS_AUTOFILL_CREDIT_CARD_HTTP_WARNING_MESSAGE));
- cc_field_http_warning_suggestion.frontend_id =
- POPUP_ITEM_ID_WARNING_MESSAGE;
- suggestions.insert(suggestions.begin(),
- cc_field_http_warning_suggestion);
- }
} else {
- bool section_is_autofilled =
- SectionIsAutofilled(*form_structure, form,
- autofill_field->section());
+ bool section_is_autofilled = SectionIsAutofilled(
+ *form_structure, form, autofill_field->section());
if (section_is_autofilled) {
// If the relevant section is auto-filled and the renderer is querying
// for suggestions, then the user is editing the value of a field.
@@ -617,6 +640,21 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id,
}
}
+ // Show a "Payment not secure" message.
+ if (!is_context_secure && is_filling_credit_card && is_http_warning_enabled) {
+#if !defined(OS_ANDROID)
+ if (!suggestions.empty()) {
+ suggestions.insert(suggestions.begin(), Suggestion());
+ suggestions.front().frontend_id = POPUP_ITEM_ID_SEPARATOR;
+ }
+#endif
+
+ suggestions.insert(
+ suggestions.begin(),
+ CreateHttpWarningMessageSuggestionItem(
+ form_structure ? form_structure->source_url() : GURL::EmptyGURL()));
+ }
+
// If there are no Autofill suggestions, consider showing Autocomplete
// suggestions. We will not show Autocomplete suggestions for a field that
// specifies autocomplete=off (or an unrecognized type), a field for which we
@@ -948,7 +986,7 @@ void AutofillManager::OnLoadedServerPredictions(
// Parse and store the server predictions.
FormStructure::ParseQueryResponse(std::move(response), queried_forms,
- client_->GetRapporService());
+ client_->GetRapporServiceImpl());
// Will log quality metrics for each FormStructure based on the presence of
// autocomplete attributes, if available.
@@ -1091,7 +1129,7 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) {
recently_autofilled_forms_.push_back(
std::map<std::string, base::string16>());
auto& map = recently_autofilled_forms_.back();
- for (const auto* field : submitted_form) {
+ for (const auto& field : submitted_form) {
AutofillType type = field->Type();
// Even though this is for development only, mask full credit card #'s.
if (type.GetStorableType() == CREDIT_CARD_NUMBER &&
@@ -1131,10 +1169,10 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) {
// their card. If no CVC is present, do nothing. We could fall back to a
// local save but we believe that sometimes offering upload and sometimes
// offering local save is a confusing user experience.
- int cvc;
- for (const AutofillField* field : submitted_form) {
+ for (const auto& field : submitted_form) {
if (field->Type().GetStorableType() == CREDIT_CARD_VERIFICATION_CODE &&
- base::StringToInt(field->value, &cvc)) {
+ IsValidCreditCardSecurityCode(field->value,
+ upload_request_.card.type())) {
upload_request_.cvc = field->value;
break;
}
@@ -1268,8 +1306,8 @@ bool AutofillManager::GetProfilesForCreditCardUpload(
void AutofillManager::CollectRapportSample(const GURL& source_url,
const char* metric_name) const {
- if (source_url.is_valid() && client_->GetRapporService()) {
- rappor::SampleDomainAndRegistryFromGURL(client_->GetRapporService(),
+ if (source_url.is_valid() && client_->GetRapporServiceImpl()) {
+ rappor::SampleDomainAndRegistryFromGURL(client_->GetRapporServiceImpl(),
metric_name, source_url);
}
}
@@ -1284,9 +1322,10 @@ void AutofillManager::UploadFormDataAsyncCallback(
const TimeTicks& interaction_time,
const TimeTicks& submission_time,
bool observed_submission) {
- submitted_form->LogQualityMetrics(
- load_time, interaction_time, submission_time, client_->GetRapporService(),
- did_show_suggestions_, observed_submission);
+ submitted_form->LogQualityMetrics(load_time, interaction_time,
+ submission_time,
+ client_->GetRapporServiceImpl(),
+ did_show_suggestions_, observed_submission);
if (submitted_form->ShouldBeCrowdsourced())
UploadFormData(*submitted_form, observed_submission);
@@ -1400,6 +1439,8 @@ bool AutofillManager::RefreshDataModels() {
is_server_data_available);
credit_card_form_event_logger_->set_is_local_data_available(
is_local_data_available);
+ credit_card_form_event_logger_->set_is_context_secure(
+ client_->IsContextSecure());
}
{
bool is_server_data_available = false;
@@ -1647,9 +1688,9 @@ bool AutofillManager::GetCachedFormAndField(const FormData& form,
// Find the AutofillField that corresponds to |field|.
*autofill_field = NULL;
- for (AutofillField* current : **form_structure) {
+ for (const auto& current : **form_structure) {
if (current->SameFieldAs(field)) {
- *autofill_field = current;
+ *autofill_field = current.get();
break;
}
}
@@ -1769,6 +1810,7 @@ std::vector<Suggestion> AutofillManager::GetCreditCardSuggestions(
for (size_t i = 0; i < suggestions.size(); i++) {
suggestions[i].frontend_id =
MakeFrontendID(suggestions[i].backend_id, std::string());
+ suggestions[i].is_value_bold = IsCreditCardPopupValueBold();
}
return suggestions;
}
diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h
index f3d3e2f762e..ce8608f03db 100644
--- a/chromium/components/autofill/core/browser/autofill_manager.h
+++ b/chromium/components/autofill/core/browser/autofill_manager.h
@@ -44,7 +44,6 @@
#endif
namespace gfx {
-class Rect;
class RectF;
}
@@ -97,6 +96,10 @@ class AutofillManager : public AutofillDownloadManager::Observer,
virtual bool ShouldShowScanCreditCard(const FormData& form,
const FormFieldData& field);
+ // Whether the |field| belongs to CREDIT_CARD |FieldTypeGroup|.
+ virtual bool IsCreditCardPopup(const FormData& form,
+ const FormFieldData& field);
+
// Whether we should show the signin promo, based on the triggered |field|
// inside the |form|.
virtual bool ShouldShowCreditCardSigninPromo(const FormData& form,
diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
index eea68f0a4b9..38264eda18c 100644
--- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -49,7 +49,7 @@
#include "components/autofill/core/common/form_field_data.h"
#include "components/prefs/pref_service.h"
#include "components/rappor/test_rappor_service.h"
-#include "components/security_state/core/switches.h"
+#include "components/security_state/core/security_state.h"
#include "components/variations/variations_associated_data.h"
#include "grit/components_strings.h"
#include "net/url_request/url_request_test_util.h"
@@ -72,6 +72,13 @@ namespace {
const int kDefaultPageID = 137;
+const std::string kUTF8MidlineEllipsis =
+ " "
+ "\xE2\x80\xA2\xE2\x80\x86"
+ "\xE2\x80\xA2\xE2\x80\x86"
+ "\xE2\x80\xA2\xE2\x80\x86"
+ "\xE2\x80\xA2\xE2\x80\x86";
+
class MockAutofillClient : public TestAutofillClient {
public:
MockAutofillClient() {}
@@ -688,7 +695,7 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate {
EXPECT_TRUE(on_suggestions_returned_seen_);
EXPECT_EQ(expected_page_id, query_id_);
- ASSERT_EQ(expected_num_suggestions, suggestions_.size());
+ ASSERT_LE(expected_num_suggestions, suggestions_.size());
for (size_t i = 0; i < expected_num_suggestions; ++i) {
SCOPED_TRACE(base::StringPrintf("i: %" PRIuS, i));
EXPECT_EQ(expected_suggestions[i].value, suggestions_[i].value);
@@ -697,6 +704,7 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate {
EXPECT_EQ(expected_suggestions[i].frontend_id,
suggestions_[i].frontend_id);
}
+ ASSERT_EQ(expected_num_suggestions, suggestions_.size());
}
// Wrappers around the above GetSuggestions call that take a hardcoded number
@@ -1012,10 +1020,8 @@ class AutofillManagerTest : public testing::Test {
}
void SetHttpWarningEnabled() {
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- security_state::switches::kMarkHttpAs,
- security_state::switches::
- kMarkHttpWithPasswordsOrCcWithChipAndFormWarning);
+ scoped_feature_list_.InitAndEnableFeature(
+ security_state::kHttpFormWarningFeature);
}
protected:
@@ -1265,6 +1271,24 @@ TEST_F(AutofillManagerTest, GetProfileSuggestions_EmptyValue) {
Suggestion("Elvis", "3734 Elvis Presley Blvd.", "", 2));
}
+// Test that the HttpWarning does not appear on non-payment forms.
+TEST_F(AutofillManagerTest, GetProfileSuggestions_EmptyValueNotSecure) {
+ SetHttpWarningEnabled();
+ // Set up our form data.
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // Test that we sent the right values to the external delegate.
+ external_delegate_->CheckSuggestions(
+ kDefaultPageID, Suggestion("Charles", "123 Apple St.", "", 1),
+ Suggestion("Elvis", "3734 Elvis Presley Blvd.", "", 2));
+}
+
// Test that we return only matching address profile suggestions when the
// selected form field has been partially filled out.
TEST_F(AutofillManagerTest, GetProfileSuggestions_MatchCharacter) {
@@ -1439,14 +1463,11 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_EmptyValue) {
// Test that we sent the right values to the external delegate.
external_delegate_->CheckSuggestions(
- kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
- "3456",
+ kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
"04/99", kVisaCard,
autofill_manager_->GetPackedCreditCardID(4)),
- Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "8765",
- "10/98", kMasterCard,
- autofill_manager_->GetPackedCreditCardID(5)));
+ Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+ kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
}
// Test that we return all credit card profile suggestions when the triggering
@@ -1464,14 +1485,11 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_Whitespace) {
// Test that we sent the right values to the external delegate.
external_delegate_->CheckSuggestions(
- kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
- "3456",
+ kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
"04/99", kVisaCard,
autofill_manager_->GetPackedCreditCardID(4)),
- Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "8765",
- "10/98", kMasterCard,
- autofill_manager_->GetPackedCreditCardID(5)));
+ Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+ kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
}
// Test that we return all credit card profile suggestions when the triggering
@@ -1489,14 +1507,11 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_StopCharsOnly) {
// Test that we sent the right values to the external delegate.
external_delegate_->CheckSuggestions(
- kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
- "3456",
+ kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
"04/99", kVisaCard,
autofill_manager_->GetPackedCreditCardID(4)),
- Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "8765",
- "10/98", kMasterCard,
- autofill_manager_->GetPackedCreditCardID(5)));
+ Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+ kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
}
// Test that we return all credit card profile suggestions when the triggering
@@ -1523,8 +1538,7 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_StopCharsWithInput) {
// Test that we sent the right value to the external delegate.
external_delegate_->CheckSuggestions(
- kDefaultPageID, Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "3123",
+ kDefaultPageID, Suggestion("MasterCard" + kUTF8MidlineEllipsis + "3123",
"08/17", kMasterCard,
autofill_manager_->GetPackedCreditCardID(7)));
}
@@ -1544,8 +1558,7 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_MatchCharacter) {
// Test that we sent the right values to the external delegate.
external_delegate_->CheckSuggestions(
- kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
- "3456",
+ kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
"04/99", kVisaCard,
autofill_manager_->GetPackedCreditCardID(4)));
}
@@ -1563,15 +1576,13 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_NonCCNumber) {
GetAutofillSuggestions(form, field);
#if defined(OS_ANDROID)
- static const char* kVisaSuggestion =
- "Visa\xC2\xA0\xE2\x8B\xAF"
- "3456";
- static const char* kMcSuggestion =
- "MasterCard\xC2\xA0\xE2\x8B\xAF"
- "8765";
+ static const std::string kVisaSuggestion =
+ "Visa" + kUTF8MidlineEllipsis + "3456";
+ static const std::string kMcSuggestion =
+ "MasterCard" + kUTF8MidlineEllipsis + "8765";
#else
- static const char* kVisaSuggestion = "*3456";
- static const char* kMcSuggestion = "*8765";
+ static const std::string kVisaSuggestion = "*3456";
+ static const std::string kMcSuggestion = "*8765";
#endif
// Test that we sent the right values to the external delegate.
@@ -1629,16 +1640,26 @@ TEST_F(AutofillManagerTest,
kDefaultPageID,
Suggestion(l10n_util::GetStringUTF8(
IDS_AUTOFILL_CREDIT_CARD_HTTP_WARNING_MESSAGE),
- "", "", -1),
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_HTTP_WARNING_LEARN_MORE),
+ "httpWarning", POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE),
+#if !defined(OS_ANDROID)
+ Suggestion("", "", "", POPUP_ITEM_ID_SEPARATOR),
+#endif
Suggestion(
- l10n_util::GetStringUTF8(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION),
- "", "", -1));
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_WARNING_PAYMENT_DISABLED), "",
+ "", POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE));
- // Clear the test credit cards and try again -- we shouldn't return a warning.
+ // Clear the test credit cards and try again -- we should still show the
+ // warning.
personal_data_.ClearCreditCards();
GetAutofillSuggestions(form, field);
- // Autocomplete suggestions are queried, but not Autofill.
- EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen());
+ // Test that we sent the right values to the external delegate.
+ external_delegate_->CheckSuggestions(
+ kDefaultPageID,
+ Suggestion(l10n_util::GetStringUTF8(
+ IDS_AUTOFILL_CREDIT_CARD_HTTP_WARNING_MESSAGE),
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_HTTP_WARNING_LEARN_MORE),
+ "httpWarning", POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE));
}
// Test that we don't show the extra "Payment not secure" warning when the page
@@ -1658,14 +1679,11 @@ TEST_F(AutofillManagerTest,
// Test that we sent the right values to the external delegate.
external_delegate_->CheckSuggestions(
- kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
- "3456",
+ kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
"04/99", kVisaCard,
autofill_manager_->GetPackedCreditCardID(4)),
- Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "8765",
- "10/98", kMasterCard,
- autofill_manager_->GetPackedCreditCardID(5)));
+ Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+ kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
}
// Test that we will eventually return the credit card signin promo when there
@@ -1749,14 +1767,11 @@ TEST_F(AutofillManagerTest,
// Test that we sent the right values to the external delegate.
external_delegate_->CheckSuggestions(
- kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
- "3456",
+ kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
"04/99", kVisaCard,
autofill_manager_->GetPackedCreditCardID(4)),
- Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "8765",
- "10/98", kMasterCard,
- autofill_manager_->GetPackedCreditCardID(5)));
+ Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+ kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
}
// Test that we return credit card suggestions for secure pages that have a
@@ -1776,14 +1791,11 @@ TEST_F(AutofillManagerTest,
// Test that we sent the right values to the external delegate.
external_delegate_->CheckSuggestions(
- kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
- "3456",
+ kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
"04/99", kVisaCard,
autofill_manager_->GetPackedCreditCardID(4)),
- Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "8765",
- "10/98", kMasterCard,
- autofill_manager_->GetPackedCreditCardID(5)));
+ Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+ kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
}
// Test that we return all credit card suggestions in the case that two cards
@@ -1810,18 +1822,13 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_RepeatedObfuscatedNumber) {
// Test that we sent the right values to the external delegate.
external_delegate_->CheckSuggestions(
- kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
- "3456",
+ kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
"04/99", kVisaCard,
autofill_manager_->GetPackedCreditCardID(4)),
- Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "8765",
- "10/98", kMasterCard,
- autofill_manager_->GetPackedCreditCardID(5)),
- Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "3456",
- "05/99", kMasterCard,
- autofill_manager_->GetPackedCreditCardID(7)));
+ Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+ kMasterCard, autofill_manager_->GetPackedCreditCardID(5)),
+ Suggestion("MasterCard" + kUTF8MidlineEllipsis + "3456", "05/99",
+ kMasterCard, autofill_manager_->GetPackedCreditCardID(7)));
}
// Test that we return profile and credit card suggestions for combined forms.
@@ -1847,14 +1854,11 @@ TEST_F(AutofillManagerTest, GetAddressAndCreditCardSuggestions) {
// Test that we sent the credit card suggestions to the external delegate.
external_delegate_->CheckSuggestions(
- kPageID2, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
- "3456",
+ kPageID2, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
"04/99", kVisaCard,
autofill_manager_->GetPackedCreditCardID(4)),
- Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "8765",
- "10/98", kMasterCard,
- autofill_manager_->GetPackedCreditCardID(5)));
+ Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98",
+ kMasterCard, autofill_manager_->GetPackedCreditCardID(5)));
}
// Test that for non-https forms with both address and credit card fields, we
@@ -3667,17 +3671,38 @@ TEST_F(AutofillManagerTest, FormSubmittedWithDefaultValues) {
}
// Tests that credit card data are saved for forms on https
-TEST_F(AutofillManagerTest, ImportFormDataCreditCardHTTPS) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_ImportFormDataCreditCardHTTPS \
+ DISABLED_ImportFormDataCreditCardHTTPS
+#else
+#define MAYBE_ImportFormDataCreditCardHTTPS ImportFormDataCreditCardHTTPS
+#endif
+TEST_F(AutofillManagerTest, MAYBE_ImportFormDataCreditCardHTTPS) {
TestSaveCreditCards(true);
}
// Tests that credit card data are saved for forms on http
-TEST_F(AutofillManagerTest, ImportFormDataCreditCardHTTP) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_ImportFormDataCreditCardHTTP DISABLED_ImportFormDataCreditCardHTTP
+#else
+#define MAYBE_ImportFormDataCreditCardHTTP ImportFormDataCreditCardHTTP
+#endif
+TEST_F(AutofillManagerTest, MAYBE_ImportFormDataCreditCardHTTP) {
TestSaveCreditCards(false);
}
// Tests that credit card data are saved when autocomplete=off for CC field.
-TEST_F(AutofillManagerTest, CreditCardSavedWhenAutocompleteOff) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_CreditCardSavedWhenAutocompleteOff \
+ DISABLED_CreditCardSavedWhenAutocompleteOff
+#else
+#define MAYBE_CreditCardSavedWhenAutocompleteOff \
+ CreditCardSavedWhenAutocompleteOff
+#endif
+TEST_F(AutofillManagerTest, MAYBE_CreditCardSavedWhenAutocompleteOff) {
// Set up our form data.
FormData form;
CreateTestCreditCardFormData(&form, false, false);
@@ -4376,8 +4401,7 @@ TEST_F(AutofillManagerTest,
GetAutofillSuggestions(form, number_field);
external_delegate_->CheckSuggestions(
- kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF"
- "3456",
+ kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456",
"04/99", kVisaCard,
autofill_manager_->GetPackedCreditCardID(4)));
}
@@ -4466,7 +4490,13 @@ TEST_F(AutofillManagerTest, FillInUpdatedExpirationDate) {
"4012888888881881");
}
-TEST_F(AutofillManagerTest, UploadCreditCard) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard DISABLED_UploadCreditCard
+#else
+#define MAYBE_UploadCreditCard UploadCreditCard
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(true);
@@ -4500,7 +4530,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard) {
AutofillMetrics::UPLOAD_OFFERED, 1);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_FeatureNotEnabled) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_FeatureNotEnabled DISABLED_UploadCreditCard_FeatureNotEnabled
+#else
+#define MAYBE_UploadCreditCard_FeatureNotEnabled UploadCreditCard_FeatureNotEnabled
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_FeatureNotEnabled) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(false);
@@ -4535,7 +4571,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_FeatureNotEnabled) {
histogram_tester.ExpectTotalCount("Autofill.CardUploadDecisionExpanded", 0);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_CvcUnavailable) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_CvcUnavailable DISABLED_UploadCreditCard_CvcUnavailable
+#else
+#define MAYBE_UploadCreditCard_CvcUnavailable UploadCreditCard_CvcUnavailable
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcUnavailable) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(true);
@@ -4571,7 +4613,60 @@ TEST_F(AutofillManagerTest, UploadCreditCard_CvcUnavailable) {
"Autofill.CardUploadDecisionExpanded",
AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1);
- rappor::TestRapporService* rappor_service =
+ rappor::TestRapporServiceImpl* rappor_service =
+ autofill_client_.test_rappor_service();
+ EXPECT_EQ(1, rappor_service->GetReportsCount());
+ std::string sample;
+ rappor::RapporType type;
+ EXPECT_TRUE(rappor_service->GetRecordedSampleForMetric(
+ "Autofill.CardUploadNotOfferedNoCvc", &sample, &type));
+ EXPECT_EQ("myform.com", sample);
+ EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type);
+}
+
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_CvcInvalidLength DISABLED_UploadCreditCard_CvcInvalidLength
+#else
+#define MAYBE_UploadCreditCard_CvcInvalidLength UploadCreditCard_CvcInvalidLength
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcInvalidLength) {
+ personal_data_.ClearAutofillProfiles();
+ autofill_manager_->set_credit_card_upload_enabled(true);
+
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
+ FormData address_form;
+ test::CreateTestAddressFormData(&address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
+ FormSubmitted(address_form);
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+ credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[3].value = ASCIIToUTF16("2017");
+ credit_card_form.fields[4].value = ASCIIToUTF16("1234");
+
+ base::HistogramTester histogram_tester;
+
+ // Neither a local save nor an upload should happen in this case.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(autofill_manager_->credit_card_was_uploaded());
+
+ // Verify that the correct histogram entry (and only that) was logged.
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.CardUploadDecisionExpanded",
+ AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1);
+
+ rappor::TestRapporServiceImpl* rappor_service =
autofill_client_.test_rappor_service();
EXPECT_EQ(1, rappor_service->GetReportsCount());
std::string sample;
@@ -4582,7 +4677,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_CvcUnavailable) {
EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_MultipleCvcFields) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_MultipleCvcFields DISABLED_UploadCreditCard_MultipleCvcFields
+#else
+#define MAYBE_UploadCreditCard_MultipleCvcFields UploadCreditCard_MultipleCvcFields
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_MultipleCvcFields) {
autofill_manager_->set_credit_card_upload_enabled(true);
// Remove the profiles that were created in the TestPersonalDataManager
@@ -4641,7 +4742,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_MultipleCvcFields) {
AutofillMetrics::UPLOAD_OFFERED, 1);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_NoProfileAvailable) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_NoProfileAvailable DISABLED_UploadCreditCard_NoProfileAvailable
+#else
+#define MAYBE_UploadCreditCard_NoProfileAvailable UploadCreditCard_NoProfileAvailable
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoProfileAvailable) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(true);
@@ -4671,7 +4778,7 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NoProfileAvailable) {
"Autofill.CardUploadDecisionExpanded",
AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS, 1);
- rappor::TestRapporService* rappor_service =
+ rappor::TestRapporServiceImpl* rappor_service =
autofill_client_.test_rappor_service();
EXPECT_EQ(1, rappor_service->GetReportsCount());
std::string sample;
@@ -4682,7 +4789,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NoProfileAvailable) {
EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_NoNameAvailable) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_NoNameAvailable DISABLED_UploadCreditCard_NoNameAvailable
+#else
+#define MAYBE_UploadCreditCard_NoNameAvailable UploadCreditCard_NoNameAvailable
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoNameAvailable) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(true);
@@ -4718,7 +4831,7 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NoNameAvailable) {
"Autofill.CardUploadDecisionExpanded",
AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME, 1);
- rappor::TestRapporService* rappor_service =
+ rappor::TestRapporServiceImpl* rappor_service =
autofill_client_.test_rappor_service();
EXPECT_EQ(1, rappor_service->GetReportsCount());
std::string sample;
@@ -4729,7 +4842,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NoNameAvailable) {
EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_ZipCodesConflict) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_ZipCodesConflict DISABLED_UploadCreditCard_ZipCodesConflict
+#else
+#define MAYBE_UploadCreditCard_ZipCodesConflict UploadCreditCard_ZipCodesConflict
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesConflict) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(true);
@@ -4774,7 +4893,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_ZipCodesConflict) {
AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS, 1);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_ZipCodesHavePrefixMatch) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch DISABLED_UploadCreditCard_ZipCodesHavePrefixMatch
+#else
+#define MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch UploadCreditCard_ZipCodesHavePrefixMatch
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(true);
@@ -4819,7 +4944,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_ZipCodesHavePrefixMatch) {
AutofillMetrics::UPLOAD_OFFERED, 1);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_NoZipCodeAvailable) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_NoZipCodeAvailable DISABLED_UploadCreditCard_NoZipCodeAvailable
+#else
+#define MAYBE_UploadCreditCard_NoZipCodeAvailable UploadCreditCard_NoZipCodeAvailable
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoZipCodeAvailable) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(true);
@@ -4863,7 +4994,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NoZipCodeAvailable) {
AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE, 1);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_NamesMatchLoosely) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_NamesMatchLoosely DISABLED_UploadCreditCard_NamesMatchLoosely
+#else
+#define MAYBE_UploadCreditCard_NamesMatchLoosely UploadCreditCard_NamesMatchLoosely
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesMatchLoosely) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(true);
@@ -4911,7 +5048,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NamesMatchLoosely) {
AutofillMetrics::UPLOAD_OFFERED, 1);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_NamesHaveToMatch) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_NamesHaveToMatch DISABLED_UploadCreditCard_NamesHaveToMatch
+#else
+#define MAYBE_UploadCreditCard_NamesHaveToMatch UploadCreditCard_NamesHaveToMatch
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesHaveToMatch) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(true);
@@ -4955,7 +5098,7 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NamesHaveToMatch) {
"Autofill.CardUploadDecisionExpanded",
AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES, 1);
- rappor::TestRapporService* rappor_service =
+ rappor::TestRapporServiceImpl* rappor_service =
autofill_client_.test_rappor_service();
EXPECT_EQ(1, rappor_service->GetReportsCount());
std::string sample;
@@ -4966,7 +5109,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NamesHaveToMatch) {
EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type);
}
-TEST_F(AutofillManagerTest, UploadCreditCard_UploadDetailsFails) {
+// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_UploadCreditCard_UploadDetailsFails DISABLED_UploadCreditCard_UploadDetailsFails
+#else
+#define MAYBE_UploadCreditCard_UploadDetailsFails UploadCreditCard_UploadDetailsFails
+#endif
+TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_UploadDetailsFails) {
personal_data_.ClearAutofillProfiles();
autofill_manager_->set_credit_card_upload_enabled(true);
@@ -5089,11 +5238,10 @@ TEST_F(AutofillManagerTest, DisplayCreditCardSuggestionsWithMatchingTokens) {
GetAutofillSuggestions(form, field);
#if defined(OS_ANDROID)
- static const char* kVisaSuggestion =
- "Visa\xC2\xA0\xE2\x8B\xAF"
- "3456";
+ static const std::string kVisaSuggestion =
+ "Visa" + kUTF8MidlineEllipsis + "3456";
#else
- static const char* kVisaSuggestion = "*3456";
+ static const std::string kVisaSuggestion = "*3456";
#endif
external_delegate_->CheckSuggestions(
diff --git a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc
index f38ffaf95f2..a4aceaab132 100644
--- a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc
@@ -8,7 +8,6 @@
#include <memory>
#include <vector>
-#include "base/feature_list.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/macros.h"
@@ -17,7 +16,6 @@
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/autofill_type.h"
@@ -186,7 +184,6 @@ class AutofillMergeTest : public DataDrivenTest,
PersonalDataManagerMock personal_data_;
private:
- base::test::ScopedFeatureList scoped_feature_list_;
std::map<std::string, ServerFieldType> string_to_field_type_map_;
DISALLOW_COPY_AND_ASSIGN(AutofillMergeTest);
@@ -205,7 +202,6 @@ AutofillMergeTest::~AutofillMergeTest() {
void AutofillMergeTest::SetUp() {
test::DisableSystemServices(nullptr);
- scoped_feature_list_.InitAndEnableFeature(kAutofillProfileCleanup);
}
void AutofillMergeTest::TearDown() {
diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc
index e655f7fe66f..66a7bfcc967 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics.cc
+++ b/chromium/components/autofill/core/browser/autofill_metrics.cc
@@ -675,10 +675,17 @@ void AutofillMetrics::LogIsQueriedCreditCardFormSecure(bool is_secure) {
UMA_HISTOGRAM_BOOLEAN("Autofill.QueriedCreditCardFormIsSecure", is_secure);
}
+// static
+void AutofillMetrics::LogShowedHttpNotSecureExplanation() {
+ base::RecordAction(
+ base::UserMetricsAction("Autofill_ShowedHttpNotSecureExplanation"));
+}
+
AutofillMetrics::FormEventLogger::FormEventLogger(bool is_for_credit_card)
: is_for_credit_card_(is_for_credit_card),
is_server_data_available_(false),
is_local_data_available_(false),
+ is_context_secure_(false),
has_logged_interacted_(false),
has_logged_suggestions_shown_(false),
has_logged_masked_server_card_suggestion_selected_(false),
@@ -686,8 +693,7 @@ AutofillMetrics::FormEventLogger::FormEventLogger(bool is_for_credit_card)
has_logged_will_submit_(false),
has_logged_submitted_(false),
logged_suggestion_filled_was_server_data_(false),
- logged_suggestion_filled_was_masked_server_card_(false) {
-}
+ logged_suggestion_filled_was_masked_server_card_(false) {}
void AutofillMetrics::FormEventLogger::OnDidInteractWithAutofillableForm() {
if (!has_logged_interacted_) {
@@ -814,6 +820,10 @@ void AutofillMetrics::FormEventLogger::OnWillSubmitForm() {
Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE);
}
+ if (has_logged_suggestions_shown_) {
+ Log(AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE);
+ }
+
base::RecordAction(base::UserMetricsAction("Autofill_OnWillSubmitForm"));
}
@@ -837,6 +847,10 @@ void AutofillMetrics::FormEventLogger::OnFormSubmitted() {
} else {
Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE);
}
+
+ if (has_logged_suggestions_shown_) {
+ Log(AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE);
+ }
}
void AutofillMetrics::FormEventLogger::Log(FormEvent event) const {
@@ -848,6 +862,14 @@ void AutofillMetrics::FormEventLogger::Log(FormEvent event) const {
name += "Address";
LogUMAHistogramEnumeration(name, event, NUM_FORM_EVENTS);
+ // Log again in a different histogram for credit card forms on nonsecure
+ // pages, so that form interactions on nonsecure pages can be analyzed on
+ // their own.
+ if (is_for_credit_card_ && !is_context_secure_) {
+ LogUMAHistogramEnumeration(name + ".OnNonsecurePage", event,
+ NUM_FORM_EVENTS);
+ }
+
// Logging again in a different histogram for segmentation purposes.
// TODO(waltercacau): Re-evaluate if we still need such fine grained
// segmentation. http://crbug.com/454018
diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h
index b8e569baf69..6fe8a34ebc3 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics.h
+++ b/chromium/components/autofill/core/browser/autofill_metrics.h
@@ -383,6 +383,13 @@ class AutofillMetrics {
FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE,
FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE,
FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+ // A dropdown with suggestions was shown and a form was submitted after
+ // that.
+ FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE,
+ // A dropdown with suggestions was shown and a form is about to be
+ // 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,
};
@@ -656,6 +663,10 @@ class AutofillMetrics {
// context.
static void LogIsQueriedCreditCardFormSecure(bool is_secure);
+ // This should be called when the user selects the Form-Not-Secure warning
+ // suggestion to show an explanation of the warning.
+ static void LogShowedHttpNotSecureExplanation();
+
// Utility to autofill form events in the relevant histograms depending on
// the presence of server and/or local data.
class FormEventLogger {
@@ -670,6 +681,10 @@ class AutofillMetrics {
is_local_data_available_ = is_local_data_available;
}
+ inline void set_is_context_secure(bool is_context_secure) {
+ is_context_secure_ = is_context_secure;
+ }
+
void OnDidInteractWithAutofillableForm();
void OnDidPollSuggestions(const FormFieldData& field);
@@ -694,6 +709,7 @@ class AutofillMetrics {
bool is_for_credit_card_;
bool is_server_data_available_;
bool is_local_data_available_;
+ bool is_context_secure_;
bool has_logged_interacted_;
bool has_logged_suggestions_shown_;
bool has_logged_masked_server_card_suggestion_selected_;
diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
index 681b131a408..f9799f5e8af 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -22,6 +22,7 @@
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/payments/payments_client.h"
#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/popup_item_ids.h"
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/browser/test_autofill_driver.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
@@ -42,7 +43,7 @@
using base::ASCIIToUTF16;
using base::Bucket;
using base::TimeTicks;
-using rappor::TestRapporService;
+using rappor::TestRapporServiceImpl;
using ::testing::ElementsAre;
namespace autofill {
@@ -1885,6 +1886,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
+ autofill_client_.set_form_origin(form.origin);
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -1918,6 +1920,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) {
autofill_manager_->Reset();
form.origin = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
+ autofill_client_.set_form_origin(form.origin);
autofill_manager_->AddSeenForm(form, field_types, field_types);
// Simulate an Autofill query on a credit card field (HTTPS form).
@@ -2413,6 +2416,24 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
+ // Simulating submission with suggestion shown.
+ base::HistogramTester histogram_tester;
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->SubmitForm(form, TimeTicks::Now());
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1);
+ }
+
+ // Reset the autofill manager state.
+ autofill_manager_->Reset();
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ {
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
@@ -2496,6 +2517,9 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
@@ -2510,6 +2534,9 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
@@ -2526,27 +2553,45 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
- // Simulating submission without previous interaction.
+ // Simulating submission with suggestion shown but without previous
+ // interaction.
base::HistogramTester histogram_tester;
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
autofill_manager_->SubmitForm(form, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
+ AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+ AutofillMetrics::
+ FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+ 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::
+ FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+ 0);
}
}
@@ -2600,6 +2645,24 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
+ // Simulating submission with suggestion shown.
+ base::HistogramTester histogram_tester;
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
+ }
+
+ // Reset the autofill manager state.
+ autofill_manager_->Reset();
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ {
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
@@ -2683,6 +2746,9 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
@@ -2697,6 +2763,9 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
@@ -2713,27 +2782,45 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
- // Simulating submission without previous interaction.
+ // Simulating submission with suggestion shown but without previous
+ // interaction.
base::HistogramTester histogram_tester;
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
+ AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+ AutofillMetrics::
+ FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+ 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
- AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::
+ FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+ 0);
}
}
@@ -2991,6 +3078,24 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
+ // Simulating submission with suggestion shown.
+ base::HistogramTester histogram_tester;
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->SubmitForm(form, TimeTicks::Now());
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1);
+ }
+
+ // Reset the autofill manager state.
+ autofill_manager_->Reset();
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ {
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
@@ -3043,6 +3148,9 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
@@ -3052,6 +3160,9 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
@@ -3063,11 +3174,16 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
- // Simulating submission without previous interaction.
+ // Simulating submission with suggestion show but without previous
+ // interaction.
base::HistogramTester histogram_tester;
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
autofill_manager_->SubmitForm(form, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
@@ -3077,6 +3193,14 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
+ AutofillMetrics::
+ FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+ 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
@@ -3084,6 +3208,11 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::
+ FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+ 0);
}
}
@@ -3135,6 +3264,24 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
+ // Simulating submission with suggestion shown.
+ base::HistogramTester histogram_tester;
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
+ }
+
+ // Reset the autofill manager state.
+ autofill_manager_->Reset();
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ {
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
@@ -3184,9 +3331,26 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::
+ FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+ 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
@@ -3194,6 +3358,11 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::
+ FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+ 0);
}
// Reset the autofill manager state.
@@ -3201,11 +3370,16 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
- // Simulating submission without previous interaction.
+ // Simulating submission with suggestion shown but without previous
+ // interaction.
base::HistogramTester histogram_tester;
+ autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
autofill_manager_->WillSubmitForm(form, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
@@ -3215,6 +3389,14 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
+ AutofillMetrics::
+ FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE,
+ 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
@@ -3222,6 +3404,11 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.FormEvents.Address",
+ AutofillMetrics::
+ FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+ 0);
}
}
@@ -4035,7 +4222,7 @@ class AutofillMetricsParseQueryResponseTest : public testing::Test {
}
protected:
- TestRapporService rappor_service_;
+ TestRapporServiceImpl rappor_service_;
std::vector<std::unique_ptr<FormStructure>> owned_forms_;
std::vector<FormStructure*> forms_;
};
@@ -4139,4 +4326,125 @@ TEST_F(AutofillMetricsParseQueryResponseTest, PartialNoServerData) {
EXPECT_EQ(0, rappor_service_.GetReportsCount());
}
+// Test that the Form-Not-Secure warning user action is recorded.
+TEST_F(AutofillMetricsTest, ShowHttpNotSecureExplanationUserAction) {
+ base::UserActionTester user_action_tester;
+ external_delegate_->DidAcceptSuggestion(
+ ASCIIToUTF16("Test"), POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE, 0);
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "Autofill_ShowedHttpNotSecureExplanation"));
+}
+
+// Tests that credit card form submissions are logged specially when the form is
+// on a non-secure page.
+TEST_F(AutofillMetricsTest, NonsecureCreditCardForm) {
+ personal_data_->RecreateCreditCards(
+ true /* include_local_credit_card */,
+ false /* include_masked_server_credit_card */,
+ false /* include_full_server_credit_card */);
+
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+ autofill_client_.set_form_origin(form.origin);
+
+ FormFieldData field;
+ std::vector<ServerFieldType> field_types;
+ test::CreateTestFormField("Name on card", "cc-name", "", "text", &field);
+ form.fields.push_back(field);
+ field_types.push_back(CREDIT_CARD_NAME_FULL);
+ test::CreateTestFormField("Credit card", "card", "", "text", &field);
+ form.fields.push_back(field);
+ field_types.push_back(CREDIT_CARD_NUMBER);
+ test::CreateTestFormField("Month", "card_month", "", "text", &field);
+ form.fields.push_back(field);
+ field_types.push_back(CREDIT_CARD_EXP_MONTH);
+
+ // Simulate having seen this form on page load.
+ // |form_structure| will be owned by |autofill_manager_|.
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ // Simulate an Autofill query on a credit card field.
+ {
+ base::UserActionTester user_action_tester;
+ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "Autofill_PolledCreditCardSuggestions"));
+ }
+
+ // Simulate submitting the credit card form.
+ {
+ base::HistogramTester histograms;
+ autofill_manager_->SubmitForm(form, TimeTicks::Now());
+ histograms.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard.OnNonsecurePage",
+ AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
+ histograms.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
+ histograms.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard.WithOnlyLocalData",
+ AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
+ }
+}
+
+// Tests that credit card form submissions are *not* logged specially when the
+// form is *not* on a non-secure page.
+TEST_F(AutofillMetricsTest,
+ NonsecureCreditCardFormMetricsNotRecordedOnSecurePage) {
+ personal_data_->RecreateCreditCards(
+ true /* include_local_credit_card */,
+ false /* include_masked_server_credit_card */,
+ false /* include_full_server_credit_card */);
+
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.origin = GURL("https://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+
+ FormFieldData field;
+ std::vector<ServerFieldType> field_types;
+ test::CreateTestFormField("Name on card", "cc-name", "", "text", &field);
+ form.fields.push_back(field);
+ field_types.push_back(CREDIT_CARD_NAME_FULL);
+ test::CreateTestFormField("Credit card", "card", "", "text", &field);
+ form.fields.push_back(field);
+ field_types.push_back(CREDIT_CARD_NUMBER);
+ test::CreateTestFormField("Month", "card_month", "", "text", &field);
+ form.fields.push_back(field);
+ field_types.push_back(CREDIT_CARD_EXP_MONTH);
+
+ // Simulate having seen this form on page load.
+ // |form_structure| will be owned by |autofill_manager_|.
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ // Simulate an Autofill query on a credit card field.
+ {
+ base::UserActionTester user_action_tester;
+ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "Autofill_PolledCreditCardSuggestions"));
+ }
+
+ // Simulate submitting the credit card form.
+ {
+ base::HistogramTester histograms;
+ autofill_manager_->SubmitForm(form, TimeTicks::Now());
+ histograms.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1);
+ histograms.ExpectBucketCount(
+ "Autofill.FormEvents.CreditCard",
+ AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
+ // Check that the nonsecure histogram was not recorded. ExpectBucketCount()
+ // can't be used here because it expects the histogram to exist.
+ EXPECT_EQ(
+ 0, histograms.GetTotalCountsForPrefix("Autofill.FormEvents.CreditCard")
+ ["Autofill.FormEvents.CreditCard.OnNonsecurePage"]);
+ }
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_popup_delegate.h b/chromium/components/autofill/core/browser/autofill_popup_delegate.h
index 99a66758a30..5a864242755 100644
--- a/chromium/components/autofill/core/browser/autofill_popup_delegate.h
+++ b/chromium/components/autofill/core/browser/autofill_popup_delegate.h
@@ -43,6 +43,9 @@ class AutofillPopupDelegate {
// Informs the delegate that the Autofill previewed form should be cleared.
virtual void ClearPreviewedForm() = 0;
+
+ // Returns true if popup is for credit card.
+ virtual bool IsCreditCardPopup() = 0;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_profile.cc b/chromium/components/autofill/core/browser/autofill_profile.cc
index 469111e4ce6..9630d5b188b 100644
--- a/chromium/components/autofill/core/browser/autofill_profile.cc
+++ b/chromium/components/autofill/core/browser/autofill_profile.cc
@@ -195,22 +195,23 @@ AutofillProfile::AutofillProfile(const std::string& guid,
const std::string& origin)
: AutofillDataModel(guid, origin),
record_type_(LOCAL_PROFILE),
- phone_number_(this) {
-}
+ phone_number_(this),
+ has_converted_(false) {}
AutofillProfile::AutofillProfile(RecordType type, const std::string& server_id)
: AutofillDataModel(base::GenerateGUID(), std::string()),
record_type_(type),
phone_number_(this),
- server_id_(server_id) {
+ server_id_(server_id),
+ has_converted_(false) {
DCHECK(type == SERVER_PROFILE);
}
AutofillProfile::AutofillProfile()
: AutofillDataModel(base::GenerateGUID(), std::string()),
record_type_(LOCAL_PROFILE),
- phone_number_(this) {
-}
+ phone_number_(this),
+ has_converted_(false) {}
AutofillProfile::AutofillProfile(const AutofillProfile& profile)
: AutofillDataModel(std::string(), std::string()), phone_number_(this) {
@@ -243,6 +244,7 @@ AutofillProfile& AutofillProfile::operator=(const AutofillProfile& profile) {
set_language_code(profile.language_code());
server_id_ = profile.server_id();
+ has_converted_ = profile.has_converted();
return *this;
}
diff --git a/chromium/components/autofill/core/browser/autofill_profile.h b/chromium/components/autofill/core/browser/autofill_profile.h
index 00be04e8592..ae7b521385d 100644
--- a/chromium/components/autofill/core/browser/autofill_profile.h
+++ b/chromium/components/autofill/core/browser/autofill_profile.h
@@ -178,6 +178,10 @@ class AutofillProfile : public AutofillDataModel {
// use.
void RecordAndLogUse();
+ // Valid only when type() == SERVER_PROFILE.
+ bool has_converted() const { return has_converted_; }
+ void set_has_converted(bool has_converted) { has_converted_ = has_converted; }
+
private:
typedef std::vector<const FormGroup*> FormGroupList;
@@ -221,6 +225,10 @@ class AutofillProfile : public AutofillDataModel {
// ID used for identifying this profile. Only set for SERVER_PROFILEs. This is
// a hash of the contents.
std::string server_id_;
+
+ // Only useful for SERVER_PROFILEs. Whether this server profile has been
+ // converted to a local profile.
+ bool has_converted_;
};
// So we can compare AutofillProfiles with EXPECT_EQ().
diff --git a/chromium/components/autofill/core/browser/autofill_profile_comparator.cc b/chromium/components/autofill/core/browser/autofill_profile_comparator.cc
index edfe2450d67..c3e8709549b 100644
--- a/chromium/components/autofill/core/browser/autofill_profile_comparator.cc
+++ b/chromium/components/autofill/core/browser/autofill_profile_comparator.cc
@@ -316,7 +316,7 @@ bool AutofillProfileComparator::MergeCompanyNames(
switch (result) {
case DIFFERENT_TOKENS:
default:
- NOTREACHED();
+ NOTREACHED() << "Unexpected mismatch: '" << c1 << "' vs '" << c2 << "'";
return false;
case S1_CONTAINS_S2:
best = &c1;
@@ -501,13 +501,72 @@ bool AutofillProfileComparator::MergeAddresses(const AutofillProfile& p1,
break;
case DIFFERENT_TOKENS:
default:
- // The addresses aren't mergeable and we shouldn't be doing any of
+ // The cities aren't mergeable and we shouldn't be doing any of
+ // this.
+ NOTREACHED() << "Unexpected mismatch: '" << city1 << "' vs '" << city2
+ << "'";
+ return false;
+ }
+ }
+
+ // One of the dependend localities is empty or one of the localities has a
+ // subset of tokens from the other. Pick the locality name with more tokens;
+ // this is usually the most explicit one.
+ const AutofillType kDependentLocality(ADDRESS_HOME_DEPENDENT_LOCALITY);
+ const base::string16& locality1 = p1.GetInfo(kDependentLocality, app_locale_);
+ const base::string16& locality2 = p2.GetInfo(kDependentLocality, app_locale_);
+ if (locality1.empty()) {
+ address->SetInfo(kDependentLocality, locality2, app_locale_);
+ } else if (locality2.empty()) {
+ address->SetInfo(kDependentLocality, locality1, app_locale_);
+ } else {
+ // Prefer the one with more tokens, making sure to apply address
+ // normalization and rewriting before doing the comparison.
+ CompareTokensResult result =
+ CompareTokens(rewriter.Rewrite(NormalizeForComparison(locality1)),
+ rewriter.Rewrite(NormalizeForComparison(locality2)));
+ switch (result) {
+ case SAME_TOKENS:
+ // They have the same set of unique tokens. Let's pick the more recently
+ // used one.
+ address->SetInfo(
+ kDependentLocality,
+ (p2.use_date() > p1.use_date() ? locality2 : locality1),
+ app_locale_);
+ break;
+ case S1_CONTAINS_S2:
+ // locality1 has more unique tokens than locality2.
+ address->SetInfo(kDependentLocality, locality1, app_locale_);
+ break;
+ case S2_CONTAINS_S1:
+ // locality2 has more unique tokens than locality1.
+ address->SetInfo(kDependentLocality, locality2, app_locale_);
+ break;
+ case DIFFERENT_TOKENS:
+ default:
+ // The localities aren't mergeable and we shouldn't be doing any of
// this.
- NOTREACHED();
+ NOTREACHED() << "Unexpected mismatch: '" << locality1 << "' vs '"
+ << locality2 << "'";
return false;
}
}
+ // One of the sorting codes is empty, they are the same, or one is a substring
+ // of the other. We prefer the most recently used sorting code.
+ const AutofillType kSortingCode(ADDRESS_HOME_SORTING_CODE);
+ const base::string16& sorting1 = p1.GetInfo(kSortingCode, app_locale_);
+ const base::string16& sorting2 = p2.GetInfo(kSortingCode, app_locale_);
+ if (sorting1.empty()) {
+ address->SetInfo(kSortingCode, sorting2, app_locale_);
+ } else if (sorting2.empty()) {
+ address->SetInfo(kSortingCode, sorting1, app_locale_);
+ } else {
+ address->SetInfo(kSortingCode,
+ (p2.use_date() > p1.use_date() ? sorting2 : sorting1),
+ app_locale_);
+ }
+
// One of the addresses is empty or one of the addresses has a subset of
// tokens from the other. Prefer the more verbosely expressed one.
const AutofillType kStreetAddress(ADDRESS_HOME_STREET_ADDRESS);
@@ -554,7 +613,8 @@ bool AutofillProfileComparator::MergeAddresses(const AutofillProfile& p1,
default:
// The addresses aren't mergeable and we shouldn't be doing any of
// this.
- NOTREACHED();
+ NOTREACHED() << "Unexpected mismatch: '" << address1 << "' vs '"
+ << address2 << "'";
return false;
}
}
@@ -771,17 +831,17 @@ bool AutofillProfileComparator::HaveMergeablePhoneNumbers(
// Parse and compare the phone numbers.
PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
switch (phone_util->IsNumberMatchWithTwoStrings(phone_1, phone_2)) {
- case PhoneNumberUtil::INVALID_NUMBER:
- case PhoneNumberUtil::NO_MATCH:
- return false;
case PhoneNumberUtil::SHORT_NSN_MATCH:
case PhoneNumberUtil::NSN_MATCH:
case PhoneNumberUtil::EXACT_MATCH:
return true;
+ case PhoneNumberUtil::INVALID_NUMBER:
+ case PhoneNumberUtil::NO_MATCH:
+ return false;
+ default:
+ NOTREACHED();
+ return false;
}
-
- NOTREACHED();
- return false;
}
bool AutofillProfileComparator::HaveMergeableAddresses(
@@ -797,10 +857,6 @@ bool AutofillProfileComparator::HaveMergeableAddresses(
return false;
}
- // TODO(rogerm): Lookup the normalization rules for the (common) country of
- // the address. The rules should be applied post NormalizeForComparison to
- // the state, city, and address bag of words comparisons.
-
// Zip
// ----
// If the addresses are definitely not in the same zip/area code then we're
@@ -816,6 +872,8 @@ bool AutofillProfileComparator::HaveMergeableAddresses(
return false;
}
+ // Use the token rewrite rules for the (common) country of the address to
+ // transform equivalent substrings to a representative token for comparison.
AddressRewriter rewriter =
AddressRewriter::ForCountryCode(country1.empty() ? country2 : country1);
@@ -845,14 +903,44 @@ bool AutofillProfileComparator::HaveMergeableAddresses(
// TODO(rogerm): If the match is between non-empty zip codes then we can infer
// that the two city strings are intended to have the same meaning. This
// handles the cases where we have a city vs one of its suburbs.
- const base::string16& city1 = rewriter.Rewrite(NormalizeForComparison(
- p1.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_)));
- const base::string16& city2 = rewriter.Rewrite(NormalizeForComparison(
- p2.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_)));
+ const AutofillType kCity(ADDRESS_HOME_CITY);
+ const base::string16& city1 =
+ rewriter.Rewrite(NormalizeForComparison(p1.GetInfo(kCity, app_locale_)));
+ const base::string16& city2 =
+ rewriter.Rewrite(NormalizeForComparison(p2.GetInfo(kCity, app_locale_)));
if (CompareTokens(city1, city2) == DIFFERENT_TOKENS) {
return false;
}
+ // Dependent Locality
+ // -------------------
+ // Heuristic: Dependent Localities are mergeable if one is a (possibly empty)
+ // bag of words subset of the other.
+ const AutofillType kDependentLocality(ADDRESS_HOME_DEPENDENT_LOCALITY);
+ const base::string16& locality1 = rewriter.Rewrite(
+ NormalizeForComparison(p1.GetInfo(kDependentLocality, app_locale_)));
+ const base::string16& locality2 = rewriter.Rewrite(
+ NormalizeForComparison(p2.GetInfo(kDependentLocality, app_locale_)));
+ if (CompareTokens(locality1, locality2) == DIFFERENT_TOKENS) {
+ return false;
+ }
+
+ // Sorting Code
+ // -------------
+ // Heuristic: Sorting codes are mergeable if one is empty or one is a
+ // substring of the other, post normalization and whitespace removed. This
+ // is similar to postal/zip codes.
+ const AutofillType kSortingCode(ADDRESS_HOME_SORTING_CODE);
+ const base::string16& sorting1 = NormalizeForComparison(
+ p1.GetInfo(kSortingCode, app_locale_), DISCARD_WHITESPACE);
+ const base::string16& sorting2 = NormalizeForComparison(
+ p2.GetInfo(kSortingCode, app_locale_), DISCARD_WHITESPACE);
+ if (!sorting1.empty() && !sorting2.empty() &&
+ sorting1.find(sorting2) == base::string16::npos &&
+ sorting2.find(sorting1) == base::string16::npos) {
+ return false;
+ }
+
// Address
// --------
// Heuristic: Street addresses are mergeable if one is a (possibly empty) bag
diff --git a/chromium/components/autofill/core/browser/autofill_profile_comparator_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_comparator_unittest.cc
index bcd0274eb66..36da4fdf765 100644
--- a/chromium/components/autofill/core/browser/autofill_profile_comparator_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_profile_comparator_unittest.cc
@@ -16,9 +16,11 @@
// Field Type Constants
using autofill::ADDRESS_HOME_CITY;
using autofill::ADDRESS_HOME_COUNTRY;
+using autofill::ADDRESS_HOME_DEPENDENT_LOCALITY;
using autofill::ADDRESS_HOME_LINE1;
using autofill::ADDRESS_HOME_LINE2;
using autofill::ADDRESS_HOME_LINE3;
+using autofill::ADDRESS_HOME_SORTING_CODE;
using autofill::ADDRESS_HOME_STATE;
using autofill::ADDRESS_HOME_STREET_ADDRESS;
using autofill::ADDRESS_HOME_ZIP;
@@ -239,6 +241,13 @@ class AutofillProfileComparatorTest : public ::testing::Test {
EXPECT_EQ(
expected.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), kLocale),
actual.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), kLocale));
+ EXPECT_EQ(
+ expected.GetInfo(AutofillType(ADDRESS_HOME_DEPENDENT_LOCALITY),
+ kLocale),
+ actual.GetInfo(AutofillType(ADDRESS_HOME_DEPENDENT_LOCALITY), kLocale));
+ EXPECT_EQ(
+ expected.GetInfo(AutofillType(ADDRESS_HOME_SORTING_CODE), kLocale),
+ actual.GetInfo(AutofillType(ADDRESS_HOME_SORTING_CODE), kLocale));
EXPECT_EQ(expected.GetInfo(AutofillType(ADDRESS_HOME_CITY), kLocale),
actual.GetInfo(AutofillType(ADDRESS_HOME_CITY), kLocale));
EXPECT_EQ(expected.GetInfo(AutofillType(ADDRESS_HOME_STATE), kLocale),
@@ -518,10 +527,14 @@ TEST_F(AutofillProfileComparatorTest, HaveMergeableAddresses) {
AutofillProfile empty = CreateProfileWithAddress("", "", "", "", "", "");
AutofillProfile p1 = CreateProfileWithAddress(
"1 Some Street", "Unit 3", "Carver", "CA - California", "90210", "US");
+ p1.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, UTF8ToUTF16("Some String"));
+ p1.SetRawInfo(ADDRESS_HOME_SORTING_CODE, UTF8ToUTF16("64205 Biarritz CEDEX"));
+
AutofillProfile p2 = CreateProfileWithAddress(
"Unit 3", "1 Some Street", "Suburb", "california", "90 210-3214", "");
AutofillProfile p3 = CreateProfileWithAddress("1 Some Street #3", "",
"Carver City", "ca", "", "us");
+
AutofillProfile differentCountry =
CopyAndModify(p1, {{ADDRESS_HOME_COUNTRY, "CA"}});
AutofillProfile differentZip =
@@ -533,6 +546,10 @@ TEST_F(AutofillProfileComparatorTest, HaveMergeableAddresses) {
AutofillProfile differentAddress =
CopyAndModify(p1, {{ADDRESS_HOME_LINE1, "17 Park Lane"},
{ADDRESS_HOME_LINE2, "Suite 150"}});
+ AutofillProfile differentLocality =
+ CopyAndModify(p1, {{ADDRESS_HOME_DEPENDENT_LOCALITY, "Funky Chicken"}});
+ AutofillProfile differentSortingCode =
+ CopyAndModify(p1, {{ADDRESS_HOME_SORTING_CODE, "98000 Monaco"}});
EXPECT_TRUE(comparator_.HaveMergeableAddresses(p1, empty));
EXPECT_TRUE(comparator_.HaveMergeableAddresses(empty, p2));
@@ -553,6 +570,8 @@ TEST_F(AutofillProfileComparatorTest, HaveMergeableAddresses) {
EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, differentState));
EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, differentCity));
EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, differentAddress));
+ EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, differentLocality));
+ EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, differentSortingCode));
}
TEST_F(AutofillProfileComparatorTest, AreMergeable) {
@@ -902,7 +921,6 @@ TEST_F(AutofillProfileComparatorTest, MergePhoneNumbers_Intl) {
}
TEST_F(AutofillProfileComparatorTest, MergeAddresses) {
- AutofillProfile empty;
AutofillProfile p1 = CreateProfileWithAddress(
"1 Some Street", "Unit 3", "Carver", "CA - California", "90210", "US");
AutofillProfile p2 = CreateProfileWithAddress(
@@ -919,8 +937,25 @@ TEST_F(AutofillProfileComparatorTest, MergeAddresses) {
MergeAddressesAndExpect(p1, p2, expected);
}
+TEST_F(AutofillProfileComparatorTest, MergeAddressesMostUniqueTokens) {
+ AutofillProfile p1 = CreateProfileWithAddress(
+ "1 Some Street", "Unit 3", "Carver", "CA - California", "90210", "US");
+ AutofillProfile p2 = CreateProfileWithAddress(
+ "1 Some Other Street", "Unit 3", "Carver City", "ca", "90210-1234", "us");
+
+ Address expected;
+ expected.SetRawInfo(ADDRESS_HOME_LINE1, UTF8ToUTF16("1 Some Other Street"));
+ expected.SetRawInfo(ADDRESS_HOME_LINE2, UTF8ToUTF16("Unit 3"));
+ expected.SetRawInfo(ADDRESS_HOME_CITY, UTF8ToUTF16("Carver City"));
+ expected.SetRawInfo(ADDRESS_HOME_STATE, UTF8ToUTF16("ca"));
+ expected.SetRawInfo(ADDRESS_HOME_ZIP, UTF8ToUTF16("90210-1234"));
+ expected.SetRawInfo(ADDRESS_HOME_COUNTRY, UTF8ToUTF16("US"));
+
+ MergeAddressesAndExpect(p1, p2, expected);
+ MergeAddressesAndExpect(p2, p1, expected);
+}
+
TEST_F(AutofillProfileComparatorTest, MergeAddressesWithRewrite) {
- AutofillProfile empty;
AutofillProfile p1 = CreateProfileWithAddress(
"6543 CH BACON", "APP 3", "MONTRÉAL", "QUÉBEC", "HHH999", "ca");
AutofillProfile p2 = CreateProfileWithAddress(
@@ -936,4 +971,34 @@ TEST_F(AutofillProfileComparatorTest, MergeAddressesWithRewrite) {
expected.SetRawInfo(ADDRESS_HOME_COUNTRY, UTF8ToUTF16("CA"));
MergeAddressesAndExpect(p1, p2, expected);
+ MergeAddressesAndExpect(p2, p1, expected);
+}
+
+TEST_F(AutofillProfileComparatorTest,
+ MergeAddressesDependendLocalityAndSortingCode) {
+ AutofillProfile p1 = CreateProfileWithAddress(
+ "6543 CH BACON", "APP 3", "MONTRÉAL", "QUÉBEC", "HHH999", "ca");
+ p1.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, UTF8ToUTF16("Some String"));
+ p1.SetRawInfo(ADDRESS_HOME_SORTING_CODE, UTF8ToUTF16("64205 Biarritz CEDEX"));
+ AutofillProfile p2 = CreateProfileWithAddress(
+ "6543, Bacon Rd", "", "Montreal", "QC", "hhh 999", "CA");
+ p2.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY,
+ UTF8ToUTF16("Some Other String"));
+ p2.SetRawInfo(ADDRESS_HOME_SORTING_CODE, UTF8ToUTF16("64205 Biarritz"));
+ p2.set_use_date(p1.use_date() + base::TimeDelta::FromMinutes(1));
+
+ Address expected;
+ expected.SetRawInfo(ADDRESS_HOME_LINE1, UTF8ToUTF16("6543 CH BACON"));
+ expected.SetRawInfo(ADDRESS_HOME_LINE2, UTF8ToUTF16("APP 3"));
+ expected.SetRawInfo(ADDRESS_HOME_CITY, UTF8ToUTF16("Montreal"));
+ expected.SetRawInfo(ADDRESS_HOME_STATE, UTF8ToUTF16("QC"));
+ expected.SetRawInfo(ADDRESS_HOME_ZIP, UTF8ToUTF16("hhh 999"));
+ expected.SetRawInfo(ADDRESS_HOME_COUNTRY, UTF8ToUTF16("CA"));
+ expected.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY,
+ UTF8ToUTF16("Some Other String"));
+ expected.SetRawInfo(ADDRESS_HOME_SORTING_CODE,
+ UTF8ToUTF16("64205 Biarritz")); // Preferred by use date.
+
+ MergeAddressesAndExpect(p1, p2, expected);
+ MergeAddressesAndExpect(p2, p1, expected);
}
diff --git a/chromium/components/autofill/core/browser/autofill_profile_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_unittest.cc
index d50ddecf7ee..c54f2ae61c4 100644
--- a/chromium/components/autofill/core/browser/autofill_profile_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_profile_unittest.cc
@@ -7,11 +7,12 @@
#include <stddef.h>
#include <memory>
+#include <vector>
#include "base/format_macros.h"
#include "base/guid.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
+#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
@@ -79,6 +80,14 @@ void SetupTestProfile(AutofillProfile& profile) {
"Hollywood", "CA", "91601", "US", "12345678910");
}
+std::vector<AutofillProfile*> ToRawPointerVector(
+ const std::vector<std::unique_ptr<AutofillProfile>>& list) {
+ std::vector<AutofillProfile*> result;
+ for (const auto& item : list)
+ result.push_back(item.get());
+ return result;
+}
+
} // namespace
// Tests different possibilities for summary string generation.
@@ -186,62 +195,32 @@ TEST(AutofillProfileTest, PreviewSummaryString) {
}
TEST(AutofillProfileTest, AdjustInferredLabels) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(
- profiles[0],
- "John",
- "",
- "Doe",
- "johndoe@hades.com",
- "Underworld",
- "666 Erebus St.",
- "",
- "Elysium", "CA",
- "91111",
- "US",
- "16502111111");
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "http://www.example.com/"));
- test::SetProfileInfo(
- profiles[1],
- "Jane",
- "",
- "Doe",
- "janedoe@tertium.com",
- "Pluto Inc.",
- "123 Letha Shore.",
- "",
- "Dis", "CA",
- "91222",
- "US",
- "12345678910");
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[0].get(), "John", "", "Doe",
+ "johndoe@hades.com", "Underworld", "666 Erebus St.", "",
+ "Elysium", "CA", "91111", "US", "16502111111");
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "http://www.example.com/"));
+ test::SetProfileInfo(profiles[1].get(), "Jane", "", "Doe",
+ "janedoe@tertium.com", "Pluto Inc.", "123 Letha Shore.",
+ "", "Dis", "CA", "91222", "US", "12345678910");
std::vector<base::string16> labels;
- AutofillProfile::CreateDifferentiatingLabels(
- profiles.get(), "en-US", &labels);
+ AutofillProfile::CreateDifferentiatingLabels(ToRawPointerVector(profiles),
+ "en-US", &labels);
ASSERT_EQ(2U, labels.size());
EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St."), labels[0]);
EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]);
profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), kSettingsOrigin));
- test::SetProfileInfo(
- profiles[2],
- "John",
- "",
- "Doe",
- "johndoe@tertium.com",
- "Underworld",
- "666 Erebus St.",
- "",
- "Elysium", "CA",
- "91111",
- "US",
- "16502111111");
+ base::MakeUnique<AutofillProfile>(base::GenerateGUID(), kSettingsOrigin));
+ test::SetProfileInfo(profiles[2].get(), "John", "", "Doe",
+ "johndoe@tertium.com", "Underworld", "666 Erebus St.",
+ "", "Elysium", "CA", "91111", "US", "16502111111");
labels.clear();
- AutofillProfile::CreateDifferentiatingLabels(
- profiles.get(), "en-US", &labels);
+ AutofillProfile::CreateDifferentiatingLabels(ToRawPointerVector(profiles),
+ "en-US", &labels);
// Profile 0 and 2 inferred label now includes an e-mail.
ASSERT_EQ(3U, labels.size());
@@ -254,24 +233,15 @@ TEST(AutofillProfileTest, AdjustInferredLabels) {
profiles.resize(2);
profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), std::string()));
- test::SetProfileInfo(
- profiles[2],
- "John",
- "",
- "Doe",
- "johndoe@hades.com",
- "Underworld",
- "666 Erebus St.",
- "",
- "Elysium", "CO", // State is different
- "91111",
- "US",
- "16502111111");
+ base::MakeUnique<AutofillProfile>(base::GenerateGUID(), std::string()));
+ test::SetProfileInfo(profiles[2].get(), "John", "", "Doe",
+ "johndoe@hades.com", "Underworld", "666 Erebus St.", "",
+ "Elysium", "CO", // State is different
+ "91111", "US", "16502111111");
labels.clear();
- AutofillProfile::CreateDifferentiatingLabels(
- profiles.get(), "en-US", &labels);
+ AutofillProfile::CreateDifferentiatingLabels(ToRawPointerVector(profiles),
+ "en-US", &labels);
// Profile 0 and 2 inferred label now includes a state.
ASSERT_EQ(3U, labels.size());
@@ -279,25 +249,17 @@ TEST(AutofillProfileTest, AdjustInferredLabels) {
EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]);
EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CO"), labels[2]);
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(
- profiles[3],
- "John",
- "",
- "Doe",
- "johndoe@hades.com",
- "Underworld",
- "666 Erebus St.",
- "",
- "Elysium", "CO", // State is different for some.
- "91111",
- "US",
- "16504444444"); // Phone is different for some.
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[3].get(), "John", "", "Doe",
+ "johndoe@hades.com", "Underworld", "666 Erebus St.", "",
+ "Elysium", "CO", // State is different for some.
+ "91111", "US",
+ "16504444444"); // Phone is different for some.
labels.clear();
- AutofillProfile::CreateDifferentiatingLabels(
- profiles.get(), "en-US", &labels);
+ AutofillProfile::CreateDifferentiatingLabels(ToRawPointerVector(profiles),
+ "en-US", &labels);
ASSERT_EQ(4U, labels.size());
EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CA"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]);
@@ -308,25 +270,18 @@ TEST(AutofillProfileTest, AdjustInferredLabels) {
EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CO, 16504444444"),
labels[3]);
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(
- profiles[4],
- "John",
- "",
- "Doe",
- "johndoe@styx.com", // E-Mail is different for some.
- "Underworld",
- "666 Erebus St.",
- "",
- "Elysium", "CO", // State is different for some.
- "91111",
- "US",
- "16504444444"); // Phone is different for some.
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[4].get(), "John", "", "Doe",
+ "johndoe@styx.com", // E-Mail is different for some.
+ "Underworld", "666 Erebus St.", "", "Elysium",
+ "CO", // State is different for some.
+ "91111", "US",
+ "16504444444"); // Phone is different for some.
labels.clear();
- AutofillProfile::CreateDifferentiatingLabels(
- profiles.get(), "en-US", &labels);
+ AutofillProfile::CreateDifferentiatingLabels(ToRawPointerVector(profiles),
+ "en-US", &labels);
ASSERT_EQ(5U, labels.size());
EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CA"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]);
@@ -341,21 +296,13 @@ TEST(AutofillProfileTest, AdjustInferredLabels) {
}
TEST(AutofillProfileTest, CreateInferredLabelsI18n_CH) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles.back(),
- "H.",
- "R.",
- "Giger",
- "hrgiger@beispiel.com",
- "Beispiel Inc",
- "Brandschenkestrasse 110",
- "",
- "Zurich", "",
- "8002",
- "CH",
- "+41 44-668-1800");
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles.back().get(), "H.", "R.", "Giger",
+ "hrgiger@beispiel.com", "Beispiel Inc",
+ "Brandschenkestrasse 110", "", "Zurich", "", "8002",
+ "CH", "+41 44-668-1800");
profiles.back()->set_language_code("de_CH");
static const char* kExpectedLabels[] = {
"",
@@ -374,8 +321,8 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_CH) {
std::vector<base::string16> labels;
for (size_t i = 0; i < arraysize(kExpectedLabels); ++i) {
- AutofillProfile::CreateInferredLabels(
- profiles.get(), NULL, UNKNOWN_TYPE, i, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL,
+ UNKNOWN_TYPE, i, "en-US", &labels);
ASSERT_FALSE(labels.empty());
EXPECT_EQ(UTF8ToUTF16(kExpectedLabels[i]), labels.back());
}
@@ -383,21 +330,12 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_CH) {
TEST(AutofillProfileTest, CreateInferredLabelsI18n_FR) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles.back(),
- "Antoine",
- "",
- "de Saint-Exupéry",
- "antoine@exemple.com",
- "Exemple Inc",
- "8 Rue de Londres",
- "",
- "Paris", "",
- "75009",
- "FR",
- "+33 (0) 1 42 68 53 00");
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles.back().get(), "Antoine", "", "de Saint-Exupéry",
+ "antoine@exemple.com", "Exemple Inc", "8 Rue de Londres",
+ "", "Paris", "", "75009", "FR", "+33 (0) 1 42 68 53 00");
profiles.back()->set_language_code("fr_FR");
profiles.back()->SetInfo(
AutofillType(ADDRESS_HOME_SORTING_CODE), UTF8ToUTF16("CEDEX"), "en-US");
@@ -422,29 +360,21 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_FR) {
std::vector<base::string16> labels;
for (size_t i = 0; i < arraysize(kExpectedLabels); ++i) {
- AutofillProfile::CreateInferredLabels(
- profiles.get(), NULL, UNKNOWN_TYPE, i, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL,
+ UNKNOWN_TYPE, i, "en-US", &labels);
ASSERT_FALSE(labels.empty());
EXPECT_EQ(UTF8ToUTF16(kExpectedLabels[i]), labels.back());
}
}
TEST(AutofillProfileTest, CreateInferredLabelsI18n_KR) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles.back(),
- "Park",
- "",
- "Jae-sang",
- "park@yeleul.com",
- "Yeleul Inc",
- "Gangnam Finance Center",
- "152 Teheran-ro",
- "Gangnam-Gu", "Seoul",
- "135-984",
- "KR",
- "+82-2-531-9000");
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles.back().get(), "Park", "", "Jae-sang",
+ "park@yeleul.com", "Yeleul Inc",
+ "Gangnam Finance Center", "152 Teheran-ro", "Gangnam-Gu",
+ "Seoul", "135-984", "KR", "+82-2-531-9000");
profiles.back()->set_language_code("ko_Latn");
profiles.back()->SetInfo(AutofillType(ADDRESS_HOME_DEPENDENT_LOCALITY),
UTF8ToUTF16("Yeoksam-Dong"),
@@ -475,29 +405,21 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_KR) {
std::vector<base::string16> labels;
for (size_t i = 0; i < arraysize(kExpectedLabels); ++i) {
- AutofillProfile::CreateInferredLabels(
- profiles.get(), NULL, UNKNOWN_TYPE, i, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL,
+ UNKNOWN_TYPE, i, "en-US", &labels);
ASSERT_FALSE(labels.empty());
EXPECT_EQ(UTF8ToUTF16(kExpectedLabels[i]), labels.back());
}
}
TEST(AutofillProfileTest, CreateInferredLabelsI18n_JP_Latn) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles.back(),
- "Miku",
- "",
- "Hatsune",
- "miku@rei.com",
- "Rei Inc",
- "Roppongi Hills Mori Tower",
- "6-10-1 Roppongi",
- "Minato-ku", "Tokyo",
- "106-6126",
- "JP",
- "+81-3-6384-9000");
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles.back().get(), "Miku", "", "Hatsune",
+ "miku@rei.com", "Rei Inc", "Roppongi Hills Mori Tower",
+ "6-10-1 Roppongi", "Minato-ku", "Tokyo", "106-6126",
+ "JP", "+81-3-6384-9000");
profiles.back()->set_language_code("ja_Latn");
static const char* kExpectedLabels[] = {
"",
@@ -521,28 +443,20 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_JP_Latn) {
std::vector<base::string16> labels;
for (size_t i = 0; i < arraysize(kExpectedLabels); ++i) {
- AutofillProfile::CreateInferredLabels(
- profiles.get(), NULL, UNKNOWN_TYPE, i, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL,
+ UNKNOWN_TYPE, i, "en-US", &labels);
ASSERT_FALSE(labels.empty());
EXPECT_EQ(UTF8ToUTF16(kExpectedLabels[i]), labels.back());
}
}
TEST(AutofillProfileTest, CreateInferredLabelsI18n_JP_ja) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles.back(),
- "ミク",
- "",
- "åˆéŸ³",
- "miku@rei.com",
- "例",
- "六本木ヒルズ森タワー",
- "六本木 6-10-1",
- "港区", "æ±äº¬éƒ½",
- "106-6126",
- "JP",
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles.back().get(), "ミク", "", "åˆéŸ³",
+ "miku@rei.com", "例", "六本木ヒルズ森タワー",
+ "六本木 6-10-1", "港区", "æ±äº¬éƒ½", "106-6126", "JP",
"03-6384-9000");
profiles.back()->set_language_code("ja_JP");
static const char* kExpectedLabels[] = {
@@ -563,53 +477,35 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_JP_ja) {
std::vector<base::string16> labels;
for (size_t i = 0; i < arraysize(kExpectedLabels); ++i) {
- AutofillProfile::CreateInferredLabels(
- profiles.get(), NULL, UNKNOWN_TYPE, i, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL,
+ UNKNOWN_TYPE, i, "en-US", &labels);
ASSERT_FALSE(labels.empty());
EXPECT_EQ(UTF8ToUTF16(kExpectedLabels[i]), labels.back());
}
}
TEST(AutofillProfileTest, CreateInferredLabels) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles[0],
- "John",
- "",
- "Doe",
- "johndoe@hades.com",
- "Underworld",
- "666 Erebus St.",
- "",
- "Elysium", "CA",
- "91111",
- "US",
- "16502111111");
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles[1],
- "Jane",
- "",
- "Doe",
- "janedoe@tertium.com",
- "Pluto Inc.",
- "123 Letha Shore.",
- "",
- "Dis", "CA",
- "91222",
- "US",
- "12345678910");
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[0].get(), "John", "", "Doe",
+ "johndoe@hades.com", "Underworld", "666 Erebus St.", "",
+ "Elysium", "CA", "91111", "US", "16502111111");
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[1].get(), "Jane", "", "Doe",
+ "janedoe@tertium.com", "Pluto Inc.", "123 Letha Shore.",
+ "", "Dis", "CA", "91222", "US", "12345678910");
std::vector<base::string16> labels;
// Two fields at least - no filter.
- AutofillProfile::CreateInferredLabels(profiles.get(), NULL, UNKNOWN_TYPE, 2,
- "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL,
+ UNKNOWN_TYPE, 2, "en-US", &labels);
EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St."), labels[0]);
EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]);
// Three fields at least - no filter.
- AutofillProfile::CreateInferredLabels(profiles.get(), NULL, UNKNOWN_TYPE, 3,
- "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL,
+ UNKNOWN_TYPE, 3, "en-US", &labels);
EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., Elysium"),
labels[0]);
EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore., Dis"),
@@ -621,21 +517,24 @@ TEST(AutofillProfileTest, CreateInferredLabels) {
suggested_fields.push_back(ADDRESS_HOME_ZIP);
// Two fields at least, from suggested fields - no filter.
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- UNKNOWN_TYPE, 2, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, UNKNOWN_TYPE, 2,
+ "en-US", &labels);
EXPECT_EQ(ASCIIToUTF16("Elysium 91111"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("Dis 91222"), labels[1]);
// Three fields at least, from suggested fields - no filter.
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- UNKNOWN_TYPE, 3, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, UNKNOWN_TYPE, 3,
+ "en-US", &labels);
EXPECT_EQ(ASCIIToUTF16("Elysium, CA 91111"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("Dis, CA 91222"), labels[1]);
// Three fields at least, from suggested fields - but filter reduces available
// fields to two.
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- ADDRESS_HOME_ZIP, 3, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, ADDRESS_HOME_ZIP, 3,
+ "en-US", &labels);
EXPECT_EQ(ASCIIToUTF16("Elysium, CA"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("Dis, CA"), labels[1]);
@@ -643,15 +542,17 @@ TEST(AutofillProfileTest, CreateInferredLabels) {
// In our implementation we always display NAME_FULL for all NAME* fields...
suggested_fields.push_back(NAME_MIDDLE);
// One field at least, from suggested fields - no filter.
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- UNKNOWN_TYPE, 1, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, UNKNOWN_TYPE, 1,
+ "en-US", &labels);
EXPECT_EQ(ASCIIToUTF16("John Doe"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("Jane Doe"), labels[1]);
// One field at least, from suggested fields - filter the same as suggested
// field.
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- NAME_MIDDLE, 1, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, NAME_MIDDLE, 1,
+ "en-US", &labels);
EXPECT_EQ(base::string16(), labels[0]);
EXPECT_EQ(base::string16(), labels[1]);
@@ -659,8 +560,9 @@ TEST(AutofillProfileTest, CreateInferredLabels) {
// In our implementation we always display NAME_FULL for NAME_MIDDLE_INITIAL
suggested_fields.push_back(NAME_MIDDLE_INITIAL);
// One field at least, from suggested fields - no filter.
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- UNKNOWN_TYPE, 1, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, UNKNOWN_TYPE, 1,
+ "en-US", &labels);
EXPECT_EQ(ASCIIToUTF16("John Doe"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("Jane Doe"), labels[1]);
@@ -670,13 +572,14 @@ TEST(AutofillProfileTest, CreateInferredLabels) {
suggested_fields.push_back(UNKNOWN_TYPE);
suggested_fields.push_back(NAME_FULL);
suggested_fields.push_back(ADDRESS_HOME_LINE1);
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- NAME_FULL, 1, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, NAME_FULL, 1,
+ "en-US", &labels);
EXPECT_EQ(base::string16(ASCIIToUTF16("666 Erebus St.")), labels[0]);
EXPECT_EQ(base::string16(ASCIIToUTF16("123 Letha Shore.")), labels[1]);
// No suggested fields, but non-unknown excluded field.
- AutofillProfile::CreateInferredLabels(profiles.get(), NULL,
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL,
NAME_FULL, 1, "en-US", &labels);
EXPECT_EQ(base::string16(ASCIIToUTF16("666 Erebus St.")), labels[0]);
EXPECT_EQ(base::string16(ASCIIToUTF16("123 Letha Shore.")), labels[1]);
@@ -685,17 +588,16 @@ TEST(AutofillProfileTest, CreateInferredLabels) {
// Test that we fall back to using the full name if there are no other
// distinguishing fields, but only if it makes sense given the suggested fields.
TEST(AutofillProfileTest, CreateInferredLabelsFallsBackToFullName) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles[0],
- "John", "", "Doe", "doe@example.com", "",
- "88 Nowhere Ave.", "", "", "", "", "", "");
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles[1],
- "Johnny", "K", "Doe", "doe@example.com", "",
- "88 Nowhere Ave.", "", "", "", "", "", "");
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[0].get(), "John", "", "Doe", "doe@example.com",
+ "", "88 Nowhere Ave.", "", "", "", "", "", "");
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[1].get(), "Johnny", "K", "Doe",
+ "doe@example.com", "", "88 Nowhere Ave.", "", "", "", "",
+ "", "");
// If the only name field in the suggested fields is the excluded field, we
// should not fall back to the full name as a distinguishing field.
@@ -704,16 +606,18 @@ TEST(AutofillProfileTest, CreateInferredLabelsFallsBackToFullName) {
suggested_fields.push_back(ADDRESS_HOME_LINE1);
suggested_fields.push_back(EMAIL_ADDRESS);
std::vector<base::string16> labels;
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- NAME_LAST, 1, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, NAME_LAST, 1,
+ "en-US", &labels);
ASSERT_EQ(2U, labels.size());
EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave."), labels[0]);
EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave."), labels[1]);
// Otherwise, we should.
suggested_fields.push_back(NAME_FIRST);
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- NAME_LAST, 1, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, NAME_LAST, 1,
+ "en-US", &labels);
ASSERT_EQ(2U, labels.size());
EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., John Doe"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., Johnny K Doe"), labels[1]);
@@ -721,17 +625,15 @@ TEST(AutofillProfileTest, CreateInferredLabelsFallsBackToFullName) {
// Test that we do not show duplicate fields in the labels.
TEST(AutofillProfileTest, CreateInferredLabelsNoDuplicatedFields) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles[0],
- "John", "", "Doe", "doe@example.com", "",
- "88 Nowhere Ave.", "", "", "", "", "", "");
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles[1],
- "John", "", "Doe", "dojo@example.com", "",
- "88 Nowhere Ave.", "", "", "", "", "", "");
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[0].get(), "John", "", "Doe", "doe@example.com",
+ "", "88 Nowhere Ave.", "", "", "", "", "", "");
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[1].get(), "John", "", "Doe", "dojo@example.com",
+ "", "88 Nowhere Ave.", "", "", "", "", "", "");
// If the only name field in the suggested fields is the excluded field, we
// should not fall back to the full name as a distinguishing field.
@@ -740,8 +642,9 @@ TEST(AutofillProfileTest, CreateInferredLabelsNoDuplicatedFields) {
suggested_fields.push_back(ADDRESS_BILLING_LINE1);
suggested_fields.push_back(EMAIL_ADDRESS);
std::vector<base::string16> labels;
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- UNKNOWN_TYPE, 2, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, UNKNOWN_TYPE, 2,
+ "en-US", &labels);
ASSERT_EQ(2U, labels.size());
EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., doe@example.com"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., dojo@example.com"), labels[1]);
@@ -749,26 +652,24 @@ TEST(AutofillProfileTest, CreateInferredLabelsNoDuplicatedFields) {
// Make sure that empty fields are not treated as distinguishing fields.
TEST(AutofillProfileTest, CreateInferredLabelsSkipsEmptyFields) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles[0],
- "John", "", "Doe", "doe@example.com",
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[0].get(), "John", "", "Doe", "doe@example.com",
"Gogole", "", "", "", "", "", "", "");
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles[1],
- "John", "", "Doe", "doe@example.com",
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[1].get(), "John", "", "Doe", "doe@example.com",
"Ggoole", "", "", "", "", "", "", "");
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles[2],
- "John", "", "Doe", "john.doe@example.com",
- "Goolge", "", "", "", "", "", "", "");
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[2].get(), "John", "", "Doe",
+ "john.doe@example.com", "Goolge", "", "", "", "", "", "",
+ "");
std::vector<base::string16> labels;
- AutofillProfile::CreateInferredLabels(profiles.get(), NULL, UNKNOWN_TYPE, 3,
- "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL,
+ UNKNOWN_TYPE, 3, "en-US", &labels);
ASSERT_EQ(3U, labels.size());
EXPECT_EQ(ASCIIToUTF16("John Doe, doe@example.com, Gogole"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("John Doe, doe@example.com, Ggoole"), labels[1]);
@@ -777,8 +678,8 @@ TEST(AutofillProfileTest, CreateInferredLabelsSkipsEmptyFields) {
// A field must have a non-empty value for each profile to be considered a
// distinguishing field.
profiles[1]->SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("88 Nowhere Ave."));
- AutofillProfile::CreateInferredLabels(profiles.get(), NULL, UNKNOWN_TYPE, 1,
- "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL,
+ UNKNOWN_TYPE, 1, "en-US", &labels);
ASSERT_EQ(3U, labels.size());
EXPECT_EQ(ASCIIToUTF16("John Doe, doe@example.com, Gogole"), labels[0]);
EXPECT_EQ(ASCIIToUTF16("John Doe, 88 Nowhere Ave., doe@example.com, Ggoole"),
@@ -788,12 +689,11 @@ TEST(AutofillProfileTest, CreateInferredLabelsSkipsEmptyFields) {
// Test that labels that would otherwise have multiline values are flattened.
TEST(AutofillProfileTest, CreateInferredLabelsFlattensMultiLineValues) {
- ScopedVector<AutofillProfile> profiles;
- profiles.push_back(
- new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
- test::SetProfileInfo(profiles[0],
- "John", "", "Doe", "doe@example.com", "",
- "88 Nowhere Ave.", "Apt. 42", "", "", "", "", "");
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ profiles.push_back(base::MakeUnique<AutofillProfile>(
+ base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[0].get(), "John", "", "Doe", "doe@example.com",
+ "", "88 Nowhere Ave.", "Apt. 42", "", "", "", "", "");
// If the only name field in the suggested fields is the excluded field, we
// should not fall back to the full name as a distinguishing field.
@@ -801,8 +701,9 @@ TEST(AutofillProfileTest, CreateInferredLabelsFlattensMultiLineValues) {
suggested_fields.push_back(NAME_FULL);
suggested_fields.push_back(ADDRESS_HOME_STREET_ADDRESS);
std::vector<base::string16> labels;
- AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields,
- NAME_FULL, 1, "en-US", &labels);
+ AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
+ &suggested_fields, NAME_FULL, 1,
+ "en-US", &labels);
ASSERT_EQ(1U, labels.size());
EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., Apt. 42"), labels[0]);
}
diff --git a/chromium/components/autofill/core/browser/autofill_scanner.cc b/chromium/components/autofill/core/browser/autofill_scanner.cc
index 47a8c4c989e..857a7b8987f 100644
--- a/chromium/components/autofill/core/browser/autofill_scanner.cc
+++ b/chromium/components/autofill/core/browser/autofill_scanner.cc
@@ -9,11 +9,16 @@
namespace autofill {
-AutofillScanner::AutofillScanner(const std::vector<AutofillField*>& fields)
- : cursor_(fields.begin()),
- saved_cursor_(fields.begin()),
- begin_(fields.begin()),
- end_(fields.end()) {
+AutofillScanner::AutofillScanner(const std::vector<AutofillField*>& fields) {
+ Init(fields);
+}
+
+AutofillScanner::AutofillScanner(
+ const std::vector<std::unique_ptr<AutofillField>>& fields) {
+ for (const auto& field : fields)
+ non_owning_.push_back(field.get());
+
+ Init(non_owning_);
}
AutofillScanner::~AutofillScanner() {
@@ -27,7 +32,7 @@ void AutofillScanner::Advance() {
AutofillField* AutofillScanner::Cursor() const {
if (IsEnd()) {
NOTREACHED();
- return NULL;
+ return nullptr;
}
return *cursor_;
@@ -54,4 +59,11 @@ size_t AutofillScanner::SaveCursor() {
return static_cast<size_t>(cursor_ - begin_);
}
+void AutofillScanner::Init(const std::vector<AutofillField*>& fields) {
+ cursor_ = fields.begin();
+ saved_cursor_ = fields.begin();
+ begin_ = fields.begin();
+ end_ = fields.end();
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_scanner.h b/chromium/components/autofill/core/browser/autofill_scanner.h
index f30d5abcbc3..88b84e6fd94 100644
--- a/chromium/components/autofill/core/browser/autofill_scanner.h
+++ b/chromium/components/autofill/core/browser/autofill_scanner.h
@@ -7,6 +7,7 @@
#include <stddef.h>
+#include <memory>
#include <vector>
#include "base/macros.h"
@@ -20,6 +21,8 @@ class AutofillField;
class AutofillScanner {
public:
explicit AutofillScanner(const std::vector<AutofillField*>& fields);
+ explicit AutofillScanner(
+ const std::vector<std::unique_ptr<AutofillField>>& fields);
~AutofillScanner();
// Advances the cursor by one step, if possible.
@@ -43,6 +46,8 @@ class AutofillScanner {
size_t SaveCursor();
private:
+ void Init(const std::vector<AutofillField*>& fields);
+
// Indicates the current position in the stream, represented as a vector.
std::vector<AutofillField*>::const_iterator cursor_;
@@ -50,10 +55,13 @@ class AutofillScanner {
std::vector<AutofillField*>::const_iterator saved_cursor_;
// The beginning pointer for the stream.
- const std::vector<AutofillField*>::const_iterator begin_;
+ std::vector<AutofillField*>::const_iterator begin_;
// The past-the-end pointer for the stream.
- const std::vector<AutofillField*>::const_iterator end_;
+ std::vector<AutofillField*>::const_iterator end_;
+
+ // The storage of non-owning pointers, used for the unique_ptr constructor.
+ std::vector<AutofillField*> non_owning_;
DISALLOW_COPY_AND_ASSIGN(AutofillScanner);
};
diff --git a/chromium/components/autofill/core/browser/autofill_type.cc b/chromium/components/autofill/core/browser/autofill_type.cc
index 47081994a9d..8dc9dbc85ad 100644
--- a/chromium/components/autofill/core/browser/autofill_type.cc
+++ b/chromium/components/autofill/core/browser/autofill_type.cc
@@ -126,6 +126,7 @@ FieldTypeGroup AutofillType::group() const {
case PROBABLY_NEW_PASSWORD:
case NOT_NEW_PASSWORD:
case PROBABLY_ACCOUNT_CREATION_PASSWORD:
+ case CONFIRMATION_PASSWORD:
return PASSWORD_FIELD;
case NO_SERVER_DATA:
@@ -757,6 +758,8 @@ std::string AutofillType::ServerFieldTypeToString(ServerFieldType type) {
return "NOT_NEW_PASSWORD";
case PROBABLY_ACCOUNT_CREATION_PASSWORD:
return "PROBABLY_ACCOUNT_CREATION_PASSWORD";
+ case CONFIRMATION_PASSWORD:
+ return "CONFIRMATION_PASSWORD";
case MAX_VALID_FIELD_TYPE:
return std::string();
diff --git a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc
index 61bf8140a6c..ecdcce20fa0 100644
--- a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc
+++ b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc
@@ -4,6 +4,8 @@
#include "components/autofill/core/browser/autofill_wallet_data_type_controller.h"
+#include <utility>
+
#include "base/bind.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
@@ -18,13 +20,15 @@ namespace browser_sync {
AutofillWalletDataTypeController::AutofillWalletDataTypeController(
syncer::ModelType type,
- const scoped_refptr<base::SingleThreadTaskRunner>& db_thread,
+ scoped_refptr<base::SingleThreadTaskRunner> db_thread,
const base::Closure& dump_stack,
syncer::SyncClient* sync_client,
const scoped_refptr<autofill::AutofillWebDataService>& web_data_service)
- : NonUIDataTypeController(type, dump_stack, sync_client),
- db_thread_(db_thread),
- sync_client_(sync_client),
+ : AsyncDirectoryTypeController(type,
+ dump_stack,
+ sync_client,
+ syncer::GROUP_DB,
+ std::move(db_thread)),
callback_registered_(false),
web_data_service_(web_data_service),
currently_enabled_(IsEnabled()) {
@@ -39,18 +43,6 @@ AutofillWalletDataTypeController::AutofillWalletDataTypeController(
AutofillWalletDataTypeController::~AutofillWalletDataTypeController() {}
-syncer::ModelSafeGroup AutofillWalletDataTypeController::model_safe_group()
- const {
- return syncer::GROUP_DB;
-}
-
-bool AutofillWalletDataTypeController::PostTaskOnBackendThread(
- const tracked_objects::Location& from_here,
- const base::Closure& task) {
- DCHECK(CalledOnValidThread());
- return db_thread_->PostTask(from_here, task);
-}
-
bool AutofillWalletDataTypeController::StartModels() {
DCHECK(CalledOnValidThread());
DCHECK_EQ(state(), MODEL_STARTING);
diff --git a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.h b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.h
index e483e0b304f..cffe8628f57 100644
--- a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.h
+++ b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.h
@@ -7,7 +7,7 @@
#include "base/macros.h"
#include "components/prefs/pref_change_registrar.h"
-#include "components/sync/driver/non_ui_data_type_controller.h"
+#include "components/sync/driver/async_directory_type_controller.h"
namespace autofill {
class AutofillWebDataService;
@@ -17,25 +17,20 @@ namespace browser_sync {
// Controls syncing of either AUTOFILL_WALLET or AUTOFILL_WALLET_METADATA.
class AutofillWalletDataTypeController
- : public syncer::NonUIDataTypeController {
+ : public syncer::AsyncDirectoryTypeController {
public:
// |type| should be either AUTOFILL_WALLET or AUTOFILL_WALLET_METADATA.
// |dump_stack| is called when an unrecoverable error occurs.
AutofillWalletDataTypeController(
syncer::ModelType type,
- const scoped_refptr<base::SingleThreadTaskRunner>& db_thread,
+ scoped_refptr<base::SingleThreadTaskRunner> db_thread,
const base::Closure& dump_stack,
syncer::SyncClient* sync_client,
const scoped_refptr<autofill::AutofillWebDataService>& web_data_service);
~AutofillWalletDataTypeController() override;
- // NonUIDataTypeController implementation.
- syncer::ModelSafeGroup model_safe_group() const override;
-
private:
- // NonUIDataTypeController implementation.
- bool PostTaskOnBackendThread(const tracked_objects::Location& from_here,
- const base::Closure& task) override;
+ // AsyncDirectoryTypeController implementation.
bool StartModels() override;
void StopModels() override;
bool ReadyForStart() const override;
@@ -46,12 +41,6 @@ class AutofillWalletDataTypeController
// Returns true if the prefs are set such that wallet sync should be enabled.
bool IsEnabled();
- // A reference to the DB thread's task runner.
- const scoped_refptr<base::SingleThreadTaskRunner> db_thread_;
-
- // A pointer to the sync client.
- syncer::SyncClient* const sync_client_;
-
// Whether the database loaded callback has been registered.
bool callback_registered_;
diff --git a/chromium/components/autofill/core/browser/credit_card.cc b/chromium/components/autofill/core/browser/credit_card.cc
index 75a34c75a26..3b402eae9e8 100644
--- a/chromium/components/autofill/core/browser/credit_card.cc
+++ b/chromium/components/autofill/core/browser/credit_card.cc
@@ -39,12 +39,15 @@ using base::ASCIIToUTF16;
namespace autofill {
-const base::char16 kMidlineEllipsis[] = { 0x22ef, 0 };
+const base::char16 kMidlineEllipsis[] = { 0x0020, 0x0020,
+ 0x2022, 0x2006,
+ 0x2022, 0x2006,
+ 0x2022, 0x2006,
+ 0x2022, 0x2006, 0 };
namespace {
const base::char16 kCreditCardObfuscationSymbol = '*';
-const base::char16 kNonBreakingSpace[] = { 0x00a0, 0 };
bool ConvertYear(const base::string16& year, int* num) {
// If the |year| is empty, clear the stored value.
@@ -72,6 +75,8 @@ base::string16 TypeForFill(const std::string& type) {
return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_JCB);
if (type == kMasterCard)
return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_MASTERCARD);
+ if (type == kMirCard)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_MIR);
if (type == kUnionPay)
return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_UNION_PAY);
if (type == kVisaCard)
@@ -130,13 +135,15 @@ int CreditCard::IconResourceId(const std::string& type) {
if (type == kAmericanExpressCard)
return IDR_AUTOFILL_CC_AMEX;
if (type == kDinersCard)
- return IDR_AUTOFILL_CC_GENERIC;
+ return IDR_AUTOFILL_CC_DINERS;
if (type == kDiscoverCard)
return IDR_AUTOFILL_CC_DISCOVER;
if (type == kJCBCard)
- return IDR_AUTOFILL_CC_GENERIC;
+ return IDR_AUTOFILL_CC_JCB;
if (type == kMasterCard)
return IDR_AUTOFILL_CC_MASTERCARD;
+ if (type == kMirCard)
+ return IDR_AUTOFILL_CC_MIR;
if (type == kUnionPay)
return IDR_AUTOFILL_CC_GENERIC;
if (type == kVisaCard)
@@ -188,6 +195,9 @@ const char* CreditCard::GetCreditCardType(const base::string16& number) {
if (!base::StringToInt(number.substr(0, 2), &first_two_digits))
return kGenericCard;
+ if (first_two_digits == 22)
+ return kMirCard;
+
if (first_two_digits == 34 || first_two_digits == 37)
return kAmericanExpressCard;
@@ -492,8 +502,7 @@ base::string16 CreditCard::TypeAndLastFourDigits() const {
return type;
// TODO(estade): i18n?
- return type + base::string16(kNonBreakingSpace) +
- base::string16(kMidlineEllipsis) + digits;
+ return type + base::string16(kMidlineEllipsis) + digits;
}
base::string16 CreditCard::AbbreviatedExpirationDateForDisplay() const {
@@ -865,6 +874,7 @@ const char kDiscoverCard[] = "discoverCC";
const char kGenericCard[] = "genericCC";
const char kJCBCard[] = "jcbCC";
const char kMasterCard[] = "masterCardCC";
+const char kMirCard[] = "mirCC";
const char kUnionPay[] = "unionPayCC";
const char kVisaCard[] = "visaCC";
diff --git a/chromium/components/autofill/core/browser/credit_card.h b/chromium/components/autofill/core/browser/credit_card.h
index 1e4efd12f37..d0a8a6eee7e 100644
--- a/chromium/components/autofill/core/browser/credit_card.h
+++ b/chromium/components/autofill/core/browser/credit_card.h
@@ -266,6 +266,7 @@ extern const char kDiscoverCard[];
extern const char kGenericCard[];
extern const char kJCBCard[];
extern const char kMasterCard[];
+extern const char kMirCard[];
extern const char kUnionPay[];
extern const char kVisaCard[];
diff --git a/chromium/components/autofill/core/browser/credit_card_field_unittest.cc b/chromium/components/autofill/core/browser/credit_card_field_unittest.cc
index b6cf5757216..5b44a651d9c 100644
--- a/chromium/components/autofill/core/browser/credit_card_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/credit_card_field_unittest.cc
@@ -5,10 +5,10 @@
#include "components/autofill/core/browser/credit_card_field.h"
#include <memory>
+#include <vector>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_scanner.h"
@@ -25,14 +25,14 @@ class CreditCardFieldTest : public testing::Test {
~CreditCardFieldTest() override {}
protected:
- ScopedVector<AutofillField> list_;
+ std::vector<std::unique_ptr<AutofillField>> list_;
std::unique_ptr<const CreditCardField> field_;
FieldCandidatesMap field_candidates_map_;
// Parses the contents of |list_| as a form, and stores the result into
// |field_|.
void Parse() {
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
std::unique_ptr<FormField> field = CreditCardField::Parse(&scanner);
field_ = base::WrapUnique(static_cast<CreditCardField*>(field.release()));
}
@@ -40,7 +40,7 @@ class CreditCardFieldTest : public testing::Test {
void MultipleParses() {
std::unique_ptr<FormField> field;
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
while (!scanner.IsEnd()) {
field = CreditCardField::Parse(&scanner);
field_ = base::WrapUnique(static_cast<CreditCardField*>(field.release()));
@@ -68,7 +68,7 @@ TEST_F(CreditCardFieldTest, Empty) {
}
TEST_F(CreditCardFieldTest, NonParse) {
- list_.push_back(new AutofillField);
+ list_.push_back(base::MakeUnique<AutofillField>());
Parse();
ASSERT_EQ(nullptr, field_.get());
}
@@ -79,11 +79,13 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNoNumber) {
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month1")));
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year2")));
Parse();
ASSERT_EQ(nullptr, field_.get());
@@ -95,7 +97,8 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNoDate) {
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number1")));
Parse();
ASSERT_EQ(nullptr, field_.get());
@@ -107,15 +110,18 @@ TEST_F(CreditCardFieldTest, ParseMiniumCreditCard) {
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number1")));
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month2")));
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year3")));
Parse();
ASSERT_NE(nullptr, field_.get());
@@ -140,30 +146,32 @@ TEST_F(CreditCardFieldTest, ParseFullCreditCard) {
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name")));
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number")));
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month")));
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year")));
field.label = ASCIIToUTF16("Verification");
field.name = ASCIIToUTF16("verification");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("cvc")));
field.form_control_type = "select-one";
field.label = ASCIIToUTF16("Card Type");
field.name = ASCIIToUTF16("card_type");
field.option_contents.push_back(ASCIIToUTF16("visa"));
field.option_values.push_back(ASCIIToUTF16("visa"));
- list_.push_back(new AutofillField(field, ASCIIToUTF16("type")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("type")));
Parse();
ASSERT_NE(nullptr, field_.get());
@@ -200,19 +208,23 @@ TEST_F(CreditCardFieldTest, ParseExpMonthYear) {
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number2")));
field.label = ASCIIToUTF16("ExpDate Month / Year");
field.name = ASCIIToUTF16("ExpDate");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month3")));
field.label = ASCIIToUTF16("ExpDate Month / Year");
field.name = ASCIIToUTF16("ExpDate");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year4")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year4")));
Parse();
ASSERT_NE(nullptr, field_.get());
@@ -241,19 +253,23 @@ TEST_F(CreditCardFieldTest, ParseExpMonthYear2) {
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number2")));
field.label = ASCIIToUTF16("Expiration date Month / Year");
field.name = ASCIIToUTF16("ExpDate");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month3")));
field.label = ASCIIToUTF16("Expiration date Month / Year");
field.name = ASCIIToUTF16("ExpDate");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year4")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year4")));
Parse();
ASSERT_NE(nullptr, field_.get());
@@ -343,18 +359,21 @@ TEST_F(CreditCardFieldTest, ParseExpField) {
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("num2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("num2")));
field.label = ASCIIToUTF16(test_case.label);
if (test_case.max_length != 0) {
field.max_length = test_case.max_length;
}
field.name = ASCIIToUTF16("cc_exp");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("exp3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("exp3")));
Parse();
@@ -397,7 +416,8 @@ TEST_F(CreditCardFieldTest, ParseCreditCardHolderNameWithCCFullName) {
field.label = ASCIIToUTF16("Name");
field.name = ASCIIToUTF16("ccfullname");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
Parse();
ASSERT_NE(nullptr, field_.get());
@@ -415,12 +435,14 @@ TEST_F(CreditCardFieldTest, ParseMonthControl) {
field.form_control_type = "text";
field.label = ASCIIToUTF16("Card number:");
field.name = ASCIIToUTF16("ccnumber");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number1")));
field.form_control_type = "month";
field.label = ASCIIToUTF16("Expiration date:");
field.name = ASCIIToUTF16("ccexp");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("date2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("date2")));
Parse();
ASSERT_NE(nullptr, field_.get());
@@ -443,15 +465,17 @@ TEST_F(CreditCardFieldTest, ParseCreditCardExpYear_2DigitMaxLength) {
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number")));
field.label = ASCIIToUTF16("Expiration Date");
field.name = ASCIIToUTF16("ccmonth");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month")));
field.name = ASCIIToUTF16("ccyear");
field.max_length = 2;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year")));
Parse();
ASSERT_NE(nullptr, field_.get());
@@ -477,17 +501,20 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNumberWithSplit) {
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number_q1");
field.max_length = 4;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number1")));
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number_q2");
field.max_length = 4;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number2")));
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number_q3");
field.max_length = 4;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number3")));
// For last credit card number input field it simply ignores the |max_length|
// attribute. So even having a very big number, does not conside it an invalid
@@ -495,15 +522,18 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNumberWithSplit) {
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number_q4");
field.max_length = 20;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number4")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number4")));
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month5")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month5")));
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year6")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year6")));
Parse();
ASSERT_NE(nullptr, field_.get());
@@ -549,23 +579,28 @@ TEST_F(CreditCardFieldTest, ParseMultipleCreditCardNumbers) {
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number2")));
field.label = ASCIIToUTF16("Confirm Card Number");
field.name = ASCIIToUTF16("confirm_card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number3")));
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month4")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month4")));
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year5")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year5")));
Parse();
ASSERT_NE(nullptr, field_.get());
@@ -599,23 +634,28 @@ TEST_F(CreditCardFieldTest, ParseFirstAndLastNames) {
field.label = ASCIIToUTF16("First Name on Card");
field.name = ASCIIToUTF16("cc-fname");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("cc-lname");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number3")));
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month4")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month4")));
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year5")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year5")));
Parse();
ASSERT_NE(nullptr, field_.get());
@@ -649,27 +689,29 @@ TEST_F(CreditCardFieldTest, ParseConsecutiveCvc) {
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name")));
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number")));
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month")));
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year")));
field.label = ASCIIToUTF16("Verification");
field.name = ASCIIToUTF16("verification");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("cvc")));
field.label = ASCIIToUTF16("Verification");
field.name = ASCIIToUTF16("verification");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc2")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("cvc2")));
MultipleParses();
@@ -705,31 +747,34 @@ TEST_F(CreditCardFieldTest, ParseNonConsecutiveCvc) {
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name")));
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("number")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number")));
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("month")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month")));
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("year")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year")));
field.label = ASCIIToUTF16("Verification");
field.name = ASCIIToUTF16("verification");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("cvc")));
field.label = ASCIIToUTF16("Unknown");
field.name = ASCIIToUTF16("unknown");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("unknown")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("unknown")));
field.label = ASCIIToUTF16("Verification");
field.name = ASCIIToUTF16("verification");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc2")));
+ list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("cvc2")));
MultipleParses();
diff --git a/chromium/components/autofill/core/browser/credit_card_unittest.cc b/chromium/components/autofill/core/browser/credit_card_unittest.cc
index 3f481ee956d..de26d8e56d3 100644
--- a/chromium/components/autofill/core/browser/credit_card_unittest.cc
+++ b/chromium/components/autofill/core/browser/credit_card_unittest.cc
@@ -59,6 +59,13 @@ const char* const kInvalidNumbers[] = {
"3056 9309 0259 04aa", /* non-digit characters */
};
+const std::string kUTF8MidlineEllipsis =
+ " "
+ "\xE2\x80\xA2\xE2\x80\x86"
+ "\xE2\x80\xA2\xE2\x80\x86"
+ "\xE2\x80\xA2\xE2\x80\x86"
+ "\xE2\x80\xA2\xE2\x80\x86";
+
} // namespace
// Tests credit card summary string generation. This test simulates a variety
@@ -93,14 +100,10 @@ TEST(CreditCardTest, PreviewSummaryAndTypeAndLastFourDigitsStrings) {
test::SetCreditCardInfo(
&credit_card2, "John Dillinger", "5105 1051 0510 5100", "", "2010");
base::string16 summary2 = credit_card2.Label();
- EXPECT_EQ(UTF8ToUTF16(
- "MasterCard\xC2\xA0\xE2\x8B\xAF"
- "5100"),
+ EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"),
summary2);
base::string16 obfuscated2 = credit_card2.TypeAndLastFourDigits();
- EXPECT_EQ(UTF8ToUTF16(
- "MasterCard\xC2\xA0\xE2\x8B\xAF"
- "5100"),
+ EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"),
obfuscated2);
// Case 3: No year.
@@ -108,14 +111,10 @@ TEST(CreditCardTest, PreviewSummaryAndTypeAndLastFourDigitsStrings) {
test::SetCreditCardInfo(
&credit_card3, "John Dillinger", "5105 1051 0510 5100", "01", "");
base::string16 summary3 = credit_card3.Label();
- EXPECT_EQ(UTF8ToUTF16(
- "MasterCard\xC2\xA0\xE2\x8B\xAF"
- "5100"),
+ EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"),
summary3);
base::string16 obfuscated3 = credit_card3.TypeAndLastFourDigits();
- EXPECT_EQ(UTF8ToUTF16(
- "MasterCard\xC2\xA0\xE2\x8B\xAF"
- "5100"),
+ EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"),
obfuscated3);
// Case 4: Have everything.
@@ -123,14 +122,10 @@ TEST(CreditCardTest, PreviewSummaryAndTypeAndLastFourDigitsStrings) {
test::SetCreditCardInfo(
&credit_card4, "John Dillinger", "5105 1051 0510 5100", "01", "2010");
base::string16 summary4 = credit_card4.Label();
- EXPECT_EQ(UTF8ToUTF16(
- "MasterCard\xC2\xA0\xE2\x8B\xAF"
- "5100, 01/2010"),
+ EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100, 01/2010"),
summary4);
base::string16 obfuscated4 = credit_card4.TypeAndLastFourDigits();
- EXPECT_EQ(UTF8ToUTF16(
- "MasterCard\xC2\xA0\xE2\x8B\xAF"
- "5100"),
+ EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"),
obfuscated4);
// Case 5: Very long credit card
@@ -140,14 +135,10 @@ TEST(CreditCardTest, PreviewSummaryAndTypeAndLastFourDigitsStrings) {
"John Dillinger",
"0123456789 0123456789 0123456789 5105 1051 0510 5100", "01", "2010");
base::string16 summary5 = credit_card5.Label();
- EXPECT_EQ(UTF8ToUTF16(
- "Card\xC2\xA0\xE2\x8B\xAF"
- "5100, 01/2010"),
+ EXPECT_EQ(UTF8ToUTF16("Card" + kUTF8MidlineEllipsis + "5100, 01/2010"),
summary5);
base::string16 obfuscated5 = credit_card5.TypeAndLastFourDigits();
- EXPECT_EQ(UTF8ToUTF16(
- "Card\xC2\xA0\xE2\x8B\xAF"
- "5100"),
+ EXPECT_EQ(UTF8ToUTF16("Card" + kUTF8MidlineEllipsis + "5100"),
obfuscated5);
}
@@ -378,14 +369,16 @@ TEST(CreditCardTest, Compare) {
TEST(CreditCardTest, IconResourceId) {
EXPECT_EQ(IDR_AUTOFILL_CC_AMEX,
CreditCard::IconResourceId(kAmericanExpressCard));
- EXPECT_EQ(IDR_AUTOFILL_CC_GENERIC,
+ EXPECT_EQ(IDR_AUTOFILL_CC_DINERS,
CreditCard::IconResourceId(kDinersCard));
EXPECT_EQ(IDR_AUTOFILL_CC_DISCOVER,
CreditCard::IconResourceId(kDiscoverCard));
- EXPECT_EQ(IDR_AUTOFILL_CC_GENERIC,
+ EXPECT_EQ(IDR_AUTOFILL_CC_JCB,
CreditCard::IconResourceId(kJCBCard));
EXPECT_EQ(IDR_AUTOFILL_CC_MASTERCARD,
CreditCard::IconResourceId(kMasterCard));
+ EXPECT_EQ(IDR_AUTOFILL_CC_MIR,
+ CreditCard::IconResourceId(kMirCard));
EXPECT_EQ(IDR_AUTOFILL_CC_VISA,
CreditCard::IconResourceId(kVisaCard));
}
@@ -659,6 +652,9 @@ TEST(CreditCardTest, GetCreditCardType) {
{ "6247130048162403", kUnionPay, true },
{ "6247130048162403", kUnionPay, true },
{ "622384452162063648", kUnionPay, true },
+ { "2204883716636153", kMirCard, true },
+ { "2200111234567898", kMirCard, true },
+ { "2200481349288130", kMirCard, true },
// Empty string
{ std::string(), kGenericCard, false },
@@ -670,13 +666,16 @@ TEST(CreditCardTest, GetCreditCardType) {
// Fails Luhn check.
{ "4111111111111112", kVisaCard, false },
{ "6247130048162413", kUnionPay, false },
+ { "2204883716636154", kMirCard, false },
// Invalid length.
{ "3434343434343434", kAmericanExpressCard, false },
{ "411111111111116", kVisaCard, false },
+ { "220011123456783", kMirCard, false },
// Issuer Identification Numbers (IINs) that Chrome recognizes.
{ "4", kVisaCard, false },
+ { "22", kMirCard, false },
{ "34", kAmericanExpressCard, false },
{ "37", kAmericanExpressCard, false },
{ "300", kDinersCard, false },
@@ -708,6 +707,7 @@ TEST(CreditCardTest, GetCreditCardType) {
{ "62", kUnionPay, false },
// Not enough data to determine an IIN uniquely.
+ { "2", kGenericCard, false },
{ "3", kGenericCard, false },
{ "30", kGenericCard, false },
{ "309", kGenericCard, false },
@@ -721,7 +721,6 @@ TEST(CreditCardTest, GetCreditCardType) {
// Unknown IINs.
{ "0", kGenericCard, false },
{ "1", kGenericCard, false },
- { "2", kGenericCard, false },
{ "306", kGenericCard, false },
{ "307", kGenericCard, false },
{ "308", kGenericCard, false },
diff --git a/chromium/components/autofill/core/browser/field_types.h b/chromium/components/autofill/core/browser/field_types.h
index 7dee4279e2e..6c2aa807752 100644
--- a/chromium/components/autofill/core/browser/field_types.h
+++ b/chromium/components/autofill/core/browser/field_types.h
@@ -159,9 +159,13 @@ enum ServerFieldType {
// for local heuristics.
PROBABLY_ACCOUNT_CREATION_PASSWORD = 94,
+ // The confirmation password field in account creation or change password
+ // forms.
+ CONFIRMATION_PASSWORD = 95,
+
// No new types can be added without a corresponding change to the Autofill
// server.
- MAX_VALID_FIELD_TYPE = 95,
+ MAX_VALID_FIELD_TYPE = 96,
};
// The list of all HTML autocomplete field type hints supported by Chrome.
diff --git a/chromium/components/autofill/core/browser/form_field.cc b/chromium/components/autofill/core/browser/form_field.cc
index 3d31c0bd59f..a1997ebbb36 100644
--- a/chromium/components/autofill/core/browser/form_field.cc
+++ b/chromium/components/autofill/core/browser/form_field.cc
@@ -27,19 +27,6 @@
#include "components/autofill/core/common/autofill_util.h"
namespace autofill {
-namespace {
-
-bool ShouldBeProcessed(const AutofillField* field) {
- // Ignore checkable fields as they interfere with parsers assuming context.
- // Eg., while parsing address, "Is PO box" checkbox after ADDRESS_LINE1
- // interferes with correctly understanding ADDRESS_LINE2.
- // Ignore fields marked as presentational. See
- // http://www.w3.org/TR/wai-aria/roles#presentation
- return !(IsCheckable(field->check_status) ||
- field->role == FormFieldData::ROLE_ATTRIBUTE_PRESENTATION);
-}
-
-} // namespace
// There's an implicit precedence determined by the values assigned here. Email
// is currently the most important followed by Phone, Address, Credit Card and
@@ -52,12 +39,22 @@ const float FormField::kBaseNameParserScore = 1.0f;
// static
FieldCandidatesMap FormField::ParseFormFields(
- const std::vector<AutofillField*>& fields,
+ const std::vector<std::unique_ptr<AutofillField>>& fields,
bool is_form_tag) {
// Set up a working copy of the fields to be processed.
std::vector<AutofillField*> processed_fields;
- std::copy_if(fields.begin(), fields.end(),
- std::back_inserter(processed_fields), ShouldBeProcessed);
+ for (const auto& field : fields) {
+ // Ignore checkable fields as they interfere with parsers assuming context.
+ // Eg., while parsing address, "Is PO box" checkbox after ADDRESS_LINE1
+ // interferes with correctly understanding ADDRESS_LINE2.
+ // Ignore fields marked as presentational. See
+ // http://www.w3.org/TR/wai-aria/roles#presentation
+ if (IsCheckable(field->check_status) ||
+ field->role == FormFieldData::ROLE_ATTRIBUTE_PRESENTATION) {
+ continue;
+ }
+ processed_fields.push_back(field.get());
+ }
FieldCandidatesMap field_candidates;
@@ -175,7 +172,7 @@ void FormField::ParseFormFieldsPass(ParseFunction parse,
FieldCandidatesMap* field_candidates) {
AutofillScanner scanner(fields);
while (!scanner.IsEnd()) {
- std::unique_ptr<FormField> form_field(parse(&scanner));
+ std::unique_ptr<FormField> form_field = parse(&scanner);
if (form_field == nullptr) {
scanner.Advance();
} else {
diff --git a/chromium/components/autofill/core/browser/form_field.h b/chromium/components/autofill/core/browser/form_field.h
index 2d7dcd7dbf4..3ec73b4cf3a 100644
--- a/chromium/components/autofill/core/browser/form_field.h
+++ b/chromium/components/autofill/core/browser/form_field.h
@@ -30,7 +30,7 @@ class FormField {
// Each field has a derived unique name that is used as the key into the
// returned FieldCandidatesMap.
static FieldCandidatesMap ParseFormFields(
- const std::vector<AutofillField*>& fields,
+ const std::vector<std::unique_ptr<AutofillField>>& fields,
bool is_form_tag);
protected:
diff --git a/chromium/components/autofill/core/browser/form_field_unittest.cc b/chromium/components/autofill/core/browser/form_field_unittest.cc
index 71e5e12d476..8962409833c 100644
--- a/chromium/components/autofill/core/browser/form_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_field_unittest.cc
@@ -2,7 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/memory/scoped_vector.h"
+#include <memory>
+#include <vector>
+
+#include "base/memory/ptr_util.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_field.h"
@@ -124,32 +127,36 @@ TEST(FormFieldTest, Match) {
// Test that we ignore checkable elements.
TEST(FormFieldTest, ParseFormFields) {
- ScopedVector<AutofillField> fields;
+ std::vector<std::unique_ptr<AutofillField>> fields;
FormFieldData field_data;
field_data.form_control_type = "text";
field_data.label = ASCIIToUTF16("Address line1");
- fields.push_back(new AutofillField(field_data, field_data.label));
+ fields.push_back(
+ base::MakeUnique<AutofillField>(field_data, field_data.label));
field_data.check_status = FormFieldData::CHECKABLE_BUT_UNCHECKED;
field_data.label = ASCIIToUTF16("Is PO Box");
- fields.push_back(new AutofillField(field_data, field_data.label));
+ fields.push_back(
+ base::MakeUnique<AutofillField>(field_data, field_data.label));
// reset |is_checkable| to false.
field_data.check_status = FormFieldData::NOT_CHECKABLE;
field_data.label = ASCIIToUTF16("Address line2");
- fields.push_back(new AutofillField(field_data, field_data.label));
+ fields.push_back(
+ base::MakeUnique<AutofillField>(field_data, field_data.label));
// Does not parse since there are only 2 recognized fields.
- ASSERT_TRUE(FormField::ParseFormFields(fields.get(), true).empty());
+ ASSERT_TRUE(FormField::ParseFormFields(fields, true).empty());
field_data.label = ASCIIToUTF16("City");
- fields.push_back(new AutofillField(field_data, field_data.label));
+ fields.push_back(
+ base::MakeUnique<AutofillField>(field_data, field_data.label));
// Checkable element shouldn't interfere with inference of Address line2.
const FieldCandidatesMap field_candidates_map =
- FormField::ParseFormFields(fields.get(), true);
+ FormField::ParseFormFields(fields, true);
ASSERT_EQ(3U, field_candidates_map.size());
EXPECT_EQ(ADDRESS_HOME_LINE1,
@@ -170,11 +177,11 @@ TEST(FormFieldTest, ParseFormFieldsImmutableForm) {
field_data.form_control_type = "text";
field_data.name = ASCIIToUTF16("business_email_address");
- ScopedVector<AutofillField> fields;
- fields.push_back(new AutofillField(field_data, unique_name));
+ std::vector<std::unique_ptr<AutofillField>> fields;
+ fields.push_back(base::MakeUnique<AutofillField>(field_data, unique_name));
const FieldCandidatesMap field_candidates_map =
- FormField::ParseFormFields(fields.get(), true);
+ FormField::ParseFormFields(fields, true);
// The input form should not be modified.
EXPECT_EQ(1U, fields.size());
diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc
index cf4ae5c7b15..278cc7b6de3 100644
--- a/chromium/components/autofill/core/browser/form_structure.cc
+++ b/chromium/components/autofill/core/browser/form_structure.cc
@@ -13,6 +13,7 @@
#include "base/command_line.h"
#include "base/i18n/case_conversion.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
#include "base/sha1.h"
#include "base/strings/string_number_conversions.h"
@@ -33,8 +34,8 @@
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/form_field_data_predictions.h"
#include "components/autofill/core/common/signatures_util.h"
-#include "components/rappor/rappor_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
namespace autofill {
namespace {
@@ -321,7 +322,7 @@ FormStructure::FormStructure(const FormData& form)
base::string16 unique_name =
field.name + base::ASCIIToUTF16("_") +
base::SizeTToString16(++unique_names[field.name]);
- fields_.push_back(new AutofillField(field, unique_name));
+ fields_.push_back(base::MakeUnique<AutofillField>(field, unique_name));
}
form_signature_ = autofill::CalculateFormSignature(form);
@@ -345,8 +346,8 @@ void FormStructure::DetermineHeuristicTypes() {
if (active_field_count() >= kRequiredFieldsForPredictionRoutines &&
(is_form_tag_ || is_formless_checkout_)) {
const FieldCandidatesMap field_type_map =
- FormField::ParseFormFields(fields_.get(), is_form_tag_);
- for (AutofillField* field : fields_) {
+ FormField::ParseFormFields(fields_, is_form_tag_);
+ for (const auto& field : fields_) {
const auto iter = field_type_map.find(field->unique_name());
if (iter != field_type_map.end())
field->set_heuristic_type(iter->second.BestHeuristicType());
@@ -379,7 +380,7 @@ bool FormStructure::EncodeUploadRequest(
// Verify that |available_field_types| agrees with the possible field types we
// are uploading.
- for (const AutofillField* field : *this) {
+ for (const auto& field : *this) {
for (const auto& type : field->possible_types()) {
DCHECK(type == UNKNOWN_TYPE || type == EMPTY_TYPE ||
available_field_types.count(type));
@@ -442,9 +443,10 @@ bool FormStructure::EncodeQueryRequest(
}
// static
-void FormStructure::ParseQueryResponse(std::string payload,
- const std::vector<FormStructure*>& forms,
- rappor::RapporService* rappor_service) {
+void FormStructure::ParseQueryResponse(
+ std::string payload,
+ const std::vector<FormStructure*>& forms,
+ rappor::RapporServiceImpl* rappor_service) {
AutofillMetrics::LogServerQueryMetric(
AutofillMetrics::QUERY_RESPONSE_RECEIVED);
@@ -467,7 +469,7 @@ void FormStructure::ParseQueryResponse(std::string payload,
response.upload_required() ? UPLOAD_REQUIRED : UPLOAD_NOT_REQUIRED;
bool query_response_has_no_server_data = true;
- for (AutofillField* field : form->fields_) {
+ for (auto& field : form->fields_) {
if (form->ShouldSkipField(*field))
continue;
@@ -532,7 +534,7 @@ std::vector<FormDataPredictions> FormStructure::GetFieldTypePredictions(
form.data.is_form_tag = form_structure->is_form_tag_;
form.signature = form_structure->FormSignatureAsStr();
- for (const AutofillField* field : form_structure->fields_) {
+ for (const auto& field : form_structure->fields_) {
form.data.fields.push_back(FormFieldData(*field));
FormFieldDataPredictions annotated_field;
@@ -573,7 +575,7 @@ bool FormStructure::IsAutofillable() const {
bool FormStructure::IsCompleteCreditCardForm() const {
bool found_cc_number = false;
bool found_cc_expiration = false;
- for (const AutofillField* field : fields_) {
+ for (const auto& field : fields_) {
ServerFieldType type = field->Type().GetStorableType();
if (!found_cc_expiration && IsCreditCardExpirationType(type)) {
found_cc_expiration = true;
@@ -588,7 +590,7 @@ bool FormStructure::IsCompleteCreditCardForm() const {
void FormStructure::UpdateAutofillCount() {
autofill_count_ = 0;
- for (const AutofillField* field : *this) {
+ for (const auto& field : *this) {
if (field && field->IsFieldFillable())
++autofill_count_;
}
@@ -609,7 +611,7 @@ bool FormStructure::ShouldBeParsed() const {
return false;
bool has_text_field = false;
- for (const AutofillField* it : *this) {
+ for (const auto& it : *this) {
has_text_field |= it->form_control_type != "select-one";
}
@@ -626,11 +628,11 @@ void FormStructure::UpdateFromCache(const FormStructure& cached_form) {
// Map from field signatures to cached fields.
std::map<std::string, const AutofillField*> cached_fields;
for (size_t i = 0; i < cached_form.field_count(); ++i) {
- const AutofillField* field = cached_form.field(i);
+ const auto& field = cached_form.field(i);
cached_fields[field->FieldSignatureAsStr()] = field;
}
- for (AutofillField* field : *this) {
+ for (auto& field : *this) {
std::map<std::string, const AutofillField*>::const_iterator cached_field =
cached_fields.find(field->FieldSignatureAsStr());
if (cached_field != cached_fields.end()) {
@@ -669,7 +671,7 @@ void FormStructure::UpdateFromCache(const FormStructure& cached_form) {
void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time,
const base::TimeTicks& interaction_time,
const base::TimeTicks& submission_time,
- rappor::RapporService* rappor_service,
+ rappor::RapporServiceImpl* rappor_service,
bool did_show_suggestions,
bool observed_submission) const {
size_t num_detected_field_types = 0;
@@ -679,7 +681,7 @@ void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time,
bool did_autofill_all_possible_fields = true;
bool did_autofill_some_possible_fields = false;
for (size_t i = 0; i < field_count(); ++i) {
- const AutofillField* field = this->field(i);
+ const auto& 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
@@ -845,7 +847,7 @@ void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time,
}
void FormStructure::LogQualityMetricsBasedOnAutocomplete() const {
- for (const AutofillField* field : fields_) {
+ 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.
@@ -886,7 +888,7 @@ void FormStructure::ParseFieldTypesFromAutocompleteAttributes() {
has_author_specified_types_ = false;
has_author_specified_sections_ = false;
- for (AutofillField* field : fields_) {
+ for (const auto& field : fields_) {
// To prevent potential section name collisions, add a default suffix for
// other fields. Without this, 'autocomplete' attribute values
// "section--shipping street-address" and "shipping street-address" would be
@@ -1006,7 +1008,7 @@ bool FormStructure::FillFields(
std::set<base::string16> FormStructure::PossibleValues(ServerFieldType type) {
std::set<base::string16> values;
AutofillType target_type(type);
- for (const AutofillField* field : fields_) {
+ for (const auto& field : fields_) {
if (field->Type().GetStorableType() != target_type.GetStorableType() ||
field->Type().group() != target_type.group()) {
continue;
@@ -1034,7 +1036,7 @@ std::set<base::string16> FormStructure::PossibleValues(ServerFieldType type) {
base::string16 FormStructure::GetUniqueValue(HtmlFieldType type) const {
base::string16 value;
- for (const AutofillField* field : fields_) {
+ for (const auto& field : fields_) {
if (field->html_type() != type)
continue;
@@ -1056,7 +1058,7 @@ const AutofillField* FormStructure::field(size_t index) const {
return NULL;
}
- return fields_[index];
+ return fields_[index].get();
}
AutofillField* FormStructure::field(size_t index) {
@@ -1107,7 +1109,7 @@ void FormStructure::EncodeFormForQuery(
DCHECK(!IsMalformed());
query_form->set_signature(form_signature());
- for (const AutofillField* field : fields_) {
+ for (const auto& field : fields_) {
if (ShouldSkipField(*field))
continue;
@@ -1128,7 +1130,7 @@ void FormStructure::EncodeFormForQuery(
void FormStructure::EncodeFormForUpload(AutofillUploadContents* upload) const {
DCHECK(!IsMalformed());
- for (const AutofillField* field : fields_) {
+ for (const auto& field : fields_) {
// Don't upload checkable fields.
if (IsCheckable(field->check_status))
continue;
@@ -1196,7 +1198,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) {
std::set<ServerFieldType> seen_types;
ServerFieldType previous_type = UNKNOWN_TYPE;
- for (AutofillField* field : fields_) {
+ for (const auto& field : fields_) {
const ServerFieldType current_type = field->Type().GetStorableType();
bool already_saw_current_type = seen_types.count(current_type) > 0;
@@ -1250,7 +1252,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) {
// Ensure that credit card and address fields are in separate sections.
// This simplifies the section-aware logic in autofill_manager.cc.
- for (AutofillField* field : fields_) {
+ for (const auto& field : fields_) {
FieldTypeGroup field_type_group = field->Type().group();
if (field_type_group == CREDIT_CARD)
field->set_section(field->section() + "-cc");
@@ -1272,7 +1274,7 @@ void FormStructure::ProcessExtractedFields() {
// Find the longest common prefix within all the field names.
std::vector<base::string16> names;
names.reserve(field_count());
- for (const AutofillField* field : *this)
+ for (const auto& field : *this)
names.push_back(field->name);
const base::string16 longest_prefix = FindLongestCommonPrefix(names);
@@ -1280,7 +1282,7 @@ void FormStructure::ProcessExtractedFields() {
return;
// The name without the prefix will be used for heuristics parsing.
- for (AutofillField* field : *this) {
+ for (auto& field : *this) {
if (field->name.size() > longest_prefix.size()) {
field->set_parseable_name(
field->name.substr(longest_prefix.size(), field->name.size()));
diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h
index e359a8f1f93..09c4ee13b89 100644
--- a/chromium/components/autofill/core/browser/form_structure.h
+++ b/chromium/components/autofill/core/browser/form_structure.h
@@ -7,6 +7,7 @@
#include <stddef.h>
+#include <memory>
#include <set>
#include <string>
#include <vector>
@@ -14,7 +15,6 @@
#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "components/autofill/core/browser/autofill_field.h"
@@ -23,8 +23,6 @@
#include "components/autofill/core/browser/proto/server.pb.h"
#include "url/gurl.h"
-class XmlWriter;
-
enum UploadRequired {
UPLOAD_NOT_REQUIRED,
UPLOAD_REQUIRED,
@@ -35,12 +33,8 @@ namespace base {
class TimeTicks;
}
-namespace buzz {
-class XmlElement;
-}
-
namespace rappor {
-class RapporService;
+class RapporServiceImpl;
}
namespace autofill {
@@ -81,7 +75,7 @@ class FormStructure {
// |rappor_service| may be null.
static void ParseQueryResponse(std::string response,
const std::vector<FormStructure*>& forms,
- rappor::RapporService* rappor_service);
+ rappor::RapporServiceImpl* rappor_service);
// Returns predictions using the details from the given |form_structures| and
// their fields' predicted types.
@@ -135,7 +129,7 @@ class FormStructure {
void LogQualityMetrics(const base::TimeTicks& load_time,
const base::TimeTicks& interaction_time,
const base::TimeTicks& submission_time,
- rappor::RapporService* rappor_service,
+ rappor::RapporServiceImpl* rappor_service,
bool did_show_suggestions,
bool observed_submission) const;
@@ -194,10 +188,10 @@ class FormStructure {
size_t autofill_count() const { return autofill_count_; }
// Used for iterating over the fields.
- std::vector<AutofillField*>::const_iterator begin() const {
+ std::vector<std::unique_ptr<AutofillField>>::const_iterator begin() const {
return fields_.begin();
}
- std::vector<AutofillField*>::const_iterator end() const {
+ std::vector<std::unique_ptr<AutofillField>>::const_iterator end() const {
return fields_.end();
}
@@ -280,7 +274,7 @@ class FormStructure {
size_t autofill_count_;
// A vector of all the input fields in the form.
- ScopedVector<AutofillField> fields_;
+ std::vector<std::unique_ptr<AutofillField>> fields_;
// The number of fields that are part of the form signature and that are
// included in queries to the Autofill server.
diff --git a/chromium/components/autofill/core/browser/legal_message_line.h b/chromium/components/autofill/core/browser/legal_message_line.h
index 681173c9d58..50955f9569c 100644
--- a/chromium/components/autofill/core/browser/legal_message_line.h
+++ b/chromium/components/autofill/core/browser/legal_message_line.h
@@ -63,9 +63,7 @@ class LegalMessageLine {
// expand correctly.
// 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. It is not possible to create
- // a completely blank line by using two consecutive newlines (they will be
- // treated as a single newline by views::StyledLabel).
+ // string, can be used to separate paragraphs.
static bool Parse(const base::DictionaryValue& legal_message,
LegalMessageLines* out);
diff --git a/chromium/components/autofill/core/browser/name_field_unittest.cc b/chromium/components/autofill/core/browser/name_field_unittest.cc
index a76c15f62b6..584becf107c 100644
--- a/chromium/components/autofill/core/browser/name_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/name_field_unittest.cc
@@ -5,10 +5,10 @@
#include "components/autofill/core/browser/name_field.h"
#include <memory>
+#include <vector>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_scanner.h"
@@ -24,7 +24,7 @@ class NameFieldTest : public testing::Test {
NameFieldTest() {}
protected:
- ScopedVector<AutofillField> list_;
+ std::vector<std::unique_ptr<AutofillField>> list_;
std::unique_ptr<NameField> field_;
FieldCandidatesMap field_candidates_map_;
@@ -44,17 +44,20 @@ TEST_F(NameFieldTest, FirstMiddleLast) {
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("First");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = ASCIIToUTF16("Middle Name");
field.name = ASCIIToUTF16("Middle");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("Last");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -78,17 +81,20 @@ TEST_F(NameFieldTest, FirstMiddleLast2) {
field.label = base::string16();
field.name = ASCIIToUTF16("firstName");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = base::string16();
field.name = ASCIIToUTF16("middleName");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
field.label = base::string16();
field.name = ASCIIToUTF16("lastName");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -112,13 +118,15 @@ TEST_F(NameFieldTest, FirstLast) {
field.label = base::string16();
field.name = ASCIIToUTF16("first_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = base::string16();
field.name = ASCIIToUTF16("last_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -138,13 +146,15 @@ TEST_F(NameFieldTest, FirstLast2) {
field.label = ASCIIToUTF16("Name");
field.name = ASCIIToUTF16("first_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = ASCIIToUTF16("Name");
field.name = ASCIIToUTF16("last_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -164,17 +174,20 @@ TEST_F(NameFieldTest, FirstLastMiddleWithSpaces) {
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("first_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = ASCIIToUTF16("Middle Name");
field.name = ASCIIToUTF16("middle_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("last_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -198,13 +211,15 @@ TEST_F(NameFieldTest, FirstLastEmpty) {
field.label = ASCIIToUTF16("Name");
field.name = ASCIIToUTF16("first_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
- field.label = base::string16();
+ field.label = base::string16();
field.name = ASCIIToUTF16("last_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -224,17 +239,20 @@ TEST_F(NameFieldTest, FirstMiddleLastEmpty) {
field.label = ASCIIToUTF16("Name");
field.name = ASCIIToUTF16("first_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = base::string16();
field.name = ASCIIToUTF16("middle_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
field.label = base::string16();
field.name = ASCIIToUTF16("last_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -258,17 +276,20 @@ TEST_F(NameFieldTest, MiddleInitial) {
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("first_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = ASCIIToUTF16("MI");
field.name = ASCIIToUTF16("middle_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("last_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -292,13 +313,15 @@ TEST_F(NameFieldTest, MiddleInitialNoLastName) {
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("first_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = ASCIIToUTF16("MI");
field.name = ASCIIToUTF16("middle_name");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_EQ(nullptr, field_.get());
}
@@ -311,17 +334,20 @@ TEST_F(NameFieldTest, MiddleInitialAtEnd) {
field.label = base::string16();
field.name = ASCIIToUTF16("XXXnameXXXfirst");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1")));
field.label = base::string16();
field.name = ASCIIToUTF16("XXXnameXXXmi");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2")));
field.label = base::string16();
field.name = ASCIIToUTF16("XXXnameXXXlast");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
diff --git a/chromium/components/autofill/core/browser/password_generator.cc b/chromium/components/autofill/core/browser/password_generator.cc
index 86877fb647b..def06733a3e 100644
--- a/chromium/components/autofill/core/browser/password_generator.cc
+++ b/chromium/components/autofill/core/browser/password_generator.cc
@@ -55,7 +55,7 @@ bool VerifyPassword(const std::string& password) {
namespace autofill {
-const int PasswordGenerator::kDefaultPasswordLength = 12;
+const int PasswordGenerator::kDefaultPasswordLength = 15;
void ForceFixPassword(std::string* password) {
for (char& it : *password) {
diff --git a/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc b/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc
index 6fa6a66d947..480f71c8aa4 100644
--- a/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc
@@ -9,6 +9,7 @@
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/credit_card.h"
diff --git a/chromium/components/autofill/core/browser/payments/payments_client.cc b/chromium/components/autofill/core/browser/payments/payments_client.cc
index 32b2cca0857..5fbeca35005 100644
--- a/chromium/components/autofill/core/browser/payments/payments_client.cc
+++ b/chromium/components/autofill/core/browser/payments/payments_client.cc
@@ -458,7 +458,7 @@ void PaymentsClient::OnURLFetchComplete(const net::URLFetcher* source) {
std::string error_code;
std::unique_ptr<base::Value> message_value = base::JSONReader::Read(data);
if (message_value.get() &&
- message_value->IsType(base::Value::TYPE_DICTIONARY)) {
+ message_value->IsType(base::Value::Type::DICTIONARY)) {
response_dict.reset(
static_cast<base::DictionaryValue*>(message_value.release()));
response_dict->GetString("error.code", &error_code);
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc
index 73ba1d35179..88096ec268a 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager.cc
@@ -292,7 +292,15 @@ void PersonalDataManager::Init(scoped_refptr<AutofillWebDataService> database,
LoadCreditCards();
database_->AddObserver(this);
- is_autofill_profile_dedupe_pending_ = IsAutofillProfileCleanupEnabled();
+
+ // Check if profile cleanup has already been performed this major version.
+ is_autofill_profile_cleanup_pending_ =
+ pref_service_->GetInteger(prefs::kAutofillLastVersionDeduped) >=
+ atoi(version_info::GetVersionNumber().c_str());
+ DVLOG(1) << "Autofill profile cleanup "
+ << (is_autofill_profile_cleanup_pending_ ? "needs to be"
+ : "has already been")
+ << " performed for this version";
}
PersonalDataManager::~PersonalDataManager() {
@@ -309,7 +317,7 @@ void PersonalDataManager::OnSyncServiceInitialized(
syncer::SyncService* sync_service) {
// We want to know when, if at all, we need to run autofill profile de-
// duplication: now or after waiting until sync has started.
- if (!is_autofill_profile_dedupe_pending_) {
+ if (!is_autofill_profile_cleanup_pending_) {
// De-duplication isn't enabled.
return;
}
@@ -419,7 +427,8 @@ void PersonalDataManager::AutofillMultipleChanged() {
}
void PersonalDataManager::SyncStarted(syncer::ModelType model_type) {
- if (model_type == syncer::AUTOFILL_PROFILE) {
+ if (model_type == syncer::AUTOFILL_PROFILE &&
+ is_autofill_profile_cleanup_pending_) {
// This runs as a one-time fix, tracked in syncable prefs. If it has already
// run, it is a NOP (other than checking the pref).
ApplyProfileUseDatesFix();
@@ -472,7 +481,7 @@ void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) {
if (credit_card->record_type() == CreditCard::LOCAL_CARD)
database_->UpdateCreditCard(*credit_card);
else
- database_->UpdateServerCardUsageStats(*credit_card);
+ database_->UpdateServerCardMetadata(*credit_card);
Refresh();
return;
@@ -485,7 +494,7 @@ void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) {
if (profile->record_type() == AutofillProfile::LOCAL_PROFILE)
database_->UpdateAutofillProfile(*profile);
else if (profile->record_type() == AutofillProfile::SERVER_PROFILE)
- database_->UpdateServerAddressUsageStats(*profile);
+ database_->UpdateServerAddressMetadata(*profile);
Refresh();
}
@@ -636,29 +645,14 @@ void PersonalDataManager::UpdateServerCreditCard(
Refresh();
}
-void PersonalDataManager::UpdateServerCardBillingAddress(
+void PersonalDataManager::UpdateServerCardMetadata(
const CreditCard& credit_card) {
DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
- if (!database_.get())
- return;
-
- CreditCard* existing_credit_card = nullptr;
- for (auto& server_card : server_credit_cards_) {
- if (credit_card.server_id() == server_card->server_id()) {
- existing_credit_card = server_card.get();
- break;
- }
- }
- if (!existing_credit_card
- || existing_credit_card->billing_address_id() ==
- credit_card.billing_address_id()) {
+ if (is_off_the_record_ || !database_.get())
return;
- }
- existing_credit_card->set_billing_address_id(
- credit_card.billing_address_id());
- database_->UpdateServerCardBillingAddress(*existing_credit_card);
+ database_->UpdateServerCardMetadata(credit_card);
Refresh();
}
@@ -1011,7 +1005,8 @@ std::string PersonalDataManager::MergeProfile(
std::string guid = new_profile.guid();
// If we have already saved this address, merge in any missing values.
- // Only merge with the first match.
+ // Only merge with the first match. Merging the new profile into the existing
+ // one preserves the validity of credit card's billing address reference.
AutofillProfileComparator comparator(app_locale);
for (const auto& existing_profile : *existing_profiles) {
if (!matching_profile_found &&
@@ -1372,7 +1367,7 @@ bool PersonalDataManager::ImportAddressProfiles(const FormStructure& form) {
// Relevant sections for address fields.
std::set<std::string> sections;
- for (const AutofillField* field : form) {
+ for (const auto& field : form) {
if (field->Type().group() != CREDIT_CARD)
sections.insert(field->section());
}
@@ -1409,7 +1404,7 @@ bool PersonalDataManager::ImportAddressProfileForSection(
std::set<ServerFieldType> types_seen;
// Go through each |form| field and attempt to constitute a valid profile.
- for (const AutofillField* field : form) {
+ for (const auto& field : form) {
// Reject fields that are not within the specified |section|.
if (field->section() != section)
continue;
@@ -1489,7 +1484,7 @@ bool PersonalDataManager::ImportCreditCard(
candidate_credit_card.set_origin(form.source_url().spec());
std::set<ServerFieldType> types_seen;
- for (const AutofillField* field : form) {
+ for (const auto& field : form) {
base::string16 value;
base::TrimWhitespace(field->value, base::TRIM_ALL, &value);
@@ -1625,6 +1620,8 @@ std::vector<Suggestion> PersonalDataManager::GetSuggestionsForCards(
suggestion->value = credit_card->TypeAndLastFourDigits();
suggestion->label = credit_card->GetInfo(
AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale_);
+ if (IsAutofillCreditCardPopupLayoutExperimentEnabled())
+ ModifyAutofillCreditCardSuggestion(suggestion);
} else if (credit_card->number().empty()) {
if (type.GetStorableType() != CREDIT_CARD_NAME_FULL) {
suggestion->label = credit_card->GetInfo(
@@ -1678,11 +1675,10 @@ void PersonalDataManager::ApplyProfileUseDatesFix() {
}
bool PersonalDataManager::ApplyDedupingRoutine() {
- if (!is_autofill_profile_dedupe_pending_)
+ if (!is_autofill_profile_cleanup_pending_)
return false;
- DCHECK(IsAutofillProfileCleanupEnabled());
- is_autofill_profile_dedupe_pending_ = false;
+ is_autofill_profile_cleanup_pending_ = false;
// No need to de-duplicate if there are less than two profiles.
if (web_profiles_.size() < 2) {
@@ -1703,9 +1699,13 @@ bool PersonalDataManager::ApplyDedupingRoutine() {
std::unordered_set<AutofillProfile*> profiles_to_delete;
profiles_to_delete.reserve(web_profiles_.size());
- DedupeProfiles(&web_profiles_, &profiles_to_delete);
+ // Create the map used to update credit card's billing addresses after the
+ // dedupe.
+ std::unordered_map<std::string, std::string> guids_merge_map;
+
+ DedupeProfiles(&web_profiles_, &profiles_to_delete, &guids_merge_map);
- // Apply the changes to the database.
+ // Apply the profile changes to the database.
for (const auto& profile : web_profiles_) {
// If the profile was set to be deleted, remove it from the database.
if (profiles_to_delete.count(profile.get())) {
@@ -1716,6 +1716,8 @@ bool PersonalDataManager::ApplyDedupingRoutine() {
}
}
+ UpdateCardsBillingAddressReference(guids_merge_map);
+
// Set the pref to the current major version.
pref_service_->SetInteger(prefs::kAutofillLastVersionDeduped,
current_major_version);
@@ -1728,7 +1730,8 @@ bool PersonalDataManager::ApplyDedupingRoutine() {
void PersonalDataManager::DedupeProfiles(
std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles,
- std::unordered_set<AutofillProfile*>* profiles_to_delete) {
+ std::unordered_set<AutofillProfile*>* profiles_to_delete,
+ std::unordered_map<std::string, std::string>* guids_merge_map) {
AutofillMetrics::LogNumberOfProfilesConsideredForDedupe(
existing_profiles->size());
@@ -1782,6 +1785,11 @@ void PersonalDataManager::DedupeProfiles(
// and will not accept updates from profile_to_merge.
if (existing_profile->SaveAdditionalInfo(*profile_to_merge,
app_locale_)) {
+ // Keep track that a credit card using |profile_to_merge|'s GUID as its
+ // billing address id should replace it by |existing_profile|'s GUID.
+ guids_merge_map->insert(std::pair<std::string, std::string>(
+ profile_to_merge->guid(), existing_profile->guid()));
+
// Since |profile_to_merge| was a duplicate of |existing_profile|
// and was merged successfully, it can now be deleted.
profiles_to_delete->insert(profile_to_merge);
@@ -1801,4 +1809,47 @@ void PersonalDataManager::DedupeProfiles(
profiles_to_delete->size());
}
+void PersonalDataManager::UpdateCardsBillingAddressReference(
+ const std::unordered_map<std::string, std::string>& guids_merge_map) {
+ /* Here is an example of what the graph might look like.
+
+ A -> B
+ \
+ -> E
+ /
+ C -> D
+ */
+
+ for (auto& credit_card : local_credit_cards_) {
+ // If the credit card is not associated with a billing address, skip it.
+ if (credit_card->billing_address_id().empty())
+ break;
+
+ // If the billing address profile associated with the card has been merged,
+ // replace it by the id of the profile in which it was merged. Repeat the
+ // process until the billing address has not been merged into another one.
+ std::unordered_map<std::string, std::string>::size_type nb_guid_changes = 0;
+ bool was_modified = false;
+ auto it = guids_merge_map.find(credit_card->billing_address_id());
+ while (it != guids_merge_map.end()) {
+ was_modified = true;
+ credit_card->set_billing_address_id(it->second);
+ it = guids_merge_map.find(credit_card->billing_address_id());
+
+ // Out of abundance of caution.
+ if (nb_guid_changes > guids_merge_map.size()) {
+ NOTREACHED();
+ // Cancel the changes for that card.
+ was_modified = false;
+ break;
+ }
+ }
+
+ // If the card was modified, apply the changes to the database.
+ if (was_modified) {
+ database_->UpdateCreditCard(*credit_card);
+ }
+ }
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h
index 89508f3a350..165862f6217 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager.h
+++ b/chromium/components/autofill/core/browser/personal_data_manager.h
@@ -9,6 +9,7 @@
#include <memory>
#include <set>
#include <string>
+#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -151,9 +152,9 @@ class PersonalDataManager : public KeyedService,
// status can be changed. Looks up the card by server ID.
virtual void UpdateServerCreditCard(const CreditCard& credit_card);
- // Updates the billing address for the server |credit_card|. Looks up the card
- // by GUID.
- void UpdateServerCardBillingAddress(const CreditCard& credit_card);
+ // Updates the use stats and billing address id for the server |credit_card|.
+ // Looks up the card by server_id.
+ void UpdateServerCardMetadata(const CreditCard& credit_card);
// Resets the card for |guid| to the masked state.
void ResetFullServerCard(const std::string& guid);
@@ -288,10 +289,16 @@ class PersonalDataManager : public KeyedService,
FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillIsEnabledAtStartup);
FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
DedupeProfiles_ProfilesToDelete);
+ FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
+ DedupeProfiles_GuidsMergeMap);
+ FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
+ UpdateCardsBillingAddressReference);
FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, ApplyProfileUseDatesFix);
FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
ApplyProfileUseDatesFix_NotAppliedTwice);
FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
+ ApplyDedupingRoutine_CardsBillingAddressIdUpdated);
+ FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
ApplyDedupingRoutine_MergedProfileValues);
FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
ApplyDedupingRoutine_VerifiedProfileFirst);
@@ -472,20 +479,28 @@ class PersonalDataManager : public KeyedService,
// Applies the deduping routine once per major version if the feature is
// enabled. Calls DedupeProfiles with the content of |web_profiles_| as a
// parameter. Removes the profiles to delete from the database and updates the
- // others. Returns true if the routine was run.
+ // others. Also updates the credit cards' billing address references. Returns
+ // true if the routine was run.
bool ApplyDedupingRoutine();
// Goes through all the |existing_profiles| and merges all similar unverified
// profiles together. Also discards unverified profiles that are similar to a
// verified profile. All the profiles except the results of the merges will be
// added to |profile_guids_to_delete|. This routine should be run once per
- // major version.
+ // major version. Records all the merges into the |guids_merge_map|.
//
// This method should only be called by ApplyDedupingRoutine. It is split for
// testing purposes.
void DedupeProfiles(
std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles,
- std::unordered_set<AutofillProfile*>* profile_guids_to_delete);
+ std::unordered_set<AutofillProfile*>* profile_guids_to_delete,
+ std::unordered_map<std::string, std::string>* guids_merge_map);
+
+ // Updates the credit cards' billing address reference based on the merges
+ // that happened during the dedupe, as defined in |guids_merge_map|. Also
+ // updates the cards entries in the database.
+ void UpdateCardsBillingAddressReference(
+ const std::unordered_map<std::string, std::string>& guids_merge_map);
const std::string app_locale_;
@@ -523,9 +538,8 @@ class PersonalDataManager : public KeyedService,
// An observer to listen for changes to prefs::kAutofillWalletImportEnabled.
std::unique_ptr<BooleanPrefMember> wallet_enabled_pref_;
- // Set to true if autofill profile deduplication is enabled and needs to be
- // performed on the next data refresh.
- bool is_autofill_profile_dedupe_pending_ = false;
+ // True if autofill profile cleanup needs to be performed.
+ bool is_autofill_profile_cleanup_pending_ = false;
#if defined(OS_ANDROID)
// The context for the request to be used to fetch libaddressinput's address
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 49bee42bf2c..18bb0aebdd4 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -15,7 +15,6 @@
#include <vector>
#include "base/command_line.h"
-#include "base/feature_list.h"
#include "base/files/scoped_temp_dir.h"
#include "base/guid.h"
#include "base/memory/ptr_util.h"
@@ -24,7 +23,6 @@
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -61,6 +59,13 @@ namespace {
enum UserMode { USER_MODE_NORMAL, USER_MODE_INCOGNITO };
+const std::string kUTF8MidlineEllipsis =
+ " "
+ "\xE2\x80\xA2\xE2\x80\x86"
+ "\xE2\x80\xA2\xE2\x80\x86"
+ "\xE2\x80\xA2\xE2\x80\x86"
+ "\xE2\x80\xA2\xE2\x80\x86";
+
ACTION(QuitMainMessageLoop) {
base::MessageLoop::current()->QuitWhenIdle();
}
@@ -143,7 +148,6 @@ class PersonalDataManagerTest : public testing::Test {
// There are no field trials enabled by default.
field_trial_list_.reset();
- scoped_feature_list_.reset();
// Reset the deduping pref to its default value.
personal_data_->pref_service_->SetInteger(
@@ -197,9 +201,7 @@ class PersonalDataManagerTest : public testing::Test {
}
void EnableAutofillProfileCleanup() {
- scoped_feature_list_.reset(new base::test::ScopedFeatureList);
- scoped_feature_list_->InitAndEnableFeature(kAutofillProfileCleanup);
- personal_data_->is_autofill_profile_dedupe_pending_ = true;
+ personal_data_->is_autofill_profile_cleanup_pending_ = true;
}
void SetupReferenceProfile() {
@@ -364,7 +366,6 @@ class PersonalDataManagerTest : public testing::Test {
std::unique_ptr<base::FieldTrialList> field_trial_list_;
scoped_refptr<base::FieldTrial> field_trial_;
- std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
};
TEST_F(PersonalDataManagerTest, AddProfile) {
@@ -3697,8 +3698,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_NumberMissing) {
AutofillType(CREDIT_CARD_NUMBER),
/* field_contents= */ base::string16());
ASSERT_EQ(1U, suggestions.size());
- EXPECT_EQ(UTF8ToUTF16("Amex\xC2\xA0\xE2\x8B\xAF"
- "8555"),
+ EXPECT_EQ(UTF8ToUTF16("Amex" + kUTF8MidlineEllipsis + "8555"),
suggestions[0].value);
EXPECT_EQ(ASCIIToUTF16("04/99"), suggestions[0].label);
}
@@ -3761,17 +3761,13 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ServerDuplicates) {
suggestions = personal_data_->GetCreditCardSuggestions(
AutofillType(CREDIT_CARD_NUMBER), /* field_contents= */ base::string16());
ASSERT_EQ(4U, suggestions.size());
- EXPECT_EQ(UTF8ToUTF16("Visa\xC2\xA0\xE2\x8B\xAF"
- "9012"),
+ EXPECT_EQ(UTF8ToUTF16("Visa" + kUTF8MidlineEllipsis + "9012"),
suggestions[0].value);
- EXPECT_EQ(UTF8ToUTF16("Amex\xC2\xA0\xE2\x8B\xAF"
- "8555"),
+ EXPECT_EQ(UTF8ToUTF16("Amex" + kUTF8MidlineEllipsis + "8555"),
suggestions[1].value);
- EXPECT_EQ(UTF8ToUTF16("MasterCard\xC2\xA0\xE2\x8B\xAF"
- "2109"),
+ EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "2109"),
suggestions[2].value);
- EXPECT_EQ(UTF8ToUTF16("Visa\xC2\xA0\xE2\x8B\xAF"
- "2109"),
+ EXPECT_EQ(UTF8ToUTF16("Visa" + kUTF8MidlineEllipsis + "2109"),
suggestions[3].value);
}
@@ -4672,8 +4668,10 @@ TEST_F(PersonalDataManagerTest, DedupeProfiles_ProfilesToDelete) {
EnableAutofillProfileCleanup();
base::HistogramTester histogram_tester;
+ std::unordered_map<std::string, std::string> guids_merge_map;
std::unordered_set<AutofillProfile*> profiles_to_delete;
- personal_data_->DedupeProfiles(&existing_profiles, &profiles_to_delete);
+ personal_data_->DedupeProfiles(&existing_profiles, &profiles_to_delete,
+ &guids_merge_map);
// 5 profiles were considered for dedupe.
histogram_tester.ExpectUniqueSample(
"Autofill.NumberOfProfilesConsideredForDedupe", 5, 1);
@@ -4696,6 +4694,272 @@ TEST_F(PersonalDataManagerTest, DedupeProfiles_ProfilesToDelete) {
EXPECT_EQ(5U, existing_profiles.size());
}
+// Tests that DedupeProfiles sets the correct merge mapping for billing address
+// id references.
+TEST_F(PersonalDataManagerTest, DedupeProfiles_GuidsMergeMap) {
+ // Create the profile for which to find duplicates. It has the highest
+ // frecency.
+ AutofillProfile* profile1 =
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(profile1, "Homer", "Jay", "Simpson",
+ "homer.simpson@abc.com", "", "742. Evergreen Terrace",
+ "", "Springfield", "IL", "91601", "US", "12345678910");
+ profile1->set_use_count(9);
+
+ // Create a different profile that should not be deduped (different address).
+ AutofillProfile* profile2 =
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(profile2, "Homer", "Jay", "Simpson",
+ "homer.simpson@abc.com", "Fox", "1234 Other Street", "",
+ "Springfield", "IL", "91601", "US", "12345678910");
+ profile2->set_use_count(7);
+
+ // Create a profile similar to profile1 which should be deduped.
+ AutofillProfile* profile3 =
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(profile3, "Homer", "Jay", "Simpson",
+ "homer.simpson@abc.com", "", "742 Evergreen Terrace", "",
+ "Springfield", "IL", "91601", "US", "12345678910");
+ profile3->set_use_count(5);
+
+ // Create another different profile that should not be deduped (different
+ // name).
+ AutofillProfile* profile4 =
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(profile4, "Marjorie", "Jacqueline", "Simpson",
+ "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace",
+ "", "Springfield", "IL", "91601", "US", "12345678910");
+ profile4->set_use_count(3);
+
+ // Create another profile similar to profile1. Since that one has the lowest
+ // frecency, the result of the merge should be in this profile at the end of
+ // the test.
+ AutofillProfile* profile5 =
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(profile5, "Homer", "Jay", "Simpson",
+ "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.",
+ "", "Springfield", "IL", "91601", "US", "12345678910");
+ profile5->set_use_count(1);
+
+ // Add the profiles.
+ std::vector<std::unique_ptr<AutofillProfile>> existing_profiles;
+ existing_profiles.push_back(base::WrapUnique(profile1));
+ existing_profiles.push_back(base::WrapUnique(profile2));
+ existing_profiles.push_back(base::WrapUnique(profile3));
+ existing_profiles.push_back(base::WrapUnique(profile4));
+ existing_profiles.push_back(base::WrapUnique(profile5));
+
+ // Enable the profile cleanup.
+ EnableAutofillProfileCleanup();
+
+ std::unordered_map<std::string, std::string> guids_merge_map;
+ std::unordered_set<AutofillProfile*> profiles_to_delete;
+ personal_data_->DedupeProfiles(&existing_profiles, &profiles_to_delete,
+ &guids_merge_map);
+
+ // The two profile merges should be recorded in the map.
+ EXPECT_EQ(2U, guids_merge_map.size());
+
+ // Profile 1 was merged into profile 3.
+ ASSERT_TRUE(guids_merge_map.count(profile1->guid()));
+ EXPECT_TRUE(guids_merge_map.at(profile1->guid()) == profile3->guid());
+
+ // Profile 3 was merged into profile 5.
+ ASSERT_TRUE(guids_merge_map.count(profile3->guid()));
+ EXPECT_TRUE(guids_merge_map.at(profile3->guid()) == profile5->guid());
+}
+
+// Tests that UpdateCardsBillingAddressReference sets the correct billing
+// address id as specified in the map.
+TEST_F(PersonalDataManagerTest, UpdateCardsBillingAddressReference) {
+ /* The merges will be as follow:
+
+ A -> B F (not merged)
+ \
+ -> E
+ /
+ C -> D
+ */
+
+ std::unordered_map<std::string, std::string> guids_merge_map;
+ guids_merge_map.insert(std::pair<std::string, std::string>("A", "B"));
+ guids_merge_map.insert(std::pair<std::string, std::string>("C", "D"));
+ guids_merge_map.insert(std::pair<std::string, std::string>("B", "E"));
+ guids_merge_map.insert(std::pair<std::string, std::string>("D", "E"));
+
+ // Create cards that use A, D, E and F as their billing address id.
+ CreditCard* credit_card1 =
+ new CreditCard(base::GenerateGUID(), "https://www.example.com");
+ credit_card1->set_billing_address_id("A");
+ CreditCard* credit_card2 =
+ new CreditCard(base::GenerateGUID(), "https://www.example.com");
+ credit_card2->set_billing_address_id("D");
+ CreditCard* credit_card3 =
+ new CreditCard(base::GenerateGUID(), "https://www.example.com");
+ credit_card3->set_billing_address_id("E");
+ CreditCard* credit_card4 =
+ new CreditCard(base::GenerateGUID(), "https://www.example.com");
+ credit_card4->set_billing_address_id("F");
+
+ // Add the credit cards to the database.
+ personal_data_->local_credit_cards_.push_back(base::WrapUnique(credit_card1));
+ personal_data_->local_credit_cards_.push_back(base::WrapUnique(credit_card2));
+ personal_data_->local_credit_cards_.push_back(base::WrapUnique(credit_card3));
+ personal_data_->local_credit_cards_.push_back(base::WrapUnique(credit_card4));
+
+ personal_data_->UpdateCardsBillingAddressReference(guids_merge_map);
+
+ // The first card's billing address should now be E.
+ EXPECT_EQ("E", credit_card1->billing_address_id());
+ // The second card's billing address should now be E.
+ EXPECT_EQ("E", credit_card2->billing_address_id());
+ // The third card's billing address should still be E.
+ EXPECT_EQ("E", credit_card3->billing_address_id());
+ // The fourth card's billing address should still be F.
+ EXPECT_EQ("F", credit_card4->billing_address_id());
+}
+
+// Tests that ApplyDedupingRoutine updates the credit cards' billing address id
+// based on the deduped profiles.
+TEST_F(PersonalDataManagerTest,
+ ApplyDedupingRoutine_CardsBillingAddressIdUpdated) {
+ // A set of 6 profiles will be created. They should merge in this way:
+ // 1 -> 2 -> 3
+ // 4 -> 5
+ // 6
+ // Set their frencency score so that profile 3 has a higher score than 5, and
+ // 5 has a higher score than 6. This will ensure a deterministic order when
+ // verifying results.
+
+ // Create a set of 3 profiles to be merged together.
+ // Create a profile with a higher frecency score.
+ AutofillProfile profile1(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile1, "Homer", "J", "Simpson",
+ "homer.simpson@abc.com", "", "742. Evergreen Terrace",
+ "", "Springfield", "IL", "91601", "US", "");
+ profile1.set_use_count(12);
+ profile1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1));
+
+ // Create a profile with a medium frecency score.
+ AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile2, "Homer", "Jay", "Simpson",
+ "homer.simpson@abc.com", "", "742 Evergreen Terrace", "",
+ "Springfield", "IL", "91601", "", "12345678910");
+ profile2.set_use_count(5);
+ profile2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3));
+
+ // Create a profile with a lower frecency score.
+ AutofillProfile profile3(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile3, "Homer", "J", "Simpson",
+ "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.",
+ "", "Springfield", "IL", "91601", "", "");
+ profile3.set_use_count(3);
+ profile3.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(5));
+
+ // Create a set of two profiles to be merged together.
+ // Create a profile with a higher frecency score.
+ AutofillProfile profile4(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile4, "Marge", "B", "Simpson",
+ "marge.simpson@abc.com", "", "742. Evergreen Terrace",
+ "", "Springfield", "IL", "91601", "US", "");
+ profile4.set_use_count(11);
+ profile4.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1));
+
+ // Create a profile with a lower frecency score.
+ AutofillProfile profile5(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile5, "Marge", "B", "Simpson",
+ "marge.simpson@abc.com", "Fox", "742 Evergreen Terrace.",
+ "", "Springfield", "IL", "91601", "", "");
+ profile5.set_use_count(5);
+ profile5.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3));
+
+ // Create a unique profile.
+ AutofillProfile profile6(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile6, "Bart", "J", "Simpson",
+ "bart.simpson@abc.com", "Fox", "742 Evergreen Terrace.",
+ "", "Springfield", "IL", "91601", "", "");
+ profile6.set_use_count(10);
+ profile6.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1));
+
+ // Add three credit cards. Give them a frecency score so that they are
+ // suggested in order (1, 2, 3). This will ensure a deterministic order for
+ // verifying results.
+ CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&credit_card1, "Clyde Barrow",
+ "347666888555" /* American Express */, "04", "2999");
+ credit_card1.set_use_count(10);
+
+ CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&credit_card2, "John Dillinger",
+ "423456789012" /* Visa */, "01", "2999");
+ credit_card2.set_use_count(5);
+
+ CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&credit_card3, "Bonnie Parker",
+ "518765432109" /* Mastercard */, "12", "2999");
+ credit_card3.set_use_count(1);
+
+ // Associate the first card with profile1.
+ credit_card1.set_billing_address_id(profile1.guid());
+ // Associate the second card with profile4.
+ credit_card2.set_billing_address_id(profile4.guid());
+ // Associate the third card with profile6.
+ credit_card3.set_billing_address_id(profile6.guid());
+
+ personal_data_->AddProfile(profile1);
+ personal_data_->AddProfile(profile2);
+ personal_data_->AddProfile(profile3);
+ personal_data_->AddProfile(profile4);
+ personal_data_->AddProfile(profile5);
+ personal_data_->AddProfile(profile6);
+ personal_data_->AddCreditCard(credit_card1);
+ personal_data_->AddCreditCard(credit_card2);
+ personal_data_->AddCreditCard(credit_card3);
+ EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+ .WillOnce(QuitMainMessageLoop());
+ base::RunLoop().Run();
+
+ // Make sure the 6 profiles and 3 credit cards were saved.
+ EXPECT_EQ(6U, personal_data_->GetProfiles().size());
+ EXPECT_EQ(3U, personal_data_->GetCreditCards().size());
+
+ // Enable the profile cleanup now. Otherwise it would be triggered by the
+ // calls to AddProfile.
+ EnableAutofillProfileCleanup();
+
+ EXPECT_TRUE(personal_data_->ApplyDedupingRoutine());
+ EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+ .WillOnce(QuitMainMessageLoop());
+ base::RunLoop().Run();
+
+ // Get the profiles and cards sorted by frecency to have a deterministic
+ // order.
+ std::vector<AutofillProfile*> profiles =
+ personal_data_->GetProfilesToSuggest();
+ std::vector<CreditCard*> credit_cards =
+ personal_data_->GetCreditCardsToSuggest();
+
+ // |profile1| should have been merged into |profile2| which should then have
+ // been merged into |profile3|. |profile4| should have been merged into
+ // |profile5| and |profile6| should not have merged. Therefore there should be
+ // 3 profile left.
+ ASSERT_EQ(3U, profiles.size());
+
+ // Make sure the remaining profiles are the expected ones.
+ EXPECT_EQ(profile3.guid(), profiles[0]->guid());
+ EXPECT_EQ(profile5.guid(), profiles[1]->guid());
+ EXPECT_EQ(profile6.guid(), profiles[2]->guid());
+
+ // |credit_card1|'s billing address should now be profile 3.
+ EXPECT_EQ(profile3.guid(), credit_cards[0]->billing_address_id());
+
+ // |credit_card2|'s billing address should now be profile 5.
+ EXPECT_EQ(profile5.guid(), credit_cards[1]->billing_address_id());
+
+ // |credit_card3|'s billing address should still be profile 6.
+ EXPECT_EQ(profile6.guid(), credit_cards[2]->billing_address_id());
+}
+
// Tests that ApplyDedupingRoutine merges the profile values correctly, i.e.
// never lose information and keep the syntax of the profile with the higher
// frecency score.
@@ -5363,9 +5627,6 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_OncePerVersion) {
"homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.",
"", "Springfield", "IL", "91601", "", "");
- // Disable the profile cleanup before adding |profile3|.
- scoped_feature_list_.reset();
-
personal_data_->AddProfile(profile3);
EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
.WillOnce(QuitMainMessageLoop());
diff --git a/chromium/components/autofill/core/browser/phone_field_unittest.cc b/chromium/components/autofill/core/browser/phone_field_unittest.cc
index f270cbfa104..868bb69675f 100644
--- a/chromium/components/autofill/core/browser/phone_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/phone_field_unittest.cc
@@ -7,10 +7,10 @@
#include <stddef.h>
#include <memory>
+#include <vector>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_field.h"
@@ -56,7 +56,7 @@ class PhoneFieldTest : public testing::Test {
EXPECT_EQ(expected_type, it->second.BestHeuristicType()) << name;
}
- ScopedVector<AutofillField> list_;
+ std::vector<std::unique_ptr<AutofillField>> list_;
std::unique_ptr<PhoneField> field_;
FieldCandidatesMap field_candidates_map_;
@@ -65,14 +65,14 @@ class PhoneFieldTest : public testing::Test {
};
TEST_F(PhoneFieldTest, Empty) {
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_EQ(nullptr, field_.get());
}
TEST_F(PhoneFieldTest, NonParse) {
- list_.push_back(new AutofillField);
- AutofillScanner scanner(list_.get());
+ list_.push_back(base::MakeUnique<AutofillField>());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_EQ(nullptr, field_.get());
}
@@ -86,9 +86,10 @@ TEST_F(PhoneFieldTest, ParseOneLinePhone) {
field.form_control_type = field_type;
field.label = ASCIIToUTF16("Phone");
field.name = ASCIIToUTF16("phone");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("phone1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone1")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -105,13 +106,15 @@ TEST_F(PhoneFieldTest, ParseTwoLinePhone) {
field.form_control_type = field_type;
field.label = ASCIIToUTF16("Area Code");
field.name = ASCIIToUTF16("area code");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("areacode1")));
field.label = ASCIIToUTF16("Phone");
field.name = ASCIIToUTF16("phone");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("phone2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone2")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -135,24 +138,28 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumber) {
field.label = ASCIIToUTF16("Phone:");
field.name = ASCIIToUTF16("dayphone1");
field.max_length = 0;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("areacode1")));
field.label = ASCIIToUTF16("-");
field.name = ASCIIToUTF16("dayphone2");
field.max_length = 3;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("prefix2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("prefix2")));
field.label = ASCIIToUTF16("-");
field.name = ASCIIToUTF16("dayphone3");
field.max_length = 4;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("suffix3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("suffix3")));
field.label = ASCIIToUTF16("ext.:");
field.name = ASCIIToUTF16("dayphone4");
field.max_length = 0;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("ext4")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("ext4")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -175,17 +182,20 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix) {
field.form_control_type = field_type;
field.label = ASCIIToUTF16("Phone:");
field.name = ASCIIToUTF16("area");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("areacode1")));
field.label = base::string16();
field.name = ASCIIToUTF16("prefix");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("prefix2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("prefix2")));
field.label = base::string16();
field.name = ASCIIToUTF16("suffix");
- list_.push_back(new AutofillField(field, ASCIIToUTF16("suffix3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("suffix3")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -205,19 +215,22 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix2) {
field.label = ASCIIToUTF16("(");
field.name = ASCIIToUTF16("phone1");
field.max_length = 3;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("phone1")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone1")));
field.label = ASCIIToUTF16(")");
field.name = ASCIIToUTF16("phone2");
field.max_length = 3;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("phone2")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone2")));
field.label = base::string16();
field.name = ASCIIToUTF16("phone3");
field.max_length = 4;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("phone3")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone3")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
@@ -239,14 +252,16 @@ TEST_F(PhoneFieldTest, CountryAndCityAndPhoneNumber) {
field.label = ASCIIToUTF16("Phone Number");
field.name = ASCIIToUTF16("CountryCode");
field.max_length = 3;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("country")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("country")));
field.label = ASCIIToUTF16("Phone Number");
field.name = ASCIIToUTF16("PhoneNumber");
field.max_length = 10;
- list_.push_back(new AutofillField(field, ASCIIToUTF16("phone")));
+ list_.push_back(
+ base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone")));
- AutofillScanner scanner(list_.get());
+ AutofillScanner scanner(list_);
field_ = Parse(&scanner);
ASSERT_NE(nullptr, field_.get());
field_->AddClassifications(&field_candidates_map_);
diff --git a/chromium/components/autofill/core/browser/popup_item_ids.h b/chromium/components/autofill/core/browser/popup_item_ids.h
index 29be19cb622..7821315f611 100644
--- a/chromium/components/autofill/core/browser/popup_item_ids.h
+++ b/chromium/components/autofill/core/browser/popup_item_ids.h
@@ -11,7 +11,7 @@ namespace autofill {
enum PopupItemId {
POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY = 0,
- POPUP_ITEM_ID_WARNING_MESSAGE = -1,
+ POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE = -1,
POPUP_ITEM_ID_PASSWORD_ENTRY = -2,
POPUP_ITEM_ID_SEPARATOR = -3,
POPUP_ITEM_ID_CLEAR_FORM = -4,
@@ -20,6 +20,8 @@ enum PopupItemId {
POPUP_ITEM_ID_SCAN_CREDIT_CARD = -7,
POPUP_ITEM_ID_TITLE = -8,
POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO = -9,
+ POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE = -10,
+ POPUP_ITEM_ID_USERNAME_ENTRY = -11,
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/proto/BUILD.gn b/chromium/components/autofill/core/browser/proto/BUILD.gn
index ad52e78646b..2a8069f6378 100644
--- a/chromium/components/autofill/core/browser/proto/BUILD.gn
+++ b/chromium/components/autofill/core/browser/proto/BUILD.gn
@@ -6,6 +6,7 @@ import("//third_party/protobuf/proto_library.gni")
proto_library("proto") {
sources = [
+ "autofill_sync.proto",
"server.proto",
]
}
diff --git a/chromium/components/autofill/core/browser/proto/autofill_sync.proto b/chromium/components/autofill/core/browser/proto/autofill_sync.proto
new file mode 100644
index 00000000000..613bdbac022
--- /dev/null
+++ b/chromium/components/autofill/core/browser/proto/autofill_sync.proto
@@ -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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package autofill;
+
+// Used to convert between autofill::AutofillKey and a std::string that can be
+// passed to sync as storage key to uniquely identify an entity of ModelType
+// syncer::AUTOFILL.
+message AutofillSyncStorageKey {
+ optional string name = 1;
+ optional string value = 2;
+}
diff --git a/chromium/components/autofill/core/browser/suggestion.cc b/chromium/components/autofill/core/browser/suggestion.cc
index 22d4ac7c89c..754ede930f8 100644
--- a/chromium/components/autofill/core/browser/suggestion.cc
+++ b/chromium/components/autofill/core/browser/suggestion.cc
@@ -10,7 +10,8 @@ namespace autofill {
Suggestion::Suggestion()
: frontend_id(0),
- match(PREFIX_MATCH) {
+ match(PREFIX_MATCH),
+ is_value_bold(false) {
}
Suggestion::Suggestion(const Suggestion& other)
@@ -19,13 +20,15 @@ Suggestion::Suggestion(const Suggestion& other)
value(other.value),
label(other.label),
icon(other.icon),
- match(other.match) {
+ match(other.match),
+ is_value_bold(other.is_value_bold) {
}
Suggestion::Suggestion(const base::string16& v)
: frontend_id(0),
value(v),
- match(PREFIX_MATCH) {
+ match(PREFIX_MATCH),
+ is_value_bold(false) {
}
Suggestion::Suggestion(const std::string& v,
@@ -36,7 +39,8 @@ Suggestion::Suggestion(const std::string& v,
value(base::UTF8ToUTF16(v)),
label(base::UTF8ToUTF16(l)),
icon(base::UTF8ToUTF16(i)),
- match(PREFIX_MATCH) {
+ match(PREFIX_MATCH),
+ is_value_bold(false) {
}
Suggestion::~Suggestion() {
diff --git a/chromium/components/autofill/core/browser/suggestion.h b/chromium/components/autofill/core/browser/suggestion.h
index 930ab230f2d..63522debfec 100644
--- a/chromium/components/autofill/core/browser/suggestion.h
+++ b/chromium/components/autofill/core/browser/suggestion.h
@@ -47,6 +47,7 @@ struct Suggestion {
base::string16 label;
base::string16 icon;
MatchMode match;
+ bool is_value_bold; // true if |value| should be displayed in bold type face.
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_autofill_client.cc b/chromium/components/autofill/core/browser/test_autofill_client.cc
index 5913dedf3bf..7df6c300ce4 100644
--- a/chromium/components/autofill/core/browser/test_autofill_client.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_client.cc
@@ -11,7 +11,8 @@ namespace autofill {
TestAutofillClient::TestAutofillClient()
: token_service_(new FakeOAuth2TokenService()),
identity_provider_(new FakeIdentityProvider(token_service_.get())),
- rappor_service_(new rappor::TestRapporService()) {}
+ rappor_service_(new rappor::TestRapporServiceImpl()),
+ form_origin_(GURL("https://example.test")) {}
TestAutofillClient::~TestAutofillClient() {
}
@@ -36,7 +37,7 @@ IdentityProvider* TestAutofillClient::GetIdentityProvider() {
return identity_provider_.get();
}
-rappor::RapporService* TestAutofillClient::GetRapporService() {
+rappor::RapporServiceImpl* TestAutofillClient::GetRapporServiceImpl() {
return rappor_service_.get();
}
@@ -115,9 +116,9 @@ void TestAutofillClient::DidFillOrPreviewField(
void TestAutofillClient::OnFirstUserGestureObserved() {
}
-bool TestAutofillClient::IsContextSecure(const GURL& form_origin) {
+bool TestAutofillClient::IsContextSecure() {
// Simplified secure context check for tests.
- return form_origin.SchemeIs("https");
+ return form_origin_.SchemeIs("https");
}
bool TestAutofillClient::ShouldShowSigninPromo() {
@@ -126,4 +127,6 @@ bool TestAutofillClient::ShouldShowSigninPromo() {
void TestAutofillClient::StartSigninFlow() {}
+void TestAutofillClient::ShowHttpNotSecureExplanation() {}
+
} // 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 013a9030d53..72cdd8c3f57 100644
--- a/chromium/components/autofill/core/browser/test_autofill_client.h
+++ b/chromium/components/autofill/core/browser/test_autofill_client.h
@@ -33,7 +33,7 @@ class TestAutofillClient : public AutofillClient {
PrefService* GetPrefs() override;
syncer::SyncService* GetSyncService() override;
IdentityProvider* GetIdentityProvider() override;
- rappor::RapporService* GetRapporService() override;
+ rappor::RapporServiceImpl* GetRapporServiceImpl() override;
void ShowAutofillSettings() override;
void ShowUnmaskPrompt(const CreditCard& card,
UnmaskCardReason reason,
@@ -67,24 +67,31 @@ class TestAutofillClient : public AutofillClient {
void DidFillOrPreviewField(const base::string16& autofilled_value,
const base::string16& profile_full_name) override;
void OnFirstUserGestureObserved() override;
- bool IsContextSecure(const GURL& form_origin) override;
+ // By default, TestAutofillClient will report that the context is
+ // secure. This can be adjusted by calling set_form_origin() with an
+ // http:// URL.
+ bool IsContextSecure() override;
bool ShouldShowSigninPromo() override;
void StartSigninFlow() override;
+ void ShowHttpNotSecureExplanation() override;
void SetPrefs(std::unique_ptr<PrefService> prefs) {
prefs_ = std::move(prefs);
}
- rappor::TestRapporService* test_rappor_service() {
+ rappor::TestRapporServiceImpl* test_rappor_service() {
return rappor_service_.get();
}
+ void set_form_origin(const GURL& url) { form_origin_ = url; }
+
private:
// NULL by default.
std::unique_ptr<PrefService> prefs_;
std::unique_ptr<FakeOAuth2TokenService> token_service_;
std::unique_ptr<FakeIdentityProvider> identity_provider_;
- std::unique_ptr<rappor::TestRapporService> rappor_service_;
+ std::unique_ptr<rappor::TestRapporServiceImpl> rappor_service_;
+ GURL form_origin_;
DISALLOW_COPY_AND_ASSIGN(TestAutofillClient);
};
diff --git a/chromium/components/autofill/core/browser/test_autofill_external_delegate.h b/chromium/components/autofill/core/browser/test_autofill_external_delegate.h
index cc7bf73ee44..9da419394b0 100644
--- a/chromium/components/autofill/core/browser/test_autofill_external_delegate.h
+++ b/chromium/components/autofill/core/browser/test_autofill_external_delegate.h
@@ -9,8 +9,6 @@
namespace autofill {
-class AutofillManager;
-
// Calls the required functions on the given external delegate to cause the
// delegate to display a popup.
void GenerateTestAutofillPopup(
diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc
index a922b5a2c75..435e00caf82 100644
--- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc
+++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc
@@ -15,6 +15,7 @@
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/ui/card_unmask_prompt_view.h"
+#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_pref_names.h"
#include "components/prefs/pref_service.h"
#include "grit/components_scaled_resources.h"
@@ -83,7 +84,7 @@ void CardUnmaskPromptControllerImpl::OnVerificationResult(
case AutofillClient::TRY_AGAIN_FAILURE: {
error_message = l10n_util::GetStringUTF16(
- IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN);
+ IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_CVC);
break;
}
@@ -219,15 +220,11 @@ base::string16 CardUnmaskPromptControllerImpl::GetWindowTitle() const {
// The iOS UI has less room for the title so it shows a shorter string.
return l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE);
#else
- int ids;
- if (reason_ == AutofillClient::UNMASK_FOR_AUTOFILL &&
- ShouldRequestExpirationDate()) {
- ids = IDS_AUTOFILL_CARD_UNMASK_PROMPT_UPDATE_TITLE;
- }
- else {
- ids = IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE;
- }
- return l10n_util::GetStringFUTF16(ids, card_.TypeAndLastFourDigits());
+ return l10n_util::GetStringFUTF16(
+ ShouldRequestExpirationDate()
+ ? IDS_AUTOFILL_CARD_UNMASK_PROMPT_EXPIRED_TITLE
+ : IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE,
+ card_.TypeAndLastFourDigits());
#endif
}
@@ -283,10 +280,7 @@ bool CardUnmaskPromptControllerImpl::InputCvcIsValid(
const base::string16& input_text) const {
base::string16 trimmed_text;
base::TrimWhitespace(input_text, base::TRIM_ALL, &trimmed_text);
- size_t input_size = card_.type() == kAmericanExpressCard ? 4 : 3;
- return trimmed_text.size() == input_size &&
- base::ContainsOnlyChars(trimmed_text,
- base::ASCIIToUTF16("0123456789"));
+ return IsValidCreditCardSecurityCode(trimmed_text, card_.type());
}
bool CardUnmaskPromptControllerImpl::InputExpirationIsValid(
@@ -303,23 +297,15 @@ bool CardUnmaskPromptControllerImpl::InputExpirationIsValid(
return false;
}
- if (month_value < 1 || month_value > 12)
- return false;
-
- base::Time::Exploded now;
- base::Time::Now().LocalExplode(&now);
-
// Convert 2 digit year to 4 digit year.
- if (year_value < 100)
+ if (year_value < 100) {
+ base::Time::Exploded now;
+ base::Time::Now().LocalExplode(&now);
year_value += (now.year / 100) * 100;
+ }
- if (year_value < now.year)
- return false;
-
- if (year_value > now.year)
- return true;
-
- return month_value >= now.month;
+ return IsValidCreditCardExpirationDate(year_value, month_value,
+ base::Time::Now());
}
base::TimeDelta CardUnmaskPromptControllerImpl::GetSuccessMessageDuration()
diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_view.h b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_view.h
index dbedeab35a2..a234739f1c5 100644
--- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_view.h
+++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_view.h
@@ -10,8 +10,6 @@
namespace autofill {
-class CardUnmaskPromptController;
-
// The cross-platform UI interface which prompts the user to unlock a masked
// Wallet instrument (credit card). This object is responsible for its own
// lifetime.
diff --git a/chromium/components/autofill/core/browser/validation.cc b/chromium/components/autofill/core/browser/validation.cc
index b0f0a2a72bc..15a96e6ab22 100644
--- a/chromium/components/autofill/core/browser/validation.cc
+++ b/chromium/components/autofill/core/browser/validation.cc
@@ -43,6 +43,7 @@ bool IsValidCreditCardNumber(const base::string16& text) {
// defined sizes.
// [1] http://www.merriampark.com/anatomycc.htm
// [2] http://en.wikipedia.org/wiki/Bank_card_number
+ // CardEditor.isCardNumberLengthMaxium() needs to be kept in sync.
const char* const type = CreditCard::GetCreditCardType(text);
if (type == kAmericanExpressCard && number.size() != 15)
return false;
@@ -54,6 +55,8 @@ bool IsValidCreditCardNumber(const base::string16& text) {
return false;
if (type == kMasterCard && number.size() != 16)
return false;
+ if (type == kMirCard && number.size() != 16)
+ return false;
if (type == kUnionPay && (number.size() < 16 || number.size() > 19))
return false;
if (type == kVisaCard && number.size() != 13 && number.size() != 16)
@@ -84,25 +87,11 @@ bool IsValidCreditCardNumber(const base::string16& text) {
return (sum % 10) == 0;
}
-bool IsValidCreditCardSecurityCode(const base::string16& text) {
- if (text.size() < 3U || text.size() > 4U)
- return false;
-
- for (const base::char16& it : text) {
- if (!base::IsAsciiDigit(it))
- return false;
- }
- return true;
-}
-
bool IsValidCreditCardSecurityCode(const base::string16& code,
- const base::string16& number) {
- const char* const type = CreditCard::GetCreditCardType(number);
- size_t required_length = 3;
- if (type == kAmericanExpressCard)
- required_length = 4;
-
- return code.length() == required_length;
+ const base::StringPiece card_type) {
+ size_t required_length = card_type == kAmericanExpressCard ? 4 : 3;
+ return code.length() == required_length &&
+ base::ContainsOnlyChars(code, base::ASCIIToUTF16("0123456789"));
}
bool IsValidEmailAddress(const base::string16& text) {
diff --git a/chromium/components/autofill/core/browser/validation.h b/chromium/components/autofill/core/browser/validation.h
index aaaedcd23b8..a09ae75cbb3 100644
--- a/chromium/components/autofill/core/browser/validation.h
+++ b/chromium/components/autofill/core/browser/validation.h
@@ -6,6 +6,7 @@
#define COMPONENTS_AUTOFILL_CORE_BROWSER_VALIDATION_H_
#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
namespace base {
class Time;
@@ -23,13 +24,10 @@ bool IsValidCreditCardExpirationDate(int year,
// Uses the Luhn formula to validate the number.
bool IsValidCreditCardNumber(const base::string16& text);
-// Returns true if |text| looks like a valid credit card security code.
-bool IsValidCreditCardSecurityCode(const base::string16& text);
-
// Returns true if |code| looks like a valid credit card security code
-// for the type of credit card designated by |number|.
+// for the given credit card type.
bool IsValidCreditCardSecurityCode(const base::string16& code,
- const base::string16& number);
+ const base::StringPiece card_type);
// Returns true if |text| looks like a valid e-mail address.
bool IsValidEmailAddress(const base::string16& text);
diff --git a/chromium/components/autofill/core/browser/validation_unittest.cc b/chromium/components/autofill/core/browser/validation_unittest.cc
index c7b880b0a17..798f53462bd 100644
--- a/chromium/components/autofill/core/browser/validation_unittest.cc
+++ b/chromium/components/autofill/core/browser/validation_unittest.cc
@@ -7,6 +7,7 @@
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
+#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/validation.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -25,6 +26,11 @@ struct IntExpirationDate {
const int month;
};
+struct SecurityCodeCardTypePair {
+ const char* security_code;
+ const char* card_type;
+};
+
// From https://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm
const char* const kValidNumbers[] = {
"378282246310005",
@@ -64,14 +70,16 @@ const IntExpirationDate kInvalidCreditCardIntExpirationDate[] = {
{ 2015, 13 }, // Not a real month.
{ 2015, 0 }, // Zero is legal in the CC class but is not a valid date.
};
-const char* const kValidCreditCardSecurityCode[] = {
- "323", // 3-digit CSC.
- "3234", // 4-digit CSC.
+const SecurityCodeCardTypePair kValidSecurityCodeCardTypePairs[] = {
+ { "323", kGenericCard }, // 3-digit CSC.
+ { "3234", kAmericanExpressCard }, // 4-digit CSC.
};
-const char* const kInvalidCreditCardSecurityCode[] = {
- "32", // CSC too short.
- "12345", // CSC too long.
- "asd", // non-numeric CSC.
+const SecurityCodeCardTypePair kInvalidSecurityCodeCardTypePairs[] = {
+ { "32", kGenericCard }, // CSC too short.
+ { "323", kAmericanExpressCard }, // CSC too short.
+ { "3234", kGenericCard }, // CSC too long.
+ { "12345", kAmericanExpressCard }, // CSC too long.
+ { "asd", kGenericCard }, // non-numeric CSC.
};
const char* const kValidEmailAddress[] = {
"user@example",
@@ -85,10 +93,6 @@ const char* const kInvalidEmailAddress[] = {
"user@",
"user@=example.com"
};
-const char kAmericanExpressCard[] = "341111111111111";
-const char kVisaCard[] = "4111111111111111";
-const char kAmericanExpressCVC[] = "1234";
-const char kVisaCVC[] = "123";
} // namespace
TEST(AutofillValidation, IsValidCreditCardNumber) {
@@ -117,14 +121,19 @@ TEST(AutofillValidation, IsValidCreditCardIntExpirationDate) {
EXPECT_TRUE(!IsValidCreditCardExpirationDate(data.year, data.month, now));
}
}
+
TEST(AutofillValidation, IsValidCreditCardSecurityCode) {
- for (const char* valid_code : kValidCreditCardSecurityCode) {
- SCOPED_TRACE(valid_code);
- EXPECT_TRUE(IsValidCreditCardSecurityCode(ASCIIToUTF16(valid_code)));
+ for (const auto data : kValidSecurityCodeCardTypePairs) {
+ SCOPED_TRACE(data.security_code);
+ SCOPED_TRACE(data.card_type);
+ EXPECT_TRUE(IsValidCreditCardSecurityCode(ASCIIToUTF16(data.security_code),
+ data.card_type));
}
- for (const char* invalid_code : kInvalidCreditCardSecurityCode) {
- SCOPED_TRACE(invalid_code);
- EXPECT_FALSE(IsValidCreditCardSecurityCode(ASCIIToUTF16(invalid_code)));
+ for (const auto data : kInvalidSecurityCodeCardTypePairs) {
+ SCOPED_TRACE(data.security_code);
+ SCOPED_TRACE(data.card_type);
+ EXPECT_FALSE(IsValidCreditCardSecurityCode(ASCIIToUTF16(data.security_code),
+ data.card_type));
}
}
@@ -139,19 +148,4 @@ TEST(AutofillValidation, IsValidEmailAddress) {
}
}
-TEST(AutofillValidation, IsValidCreditCardSecurityCodeWithNumber) {
- EXPECT_TRUE(IsValidCreditCardSecurityCode(
- ASCIIToUTF16(kAmericanExpressCVC), ASCIIToUTF16(kAmericanExpressCard)));
- EXPECT_TRUE(IsValidCreditCardSecurityCode(
- ASCIIToUTF16(kVisaCVC), ASCIIToUTF16(kVisaCard)));
- EXPECT_FALSE(IsValidCreditCardSecurityCode(
- ASCIIToUTF16(kVisaCVC), ASCIIToUTF16(kAmericanExpressCard)));
- EXPECT_FALSE(IsValidCreditCardSecurityCode(
- ASCIIToUTF16(kAmericanExpressCVC), ASCIIToUTF16(kVisaCard)));
- EXPECT_TRUE(IsValidCreditCardSecurityCode(
- ASCIIToUTF16(kVisaCVC), ASCIIToUTF16(kInvalidNumbers[0])));
- EXPECT_FALSE(IsValidCreditCardSecurityCode(
- ASCIIToUTF16(kAmericanExpressCVC), ASCIIToUTF16(kInvalidNumbers[0])));
-}
-
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
new file mode 100644
index 00000000000..0bf47d314ba
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
@@ -0,0 +1,477 @@
+// 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/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
+
+#include <algorithm>
+#include <set>
+#include <unordered_set>
+#include <utility>
+#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"
+#include "components/autofill/core/browser/webdata/autofill_metadata_change_list.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/sync/model/entity_data.h"
+#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "net/base/escape.h"
+
+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;
+using syncer::MutableDataBatch;
+
+namespace autofill {
+
+namespace {
+
+const char kAutocompleteEntryNamespaceTag[] = "autofill_entry|";
+const char kAutocompleteTagDelimiter[] = "|";
+
+// Simplify checking for optional errors and returning only when present.
+#define RETURN_IF_ERROR(x) \
+ if (Optional<ModelError> ret_val = x) { \
+ return ret_val; \
+ }
+
+void* UserDataKey() {
+ // Use the address of a static that COMDAT folding won't ever collide
+ // with something else.
+ static int user_data_key = 0;
+ return reinterpret_cast<void*>(&user_data_key);
+}
+
+std::unique_ptr<EntityData> CreateEntityData(const AutofillEntry& entry) {
+ auto entity_data = base::MakeUnique<EntityData>();
+ entity_data->non_unique_name = base::UTF16ToUTF8(entry.key().name());
+ AutofillSpecifics* autofill = entity_data->specifics.mutable_autofill();
+ autofill->set_name(base::UTF16ToUTF8(entry.key().name()));
+ autofill->set_value(base::UTF16ToUTF8(entry.key().value()));
+ autofill->add_usage_timestamp(entry.date_created().ToInternalValue());
+ if (entry.date_created() != entry.date_last_used())
+ autofill->add_usage_timestamp(entry.date_last_used().ToInternalValue());
+ return entity_data;
+}
+
+std::string BuildSerializedStorageKey(const std::string& name,
+ const std::string& value) {
+ AutofillSyncStorageKey proto;
+ proto.set_name(name);
+ proto.set_value(value);
+ return proto.SerializeAsString();
+}
+
+std::string GetStorageKeyFromModel(const AutofillKey& key) {
+ return BuildSerializedStorageKey(base::UTF16ToUTF8(key.name()),
+ base::UTF16ToUTF8(key.value()));
+}
+
+AutofillEntry MergeEntryDates(const AutofillEntry& entry1,
+ const AutofillEntry& entry2) {
+ DCHECK(entry1.key() == entry2.key());
+ return AutofillEntry(
+ entry1.key(), std::min(entry1.date_created(), entry2.date_created()),
+ std::max(entry1.date_last_used(), entry2.date_last_used()));
+}
+
+bool ParseStorageKey(const std::string& storage_key, AutofillKey* out_key) {
+ AutofillSyncStorageKey proto;
+ if (proto.ParseFromString(storage_key)) {
+ *out_key = AutofillKey(base::UTF8ToUTF16(proto.name()),
+ base::UTF8ToUTF16((proto.value())));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+AutofillEntry CreateAutofillEntry(const AutofillSpecifics& autofill_specifics) {
+ AutofillKey key(base::UTF8ToUTF16(autofill_specifics.name()),
+ base::UTF8ToUTF16(autofill_specifics.value()));
+ Time date_created, date_last_used;
+ const google::protobuf::RepeatedField<int64_t>& timestamps =
+ autofill_specifics.usage_timestamp();
+ if (!timestamps.empty()) {
+ auto iter_pair = std::minmax_element(timestamps.begin(), timestamps.end());
+ date_created = Time::FromInternalValue(*iter_pair.first);
+ date_last_used = Time::FromInternalValue(*iter_pair.second);
+ }
+ return AutofillEntry(key, date_created, date_last_used);
+}
+
+// This is used to respond to ApplySyncChanges() and MergeSyncData(). Attempts
+// to lazily load local data, and then react to sync data by maintaining
+// internal state until flush calls are made, at which point the applicable
+// modification should be sent towards local and sync directions.
+class SyncDifferenceTracker {
+ public:
+ explicit SyncDifferenceTracker(AutofillTable* table) : table_(table) {}
+
+ Optional<ModelError> IncorporateRemoteSpecifics(
+ const std::string& storage_key,
+ const AutofillSpecifics& specifics) {
+ if (!specifics.has_value()) {
+ // A long time ago autofill had a different format, and it's possible we
+ // could encounter some of that legacy data. It is not useful to us,
+ // because an autofill entry with no value will not place any text in a
+ // form for the user. So drop all of these on the floor.
+ DVLOG(1) << "Dropping old-style autofill profile change.";
+ return {};
+ }
+
+ const AutofillEntry remote = CreateAutofillEntry(specifics);
+ DCHECK_EQ(storage_key, GetStorageKeyFromModel(remote.key()));
+
+ Optional<AutofillEntry> local;
+ if (!ReadEntry(remote.key(), &local)) {
+ return ModelError(FROM_HERE, "Failed reading from WebDatabase.");
+ } else if (!local) {
+ save_to_local_.push_back(remote);
+ } else if (remote != local.value()) {
+ if (specifics.usage_timestamp().empty()) {
+ // Skip merging if there are no timestamps. We don't want to wipe out
+ // a local value of |date_created| if the remote copy is oddly formed.
+ save_to_sync_.push_back(local.value());
+ } else {
+ const AutofillEntry merged = MergeEntryDates(local.value(), remote);
+ save_to_local_.push_back(merged);
+ save_to_sync_.push_back(merged);
+ }
+ unique_to_local_.erase(local.value());
+ }
+ return {};
+ }
+
+ Optional<ModelError> IncorporateRemoteDelete(const std::string& storage_key) {
+ AutofillKey key;
+ if (!ParseStorageKey(storage_key, &key)) {
+ return ModelError(FROM_HERE, "Failed parsing storage key.");
+ }
+ delete_from_local_.insert(key);
+ return {};
+ }
+
+ Optional<ModelError> FlushToLocal(AutofillWebDataBackend* web_data_backend) {
+ for (const AutofillKey& key : delete_from_local_) {
+ if (!table_->RemoveFormElement(key.name(), key.value())) {
+ return ModelError(FROM_HERE, "Failed deleting from WebDatabase");
+ }
+ }
+ if (!table_->UpdateAutofillEntries(save_to_local_)) {
+ return ModelError(FROM_HERE, "Failed updating WebDatabase");
+ }
+ if (!delete_from_local_.empty() || !save_to_local_.empty()) {
+ web_data_backend->NotifyOfMultipleAutofillChanges();
+ }
+ return {};
+ }
+
+ Optional<ModelError> FlushToSync(
+ bool include_local_only,
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ ModelTypeChangeProcessor* change_processor) {
+ for (const AutofillEntry& entry : save_to_sync_) {
+ change_processor->Put(GetStorageKeyFromModel(entry.key()),
+ CreateEntityData(entry),
+ metadata_change_list.get());
+ }
+ if (include_local_only) {
+ if (!InitializeIfNeeded()) {
+ return ModelError(FROM_HERE, "Failed reading from WebDatabase.");
+ }
+ for (const AutofillEntry& entry : unique_to_local_) {
+ // This should never be true because only ApplySyncChanges should be
+ // calling IncorporateRemoteDelete, while only MergeSyncData should be
+ // passing in true for |include_local_only|. If this requirement
+ // changes, this DCHECK can change to act as a filter.
+ DCHECK(delete_from_local_.find(entry.key()) ==
+ delete_from_local_.end());
+ change_processor->Put(GetStorageKeyFromModel(entry.key()),
+ CreateEntityData(entry),
+ metadata_change_list.get());
+ }
+ }
+ return static_cast<AutofillMetadataChangeList*>(metadata_change_list.get())
+ ->TakeError();
+ }
+
+ private:
+ // There are three major outcomes of this method.
+ // 1. An error is encountered reading from the db, false is returned.
+ // 2. The entry is not found, |entry| will not be touched.
+ // 3. The entry is found, |entry| will be set.
+ bool ReadEntry(const AutofillKey& key, Optional<AutofillEntry>* entry) {
+ if (!InitializeIfNeeded()) {
+ return false;
+ }
+ auto iter = unique_to_local_.find(AutofillEntry(key, Time(), Time()));
+ if (iter != unique_to_local_.end()) {
+ *entry = *iter;
+ }
+ return true;
+ }
+
+ bool InitializeIfNeeded() {
+ if (initialized_) {
+ return true;
+ }
+
+ std::vector<AutofillEntry> vector;
+ if (!table_->GetAllAutofillEntries(&vector)) {
+ return false;
+ }
+
+ unique_to_local_ = std::set<AutofillEntry>(vector.begin(), vector.end());
+ initialized_ = true;
+ return true;
+ }
+
+ AutofillTable* table_;
+
+ // This class attempts to lazily load data from |table_|. This field tracks
+ // if that has happened or not yet. To facilitate this, the first usage of
+ // |unique_to_local_| should typically be done through ReadEntry().
+ bool initialized_ = false;
+
+ // Important to note that because AutofillEntry's operator < simply compares
+ // contained AutofillKeys, this acts as a map<AutofillKey, AutofillEntry>.
+ // Shouldn't be accessed until either ReadEntry() or InitializeIfNeeded() is
+ // called, afterward it will start with all the local data. As sync data is
+ // encountered entries are removed from here, leaving only entries that exist
+ // solely on the local client.
+ std::set<AutofillEntry> unique_to_local_;
+
+ std::set<AutofillKey> delete_from_local_;
+ std::vector<AutofillEntry> save_to_local_;
+
+ // Contains merged data for entries that existed on both sync and local sides
+ // and need to be saved back to sync.
+ std::vector<AutofillEntry> save_to_sync_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncDifferenceTracker);
+};
+
+} // namespace
+
+// static
+void AutocompleteSyncBridge::CreateForWebDataServiceAndBackend(
+ AutofillWebDataService* web_data_service,
+ AutofillWebDataBackend* web_data_backend) {
+ web_data_service->GetDBUserData()->SetUserData(
+ UserDataKey(),
+ new AutocompleteSyncBridge(
+ web_data_backend,
+ base::BindRepeating(
+ &ModelTypeChangeProcessor::Create,
+ base::BindRepeating(base::IgnoreResult(&DumpWithoutCrashing)))));
+}
+
+// static
+AutocompleteSyncBridge* AutocompleteSyncBridge::FromWebDataService(
+ AutofillWebDataService* web_data_service) {
+ return static_cast<AutocompleteSyncBridge*>(
+ web_data_service->GetDBUserData()->GetUserData(UserDataKey()));
+}
+
+AutocompleteSyncBridge::AutocompleteSyncBridge(
+ AutofillWebDataBackend* backend,
+ const ChangeProcessorFactory& change_processor_factory)
+ : ModelTypeSyncBridge(change_processor_factory, syncer::AUTOFILL),
+ web_data_backend_(backend),
+ scoped_observer_(this) {
+ DCHECK(web_data_backend_);
+
+ scoped_observer_.Add(web_data_backend_);
+
+ LoadMetadata();
+}
+
+AutocompleteSyncBridge::~AutocompleteSyncBridge() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+std::unique_ptr<MetadataChangeList>
+AutocompleteSyncBridge::CreateMetadataChangeList() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return base::MakeUnique<AutofillMetadataChangeList>(GetAutofillTable(),
+ syncer::AUTOFILL);
+}
+
+Optional<syncer::ModelError> AutocompleteSyncBridge::MergeSyncData(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ EntityDataMap entity_data_map) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // TODO(skym, crbug.com/680218): Uncomment and add unit tests.
+ /*SyncDifferenceTracker tracker(GetAutofillTable());
+ for (auto kv : entity_data_map) {
+ DCHECK(kv.second->specifics.has_autofill());
+ RETURN_IF_ERROR(tracker.IncorporateRemoteSpecifics(
+ kv.first, kv.second->specifics.autofill()));
+ }
+
+ RETURN_IF_ERROR(tracker.FlushToLocal(web_data_backend_));
+ RETURN_IF_ERROR(tracker.FlushToSync(true, std::move(metadata_change_list),
+ change_processor()));
+ web_data_backend_->RemoveExpiredFormElements();
+ web_data_backend_->NotifyThatSyncHasStarted(syncer::AUTOFILL);*/
+ return {};
+}
+
+Optional<ModelError> AutocompleteSyncBridge::ApplySyncChanges(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ EntityChangeList entity_changes) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ SyncDifferenceTracker tracker(GetAutofillTable());
+ for (const EntityChange& change : entity_changes) {
+ if (change.type() == EntityChange::ACTION_DELETE) {
+ RETURN_IF_ERROR(tracker.IncorporateRemoteDelete(change.storage_key()));
+ } else {
+ DCHECK(change.data().specifics.has_autofill());
+ RETURN_IF_ERROR(tracker.IncorporateRemoteSpecifics(
+ change.storage_key(), change.data().specifics.autofill()));
+ }
+ }
+
+ RETURN_IF_ERROR(tracker.FlushToLocal(web_data_backend_));
+ RETURN_IF_ERROR(tracker.FlushToSync(false, std::move(metadata_change_list),
+ change_processor()));
+ web_data_backend_->RemoveExpiredFormElements();
+ return {};
+}
+
+void AutocompleteSyncBridge::AutocompleteSyncBridge::GetData(
+ StorageKeyList storage_keys,
+ DataCallback callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ std::vector<AutofillEntry> entries;
+ if (!GetAutofillTable()->GetAllAutofillEntries(&entries)) {
+ change_processor()->ReportError(FROM_HERE,
+ "Failed to load entries from table.");
+ return;
+ }
+
+ std::unordered_set<std::string> keys_set(storage_keys.begin(),
+ storage_keys.end());
+ auto batch = base::MakeUnique<MutableDataBatch>();
+ for (const AutofillEntry& entry : entries) {
+ std::string key = GetStorageKeyFromModel(entry.key());
+ if (keys_set.find(key) != keys_set.end()) {
+ batch->Put(key, CreateEntityData(entry));
+ }
+ }
+ callback.Run(std::move(batch));
+}
+
+void AutocompleteSyncBridge::GetAllData(DataCallback callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ std::vector<AutofillEntry> entries;
+ if (!GetAutofillTable()->GetAllAutofillEntries(&entries)) {
+ change_processor()->ReportError(FROM_HERE,
+ "Failed to load entries from table.");
+ return;
+ }
+
+ auto batch = base::MakeUnique<MutableDataBatch>();
+ for (const AutofillEntry& entry : entries) {
+ batch->Put(GetStorageKeyFromModel(entry.key()), CreateEntityData(entry));
+ }
+ callback.Run(std::move(batch));
+}
+
+void AutocompleteSyncBridge::ActOnLocalChanges(
+ const AutofillChangeList& changes) {
+ if (!change_processor()->IsTrackingMetadata()) {
+ return;
+ }
+
+ auto metadata_change_list = base::MakeUnique<AutofillMetadataChangeList>(
+ GetAutofillTable(), syncer::AUTOFILL);
+ for (const auto& change : changes) {
+ const std::string storage_key = GetStorageKeyFromModel(change.key());
+ switch (change.type()) {
+ case AutofillChange::ADD:
+ case AutofillChange::UPDATE: {
+ base::Time date_created, date_last_used;
+ bool success = GetAutofillTable()->GetAutofillTimestamps(
+ change.key().name(), change.key().value(), &date_created,
+ &date_last_used);
+ if (!success) {
+ change_processor()->ReportError(
+ FROM_HERE, "Failed reading autofill entry from WebDatabase.");
+ return;
+ }
+
+ const AutofillEntry entry(change.key(), date_created, date_last_used);
+ change_processor()->Put(storage_key, CreateEntityData(entry),
+ metadata_change_list.get());
+ break;
+ }
+ case AutofillChange::REMOVE: {
+ change_processor()->Delete(storage_key, metadata_change_list.get());
+ break;
+ }
+ }
+ }
+
+ if (Optional<ModelError> error = metadata_change_list->TakeError())
+ change_processor()->ReportError(error.value());
+}
+
+void AutocompleteSyncBridge::LoadMetadata() {
+ auto batch = base::MakeUnique<syncer::MetadataBatch>();
+ if (!GetAutofillTable()->GetAllSyncMetadata(syncer::AUTOFILL, batch.get())) {
+ change_processor()->ReportError(
+ FROM_HERE, "Failed reading autofill metadata from WebDatabase.");
+ return;
+ }
+ change_processor()->ModelReadyToSync(std::move(batch));
+}
+
+std::string AutocompleteSyncBridge::GetClientTag(
+ const EntityData& entity_data) {
+ DCHECK(entity_data.specifics.has_autofill());
+ const AutofillSpecifics specifics = entity_data.specifics.autofill();
+ return std::string(kAutocompleteEntryNamespaceTag) +
+ net::EscapePath(specifics.name()) +
+ std::string(kAutocompleteTagDelimiter) +
+ net::EscapePath(specifics.value());
+}
+
+std::string AutocompleteSyncBridge::GetStorageKey(
+ const EntityData& entity_data) {
+ DCHECK(entity_data.specifics.has_autofill());
+ const AutofillSpecifics specifics = entity_data.specifics.autofill();
+ return BuildSerializedStorageKey(specifics.name(), specifics.value());
+}
+
+// AutofillWebDataServiceObserverOnDBThread implementation.
+void AutocompleteSyncBridge::AutofillEntriesChanged(
+ const AutofillChangeList& changes) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ActOnLocalChanges(changes);
+}
+
+AutofillTable* AutocompleteSyncBridge::GetAutofillTable() const {
+ return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
new file mode 100644
index 00000000000..cd350bec322
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
@@ -0,0 +1,95 @@
+// 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_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOCOMPLETE_SYNC_BRIDGE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOCOMPLETE_SYNC_BRIDGE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/scoped_observer.h"
+#include "base/supports_user_data.h"
+#include "base/threading/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"
+#include "components/sync/model/model_error.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+
+namespace autofill {
+
+class AutofillTable;
+class AutofillWebDataBackend;
+class AutofillWebDataService;
+
+class AutocompleteSyncBridge : public base::SupportsUserData::Data,
+ public syncer::ModelTypeSyncBridge,
+ public AutofillWebDataServiceObserverOnDBThread {
+ public:
+ AutocompleteSyncBridge();
+ AutocompleteSyncBridge(
+ AutofillWebDataBackend* backend,
+ const ChangeProcessorFactory& change_processor_factory);
+ ~AutocompleteSyncBridge() override;
+
+ static void CreateForWebDataServiceAndBackend(
+ AutofillWebDataService* web_data_service,
+ AutofillWebDataBackend* web_data_backend);
+
+ static AutocompleteSyncBridge* FromWebDataService(
+ AutofillWebDataService* web_data_service);
+
+ // syncer::ModelTypeSyncBridge implementation.
+ std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+ override;
+ base::Optional<syncer::ModelError> MergeSyncData(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityDataMap entity_data_map) override;
+ base::Optional<syncer::ModelError> ApplySyncChanges(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_changes) override;
+ void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+ void GetAllData(DataCallback callback) override;
+ // Generate a tag that uniquely identifies |entity_data| across all data
+ // types. This is used to identify the entity on the server. The format, which
+ // must remain the same for server compatibility, is:
+ // "autofill_entry|$name|$value" where $name and $value are escaped.
+ std::string GetClientTag(const syncer::EntityData& entity_data) override;
+ // Generate a string key uniquely identifying |entity_data| in the context of
+ // local storage. The format, which should stay the same, is $name|$value"
+ // where $name and $value are escaped.
+ std::string GetStorageKey(const syncer::EntityData& entity_data) override;
+
+ // AutofillWebDataServiceObserverOnDBThread implementation.
+ void AutofillEntriesChanged(const AutofillChangeList& changes) override;
+
+ private:
+ // Returns the table associated with the |web_data_backend_|.
+ AutofillTable* GetAutofillTable() const;
+
+ // Respond to local autofill entries changing by notifying sync of the
+ // changes.
+ void ActOnLocalChanges(const AutofillChangeList& changes);
+
+ // Synchronously load sync metadata from the autofill table and pass it to the
+ // processor so that it can start tracking changes.
+ void LoadMetadata();
+
+ base::ThreadChecker thread_checker_;
+
+ // AutocompleteSyncBridge is owned by |web_data_backend_| through
+ // SupportsUserData, so it's guaranteed to outlive |this|.
+ AutofillWebDataBackend* const web_data_backend_;
+
+ ScopedObserver<AutofillWebDataBackend, AutocompleteSyncBridge>
+ scoped_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutocompleteSyncBridge);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOCOMPLETE_SYNC_BRIDGE_H_
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
new file mode 100644
index 00000000000..b12d0ef223f
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
@@ -0,0 +1,524 @@
+// 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/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/webdata/autofill_entry.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h"
+#include "components/sync/model/data_batch.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/model_error.h"
+#include "components/sync/model/recording_model_type_change_processor.h"
+#include "components/webdata/common/web_database.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::UTF8ToUTF16;
+using base::ScopedTempDir;
+using base::Time;
+using base::TimeDelta;
+using sync_pb::AutofillSpecifics;
+using sync_pb::EntitySpecifics;
+using syncer::DataBatch;
+using syncer::EntityData;
+using syncer::EntityDataPtr;
+using syncer::EntityChange;
+using syncer::EntityChangeList;
+using syncer::FakeModelTypeChangeProcessor;
+using syncer::KeyAndData;
+using syncer::ModelError;
+using syncer::ModelType;
+using syncer::ModelTypeChangeProcessor;
+using syncer::ModelTypeSyncBridge;
+
+namespace autofill {
+
+namespace {
+
+const char kNameFormat[] = "name %d";
+const char kValueFormat[] = "value %d";
+
+void VerifyEqual(const AutofillSpecifics& s1, const AutofillSpecifics& s2) {
+ // Instead of just comparing serialized strings, manually check fields to show
+ // differences on failure.
+ EXPECT_EQ(s1.name(), s2.name());
+ EXPECT_EQ(s1.value(), s2.value());
+ EXPECT_EQ(s1.usage_timestamp().size(), s2.usage_timestamp().size());
+ int size = std::min(s1.usage_timestamp().size(), s2.usage_timestamp().size());
+ for (int i = 0; i < size; ++i) {
+ EXPECT_EQ(s1.usage_timestamp(i), s2.usage_timestamp(i))
+ << "Values differ at index " << i;
+ }
+ // Neither should have any profile data, so we don't have to compare it.
+ EXPECT_FALSE(s1.has_profile());
+ EXPECT_FALSE(s2.has_profile());
+}
+
+void VerifyDataBatch(std::map<std::string, AutofillSpecifics> expected,
+ std::unique_ptr<DataBatch> batch) {
+ while (batch->HasNext()) {
+ const KeyAndData& pair = batch->Next();
+ auto iter = expected.find(pair.first);
+ ASSERT_NE(iter, expected.end());
+ VerifyEqual(iter->second, pair.second->specifics.autofill());
+ // 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());
+}
+
+AutofillEntry CreateAutofillEntry(const AutofillSpecifics& autofill_specifics) {
+ AutofillKey key(UTF8ToUTF16(autofill_specifics.name()),
+ UTF8ToUTF16(autofill_specifics.value()));
+ Time date_created, date_last_used;
+ const google::protobuf::RepeatedField<int64_t>& timestamps =
+ autofill_specifics.usage_timestamp();
+ if (!timestamps.empty()) {
+ date_created = Time::FromInternalValue(*timestamps.begin());
+ date_last_used = Time::FromInternalValue(*timestamps.rbegin());
+ }
+ return AutofillEntry(key, date_created, date_last_used);
+}
+
+EntityDataPtr SpecificsToEntity(const AutofillSpecifics& specifics) {
+ EntityData data;
+ data.client_tag_hash = "ignored";
+ *data.specifics.mutable_autofill() = specifics;
+ return data.PassToPtr();
+}
+
+class FakeAutofillBackend : public AutofillWebDataBackend {
+ public:
+ FakeAutofillBackend() {}
+ ~FakeAutofillBackend() override {}
+ WebDatabase* GetDatabase() override { return db_; }
+ void AddObserver(
+ autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {}
+ void RemoveObserver(
+ autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {}
+ void RemoveExpiredFormElements() override {}
+ void NotifyOfMultipleAutofillChanges() override {}
+ void NotifyThatSyncHasStarted(ModelType model_type) override {}
+ void SetWebDatabase(WebDatabase* db) { db_ = db; }
+
+ private:
+ WebDatabase* db_;
+};
+
+} // namespace
+
+class AutocompleteSyncBridgeTest : public testing::Test {
+ public:
+ AutocompleteSyncBridgeTest() {
+ if (temp_dir_.CreateUniqueTempDir()) {
+ db_.AddTable(&table_);
+ db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
+ backend_.SetWebDatabase(&db_);
+
+ sync_pb::ModelTypeState model_type_state;
+ model_type_state.set_initial_sync_done(true);
+ table_.UpdateModelTypeState(syncer::AUTOFILL, model_type_state);
+
+ bridge_.reset(new AutocompleteSyncBridge(
+ &backend_,
+ base::Bind(
+ &AutocompleteSyncBridgeTest::CreateModelTypeChangeProcessor,
+ base::Unretained(this))));
+ }
+ }
+ ~AutocompleteSyncBridgeTest() override {}
+
+ void SaveSpecificsToTable(
+ const std::vector<AutofillSpecifics>& specifics_list) {
+ std::vector<AutofillEntry> new_entries;
+ for (const auto& specifics : specifics_list) {
+ new_entries.push_back(CreateAutofillEntry(specifics));
+ }
+ table_.UpdateAutofillEntries(new_entries);
+ }
+
+ AutofillSpecifics CreateSpecifics(const std::string& name,
+ const std::string& value,
+ const std::vector<int>& timestamps) {
+ AutofillSpecifics specifics;
+ specifics.set_name(name);
+ specifics.set_value(value);
+ for (int timestamp : timestamps) {
+ specifics.add_usage_timestamp(
+ Time::FromTimeT(timestamp).ToInternalValue());
+ }
+ return specifics;
+ }
+
+ AutofillSpecifics CreateSpecifics(int suffix,
+ const std::vector<int>& timestamps) {
+ return CreateSpecifics(base::StringPrintf(kNameFormat, suffix),
+ base::StringPrintf(kValueFormat, suffix),
+ timestamps);
+ }
+
+ AutofillSpecifics CreateSpecifics(int suffix) {
+ return CreateSpecifics(suffix, std::vector<int>{0});
+ }
+
+ std::string GetStorageKey(const AutofillSpecifics& specifics) {
+ std::string key =
+ bridge()->GetStorageKey(SpecificsToEntity(specifics).value());
+ EXPECT_FALSE(key.empty());
+ return key;
+ }
+
+ EntityChangeList EntityAddList(
+ const std::vector<AutofillSpecifics>& specifics_vector) {
+ EntityChangeList changes;
+ for (const auto& specifics : specifics_vector) {
+ changes.push_back(EntityChange::CreateAdd(GetStorageKey(specifics),
+ SpecificsToEntity(specifics)));
+ }
+ return changes;
+ }
+
+ void VerifyApplyChanges(const std::vector<EntityChange>& changes) {
+ const auto error = bridge()->ApplySyncChanges(
+ bridge()->CreateMetadataChangeList(), changes);
+ EXPECT_FALSE(error);
+ }
+
+ void VerifyApplyAdds(const std::vector<AutofillSpecifics>& specifics) {
+ VerifyApplyChanges(EntityAddList(specifics));
+ }
+
+ std::map<std::string, AutofillSpecifics> ExpectedMap(
+ const std::vector<AutofillSpecifics>& specifics_vector) {
+ std::map<std::string, AutofillSpecifics> map;
+ for (const auto& specifics : specifics_vector) {
+ map[GetStorageKey(specifics)] = specifics;
+ }
+ return map;
+ }
+
+ void VerifyAllData(const std::vector<AutofillSpecifics>& expected) {
+ bridge()->GetAllData(base::Bind(&VerifyDataBatch, ExpectedMap(expected)));
+ }
+
+ void VerifyProcessorRecordedPut(const AutofillSpecifics& specifics,
+ int position = 0) {
+ const std::string storage_key = GetStorageKey(specifics);
+ auto recorded_specifics_iterator =
+ processor()->put_multimap().find(storage_key);
+
+ EXPECT_NE(processor()->put_multimap().end(), recorded_specifics_iterator);
+ while (position > 0) {
+ recorded_specifics_iterator++;
+ EXPECT_NE(processor()->put_multimap().end(), recorded_specifics_iterator);
+ position--;
+ }
+
+ AutofillSpecifics recorded_specifics =
+ recorded_specifics_iterator->second->specifics.autofill();
+ VerifyEqual(recorded_specifics, specifics);
+ }
+
+ AutocompleteSyncBridge* bridge() { return bridge_.get(); }
+
+ syncer::RecordingModelTypeChangeProcessor* processor() { return processor_; }
+
+ AutofillTable* table() { return &table_; }
+
+ private:
+ std::unique_ptr<syncer::ModelTypeChangeProcessor>
+ CreateModelTypeChangeProcessor(syncer::ModelType type,
+ syncer::ModelTypeSyncBridge* bridge) {
+ auto processor =
+ base::MakeUnique<syncer::RecordingModelTypeChangeProcessor>();
+ processor_ = processor.get();
+ return std::move(processor);
+ }
+
+ ScopedTempDir temp_dir_;
+ base::MessageLoop message_loop_;
+ FakeAutofillBackend backend_;
+ AutofillTable table_;
+ WebDatabase db_;
+ std::unique_ptr<AutocompleteSyncBridge> bridge_;
+ // A non-owning pointer to the processor given to the bridge. Will be null
+ // before being given to the bridge, to make ownership easier.
+ syncer::RecordingModelTypeChangeProcessor* processor_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(AutocompleteSyncBridgeTest);
+};
+
+TEST_F(AutocompleteSyncBridgeTest, GetClientTag) {
+ // TODO(skym, crbug.com/675991): Implementation.
+}
+
+TEST_F(AutocompleteSyncBridgeTest, GetStorageKey) {
+ std::string key = GetStorageKey(CreateSpecifics(1));
+ EXPECT_EQ(key, GetStorageKey(CreateSpecifics(1)));
+ EXPECT_NE(key, GetStorageKey(CreateSpecifics(2)));
+}
+
+// Timestamps should not affect storage keys.
+TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyTimestamp) {
+ AutofillSpecifics specifics = CreateSpecifics(1);
+ std::string key = GetStorageKey(specifics);
+
+ specifics.add_usage_timestamp(1);
+ EXPECT_EQ(key, GetStorageKey(specifics));
+
+ specifics.add_usage_timestamp(0);
+ EXPECT_EQ(key, GetStorageKey(specifics));
+
+ specifics.add_usage_timestamp(-1);
+ EXPECT_EQ(key, GetStorageKey(specifics));
+}
+
+// Verify that the \0 character is respected as a difference.
+TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyNull) {
+ AutofillSpecifics specifics;
+ std::string key = GetStorageKey(specifics);
+
+ specifics.set_value(std::string("\0", 1));
+ EXPECT_NE(key, GetStorageKey(specifics));
+}
+
+// The storage key should never accidentally change for existing data. This
+// would cause lookups to fail and either lose or duplicate user data. It should
+// be possible for the model type to migrate storage key formats, but doing so
+// would need to be done very carefully.
+TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyFixed) {
+ EXPECT_EQ("\n\x6name 1\x12\avalue 1", GetStorageKey(CreateSpecifics(1)));
+ EXPECT_EQ("\n\x6name 2\x12\avalue 2", GetStorageKey(CreateSpecifics(2)));
+ // This literal contains the null terminating character, which causes
+ // std::string to stop copying early if we don't tell it how much to read.
+ EXPECT_EQ(std::string("\n\0\x12\0", 4), GetStorageKey(AutofillSpecifics()));
+ AutofillSpecifics specifics;
+ specifics.set_name("\xEC\xA4\x91");
+ specifics.set_value("\xD0\x80");
+ EXPECT_EQ("\n\x3\xEC\xA4\x91\x12\x2\xD0\x80", GetStorageKey(specifics));
+}
+
+TEST_F(AutocompleteSyncBridgeTest, GetData) {
+ const AutofillSpecifics specifics1 = CreateSpecifics(1);
+ const AutofillSpecifics specifics2 = CreateSpecifics(2);
+ const AutofillSpecifics specifics3 = CreateSpecifics(3);
+ SaveSpecificsToTable({specifics1, specifics2, specifics3});
+ bridge()->GetData(
+ {GetStorageKey(specifics1), GetStorageKey(specifics3)},
+ base::Bind(&VerifyDataBatch, ExpectedMap({specifics1, specifics3})));
+}
+
+TEST_F(AutocompleteSyncBridgeTest, GetDataNotExist) {
+ const AutofillSpecifics specifics1 = CreateSpecifics(1);
+ const AutofillSpecifics specifics2 = CreateSpecifics(2);
+ const AutofillSpecifics specifics3 = CreateSpecifics(3);
+ SaveSpecificsToTable({specifics1, specifics2});
+ bridge()->GetData(
+ {GetStorageKey(specifics1), GetStorageKey(specifics2),
+ GetStorageKey(specifics3)},
+ base::Bind(&VerifyDataBatch, ExpectedMap({specifics1, specifics2})));
+}
+
+TEST_F(AutocompleteSyncBridgeTest, GetAllData) {
+ const AutofillSpecifics specifics1 = CreateSpecifics(1);
+ const AutofillSpecifics specifics2 = CreateSpecifics(2);
+ const AutofillSpecifics specifics3 = CreateSpecifics(3);
+ SaveSpecificsToTable({specifics1, specifics2, specifics3});
+ VerifyAllData({specifics1, specifics2, specifics3});
+}
+
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesEmpty) {
+ // TODO(skym, crbug.com/672619): Ideally would like to verify that the db is
+ // not accessed.
+ VerifyApplyAdds(std::vector<AutofillSpecifics>());
+}
+
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesSimple) {
+ AutofillSpecifics specifics1 = CreateSpecifics(1);
+ AutofillSpecifics specifics2 = CreateSpecifics(2);
+ ASSERT_NE(specifics1.SerializeAsString(), specifics2.SerializeAsString());
+ ASSERT_NE(GetStorageKey(specifics1), GetStorageKey(specifics2));
+
+ VerifyApplyAdds({specifics1, specifics2});
+ VerifyAllData({specifics1, specifics2});
+
+ VerifyApplyChanges({EntityChange::CreateDelete(GetStorageKey(specifics1))});
+ VerifyAllData({specifics2});
+}
+
+// Should be resilient to deleting and updating non-existent things, and adding
+// existing ones.
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesWrongChangeType) {
+ AutofillSpecifics specifics = CreateSpecifics(1, {1});
+ VerifyApplyChanges({EntityChange::CreateDelete(GetStorageKey(specifics))});
+ VerifyAllData(std::vector<AutofillSpecifics>());
+
+ VerifyApplyChanges({EntityChange::CreateUpdate(
+ GetStorageKey(specifics), SpecificsToEntity(specifics))});
+ VerifyAllData({specifics});
+
+ specifics.add_usage_timestamp(Time::FromTimeT(2).ToInternalValue());
+ VerifyApplyAdds({specifics});
+ VerifyAllData({specifics});
+}
+
+// The format in the table has a fixed 2 timestamp format. Round tripping is
+// lossy and the middle value should be thrown out.
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesThreeTimestamps) {
+ VerifyApplyAdds({CreateSpecifics(1, {1, 2, 3})});
+ VerifyAllData({CreateSpecifics(1, {1, 3})});
+}
+
+// The correct format of timestamps is that the first should be smaller and the
+// second should be larger. Bad foreign data should be repaired.
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesWrongOrder) {
+ VerifyApplyAdds({CreateSpecifics(1, {3, 2})});
+ VerifyAllData({CreateSpecifics(1, {2, 3})});
+}
+
+// In a minor attempt to save bandwidth, we only send one of the two timestamps
+// when they share a value.
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesRepeatedTime) {
+ VerifyApplyAdds({CreateSpecifics(1, {2, 2})});
+ VerifyAllData({CreateSpecifics(1, {2})});
+}
+
+// Again, the format in the table is lossy, and cannot distinguish between no
+// time, and valid time zero.
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoTime) {
+ VerifyApplyAdds({CreateSpecifics(1, std::vector<int>())});
+ VerifyAllData({CreateSpecifics(1, {0})});
+}
+
+// If has_value() returns false, then the specifics are determined to be old
+// style and ignored.
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoValue) {
+ AutofillSpecifics input = CreateSpecifics(1, {2, 3});
+ input.clear_value();
+ VerifyApplyAdds({input});
+ VerifyAllData(std::vector<AutofillSpecifics>());
+}
+
+// Should be treated the same as an empty string name. This inconsistency is
+// being perpetuated from the previous sync integration.
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoName) {
+ AutofillSpecifics input = CreateSpecifics(1, {2, 3});
+ input.clear_name();
+ VerifyApplyAdds({input});
+ VerifyAllData({input});
+}
+
+// UTF-8 characters should not be dropped when round tripping, including middle
+// of string \0 characters.
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesUTF) {
+ const AutofillSpecifics specifics =
+ CreateSpecifics(std::string("\n\0\x12\0", 4), "\xEC\xA4\x91", {1});
+ VerifyApplyAdds({specifics});
+ VerifyAllData({specifics});
+}
+
+// Timestamps should always use the oldest creation time, and the most recent
+// usage time.
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesMinMaxTimestamps) {
+ const AutofillSpecifics initial = CreateSpecifics(1, {3, 6});
+ VerifyApplyAdds({initial});
+ VerifyAllData({initial});
+
+ VerifyApplyAdds({CreateSpecifics(1, {2, 5})});
+ VerifyAllData({CreateSpecifics(1, {2, 6})});
+
+ VerifyApplyAdds({CreateSpecifics(1, {4, 7})});
+ VerifyAllData({CreateSpecifics(1, {2, 7})});
+}
+
+// An error should be generated when parsing the storage key happens. This
+// should never happen in practice because storage keys should be generated at
+// runtime by the bridge and not loaded from disk.
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesBadStorageKey) {
+ const auto error = bridge()->ApplySyncChanges(
+ bridge()->CreateMetadataChangeList(),
+ {EntityChange::CreateDelete("bogus storage key")});
+ EXPECT_TRUE(error);
+}
+
+TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesDatabaseFailure) {
+ // TODO(skym, crbug.com/672619): Should have tests that get false back when
+ // making db calls and verify the errors are propagated up.
+}
+
+TEST_F(AutocompleteSyncBridgeTest, LocalEntriesAdded) {
+ const AutofillSpecifics added_specifics1 = CreateSpecifics(1, {2, 3});
+ const AutofillSpecifics added_specifics2 = CreateSpecifics(2, {2, 3});
+
+ const AutofillEntry added_entry1 = CreateAutofillEntry(added_specifics1);
+ const AutofillEntry added_entry2 = CreateAutofillEntry(added_specifics2);
+
+ table()->UpdateAutofillEntries({added_entry1, added_entry2});
+
+ bridge()->AutofillEntriesChanged(
+ {AutofillChange(AutofillChange::ADD, added_entry1.key()),
+ AutofillChange(AutofillChange::ADD, added_entry2.key())});
+
+ EXPECT_EQ(2u, processor()->put_multimap().size());
+
+ VerifyProcessorRecordedPut(added_specifics1);
+ VerifyProcessorRecordedPut(added_specifics2);
+}
+
+TEST_F(AutocompleteSyncBridgeTest, LocalEntryAddedThenUpdated) {
+ const AutofillSpecifics added_specifics = CreateSpecifics(1, {2, 3});
+ const AutofillEntry added_entry = CreateAutofillEntry(added_specifics);
+ table()->UpdateAutofillEntries({added_entry});
+
+ bridge()->AutofillEntriesChanged(
+ {AutofillChange(AutofillChange::ADD, added_entry.key())});
+
+ EXPECT_EQ(1u, processor()->put_multimap().size());
+
+ VerifyProcessorRecordedPut(added_specifics);
+
+ const AutofillSpecifics updated_specifics = CreateSpecifics(1, {2, 4});
+ const AutofillEntry updated_entry = CreateAutofillEntry(updated_specifics);
+ table()->UpdateAutofillEntries({updated_entry});
+
+ bridge()->AutofillEntriesChanged(
+ {AutofillChange(AutofillChange::UPDATE, updated_entry.key())});
+
+ VerifyProcessorRecordedPut(updated_specifics, 1);
+}
+
+TEST_F(AutocompleteSyncBridgeTest, LocalEntryDeleted) {
+ const AutofillSpecifics deleted_specifics = CreateSpecifics(1, {2, 3});
+ const AutofillEntry deleted_entry = CreateAutofillEntry(deleted_specifics);
+ const std::string storage_key = GetStorageKey(deleted_specifics);
+
+ bridge()->AutofillEntriesChanged(
+ {AutofillChange(AutofillChange::REMOVE, deleted_entry.key())});
+
+ EXPECT_EQ(1u, processor()->delete_set().size());
+ EXPECT_NE(processor()->delete_set().end(),
+ processor()->delete_set().find(storage_key));
+}
+
+TEST_F(AutocompleteSyncBridgeTest, LoadMetadataCalled) {
+ EXPECT_NE(processor()->metadata(), nullptr);
+ EXPECT_TRUE(processor()->metadata()->GetModelTypeState().initial_sync_done());
+}
+
+} // namespace autofill
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 76fb7fbdbcc..aff312184ea 100644
--- a/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc
@@ -317,7 +317,7 @@ void AutocompleteSyncableService::CreateOrUpdateEntry(
if (it == loaded_data->end()) {
// New entry.
base::Time date_created, date_last_used;
- if (timestamps.size() > 0) {
+ if (!timestamps.empty()) {
date_created = base::Time::FromInternalValue(*timestamps.begin());
date_last_used = base::Time::FromInternalValue(*timestamps.rbegin());
}
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.cc b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.cc
index d1399bfc3a2..fc3beeae20d 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.cc
@@ -4,6 +4,8 @@
#include "components/autofill/core/browser/webdata/autofill_data_type_controller.h"
+#include <utility>
+
#include "base/bind.h"
#include "base/metrics/histogram.h"
#include "components/autofill/core/browser/webdata/autocomplete_syncable_service.h"
@@ -14,18 +16,17 @@
namespace browser_sync {
AutofillDataTypeController::AutofillDataTypeController(
- const scoped_refptr<base::SingleThreadTaskRunner>& db_thread,
+ scoped_refptr<base::SingleThreadTaskRunner> db_thread,
const base::Closure& dump_stack,
syncer::SyncClient* sync_client,
const scoped_refptr<autofill::AutofillWebDataService>& web_data_service)
- : NonUIDataTypeController(syncer::AUTOFILL, dump_stack, sync_client),
- db_thread_(db_thread),
+ : AsyncDirectoryTypeController(syncer::AUTOFILL,
+ dump_stack,
+ sync_client,
+ syncer::GROUP_DB,
+ std::move(db_thread)),
web_data_service_(web_data_service) {}
-syncer::ModelSafeGroup AutofillDataTypeController::model_safe_group() const {
- return syncer::GROUP_DB;
-}
-
void AutofillDataTypeController::WebDatabaseLoaded() {
DCHECK(CalledOnValidThread());
DCHECK_EQ(MODEL_STARTING, state());
@@ -37,13 +38,6 @@ AutofillDataTypeController::~AutofillDataTypeController() {
DCHECK(CalledOnValidThread());
}
-bool AutofillDataTypeController::PostTaskOnBackendThread(
- const tracked_objects::Location& from_here,
- const base::Closure& task) {
- DCHECK(CalledOnValidThread());
- return db_thread_->PostTask(from_here, task);
-}
-
bool AutofillDataTypeController::StartModels() {
DCHECK(CalledOnValidThread());
DCHECK_EQ(MODEL_STARTING, state());
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.h b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.h
index 90559399957..bccdbf7d9f7 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.h
@@ -11,7 +11,7 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "components/sync/driver/non_ui_data_type_controller.h"
+#include "components/sync/driver/async_directory_type_controller.h"
namespace autofill {
class AutofillWebDataService;
@@ -20,23 +20,18 @@ class AutofillWebDataService;
namespace browser_sync {
// A class that manages the startup and shutdown of autofill sync.
-class AutofillDataTypeController : public syncer::NonUIDataTypeController {
+class AutofillDataTypeController : public syncer::AsyncDirectoryTypeController {
public:
// |dump_stack| is called when an unrecoverable error occurs.
AutofillDataTypeController(
- const scoped_refptr<base::SingleThreadTaskRunner>& db_thread,
+ scoped_refptr<base::SingleThreadTaskRunner> db_thread,
const base::Closure& dump_stack,
syncer::SyncClient* sync_client,
const scoped_refptr<autofill::AutofillWebDataService>& web_data_service);
~AutofillDataTypeController() override;
- // NonUIDataTypeController implementation.
- syncer::ModelSafeGroup model_safe_group() const override;
-
protected:
- // NonUIDataTypeController implementation.
- bool PostTaskOnBackendThread(const tracked_objects::Location& from_here,
- const base::Closure& task) override;
+ // AsyncDirectoryTypeController implementation.
bool StartModels() override;
private:
@@ -47,9 +42,6 @@ class AutofillDataTypeController : public syncer::NonUIDataTypeController {
// Callback once WebDatabase has loaded.
void WebDatabaseLoaded();
- // A reference to the DB thread's task runner.
- const scoped_refptr<base::SingleThreadTaskRunner> db_thread_;
-
// A reference to the AutofillWebDataService for this controller.
scoped_refptr<autofill::AutofillWebDataService> web_data_service_;
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller_unittest.cc
index b0ac2cd33df..90aa68b77d8 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller_unittest.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller_unittest.cc
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_entry.cc b/chromium/components/autofill/core/browser/webdata/autofill_entry.cc
index 83fd639bbbd..58494a7c93e 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_entry.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_entry.cc
@@ -38,6 +38,8 @@ bool AutofillKey::operator<(const AutofillKey& key) const {
return std::tie(name_, value_) < std::tie(key.name(), key.value());
}
+AutofillEntry::AutofillEntry() {}
+
AutofillEntry::AutofillEntry(const AutofillKey& key,
const base::Time& date_created,
const base::Time& date_last_used)
@@ -53,6 +55,10 @@ bool AutofillEntry::operator==(const AutofillEntry& entry) const {
date_last_used() == entry.date_last_used();
}
+bool AutofillEntry::operator!=(const AutofillEntry& entry) const {
+ return !(*this == entry);
+}
+
bool AutofillEntry::operator<(const AutofillEntry& entry) const {
return key_ < entry.key();
}
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_entry.h b/chromium/components/autofill/core/browser/webdata/autofill_entry.h
index 1edc4010dd5..4a934dd4510 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_entry.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_entry.h
@@ -31,6 +31,7 @@ class AutofillKey {
class AutofillEntry {
public:
+ AutofillEntry();
AutofillEntry(const AutofillKey& key,
const base::Time& date_created,
const base::Time& date_last_used);
@@ -41,6 +42,7 @@ class AutofillEntry {
const base::Time& date_last_used() const { return date_last_used_; }
bool operator==(const AutofillEntry& entry) const;
+ bool operator!=(const AutofillEntry& entry) const;
bool operator<(const AutofillEntry& entry) const;
private:
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.cc b/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.cc
new file mode 100644
index 00000000000..24876e4a0cf
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.cc
@@ -0,0 +1,75 @@
+// 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/autofill/core/browser/webdata/autofill_metadata_change_list.h"
+
+#include "base/location.h"
+
+using base::Optional;
+using syncer::ModelError;
+
+namespace autofill {
+
+AutofillMetadataChangeList::AutofillMetadataChangeList(AutofillTable* table,
+ syncer::ModelType type)
+ : table_(table), type_(type) {
+ DCHECK(table_);
+ // This should be changed as new autofill types are converted to USS.
+ DCHECK_EQ(syncer::AUTOFILL, type_);
+}
+
+AutofillMetadataChangeList::~AutofillMetadataChangeList() {
+ DCHECK(!error_);
+}
+
+void AutofillMetadataChangeList::UpdateModelTypeState(
+ const sync_pb::ModelTypeState& model_type_state) {
+ if (error_) {
+ return;
+ }
+
+ if (!table_->UpdateModelTypeState(type_, model_type_state)) {
+ error_ = ModelError(FROM_HERE, "Failed to update ModelTypeState.");
+ }
+}
+
+void AutofillMetadataChangeList::ClearModelTypeState() {
+ if (error_) {
+ return;
+ }
+
+ if (!table_->ClearModelTypeState(type_)) {
+ error_ = ModelError(FROM_HERE, "Failed to clear ModelTypeState.");
+ }
+}
+
+void AutofillMetadataChangeList::UpdateMetadata(
+ const std::string& storage_key,
+ const sync_pb::EntityMetadata& metadata) {
+ if (error_) {
+ return;
+ }
+
+ if (!table_->UpdateSyncMetadata(type_, storage_key, metadata)) {
+ error_ = ModelError(FROM_HERE, "Failed to update entity metadata.");
+ }
+}
+
+void AutofillMetadataChangeList::ClearMetadata(const std::string& storage_key) {
+ if (error_) {
+ return;
+ }
+
+ if (!table_->ClearSyncMetadata(type_, storage_key)) {
+ error_ = ModelError(FROM_HERE, "Failed to clear entity metadata.");
+ }
+}
+
+Optional<ModelError> AutofillMetadataChangeList::TakeError() {
+ Optional<ModelError> temp = error_;
+ error_.reset();
+ return temp;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.h b/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.h
new file mode 100644
index 00000000000..e924e2070a6
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.h
@@ -0,0 +1,55 @@
+// 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_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_METADATA_CHANGE_LIST_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_METADATA_CHANGE_LIST_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/model/metadata_change_list.h"
+#include "components/sync/model/model_error.h"
+#include "components/sync/model/sync_error.h"
+#include "components/sync/protocol/entity_metadata.pb.h"
+#include "components/sync/protocol/model_type_state.pb.h"
+
+namespace autofill {
+
+// A thin wrapper around an AutofillTable that implements sync's
+// MetadataChangeList interface. Changes are passed directly into the table and
+// not stored inside this object. Since the table calls can fail, |TakeError()|
+// must be called before this object is destroyed to check whether any
+// operations failed.
+class AutofillMetadataChangeList : public syncer::MetadataChangeList {
+ public:
+ AutofillMetadataChangeList(AutofillTable* table, syncer::ModelType type);
+ ~AutofillMetadataChangeList() override;
+
+ // syncer::MetadataChangeList implementation.
+ void UpdateModelTypeState(
+ const sync_pb::ModelTypeState& model_type_state) override;
+ void ClearModelTypeState() override;
+ void UpdateMetadata(const std::string& storage_key,
+ const sync_pb::EntityMetadata& metadata) override;
+ void ClearMetadata(const std::string& storage_key) override;
+
+ // Returns the value of |error_| and unsets it.
+ base::Optional<syncer::ModelError> TakeError();
+
+ private:
+ // The autofill table to store metadata in; always outlives |this|.
+ AutofillTable* table_;
+
+ // The sync model type for this metadata.
+ syncer::ModelType type_;
+
+ // The first error encountered by this object, if any.
+ base::Optional<syncer::ModelError> error_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_METADATA_CHANGE_LIST_H_
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.cc
index 9d0f57057d8..f883c970e26 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.cc
@@ -4,6 +4,8 @@
#include "components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h"
+#include <utility>
+
#include "base/bind.h"
#include "base/metrics/histogram.h"
#include "components/autofill/core/browser/personal_data_manager.h"
@@ -17,23 +19,19 @@ using autofill::AutofillWebDataService;
namespace browser_sync {
AutofillProfileDataTypeController::AutofillProfileDataTypeController(
- const scoped_refptr<base::SingleThreadTaskRunner>& db_thread,
+ scoped_refptr<base::SingleThreadTaskRunner> db_thread,
const base::Closure& dump_stack,
syncer::SyncClient* sync_client,
const scoped_refptr<autofill::AutofillWebDataService>& web_data_service)
- : NonUIDataTypeController(syncer::AUTOFILL_PROFILE,
- dump_stack,
- sync_client),
- db_thread_(db_thread),
+ : AsyncDirectoryTypeController(syncer::AUTOFILL_PROFILE,
+ dump_stack,
+ sync_client,
+ syncer::GROUP_DB,
+ std::move(db_thread)),
sync_client_(sync_client),
web_data_service_(web_data_service),
callback_registered_(false) {}
-syncer::ModelSafeGroup AutofillProfileDataTypeController::model_safe_group()
- const {
- return syncer::GROUP_DB;
-}
-
void AutofillProfileDataTypeController::WebDatabaseLoaded() {
DCHECK(CalledOnValidThread());
OnModelLoaded();
@@ -60,13 +58,6 @@ void AutofillProfileDataTypeController::OnPersonalDataChanged() {
AutofillProfileDataTypeController::~AutofillProfileDataTypeController() {}
-bool AutofillProfileDataTypeController::PostTaskOnBackendThread(
- const tracked_objects::Location& from_here,
- const base::Closure& task) {
- DCHECK(CalledOnValidThread());
- return db_thread_->PostTask(from_here, task);
-}
-
bool AutofillProfileDataTypeController::StartModels() {
DCHECK(CalledOnValidThread());
DCHECK_EQ(state(), MODEL_STARTING);
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h b/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h
index cf68f4af67d..6dd5fd55893 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h
@@ -10,38 +10,32 @@
#include "base/memory/ref_counted.h"
#include "base/scoped_observer.h"
#include "components/autofill/core/browser/personal_data_manager_observer.h"
-#include "components/sync/driver/non_ui_data_type_controller.h"
+#include "components/sync/driver/async_directory_type_controller.h"
namespace autofill {
class AutofillWebDataService;
-class PersonalDataManager;
} // namespace autofill
namespace browser_sync {
// Controls syncing of the AUTOFILL_PROFILE data type.
class AutofillProfileDataTypeController
- : public syncer::NonUIDataTypeController,
+ : public syncer::AsyncDirectoryTypeController,
public autofill::PersonalDataManagerObserver {
public:
// |dump_stack| is called when an unrecoverable error occurs.
AutofillProfileDataTypeController(
- const scoped_refptr<base::SingleThreadTaskRunner>& db_thread,
+ scoped_refptr<base::SingleThreadTaskRunner> db_thread,
const base::Closure& dump_stack,
syncer::SyncClient* sync_client,
const scoped_refptr<autofill::AutofillWebDataService>& web_data_service);
~AutofillProfileDataTypeController() override;
- // NonUIDataTypeController:
- syncer::ModelSafeGroup model_safe_group() const override;
-
// PersonalDataManagerObserver:
void OnPersonalDataChanged() override;
protected:
- // NonUIDataTypeController:
- bool PostTaskOnBackendThread(const tracked_objects::Location& from_here,
- const base::Closure& task) override;
+ // AsyncDirectoryTypeController:
bool StartModels() override;
void StopModels() override;
@@ -49,9 +43,6 @@ class AutofillProfileDataTypeController
// Callback to notify that WebDatabase has loaded.
void WebDatabaseLoaded();
- // A reference to the DB thread's task runner.
- const scoped_refptr<base::SingleThreadTaskRunner> db_thread_;
-
// A pointer to the sync client.
syncer::SyncClient* const sync_client_;
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
index 88867361266..503eb0fa9e7 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
@@ -35,6 +35,10 @@
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/os_crypt/os_crypt.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/protocol/entity_metadata.pb.h"
+#include "components/sync/protocol/model_type_state.pb.h"
#include "components/webdata/common/web_database.h"
#include "sql/statement.h"
#include "sql/transaction.h"
@@ -416,7 +420,8 @@ bool AutofillTable::CreateTablesIfNecessary() {
InitProfilePhonesTable() && InitProfileTrashTable() &&
InitMaskedCreditCardsTable() && InitUnmaskedCreditCardsTable() &&
InitServerCardMetadataTable() && InitServerAddressesTable() &&
- InitServerAddressMetadataTable());
+ InitServerAddressMetadataTable() && InitAutofillSyncMetadataTable() &&
+ InitModelTypeStateTable());
}
bool AutofillTable::IsSyncable() {
@@ -463,6 +468,12 @@ bool AutofillTable::MigrateToVersion(int version,
case 67:
*update_compatible_version = false;
return MigrateToVersion67AddMaskedCardBillingAddress();
+ case 70:
+ *update_compatible_version = false;
+ return MigrateToVersion70AddSyncMetadata();
+ case 71:
+ *update_compatible_version = true;
+ return MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata();
}
return true;
}
@@ -934,7 +945,7 @@ bool AutofillTable::GetAutofillProfiles(
}
bool AutofillTable::GetServerProfiles(
- std::vector<std::unique_ptr<AutofillProfile>>* profiles) {
+ std::vector<std::unique_ptr<AutofillProfile>>* profiles) const {
profiles->clear();
sql::Statement s(db_->GetUniqueStatement(
@@ -1197,22 +1208,22 @@ bool AutofillTable::GetCreditCards(
}
bool AutofillTable::GetServerCreditCards(
- std::vector<std::unique_ptr<CreditCard>>* credit_cards) {
+ std::vector<std::unique_ptr<CreditCard>>* credit_cards) const {
credit_cards->clear();
sql::Statement s(db_->GetUniqueStatement(
"SELECT "
- "card_number_encrypted, " // 0
- "last_four," // 1
- "masked.id," // 2
- "metadata.use_count," // 3
- "metadata.use_date," // 4
- "type," // 5
- "status," // 6
- "name_on_card," // 7
- "exp_month," // 8
- "exp_year," // 9
- "billing_address_id " // 10
+ "card_number_encrypted, " // 0
+ "last_four," // 1
+ "masked.id," // 2
+ "metadata.use_count," // 3
+ "metadata.use_date," // 4
+ "type," // 5
+ "status," // 6
+ "name_on_card," // 7
+ "exp_month," // 8
+ "exp_year," // 9
+ "metadata.billing_address_id " // 10
"FROM masked_credit_cards masked "
"LEFT OUTER JOIN unmasked_credit_cards USING (id) "
"LEFT OUTER JOIN server_card_metadata metadata USING (id)"));
@@ -1271,17 +1282,16 @@ void AutofillTable::SetServerCreditCards(
"DELETE FROM masked_credit_cards"));
masked_delete.Run();
- sql::Statement masked_insert(db_->GetUniqueStatement(
- "INSERT INTO masked_credit_cards("
- "id," // 0
- "type," // 1
- "status," // 2
- "name_on_card," // 3
- "last_four," // 4
- "exp_month," // 5
- "exp_year," // 6
- "billing_address_id) " // 7
- "VALUES (?,?,?,?,?,?,?,?)"));
+ sql::Statement masked_insert(
+ db_->GetUniqueStatement("INSERT INTO masked_credit_cards("
+ "id," // 0
+ "type," // 1
+ "status," // 2
+ "name_on_card," // 3
+ "last_four," // 4
+ "exp_month," // 5
+ "exp_year)" // 6
+ "VALUES (?,?,?,?,?,?,?)"));
for (const CreditCard& card : credit_cards) {
DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
@@ -1294,13 +1304,12 @@ void AutofillTable::SetServerCreditCards(
masked_insert.BindString16(5, card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
masked_insert.BindString16(6,
card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
- masked_insert.BindString(7, card.billing_address_id());
masked_insert.Run();
masked_insert.Reset(true);
// Save the use count and use date of the card.
- UpdateServerCardUsageStats(card);
+ UpdateServerCardMetadata(card);
}
// Delete all items in the unmasked table that aren't in the new set.
@@ -1341,7 +1350,7 @@ bool AutofillTable::UnmaskServerCreditCard(const CreditCard& masked,
unmasked.set_record_type(CreditCard::FULL_SERVER_CARD);
unmasked.SetNumber(full_number);
unmasked.RecordAndLogUse();
- UpdateServerCardUsageStats(unmasked);
+ UpdateServerCardMetadata(unmasked);
return db_->GetLastChangeCount() > 0;
}
@@ -1354,8 +1363,7 @@ bool AutofillTable::MaskServerCreditCard(const std::string& id) {
return db_->GetLastChangeCount() > 0;
}
-bool AutofillTable::UpdateServerCardUsageStats(
- const CreditCard& credit_card) {
+bool AutofillTable::UpdateServerCardMetadata(const CreditCard& credit_card) {
DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
sql::Transaction transaction(db_);
if (!transaction.Begin())
@@ -1366,12 +1374,14 @@ bool AutofillTable::UpdateServerCardUsageStats(
remove.BindString(0, credit_card.server_id());
remove.Run();
- sql::Statement s(db_->GetUniqueStatement(
- "INSERT INTO server_card_metadata(use_count, use_date, id)"
- "VALUES (?,?,?)"));
+ sql::Statement s(
+ db_->GetUniqueStatement("INSERT INTO server_card_metadata(use_count, "
+ "use_date, billing_address_id, id)"
+ "VALUES (?,?,?,?)"));
s.BindInt64(0, credit_card.use_count());
s.BindInt64(1, credit_card.use_date().ToInternalValue());
- s.BindString(2, credit_card.server_id());
+ s.BindString(2, credit_card.billing_address_id());
+ s.BindString(3, credit_card.server_id());
s.Run();
transaction.Commit();
@@ -1379,7 +1389,9 @@ bool AutofillTable::UpdateServerCardUsageStats(
return db_->GetLastChangeCount() > 0;
}
-bool AutofillTable::UpdateServerAddressUsageStats(
+// TODO(crbug.com/680182): Record the address conversion status when a server
+// address gets converted.
+bool AutofillTable::UpdateServerAddressMetadata(
const AutofillProfile& profile) {
DCHECK_EQ(AutofillProfile::SERVER_PROFILE, profile.record_type());
@@ -1392,12 +1404,14 @@ bool AutofillTable::UpdateServerAddressUsageStats(
remove.BindString(0, profile.server_id());
remove.Run();
- sql::Statement s(db_->GetUniqueStatement(
- "INSERT INTO server_address_metadata(use_count, use_date, id)"
- "VALUES (?,?,?)"));
+ sql::Statement s(
+ db_->GetUniqueStatement("INSERT INTO server_address_metadata(use_count, "
+ "use_date, has_converted, id)"
+ "VALUES (?,?,?,?)"));
s.BindInt64(0, profile.use_count());
s.BindInt64(1, profile.use_date().ToInternalValue());
- s.BindString(2, profile.server_id());
+ s.BindBool(2, false);
+ s.BindString(3, profile.server_id());
s.Run();
transaction.Commit();
@@ -1405,21 +1419,6 @@ bool AutofillTable::UpdateServerAddressUsageStats(
return db_->GetLastChangeCount() > 0;
}
-bool AutofillTable::UpdateServerCardBillingAddress(
- const CreditCard& credit_card) {
- DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
-
- sql::Statement update(db_->GetUniqueStatement(
- "UPDATE masked_credit_cards SET billing_address_id = ? "
- "WHERE id = ?"));
- update.BindString(0, credit_card.billing_address_id());
- update.BindString(1, credit_card.server_id());
- if (!update.Run())
- return false;
-
- return db_->GetLastChangeCount() > 0;
-}
-
bool AutofillTable::ClearAllServerData() {
sql::Transaction transaction(db_);
if (!transaction.Begin())
@@ -1680,6 +1679,122 @@ bool AutofillTable::IsAutofillGUIDInTrash(const std::string& guid) {
return s.Step();
}
+bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type,
+ syncer::MetadataBatch* metadata_batch) {
+ DCHECK_EQ(model_type, syncer::AUTOFILL)
+ << "Only the AUTOFILL model type is supported";
+ syncer::EntityMetadataMap metadata_records;
+ if (GetAllSyncEntityMetadata(model_type, &metadata_records)) {
+ for (const auto& pair : metadata_records) {
+ // todo(pnoland): add batch transfer of metadata map
+ metadata_batch->AddMetadata(pair.first, pair.second);
+ }
+ } else {
+ return false;
+ }
+
+ sync_pb::ModelTypeState model_type_state;
+ if (GetModelTypeState(model_type, &model_type_state)) {
+ metadata_batch->SetModelTypeState(model_type_state);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::GetAllSyncEntityMetadata(
+ syncer::ModelType model_type,
+ syncer::EntityMetadataMap* metadata_records) {
+ DCHECK_EQ(model_type, syncer::AUTOFILL)
+ << "Only the AUTOFILL model type is supported";
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT storage_key, value FROM autofill_sync_metadata"));
+
+ while (s.Step()) {
+ std::string storage_key = s.ColumnString(0);
+ std::string serialized_metadata = s.ColumnString(1);
+ sync_pb::EntityMetadata metadata_record;
+ if (metadata_record.ParseFromString(serialized_metadata)) {
+ metadata_records->insert(std::make_pair(storage_key, metadata_record));
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::UpdateSyncMetadata(
+ syncer::ModelType model_type,
+ const std::string& storage_key,
+ const sync_pb::EntityMetadata& metadata) {
+ DCHECK_EQ(model_type, syncer::AUTOFILL)
+ << "Only the AUTOFILL model type is supported";
+
+ sql::Statement s(
+ db_->GetUniqueStatement("INSERT OR REPLACE INTO autofill_sync_metadata "
+ "(storage_key, value) VALUES(?, ?)"));
+ s.BindString(0, storage_key);
+ s.BindString(1, metadata.SerializeAsString());
+
+ return s.Run();
+}
+
+bool AutofillTable::ClearSyncMetadata(syncer::ModelType model_type,
+ const std::string& storage_key) {
+ DCHECK_EQ(model_type, syncer::AUTOFILL)
+ << "Only the AUTOFILL model type is supported";
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill_sync_metadata WHERE storage_key=?"));
+ s.BindString(0, storage_key);
+
+ return s.Run();
+}
+
+bool AutofillTable::GetModelTypeState(syncer::ModelType model_type,
+ sync_pb::ModelTypeState* state) {
+ DCHECK_EQ(model_type, syncer::AUTOFILL)
+ << "Only the AUTOFILL model type is supported";
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT value FROM autofill_model_type_state WHERE id=1"));
+
+ if (!s.Step()) {
+ return false;
+ }
+
+ std::string serialized_state = s.ColumnString(0);
+ return state->ParseFromString(serialized_state);
+}
+
+bool AutofillTable::UpdateModelTypeState(
+ syncer::ModelType model_type,
+ const sync_pb::ModelTypeState& model_type_state) {
+ DCHECK_EQ(model_type, syncer::AUTOFILL)
+ << "Only the AUTOFILL model type is supported";
+
+ // Hardcode the id to force a collision, ensuring that there remains only a
+ // single entry.
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT OR REPLACE INTO autofill_model_type_state (id, value) "
+ "VALUES(1,?)"));
+ s.BindString(0, model_type_state.SerializeAsString());
+
+ return s.Run();
+}
+
+bool AutofillTable::ClearModelTypeState(syncer::ModelType model_type) {
+ DCHECK_EQ(model_type, syncer::AUTOFILL)
+ << "Only the AUTOFILL model type is supported";
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill_model_type_state WHERE id=1"));
+
+ return s.Run();
+}
+
bool AutofillTable::InitMainTable() {
if (!db_->DoesTableExist("autofill")) {
if (!db_->Execute("CREATE TABLE autofill ("
@@ -1804,8 +1919,7 @@ bool AutofillTable::InitMaskedCreditCardsTable() {
"type VARCHAR,"
"last_four VARCHAR,"
"exp_month INTEGER DEFAULT 0,"
- "exp_year INTEGER DEFAULT 0, "
- "billing_address_id VARCHAR)")) {
+ "exp_year INTEGER DEFAULT 0)")) {
NOTREACHED();
return false;
}
@@ -1833,7 +1947,8 @@ bool AutofillTable::InitServerCardMetadataTable() {
if (!db_->Execute("CREATE TABLE server_card_metadata ("
"id VARCHAR NOT NULL,"
"use_count INTEGER NOT NULL DEFAULT 0, "
- "use_date INTEGER NOT NULL DEFAULT 0)")) {
+ "use_date INTEGER NOT NULL DEFAULT 0, "
+ "billing_address_id VARCHAR)")) {
NOTREACHED();
return false;
}
@@ -1871,7 +1986,31 @@ bool AutofillTable::InitServerAddressMetadataTable() {
if (!db_->Execute("CREATE TABLE server_address_metadata ("
"id VARCHAR NOT NULL,"
"use_count INTEGER NOT NULL DEFAULT 0, "
- "use_date INTEGER NOT NULL DEFAULT 0)")) {
+ "use_date INTEGER NOT NULL DEFAULT 0, "
+ "has_converted BOOL NOT NULL DEFAULT FALSE)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitAutofillSyncMetadataTable() {
+ if (!db_->DoesTableExist("autofill_sync_metadata")) {
+ if (!db_->Execute("CREATE TABLE autofill_sync_metadata ("
+ "storage_key VARCHAR PRIMARY KEY NOT NULL,"
+ "value BLOB)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitModelTypeStateTable() {
+ if (!db_->DoesTableExist("autofill_model_type_state")) {
+ if (!db_->Execute("CREATE TABLE autofill_model_type_state (id INTEGER "
+ "PRIMARY KEY, value BLOB)")) {
NOTREACHED();
return false;
}
@@ -2315,4 +2454,77 @@ bool AutofillTable::MigrateToVersion67AddMaskedCardBillingAddress() {
"ALTER TABLE masked_credit_cards ADD COLUMN billing_address_id VARCHAR");
}
-} // namespace autofill
+bool AutofillTable::MigrateToVersion70AddSyncMetadata() {
+ if (!db_->Execute("CREATE TABLE autofill_sync_metadata ("
+ "storage_key VARCHAR PRIMARY KEY NOT NULL,"
+ "value BLOB)")) {
+ return false;
+ }
+ return db_->Execute(
+ "CREATE TABLE autofill_model_type_state (id INTEGER PRIMARY KEY, value "
+ "BLOB)");
+}
+
+bool AutofillTable::
+ MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata() {
+ sql::Transaction transaction(db_);
+ if (!transaction.Begin())
+ return false;
+
+ // Add the new has_converted column to the server_address_metadata table.
+ if (!db_->DoesColumnExist("server_address_metadata", "has_converted") &&
+ !db_->Execute("ALTER TABLE server_address_metadata ADD COLUMN "
+ "has_converted BOOL NOT NULL DEFAULT FALSE")) {
+ return false;
+ }
+
+ // Add the new billing_address_id column to the server_card_metadata table.
+ if (!db_->DoesColumnExist("server_card_metadata", "billing_address_id") &&
+ !db_->Execute("ALTER TABLE server_card_metadata ADD COLUMN "
+ "billing_address_id VARCHAR")) {
+ return false;
+ }
+
+ // Copy over the billing_address_id from the masked_server_cards to
+ // server_card_metadata.
+ if (!db_->Execute("UPDATE server_card_metadata "
+ "SET billing_address_id = "
+ "(SELECT billing_address_id "
+ "FROM masked_credit_cards "
+ "WHERE id = server_card_metadata.id)")) {
+ return false;
+ }
+
+ // Remove the billing_address_id column from the masked_credit_cards table.
+ // Create a temporary table that is a copy of masked_credit_cards but without
+ // the billing_address_id column.
+ if (db_->DoesTableExist("masked_credit_cards_temp") ||
+ !db_->Execute("CREATE TABLE masked_credit_cards_temp ("
+ "id VARCHAR,"
+ "status VARCHAR,"
+ "name_on_card VARCHAR,"
+ "type VARCHAR,"
+ "last_four VARCHAR,"
+ "exp_month INTEGER DEFAULT 0,"
+ "exp_year INTEGER DEFAULT 0)")) {
+ return false;
+ }
+ // Copy over the data from the original masked_credit_cards table.
+ if (!db_->Execute("INSERT INTO masked_credit_cards_temp "
+ "SELECT id, status, name_on_card, type, last_four, "
+ "exp_month, exp_year "
+ "FROM masked_credit_cards")) {
+ return false;
+ }
+ // Delete the existing table and replace it with the contents of the
+ // temporary table.
+ if (!db_->Execute("DROP TABLE masked_credit_cards") ||
+ !db_->Execute("ALTER TABLE masked_credit_cards_temp "
+ "RENAME TO masked_credit_cards")) {
+ return false;
+ }
+
+ return transaction.Commit();
+}
+
+} // namespace autofill \ No newline at end of file
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.h b/chromium/components/autofill/core/browser/webdata/autofill_table.h
index a8eceb0ae1b..2f5fff15431 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h
@@ -13,6 +13,8 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/strings/string16.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/model/metadata_batch.h"
#include "components/webdata/common/web_database_table.h"
class WebDatabase;
@@ -21,6 +23,11 @@ namespace base {
class Time;
}
+namespace sync_pb {
+class EntityMetadata;
+class ModelTypeState;
+}
+
namespace autofill {
class AutofillChange;
@@ -160,10 +167,6 @@ struct FormFieldData;
// with locally stored cards and generating descriptions.
// exp_month Expiration month: 1-12
// exp_year Four-digit year: 2017
-// billing_address_id The guid string that identifies the local profile which
-// is the billing address for this card. Can be null in the
-// database, but always returned as an empty string in
-// CreditCard. Added in version 67.
//
// unmasked_credit_cards
// When a masked credit credit card is unmasked and the
@@ -175,6 +178,7 @@ struct FormFieldData;
// Full card number, encrypted.
// use_count DEPRECATED in version 65. See server_card_metadata.
// use_date DEPRECATED in version 65. See server_card_metadata.
+// TODO(crbug.com/682326): Remove deprecated columns.
// unmask_date The date this card was unmasked in units of
// Time::ToInternalValue. Added in version 64.
//
@@ -188,6 +192,10 @@ struct FormFieldData;
// a form.
// use_date The date this card was last used to fill a form,
// in internal t.
+// billing_address_id The string that identifies the profile which is the
+// billing address for this card. Can be null in the
+// database, but always returned as an empty string in
+// CreditCard. Added in version 71.
//
// server_addresses This table contains Autofill address data synced from
// the wallet server. It's basically the same as the
@@ -228,6 +236,21 @@ struct FormFieldData;
// a form.
// use_date The date this address was last used to fill a form,
// in internal t.
+// has_converted Whether this server address has been converted to a
+// local autofill profile.
+//
+// autofill_sync_metadata
+// Sync-specific metadata for autofill records.
+//
+// storage_key A string that uniquely identifies the metadata record
+// as well as the corresponding autofill record.
+// value The serialized EntityMetadata record.
+//
+// autofill_model_type_state
+// Single row table that contains the sync ModelTypeState
+// for the autofill model type.
+//
+// value The serialized ModelTypeState record.
class AutofillTable : public WebDatabaseTable {
public:
@@ -320,7 +343,7 @@ class AutofillTable : public WebDatabaseTable {
virtual bool GetAutofillProfiles(
std::vector<std::unique_ptr<AutofillProfile>>* profiles);
virtual bool GetServerProfiles(
- std::vector<std::unique_ptr<AutofillProfile>>* profiles);
+ std::vector<std::unique_ptr<AutofillProfile>>* profiles) const;
// Sets the server profiles. All old profiles are deleted and replaced with
// the given ones.
@@ -343,7 +366,7 @@ class AutofillTable : public WebDatabaseTable {
virtual bool GetCreditCards(
std::vector<std::unique_ptr<CreditCard>>* credit_cards);
virtual bool GetServerCreditCards(
- std::vector<std::unique_ptr<CreditCard>>* credit_cards);
+ std::vector<std::unique_ptr<CreditCard>>* credit_cards) const;
// Replaces all server credit cards with the given vector. Unmasked cards
// present in the new list will be preserved (even if the input is MASKED).
@@ -356,10 +379,8 @@ class AutofillTable : public WebDatabaseTable {
const base::string16& full_number);
bool MaskServerCreditCard(const std::string& id);
- bool UpdateServerCardUsageStats(const CreditCard& credit_card);
- bool UpdateServerAddressUsageStats(const AutofillProfile& profile);
-
- bool UpdateServerCardBillingAddress(const CreditCard& credit_card);
+ bool UpdateServerCardMetadata(const CreditCard& credit_card);
+ bool UpdateServerAddressMetadata(const AutofillProfile& profile);
// Deletes all data from the server card and profile tables. Returns true if
// any data was deleted, false if not (so false means "commit not needed"
@@ -403,6 +424,28 @@ class AutofillTable : public WebDatabaseTable {
// Clear all profiles.
bool ClearAutofillProfiles();
+ // Read all the stored metadata for |model_type| and fill |metadata_batch|
+ // with it.
+ bool GetAllSyncMetadata(syncer::ModelType model_type,
+ syncer::MetadataBatch* metadata_batch);
+
+ // Update the metadata row for |model_type|, keyed by |storage_key|, to
+ // contain the contents of |metadata|.
+ bool UpdateSyncMetadata(syncer::ModelType model_type,
+ const std::string& storage_key,
+ const sync_pb::EntityMetadata& metadata);
+
+ // Remove the metadata row of type |model_type| keyed by |storage|key|.
+ bool ClearSyncMetadata(syncer::ModelType model_type,
+ const std::string& storage_key);
+
+ // Update the stored sync state for the |model_type|.
+ bool UpdateModelTypeState(syncer::ModelType model_type,
+ const sync_pb::ModelTypeState& model_type_state);
+
+ // Clear the stored sync state for |model_type|.
+ bool ClearModelTypeState(syncer::ModelType model_type);
+
// Table migration functions. NB: These do not and should not rely on other
// functions in this class. The implementation of a function such as
// GetCreditCard may change over time, but MigrateToVersionXX should never
@@ -419,6 +462,8 @@ class AutofillTable : public WebDatabaseTable {
bool MigrateToVersion65AddServerMetadataTables();
bool MigrateToVersion66AddCardBillingAddress();
bool MigrateToVersion67AddMaskedCardBillingAddress();
+ bool MigrateToVersion70AddSyncMetadata();
+ bool MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata();
// Max data length saved in the table, AKA the maximum length allowed for
// form data.
@@ -474,6 +519,12 @@ class AutofillTable : public WebDatabaseTable {
std::vector<AutofillChange>* changes,
base::Time time);
+ bool GetAllSyncEntityMetadata(syncer::ModelType model_type,
+ syncer::EntityMetadataMap* metadata_records);
+
+ bool GetModelTypeState(syncer::ModelType model_type,
+ sync_pb::ModelTypeState* state);
+
// Insert a single AutofillEntry into the autofill table.
bool InsertAutofillEntry(const AutofillEntry& entry);
@@ -496,6 +547,8 @@ class AutofillTable : public WebDatabaseTable {
bool InitServerCardMetadataTable();
bool InitServerAddressesTable();
bool InitServerAddressMetadataTable();
+ bool InitAutofillSyncMetadataTable();
+ bool InitModelTypeStateTable();
DISALLOW_COPY_AND_ASSIGN(AutofillTable);
};
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 18277b1f121..e8da2b12843 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -35,6 +35,8 @@
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/os_crypt/os_crypt_mocker.h"
+#include "components/sync/protocol/entity_metadata.pb.h"
+#include "components/sync/protocol/model_type_state.pb.h"
#include "components/webdata/common/web_database.h"
#include "sql/statement.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -1716,7 +1718,7 @@ TEST_F(AutofillTableTest, SetServerCardModify) {
outputs.clear();
}
-TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) {
+TEST_F(AutofillTableTest, SetServerCardUpdateUsageStatsAndBillingAddress) {
// Add a masked card.
CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123");
masked_card.SetRawInfo(CREDIT_CARD_NAME_FULL,
@@ -1724,6 +1726,7 @@ TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) {
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.set_billing_address_id("1");
masked_card.SetTypeForMaskedCard(kVisaCard);
std::vector<CreditCard> inputs;
@@ -1744,13 +1747,15 @@ TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) {
// Update the usage stats; make sure they're reflected in GetServerProfiles.
inputs.back().set_use_count(4U);
inputs.back().set_use_date(base::Time());
- table_->UpdateServerCardUsageStats(inputs.back());
+ inputs.back().set_billing_address_id("2");
+ table_->UpdateServerCardMetadata(inputs.back());
table_->GetServerCreditCards(&outputs);
ASSERT_EQ(1u, outputs.size());
EXPECT_EQ(masked_card.server_id(), outputs[0]->server_id());
EXPECT_EQ(4U, outputs[0]->use_count());
EXPECT_EQ(base::Time(), outputs[0]->use_date());
EXPECT_EQ(base::Time(), outputs[0]->modification_date());
+ EXPECT_EQ("2", outputs[0]->billing_address_id());
outputs.clear();
// Setting the cards again shouldn't delete the usage stats.
@@ -1761,6 +1766,7 @@ TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) {
EXPECT_EQ(4U, outputs[0]->use_count());
EXPECT_EQ(base::Time(), outputs[0]->use_date());
EXPECT_EQ(base::Time(), outputs[0]->modification_date());
+ EXPECT_EQ("2", outputs[0]->billing_address_id());
outputs.clear();
// Set a card list where the card is missing --- this should clear metadata.
@@ -1777,36 +1783,10 @@ TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) {
EXPECT_EQ(1U, outputs[0]->use_count());
EXPECT_NE(base::Time(), outputs[0]->use_date());
EXPECT_EQ(base::Time(), outputs[0]->modification_date());
+ EXPECT_EQ("1", outputs[0]->billing_address_id());
outputs.clear();
}
-TEST_F(AutofillTableTest, UpdateServerCardBillingAddress) {
- // 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.set_billing_address_id("billing-address-id-1");
- masked_card.SetTypeForMaskedCard(kVisaCard);
- test::SetServerCreditCards(table_.get(),
- std::vector<CreditCard>(1, masked_card));
- std::vector<std::unique_ptr<CreditCard>> outputs;
- table_->GetServerCreditCards(&outputs);
- ASSERT_EQ(1u, outputs.size());
-
- EXPECT_EQ("billing-address-id-1", outputs[0]->billing_address_id());
-
- masked_card.set_billing_address_id("billing-address-id-2");
- table_->UpdateServerCardBillingAddress(masked_card);
- outputs.clear();
- table_->GetServerCreditCards(&outputs);
- ASSERT_EQ(1u, outputs.size());
-
- EXPECT_EQ("billing-address-id-2", outputs[0]->billing_address_id());
-}
-
TEST_F(AutofillTableTest, SetServerProfile) {
AutofillProfile one(AutofillProfile::SERVER_PROFILE, "a123");
std::vector<AutofillProfile> inputs;
@@ -1853,7 +1833,7 @@ TEST_F(AutofillTableTest, SetServerProfileUpdateUsageStats) {
// Update the usage stats; make sure they're reflected in GetServerProfiles.
inputs.back().set_use_count(4U);
inputs.back().set_use_date(base::Time::Now());
- table_->UpdateServerAddressUsageStats(inputs.back());
+ table_->UpdateServerAddressMetadata(inputs.back());
table_->GetServerProfiles(&outputs);
ASSERT_EQ(1u, outputs.size());
EXPECT_EQ(one.server_id(), outputs[0]->server_id());
@@ -2016,4 +1996,90 @@ TEST_F(AutofillTableTest, GetFormValuesForElementName_SubstringMatchEnabled) {
}
}
+TEST_F(AutofillTableTest, GetAllSyncMetadata) {
+ sync_pb::EntityMetadata metadata;
+ std::string storage_key = "storage_key";
+ std::string storage_key2 = "storage_key2";
+ metadata.set_sequence_number(1);
+
+ EXPECT_TRUE(
+ table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key, metadata));
+
+ sync_pb::ModelTypeState model_type_state;
+ model_type_state.set_initial_sync_done(true);
+
+ EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state));
+
+ metadata.set_sequence_number(2);
+ EXPECT_TRUE(
+ table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key2, metadata));
+
+ syncer::MetadataBatch metadata_batch;
+ EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+
+ EXPECT_TRUE(metadata_batch.GetModelTypeState().initial_sync_done());
+
+ syncer::EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata();
+
+ EXPECT_EQ(metadata_records.size(), 2u);
+ EXPECT_EQ(metadata_records[storage_key].sequence_number(), 1);
+ EXPECT_EQ(metadata_records[storage_key2].sequence_number(), 2);
+
+ // Now check that a model type state update replaces the old value
+ model_type_state.set_initial_sync_done(false);
+ EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state));
+
+ EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+ EXPECT_FALSE(metadata_batch.GetModelTypeState().initial_sync_done());
+}
+
+TEST_F(AutofillTableTest, WriteThenDeleteSyncMetadata) {
+ sync_pb::EntityMetadata metadata;
+ syncer::MetadataBatch metadata_batch;
+ std::string storage_key = "storage_key";
+ sync_pb::ModelTypeState model_type_state;
+
+ model_type_state.set_initial_sync_done(true);
+
+ metadata.set_client_tag_hash("client_hash");
+
+ // Write the data into the store.
+ EXPECT_TRUE(
+ table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key, metadata));
+ EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state));
+ // Delete the data we just wrote.
+ EXPECT_TRUE(table_->ClearSyncMetadata(syncer::AUTOFILL, storage_key));
+ // It shouldn't be there any more.
+ EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+
+ syncer::EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata();
+ EXPECT_EQ(metadata_records.size(), 0u);
+
+ // Now delete the model type state.
+ EXPECT_TRUE(table_->ClearModelTypeState(syncer::AUTOFILL));
+ EXPECT_FALSE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+}
+
+TEST_F(AutofillTableTest, CorruptSyncMetadata) {
+ syncer::MetadataBatch metadata_batch;
+ sync_pb::ModelTypeState state;
+ std::string storage_key = "storage_key";
+
+ sql::Statement s(db_->GetSQLConnection()->GetUniqueStatement(
+ "INSERT OR REPLACE INTO autofill_sync_metadata "
+ "(storage_key, value) VALUES(?, ?)"));
+ s.BindString(0, storage_key);
+ s.BindString(1, "unparseable");
+
+ sql::Statement s2(db_->GetSQLConnection()->GetUniqueStatement(
+ "INSERT OR REPLACE INTO autofill_model_type_state "
+ "(rowid, value) VALUES(1, ?)"));
+ s2.BindString(0, "unparseable");
+
+ EXPECT_TRUE(s.Run());
+ EXPECT_TRUE(s2.Run());
+
+ EXPECT_FALSE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+}
+
} // namespace autofill
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 3706d27701e..326ddc4ee15 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
@@ -10,7 +10,6 @@
#include "base/base64.h"
#include "base/bind.h"
-#include "base/containers/scoped_ptr_hash_map.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
@@ -41,29 +40,42 @@ void* UserDataKey() {
return reinterpret_cast<void*>(&user_data_key);
}
-// Returns syncable metadata for the |local| profile or credit card.
+// Sets the common syncable |metadata| for the |local_data_model|.
+void SetCommonMetadata(sync_pb::WalletMetadataSpecifics::Type type,
+ const std::string& server_id,
+ const AutofillDataModel& local_data_model,
+ sync_pb::WalletMetadataSpecifics* metadata) {
+ metadata->set_type(type);
+ metadata->set_id(server_id);
+ metadata->set_use_count(local_data_model.use_count());
+ metadata->set_use_date(local_data_model.use_date().ToInternalValue());
+}
+
+// Returns syncable metadata for the |local_profile|.
syncer::SyncData BuildSyncData(sync_pb::WalletMetadataSpecifics::Type type,
const std::string& server_id,
- const AutofillDataModel& local) {
+ const AutofillProfile& local_profile) {
sync_pb::EntitySpecifics entity;
sync_pb::WalletMetadataSpecifics* metadata = entity.mutable_wallet_metadata();
- metadata->set_type(type);
- metadata->set_id(server_id);
- metadata->set_use_count(local.use_count());
- metadata->set_use_date(local.use_date().ToInternalValue());
-
- std::string sync_tag;
- switch (type) {
- case sync_pb::WalletMetadataSpecifics::ADDRESS:
- sync_tag = "address-" + server_id;
- break;
- case sync_pb::WalletMetadataSpecifics::CARD:
- sync_tag = "card-" + server_id;
- break;
- case sync_pb::WalletMetadataSpecifics::UNKNOWN:
- NOTREACHED();
- break;
- }
+ SetCommonMetadata(type, server_id, local_profile, metadata);
+ metadata->set_address_has_converted(local_profile.has_converted());
+ std::string sync_tag = "address-" + server_id;
+
+ return syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity);
+}
+
+// Returns syncable metadata for the |local_card|.
+syncer::SyncData BuildSyncData(sync_pb::WalletMetadataSpecifics::Type type,
+ const std::string& server_id,
+ const CreditCard& local_card) {
+ sync_pb::EntitySpecifics entity;
+ sync_pb::WalletMetadataSpecifics* metadata = entity.mutable_wallet_metadata();
+ SetCommonMetadata(type, server_id, local_card, metadata);
+ // The strings must be in valid UTF-8 to sync.
+ std::string billing_address_id;
+ base::Base64Encode(local_card.billing_address_id(), &billing_address_id);
+ metadata->set_card_billing_address_id(billing_address_id);
+ std::string sync_tag = "card-" + server_id;
return syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity);
}
@@ -73,11 +85,12 @@ template <class DataType>
void UndeleteMetadataIfExisting(
const std::string& server_id,
const sync_pb::WalletMetadataSpecifics::Type& metadata_type,
- base::ScopedPtrHashMap<std::string, std::unique_ptr<DataType>>* locals,
+ std::unordered_map<std::string, std::unique_ptr<DataType>>* locals,
syncer::SyncChangeList* changes_to_sync) {
const auto& it = locals->find(server_id);
if (it != locals->end()) {
- std::unique_ptr<DataType> local_metadata = locals->take_and_erase(it);
+ std::unique_ptr<DataType> local_metadata = std::move(it->second);
+ locals->erase(it);
changes_to_sync->push_back(syncer::SyncChange(
FROM_HERE, syncer::SyncChange::ACTION_ADD,
BuildSyncData(metadata_type, server_id, *local_metadata)));
@@ -142,6 +155,89 @@ void ApplyChangesToCache(const syncer::SyncChangeList& changes,
}
}
+// Merges the metadata of the remote and local versions of the data model.
+void MergeCommonMetadata(
+ const sync_pb::WalletMetadataSpecifics& remote_metadata,
+ AutofillDataModel* local_model,
+ bool* is_remote_outdated,
+ bool* is_local_modified) {
+ size_t remote_use_count =
+ base::checked_cast<size_t>(remote_metadata.use_count());
+ if (local_model->use_count() < remote_use_count) {
+ local_model->set_use_count(remote_use_count);
+ *is_local_modified = true;
+ } else if (local_model->use_count() > remote_use_count) {
+ *is_remote_outdated = true;
+ }
+
+ base::Time remote_use_date =
+ base::Time::FromInternalValue(remote_metadata.use_date());
+ if (local_model->use_date() < remote_use_date) {
+ local_model->set_use_date(remote_use_date);
+ *is_local_modified = true;
+ } else if (local_model->use_date() > remote_use_date) {
+ *is_remote_outdated = true;
+ }
+}
+
+// Merges the metadata of the remote and local versions of the profile.
+void MergeMetadata(const sync_pb::WalletMetadataSpecifics& remote_metadata,
+ AutofillProfile* local_profile,
+ bool* is_remote_outdated,
+ bool* is_local_modified) {
+ // Merge the has_converted status.
+ if (local_profile->has_converted() !=
+ remote_metadata.address_has_converted()) {
+ if (!local_profile->has_converted()) {
+ local_profile->set_has_converted(true);
+ *is_local_modified = true;
+ } else {
+ *is_remote_outdated = true;
+ }
+ }
+
+ // Merge the use_count and use_date.
+ MergeCommonMetadata(remote_metadata, local_profile, is_remote_outdated,
+ is_local_modified);
+}
+
+// Merges the metadata of the remote and local versions of the credit card.
+void MergeMetadata(const sync_pb::WalletMetadataSpecifics& remote_metadata,
+ CreditCard* local_card,
+ bool* is_remote_outdated,
+ bool* is_local_modified) {
+ // Merge the billing_address_id. Do this before updating the use_count
+ // because it may be used to determine what id to keep.
+ std::string remote_billing_address_id;
+ base::Base64Decode(remote_metadata.card_billing_address_id(),
+ &remote_billing_address_id);
+
+ if (local_card->billing_address_id() != remote_billing_address_id) {
+ // If one of the values is empty, update it with the non empty value.
+ if (local_card->billing_address_id().empty()) {
+ local_card->set_billing_address_id(remote_billing_address_id);
+ *is_local_modified = true;
+ } else if (remote_billing_address_id.empty()) {
+ *is_remote_outdated = true;
+ } else {
+ // The cards have a different non-empty billing address id. Keep the
+ // billing address id of the most recently used card.
+ base::Time remote_use_date =
+ base::Time::FromInternalValue(remote_metadata.use_date());
+ if (local_card->use_date() < remote_use_date) {
+ local_card->set_billing_address_id(remote_billing_address_id);
+ *is_local_modified = true;
+ } else {
+ *is_remote_outdated = true;
+ }
+ }
+ }
+
+ // Merge the use_count and use_date.
+ MergeCommonMetadata(remote_metadata, local_card, is_remote_outdated,
+ is_local_modified);
+}
+
// Merges |remote| metadata into a collection of metadata |locals|. Returns true
// if the corresponding local metadata was found.
//
@@ -151,7 +247,7 @@ template <class DataType>
bool MergeRemote(
const syncer::SyncData& remote,
const base::Callback<bool(const DataType&)>& updater,
- base::ScopedPtrHashMap<std::string, std::unique_ptr<DataType>>* locals,
+ std::unordered_map<std::string, std::unique_ptr<DataType>>* locals,
syncer::SyncChangeList* changes_to_sync) {
DCHECK(locals);
DCHECK(changes_to_sync);
@@ -162,27 +258,13 @@ bool MergeRemote(
if (it == locals->end())
return false;
- std::unique_ptr<DataType> local_metadata = locals->take_and_erase(it);
+ std::unique_ptr<DataType> local_metadata = std::move(it->second);
+ locals->erase(it);
- size_t remote_use_count =
- base::checked_cast<size_t>(remote_metadata.use_count());
bool is_local_modified = false;
bool is_remote_outdated = false;
- if (local_metadata->use_count() < remote_use_count) {
- local_metadata->set_use_count(remote_use_count);
- is_local_modified = true;
- } else if (local_metadata->use_count() > remote_use_count) {
- is_remote_outdated = true;
- }
-
- base::Time remote_use_date =
- base::Time::FromInternalValue(remote_metadata.use_date());
- if (local_metadata->use_date() < remote_use_date) {
- local_metadata->set_use_date(remote_use_date);
- is_local_modified = true;
- } else if (local_metadata->use_date() > remote_use_date) {
- is_remote_outdated = true;
- }
+ MergeMetadata(remote_metadata, local_metadata.get(), &is_remote_outdated,
+ &is_local_modified);
if (is_remote_outdated) {
changes_to_sync->push_back(syncer::SyncChange(
@@ -247,9 +329,8 @@ syncer::SyncDataList AutofillWalletMetadataSyncableService::GetAllSyncData(
DCHECK_EQ(syncer::AUTOFILL_WALLET_METADATA, type);
syncer::SyncDataList data_list;
- base::ScopedPtrHashMap<std::string, std::unique_ptr<AutofillProfile>>
- profiles;
- base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>> cards;
+ std::unordered_map<std::string, std::unique_ptr<AutofillProfile>> profiles;
+ std::unordered_map<std::string, std::unique_ptr<CreditCard>> cards;
if (GetLocalData(&profiles, &cards)) {
for (const auto& it : profiles) {
data_list.push_back(BuildSyncData(
@@ -272,9 +353,8 @@ syncer::SyncError AutofillWalletMetadataSyncableService::ProcessSyncChanges(
ApplyChangesToCache(changes_from_sync, &cache_);
- base::ScopedPtrHashMap<std::string, std::unique_ptr<AutofillProfile>>
- profiles;
- base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>> cards;
+ std::unordered_map<std::string, std::unique_ptr<AutofillProfile>> profiles;
+ std::unordered_map<std::string, std::unique_ptr<CreditCard>> cards;
GetLocalData(&profiles, &cards);
// base::Unretained is used because the callbacks are invoked synchronously.
@@ -399,17 +479,15 @@ AutofillWalletMetadataSyncableService::AutofillWalletMetadataSyncableService(
}
bool AutofillWalletMetadataSyncableService::GetLocalData(
- base::ScopedPtrHashMap<std::string, std::unique_ptr<AutofillProfile>>*
- profiles,
- base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>>* cards)
- const {
+ std::unordered_map<std::string, std::unique_ptr<AutofillProfile>>* profiles,
+ std::unordered_map<std::string, std::unique_ptr<CreditCard>>* cards) const {
std::vector<std::unique_ptr<AutofillProfile>> profile_list;
bool success =
AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase())
->GetServerProfiles(&profile_list);
while (!profile_list.empty()) {
auto server_id = GetServerId(*profile_list.front());
- profiles->add(server_id, std::move(profile_list.front()));
+ (*profiles)[server_id] = std::move(profile_list.front());
profile_list.erase(profile_list.begin());
}
@@ -418,7 +496,7 @@ bool AutofillWalletMetadataSyncableService::GetLocalData(
->GetServerCreditCards(&card_list);
while (!card_list.empty()) {
auto server_id = GetServerId(*card_list.front());
- cards->add(server_id, std::move(card_list.front()));
+ (*cards)[server_id] = std::move(card_list.front());
card_list.erase(card_list.begin());
}
@@ -428,13 +506,13 @@ bool AutofillWalletMetadataSyncableService::GetLocalData(
bool AutofillWalletMetadataSyncableService::UpdateAddressStats(
const AutofillProfile& profile) {
return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase())
- ->UpdateServerAddressUsageStats(profile);
+ ->UpdateServerAddressMetadata(profile);
}
bool AutofillWalletMetadataSyncableService::UpdateCardStats(
const CreditCard& credit_card) {
return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase())
- ->UpdateServerCardUsageStats(credit_card);
+ ->UpdateServerCardMetadata(credit_card);
}
syncer::SyncError
@@ -447,9 +525,8 @@ AutofillWalletMetadataSyncableService::SendChangesToSyncServer(
syncer::SyncMergeResult AutofillWalletMetadataSyncableService::MergeData(
const syncer::SyncDataList& sync_data) {
- base::ScopedPtrHashMap<std::string, std::unique_ptr<AutofillProfile>>
- profiles;
- base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>> cards;
+ std::unordered_map<std::string, std::unique_ptr<AutofillProfile>> profiles;
+ std::unordered_map<std::string, std::unique_ptr<CreditCard>> cards;
GetLocalData(&profiles, &cards);
syncer::SyncMergeResult result(syncer::AUTOFILL_WALLET_METADATA);
@@ -518,16 +595,18 @@ syncer::SyncMergeResult AutofillWalletMetadataSyncableService::MergeData(
return result;
}
+template <class DataType>
void AutofillWalletMetadataSyncableService::AutofillDataModelChanged(
const std::string& server_id,
const sync_pb::WalletMetadataSpecifics::Type& type,
- const AutofillDataModel& local) {
+ const DataType& local) {
auto it = FindServerIdAndTypeInCache(server_id, type, &cache_);
if (it == cache_.end())
return;
const sync_pb::WalletMetadataSpecifics& remote =
it->GetSpecifics().wallet_metadata();
+
if (base::checked_cast<size_t>(remote.use_count()) < local.use_count() &&
base::Time::FromInternalValue(remote.use_date()) < local.use_date()) {
SendChangesToSyncServer(syncer::SyncChangeList(
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h
index 4987768f7e7..23edced9e32 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h
@@ -7,6 +7,7 @@
#include <memory>
#include <string>
+#include <unordered_map>
#include "base/callback_forward.h"
#include "base/macros.h"
@@ -20,14 +21,8 @@
#include "components/sync/model/syncable_service.h"
#include "components/sync/protocol/autofill_specifics.pb.h"
-namespace base {
-template <typename, typename>
-class ScopedPtrHashMap;
-}
-
namespace syncer {
class SyncChangeProcessor;
-class SyncData;
class SyncErrorFactory;
}
@@ -37,7 +32,6 @@ class Location;
namespace autofill {
-class AutofillDataModel;
class AutofillProfile;
class AutofillWebDataBackend;
class AutofillWebDataService;
@@ -97,9 +91,9 @@ class AutofillWalletMetadataSyncableService
// to server profiles and server cards read from disk. This data contains the
// usage stats. Returns true on success.
virtual bool GetLocalData(
- base::ScopedPtrHashMap<std::string, std::unique_ptr<AutofillProfile>>*
+ std::unordered_map<std::string, std::unique_ptr<AutofillProfile>>*
profiles,
- base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>>* cards)
+ std::unordered_map<std::string, std::unique_ptr<CreditCard>>* cards)
const;
// Updates the stats for |profile| stored on disk. Does not trigger
@@ -128,11 +122,13 @@ class AutofillWalletMetadataSyncableService
// is not present locally.
syncer::SyncMergeResult MergeData(const syncer::SyncDataList& sync_data);
- // Sends updates to the sync server.
+ // Sends the autofill data model updates to the sync server if the local
+ // version is more recent. Used for both profiles and credit cards.
+ template <class DataType>
void AutofillDataModelChanged(
const std::string& server_id,
const sync_pb::WalletMetadataSpecifics::Type& type,
- const AutofillDataModel& local);
+ const DataType& local);
base::ThreadChecker thread_checker_;
AutofillWebDataBackend* web_data_backend_; // Weak ref.
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc
index 70438328595..89439d7184c 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc
@@ -11,7 +11,6 @@
#include <vector>
#include "base/base64.h"
-#include "base/containers/scoped_ptr_hash_map.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
@@ -65,13 +64,13 @@ ACTION_P2(GetCopiesOf, profiles, cards) {
for (const auto& profile : *profiles) {
std::string utf8_server_id;
base::Base64Encode(profile.server_id(), &utf8_server_id);
- arg0->add(utf8_server_id, base::WrapUnique(new AutofillProfile(profile)));
+ (*arg0)[utf8_server_id] = base::MakeUnique<AutofillProfile>(profile);
}
for (const auto& card : *cards) {
std::string utf8_server_id;
base::Base64Encode(card.server_id(), &utf8_server_id);
- arg1->add(utf8_server_id, base::WrapUnique(new CreditCard(card)));
+ (*arg1)[utf8_server_id] = base::MakeUnique<CreditCard>(card);
}
}
@@ -121,9 +120,8 @@ class MockService : public AutofillWalletMetadataSyncableService {
private:
MOCK_CONST_METHOD2(
GetLocalData,
- bool(base::ScopedPtrHashMap<std::string,
- std::unique_ptr<AutofillProfile>>*,
- base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>>*));
+ bool(std::unordered_map<std::string, std::unique_ptr<AutofillProfile>>*,
+ std::unordered_map<std::string, std::unique_ptr<CreditCard>>*));
syncer::SyncError SendChangesToSyncServerConcrete(
const syncer::SyncChangeList& changes) {
@@ -185,28 +183,33 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, NoMetadataToReturn) {
AutofillProfile BuildAddress(const std::string& server_id,
int64_t use_count,
- int64_t use_date) {
+ int64_t use_date,
+ bool has_converted) {
AutofillProfile profile(AutofillProfile::SERVER_PROFILE, server_id);
profile.set_use_count(use_count);
profile.set_use_date(base::Time::FromInternalValue(use_date));
+ profile.set_has_converted(has_converted);
return profile;
}
CreditCard BuildCard(const std::string& server_id,
int64_t use_count,
- int64_t use_date) {
+ int64_t use_date,
+ const std::string& billing_address_id) {
CreditCard card(CreditCard::MASKED_SERVER_CARD, server_id);
card.set_use_count(use_count);
card.set_use_date(base::Time::FromInternalValue(use_date));
+ card.set_billing_address_id(billing_address_id);
return card;
}
-MATCHER_P5(SyncDataMatches,
+MATCHER_P6(SyncAddressDataMatches,
sync_tag,
metadata_type,
server_id,
use_count,
use_date,
+ has_converted,
"") {
return arg.IsValid() &&
syncer::AUTOFILL_WALLET_METADATA == arg.GetDataType() &&
@@ -214,22 +217,43 @@ MATCHER_P5(SyncDataMatches,
metadata_type == arg.GetSpecifics().wallet_metadata().type() &&
server_id == arg.GetSpecifics().wallet_metadata().id() &&
use_count == arg.GetSpecifics().wallet_metadata().use_count() &&
- use_date == arg.GetSpecifics().wallet_metadata().use_date();
+ use_date == arg.GetSpecifics().wallet_metadata().use_date() &&
+ has_converted ==
+ arg.GetSpecifics().wallet_metadata().address_has_converted();
+}
+
+MATCHER_P6(SyncCardDataMatches,
+ sync_tag,
+ metadata_type,
+ server_id,
+ use_count,
+ use_date,
+ billing_address_id,
+ "") {
+ return arg.IsValid() &&
+ syncer::AUTOFILL_WALLET_METADATA == arg.GetDataType() &&
+ sync_tag == syncer::SyncDataLocal(arg).GetTag() &&
+ metadata_type == arg.GetSpecifics().wallet_metadata().type() &&
+ server_id == arg.GetSpecifics().wallet_metadata().id() &&
+ use_count == arg.GetSpecifics().wallet_metadata().use_count() &&
+ use_date == arg.GetSpecifics().wallet_metadata().use_date() &&
+ billing_address_id ==
+ arg.GetSpecifics().wallet_metadata().card_billing_address_id();
}
// Verify that all metadata from disk is sent to the sync server.
TEST_F(AutofillWalletMetadataSyncableServiceTest, ReturnAllMetadata) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
-
- EXPECT_THAT(
- local_.GetAllSyncData(syncer::AUTOFILL_WALLET_METADATA),
- UnorderedElementsAre(
- SyncDataMatches(kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8,
- 1, 2),
- SyncDataMatches(kCard1SyncTag, sync_pb::WalletMetadataSpecifics::CARD,
- kCard1Utf8, 3, 4)));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+
+ EXPECT_THAT(local_.GetAllSyncData(syncer::AUTOFILL_WALLET_METADATA),
+ UnorderedElementsAre(
+ SyncAddressDataMatches(
+ kAddr1SyncTag, sync_pb::WalletMetadataSpecifics::ADDRESS,
+ kAddr1Utf8, 1, 2, true),
+ SyncCardDataMatches(kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard1Utf8, 3, 4, kAddr1Utf8)));
}
void MergeMetadata(MockService* local, MockService* remote) {
@@ -272,8 +296,8 @@ MATCHER_P2(SyncChangeMatches, change_type, sync_tag, "") {
// Verify that remote data without local counterpart is deleted during the
// initial merge.
TEST_F(AutofillWalletMetadataSyncableServiceTest, DeleteFromServerOnMerge) {
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
@@ -287,37 +311,53 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, DeleteFromServerOnMerge) {
MergeMetadata(&local_, &remote_);
}
-MATCHER_P6(SyncChangeAndDataMatch,
+MATCHER_P7(SyncAddressChangeAndDataMatch,
+ change_type,
+ sync_tag,
+ metadata_type,
+ server_id,
+ use_count,
+ use_date,
+ has_converted,
+ "") {
+ return Value(arg, SyncChangeMatches(change_type, sync_tag)) &&
+ Value(arg.sync_data(),
+ SyncAddressDataMatches(sync_tag, metadata_type, server_id,
+ use_count, use_date, has_converted));
+}
+
+MATCHER_P7(SyncCardChangeAndDataMatch,
change_type,
sync_tag,
metadata_type,
server_id,
use_count,
use_date,
+ billing_address_id,
"") {
return Value(arg, SyncChangeMatches(change_type, sync_tag)) &&
Value(arg.sync_data(),
- SyncDataMatches(sync_tag, metadata_type, server_id, use_count,
- use_date));
+ SyncCardDataMatches(sync_tag, metadata_type, server_id,
+ use_count, use_date, billing_address_id));
}
// Verify that local data is sent to the sync server during the initial merge,
// if the server does not have the data already.
TEST_F(AutofillWalletMetadataSyncableServiceTest, AddToServerOnMerge) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
- EXPECT_CALL(
- local_,
- SendChangesToSyncServer(UnorderedElementsAre(
- SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS,
- kAddr1Utf8, 1, 2),
- SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD,
- kCard1Utf8, 3, 4))));
+ EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre(
+ SyncAddressChangeAndDataMatch(
+ syncer::SyncChange::ACTION_ADD, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS,
+ kAddr1Utf8, 1, 2, true),
+ SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_ADD, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard1Utf8, 3, 4, kAddr1Utf8))));
MergeMetadata(&local_, &remote_);
}
@@ -326,10 +366,10 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, AddToServerOnMerge) {
// local and remote data are identical during the initial merge.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
IgnoreIdenticalValuesOnMerge) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
@@ -338,24 +378,43 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
MergeMetadata(&local_, &remote_);
}
-MATCHER_P3(AutofillMetadataMatches, server_id, use_count, use_date, "") {
+MATCHER_P4(AutofillAddressMetadataMatches,
+ server_id,
+ use_count,
+ use_date,
+ has_converted,
+ "") {
+ return arg.server_id() == server_id &&
+ arg.use_count() == base::checked_cast<size_t>(use_count) &&
+ arg.use_date() == base::Time::FromInternalValue(use_date) &&
+ arg.has_converted() == has_converted;
+}
+
+MATCHER_P4(AutofillCardMetadataMatches,
+ server_id,
+ use_count,
+ use_date,
+ billing_address_id,
+ "") {
return arg.server_id() == server_id &&
arg.use_count() == base::checked_cast<size_t>(use_count) &&
- arg.use_date() == base::Time::FromInternalValue(use_date);
+ arg.use_date() == base::Time::FromInternalValue(use_date) &&
+ arg.billing_address_id() == billing_address_id;
}
// Verify that remote data with higher values of use count and last use date is
// saved to disk during the initial merge.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
SaveHigherValuesLocallyOnMerge) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 10, 20));
- remote_.UpdateCardStats(BuildCard(kCard1, 30, 40));
-
- EXPECT_CALL(local_,
- UpdateAddressStats(AutofillMetadataMatches(kAddr1, 10, 20)));
- EXPECT_CALL(local_, UpdateCardStats(AutofillMetadataMatches(kCard1, 30, 40)));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 10, 20, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 30, 40, kAddr1));
+
+ EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches(
+ kAddr1, 10, 20, true)));
+ EXPECT_CALL(local_, UpdateCardStats(
+ AutofillCardMetadataMatches(kCard1, 30, 40, kAddr1)));
EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0);
MergeMetadata(&local_, &remote_);
@@ -365,22 +424,22 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// sent to the sync server during the initial merge.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
SendHigherValuesToServerOnMerge) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 10, 20));
- local_.UpdateCardStats(BuildCard(kCard1, 30, 40));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 10, 20, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 30, 40, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr2));
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
- EXPECT_CALL(
- local_,
- SendChangesToSyncServer(UnorderedElementsAre(
- SyncChangeAndDataMatch(
- syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20),
- SyncChangeAndDataMatch(
- syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 30, 40))));
+ EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre(
+ SyncAddressChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS,
+ kAddr1Utf8, 10, 20, true),
+ SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard1Utf8, 30, 40, kAddr1Utf8))));
MergeMetadata(&local_, &remote_);
}
@@ -389,13 +448,13 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// local metadata is updated.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
DontSendLowerValueToServerOnSingleChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
- AutofillProfile address = BuildAddress(kAddr1, 0, 0);
- CreditCard card = BuildCard(kCard1, 0, 0);
+ AutofillProfile address = BuildAddress(kAddr1, 0, 0, false);
+ CreditCard card = BuildCard(kCard1, 0, 0, kAddr2);
EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0);
@@ -409,24 +468,24 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// metadata is updated.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
SendHigherValuesToServerOnLocalSingleChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
- AutofillProfile address = BuildAddress(kAddr1, 10, 20);
- CreditCard card = BuildCard(kCard1, 30, 40);
+ AutofillProfile address = BuildAddress(kAddr1, 10, 20, true);
+ CreditCard card = BuildCard(kCard1, 30, 40, kAddr2);
- EXPECT_CALL(
- local_,
- SendChangesToSyncServer(ElementsAre(SyncChangeAndDataMatch(
- syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20))));
- EXPECT_CALL(
- local_,
- SendChangesToSyncServer(ElementsAre(SyncChangeAndDataMatch(
- syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 30, 40))));
+ EXPECT_CALL(local_,
+ SendChangesToSyncServer(ElementsAre(SyncAddressChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20,
+ true))));
+ EXPECT_CALL(local_,
+ SendChangesToSyncServer(ElementsAre(SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 30, 40,
+ kAddr2Utf8))));
local_.AutofillProfileChanged(AutofillProfileChange(
AutofillProfileChange::UPDATE, address.guid(), &address));
@@ -439,13 +498,13 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// instead.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
DontAddToServerOnSingleChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
- AutofillProfile address = BuildAddress(kAddr2, 5, 6);
- CreditCard card = BuildCard(kCard2, 7, 8);
+ AutofillProfile address = BuildAddress(kAddr2, 5, 6, false);
+ CreditCard card = BuildCard(kCard2, 7, 8, kAddr2);
EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0);
@@ -458,24 +517,24 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// Verify that new metadata is sent to the sync server when multiple metadata
// values change at once.
TEST_F(AutofillWalletMetadataSyncableServiceTest, AddToServerOnMultiChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
// These methods do not trigger notifications or sync:
- local_.UpdateAddressStats(BuildAddress(kAddr2, 5, 6));
- local_.UpdateCardStats(BuildCard(kCard2, 7, 8));
-
- EXPECT_CALL(
- local_,
- SendChangesToSyncServer(UnorderedElementsAre(
- SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kAddr2SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS,
- kAddr2Utf8, 5, 6),
- SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kCard2SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD,
- kCard2Utf8, 7, 8))));
+ local_.UpdateAddressStats(BuildAddress(kAddr2, 5, 6, true));
+ local_.UpdateCardStats(BuildCard(kCard2, 7, 8, kAddr2));
+
+ EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre(
+ SyncAddressChangeAndDataMatch(
+ syncer::SyncChange::ACTION_ADD, kAddr2SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS,
+ kAddr2Utf8, 5, 6, true),
+ SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_ADD, kCard2SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard2Utf8, 7, 8, kAddr2Utf8))));
local_.AutofillMultipleChanged();
}
@@ -484,24 +543,24 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, AddToServerOnMultiChange) {
// when multiple metadata values change at once.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
UpdateToHigherValueOnServerOnMultiChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
// These methods do not trigger notifications or sync:
- local_.UpdateAddressStats(BuildAddress(kAddr1, 5, 6));
- local_.UpdateCardStats(BuildCard(kCard1, 7, 8));
-
- EXPECT_CALL(
- local_,
- SendChangesToSyncServer(UnorderedElementsAre(
- SyncChangeAndDataMatch(
- syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 5, 6),
- SyncChangeAndDataMatch(
- syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 7, 8))));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 5, 6, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 7, 8, kAddr2));
+
+ EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre(
+ SyncAddressChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS,
+ kAddr1Utf8, 5, 6, true),
+ SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard1Utf8, 7, 8, kAddr2Utf8))));
local_.AutofillMultipleChanged();
}
@@ -510,14 +569,14 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// when multiple metadata values change at once.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
DontUpdateToLowerValueOnServerOnMultiChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
// These methods do not trigger notifications or sync:
- local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0));
- local_.UpdateCardStats(BuildCard(kCard1, 0, 0));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0, false));
+ local_.UpdateCardStats(BuildCard(kCard1, 0, 0, kAddr2));
EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0);
@@ -528,10 +587,10 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// multiple metadata values change at once.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
DeleteFromServerOnMultiChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
// This method dooes not trigger notifications or sync:
local_.ClearServerData();
@@ -549,10 +608,10 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// Verify that empty sync change from the sync server does not trigger writing
// to disk or sending any data to the sync server.
TEST_F(AutofillWalletMetadataSyncableServiceTest, EmptySyncChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
@@ -562,18 +621,49 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, EmptySyncChange) {
local_.ProcessSyncChanges(FROM_HERE, syncer::SyncChangeList());
}
-syncer::SyncChange BuildChange(
+void BuildBasicChange(syncer::SyncChange::SyncChangeType change_type,
+ const std::string& sync_tag,
+ sync_pb::WalletMetadataSpecifics::Type metadata_type,
+ const std::string& server_id,
+ int64_t use_count,
+ int64_t use_date,
+ sync_pb::EntitySpecifics* entity) {
+ entity->mutable_wallet_metadata()->set_type(metadata_type);
+ entity->mutable_wallet_metadata()->set_id(server_id);
+ entity->mutable_wallet_metadata()->set_use_count(use_count);
+ entity->mutable_wallet_metadata()->set_use_date(use_date);
+}
+
+syncer::SyncChange BuildAddressChange(
+ syncer::SyncChange::SyncChangeType change_type,
+ const std::string& sync_tag,
+ sync_pb::WalletMetadataSpecifics::Type metadata_type,
+ const std::string& server_id,
+ int64_t use_count,
+ int64_t use_date,
+ bool has_converted) {
+ sync_pb::EntitySpecifics entity;
+ BuildBasicChange(change_type, sync_tag, metadata_type, server_id, use_count,
+ use_date, &entity);
+ entity.mutable_wallet_metadata()->set_address_has_converted(has_converted);
+ return syncer::SyncChange(
+ FROM_HERE, change_type,
+ syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity));
+}
+
+syncer::SyncChange BuildCardChange(
syncer::SyncChange::SyncChangeType change_type,
const std::string& sync_tag,
sync_pb::WalletMetadataSpecifics::Type metadata_type,
const std::string& server_id,
int64_t use_count,
- int64_t use_date) {
+ int64_t use_date,
+ const std::string& billing_address_id) {
sync_pb::EntitySpecifics entity;
- entity.mutable_wallet_metadata()->set_type(metadata_type);
- entity.mutable_wallet_metadata()->set_id(server_id);
- entity.mutable_wallet_metadata()->set_use_count(use_count);
- entity.mutable_wallet_metadata()->set_use_date(use_date);
+ BuildBasicChange(change_type, sync_tag, metadata_type, server_id, use_count,
+ use_date, &entity);
+ entity.mutable_wallet_metadata()->set_card_billing_address_id(
+ billing_address_id);
return syncer::SyncChange(
FROM_HERE, change_type,
syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity));
@@ -584,18 +674,18 @@ syncer::SyncChange BuildChange(
// server.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
IgnoreNewMetadataFromServerOnSyncChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
syncer::SyncChangeList changes;
- changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, kAddr2SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS,
- kAddr2Utf8, 5, 6));
- changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, kCard2SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD,
- kCard2Utf8, 7, 8));
+ changes.push_back(BuildAddressChange(
+ syncer::SyncChange::ACTION_ADD, kAddr2SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 5, 6, true));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_ADD, kCard2SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 7, 8, kAddr2Utf8));
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
@@ -608,22 +698,23 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// disk when processing on-going sync changes.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
SaveHigherValuesFromServerOnSyncChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
syncer::SyncChangeList changes;
- changes.push_back(BuildChange(
+ changes.push_back(BuildAddressChange(
syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20));
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 30, 40));
-
- EXPECT_CALL(local_,
- UpdateAddressStats(AutofillMetadataMatches(kAddr1, 10, 20)));
- EXPECT_CALL(local_, UpdateCardStats(AutofillMetadataMatches(kCard1, 30, 40)));
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20, true));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 30, 40, kAddr2Utf8));
+
+ EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches(
+ kAddr1, 10, 20, true)));
+ EXPECT_CALL(local_, UpdateCardStats(
+ AutofillCardMetadataMatches(kCard1, 30, 40, kAddr2)));
EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0);
local_.ProcessSyncChanges(FROM_HERE, changes);
@@ -633,30 +724,30 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// processing on-going sync changes.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
SendHigherValuesToServerOnSyncChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
syncer::SyncChangeList changes;
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 0, 0));
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 0, 0));
+ changes.push_back(BuildAddressChange(
+ syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 0, 0, false));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 0, 0, kAddr2Utf8));
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
- EXPECT_CALL(
- local_,
- SendChangesToSyncServer(UnorderedElementsAre(
- SyncChangeAndDataMatch(
- syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 1, 2),
- SyncChangeAndDataMatch(
- syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 3, 4))));
+ EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre(
+ SyncAddressChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS,
+ kAddr1Utf8, 1, 2, true),
+ SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard1Utf8, 3, 4, kAddr1Utf8))));
local_.ProcessSyncChanges(FROM_HERE, changes);
}
@@ -664,30 +755,30 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// Verify that addition of known metadata is treated the same as an update.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
TreatAdditionOfKnownMetadataAsUpdateOnSyncChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
syncer::SyncChangeList changes;
- changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS,
- kAddr1Utf8, 0, 0));
- changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD,
- kCard1Utf8, 0, 0));
+ changes.push_back(BuildAddressChange(
+ syncer::SyncChange::ACTION_ADD, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 0, 0, false));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_ADD, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 0, 0, kAddr2Utf8));
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
- EXPECT_CALL(
- local_,
- SendChangesToSyncServer(UnorderedElementsAre(
- SyncChangeAndDataMatch(
- syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 1, 2),
- SyncChangeAndDataMatch(
- syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 3, 4))));
+ EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre(
+ SyncAddressChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS,
+ kAddr1Utf8, 1, 2, true),
+ SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard1Utf8, 3, 4, kAddr1Utf8))));
local_.ProcessSyncChanges(FROM_HERE, changes);
}
@@ -696,18 +787,18 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// no disk writes and no messages sent to the server.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
IgnoreUpdateOfUnknownMetadataOnSyncChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
syncer::SyncChangeList changes;
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_UPDATE, kAddr2SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 0, 0));
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard2SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 0, 0));
+ changes.push_back(BuildAddressChange(
+ syncer::SyncChange::ACTION_UPDATE, kAddr2SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 0, 0, false));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_UPDATE, kCard2SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 0, 0, kAddr2Utf8));
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
@@ -720,18 +811,18 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// ignored. There should be no disk writes and no messages sent to the server.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
IgnoreDeleteOfUnknownMetadataOnSyncChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
syncer::SyncChangeList changes;
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_DELETE, kAddr2SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 0, 0));
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_DELETE, kCard2SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 0, 0));
+ changes.push_back(BuildAddressChange(
+ syncer::SyncChange::ACTION_DELETE, kAddr2SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 0, 0, false));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_DELETE, kCard2SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 0, 0, kAddr2Utf8));
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
@@ -744,30 +835,30 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// trigger an undelete message sent to the server.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
UndeleteExistingMetadataOnSyncChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateCardStats(BuildCard(kCard1, 3, 4));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
MergeMetadata(&local_, &remote_);
syncer::SyncChangeList changes;
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_DELETE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 0, 0));
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_DELETE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 0, 0));
+ changes.push_back(BuildAddressChange(
+ syncer::SyncChange::ACTION_DELETE, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 0, 0, false));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_DELETE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 0, 0, kAddr2Utf8));
EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0);
EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
- EXPECT_CALL(
- local_,
- SendChangesToSyncServer(UnorderedElementsAre(
- SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS,
- kAddr1Utf8, 1, 2),
- SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD,
- kCard1Utf8, 3, 4))));
+ EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre(
+ SyncAddressChangeAndDataMatch(
+ syncer::SyncChange::ACTION_ADD, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS,
+ kAddr1Utf8, 1, 2, true),
+ SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_ADD, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard1Utf8, 3, 4, kAddr1Utf8))));
local_.ProcessSyncChanges(FROM_HERE, changes);
}
@@ -776,22 +867,22 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// data, which is used to avoid calling the expensive GetAllSyncData() function.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
CacheIsUpToDateAfterSyncChange) {
- local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- local_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4));
- local_.UpdateCardStats(BuildCard(kCard1, 5, 6));
- local_.UpdateCardStats(BuildCard(kCard2, 7, 8));
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4));
- remote_.UpdateCardStats(BuildCard(kCard1, 5, 6));
- remote_.UpdateCardStats(BuildCard(kCard2, 7, 8));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ local_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4, false));
+ local_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1));
+ local_.UpdateCardStats(BuildCard(kCard2, 7, 8, kAddr2));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true));
+ remote_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4, false));
+ remote_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1));
+ remote_.UpdateCardStats(BuildCard(kCard2, 7, 8, kAddr2));
MergeMetadata(&local_, &remote_);
syncer::SyncChangeList changes;
- changes.push_back(BuildChange(
+ changes.push_back(BuildAddressChange(
syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20));
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 50, 60));
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20, false));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 50, 60, kAddr1Utf8));
local_.ProcessSyncChanges(FROM_HERE, changes);
// This method dooes not trigger notifications or sync:
local_.ClearServerData();
@@ -812,23 +903,24 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// values to the sync server.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
SaveHigherValuesLocallyOnLateDataArrival) {
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateCardStats(BuildCard(kCard1, 3, 4));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ remote_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1));
MergeMetadata(&local_, &remote_);
syncer::SyncChangeList changes;
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 5, 6));
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 7, 8));
+ changes.push_back(BuildAddressChange(
+ syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 5, 6, true));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 7, 8, kAddr2Utf8));
local_.ProcessSyncChanges(FROM_HERE, changes);
- local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0));
- local_.UpdateCardStats(BuildCard(kCard1, 0, 0));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 0, 0, kAddr2));
- EXPECT_CALL(local_,
- UpdateAddressStats(AutofillMetadataMatches(kAddr1, 5, 6)));
- EXPECT_CALL(local_, UpdateCardStats(AutofillMetadataMatches(kCard1, 7, 8)));
+ EXPECT_CALL(local_, UpdateAddressStats(
+ AutofillAddressMetadataMatches(kAddr1, 5, 6, true)));
+ EXPECT_CALL(local_, UpdateCardStats(
+ AutofillCardMetadataMatches(kCard1, 7, 8, kAddr2)));
EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0);
local_.AutofillMultipleChanged();
@@ -839,42 +931,133 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest,
// once the data finally arrives.
TEST_F(AutofillWalletMetadataSyncableServiceTest,
SaveHigherValuesLocallyOnLateDataArrivalAfterPartialUpdates) {
- remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2));
- remote_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4));
- remote_.UpdateCardStats(BuildCard(kCard1, 5, 6));
- remote_.UpdateCardStats(BuildCard(kCard2, 7, 8));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false));
+ remote_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4, false));
+ remote_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1));
+ remote_.UpdateCardStats(BuildCard(kCard2, 7, 8, kAddr1));
MergeMetadata(&local_, &remote_);
syncer::SyncChangeList changes;
- changes.push_back(BuildChange(
+ changes.push_back(BuildAddressChange(
syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 9, 10));
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 11, 12));
- changes.push_back(BuildChange(
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 9, 10, false));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 11, 12, kAddr2Utf8));
+ changes.push_back(BuildAddressChange(
syncer::SyncChange::ACTION_UPDATE, kAddr2SyncTag,
- sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 13, 14));
- changes.push_back(
- BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard2SyncTag,
- sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 15, 16));
+ sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 13, 14, true));
+ changes.push_back(BuildCardChange(
+ syncer::SyncChange::ACTION_UPDATE, kCard2SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 15, 16, kAddr1Utf8));
local_.ProcessSyncChanges(FROM_HERE, changes);
changes.resize(2);
local_.ProcessSyncChanges(FROM_HERE, changes);
- local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0));
- local_.UpdateAddressStats(BuildAddress(kAddr2, 0, 0));
- local_.UpdateCardStats(BuildCard(kCard1, 0, 0));
- local_.UpdateCardStats(BuildCard(kCard2, 0, 0));
-
- EXPECT_CALL(local_,
- UpdateAddressStats(AutofillMetadataMatches(kAddr1, 9, 10)));
- EXPECT_CALL(local_, UpdateCardStats(AutofillMetadataMatches(kCard1, 11, 12)));
- EXPECT_CALL(local_,
- UpdateAddressStats(AutofillMetadataMatches(kAddr2, 13, 14)));
- EXPECT_CALL(local_, UpdateCardStats(AutofillMetadataMatches(kCard2, 15, 16)));
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0, false));
+ local_.UpdateAddressStats(BuildAddress(kAddr2, 0, 0, false));
+ local_.UpdateCardStats(BuildCard(kCard1, 0, 0, kAddr1));
+ local_.UpdateCardStats(BuildCard(kCard2, 0, 0, kAddr2));
+
+ EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches(
+ kAddr1, 9, 10, false)));
+ EXPECT_CALL(local_, UpdateCardStats(
+ AutofillCardMetadataMatches(kCard1, 11, 12, kAddr2)));
+ EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches(
+ kAddr2, 13, 14, true)));
+ EXPECT_CALL(local_, UpdateCardStats(
+ AutofillCardMetadataMatches(kCard2, 15, 16, kAddr1)));
EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0);
local_.AutofillMultipleChanged();
}
+// Verify that the merge logic keeps the best data on a field by field basis.
+// Make sure that if the better data is split across the local and server
+// version, both are updated with the merge results.
+TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValues_Mixed1) {
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 20, true));
+ local_.UpdateCardStats(BuildCard(kCard1, 30, 4, ""));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 10, 2, false));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr1));
+
+ EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches(
+ kAddr1, 10, 20, true)));
+ EXPECT_CALL(local_, UpdateCardStats(
+ AutofillCardMetadataMatches(kCard1, 30, 40, kAddr1)));
+ EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre(
+ SyncAddressChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS,
+ kAddr1Utf8, 10, 20, true),
+ SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard1Utf8, 30, 40, kAddr1Utf8))));
+
+ MergeMetadata(&local_, &remote_);
+}
+
+// Verify that the merge logic keeps the best data on a field by field basis.
+// Make sure that if the better data is split across the local and server
+// version, both are updated with the merge results.
+// Same as SaveHigherValues_Mixed1 but with the higher values moved from local
+// to server and vice versa.
+TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValues_Mixed2) {
+ local_.UpdateAddressStats(BuildAddress(kAddr1, 10, 2, false));
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr1));
+ remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 20, true));
+ remote_.UpdateCardStats(BuildCard(kCard1, 30, 4, ""));
+
+ EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches(
+ kAddr1, 10, 20, true)));
+ EXPECT_CALL(local_, UpdateCardStats(
+ AutofillCardMetadataMatches(kCard1, 30, 40, kAddr1)));
+ EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre(
+ SyncAddressChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag,
+ sync_pb::WalletMetadataSpecifics::ADDRESS,
+ kAddr1Utf8, 10, 20, true),
+ SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard1Utf8, 30, 40, kAddr1Utf8))));
+
+ MergeMetadata(&local_, &remote_);
+}
+
+// Verify that if both local and server have a different non empty billing
+// address id, the one with the most recent (bigger) use date is kept.
+TEST_F(AutofillWalletMetadataSyncableServiceTest,
+ SaveHigherValues_DifferentBillingAddressId_LocalMostRecent) {
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr1));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr2));
+
+ // The value from the local should be kept because it has a more recent use
+ // date.
+ EXPECT_CALL(local_, UpdateCardStats(_)).Times(0);
+ EXPECT_CALL(local_, SendChangesToSyncServer(
+ UnorderedElementsAre(SyncCardChangeAndDataMatch(
+ syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag,
+ sync_pb::WalletMetadataSpecifics::CARD,
+ kCard1Utf8, 3, 40, kAddr1Utf8))));
+
+ MergeMetadata(&local_, &remote_);
+}
+
+// Verify that if both local and server have a different non empty billing
+// address id, the one with the most recent (bigger) use date is kept.
+TEST_F(AutofillWalletMetadataSyncableServiceTest,
+ SaveHigherValues_DifferentBillingAddressId_RemoteMostRecent) {
+ local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1));
+ remote_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr2));
+
+ // The value from the remote should be kept because it has a more recent use
+ // date.
+ EXPECT_CALL(local_, UpdateCardStats(
+ AutofillCardMetadataMatches(kCard1, 3, 40, kAddr2)));
+ EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0);
+
+ MergeMetadata(&local_, &remote_);
+}
+
} // namespace
} // namespace autofill
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 de80ecd1728..8ad43337b41 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
@@ -12,8 +12,6 @@
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "components/autofill/core/browser/autofill_profile.h"
-#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
@@ -24,6 +22,10 @@ namespace autofill {
namespace {
+// The length of the GUIDs used for local autofill data. It is different than
+// the length used for server autofill data.
+const int kLocalGuidSize = 36;
+
void* UserDataKey() {
// Use the address of a static so that COMDAT folding won't ever fold
// with something else.
@@ -75,6 +77,7 @@ CreditCard CardFromSpecifics(const sync_pb::WalletMaskedCreditCard& card) {
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());
return result;
}
@@ -118,25 +121,6 @@ AutofillProfile ProfileFromSpecifics(
return profile;
}
-// Searches for CreditCards with identical server IDs and copies the billing
-// address ID from the existing cards on disk into the new cards from server.
-// The credit card's IDs do not change over time.
-void CopyBillingAddressesFromDisk(AutofillTable* table,
- std::vector<CreditCard>* cards_from_server) {
- std::vector<std::unique_ptr<CreditCard>> cards_on_disk;
- table->GetServerCreditCards(&cards_on_disk);
-
- // The reasons behind brute-force search are explained in SetDataIfChanged.
- for (const auto& saved_card : cards_on_disk) {
- for (CreditCard& server_card : *cards_from_server) {
- if (saved_card->server_id() == server_card.server_id()) {
- server_card.set_billing_address_id(saved_card->billing_address_id());
- break;
- }
- }
- }
-}
-
// This function handles conditionally updating the AutofillTable with either
// a set of CreditCards or AutocompleteProfiles only when the existing data
// doesn't match.
@@ -154,7 +138,7 @@ template <class Data>
bool SetDataIfChanged(
AutofillTable* table,
const std::vector<Data>& data,
- bool (AutofillTable::*getter)(std::vector<std::unique_ptr<Data>>*),
+ bool (AutofillTable::*getter)(std::vector<std::unique_ptr<Data>>*) const,
void (AutofillTable::*setter)(const std::vector<Data>&),
size_t* prev_item_count) {
std::vector<std::unique_ptr<Data>> existing_data;
@@ -270,10 +254,12 @@ void AutofillWalletSyncableService::InjectStartSyncFlare(
flare_ = flare;
}
-syncer::SyncMergeResult AutofillWalletSyncableService::SetSyncData(
- const syncer::SyncDataList& data_list) {
- std::vector<CreditCard> wallet_cards;
- std::vector<AutofillProfile> wallet_addresses;
+// static
+void AutofillWalletSyncableService::PopulateWalletCardsAndAddresses(
+ const syncer::SyncDataList& data_list,
+ std::vector<CreditCard>* wallet_cards,
+ std::vector<AutofillProfile>* wallet_addresses) {
+ std::map<std::string, std::string> ids;
for (const syncer::SyncData& data : data_list) {
DCHECK_EQ(syncer::AUTOFILL_WALLET_DATA, data.GetDataType());
@@ -281,23 +267,64 @@ syncer::SyncMergeResult AutofillWalletSyncableService::SetSyncData(
data.GetSpecifics().autofill_wallet();
if (autofill_specifics.type() ==
sync_pb::AutofillWalletSpecifics::MASKED_CREDIT_CARD) {
- wallet_cards.push_back(
+ wallet_cards->push_back(
CardFromSpecifics(autofill_specifics.masked_card()));
} else {
DCHECK_EQ(sync_pb::AutofillWalletSpecifics::POSTAL_ADDRESS,
autofill_specifics.type());
- wallet_addresses.push_back(
+ wallet_addresses->push_back(
ProfileFromSpecifics(autofill_specifics.address()));
+
+ // Map the sync billing address id to the profile's id.
+ ids[autofill_specifics.address().id()] =
+ wallet_addresses->back().server_id();
}
}
+ // Set the billing address of the wallet cards to the id of the appropriate
+ // profile.
+ for (CreditCard& card : *wallet_cards) {
+ auto it = ids.find(card.billing_address_id());
+ if (it != ids.end())
+ card.set_billing_address_id(it->second);
+ }
+}
+
+// static
+void AutofillWalletSyncableService::CopyRelevantBillingAddressesFromDisk(
+ const AutofillTable& table,
+ std::vector<CreditCard>* cards_from_server) {
+ std::vector<std::unique_ptr<CreditCard>> cards_on_disk;
+ table.GetServerCreditCards(&cards_on_disk);
+
+ // The reasons behind brute-force search are explained in SetDataIfChanged.
+ for (const auto& saved_card : cards_on_disk) {
+ for (CreditCard& server_card : *cards_from_server) {
+ if (saved_card->server_id() == server_card.server_id()) {
+ // Keep the billing address id of the saved cards only if it points to
+ // a local address.
+ if (saved_card->billing_address_id().length() == kLocalGuidSize) {
+ server_card.set_billing_address_id(saved_card->billing_address_id());
+ break;
+ }
+ }
+ }
+ }
+}
+
+syncer::SyncMergeResult AutofillWalletSyncableService::SetSyncData(
+ const syncer::SyncDataList& data_list) {
+ std::vector<CreditCard> wallet_cards;
+ std::vector<AutofillProfile> wallet_addresses;
+ PopulateWalletCardsAndAddresses(data_list, &wallet_cards, &wallet_addresses);
+
// Users can set billing address of the server credit card locally, but that
// information does not propagate to either Chrome Sync or Google Payments
// server. To preserve user's preferred billing address, copy the billing
// addresses from disk into |wallet_cards|.
AutofillTable* table =
AutofillTable::FromWebDatabase(webdata_backend_->GetDatabase());
- CopyBillingAddressesFromDisk(table, &wallet_cards);
+ CopyRelevantBillingAddressesFromDisk(*table, &wallet_cards);
// In the common case, the database won't have changed. Committing an update
// to the database will require at least one DB page write and will schedule
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h
index eb4b9e044ef..03f13116409 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h
@@ -8,10 +8,13 @@
#include "base/macros.h"
#include "base/supports_user_data.h"
#include "base/threading/thread_checker.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
#include "components/sync/model/syncable_service.h"
namespace autofill {
+class AutofillTable;
class AutofillWebDataBackend;
class AutofillWebDataService;
@@ -58,8 +61,33 @@ class AutofillWalletSyncableService
const std::string& app_locale);
private:
+ FRIEND_TEST_ALL_PREFIXES(
+ AutofillWalletSyncableServiceTest,
+ CopyRelevantBillingAddressesFromDisk_KeepLocalAddresses);
+ FRIEND_TEST_ALL_PREFIXES(
+ AutofillWalletSyncableServiceTest,
+ CopyRelevantBillingAddressesFromDisk_OverwriteOtherAddresses);
+ FRIEND_TEST_ALL_PREFIXES(
+ AutofillWalletSyncableServiceTest,
+ PopulateWalletCardsAndAddresses_BillingAddressIdTransfer);
+
syncer::SyncMergeResult SetSyncData(const syncer::SyncDataList& data_list);
+ // Populates the wallet cards and addresses from the sync data and uses the
+ // sync data to link the card to its billing address.
+ static void PopulateWalletCardsAndAddresses(
+ const syncer::SyncDataList& data_list,
+ std::vector<CreditCard>* wallet_cards,
+ std::vector<AutofillProfile>* wallet_addresses);
+
+ // Finds the copies of the same credit card from the server and on disk and
+ // overwrites the server version with the billing id saved on disk if it
+ // refers to a local autofill profile. The credit card's IDs do not change
+ // over time.
+ static void CopyRelevantBillingAddressesFromDisk(
+ const AutofillTable& table,
+ std::vector<CreditCard>* cards_from_server);
+
base::ThreadChecker thread_checker_;
AutofillWebDataBackend* webdata_backend_; // Weak ref.
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service_unittest.cc
new file mode 100644
index 00000000000..9561a8b1c9b
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service_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/autofill/core/browser/webdata/autofill_wallet_syncable_service.h"
+
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/sync/protocol/autofill_specifics.pb.h"
+#include "components/sync/protocol/sync.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+namespace {
+
+syncer::SyncData CreateSyncDataForWalletCreditCard(
+ const std::string& id,
+ const std::string& billing_address_id) {
+ sync_pb::EntitySpecifics specifics;
+
+ sync_pb::AutofillWalletSpecifics* wallet_specifics =
+ specifics.mutable_autofill_wallet();
+ wallet_specifics->set_type(
+ sync_pb::AutofillWalletSpecifics_WalletInfoType::
+ AutofillWalletSpecifics_WalletInfoType_MASKED_CREDIT_CARD);
+
+ sync_pb::WalletMaskedCreditCard* card_specifics =
+ wallet_specifics->mutable_masked_card();
+ card_specifics->set_id(id);
+ card_specifics->set_billing_address_id(billing_address_id);
+ return syncer::SyncData::CreateLocalData(id, id, specifics);
+}
+
+syncer::SyncData CreateSyncDataForWalletAddress(const std::string& id) {
+ sync_pb::EntitySpecifics specifics;
+
+ sync_pb::AutofillWalletSpecifics* wallet_specifics =
+ specifics.mutable_autofill_wallet();
+ wallet_specifics->set_type(
+ sync_pb::AutofillWalletSpecifics_WalletInfoType::
+ AutofillWalletSpecifics_WalletInfoType_POSTAL_ADDRESS);
+
+ sync_pb::WalletPostalAddress* address_specifics =
+ wallet_specifics->mutable_address();
+ address_specifics->set_id(id);
+ return syncer::SyncData::CreateLocalData(id, id, specifics);
+}
+
+class TestAutofillTable : public AutofillTable {
+ public:
+ explicit TestAutofillTable(std::vector<CreditCard> cards_on_disk)
+ : cards_on_disk_(cards_on_disk) {}
+
+ ~TestAutofillTable() override {}
+
+ bool GetServerCreditCards(
+ std::vector<std::unique_ptr<CreditCard>>* cards) const override {
+ for (const auto& card_on_disk : cards_on_disk_)
+ cards->push_back(base::MakeUnique<CreditCard>(card_on_disk));
+ return true;
+ }
+
+ private:
+ std::vector<CreditCard> cards_on_disk_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAutofillTable);
+};
+
+} // anonymous namespace
+
+// Verify that the link between a card and its billing address from sync is
+// present in the generated Autofill objects.
+TEST(AutofillWalletSyncableServiceTest,
+ PopulateWalletCardsAndAddresses_BillingAddressIdTransfer) {
+ std::vector<CreditCard> wallet_cards;
+ std::vector<AutofillProfile> wallet_addresses;
+ syncer::SyncDataList data_list;
+
+ // Create a Sync data for a card and its billing address.
+ data_list.push_back(CreateSyncDataForWalletAddress("1" /* id */));
+ data_list.push_back(CreateSyncDataForWalletCreditCard(
+ "card1" /* id */, "1" /* billing_address_id */));
+
+ AutofillWalletSyncableService::PopulateWalletCardsAndAddresses(
+ data_list, &wallet_cards, &wallet_addresses);
+
+ ASSERT_EQ(1U, wallet_cards.size());
+ ASSERT_EQ(1U, wallet_addresses.size());
+
+ // Make sure the card's billing address id is equal to the address' server id.
+ EXPECT_EQ(wallet_addresses.back().server_id(),
+ wallet_cards.back().billing_address_id());
+}
+
+// Verify that the billing address id from the card saved on disk is kept if it
+// is a local profile guid.
+TEST(AutofillWalletSyncableServiceTest,
+ CopyRelevantBillingAddressesFromDisk_KeepLocalAddresses) {
+ std::vector<CreditCard> cards_on_disk;
+ std::vector<CreditCard> wallet_cards;
+
+ // Create a local profile to be used as a billing address.
+ AutofillProfile billing_address;
+
+ // Create a card on disk that refers to that local profile as its billing
+ // address.
+ cards_on_disk.push_back(CreditCard());
+ cards_on_disk.back().set_billing_address_id(billing_address.guid());
+
+ // Create a card pulled from wallet with the same id, but a different billing
+ // address id.
+ wallet_cards.push_back(CreditCard(cards_on_disk.back()));
+ wallet_cards.back().set_billing_address_id("1234");
+
+ // Setup the TestAutofillTable with the cards_on_disk.
+ TestAutofillTable table(cards_on_disk);
+
+ AutofillWalletSyncableService::CopyRelevantBillingAddressesFromDisk(
+ table, &wallet_cards);
+
+ ASSERT_EQ(1U, wallet_cards.size());
+
+ // Make sure the wallet card replace its billind address id for the one that
+ // was saved on disk.
+ EXPECT_EQ(cards_on_disk.back().billing_address_id(),
+ wallet_cards.back().billing_address_id());
+}
+
+// Verify that the billing address id from the card saved on disk is overwritten
+// if it does not refer to a local profile.
+TEST(AutofillWalletSyncableServiceTest,
+ CopyRelevantBillingAddressesFromDisk_OverwriteOtherAddresses) {
+ std::string old_billing_id = "1234";
+ std::string new_billing_id = "9876";
+ std::vector<CreditCard> cards_on_disk;
+ std::vector<CreditCard> wallet_cards;
+
+ // Create a card on disk that does not refer to a local profile (which have 36
+ // chars ids).
+ cards_on_disk.push_back(CreditCard());
+ cards_on_disk.back().set_billing_address_id(old_billing_id);
+
+ // Create a card pulled from wallet with the same id, but a different billing
+ // address id.
+ wallet_cards.push_back(CreditCard(cards_on_disk.back()));
+ wallet_cards.back().set_billing_address_id(new_billing_id);
+
+ // Setup the TestAutofillTable with the cards_on_disk.
+ TestAutofillTable table(cards_on_disk);
+
+ AutofillWalletSyncableService::CopyRelevantBillingAddressesFromDisk(
+ table, &wallet_cards);
+
+ ASSERT_EQ(1U, wallet_cards.size());
+
+ // Make sure the local address billing id that was saved on disk did not
+ // replace the new one.
+ EXPECT_EQ(new_billing_id, wallet_cards.back().billing_address_id());
+}
+
+} // namespace autofill \ No newline at end of file
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata.h
index 3cdd07322a9..eb573874b12 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_webdata.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata.h
@@ -17,7 +17,6 @@ class Time;
} // namespace base
-class Profile;
class WebDataServiceConsumer;
namespace autofill {
@@ -110,16 +109,11 @@ class AutofillWebData {
const base::string16& full_number) = 0;
virtual void MaskServerCreditCard(const std::string& id) = 0;
- // Updates the use count and last use date for a server card (masked or not).
- virtual void UpdateServerCardUsageStats(const CreditCard& credit_card) = 0;
+ // Updates the metadata for a server card (masked or not).
+ virtual void UpdateServerCardMetadata(const CreditCard& credit_card) = 0;
- // Updates the use count and last use date for a server address.
- virtual void UpdateServerAddressUsageStats(const AutofillProfile& profile)
- = 0;
-
- // Updates the billing address for a server card (masked or not).
- virtual void UpdateServerCardBillingAddress(const CreditCard& credit_card)
- = 0;
+ // Updates the metadata for a server address.
+ virtual void UpdateServerAddressMetadata(const AutofillProfile& profile) = 0;
// Removes Autofill records from the database.
virtual void RemoveAutofillDataModifiedBetween(
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
index ccda7ac2eca..2129da0abdd 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
@@ -29,14 +29,12 @@ AutofillWebDataBackendImpl::AutofillWebDataBackendImpl(
scoped_refptr<base::SingleThreadTaskRunner> db_thread,
const base::Closure& on_changed_callback,
const base::Callback<void(syncer::ModelType)>& on_sync_started_callback)
- : base::RefCountedDeleteOnMessageLoop<AutofillWebDataBackendImpl>(
- db_thread),
+ : base::RefCountedDeleteOnSequence<AutofillWebDataBackendImpl>(db_thread),
ui_thread_(ui_thread),
db_thread_(db_thread),
web_database_backend_(web_database_backend),
on_changed_callback_(on_changed_callback),
- on_sync_started_callback_(on_sync_started_callback) {
-}
+ on_sync_started_callback_(on_sync_started_callback) {}
void AutofillWebDataBackendImpl::AddObserver(
AutofillWebDataServiceObserverOnDBThread* observer) {
@@ -372,11 +370,11 @@ WebDatabase::State
return WebDatabase::COMMIT_NOT_NEEDED;
}
-WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardUsageStats(
+WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardMetadata(
const CreditCard& card,
WebDatabase* db) {
DCHECK(db_thread_->BelongsToCurrentThread());
- if (!AutofillTable::FromWebDatabase(db)->UpdateServerCardUsageStats(card))
+ if (!AutofillTable::FromWebDatabase(db)->UpdateServerCardMetadata(card))
return WebDatabase::COMMIT_NOT_NEEDED;
for (auto& db_observer : db_observer_list_) {
@@ -387,11 +385,11 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardUsageStats(
return WebDatabase::COMMIT_NEEDED;
}
-WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressUsageStats(
+WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressMetadata(
const AutofillProfile& profile,
WebDatabase* db) {
DCHECK(db_thread_->BelongsToCurrentThread());
- if (!AutofillTable::FromWebDatabase(db)->UpdateServerAddressUsageStats(
+ if (!AutofillTable::FromWebDatabase(db)->UpdateServerAddressMetadata(
profile)) {
return WebDatabase::COMMIT_NOT_NEEDED;
}
@@ -404,23 +402,6 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressUsageStats(
return WebDatabase::COMMIT_NEEDED;
}
-WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardBillingAddress(
- const CreditCard& card,
- WebDatabase* db) {
- DCHECK(db_thread_->BelongsToCurrentThread());
- if (!AutofillTable::FromWebDatabase(db)->UpdateServerCardBillingAddress(
- card)) {
- return WebDatabase::COMMIT_NOT_NEEDED;
- }
-
- for (auto& db_observer : db_observer_list_) {
- db_observer.CreditCardChanged(
- CreditCardChange(CreditCardChange::UPDATE, card.guid(), &card));
- }
-
- return WebDatabase::COMMIT_NEEDED;
-}
-
WebDatabase::State AutofillWebDataBackendImpl::ClearAllServerData(
WebDatabase* db) {
DCHECK(db_thread_->BelongsToCurrentThread());
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
index 516e4d14151..9badefeb70c 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
@@ -9,7 +9,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/ref_counted_delete_on_message_loop.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/observer_list.h"
#include "base/supports_user_data.h"
#include "components/autofill/core/browser/webdata/autofill_webdata.h"
@@ -39,7 +39,7 @@ class CreditCard;
// WebDataService.
// This class is destroyed on the DB thread.
class AutofillWebDataBackendImpl
- : public base::RefCountedDeleteOnMessageLoop<AutofillWebDataBackendImpl>,
+ : public base::RefCountedDeleteOnSequence<AutofillWebDataBackendImpl>,
public AutofillWebDataBackend {
public:
// |web_database_backend| is used to access the WebDatabase directly for
@@ -152,17 +152,11 @@ class AutofillWebDataBackendImpl
WebDatabase::State MaskServerCreditCard(const std::string& id,
WebDatabase* db);
- WebDatabase::State UpdateServerCardUsageStats(
- const CreditCard& credit_card,
- WebDatabase* db);
-
- WebDatabase::State UpdateServerAddressUsageStats(
- const AutofillProfile& profile,
- WebDatabase* db);
+ WebDatabase::State UpdateServerCardMetadata(const CreditCard& credit_card,
+ WebDatabase* db);
- WebDatabase::State UpdateServerCardBillingAddress(
- const CreditCard& card,
- WebDatabase* db);
+ WebDatabase::State UpdateServerAddressMetadata(const AutofillProfile& profile,
+ WebDatabase* db);
WebDatabase::State ClearAllServerData(WebDatabase* db);
@@ -184,7 +178,7 @@ class AutofillWebDataBackendImpl
~AutofillWebDataBackendImpl() override;
private:
- friend class base::RefCountedDeleteOnMessageLoop<AutofillWebDataBackendImpl>;
+ friend class base::RefCountedDeleteOnSequence<AutofillWebDataBackendImpl>;
friend class base::DeleteHelper<AutofillWebDataBackendImpl>;
// This makes the destructor public, and thus allows us to aggregate
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc
index 22377e9e3af..d8d072f0d43 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc
@@ -212,28 +212,18 @@ void AutofillWebDataService::ClearAllServerData() {
autofill_backend_));
}
-void AutofillWebDataService::UpdateServerCardUsageStats(
+void AutofillWebDataService::UpdateServerCardMetadata(
const CreditCard& credit_card) {
wdbs_->ScheduleDBTask(
- FROM_HERE,
- Bind(&AutofillWebDataBackendImpl::UpdateServerCardUsageStats,
- autofill_backend_, credit_card));
+ FROM_HERE, Bind(&AutofillWebDataBackendImpl::UpdateServerCardMetadata,
+ autofill_backend_, credit_card));
}
-void AutofillWebDataService::UpdateServerAddressUsageStats(
+void AutofillWebDataService::UpdateServerAddressMetadata(
const AutofillProfile& profile) {
wdbs_->ScheduleDBTask(
- FROM_HERE,
- Bind(&AutofillWebDataBackendImpl::UpdateServerAddressUsageStats,
- autofill_backend_, profile));
-}
-
-void AutofillWebDataService::UpdateServerCardBillingAddress(
- const CreditCard& credit_card) {
- wdbs_->ScheduleDBTask(
- FROM_HERE,
- Bind(&AutofillWebDataBackendImpl::UpdateServerCardBillingAddress,
- autofill_backend_, credit_card));
+ FROM_HERE, Bind(&AutofillWebDataBackendImpl::UpdateServerAddressMetadata,
+ autofill_backend_, profile));
}
void AutofillWebDataService::RemoveAutofillDataModifiedBetween(
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h
index ab0ac2c86e5..dfc35afca5f 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h
@@ -28,7 +28,6 @@ class SingleThreadTaskRunner;
namespace autofill {
-class AutofillChange;
class AutofillEntry;
class AutofillProfile;
class AutofillWebDataBackend;
@@ -98,9 +97,8 @@ class AutofillWebDataService : public AutofillWebData,
void ClearAllServerData();
- void UpdateServerCardUsageStats(const CreditCard& credit_card) override;
- void UpdateServerAddressUsageStats(const AutofillProfile& profile) override;
- void UpdateServerCardBillingAddress(const CreditCard& credit_card) override;
+ void UpdateServerCardMetadata(const CreditCard& credit_card) override;
+ void UpdateServerAddressMetadata(const AutofillProfile& profile) override;
void RemoveAutofillDataModifiedBetween(const base::Time& delete_begin,
const base::Time& delete_end) override;
diff --git a/chromium/components/autofill/core/common/autofill_regexes.cc b/chromium/components/autofill/core/common/autofill_regexes.cc
index dc3dd58e04b..c9fa5222ac1 100644
--- a/chromium/components/autofill/core/common/autofill_regexes.cc
+++ b/chromium/components/autofill/core/common/autofill_regexes.cc
@@ -5,9 +5,9 @@
#include "components/autofill/core/common/autofill_regexes.h"
#include <memory>
+#include <unordered_map>
#include <utility>
-#include "base/containers/scoped_ptr_hash_map.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
@@ -30,7 +30,7 @@ class AutofillRegexes {
friend struct base::DefaultSingletonTraits<AutofillRegexes>;
// Maps patterns to their corresponding regex matchers.
- base::ScopedPtrHashMap<base::string16, std::unique_ptr<icu::RegexMatcher>>
+ std::unordered_map<base::string16, std::unique_ptr<icu::RegexMatcher>>
matchers_;
DISALLOW_COPY_AND_ASSIGN(AutofillRegexes);
@@ -57,11 +57,11 @@ icu::RegexMatcher* AutofillRegexes::GetMatcher(const base::string16& pattern) {
new icu::RegexMatcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status));
DCHECK(U_SUCCESS(status));
- auto result = matchers_.add(pattern, std::move(matcher));
+ auto result = matchers_.insert(std::make_pair(pattern, std::move(matcher)));
DCHECK(result.second);
it = result.first;
}
- return it->second;
+ return it->second.get();
}
} // namespace
diff --git a/chromium/components/autofill/core/common/password_form.cc b/chromium/components/autofill/core/common/password_form.cc
index 17db56672b2..3969e1ff4e4 100644
--- a/chromium/components/autofill/core/common/password_form.cc
+++ b/chromium/components/autofill/core/common/password_form.cc
@@ -145,8 +145,9 @@ bool ArePasswordFormUniqueKeyEqual(const PasswordForm& left,
left.password_element == right.password_element);
}
-bool LessThanUniqueKey::operator()(const PasswordForm* left,
- const PasswordForm* right) const {
+bool LessThanUniqueKey::operator()(
+ const std::unique_ptr<PasswordForm>& left,
+ const std::unique_ptr<PasswordForm>& right) const {
int result = left->signon_realm.compare(right->signon_realm);
if (result)
return result < 0;
diff --git a/chromium/components/autofill/core/common/password_form.h b/chromium/components/autofill/core/common/password_form.h
index c70da901fc9..74716230236 100644
--- a/chromium/components/autofill/core/common/password_form.h
+++ b/chromium/components/autofill/core/common/password_form.h
@@ -165,6 +165,10 @@ struct PasswordForm {
// element corresponding to the new password. Optional, and not persisted.
base::string16 new_password_element;
+ // The confirmation password element. Optional, only set on form parsing, and
+ // not persisted.
+ base::string16 confirmation_password_element;
+
// The new password. Optional, and not persisted.
base::string16 new_password_value;
@@ -293,7 +297,8 @@ bool ArePasswordFormUniqueKeyEqual(const PasswordForm& left,
// A comparator for the unique key.
struct LessThanUniqueKey {
- bool operator()(const PasswordForm* left, const PasswordForm* right) const;
+ bool operator()(const std::unique_ptr<PasswordForm>& left,
+ const std::unique_ptr<PasswordForm>& right) const;
};
// For testing.
diff --git a/chromium/components/autofill/core/common/password_form_fill_data.cc b/chromium/components/autofill/core/common/password_form_fill_data.cc
index b4d5fd047bc..9b1383046e0 100644
--- a/chromium/components/autofill/core/common/password_form_fill_data.cc
+++ b/chromium/components/autofill/core/common/password_form_fill_data.cc
@@ -22,9 +22,7 @@ bool UsernamesCollectionKey::operator<(
}
PasswordFormFillData::PasswordFormFillData()
- : wait_for_username(false),
- is_possible_change_password_form(false) {
-}
+ : wait_for_username(false), is_possible_change_password_form(false) {}
PasswordFormFillData::PasswordFormFillData(const PasswordFormFillData& other) =
default;
diff --git a/chromium/components/autofill/core/common/save_password_progress_logger.cc b/chromium/components/autofill/core/common/save_password_progress_logger.cc
index b2dd78b63c8..93d1bc6c500 100644
--- a/chromium/components/autofill/core/common/save_password_progress_logger.cc
+++ b/chromium/components/autofill/core/common/save_password_progress_logger.cc
@@ -266,6 +266,8 @@ std::string SavePasswordProgressLogger::GetStringFromID(
return "Invalid form";
case SavePasswordProgressLogger::STRING_SYNC_CREDENTIAL:
return "Credential is used for syncing passwords";
+ case STRING_BLOCK_PASSWORD_SAME_ORIGIN_INSECURE_SCHEME:
+ return "Blocked password due to same origin but insecure scheme";
case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVED_FORM:
return "provisionally_saved_form";
case SavePasswordProgressLogger::STRING_IGNORE_POSSIBLE_USERNAMES:
@@ -318,11 +320,11 @@ std::string SavePasswordProgressLogger::GetStringFromID(
case SavePasswordProgressLogger::STRING_BEST_SCORE:
return "best_score";
case SavePasswordProgressLogger::STRING_ON_GET_STORE_RESULTS_METHOD:
- return "PasswordFormManager::OnGetPasswordStoreResults";
+ return "FormFetcherImpl::OnGetPasswordStoreResults";
case SavePasswordProgressLogger::STRING_NUMBER_RESULTS:
return "Number of results from the password store";
- case SavePasswordProgressLogger::STRING_FETCH_LOGINS_METHOD:
- return "PasswordFormManager::FetchMatchingLoginsFromPasswordStore";
+ case SavePasswordProgressLogger::STRING_FETCH_METHOD:
+ return "FormFetcherImpl::Fetch";
case SavePasswordProgressLogger::STRING_NO_STORE:
return "PasswordStore is not available";
case SavePasswordProgressLogger::STRING_CREATE_LOGIN_MANAGERS_METHOD:
@@ -346,8 +348,8 @@ std::string SavePasswordProgressLogger::GetStringFromID(
return "PasswordFormManager::ProcessFrame";
case SavePasswordProgressLogger::STRING_FORM_SIGNATURE:
return "Signature of form";
- case SavePasswordProgressLogger::STRING_FORM_MANAGER_STATE:
- return "PasswordFormManager::state_";
+ case SavePasswordProgressLogger::STRING_FORM_FETCHER_STATE:
+ return "FormFetcherImpl::state_";
case SavePasswordProgressLogger::STRING_ADDING_SIGNATURE:
return "Adding manager for form";
case SavePasswordProgressLogger::STRING_UNOWNED_INPUTS_VISIBLE:
@@ -388,6 +390,8 @@ std::string SavePasswordProgressLogger::GetStringFromID(
return "Server predictions";
case SavePasswordProgressLogger::STRING_FORM_VOTES:
return "Form votes";
+ case SavePasswordProgressLogger::STRING_REUSE_FOUND:
+ return "Password reused from ";
case SavePasswordProgressLogger::STRING_INVALID:
return "INVALID";
// Intentionally no default: clause here -- all IDs need to get covered.
diff --git a/chromium/components/autofill/core/common/save_password_progress_logger.h b/chromium/components/autofill/core/common/save_password_progress_logger.h
index d8c9fe1ac08..de8a08232b4 100644
--- a/chromium/components/autofill/core/common/save_password_progress_logger.h
+++ b/chromium/components/autofill/core/common/save_password_progress_logger.h
@@ -90,6 +90,7 @@ class SavePasswordProgressLogger {
STRING_FORM_BLACKLISTED,
STRING_INVALID_FORM,
STRING_SYNC_CREDENTIAL,
+ STRING_BLOCK_PASSWORD_SAME_ORIGIN_INSECURE_SCHEME,
STRING_PROVISIONALLY_SAVED_FORM,
STRING_IGNORE_POSSIBLE_USERNAMES,
STRING_ON_PASSWORD_FORMS_RENDERED_METHOD,
@@ -116,7 +117,7 @@ class SavePasswordProgressLogger {
STRING_BEST_SCORE,
STRING_ON_GET_STORE_RESULTS_METHOD,
STRING_NUMBER_RESULTS,
- STRING_FETCH_LOGINS_METHOD,
+ STRING_FETCH_METHOD,
STRING_NO_STORE,
STRING_CREATE_LOGIN_MANAGERS_METHOD,
STRING_OLD_NUMBER_LOGIN_MANAGERS,
@@ -129,7 +130,7 @@ class SavePasswordProgressLogger {
STRING_PROCESS_FRAME_METHOD,
STRING_FORM_SIGNATURE,
STRING_ADDING_SIGNATURE,
- STRING_FORM_MANAGER_STATE,
+ STRING_FORM_FETCHER_STATE,
STRING_UNOWNED_INPUTS_VISIBLE,
STRING_ON_FILL_PASSWORD_FORM_METHOD,
STRING_ON_SHOW_INITIAL_PASSWORD_ACCOUNT_SUGGESTIONS,
@@ -148,6 +149,7 @@ class SavePasswordProgressLogger {
STRING_FIELDS,
STRING_SERVER_PREDICTIONS,
STRING_FORM_VOTES,
+ STRING_REUSE_FOUND,
STRING_INVALID, // Represents a string returned in a case of an error.
STRING_MAX = STRING_INVALID
};
diff --git a/chromium/components/autofill/ios/browser/BUILD.gn b/chromium/components/autofill/ios/browser/BUILD.gn
index 91dbed34da0..c16d7d83090 100644
--- a/chromium/components/autofill/ios/browser/BUILD.gn
+++ b/chromium/components/autofill/ios/browser/BUILD.gn
@@ -27,7 +27,6 @@ source_set("browser") {
"//base",
"//components/autofill/core/browser",
"//components/autofill/core/common",
- "//ios/public/provider/web",
"//ios/web",
"//ui/gfx/geometry",
]
diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.h b/chromium/components/autofill/ios/browser/autofill_driver_ios.h
index 2da12651251..c6678f31c49 100644
--- a/chromium/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.h
@@ -21,8 +21,6 @@ class WebState;
namespace autofill {
-class AutofillManagerDelegate;
-
// Class that drives autofill flow on iOS. There is one instance per
// WebContents.
class AutofillDriverIOS : public AutofillDriver,
diff --git a/chromium/components/autofill/ios/browser/form_suggestion.h b/chromium/components/autofill/ios/browser/form_suggestion.h
index 7fd9df1ddbd..8fb6689d694 100644
--- a/chromium/components/autofill/ios/browser/form_suggestion.h
+++ b/chromium/components/autofill/ios/browser/form_suggestion.h
@@ -7,8 +7,6 @@
#import <Foundation/Foundation.h>
-#include "base/mac/objc_property_releaser.h"
-
// Represents a user-selectable suggestion for a single field within a form
// on a web page.
@interface FormSuggestion : NSObject
diff --git a/chromium/components/autofill/ios/browser/form_suggestion.mm b/chromium/components/autofill/ios/browser/form_suggestion.mm
index 709bb63b4ce..f4bb40c37c3 100644
--- a/chromium/components/autofill/ios/browser/form_suggestion.mm
+++ b/chromium/components/autofill/ios/browser/form_suggestion.mm
@@ -4,6 +4,8 @@
#import "components/autofill/ios/browser/form_suggestion.h"
+#include "base/mac/objc_property_releaser.h"
+
@interface FormSuggestion ()
// Local initializer for a FormSuggestion.
- (id)initWithValue:(NSString*)value
diff --git a/chromium/components/autofill_strings.grdp b/chromium/components/autofill_strings.grdp
index fa85cc94906..e2a1b8ec882 100644
--- a/chromium/components/autofill_strings.grdp
+++ b/chromium/components/autofill_strings.grdp
@@ -17,9 +17,15 @@
<message name="IDS_AUTOFILL_WARNING_FORM_DISABLED" desc="Warning text to show when autofill is disabled by the website for a given form.">
This webpage has disabled automatic filling for this form.
</message>
+ <message name="IDS_AUTOFILL_WARNING_PAYMENT_DISABLED" desc="Warning text to show when credit card autofill is disabled because the website is not using a secure connection. This warning text is shown below a separate warning indicating that the form is not secure, so this message indicates just that payment autofilling has been disabled (not why).">
+ Payment autofilling disabled
+ </message>
<message name="IDS_AUTOFILL_WARNING_INSECURE_CONNECTION" desc="Warning text to show when credit card autofill is disabled because the website is not using a secure connection.">
Automatic credit card filling is disabled because this form does not use a secure connection.
</message>
+ <message name="IDS_AUTOFILL_HTTP_WARNING_LEARN_MORE" desc="The text displayed with http bad warning message in the Autofill popup to direct the user to the Chrome security connection help center page.">
+ Learn more
+ </message>
<message name="IDS_AUTOFILL_CREDIT_CARD_HTTP_WARNING_MESSAGE" desc="Chrome can help the user fill credit card web forms by showing a pop-up with text suggestions next to a focused credit card text field. If the form is on an insecure site (e.g., http://), this text is shown on top of those suggestions.">
Payment not secure
</message>
@@ -66,6 +72,9 @@
<message name="IDS_AUTOFILL_CC_MASTERCARD" desc="MasterCard credit card name." formatter_data="android_java">
MasterCard
</message>
+ <message name="IDS_AUTOFILL_CC_MIR" desc="Mir credit card name." formatter_data="android_java">
+ Mir
+ </message>
<message name="IDS_AUTOFILL_CC_UNION_PAY" desc="China UnionPay credit card name." formatter_data="android_java">
China UnionPay
</message>
@@ -78,7 +87,7 @@
<message name="IDS_AUTOFILL_ADDRESS_LINE_SEPARATOR" desc="The separator character used to join multi-line addresses.">
, '''
</message>
- <message name="IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR" desc="The separator character used in the summary of an address.">
+ <message name="IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR" desc="The separator character used in the summary of an address." formatter_data="android_java">
, '''
</message>
@@ -151,6 +160,17 @@
</message>
</if>
+ <if expr="_google_chrome">
+ <message name="IDS_AUTOFILL_CREDIT_CARD_OPTIONS_POPUP" desc="The label of the text displayed in the Autofill Credit Card popup to direct the user to the Autofill settings UI.">
+ Chrome Autofill settings...
+ </message>
+ </if>
+ <if expr="not _google_chrome">
+ <message name="IDS_AUTOFILL_CREDIT_CARD_OPTIONS_POPUP" desc="The label of the text displayed in the Autofill Credit Card popup to direct the user to the Autofill settings UI.">
+ Chromium Autofill settings...
+ </message>
+ </if>
+
<message name="IDS_AUTOFILL_OPTIONS_CONTENT_DESCRIPTION" desc="The text verbalised by a screen reader for the button that directs the user to the Autofill settings UI. This string is not displayed.">
settings
</message>
@@ -177,7 +197,7 @@
Use password for:
</message>
- <message name="IDS_AUTOFILL_PASSWORD_HTTP_WARNING_MESSAGE" desc="Chrome can help the user fill password web forms by showing a pop-up with text suggestions next to a focused password text field. If the form is on an insecure site (e.g., http://), this text is shown on top of those suggestions.">
+ <message name="IDS_AUTOFILL_LOGIN_HTTP_WARNING_MESSAGE" desc="Chrome can help the user fill login web forms by showing a pop-up with text suggestions next to a focused text field. If the form is on an insecure site (e.g., http://), this text is shown on top of those suggestions.">
Login not secure
</message>
@@ -223,13 +243,26 @@
Exp: <ph name="EXPIRATION_MONTH">$1<ex>06</ex></ph>/<ph name="EXPIRATION_YEAR">$2<ex>17</ex></ph>
</message>
+ <message name="IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_LABEL_AND_ABBR" desc="text displayed in the Autofill Credit Card popup before the credit card expiration date and the abbreviated expiration date.">
+ , exp <ph name="EXPIRATION_DATE_ABBR">$1<ex>06/17</ex></ph>
+ </message>
+
<!-- Autofill credit card unmask prompt -->
- <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN" desc="Error message that encourages the user to try to re-enter their credit card CVC after a previous failed attempt.">
+ <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_CVC" desc="Error message that encourages the user to try to re-enter their credit card CVC after a previous failed attempt." formatter_data="android_java">
Check your CVC and try again
</message>
- <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_WITH_EXPIRATION" desc="Error message that encourages the user to try to re-enter their credit card expiration date and CVC after a previous failed attempt.">
+ <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_EXPIRATION_DATE" desc="Error message that encourages the user to try to re-enter their credit card expiration date after a previous failed attempt." formatter_data="android_java">
+ Check your expiration date and try again
+ </message>
+ <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_CVC_AND_EXPIRATION" desc="Error message that encourages the user to try to re-enter their credit card expiration date and CVC after a previous failed attempt." formatter_data="android_java">
Check your expiration date and CVC and try again
</message>
+ <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_EXPIRATION_MONTH" desc="Error message that encourages the user to try to re-enter their credit card expiration month after a previous failed attempt." formatter_data="android_java">
+ Check your expiration month and try again
+ </message>
+ <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_EXPIRATION_YEAR" desc="Error message that encourages the user to try to re-enter their credit card expiration year after a previous failed attempt." formatter_data="android_java">
+ Check your expiration year and try again
+ </message>
<if expr="_google_chrome">
<message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT" desc="Error message to show when a credit card cannot be verified and the user isn't allowed to retry.">
Chrome was unable to confirm your card at this time. Please try again later.
@@ -247,8 +280,8 @@
<message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE" desc="Title for the credit card unmasking dialog.">
Enter the CVC for <ph name="CREDIT_CARD">$1<ex>Visa - 5679</ex></ph>
</message>
- <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_UPDATE_TITLE" desc="Title for the credit card unmasking dialog when the credit card is expired.">
- Enter the expiration date and CVC for <ph name="CREDIT_CARD">$1<ex>Visa - 5679</ex></ph> to update your card details
+ <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_EXPIRED_TITLE" desc="Title for the credit card unmasking dialog when the credit card is expired.">
+ Enter the expiration date and CVC for <ph name="CREDIT_CARD">$1<ex>Visa - 5679</ex></ph>
</message>
<message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS" desc="Text explaining what the user should do in the card unmasking dialog.">
Once you confirm, your card details will be shared with this site
@@ -314,4 +347,23 @@
CVC
</message>
+ <!-- Payment Request -->
+ <message name="IDS_PAYMENT_REQUEST_ORDER_SUMMARY_SECTION_NAME" desc="The name of the Order Summary section in the Payment Sheet of the Payment Request dialog.">
+ Order summary
+ </message>
+ <message name="IDS_PAYMENT_REQUEST_ORDER_SUMMARY_SECTION_TOTAL_FORMAT" desc="The format specifier of the Total label in the Order Summary section of the Payment Sheet of the Payment Request dialog.">
+ <ph name="TOTAL_LABEL">$1<ex>Total</ex></ph> <ph name="CURRENCY_CODE">$2<ex>USD</ex></ph> <ph name="FORMATTED_TOTAL_AMOUNT">$3<ex>$ 12.34</ex></ph>
+ </message>
+ <message name="IDS_PAYMENT_REQUEST_PAYMENT_METHOD_SECTION_NAME" desc="The name of the Payment Method section in the Payment Sheet of the Payment Request dialog.">
+ Payment
+ </message>
+ <message name="IDS_PAYMENT_REQUEST_ORDER_SUMMARY_SHEET_TOTAL_FORMAT" desc="The format specifier of the Total label in the Order Summary Sheet of the Payment Request dialog.">
+ <ph name="CURRENCY_CODE">$1<ex>USD</ex></ph> <ph name="FORMATTED_TOTAL_AMOUNT">$2<ex>$ 12.34</ex></ph>
+ </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
+ </message>
+ <message name="IDS_PAYMENT_REQUEST_CONTACT_INFO_SECTION_NAME" desc="The name of the Contact Info section in the Payment Sheet of the Payment Request dialog.">
+ Contact info
+ </message>
</grit-part>
diff --git a/chromium/components/bookmark_component_strings.grdp b/chromium/components/bookmark_component_strings.grdp
index 6acef339887..efd47772f4d 100644
--- a/chromium/components/bookmark_component_strings.grdp
+++ b/chromium/components/bookmark_component_strings.grdp
@@ -7,7 +7,7 @@
New folder
</message>
<message name="IDS_BOOKMARK_EDITOR_TITLE" desc="Title of the window the bookmark editor is in.">
- Edit Bookmark
+ Edit bookmark
</message>
<if expr="not use_titlecase">
<message name="IDS_BOOKMARK_MANAGER_NAME_INPUT_PLACE_HOLDER" desc="Text to show in the URL input field when editing or creating bookmarks.">
diff --git a/chromium/components/bookmarks/browser/BUILD.gn b/chromium/components/bookmarks/browser/BUILD.gn
index 05e81256e48..b606dc4b13d 100644
--- a/chromium/components/bookmarks/browser/BUILD.gn
+++ b/chromium/components/bookmarks/browser/BUILD.gn
@@ -14,10 +14,6 @@ static_library("browser") {
"bookmark_codec.h",
"bookmark_expanded_state_tracker.cc",
"bookmark_expanded_state_tracker.h",
- "bookmark_index.cc",
- "bookmark_index.h",
- "bookmark_match.cc",
- "bookmark_match.h",
"bookmark_model.cc",
"bookmark_model.h",
"bookmark_model_observer.h",
@@ -39,6 +35,14 @@ static_library("browser") {
"scoped_group_bookmark_actions.h",
"startup_task_runner_service.cc",
"startup_task_runner_service.h",
+ "titled_url_index.cc",
+ "titled_url_index.h",
+ "titled_url_match.cc",
+ "titled_url_match.h",
+ "titled_url_node.h",
+ "titled_url_node_sorter.h",
+ "typed_count_sorter.cc",
+ "typed_count_sorter.h",
]
public_deps = [
@@ -96,6 +100,7 @@ source_set("unit_tests") {
"bookmark_index_unittest.cc",
"bookmark_model_unittest.cc",
"bookmark_utils_unittest.cc",
+ "titled_url_match_unittest.cc",
]
if (toolkit_views) {
diff --git a/chromium/components/bookmarks/browser/bookmark_client.cc b/chromium/components/bookmarks/browser/bookmark_client.cc
index 93250153eaf..a031ee75500 100644
--- a/chromium/components/bookmarks/browser/bookmark_client.cc
+++ b/chromium/components/bookmarks/browser/bookmark_client.cc
@@ -22,13 +22,12 @@ base::CancelableTaskTracker::TaskId BookmarkClient::GetFaviconImageForPageURL(
return base::CancelableTaskTracker::kBadTaskId;
}
-bool BookmarkClient::SupportsTypedCountForNodes() {
+bool BookmarkClient::SupportsTypedCountForUrls() {
return false;
}
-void BookmarkClient::GetTypedCountForNodes(
- const NodeSet& nodes,
- NodeTypedCountPairs* node_typed_count_pairs) {
+void BookmarkClient::GetTypedCountForUrls(
+ UrlTypedCountMap* url_typed_count_map) {
NOTREACHED();
}
diff --git a/chromium/components/bookmarks/browser/bookmark_client.h b/chromium/components/bookmarks/browser/bookmark_client.h
index 5eb625fa88f..7648cb9233c 100644
--- a/chromium/components/bookmarks/browser/bookmark_client.h
+++ b/chromium/components/bookmarks/browser/bookmark_client.h
@@ -5,9 +5,8 @@
#ifndef COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_CLIENT_H_
#define COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_CLIENT_H_
-#include <set>
+#include <map>
#include <utility>
-#include <vector>
#include "base/callback_forward.h"
#include "base/task/cancelable_task_tracker.h"
@@ -32,12 +31,9 @@ class BookmarkPermanentNode;
// e.g. Chrome.
class BookmarkClient {
public:
- // Types representing a set of BookmarkNode and a mapping from BookmarkNode
- // to the number of time the corresponding URL has been typed by the user in
- // the Omnibox.
- typedef std::set<const BookmarkNode*> NodeSet;
- typedef std::pair<const BookmarkNode*, int> NodeTypedCountPair;
- typedef std::vector<NodeTypedCountPair> NodeTypedCountPairs;
+ // Type representing a mapping from URLs to the number of times the URL has
+ // been typed by the user in the Omnibox.
+ using UrlTypedCountMap = std::unordered_map<const GURL*, int>;
virtual ~BookmarkClient() {}
@@ -62,13 +58,13 @@ class BookmarkClient {
base::CancelableTaskTracker* tracker);
// Returns true if the embedder supports typed count for URL.
- virtual bool SupportsTypedCountForNodes();
+ virtual bool SupportsTypedCountForUrls();
- // Retrieves the number of time each BookmarkNode URL has been typed in
- // the Omnibox by the user.
- virtual void GetTypedCountForNodes(
- const NodeSet& nodes,
- NodeTypedCountPairs* node_typed_count_pairs);
+ // Retrieves the number of times each bookmark URL has been typed in
+ // the Omnibox by the user. For each key (URL) in |url_typed_count_map|,
+ // the corresponding value will be updated with the typed count of that URL.
+ // |url_typed_count_map| must not be null.
+ virtual void GetTypedCountForUrls(UrlTypedCountMap* url_typed_count_map);
// Returns whether the embedder wants permanent node |node|
// to always be visible or to only show them when not empty.
diff --git a/chromium/components/bookmarks/browser/bookmark_codec.cc b/chromium/components/bookmarks/browser/bookmark_codec.cc
index 1d6ff890bb6..eadb6660cb0 100644
--- a/chromium/components/bookmarks/browser/bookmark_codec.cc
+++ b/chromium/components/bookmarks/browser/bookmark_codec.cc
@@ -177,7 +177,7 @@ bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
const base::Value* checksum_value;
if (d_value->Get(kChecksumKey, &checksum_value)) {
- if (checksum_value->GetType() != base::Value::TYPE_STRING)
+ if (checksum_value->GetType() != base::Value::Type::STRING)
return false;
if (!checksum_value->GetAsString(&stored_checksum_))
return false;
@@ -333,7 +333,7 @@ bool BookmarkCodec::DecodeNode(const base::DictionaryValue& value,
if (!value.Get(kChildrenKey, &child_values))
return false;
- if (child_values->GetType() != base::Value::TYPE_LIST)
+ if (child_values->GetType() != base::Value::Type::LIST)
return false;
if (!node) {
@@ -395,7 +395,7 @@ bool BookmarkCodec::DecodeMetaInfo(const base::DictionaryValue& value,
// Meta info used to be stored as a serialized dictionary, so attempt to
// parse the value as one.
- if (meta_info->IsType(base::Value::TYPE_STRING)) {
+ if (meta_info->IsType(base::Value::Type::STRING)) {
std::string meta_info_str;
meta_info->GetAsString(&meta_info_str);
JSONStringValueDeserializer deserializer(meta_info_str);
@@ -433,11 +433,11 @@ void BookmarkCodec::DecodeMetaInfoHelper(
const std::string& prefix,
BookmarkNode::MetaInfoMap* meta_info_map) {
for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
- if (it.value().IsType(base::Value::TYPE_DICTIONARY)) {
+ if (it.value().IsType(base::Value::Type::DICTIONARY)) {
const base::DictionaryValue* subdict;
it.value().GetAsDictionary(&subdict);
DecodeMetaInfoHelper(*subdict, prefix + it.key() + ".", meta_info_map);
- } else if (it.value().IsType(base::Value::TYPE_STRING)) {
+ } else if (it.value().IsType(base::Value::Type::STRING)) {
it.value().GetAsString(&(*meta_info_map)[prefix + it.key()]);
}
}
diff --git a/chromium/components/bookmarks/browser/bookmark_codec_unittest.cc b/chromium/components/bookmarks/browser/bookmark_codec_unittest.cc
index edac5fe314f..a9d8523658a 100644
--- a/chromium/components/bookmarks/browser/bookmark_codec_unittest.cc
+++ b/chromium/components/bookmarks/browser/bookmark_codec_unittest.cc
@@ -114,33 +114,33 @@ class BookmarkCodecTest : public testing::Test {
void GetBookmarksBarChildValue(base::Value* value,
size_t index,
base::DictionaryValue** result_value) {
- ASSERT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+ ASSERT_EQ(base::Value::Type::DICTIONARY, value->GetType());
base::DictionaryValue* d_value = nullptr;
value->GetAsDictionary(&d_value);
base::Value* roots;
ASSERT_TRUE(d_value->Get(BookmarkCodec::kRootsKey, &roots));
- ASSERT_EQ(base::Value::TYPE_DICTIONARY, roots->GetType());
+ ASSERT_EQ(base::Value::Type::DICTIONARY, roots->GetType());
base::DictionaryValue* roots_d_value = nullptr;
roots->GetAsDictionary(&roots_d_value);
base::Value* bb_value;
ASSERT_TRUE(
roots_d_value->Get(BookmarkCodec::kRootFolderNameKey, &bb_value));
- ASSERT_EQ(base::Value::TYPE_DICTIONARY, bb_value->GetType());
+ ASSERT_EQ(base::Value::Type::DICTIONARY, bb_value->GetType());
base::DictionaryValue* bb_d_value = nullptr;
bb_value->GetAsDictionary(&bb_d_value);
base::Value* bb_children_value;
ASSERT_TRUE(
bb_d_value->Get(BookmarkCodec::kChildrenKey, &bb_children_value));
- ASSERT_EQ(base::Value::TYPE_LIST, bb_children_value->GetType());
+ ASSERT_EQ(base::Value::Type::LIST, bb_children_value->GetType());
base::ListValue* bb_children_l_value = nullptr;
bb_children_value->GetAsList(&bb_children_l_value);
base::Value* child_value;
ASSERT_TRUE(bb_children_l_value->Get(index, &child_value));
- ASSERT_EQ(base::Value::TYPE_DICTIONARY, child_value->GetType());
+ ASSERT_EQ(base::Value::Type::DICTIONARY, child_value->GetType());
child_value->GetAsDictionary(result_value);
}
diff --git a/chromium/components/bookmarks/browser/bookmark_index.h b/chromium/components/bookmarks/browser/bookmark_index.h
deleted file mode 100644
index 1e00c8d5224..00000000000
--- a/chromium/components/bookmarks/browser/bookmark_index.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_BOOKMARKS_BROWSER_BOOKMARK_INDEX_H_
-#define COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_INDEX_H_
-
-#include <stddef.h>
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "components/query_parser/query_parser.h"
-
-namespace bookmarks {
-
-class BookmarkClient;
-class BookmarkNode;
-struct BookmarkMatch;
-
-// BookmarkIndex maintains an index of the titles and URLs of bookmarks for
-// quick look up. BookmarkIndex is owned and maintained by BookmarkModel, you
-// shouldn't need to interact directly with BookmarkIndex.
-//
-// BookmarkIndex maintains the index (index_) as a map of sets. The map (type
-// Index) maps from a lower case string to the set (type NodeSet) of
-// BookmarkNodes that contain that string in their title or URL.
-class BookmarkIndex {
- public:
- BookmarkIndex(BookmarkClient* client);
- ~BookmarkIndex();
-
- // Invoked when a bookmark has been added to the model.
- void Add(const BookmarkNode* node);
-
- // Invoked when a bookmark has been removed from the model.
- void Remove(const BookmarkNode* node);
-
- // Returns up to |max_count| of bookmarks containing each term from the text
- // |query| in either the title or the URL.
- void GetBookmarksMatching(const base::string16& query,
- size_t max_count,
- query_parser::MatchingAlgorithm matching_algorithm,
- std::vector<BookmarkMatch>* results);
-
- private:
- typedef std::vector<const BookmarkNode*> Nodes;
- typedef std::set<const BookmarkNode*> NodeSet;
- typedef std::map<base::string16, NodeSet> Index;
-
- // Constructs |sorted_nodes| by taking the matches in |matches| and sorting
- // them in decreasing order of typed count (if supported by the client) and
- // deduping them.
- void SortMatches(const NodeSet& matches, Nodes* sorted_nodes) const;
-
- // Add |node| to |results| if the node matches the query.
- void AddMatchToResults(const BookmarkNode* node,
- query_parser::QueryParser* parser,
- const query_parser::QueryNodeVector& query_nodes,
- std::vector<BookmarkMatch>* results);
-
- // Populates |matches| for the specified term. If |first_term| is true, this
- // is the first term in the query. Returns true if there is at least one node
- // matching the term.
- bool GetBookmarksMatchingTerm(
- const base::string16& term,
- bool first_term,
- query_parser::MatchingAlgorithm matching_algorithm,
- NodeSet* matches);
-
- // Returns the set of query words from |query|.
- std::vector<base::string16> ExtractQueryWords(const base::string16& query);
-
- // Adds |node| to |index_|.
- void RegisterNode(const base::string16& term, const BookmarkNode* node);
-
- // Removes |node| from |index_|.
- void UnregisterNode(const base::string16& term, const BookmarkNode* node);
-
- Index index_;
-
- BookmarkClient* const client_;
-
- DISALLOW_COPY_AND_ASSIGN(BookmarkIndex);
-};
-
-} // namespace bookmarks
-
-#endif // COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_INDEX_H_
diff --git a/chromium/components/bookmarks/browser/bookmark_index_unittest.cc b/chromium/components/bookmarks/browser/bookmark_index_unittest.cc
index 3e23cc38080..5e80f391bbe 100644
--- a/chromium/components/bookmarks/browser/bookmark_index_unittest.cc
+++ b/chromium/components/bookmarks/browser/bookmark_index_unittest.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/bookmarks/browser/bookmark_index.h"
-
#include <stddef.h>
#include <string>
@@ -15,8 +13,8 @@
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "components/bookmarks/browser/bookmark_match.h"
#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/titled_url_match.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/bookmarks/test/test_bookmark_client.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -34,19 +32,19 @@ class BookmarkClientMock : public TestBookmarkClient {
BookmarkClientMock(const std::map<GURL, int>& typed_count_map)
: typed_count_map_(typed_count_map) {}
- bool SupportsTypedCountForNodes() override { return true; }
+ bool SupportsTypedCountForUrls() override { return true; }
+
+ void GetTypedCountForUrls(UrlTypedCountMap* url_typed_count_map) override {
+ for (auto& url_typed_count_pair : *url_typed_count_map) {
+ const GURL* url = url_typed_count_pair.first;
+ if (!url)
+ continue;
- void GetTypedCountForNodes(
- const NodeSet& nodes,
- NodeTypedCountPairs* node_typed_count_pairs) override {
- for (NodeSet::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
- const BookmarkNode* node = *it;
- std::map<GURL, int>::const_iterator found =
- typed_count_map_.find(node->url());
+ auto found = typed_count_map_.find(*url);
if (found == typed_count_map_.end())
continue;
- node_typed_count_pairs->push_back(std::make_pair(node, found->second));
+ url_typed_count_pair.second = found->second;
}
}
@@ -93,14 +91,15 @@ class BookmarkIndexTest : public testing::Test {
void ExpectMatches(const std::string& query,
query_parser::MatchingAlgorithm matching_algorithm,
const std::vector<std::string>& expected_titles) {
- std::vector<BookmarkMatch> matches;
+ std::vector<TitledUrlMatch> matches;
model_->GetBookmarksMatching(ASCIIToUTF16(query), 1000, matching_algorithm,
&matches);
ASSERT_EQ(expected_titles.size(), matches.size());
for (size_t i = 0; i < expected_titles.size(); ++i) {
bool found = false;
for (size_t j = 0; j < matches.size(); ++j) {
- if (ASCIIToUTF16(expected_titles[i]) == matches[j].node->GetTitle()) {
+ const base::string16& title = matches[j].node->GetTitledUrlNodeTitle();
+ if (ASCIIToUTF16(expected_titles[i]) == title) {
matches.erase(matches.begin() + j);
found = true;
break;
@@ -111,14 +110,14 @@ class BookmarkIndexTest : public testing::Test {
}
void ExtractMatchPositions(const std::string& string,
- BookmarkMatch::MatchPositions* matches) {
+ TitledUrlMatch::MatchPositions* matches) {
for (const base::StringPiece& match :
base::SplitStringPiece(string, ":",
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
std::vector<base::StringPiece> chunks = base::SplitStringPiece(
match, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
ASSERT_EQ(2U, chunks.size());
- matches->push_back(BookmarkMatch::MatchPosition());
+ matches->push_back(TitledUrlMatch::MatchPosition());
int chunks0, chunks1;
EXPECT_TRUE(base::StringToInt(chunks[0], &chunks0));
EXPECT_TRUE(base::StringToInt(chunks[1], &chunks1));
@@ -128,8 +127,8 @@ class BookmarkIndexTest : public testing::Test {
}
void ExpectMatchPositions(
- const BookmarkMatch::MatchPositions& actual_positions,
- const BookmarkMatch::MatchPositions& expected_positions) {
+ const TitledUrlMatch::MatchPositions& actual_positions,
+ const TitledUrlMatch::MatchPositions& expected_positions) {
ASSERT_EQ(expected_positions.size(), actual_positions.size());
for (size_t i = 0; i < expected_positions.size(); ++i) {
EXPECT_EQ(expected_positions[i].first, actual_positions[i].first);
@@ -358,7 +357,7 @@ TEST_F(BookmarkIndexTest, Normalization) {
GURL url(kAboutBlankURL);
for (size_t i = 0; i < arraysize(data); ++i) {
model_->AddURL(model_->other_node(), 0, UTF8ToUTF16(data[i].title), url);
- std::vector<BookmarkMatch> matches;
+ std::vector<TitledUrlMatch> matches;
model_->GetBookmarksMatching(UTF8ToUTF16(data[i].query), 10, &matches);
EXPECT_EQ(1u, matches.size());
model_ = TestBookmarkClient::CreateModel();
@@ -388,11 +387,11 @@ TEST_F(BookmarkIndexTest, MatchPositionsTitles) {
bookmarks.push_back(bookmark);
AddBookmarks(bookmarks);
- std::vector<BookmarkMatch> matches;
+ std::vector<TitledUrlMatch> matches;
model_->GetBookmarksMatching(ASCIIToUTF16(data[i].query), 1000, &matches);
ASSERT_EQ(1U, matches.size());
- BookmarkMatch::MatchPositions expected_title_matches;
+ TitledUrlMatch::MatchPositions expected_title_matches;
ExtractMatchPositions(data[i].expected_title_match_positions,
&expected_title_matches);
ExpectMatchPositions(matches[0].title_match_positions,
@@ -440,11 +439,11 @@ TEST_F(BookmarkIndexTest, MatchPositionsURLs) {
bookmarks.push_back(bookmark);
AddBookmarks(bookmarks);
- std::vector<BookmarkMatch> matches;
+ std::vector<TitledUrlMatch> matches;
model_->GetBookmarksMatching(UTF8ToUTF16(data[i].query), 1000, &matches);
ASSERT_EQ(1U, matches.size()) << data[i].url << data[i].query;
- BookmarkMatch::MatchPositions expected_url_matches;
+ TitledUrlMatch::MatchPositions expected_url_matches;
ExtractMatchPositions(data[i].expected_url_match_positions,
&expected_url_matches);
ExpectMatchPositions(matches[0].url_match_positions, expected_url_matches);
@@ -492,7 +491,7 @@ TEST_F(BookmarkIndexTest, HonorMax) {
const char* urls[] = {kAboutBlankURL, kAboutBlankURL};
AddBookmarks(titles, urls, arraysize(titles));
- std::vector<BookmarkMatch> matches;
+ std::vector<TitledUrlMatch> matches;
model_->GetBookmarksMatching(ASCIIToUTF16("ABc"), 1, &matches);
EXPECT_EQ(1U, matches.size());
}
@@ -504,7 +503,7 @@ TEST_F(BookmarkIndexTest, EmptyMatchOnMultiwideLowercaseString) {
base::WideToUTF16(L"\u0130 i"),
GURL("http://www.google.com"));
- std::vector<BookmarkMatch> matches;
+ std::vector<TitledUrlMatch> matches;
model_->GetBookmarksMatching(ASCIIToUTF16("i"), 100, &matches);
ASSERT_EQ(1U, matches.size());
EXPECT_EQ(n1, matches[0].node);
@@ -532,12 +531,12 @@ TEST_F(BookmarkIndexTest, GetResultsSortedByTypedCount) {
base::MakeUnique<BookmarkClientMock>(typed_count_map));
for (size_t i = 0; i < arraysize(data); ++i)
- // Populate the BookmarkIndex.
+ // Populate the bookmark index.
model->AddURL(
model->other_node(), i, UTF8ToUTF16(data[i].title), data[i].url);
// Populate match nodes.
- std::vector<BookmarkMatch> matches;
+ std::vector<TitledUrlMatch> matches;
model->GetBookmarksMatching(ASCIIToUTF16("google"), 4, &matches);
// The resulting order should be:
@@ -546,18 +545,18 @@ TEST_F(BookmarkIndexTest, GetResultsSortedByTypedCount) {
// 3. Google Docs (docs.google.com) 50
// 4. Google Maps (maps.google.com) 40
ASSERT_EQ(4U, matches.size());
- EXPECT_EQ(data[0].url, matches[0].node->url());
- EXPECT_EQ(data[3].url, matches[1].node->url());
- EXPECT_EQ(data[2].url, matches[2].node->url());
- EXPECT_EQ(data[1].url, matches[3].node->url());
+ EXPECT_EQ(data[0].url, matches[0].node->GetTitledUrlNodeUrl());
+ EXPECT_EQ(data[3].url, matches[1].node->GetTitledUrlNodeUrl());
+ EXPECT_EQ(data[2].url, matches[2].node->GetTitledUrlNodeUrl());
+ EXPECT_EQ(data[1].url, matches[3].node->GetTitledUrlNodeUrl());
matches.clear();
// Select top two matches.
model->GetBookmarksMatching(ASCIIToUTF16("google"), 2, &matches);
ASSERT_EQ(2U, matches.size());
- EXPECT_EQ(data[0].url, matches[0].node->url());
- EXPECT_EQ(data[3].url, matches[1].node->url());
+ EXPECT_EQ(data[0].url, matches[0].node->GetTitledUrlNodeUrl());
+ EXPECT_EQ(data[3].url, matches[1].node->GetTitledUrlNodeUrl());
}
} // namespace
diff --git a/chromium/components/bookmarks/browser/bookmark_model.cc b/chromium/components/bookmarks/browser/bookmark_model.cc
index a399809ceac..8639f752f30 100644
--- a/chromium/components/bookmarks/browser/bookmark_model.cc
+++ b/chromium/components/bookmarks/browser/bookmark_model.cc
@@ -18,13 +18,14 @@
#include "base/profiler/scoped_tracker.h"
#include "base/strings/string_util.h"
#include "components/bookmarks/browser/bookmark_expanded_state_tracker.h"
-#include "components/bookmarks/browser/bookmark_index.h"
-#include "components/bookmarks/browser/bookmark_match.h"
#include "components/bookmarks/browser/bookmark_model_observer.h"
#include "components/bookmarks/browser/bookmark_node_data.h"
#include "components/bookmarks/browser/bookmark_storage.h"
#include "components/bookmarks/browser/bookmark_undo_delegate.h"
#include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/bookmarks/browser/titled_url_index.h"
+#include "components/bookmarks/browser/titled_url_match.h"
+#include "components/bookmarks/browser/typed_count_sorter.h"
#include "components/favicon_base/favicon_types.h"
#include "grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
@@ -717,7 +718,7 @@ void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
void BookmarkModel::GetBookmarksMatching(const base::string16& text,
size_t max_count,
- std::vector<BookmarkMatch>* matches) {
+ std::vector<TitledUrlMatch>* matches) {
GetBookmarksMatching(text, max_count,
query_parser::MatchingAlgorithm::DEFAULT, matches);
}
@@ -726,11 +727,11 @@ void BookmarkModel::GetBookmarksMatching(
const base::string16& text,
size_t max_count,
query_parser::MatchingAlgorithm matching_algorithm,
- std::vector<BookmarkMatch>* matches) {
+ std::vector<TitledUrlMatch>* matches) {
if (!loaded_)
return;
- index_->GetBookmarksMatching(text, max_count, matching_algorithm, matches);
+ index_->GetResultsMatching(text, max_count, matching_algorithm, matches);
}
void BookmarkModel::ClearStore() {
@@ -1111,9 +1112,11 @@ std::unique_ptr<BookmarkLoadDetails> BookmarkModel::CreateLoadDetails() {
CreatePermanentNode(BookmarkNode::OTHER_NODE);
BookmarkPermanentNode* mobile_node =
CreatePermanentNode(BookmarkNode::MOBILE);
+ std::unique_ptr<TitledUrlNodeSorter> node_sorter =
+ base::MakeUnique<TypedCountSorter>(client_.get());
return std::unique_ptr<BookmarkLoadDetails>(new BookmarkLoadDetails(
bb_node, other_node, mobile_node, client_->GetLoadExtraNodesCallback(),
- new BookmarkIndex(client_.get()), next_node_id_));
+ new TitledUrlIndex(std::move(node_sorter)), next_node_id_));
}
void BookmarkModel::SetUndoDelegate(BookmarkUndoDelegate* undo_delegate) {
diff --git a/chromium/components/bookmarks/browser/bookmark_model.h b/chromium/components/bookmarks/browser/bookmark_model.h
index 309e806a202..2a52eb1e2db 100644
--- a/chromium/components/bookmarks/browser/bookmark_model.h
+++ b/chromium/components/bookmarks/browser/bookmark_model.h
@@ -46,14 +46,14 @@ namespace bookmarks {
class BookmarkCodecTest;
class BookmarkExpandedStateTracker;
-class BookmarkIndex;
class BookmarkLoadDetails;
class BookmarkModelObserver;
class BookmarkStorage;
class BookmarkUndoDelegate;
class ScopedGroupBookmarkActions;
class TestBookmarkClient;
-struct BookmarkMatch;
+class TitledUrlIndex;
+struct TitledUrlMatch;
// BookmarkModel --------------------------------------------------------------
@@ -243,14 +243,14 @@ class BookmarkModel : public BookmarkUndoProvider,
// in either the title or the URL. It uses default matching algorithm.
void GetBookmarksMatching(const base::string16& text,
size_t max_count,
- std::vector<BookmarkMatch>* matches);
+ std::vector<TitledUrlMatch>* matches);
// Returns up to |max_count| of bookmarks containing each term from |text|
// in either the title or the URL.
void GetBookmarksMatching(const base::string16& text,
size_t max_count,
query_parser::MatchingAlgorithm matching_algorithm,
- std::vector<BookmarkMatch>* matches);
+ std::vector<TitledUrlMatch>* matches);
// Sets the store to NULL, making it so the BookmarkModel does not persist
// any changes to disk. This is only useful during testing to speed up
@@ -450,7 +450,7 @@ class BookmarkModel : public BookmarkUndoProvider,
// Reads/writes bookmarks to disk.
std::unique_ptr<BookmarkStorage> store_;
- std::unique_ptr<BookmarkIndex> index_;
+ std::unique_ptr<TitledUrlIndex> index_;
base::WaitableEvent loaded_signal_;
diff --git a/chromium/components/bookmarks/browser/bookmark_node.cc b/chromium/components/bookmarks/browser/bookmark_node.cc
index 9574cf1ea5b..8bc1c292318 100644
--- a/chromium/components/bookmarks/browser/bookmark_node.cc
+++ b/chromium/components/bookmarks/browser/bookmark_node.cc
@@ -103,6 +103,14 @@ const BookmarkNode::MetaInfoMap* BookmarkNode::GetMetaInfoMap() const {
return meta_info_map_.get();
}
+const base::string16& BookmarkNode::GetTitledUrlNodeTitle() const {
+ return GetTitle();
+}
+
+const GURL& BookmarkNode::GetTitledUrlNodeUrl() const {
+ return url_;
+}
+
void BookmarkNode::Initialize(int64_t id) {
id_ = id;
type_ = url_.is_empty() ? FOLDER : URL;
diff --git a/chromium/components/bookmarks/browser/bookmark_node.h b/chromium/components/bookmarks/browser/bookmark_node.h
index 84cea2b07d1..d125c44e52c 100644
--- a/chromium/components/bookmarks/browser/bookmark_node.h
+++ b/chromium/components/bookmarks/browser/bookmark_node.h
@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/time/time.h"
+#include "components/bookmarks/browser/titled_url_node.h"
#include "components/favicon_base/favicon_types.h"
#include "ui/base/models/tree_node_model.h"
#include "ui/gfx/image/image.h"
@@ -25,7 +26,7 @@ class BookmarkModel;
// BookmarkNode contains information about a starred entry: title, URL, favicon,
// id and type. BookmarkNodes are returned from BookmarkModel.
-class BookmarkNode : public ui::TreeNode<BookmarkNode> {
+class BookmarkNode : public ui::TreeNode<BookmarkNode>, public TitledUrlNode {
public:
enum Type {
URL,
@@ -115,6 +116,10 @@ class BookmarkNode : public ui::TreeNode<BookmarkNode> {
}
int64_t sync_transaction_version() const { return sync_transaction_version_; }
+ // TitledUrlNode interface methods.
+ const base::string16& GetTitledUrlNodeTitle() const override;
+ const GURL& GetTitledUrlNodeUrl() const override;
+
// TODO(sky): Consider adding last visit time here, it'll greatly simplify
// HistoryContentsProvider.
diff --git a/chromium/components/bookmarks/browser/bookmark_storage.cc b/chromium/components/bookmarks/browser/bookmark_storage.cc
index a71c3e3d0db..d32a90c8806 100644
--- a/chromium/components/bookmarks/browser/bookmark_storage.cc
+++ b/chromium/components/bookmarks/browser/bookmark_storage.cc
@@ -18,8 +18,8 @@
#include "base/sequenced_task_runner.h"
#include "base/time/time.h"
#include "components/bookmarks/browser/bookmark_codec.h"
-#include "components/bookmarks/browser/bookmark_index.h"
#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/titled_url_index.h"
#include "components/bookmarks/common/bookmark_constants.h"
using base::TimeTicks;
@@ -124,7 +124,7 @@ BookmarkLoadDetails::BookmarkLoadDetails(
BookmarkPermanentNode* other_folder_node,
BookmarkPermanentNode* mobile_folder_node,
const LoadExtraCallback& load_extra_callback,
- BookmarkIndex* index,
+ TitledUrlIndex* index,
int64_t max_id)
: bb_node_(bb_node),
other_folder_node_(other_folder_node),
diff --git a/chromium/components/bookmarks/browser/bookmark_storage.h b/chromium/components/bookmarks/browser/bookmark_storage.h
index 2496e42c34f..6f86188d3fc 100644
--- a/chromium/components/bookmarks/browser/bookmark_storage.h
+++ b/chromium/components/bookmarks/browser/bookmark_storage.h
@@ -17,8 +17,8 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "components/bookmarks/browser/bookmark_index.h"
#include "components/bookmarks/browser/bookmark_node.h"
+#include "components/bookmarks/browser/titled_url_index.h"
namespace base {
class SequencedTaskRunner;
@@ -51,7 +51,7 @@ class BookmarkLoadDetails {
BookmarkPermanentNode* other_folder_node,
BookmarkPermanentNode* mobile_folder_node,
const LoadExtraCallback& load_extra_callback,
- BookmarkIndex* index,
+ TitledUrlIndex* index,
int64_t max_id);
~BookmarkLoadDetails();
@@ -79,8 +79,8 @@ class BookmarkLoadDetails {
BookmarkPermanentNodeList owned_extra_nodes() {
return std::move(extra_nodes_);
}
- BookmarkIndex* index() { return index_.get(); }
- std::unique_ptr<BookmarkIndex> owned_index() { return std::move(index_); }
+ TitledUrlIndex* index() { return index_.get(); }
+ std::unique_ptr<TitledUrlIndex> owned_index() { return std::move(index_); }
const BookmarkNode::MetaInfoMap& model_meta_info_map() const {
return model_meta_info_map_;
@@ -125,7 +125,7 @@ class BookmarkLoadDetails {
std::unique_ptr<BookmarkPermanentNode> mobile_folder_node_;
LoadExtraCallback load_extra_callback_;
BookmarkPermanentNodeList extra_nodes_;
- std::unique_ptr<BookmarkIndex> index_;
+ std::unique_ptr<TitledUrlIndex> index_;
BookmarkNode::MetaInfoMap model_meta_info_map_;
int64_t model_sync_transaction_version_;
int64_t max_id_;
diff --git a/chromium/components/bookmarks/browser/bookmark_index.cc b/chromium/components/bookmarks/browser/titled_url_index.cc
index 9c34e5a2ca5..abde1aa9e54 100644
--- a/chromium/components/bookmarks/browser/bookmark_index.cc
+++ b/chromium/components/bookmarks/browser/titled_url_index.cc
@@ -2,33 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/bookmarks/browser/bookmark_index.h"
+#include "components/bookmarks/browser/titled_url_index.h"
#include <stdint.h>
-#include <algorithm>
-#include <functional>
-#include <iterator>
-#include <list>
-
#include "base/i18n/case_conversion.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/utf_offset_string_conversions.h"
#include "build/build_config.h"
-#include "components/bookmarks/browser/bookmark_client.h"
-#include "components/bookmarks/browser/bookmark_match.h"
-#include "components/bookmarks/browser/bookmark_node.h"
#include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/bookmarks/browser/titled_url_match.h"
+#include "components/bookmarks/browser/titled_url_node.h"
+#include "components/bookmarks/browser/titled_url_node_sorter.h"
#include "components/query_parser/snippet.h"
#include "third_party/icu/source/common/unicode/normalizer2.h"
#include "third_party/icu/source/common/unicode/utypes.h"
namespace bookmarks {
-typedef BookmarkClient::NodeTypedCountPair NodeTypedCountPair;
-typedef BookmarkClient::NodeTypedCountPairs NodeTypedCountPairs;
-
namespace {
// Returns a normalized version of the UTF16 string |text|. If it fails to
@@ -54,78 +46,56 @@ base::string16 Normalize(const base::string16& text) {
unicode_normalized_text.length());
}
-// Sort functor for NodeTypedCountPairs. We sort in decreasing order of typed
-// count so that the best matches will always be added to the results.
-struct NodeTypedCountPairSortFunctor {
- bool operator()(const NodeTypedCountPair& a,
- const NodeTypedCountPair& b) const {
- return a.second > b.second;
- }
-};
-
-// Extract the const Node* stored in a BookmarkClient::NodeTypedCountPair.
-struct NodeTypedCountPairExtractNodeFunctor {
- const BookmarkNode* operator()(const NodeTypedCountPair& pair) const {
- return pair.first;
- }
-};
-
} // namespace
-BookmarkIndex::BookmarkIndex(BookmarkClient* client)
- : client_(client) {
- DCHECK(client_);
+TitledUrlIndex::TitledUrlIndex(std::unique_ptr<TitledUrlNodeSorter> sorter)
+ : sorter_(std::move(sorter)) {
}
-BookmarkIndex::~BookmarkIndex() {
+TitledUrlIndex::~TitledUrlIndex() {
}
-void BookmarkIndex::Add(const BookmarkNode* node) {
- if (!node->is_url())
- return;
+void TitledUrlIndex::Add(const TitledUrlNode* node) {
std::vector<base::string16> terms =
- ExtractQueryWords(Normalize(node->GetTitle()));
+ ExtractQueryWords(Normalize(node->GetTitledUrlNodeTitle()));
for (size_t i = 0; i < terms.size(); ++i)
RegisterNode(terms[i], node);
- terms =
- ExtractQueryWords(CleanUpUrlForMatching(node->url(), nullptr));
+ terms = ExtractQueryWords(
+ CleanUpUrlForMatching(node->GetTitledUrlNodeUrl(), nullptr));
for (size_t i = 0; i < terms.size(); ++i)
RegisterNode(terms[i], node);
}
-void BookmarkIndex::Remove(const BookmarkNode* node) {
- if (!node->is_url())
- return;
-
+void TitledUrlIndex::Remove(const TitledUrlNode* node) {
std::vector<base::string16> terms =
- ExtractQueryWords(Normalize(node->GetTitle()));
+ ExtractQueryWords(Normalize(node->GetTitledUrlNodeTitle()));
for (size_t i = 0; i < terms.size(); ++i)
UnregisterNode(terms[i], node);
- terms =
- ExtractQueryWords(CleanUpUrlForMatching(node->url(), nullptr));
+ terms = ExtractQueryWords(
+ CleanUpUrlForMatching(node->GetTitledUrlNodeUrl(), nullptr));
for (size_t i = 0; i < terms.size(); ++i)
UnregisterNode(terms[i], node);
}
-void BookmarkIndex::GetBookmarksMatching(
+void TitledUrlIndex::GetResultsMatching(
const base::string16& input_query,
size_t max_count,
query_parser::MatchingAlgorithm matching_algorithm,
- std::vector<BookmarkMatch>* results) {
+ std::vector<TitledUrlMatch>* results) {
const base::string16 query = Normalize(input_query);
std::vector<base::string16> terms = ExtractQueryWords(query);
if (terms.empty())
return;
- NodeSet matches;
+ TitledUrlNodeSet matches;
for (size_t i = 0; i < terms.size(); ++i) {
- if (!GetBookmarksMatchingTerm(
- terms[i], i == 0, matching_algorithm, &matches)) {
+ if (!GetResultsMatchingTerm(terms[i], i == 0, matching_algorithm,
+ &matches)) {
return;
}
}
- Nodes sorted_nodes;
+ TitledUrlNodes sorted_nodes;
SortMatches(matches, &sorted_nodes);
// We use a QueryParser to fill in match positions for us. It's not the most
@@ -140,47 +110,41 @@ void BookmarkIndex::GetBookmarksMatching(
// that calculates result relevance in HistoryContentsProvider::ConvertResults
// will run backwards to assure higher relevance will be attributed to the
// best matches.
- for (Nodes::const_iterator i = sorted_nodes.begin();
+ for (TitledUrlNodes::const_iterator i = sorted_nodes.begin();
i != sorted_nodes.end() && results->size() < max_count;
++i)
AddMatchToResults(*i, &parser, query_nodes, results);
}
-void BookmarkIndex::SortMatches(const NodeSet& matches,
- Nodes* sorted_nodes) const {
- sorted_nodes->reserve(matches.size());
- if (client_->SupportsTypedCountForNodes()) {
- NodeTypedCountPairs node_typed_counts;
- client_->GetTypedCountForNodes(matches, &node_typed_counts);
- std::sort(node_typed_counts.begin(),
- node_typed_counts.end(),
- NodeTypedCountPairSortFunctor());
- std::transform(node_typed_counts.begin(),
- node_typed_counts.end(),
- std::back_inserter(*sorted_nodes),
- NodeTypedCountPairExtractNodeFunctor());
+void TitledUrlIndex::SortMatches(const TitledUrlNodeSet& matches,
+ TitledUrlNodes* sorted_nodes) const {
+ if (sorter_) {
+ sorter_->SortMatches(matches, sorted_nodes);
} else {
sorted_nodes->insert(sorted_nodes->end(), matches.begin(), matches.end());
}
}
-void BookmarkIndex::AddMatchToResults(
- const BookmarkNode* node,
+void TitledUrlIndex::AddMatchToResults(
+ const TitledUrlNode* node,
query_parser::QueryParser* parser,
const query_parser::QueryNodeVector& query_nodes,
- std::vector<BookmarkMatch>* results) {
+ std::vector<TitledUrlMatch>* results) {
+ if (!node) {
+ return;
+ }
// Check that the result matches the query. The previous search
// was a simple per-word search, while the more complex matching
// of QueryParser may filter it out. For example, the query
- // ["thi"] will match the bookmark titled [Thinking], but since
+ // ["thi"] will match the title [Thinking], but since
// ["thi"] is quoted we don't want to do a prefix match.
query_parser::QueryWordVector title_words, url_words;
const base::string16 lower_title =
- base::i18n::ToLower(Normalize(node->GetTitle()));
+ base::i18n::ToLower(Normalize(node->GetTitledUrlNodeTitle()));
parser->ExtractQueryWords(lower_title, &title_words);
base::OffsetAdjuster::Adjustments adjustments;
parser->ExtractQueryWords(
- CleanUpUrlForMatching(node->url(), &adjustments),
+ CleanUpUrlForMatching(node->GetTitledUrlNodeUrl(), &adjustments),
&url_words);
query_parser::Snippet::MatchPositions title_matches, url_matches;
for (const auto& node : query_nodes) {
@@ -192,8 +156,8 @@ void BookmarkIndex::AddMatchToResults(
query_parser::QueryParser::SortAndCoalesceMatchPositions(&title_matches);
query_parser::QueryParser::SortAndCoalesceMatchPositions(&url_matches);
}
- BookmarkMatch match;
- if (lower_title.length() == node->GetTitle().length()) {
+ TitledUrlMatch match;
+ if (lower_title.length() == node->GetTitledUrlNodeTitle().length()) {
// Only use title matches if the lowercase string is the same length
// as the original string, otherwise the matches are meaningless.
// TODO(mpearson): revise match positions appropriately.
@@ -203,20 +167,20 @@ void BookmarkIndex::AddMatchToResults(
// matches in |url_matches| so they point to offsets in the original URL
// spec, not the cleaned-up URL string that we used for matching.
std::vector<size_t> offsets =
- BookmarkMatch::OffsetsFromMatchPositions(url_matches);
+ TitledUrlMatch::OffsetsFromMatchPositions(url_matches);
base::OffsetAdjuster::UnadjustOffsets(adjustments, &offsets);
url_matches =
- BookmarkMatch::ReplaceOffsetsInMatchPositions(url_matches, offsets);
+ TitledUrlMatch::ReplaceOffsetsInMatchPositions(url_matches, offsets);
match.url_match_positions.swap(url_matches);
match.node = node;
results->push_back(match);
}
-bool BookmarkIndex::GetBookmarksMatchingTerm(
+bool TitledUrlIndex::GetResultsMatchingTerm(
const base::string16& term,
bool first_term,
query_parser::MatchingAlgorithm matching_algorithm,
- NodeSet* matches) {
+ TitledUrlNodeSet* matches) {
Index::const_iterator i = index_.lower_bound(term);
if (i == index_.end())
return false;
@@ -225,20 +189,21 @@ bool BookmarkIndex::GetBookmarksMatchingTerm(
term, matching_algorithm)) {
// Term is too short for prefix match, compare using exact match.
if (i->first != term)
- return false; // No bookmarks with this term.
+ return false; // No title/URL pairs with this term.
if (first_term) {
(*matches) = i->second;
return true;
}
- *matches = base::STLSetIntersection<NodeSet>(i->second, *matches);
+ *matches = base::STLSetIntersection<TitledUrlNodeSet>(i->second, *matches);
} else {
// Loop through index adding all entries that start with term to
// |prefix_matches|.
- NodeSet tmp_prefix_matches;
+ TitledUrlNodeSet tmp_prefix_matches;
// If this is the first term, then store the result directly in |matches|
// to avoid calling stl intersection (which requires a copy).
- NodeSet* prefix_matches = first_term ? matches : &tmp_prefix_matches;
+ TitledUrlNodeSet* prefix_matches =
+ first_term ? matches : &tmp_prefix_matches;
while (i != index_.end() &&
i->first.size() >= term.size() &&
term.compare(0, term.size(), i->first, 0, term.size()) == 0) {
@@ -247,19 +212,22 @@ bool BookmarkIndex::GetBookmarksMatchingTerm(
#else
// Work around a bug in the implementation of std::set::insert in the STL
// used on android (http://crbug.com/367050).
- for (NodeSet::const_iterator n = i->second.begin(); n != i->second.end();
+ for (TitledUrlNodeSet::const_iterator n = i->second.begin();
+ n != i->second.end();
++n)
prefix_matches->insert(prefix_matches->end(), *n);
#endif
++i;
}
- if (!first_term)
- *matches = base::STLSetIntersection<NodeSet>(*prefix_matches, *matches);
+ if (!first_term) {
+ *matches =
+ base::STLSetIntersection<TitledUrlNodeSet>(*prefix_matches, *matches);
+ }
}
return !matches->empty();
}
-std::vector<base::string16> BookmarkIndex::ExtractQueryWords(
+std::vector<base::string16> TitledUrlIndex::ExtractQueryWords(
const base::string16& query) {
std::vector<base::string16> terms;
if (query.empty())
@@ -271,17 +239,17 @@ std::vector<base::string16> BookmarkIndex::ExtractQueryWords(
return terms;
}
-void BookmarkIndex::RegisterNode(const base::string16& term,
- const BookmarkNode* node) {
+void TitledUrlIndex::RegisterNode(const base::string16& term,
+ const TitledUrlNode* node) {
index_[term].insert(node);
}
-void BookmarkIndex::UnregisterNode(const base::string16& term,
- const BookmarkNode* node) {
+void TitledUrlIndex::UnregisterNode(const base::string16& term,
+ const TitledUrlNode* node) {
Index::iterator i = index_.find(term);
if (i == index_.end()) {
// We can get here if the node has the same term more than once. For
- // example, a bookmark with the title 'foo foo' would end up here.
+ // example, a node with the title 'foo foo' would end up here.
return;
}
i->second.erase(node);
diff --git a/chromium/components/bookmarks/browser/titled_url_index.h b/chromium/components/bookmarks/browser/titled_url_index.h
new file mode 100644
index 00000000000..d6a0fbae8d3
--- /dev/null
+++ b/chromium/components/bookmarks/browser/titled_url_index.h
@@ -0,0 +1,94 @@
+// 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_BOOKMARKS_BROWSER_TITLED_URL_INDEX_H_
+#define COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_INDEX_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/query_parser/query_parser.h"
+
+namespace bookmarks {
+
+class TitledUrlNode;
+class TitledUrlNodeSorter;
+struct TitledUrlMatch;
+
+// TitledUrlIndex maintains an index of paired titles and URLs for quick lookup.
+//
+// TitledUrlIndex maintains the index (index_) as a map of sets. The map (type
+// Index) maps from a lower case string to the set (type TitledUrlNodeSet) of
+// TitledUrlNodes that contain that string in their title or URL.
+class TitledUrlIndex {
+ public:
+ // Constructs a TitledUrlIndex. |sorter| is used to construct a sorted list
+ // of matches when matches are returned from the index. If null, matches are
+ // returned unsorted.
+ TitledUrlIndex(std::unique_ptr<TitledUrlNodeSorter> sorter);
+ ~TitledUrlIndex();
+
+ // Invoked when a title/URL pair has been added to the model.
+ void Add(const TitledUrlNode* node);
+
+ // Invoked when a title/URL pair has been removed from the model.
+ void Remove(const TitledUrlNode* node);
+
+ // Returns up to |max_count| of matches containing each term from the text
+ // |query| in either the title or the URL.
+ void GetResultsMatching(const base::string16& query,
+ size_t max_count,
+ query_parser::MatchingAlgorithm matching_algorithm,
+ std::vector<TitledUrlMatch>* results);
+
+ private:
+ using TitledUrlNodes = std::vector<const TitledUrlNode*>;
+ using TitledUrlNodeSet = std::set<const TitledUrlNode*>;
+ using Index = std::map<base::string16, TitledUrlNodeSet>;
+
+ // Constructs |sorted_nodes| by copying the matches in |matches| and sorting
+ // them.
+ void SortMatches(const TitledUrlNodeSet& matches,
+ TitledUrlNodes* sorted_nodes) const;
+
+ // Add |node| to |results| if the node matches the query.
+ void AddMatchToResults(const TitledUrlNode* node,
+ query_parser::QueryParser* parser,
+ const query_parser::QueryNodeVector& query_nodes,
+ std::vector<TitledUrlMatch>* results);
+
+ // Populates |matches| for the specified term. If |first_term| is true, this
+ // is the first term in the query. Returns true if there is at least one node
+ // matching the term.
+ bool GetResultsMatchingTerm(
+ const base::string16& term,
+ bool first_term,
+ query_parser::MatchingAlgorithm matching_algorithm,
+ TitledUrlNodeSet* matches);
+
+ // Returns the set of query words from |query|.
+ std::vector<base::string16> ExtractQueryWords(const base::string16& query);
+
+ // Adds |node| to |index_|.
+ void RegisterNode(const base::string16& term, const TitledUrlNode* node);
+
+ // Removes |node| from |index_|.
+ void UnregisterNode(const base::string16& term, const TitledUrlNode* node);
+
+ Index index_;
+
+ std::unique_ptr<TitledUrlNodeSorter> sorter_;
+
+ DISALLOW_COPY_AND_ASSIGN(TitledUrlIndex);
+};
+
+} // namespace bookmarks
+
+#endif // COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_INDEX_H_
diff --git a/chromium/components/bookmarks/browser/bookmark_match.cc b/chromium/components/bookmarks/browser/titled_url_match.cc
index f813cbbbb7f..313ae4ef177 100644
--- a/chromium/components/bookmarks/browser/bookmark_match.cc
+++ b/chromium/components/bookmarks/browser/titled_url_match.cc
@@ -2,21 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/bookmarks/browser/bookmark_match.h"
+#include "components/bookmarks/browser/titled_url_match.h"
#include "base/logging.h"
#include "base/strings/string16.h"
namespace bookmarks {
-BookmarkMatch::BookmarkMatch() : node(NULL) {}
+TitledUrlMatch::TitledUrlMatch() : node(NULL) {}
-BookmarkMatch::BookmarkMatch(const BookmarkMatch& other) = default;
+TitledUrlMatch::TitledUrlMatch(const TitledUrlMatch& other) = default;
-BookmarkMatch::~BookmarkMatch() {}
+TitledUrlMatch::~TitledUrlMatch() {}
// static
-std::vector<size_t> BookmarkMatch::OffsetsFromMatchPositions(
+std::vector<size_t> TitledUrlMatch::OffsetsFromMatchPositions(
const MatchPositions& match_positions) {
std::vector<size_t> offsets;
for (MatchPositions::const_iterator i = match_positions.begin();
@@ -28,7 +28,7 @@ std::vector<size_t> BookmarkMatch::OffsetsFromMatchPositions(
}
// static
-BookmarkMatch::MatchPositions BookmarkMatch::ReplaceOffsetsInMatchPositions(
+TitledUrlMatch::MatchPositions TitledUrlMatch::ReplaceOffsetsInMatchPositions(
const MatchPositions& match_positions,
const std::vector<size_t>& offsets) {
DCHECK_EQ(2 * match_positions.size(), offsets.size());
diff --git a/chromium/components/bookmarks/browser/bookmark_match.h b/chromium/components/bookmarks/browser/titled_url_match.h
index ce658939ca8..767a1907d06 100644
--- a/chromium/components/bookmarks/browser/bookmark_match.h
+++ b/chromium/components/bookmarks/browser/titled_url_match.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_BOOKMARKS_BROWSER_BOOKMARK_TITLE_MATCH_H_
-#define COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_TITLE_MATCH_H_
+#ifndef COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_MATCH_H_
+#define COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_MATCH_H_
#include <stddef.h>
@@ -13,17 +13,17 @@
namespace bookmarks {
-class BookmarkNode;
+class TitledUrlNode;
-struct BookmarkMatch {
+struct TitledUrlMatch {
// Each MatchPosition is the [begin, end) positions of a match within a
// string.
- typedef std::pair<size_t, size_t> MatchPosition;
- typedef std::vector<MatchPosition> MatchPositions;
+ using MatchPosition = std::pair<size_t, size_t>;
+ using MatchPositions = std::vector<MatchPosition>;
- BookmarkMatch();
- BookmarkMatch(const BookmarkMatch& other);
- ~BookmarkMatch();
+ TitledUrlMatch();
+ TitledUrlMatch(const TitledUrlMatch& other);
+ ~TitledUrlMatch();
// Extracts and returns the offsets from |match_positions|.
static std::vector<size_t> OffsetsFromMatchPositions(
@@ -37,7 +37,7 @@ struct BookmarkMatch {
const std::vector<size_t>& offsets);
// The matching node of a query.
- const BookmarkNode* node;
+ const TitledUrlNode* node;
// Location of the matching words in the title of the node.
MatchPositions title_match_positions;
@@ -48,4 +48,4 @@ struct BookmarkMatch {
} // namespace bookmarks
-#endif // COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_TITLE_MATCH_H_
+#endif // COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_MATCH_H_
diff --git a/chromium/components/bookmarks/browser/titled_url_match_unittest.cc b/chromium/components/bookmarks/browser/titled_url_match_unittest.cc
new file mode 100644
index 00000000000..8ccd2da52a2
--- /dev/null
+++ b/chromium/components/bookmarks/browser/titled_url_match_unittest.cc
@@ -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.
+
+#include "components/bookmarks/browser/titled_url_match.h"
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace bookmarks {
+
+using MatchPositions = TitledUrlMatch::MatchPositions;
+
+TEST(TitledUrlMatchTest, EmptyOffsetsForEmptyMatchPositions) {
+ auto offsets = TitledUrlMatch::OffsetsFromMatchPositions(MatchPositions());
+ EXPECT_TRUE(offsets.empty());
+}
+
+TEST(TitledUrlMatchTest, OffsetsFromMatchPositions) {
+ MatchPositions match_positions = {{1, 3}, {4, 5}, {10, 15}};
+ std::vector<size_t> expected_offsets = {1, 3, 4, 5, 10, 15};
+ auto offsets = TitledUrlMatch::OffsetsFromMatchPositions(match_positions);
+ EXPECT_TRUE(
+ std::equal(offsets.begin(), offsets.end(), expected_offsets.begin()));
+}
+
+TEST(TitledUrlMatchTest, ReplaceOffsetsInEmptyMatchPositions) {
+ auto match_positions = TitledUrlMatch::ReplaceOffsetsInMatchPositions(
+ MatchPositions(), std::vector<size_t>());
+ EXPECT_TRUE(match_positions.empty());
+}
+
+TEST(TitledUrlMatchTest, ReplaceOffsetsInMatchPositions) {
+ MatchPositions orig_match_positions = {{1, 3}, {4, 5}, {10, 15}};
+ std::vector<size_t> offsets = {0, 2, 3, 4, 9, 14};
+ MatchPositions expected_match_positions = {{0, 2}, {3, 4}, {9, 14}};
+ auto match_positions = TitledUrlMatch::ReplaceOffsetsInMatchPositions(
+ orig_match_positions, offsets);
+ EXPECT_TRUE(std::equal(match_positions.begin(), match_positions.end(),
+ expected_match_positions.begin()));
+}
+
+TEST(TitledUrlMatchTest, ReplaceOffsetsRemovesItemsWithNposOffsets) {
+ MatchPositions orig_match_positions = {{1, 3}, {4, 5}, {10, 15}, {17, 20}};
+ std::vector<size_t> offsets = {0,
+ base::string16::npos,
+ base::string16::npos,
+ 4,
+ base::string16::npos,
+ base::string16::npos,
+ 17,
+ 20};
+ MatchPositions expected_match_positions = {{17, 20}};
+ auto match_positions = TitledUrlMatch::ReplaceOffsetsInMatchPositions(
+ orig_match_positions, offsets);
+ EXPECT_TRUE(std::equal(match_positions.begin(), match_positions.end(),
+ expected_match_positions.begin()));
+}
+
+}
diff --git a/chromium/components/bookmarks/browser/titled_url_node.h b/chromium/components/bookmarks/browser/titled_url_node.h
new file mode 100644
index 00000000000..bbd6759b770
--- /dev/null
+++ b/chromium/components/bookmarks/browser/titled_url_node.h
@@ -0,0 +1,29 @@
+// 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_BOOKMARKS_BROWSER_TITLED_URL_NODE_H_
+#define COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_NODE_H_
+
+#include "url/gurl.h"
+
+namespace bookmarks {
+
+// TitledUrlNode is an interface for objects like bookmarks that expose a title
+// and URL. TitledUrlNodes can be added to a BookmarkIndex to quickly retrieve
+// all nodes that contain a particular word in their title or URL.
+class TitledUrlNode {
+ public:
+ // Returns the title for the node.
+ virtual const base::string16& GetTitledUrlNodeTitle() const = 0;
+
+ // Returns the URL for the node.
+ virtual const GURL& GetTitledUrlNodeUrl() const = 0;
+
+ protected:
+ virtual ~TitledUrlNode() {}
+};
+
+} // namespace bookmarks
+
+#endif // COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_NODE_H_
diff --git a/chromium/components/bookmarks/browser/titled_url_node_sorter.h b/chromium/components/bookmarks/browser/titled_url_node_sorter.h
new file mode 100644
index 00000000000..3327b5980c6
--- /dev/null
+++ b/chromium/components/bookmarks/browser/titled_url_node_sorter.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_BOOKMARKS_BROWSER_TITLED_URL_NODE_SORTER_H_
+#define COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_NODE_SORTER_H_
+
+#include <set>
+#include <vector>
+
+namespace bookmarks {
+
+class TitledUrlNode;
+
+class TitledUrlNodeSorter {
+ public:
+ using TitledUrlNodes = std::vector<const TitledUrlNode*>;
+ using TitledUrlNodeSet = std::set<const TitledUrlNode*>;
+
+ virtual ~TitledUrlNodeSorter() {};
+
+ // Sorts |matches| in an implementation-specific way, placing the results in
+ // |sorted_nodes|.
+ virtual void SortMatches(const TitledUrlNodeSet& matches,
+ TitledUrlNodes* sorted_nodes) const = 0;
+};
+
+} // namespace bookmarks
+
+#endif // COMPONENTS_BOOKMARKS_BROWSER_TITLED_URL_NODE_SORTER_H_
diff --git a/chromium/components/bookmarks/browser/typed_count_sorter.cc b/chromium/components/bookmarks/browser/typed_count_sorter.cc
new file mode 100644
index 00000000000..311f13836c9
--- /dev/null
+++ b/chromium/components/bookmarks/browser/typed_count_sorter.cc
@@ -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.
+
+#include "components/bookmarks/browser/typed_count_sorter.h"
+
+#include "components/bookmarks/browser/bookmark_client.h"
+
+namespace bookmarks {
+
+using UrlTypedCountMap = BookmarkClient::UrlTypedCountMap;
+
+namespace {
+
+using UrlNodeMap = std::map<const GURL*, const TitledUrlNode*>;
+using UrlTypedCountPair = std::pair<const GURL*, int>;
+using UrlTypedCountPairs = std::vector<UrlTypedCountPair>;
+
+// Sort functor for UrlTypedCountPairs. We sort in decreasing order of typed
+// count so that the best matches will always be added to the results.
+struct UrlTypedCountPairSortFunctor {
+ bool operator()(const UrlTypedCountPair& a,
+ const UrlTypedCountPair& b) const {
+ return a.second > b.second;
+ }
+};
+
+// Extract the GURL stored in an UrlTypedCountPair and use it to look up the
+// corresponding TitledUrlNode.
+class UrlTypedCountPairNodeLookupFunctor {
+ public:
+ UrlTypedCountPairNodeLookupFunctor(UrlNodeMap& url_node_map)
+ : url_node_map_(url_node_map) {
+ }
+
+ const TitledUrlNode* operator()(const UrlTypedCountPair& pair) const {
+ return url_node_map_[pair.first];
+ }
+
+ private:
+ UrlNodeMap& url_node_map_;
+};
+
+} // namespace
+
+TypedCountSorter::TypedCountSorter(BookmarkClient* client)
+ : client_(client) {
+ DCHECK(client_);
+}
+
+TypedCountSorter::~TypedCountSorter() {}
+
+void TypedCountSorter::SortMatches(const TitledUrlNodeSet& matches,
+ TitledUrlNodes* sorted_nodes) const {
+ sorted_nodes->reserve(matches.size());
+ if (client_->SupportsTypedCountForUrls()) {
+ UrlNodeMap url_node_map;
+ UrlTypedCountMap url_typed_count_map;
+ for (auto node : matches) {
+ const GURL& url = node->GetTitledUrlNodeUrl();
+ url_node_map.insert(std::make_pair(&url, node));
+ url_typed_count_map.insert(std::make_pair(&url, 0));
+ }
+
+ client_->GetTypedCountForUrls(&url_typed_count_map);
+
+ UrlTypedCountPairs url_typed_counts;
+ std::copy(url_typed_count_map.begin(),
+ url_typed_count_map.end(),
+ std::back_inserter(url_typed_counts));
+ std::sort(url_typed_counts.begin(),
+ url_typed_counts.end(),
+ UrlTypedCountPairSortFunctor());
+ std::transform(url_typed_counts.begin(),
+ url_typed_counts.end(),
+ std::back_inserter(*sorted_nodes),
+ UrlTypedCountPairNodeLookupFunctor(url_node_map));
+ } else {
+ sorted_nodes->insert(sorted_nodes->end(), matches.begin(), matches.end());
+ }
+}
+
+} // namespace bookmarks
diff --git a/chromium/components/bookmarks/browser/typed_count_sorter.h b/chromium/components/bookmarks/browser/typed_count_sorter.h
new file mode 100644
index 00000000000..b62b782948a
--- /dev/null
+++ b/chromium/components/bookmarks/browser/typed_count_sorter.h
@@ -0,0 +1,35 @@
+// 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_BOOKMARKS_BROWSER_TYPED_COUNT_SORTER_H_
+#define COMPONENTS_BOOKMARKS_BROWSER_TYPED_COUNT_SORTER_H_
+
+#include "base/macros.h"
+#include "components/bookmarks/browser/titled_url_node_sorter.h"
+
+namespace bookmarks {
+
+class BookmarkClient;
+
+// A sort method for sorting a set of bookmarks in decreasing order according to
+// the number of times each bookmark's URL was typed into the omnibox. Duplicate
+// matches are removed.
+class TypedCountSorter : public TitledUrlNodeSorter {
+ public:
+ explicit TypedCountSorter(BookmarkClient* client);
+ ~TypedCountSorter() override;
+
+ // TitledUrlNodeSorter
+ void SortMatches(const TitledUrlNodeSet& matches,
+ TitledUrlNodes* sorted_nodes) const override;
+
+ private:
+ BookmarkClient* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(TypedCountSorter);
+};
+
+}
+
+#endif // COMPONENTS_BOOKMARKS_BROWSER_TYPED_COUNT_SORTER_H_
diff --git a/chromium/components/browser_sync/BUILD.gn b/chromium/components/browser_sync/BUILD.gn
index 1949c1cf71b..5b870f0a7ee 100644
--- a/chromium/components/browser_sync/BUILD.gn
+++ b/chromium/components/browser_sync/BUILD.gn
@@ -33,6 +33,8 @@ static_library("browser_sync") {
"//components/password_manager/sync/browser",
"//components/pref_registry",
"//components/prefs",
+ "//components/reading_list/core",
+ "//components/reading_list/core:reading_list_enable_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 14709a958de..83dd8c54333 100644
--- a/chromium/components/browser_sync/DEPS
+++ b/chromium/components/browser_sync/DEPS
@@ -11,6 +11,7 @@ include_rules = [
"+components/password_manager/sync/browser",
"+components/pref_registry",
"+components/prefs",
+ "+components/reading_list/core",
"+components/signin/core/browser",
"+components/signin/core/common",
"+components/sync",
diff --git a/chromium/components/browser_sync/abstract_profile_sync_service_test.cc b/chromium/components/browser_sync/abstract_profile_sync_service_test.cc
index 153962eb4c3..d0346007328 100644
--- a/chromium/components/browser_sync/abstract_profile_sync_service_test.cc
+++ b/chromium/components/browser_sync/abstract_profile_sync_service_test.cc
@@ -12,6 +12,7 @@
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "components/browser_sync/test_http_bridge_factory.h"
#include "components/browser_sync/test_profile_sync_service.h"
#include "components/sync/driver/glue/sync_backend_host_core.h"
@@ -31,95 +32,73 @@ namespace browser_sync {
namespace {
-class SyncBackendHostForProfileSyncTest : public SyncBackendHostImpl {
+std::unique_ptr<syncer::HttpPostProviderFactory> GetHttpPostProviderFactory(
+ syncer::CancelationSignal* signal) {
+ return base::MakeUnique<TestHttpBridgeFactory>();
+}
+
+class SyncEngineForProfileSyncTest : public SyncBackendHostImpl {
public:
- SyncBackendHostForProfileSyncTest(
+ SyncEngineForProfileSyncTest(
const base::FilePath& temp_dir,
syncer::SyncClient* sync_client,
- const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread,
invalidation::InvalidationService* invalidator,
const base::WeakPtr<syncer::SyncPrefs>& sync_prefs,
const base::Closure& callback);
- ~SyncBackendHostForProfileSyncTest() override;
-
- void RequestConfigureSyncer(
- syncer::ConfigureReason reason,
- syncer::ModelTypeSet to_download,
- syncer::ModelTypeSet to_purge,
- syncer::ModelTypeSet to_journal,
- syncer::ModelTypeSet to_unapply,
- syncer::ModelTypeSet to_ignore,
- const syncer::ModelSafeRoutingInfo& routing_info,
- const base::Callback<void(syncer::ModelTypeSet, syncer::ModelTypeSet)>&
- ready_task,
- const base::Closure& retry_callback) override;
-
- protected:
- void InitCore(std::unique_ptr<syncer::DoInitializeOptions> options) override;
+ ~SyncEngineForProfileSyncTest() override;
+
+ void Initialize(InitParams params) override;
+
+ void ConfigureDataTypes(ConfigureParams params) override;
private:
// Invoked at the start of HandleSyncManagerInitializationOnFrontendLoop.
- // Allows extra initialization work to be performed before the backend comes
+ // Allows extra initialization work to be performed before the engine comes
// up.
base::Closure callback_;
- DISALLOW_COPY_AND_ASSIGN(SyncBackendHostForProfileSyncTest);
+ DISALLOW_COPY_AND_ASSIGN(SyncEngineForProfileSyncTest);
};
-SyncBackendHostForProfileSyncTest::SyncBackendHostForProfileSyncTest(
+SyncEngineForProfileSyncTest::SyncEngineForProfileSyncTest(
const base::FilePath& temp_dir,
syncer::SyncClient* sync_client,
- const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread,
invalidation::InvalidationService* invalidator,
const base::WeakPtr<syncer::SyncPrefs>& sync_prefs,
const base::Closure& callback)
: SyncBackendHostImpl(
"dummy_debug_name",
sync_client,
- ui_thread,
invalidator,
sync_prefs,
temp_dir.Append(base::FilePath(FILE_PATH_LITERAL("test")))),
callback_(callback) {}
-SyncBackendHostForProfileSyncTest::~SyncBackendHostForProfileSyncTest() {}
+SyncEngineForProfileSyncTest::~SyncEngineForProfileSyncTest() {}
-void SyncBackendHostForProfileSyncTest::InitCore(
- std::unique_ptr<syncer::DoInitializeOptions> options) {
- options->http_bridge_factory = base::MakeUnique<TestHttpBridgeFactory>();
- options->sync_manager_factory =
+void SyncEngineForProfileSyncTest::Initialize(InitParams params) {
+ params.http_factory_getter = base::Bind(&GetHttpPostProviderFactory);
+ params.sync_manager_factory =
base::MakeUnique<syncer::SyncManagerFactoryForProfileSyncTest>(callback_);
- options->credentials.email = "testuser@gmail.com";
- options->credentials.sync_token = "token";
- options->credentials.scope_set.insert(GaiaConstants::kChromeSyncOAuth2Scope);
- options->restored_key_for_bootstrapping.clear();
+ params.credentials.email = "testuser@gmail.com";
+ params.credentials.sync_token = "token";
+ params.credentials.scope_set.insert(GaiaConstants::kChromeSyncOAuth2Scope);
+ params.restored_key_for_bootstrapping.clear();
// It'd be nice if we avoided creating the EngineComponentsFactory in the
- // first place, but SyncBackendHost will have created one by now so we must
- // free it. Grab the switches to pass on first.
+ // first place, but SyncEngine will have created one by now so we must free
+ // it. Grab the switches to pass on first.
syncer::EngineComponentsFactory::Switches factory_switches =
- options->engine_components_factory->GetSwitches();
- options->engine_components_factory =
+ params.engine_components_factory->GetSwitches();
+ params.engine_components_factory =
base::MakeUnique<syncer::TestEngineComponentsFactory>(
factory_switches, syncer::EngineComponentsFactory::STORAGE_IN_MEMORY,
nullptr);
- SyncBackendHostImpl::InitCore(std::move(options));
+ SyncBackendHostImpl::Initialize(std::move(params));
}
-void SyncBackendHostForProfileSyncTest::RequestConfigureSyncer(
- syncer::ConfigureReason reason,
- syncer::ModelTypeSet to_download,
- syncer::ModelTypeSet to_purge,
- syncer::ModelTypeSet to_journal,
- syncer::ModelTypeSet to_unapply,
- syncer::ModelTypeSet to_ignore,
- const syncer::ModelSafeRoutingInfo& routing_info,
- const base::Callback<void(syncer::ModelTypeSet, syncer::ModelTypeSet)>&
- ready_task,
- const base::Closure& retry_callback) {
- syncer::ModelTypeSet failed_configuration_types;
-
+void SyncEngineForProfileSyncTest::ConfigureDataTypes(ConfigureParams params) {
// The first parameter there should be the set of enabled types. That's not
// something we have access to from this strange test harness. We'll just
// send back the list of newly configured types instead and hope it doesn't
@@ -127,12 +106,10 @@ void SyncBackendHostForProfileSyncTest::RequestConfigureSyncer(
// Posted to avoid re-entrancy issues.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
- base::Bind(&SyncBackendHostForProfileSyncTest::
- FinishConfigureDataTypesOnFrontendLoop,
- base::Unretained(this),
- syncer::Difference(to_download, failed_configuration_types),
- syncer::Difference(to_download, failed_configuration_types),
- failed_configuration_types, ready_task));
+ base::Bind(
+ &SyncEngineForProfileSyncTest::FinishConfigureDataTypesOnFrontendLoop,
+ base::Unretained(this), params.to_download, params.to_download,
+ syncer::ModelTypeSet(), params.ready_task));
}
// Helper function for return-type-upcasting of the callback.
@@ -194,10 +171,9 @@ void AbstractProfileSyncServiceTest::CreateSyncService(
syncer::SyncApiComponentFactoryMock* components =
profile_sync_service_bundle_.component_factory();
- EXPECT_CALL(*components, CreateSyncBackendHost(_, _, _, _))
- .WillOnce(Return(new SyncBackendHostForProfileSyncTest(
+ EXPECT_CALL(*components, CreateSyncEngine(_, _, _, _))
+ .WillOnce(Return(new SyncEngineForProfileSyncTest(
temp_dir_.GetPath(), sync_service_->GetSyncClient(),
- base::ThreadTaskRunnerHandle::Get(),
profile_sync_service_bundle_.fake_invalidation_service(),
sync_service_->sync_prefs()->AsWeakPtr(),
initialization_success_callback)));
diff --git a/chromium/components/browser_sync/abstract_profile_sync_service_test.h b/chromium/components/browser_sync/abstract_profile_sync_service_test.h
index dc2fc7c976b..f7d3d12a294 100644
--- a/chromium/components/browser_sync/abstract_profile_sync_service_test.h
+++ b/chromium/components/browser_sync/abstract_profile_sync_service_test.h
@@ -19,10 +19,6 @@
#include "components/sync/syncable/change_record.h"
#include "testing/gtest/include/gtest/gtest.h"
-namespace syncer {
-struct UserShare;
-} // namespace syncer
-
namespace browser_sync {
class TestProfileSyncService;
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 f71096c80b0..ba1f82b5675 100644
--- a/chromium/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/chromium/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -6,11 +6,9 @@
#include <utility>
-#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_wallet_data_type_controller.h"
#include "components/autofill/core/browser/webdata/autofill_data_type_controller.h"
@@ -26,19 +24,20 @@
#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/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"
+#include "components/sync/driver/async_directory_type_controller.h"
#include "components/sync/driver/data_type_manager_impl.h"
-#include "components/sync/driver/glue/sync_backend_host.h"
#include "components/sync/driver/glue/sync_backend_host_impl.h"
#include "components/sync/driver/model_type_controller.h"
#include "components/sync/driver/proxy_data_type_controller.h"
#include "components/sync/driver/sync_client.h"
#include "components/sync/driver/sync_driver_switches.h"
-#include "components/sync/driver/ui_data_type_controller.h"
#include "components/sync/engine/attachments/attachment_downloader.h"
#include "components/sync/engine/attachments/attachment_uploader.h"
+#include "components/sync/engine/sync_engine.h"
#include "components/sync/model/attachments/attachment_service.h"
#include "components/sync_bookmarks/bookmark_change_processor.h"
#include "components/sync_bookmarks/bookmark_data_type_controller.h"
@@ -52,15 +51,15 @@ using bookmarks::BookmarkModel;
using sync_bookmarks::BookmarkChangeProcessor;
using sync_bookmarks::BookmarkDataTypeController;
using sync_bookmarks::BookmarkModelAssociator;
+using sync_sessions::SessionDataTypeController;
+using syncer::AsyncDirectoryTypeController;
using syncer::DataTypeController;
using syncer::DataTypeManager;
using syncer::DataTypeManagerImpl;
using syncer::DataTypeManagerObserver;
using syncer::DeviceInfoDataTypeController;
-using syncer::ProxyDataTypeController;
-using syncer::UIDataTypeController;
using syncer::ModelTypeController;
-using sync_sessions::SessionDataTypeController;
+using syncer::ProxyDataTypeController;
namespace browser_sync {
@@ -142,9 +141,8 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
// Use an error callback that always uploads a stacktrace if it can to help
// get USS as stable as possible.
sync_service->RegisterDataTypeController(
- base::MakeUnique<ModelTypeController>(
- syncer::DEVICE_INFO, base::Bind(&base::debug::DumpWithoutCrashing),
- sync_client_, base::ThreadTaskRunnerHandle::Get()));
+ base::MakeUnique<ModelTypeController>(syncer::DEVICE_INFO, sync_client_,
+ ui_thread_));
} else {
sync_service->RegisterDataTypeController(
base::MakeUnique<DeviceInfoDataTypeController>(
@@ -152,15 +150,21 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
sync_service->GetLocalDeviceInfoProvider()));
}
- // Autofill sync is enabled by default. Register unless explicitly
+ // Autocomplete sync is enabled by default. Register unless explicitly
// disabled.
if (!disabled_types.Has(syncer::AUTOFILL)) {
- sync_service->RegisterDataTypeController(
- base::MakeUnique<AutofillDataTypeController>(
- db_thread_, error_callback, sync_client_, web_data_service_));
+ if (base::FeatureList::IsEnabled(switches::kSyncUSSAutocomplete)) {
+ sync_service->RegisterDataTypeController(
+ base::MakeUnique<ModelTypeController>(syncer::AUTOFILL, sync_client_,
+ db_thread_));
+ } else {
+ sync_service->RegisterDataTypeController(
+ base::MakeUnique<AutofillDataTypeController>(
+ db_thread_, error_callback, sync_client_, web_data_service_));
+ }
}
- // Autofill profile sync is enabled by default. Register unless explicitly
+ // Autofill sync is enabled by default. Register unless explicitly
// disabled.
if (!disabled_types.Has(syncer::AUTOFILL_PROFILE)) {
sync_service->RegisterDataTypeController(
@@ -233,11 +237,13 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
!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<UIDataTypeController>(syncer::FAVICON_IMAGES,
- base::Closure(), sync_client_));
+ base::MakeUnique<AsyncDirectoryTypeController>(
+ syncer::FAVICON_IMAGES, base::Closure(), sync_client_,
+ syncer::GROUP_UI, ui_thread_));
sync_service->RegisterDataTypeController(
- base::MakeUnique<UIDataTypeController>(syncer::FAVICON_TRACKING,
- base::Closure(), sync_client_));
+ base::MakeUnique<AsyncDirectoryTypeController>(
+ syncer::FAVICON_TRACKING, base::Closure(), sync_client_,
+ syncer::GROUP_UI, ui_thread_));
}
// Password sync is enabled by default. Register unless explicitly
@@ -252,49 +258,61 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
if (!disabled_types.Has(syncer::PREFERENCES)) {
if (!override_prefs_controller_to_uss_for_test_) {
sync_service->RegisterDataTypeController(
- base::MakeUnique<UIDataTypeController>(syncer::PREFERENCES,
- error_callback, sync_client_));
+ base::MakeUnique<AsyncDirectoryTypeController>(
+ syncer::PREFERENCES, error_callback, sync_client_,
+ syncer::GROUP_UI, ui_thread_));
} else {
sync_service->RegisterDataTypeController(
- base::MakeUnique<ModelTypeController>(
- syncer::PREFERENCES, error_callback, sync_client_,
- base::ThreadTaskRunnerHandle::Get()));
+ base::MakeUnique<ModelTypeController>(syncer::PREFERENCES,
+ sync_client_, ui_thread_));
}
}
if (!disabled_types.Has(syncer::PRIORITY_PREFERENCES)) {
sync_service->RegisterDataTypeController(
- base::MakeUnique<UIDataTypeController>(syncer::PRIORITY_PREFERENCES,
- error_callback, sync_client_));
+ base::MakeUnique<AsyncDirectoryTypeController>(
+ syncer::PRIORITY_PREFERENCES, error_callback, sync_client_,
+ syncer::GROUP_UI, ui_thread_));
}
// Article sync is disabled by default. Register only if explicitly enabled.
if (dom_distiller::IsEnableSyncArticlesSet()) {
sync_service->RegisterDataTypeController(
- base::MakeUnique<UIDataTypeController>(syncer::ARTICLES, error_callback,
- sync_client_));
+ base::MakeUnique<AsyncDirectoryTypeController>(
+ syncer::ARTICLES, error_callback, sync_client_, syncer::GROUP_UI,
+ ui_thread_));
+ }
+
+ // Reading list sync is enabled by default only on iOS. Register unless
+ // Reading List or Reading List Sync is explicitly disabled.
+ if (!disabled_types.Has(syncer::READING_LIST) &&
+ reading_list::switches::IsReadingListEnabled()) {
+ sync_service->RegisterDataTypeController(
+ base::MakeUnique<ModelTypeController>(syncer::READING_LIST,
+ sync_client_, ui_thread_));
}
}
DataTypeManager* ProfileSyncComponentsFactoryImpl::CreateDataTypeManager(
+ syncer::ModelTypeSet initial_types,
const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
debug_info_listener,
const DataTypeController::TypeMap* controllers,
const syncer::DataTypeEncryptionHandler* encryption_handler,
- syncer::SyncBackendHost* backend,
+ syncer::ModelTypeConfigurer* configurer,
DataTypeManagerObserver* observer) {
- return new DataTypeManagerImpl(debug_info_listener, controllers,
- encryption_handler, backend, observer);
+ return new DataTypeManagerImpl(sync_client_, initial_types,
+ debug_info_listener, controllers,
+ encryption_handler, configurer, observer);
}
-syncer::SyncBackendHost*
-ProfileSyncComponentsFactoryImpl::CreateSyncBackendHost(
+syncer::SyncEngine* ProfileSyncComponentsFactoryImpl::CreateSyncEngine(
const std::string& name,
invalidation::InvalidationService* invalidator,
const base::WeakPtr<syncer::SyncPrefs>& sync_prefs,
- const base::FilePath& sync_folder) {
- return new syncer::SyncBackendHostImpl(name, sync_client_, ui_thread_,
- invalidator, sync_prefs, sync_folder);
+ const base::FilePath& sync_data_folder) {
+ return new syncer::SyncBackendHostImpl(name, sync_client_, invalidator,
+ sync_prefs, sync_data_folder);
}
std::unique_ptr<syncer::LocalDeviceInfoProvider>
diff --git a/chromium/components/browser_sync/profile_sync_components_factory_impl.h b/chromium/components/browser_sync/profile_sync_components_factory_impl.h
index de43525d415..9fc0538c524 100644
--- a/chromium/components/browser_sync/profile_sync_components_factory_impl.h
+++ b/chromium/components/browser_sync/profile_sync_components_factory_impl.h
@@ -18,7 +18,6 @@
#include "url/gurl.h"
class OAuth2TokenService;
-class Profile;
namespace autofill {
class AutofillWebDataService;
@@ -66,17 +65,18 @@ class ProfileSyncComponentsFactoryImpl
syncer::SyncService* sync_service,
const RegisterDataTypesMethod& register_platform_types_method) override;
syncer::DataTypeManager* CreateDataTypeManager(
+ syncer::ModelTypeSet initial_types,
const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
debug_info_listener,
const syncer::DataTypeController::TypeMap* controllers,
const syncer::DataTypeEncryptionHandler* encryption_handler,
- syncer::SyncBackendHost* backend,
+ syncer::ModelTypeConfigurer* configurer,
syncer::DataTypeManagerObserver* observer) override;
- syncer::SyncBackendHost* CreateSyncBackendHost(
+ syncer::SyncEngine* CreateSyncEngine(
const std::string& name,
invalidation::InvalidationService* invalidator,
const base::WeakPtr<syncer::SyncPrefs>& sync_prefs,
- const base::FilePath& sync_folder) override;
+ const base::FilePath& sync_data_folder) override;
std::unique_ptr<syncer::LocalDeviceInfoProvider>
CreateLocalDeviceInfoProvider() override;
std::unique_ptr<syncer::AttachmentService> CreateAttachmentService(
diff --git a/chromium/components/browser_sync/profile_sync_service.cc b/chromium/components/browser_sync/profile_sync_service.cc
index 222bf630f96..a28a87f4530 100644
--- a/chromium/components/browser_sync/profile_sync_service.cc
+++ b/chromium/components/browser_sync/profile_sync_service.cc
@@ -20,7 +20,6 @@
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram.h"
-#include "base/path_service.h"
#include "base/profiler/scoped_tracker.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
@@ -33,6 +32,7 @@
#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/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"
@@ -69,7 +69,6 @@
#include "components/sync/js/js_event_details.h"
#include "components/sync/model/change_processor.h"
#include "components/sync/model/model_type_change_processor.h"
-#include "components/sync/model/model_type_store.h"
#include "components/sync/model/sync_error.h"
#include "components/sync/protocol/sync.pb.h"
#include "components/sync/syncable/directory.h"
@@ -108,15 +107,37 @@ using syncer::ModelTypeChangeProcessor;
using syncer::ModelTypeSet;
using syncer::ModelTypeStore;
using syncer::ProtocolEventObserver;
-using syncer::SyncBackendHost;
+using syncer::SyncEngine;
using syncer::SyncCredentials;
using syncer::SyncProtocolError;
using syncer::WeakHandle;
namespace browser_sync {
+namespace {
+
typedef GoogleServiceAuthError AuthError;
+// Events in ClearServerData flow to be recorded in histogram. Existing
+// constants should not be deleted or reordered. New ones shold be added at the
+// end, before CLEAR_SERVER_DATA_MAX.
+enum ClearServerDataEvents {
+ // ClearServerData started after user switched to custom passphrase.
+ CLEAR_SERVER_DATA_STARTED,
+ // DataTypeManager reported that catchup configuration failed.
+ CLEAR_SERVER_DATA_CATCHUP_FAILED,
+ // ClearServerData flow restarted after browser restart.
+ CLEAR_SERVER_DATA_RETRIED,
+ // Success.
+ CLEAR_SERVER_DATA_SUCCEEDED,
+ // Client received RECET_LOCAL_SYNC_DATA after custom passphrase was enabled
+ // on different client.
+ CLEAR_SERVER_DATA_RESET_LOCAL_DATA_RECEIVED,
+ CLEAR_SERVER_DATA_MAX
+};
+
+const char kClearServerDataEventsHistogramName[] = "Sync.ClearServerDataEvents";
+
const char kSyncUnrecoverableErrorHistogram[] = "Sync.UnrecoverableErrors";
const net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = {
@@ -147,66 +168,36 @@ const net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = {
false,
};
-static const base::FilePath::CharType kSyncDataFolderName[] =
- FILE_PATH_LITERAL("Sync Data");
-static const base::FilePath::CharType kLevelDBFolderName[] =
+const base::FilePath::CharType kLevelDBFolderName[] =
FILE_PATH_LITERAL("LevelDB");
-#if defined(OS_WIN)
-static const base::FilePath::CharType kLoopbackServerBackendFilename[] =
- FILE_PATH_LITERAL("profile.pb");
-#endif
-
-namespace {
-
-// Perform the actual sync data folder deletion.
-// This should only be called on the sync thread.
-void DeleteSyncDataFolder(const base::FilePath& directory_path) {
- if (base::DirectoryExists(directory_path)) {
- if (!base::DeleteFile(directory_path, true))
- LOG(DFATAL) << "Could not delete the Sync Data folder.";
- }
-}
-
} // namespace
ProfileSyncService::InitParams::InitParams() = default;
+ProfileSyncService::InitParams::InitParams(InitParams&& other) = default;
ProfileSyncService::InitParams::~InitParams() = default;
-ProfileSyncService::InitParams::InitParams(InitParams&& other) // NOLINT
- : sync_client(std::move(other.sync_client)),
- signin_wrapper(std::move(other.signin_wrapper)),
- oauth2_token_service(other.oauth2_token_service),
- gaia_cookie_manager_service(other.gaia_cookie_manager_service),
- start_behavior(other.start_behavior),
- network_time_update_callback(
- std::move(other.network_time_update_callback)),
- base_directory(std::move(other.base_directory)),
- url_request_context(std::move(other.url_request_context)),
- debug_identifier(std::move(other.debug_identifier)),
- channel(other.channel),
- blocking_pool(other.blocking_pool) {}
ProfileSyncService::ProfileSyncService(InitParams init_params)
- : OAuth2TokenService::Consumer("sync"),
+ : SyncServiceBase(std::move(init_params.sync_client),
+ std::move(init_params.signin_wrapper),
+ init_params.channel,
+ init_params.base_directory,
+ init_params.debug_identifier),
+ OAuth2TokenService::Consumer("sync"),
last_auth_error_(AuthError::AuthErrorNone()),
passphrase_required_reason_(syncer::REASON_PASSPHRASE_NOT_REQUIRED),
- sync_client_(std::move(init_params.sync_client)),
- sync_prefs_(sync_client_->GetPrefService()),
sync_service_url_(
syncer::GetSyncServiceURL(*base::CommandLine::ForCurrentProcess(),
init_params.channel)),
network_time_update_callback_(
std::move(init_params.network_time_update_callback)),
- base_directory_(init_params.base_directory),
url_request_context_(init_params.url_request_context),
- debug_identifier_(std::move(init_params.debug_identifier)),
- channel_(init_params.channel),
- blocking_pool_(init_params.blocking_pool),
+ blocking_task_runner_(std::move(init_params.blocking_task_runner)),
is_first_time_sync_configure_(false),
- backend_initialized_(false),
+ engine_initialized_(false),
sync_disabled_by_admin_(false),
is_auth_in_progress_(false),
- signin_(std::move(init_params.signin_wrapper)),
+ local_sync_backend_folder_(init_params.local_sync_backend_folder),
unrecoverable_error_reason_(ERROR_REASON_UNSET),
expect_sync_configuration_aborted_(false),
encrypted_types_(syncer::SyncEncryptionHandler::SensitiveTypes()),
@@ -221,8 +212,6 @@ ProfileSyncService::ProfileSyncService(InitParams init_params)
gaia_cookie_manager_service_(init_params.gaia_cookie_manager_service),
network_resources_(new syncer::HttpBridgeNetworkResources),
start_behavior_(init_params.start_behavior),
- directory_path_(
- base_directory_.Append(base::FilePath(kSyncDataFolderName))),
catch_up_configure_in_progress_(false),
passphrase_prompt_triggered_by_version_(false),
sync_enabled_weak_factory_(this),
@@ -242,26 +231,30 @@ ProfileSyncService::ProfileSyncService(InitParams init_params)
}
ProfileSyncService::~ProfileSyncService() {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (gaia_cookie_manager_service_)
gaia_cookie_manager_service_->RemoveObserver(this);
sync_prefs_.RemoveSyncPrefObserver(this);
// Shutdown() should have been called before destruction.
- CHECK(!backend_initialized_);
+ CHECK(!engine_initialized_);
}
bool ProfileSyncService::CanSyncStart() const {
- return IsSyncAllowed() && IsSyncRequested() && IsSignedIn();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return (IsSyncAllowed() && IsSyncRequested() &&
+ (IsLocalSyncEnabled() || IsSignedIn()));
}
void ProfileSyncService::Initialize() {
+ DCHECK(thread_checker_.CalledOnValidThread());
sync_client_->Initialize();
// We don't pass StartupController an Unretained reference to future-proof
// against the controller impl changing to post tasks.
startup_controller_ = base::MakeUnique<syncer::StartupController>(
&sync_prefs_,
- base::Bind(&ProfileSyncService::CanBackendStart, base::Unretained(this)),
- base::Bind(&ProfileSyncService::StartUpSlowBackendComponents,
+ base::Bind(&ProfileSyncService::CanEngineStart, base::Unretained(this)),
+ base::Bind(&ProfileSyncService::StartUpSlowEngineComponents,
weak_factory_.GetWeakPtr()));
std::unique_ptr<sync_sessions::LocalSessionEventRouter> router(
sync_client_->GetSyncSessionsClient()->GetLocalSessionEventRouter());
@@ -280,20 +273,14 @@ void ProfileSyncService::Initialize() {
syncer::ModelTypeSet(syncer::SESSIONS)));
if (base::FeatureList::IsEnabled(switches::kSyncUSSDeviceInfo)) {
- scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
- blocking_pool_->GetSequencedTaskRunnerWithShutdownBehavior(
- blocking_pool_->GetSequenceToken(),
- base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
// TODO(skym): Stop creating leveldb files when signed out.
// TODO(skym): Verify using AsUTF8Unsafe is okay here. Should work as long
// as the Local State file is guaranteed to be UTF-8.
- device_info_service_ = base::MakeUnique<DeviceInfoSyncBridge>(
- local_device_.get(),
- base::Bind(&ModelTypeStore::CreateStore, syncer::DEVICE_INFO,
- directory_path_.Append(base::FilePath(kLevelDBFolderName))
- .AsUTF8Unsafe(),
- blocking_task_runner),
- base::Bind(&ModelTypeChangeProcessor::Create));
+ device_info_sync_bridge_ = base::MakeUnique<DeviceInfoSyncBridge>(
+ local_device_.get(), GetModelTypeStoreFactory(syncer::DEVICE_INFO),
+ base::BindRepeating(
+ &ModelTypeChangeProcessor::Create,
+ base::BindRepeating(&syncer::ReportUnrecoverableError, channel_)));
} else {
device_info_sync_service_ =
base::MakeUnique<DeviceInfoSyncService>(local_device_.get());
@@ -316,7 +303,7 @@ void ProfileSyncService::Initialize() {
sync_prefs_.AddSyncPrefObserver(this);
SyncInitialState sync_state = CAN_START;
- if (!IsSignedIn()) {
+ if (!IsLocalSyncEnabled() && !IsSignedIn()) {
sync_state = NOT_SIGNED_IN;
} else if (IsManaged()) {
sync_state = IS_MANAGED;
@@ -343,11 +330,13 @@ void ProfileSyncService::Initialize() {
return;
}
- RegisterAuthNotifications();
+ if (!IsLocalSyncEnabled()) {
+ RegisterAuthNotifications();
- if (!IsSignedIn()) {
- // Clean up in case of previous crash during signout.
- StopImpl(CLEAR_DATA);
+ if (!IsSignedIn()) {
+ // Clean up in case of previous crash during signout.
+ StopImpl(CLEAR_DATA);
+ }
}
#if defined(OS_CHROMEOS)
@@ -370,8 +359,8 @@ void ProfileSyncService::Initialize() {
sync_enabled_weak_factory_.GetWeakPtr()));
startup_controller_->Reset(GetRegisteredDataTypes());
- // Auto-start means means the first time the profile starts up, sync should
- // start up immediately.
+ // Auto-start means the first time the profile starts up, sync should start up
+ // immediately.
if (start_behavior_ == AUTO_START && IsSyncRequested() &&
!IsFirstSetupComplete()) {
startup_controller_->TryStartImmediately();
@@ -381,33 +370,39 @@ void ProfileSyncService::Initialize() {
}
void ProfileSyncService::StartSyncingWithServer() {
- DCHECK(thread_checker_.CalledOnValidThread());
-
if (base::FeatureList::IsEnabled(
switches::kSyncClearDataOnPassphraseEncryption) &&
sync_prefs_.GetPassphraseEncryptionTransitionInProgress()) {
+ // We are restarting catchup configuration after browser restart.
+ UMA_HISTOGRAM_ENUMERATION(kClearServerDataEventsHistogramName,
+ CLEAR_SERVER_DATA_RETRIED, CLEAR_SERVER_DATA_MAX);
+
BeginConfigureCatchUpBeforeClear();
return;
}
- if (backend_)
- backend_->StartSyncingWithServer();
+ if (engine_)
+ engine_->StartSyncingWithServer();
}
void ProfileSyncService::RegisterAuthNotifications() {
+ DCHECK(thread_checker_.CalledOnValidThread());
oauth2_token_service_->AddObserver(this);
if (signin())
signin()->AddObserver(this);
}
void ProfileSyncService::UnregisterAuthNotifications() {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (signin())
signin()->RemoveObserver(this);
- oauth2_token_service_->RemoveObserver(this);
+ if (oauth2_token_service_)
+ oauth2_token_service_->RemoveObserver(this);
}
void ProfileSyncService::RegisterDataTypeController(
std::unique_ptr<syncer::DataTypeController> data_type_controller) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(data_type_controllers_.count(data_type_controller->type()), 0U);
data_type_controllers_[data_type_controller->type()] =
std::move(data_type_controller);
@@ -415,6 +410,7 @@ void ProfileSyncService::RegisterDataTypeController(
bool ProfileSyncService::IsDataTypeControllerRunning(
syncer::ModelType type) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
DataTypeController::TypeMap::const_iterator iter =
data_type_controllers_.find(type);
if (iter == data_type_controllers_.end()) {
@@ -424,6 +420,7 @@ bool ProfileSyncService::IsDataTypeControllerRunning(
}
sync_sessions::OpenTabsUIDelegate* ProfileSyncService::GetOpenTabsUIDelegate() {
+ DCHECK(thread_checker_.CalledOnValidThread());
// Although the backing data actually is of type |SESSIONS|, the desire to use
// open tabs functionality is tracked by the state of the |PROXY_TABS| type.
return IsDataTypeControllerRunning(syncer::PROXY_TABS)
@@ -432,13 +429,15 @@ sync_sessions::OpenTabsUIDelegate* ProfileSyncService::GetOpenTabsUIDelegate() {
}
sync_sessions::FaviconCache* ProfileSyncService::GetFaviconCache() {
+ DCHECK(thread_checker_.CalledOnValidThread());
return sessions_sync_manager_->GetFaviconCache();
}
syncer::DeviceInfoTracker* ProfileSyncService::GetDeviceInfoTracker() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
// One of the two should always be non-null after initialization is done.
- if (device_info_service_) {
- return device_info_service_.get();
+ if (device_info_sync_bridge_) {
+ return device_info_sync_bridge_.get();
} else {
return device_info_sync_service_.get();
}
@@ -446,11 +445,13 @@ syncer::DeviceInfoTracker* ProfileSyncService::GetDeviceInfoTracker() const {
syncer::LocalDeviceInfoProvider*
ProfileSyncService::GetLocalDeviceInfoProvider() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return local_device_.get();
}
void ProfileSyncService::GetDataTypeControllerStates(
DataTypeController::StateMap* state_map) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
for (DataTypeController::TypeMap::const_iterator iter =
data_type_controllers_.begin();
iter != data_type_controllers_.end(); ++iter)
@@ -458,6 +459,7 @@ void ProfileSyncService::GetDataTypeControllerStates(
}
void ProfileSyncService::OnSessionRestoreComplete() {
+ DCHECK(thread_checker_.CalledOnValidThread());
DataTypeController::TypeMap::const_iterator iter =
data_type_controllers_.find(syncer::SESSIONS);
if (iter == data_type_controllers_.end()) {
@@ -471,6 +473,11 @@ void ProfileSyncService::OnSessionRestoreComplete() {
SyncCredentials ProfileSyncService::GetCredentials() {
SyncCredentials credentials;
+
+ // No credentials exist or are needed for the local sync backend.
+ if (IsLocalSyncEnabled())
+ return credentials;
+
credentials.account_id = signin_->GetAccountIdToUse();
DCHECK(!credentials.account_id.empty());
credentials.email = signin_->GetEffectiveUsername();
@@ -484,72 +491,26 @@ SyncCredentials ProfileSyncService::GetCredentials() {
return credentials;
}
-bool ProfileSyncService::ShouldDeleteSyncFolder() {
- return !IsFirstSetupComplete();
+syncer::WeakHandle<syncer::JsEventHandler>
+ProfileSyncService::GetJsEventHandler() {
+ return syncer::MakeWeakHandle(sync_js_controller_.AsWeakPtr());
}
-void ProfileSyncService::InitializeBackend(bool delete_stale_data) {
- if (!backend_) {
- NOTREACHED();
- return;
- }
-
- if (!sync_thread_) {
- sync_thread_ = base::MakeUnique<base::Thread>("Chrome_SyncThread");
- base::Thread::Options options;
- options.timer_slack = base::TIMER_SLACK_MAXIMUM;
- CHECK(sync_thread_->StartWithOptions(options));
- }
-
- SyncCredentials credentials = GetCredentials();
+syncer::SyncEngine::HttpPostProviderFactoryGetter
+ProfileSyncService::MakeHttpPostProviderFactoryGetter() {
+ return base::Bind(&syncer::NetworkResources::GetHttpPostProviderFactory,
+ base::Unretained(network_resources_.get()),
+ url_request_context_, network_time_update_callback_);
+}
- if (delete_stale_data)
- ClearStaleErrors();
+std::unique_ptr<syncer::SyncEncryptionHandler::NigoriState>
+ProfileSyncService::MoveSavedNigoriState() {
+ return std::move(saved_nigori_state_);
+}
- bool enable_local_sync_backend = false;
- base::FilePath local_sync_backend_folder;
-#if defined(OS_WIN)
- enable_local_sync_backend = base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableLocalSyncBackend);
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kLocalSyncBackendDir)) {
- local_sync_backend_folder =
- base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
- switches::kLocalSyncBackendDir);
- } else {
- // TODO(pastarmovj): Add DIR_ROAMING_USER_DATA to PathService to simplify
- // this code and move the logic in its right place. See crbug/657810.
- CHECK(
- base::PathService::Get(base::DIR_APP_DATA, &local_sync_backend_folder));
- local_sync_backend_folder =
- local_sync_backend_folder.Append(FILE_PATH_LITERAL("Chrome/User Data"));
- }
- // This code as it is now will assume the same profile order is present on all
- // machines, which is not a given. It is to be defined if only the Default
- // profile should get this treatment or all profile as is the case now. The
- // solution for now will be to assume profiles are created in the same order
- // on all machines and in the future decide if only the Default one should be
- // considered roamed.
- local_sync_backend_folder =
- local_sync_backend_folder.Append(base_directory_.BaseName());
- local_sync_backend_folder =
- local_sync_backend_folder.Append(kLoopbackServerBackendFilename);
-#endif // defined(OS_WIN)
-
- SyncBackendHost::HttpPostProviderFactoryGetter
- http_post_provider_factory_getter =
- base::Bind(&syncer::NetworkResources::GetHttpPostProviderFactory,
- base::Unretained(network_resources_.get()),
- url_request_context_, network_time_update_callback_);
-
- backend_->Initialize(
- this, sync_thread_.get(), GetJsEventHandler(), sync_service_url_,
- local_device_->GetSyncUserAgent(), credentials, delete_stale_data,
- enable_local_sync_backend, local_sync_backend_folder,
- base::MakeUnique<syncer::SyncManagerFactory>(),
- MakeWeakHandle(sync_enabled_weak_factory_.GetWeakPtr()),
- base::Bind(syncer::ReportUnrecoverableError, channel_),
- http_post_provider_factory_getter, std::move(saved_nigori_state_));
+syncer::WeakHandle<syncer::UnrecoverableErrorHandler>
+ProfileSyncService::GetUnrecoverableErrorHandler() {
+ return syncer::MakeWeakHandle(sync_enabled_weak_factory_.GetWeakPtr());
}
bool ProfileSyncService::IsEncryptedDatatypeEnabled() const {
@@ -562,6 +523,7 @@ bool ProfileSyncService::IsEncryptedDatatypeEnabled() const {
}
void ProfileSyncService::OnProtocolEvent(const syncer::ProtocolEvent& event) {
+ DCHECK(thread_checker_.CalledOnValidThread());
for (auto& observer : protocol_event_observers_)
observer.OnProtocolEvent(event);
}
@@ -569,6 +531,7 @@ void ProfileSyncService::OnProtocolEvent(const syncer::ProtocolEvent& event) {
void ProfileSyncService::OnDirectoryTypeCommitCounterUpdated(
syncer::ModelType type,
const syncer::CommitCounters& counters) {
+ DCHECK(thread_checker_.CalledOnValidThread());
for (auto& observer : type_debug_info_observers_)
observer.OnCommitCountersUpdated(type, counters);
}
@@ -576,6 +539,7 @@ void ProfileSyncService::OnDirectoryTypeCommitCounterUpdated(
void ProfileSyncService::OnDirectoryTypeUpdateCounterUpdated(
syncer::ModelType type,
const syncer::UpdateCounters& counters) {
+ DCHECK(thread_checker_.CalledOnValidThread());
for (auto& observer : type_debug_info_observers_)
observer.OnUpdateCountersUpdated(type, counters);
}
@@ -583,11 +547,13 @@ void ProfileSyncService::OnDirectoryTypeUpdateCounterUpdated(
void ProfileSyncService::OnDatatypeStatusCounterUpdated(
syncer::ModelType type,
const syncer::StatusCounters& counters) {
+ DCHECK(thread_checker_.CalledOnValidThread());
for (auto& observer : type_debug_info_observers_)
observer.OnStatusCountersUpdated(type, counters);
}
void ProfileSyncService::OnDataTypeRequestsSyncStartup(syncer::ModelType type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(syncer::UserTypes().Has(type));
if (!GetPreferredDataTypes().Has(type)) {
@@ -608,7 +574,7 @@ void ProfileSyncService::OnDataTypeRequestsSyncStartup(syncer::ModelType type) {
NotifyObservers();
}
- if (backend_.get()) {
+ if (engine_) {
DVLOG(1) << "A data type requested sync startup, but it looks like "
"something else beat it to the punch.";
return;
@@ -617,19 +583,19 @@ void ProfileSyncService::OnDataTypeRequestsSyncStartup(syncer::ModelType type) {
startup_controller_->OnDataTypeRequestsSyncStartup(type);
}
-void ProfileSyncService::StartUpSlowBackendComponents() {
+void ProfileSyncService::StartUpSlowEngineComponents() {
invalidation::InvalidationService* invalidator =
sync_client_->GetInvalidationService();
- backend_.reset(
- sync_client_->GetSyncApiComponentFactory()->CreateSyncBackendHost(
- debug_identifier_, invalidator, sync_prefs_.AsWeakPtr(),
- directory_path_));
+ engine_.reset(sync_client_->GetSyncApiComponentFactory()->CreateSyncEngine(
+ debug_identifier_, invalidator, sync_prefs_.AsWeakPtr(),
+ sync_data_folder_));
+
+ // Clear any old errors the first time sync starts.
+ if (!IsFirstSetupComplete())
+ ClearStaleErrors();
- // Initialize the backend. Every time we start up a new SyncBackendHost,
- // we'll want to start from a fresh SyncDB, so delete any old one that might
- // be there.
- InitializeBackend(ShouldDeleteSyncFolder());
+ InitializeEngine();
UpdateFirstSyncTimePref();
@@ -640,6 +606,7 @@ void ProfileSyncService::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(access_token_request_.get(), request);
access_token_request_.reset();
access_token_ = access_token;
@@ -650,8 +617,8 @@ void ProfileSyncService::OnGetTokenSuccess(
sync_prefs_.SetSyncAuthError(false);
}
- if (HasSyncingBackend())
- backend_->UpdateCredentials(GetCredentials());
+ if (HasSyncingEngine())
+ engine_->UpdateCredentials(GetCredentials());
else
startup_controller_->TryStart();
}
@@ -659,6 +626,7 @@ void ProfileSyncService::OnGetTokenSuccess(
void ProfileSyncService::OnGetTokenFailure(
const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(access_token_request_.get(), request);
DCHECK_NE(error.state(), GoogleServiceAuthError::NONE);
access_token_request_.reset();
@@ -700,6 +668,7 @@ void ProfileSyncService::OnGetTokenFailure(
void ProfileSyncService::OnRefreshTokenAvailable(
const std::string& account_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
// fixed.
tracked_objects::ScopedTracker tracking_profile(
@@ -711,6 +680,7 @@ void ProfileSyncService::OnRefreshTokenAvailable(
}
void ProfileSyncService::OnRefreshTokenRevoked(const std::string& account_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (account_id == signin_->GetAccountIdToUse()) {
access_token_.clear();
UpdateAuthErrorState(
@@ -719,12 +689,12 @@ void ProfileSyncService::OnRefreshTokenRevoked(const std::string& account_id) {
}
void ProfileSyncService::OnRefreshTokensLoaded() {
- // This notification gets fired when OAuth2TokenService loads the tokens
- // from storage.
- // Initialize the backend if sync is enabled. If the sync token was
- // not loaded, GetCredentials() will generate invalid credentials to
- // cause the backend to generate an auth error (crbug.com/121755).
- if (HasSyncingBackend()) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // This notification gets fired when OAuth2TokenService loads the tokens from
+ // storage. Initialize the engine if sync is enabled. If the sync token was
+ // not loaded, GetCredentials() will generate invalid credentials to cause the
+ // engine to generate an auth error (crbug.com/121755).
+ if (HasSyncingEngine()) {
RequestAccessToken();
} else {
startup_controller_->TryStart();
@@ -732,6 +702,7 @@ void ProfileSyncService::OnRefreshTokensLoaded() {
}
void ProfileSyncService::Shutdown() {
+ DCHECK(thread_checker_.CalledOnValidThread());
UnregisterAuthNotifications();
ShutdownImpl(syncer::BROWSER_SHUTDOWN);
@@ -746,12 +717,14 @@ void ProfileSyncService::Shutdown() {
}
void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
- if (!backend_) {
+ if (!engine_) {
if (reason == syncer::ShutdownReason::DISABLE_SYNC && sync_thread_) {
- // If the backend is already shut down when a DISABLE_SYNC happens,
+ // If the engine is already shut down when a DISABLE_SYNC happens,
// the data directory needs to be cleaned up here.
sync_thread_->task_runner()->PostTask(
- FROM_HERE, base::Bind(&DeleteSyncDataFolder, directory_path_));
+ FROM_HERE,
+ base::Bind(&syncer::syncable::Directory::DeleteDirectoryFiles,
+ sync_data_folder_));
}
return;
}
@@ -761,16 +734,16 @@ void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
RemoveClientFromServer();
}
- // First, we spin down the backend to stop change processing as soon as
+ // First, we spin down the engine to stop change processing as soon as
// possible.
base::Time shutdown_start_time = base::Time::Now();
- backend_->StopSyncingForShutdown();
+ engine_->StopSyncingForShutdown();
- // Stop all data type controllers, if needed. Note that until Stop
- // completes, it is possible in theory to have a ChangeProcessor apply a
- // change from a native model. In that case, it will get applied to the sync
- // database (which doesn't get destroyed until we destroy the backend below)
- // as an unsynced change. That will be persisted, and committed on restart.
+ // Stop all data type controllers, if needed. Note that until Stop completes,
+ // it is possible in theory to have a ChangeProcessor apply a change from a
+ // native model. In that case, it will get applied to the sync database (which
+ // doesn't get destroyed until we destroy the engine below) as an unsynced
+ // change. That will be persisted, and committed on restart.
if (data_type_manager_) {
if (data_type_manager_->state() != DataTypeManager::STOPPED) {
// When aborting as part of shutdown, we should expect an aborted sync
@@ -781,18 +754,14 @@ void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
data_type_manager_.reset();
}
- // Shutdown the migrator before the backend to ensure it doesn't pull a null
+ // Shutdown the migrator before the engine to ensure it doesn't pull a null
// snapshot.
migrator_.reset();
sync_js_controller_.AttachJsBackend(WeakHandle<syncer::JsBackend>());
- // Move aside the backend so nobody else tries to use it while we are
- // shutting it down.
- std::unique_ptr<SyncBackendHost> doomed_backend(backend_.release());
- if (doomed_backend) {
- doomed_backend->Shutdown(reason);
- doomed_backend.reset();
- }
+ engine_->Shutdown(reason);
+ engine_.reset();
+
base::TimeDelta shutdown_time = base::Time::Now() - shutdown_start_time;
UMA_HISTOGRAM_TIMES("Sync.Shutdown.BackendDestroyedTime", shutdown_time);
@@ -806,10 +775,10 @@ void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
local_device_->Clear();
}
- // Clear various flags.
+ // Clear various state.
expect_sync_configuration_aborted_ = false;
is_auth_in_progress_ = false;
- backend_initialized_ = false;
+ engine_initialized_ = false;
cached_passphrase_.clear();
encryption_pending_ = false;
encrypt_everything_ = false;
@@ -818,6 +787,7 @@ void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
catch_up_configure_in_progress_ = false;
access_token_.clear();
request_access_token_retry_timer_.Stop();
+ last_snapshot_ = syncer::SyncCycleSnapshot();
// Revert to "no auth error".
if (last_auth_error_.state() != GoogleServiceAuthError::NONE)
UpdateAuthErrorState(GoogleServiceAuthError::AuthErrorNone());
@@ -831,11 +801,6 @@ void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
void ProfileSyncService::StopImpl(SyncStopDataFate data_fate) {
switch (data_fate) {
case KEEP_DATA:
- // TODO(maxbogue): Investigate whether this logic can/should be moved
- // into ShutdownImpl or SyncBackendHost itself.
- if (HasSyncingBackend()) {
- backend_->UnregisterInvalidationIds();
- }
ShutdownImpl(syncer::STOP_SYNC);
break;
case CLEAR_DATA:
@@ -849,12 +814,14 @@ void ProfileSyncService::StopImpl(SyncStopDataFate data_fate) {
}
bool ProfileSyncService::IsFirstSetupComplete() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return sync_prefs_.IsFirstSetupComplete();
}
void ProfileSyncService::SetFirstSetupComplete() {
+ DCHECK(thread_checker_.CalledOnValidThread());
sync_prefs_.SetFirstSetupComplete();
- if (IsBackendInitialized()) {
+ if (IsEngineInitialized()) {
ReconfigureDatatypeManager();
}
}
@@ -897,6 +864,7 @@ void ProfileSyncService::ClearUnrecoverableError() {
void ProfileSyncService::OnUnrecoverableError(
const tracked_objects::Location& from_here,
const std::string& message) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// Unrecoverable errors that arrive via the syncer::UnrecoverableErrorHandler
// interface are assumed to originate within the syncer.
unrecoverable_error_reason_ = ERROR_REASON_SYNCER;
@@ -927,12 +895,13 @@ void ProfileSyncService::OnUnrecoverableErrorImpl(
}
void ProfileSyncService::ReenableDatatype(syncer::ModelType type) {
- if (!backend_initialized_ || !data_type_manager_)
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!engine_initialized_ || !data_type_manager_)
return;
data_type_manager_->ReenableType(type);
}
-void ProfileSyncService::UpdateBackendInitUMA(bool success) {
+void ProfileSyncService::UpdateEngineInitUMA(bool success) {
is_first_time_sync_configure_ = !IsFirstSetupComplete();
if (is_first_time_sync_configure_) {
@@ -941,9 +910,9 @@ void ProfileSyncService::UpdateBackendInitUMA(bool success) {
UMA_HISTOGRAM_BOOLEAN("Sync.BackendInitializeRestoreSuccess", success);
}
- base::Time on_backend_initialized_time = base::Time::Now();
+ base::Time on_engine_initialized_time = base::Time::Now();
base::TimeDelta delta =
- on_backend_initialized_time - startup_controller_->start_backend_time();
+ on_engine_initialized_time - startup_controller_->start_engine_time();
if (is_first_time_sync_configure_) {
UMA_HISTOGRAM_LONG_TIMES("Sync.BackendInitializeFirstTime", delta);
} else {
@@ -951,13 +920,59 @@ void ProfileSyncService::UpdateBackendInitUMA(bool success) {
}
}
-void ProfileSyncService::PostBackendInitialization() {
+void ProfileSyncService::OnEngineInitialized(
+ ModelTypeSet initial_types,
+ const syncer::WeakHandle<syncer::JsBackend>& js_backend,
+ const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
+ debug_info_listener,
+ const std::string& cache_guid,
+ bool success) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ UpdateEngineInitUMA(success);
+
+ if (!success) {
+ // Something went unexpectedly wrong. Play it safe: stop syncing at once
+ // and surface error UI to alert the user sync has stopped.
+ // Keep the directory around for now so that on restart we will retry
+ // again and potentially succeed in presence of transient file IO failures
+ // or permissions issues, etc.
+ //
+ // TODO(rlarocque): Consider making this UnrecoverableError less special.
+ // Unlike every other UnrecoverableError, it does not delete our sync data.
+ // This exception made sense at the time it was implemented, but our new
+ // directory corruption recovery mechanism makes it obsolete. By the time
+ // we get here, we will have already tried and failed to delete the
+ // directory. It would be no big deal if we tried to delete it again.
+ OnInternalUnrecoverableError(FROM_HERE, "BackendInitialize failure", false,
+ ERROR_REASON_ENGINE_INIT_FAILURE);
+ return;
+ }
+
+ engine_initialized_ = true;
+
+ sync_js_controller_.AttachJsBackend(js_backend);
+ debug_info_listener_ = debug_info_listener;
+
+ std::string signin_scoped_device_id;
+ if (IsLocalSyncEnabled()) {
+ signin_scoped_device_id = "local_device";
+ } else {
+ SigninClient* signin_client = signin_->GetOriginal()->signin_client();
+ DCHECK(signin_client);
+ std::string signin_scoped_device_id =
+ signin_client->GetSigninScopedDeviceId();
+ }
+
+ // Initialize local device info.
+ local_device_->Initialize(cache_guid, signin_scoped_device_id,
+ blocking_task_runner_);
+
if (protocol_event_observers_.might_have_observers()) {
- backend_->RequestBufferedProtocolEventsAndEnableForwarding();
+ engine_->RequestBufferedProtocolEventsAndEnableForwarding();
}
if (type_debug_info_observers_.might_have_observers()) {
- backend_->EnableDirectoryTypeDebugInfoForwarding();
+ engine_->EnableDirectoryTypeDebugInfoForwarding();
}
// If we have a cached passphrase use it to decrypt/encrypt data now that the
@@ -972,6 +987,11 @@ void ProfileSyncService::PostBackendInitialization() {
UpdateLastSyncedTime();
}
+ data_type_manager_.reset(
+ sync_client_->GetSyncApiComponentFactory()->CreateDataTypeManager(
+ initial_types, debug_info_listener_, &data_type_controllers_, this,
+ engine_.get(), this));
+
// Auto-start means IsFirstSetupComplete gets set automatically.
if (start_behavior_ == AUTO_START && !IsFirstSetupComplete()) {
// This will trigger a configure if it completes setup.
@@ -991,54 +1011,22 @@ void ProfileSyncService::PostBackendInitialization() {
}
NotifyObservers();
-}
-
-void ProfileSyncService::OnBackendInitialized(
- const syncer::WeakHandle<syncer::JsBackend>& js_backend,
- const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
- debug_info_listener,
- const std::string& cache_guid,
- bool success) {
- UpdateBackendInitUMA(success);
-
- if (!success) {
- // Something went unexpectedly wrong. Play it safe: stop syncing at once
- // and surface error UI to alert the user sync has stopped.
- // Keep the directory around for now so that on restart we will retry
- // again and potentially succeed in presence of transient file IO failures
- // or permissions issues, etc.
- //
- // TODO(rlarocque): Consider making this UnrecoverableError less special.
- // Unlike every other UnrecoverableError, it does not delete our sync data.
- // This exception made sense at the time it was implemented, but our new
- // directory corruption recovery mechanism makes it obsolete. By the time
- // we get here, we will have already tried and failed to delete the
- // directory. It would be no big deal if we tried to delete it again.
- OnInternalUnrecoverableError(FROM_HERE, "BackendInitialize failure", false,
- ERROR_REASON_BACKEND_INIT_FAILURE);
- return;
- }
-
- backend_initialized_ = true;
-
- sync_js_controller_.AttachJsBackend(js_backend);
- debug_info_listener_ = debug_info_listener;
- SigninClient* signin_client = signin_->GetOriginal()->signin_client();
- DCHECK(signin_client);
- std::string signin_scoped_device_id =
- signin_client->GetSigninScopedDeviceId();
+ // Nobody will call us to start if no sign in is going to happen.
+ if (IsLocalSyncEnabled())
+ RequestStart();
+}
- // Initialize local device info.
- local_device_->Initialize(cache_guid, signin_scoped_device_id,
- blocking_pool_);
+void ProfileSyncService::OnSyncCycleCompleted(
+ const syncer::SyncCycleSnapshot& snapshot) {
+ DCHECK(thread_checker_.CalledOnValidThread());
- PostBackendInitialization();
-}
+ last_snapshot_ = snapshot;
-void ProfileSyncService::OnSyncCycleCompleted() {
UpdateLastSyncedTime();
- const syncer::SyncCycleSnapshot snapshot = GetLastCycleSnapshot();
+ if (!snapshot.poll_finish_time().is_null())
+ sync_prefs_.SetLastPollTime(snapshot.poll_finish_time());
+
if (IsDataTypeControllerRunning(syncer::SESSIONS) &&
snapshot.model_neutral_state().get_updates_request_types.Has(
syncer::SESSIONS) &&
@@ -1055,6 +1043,7 @@ void ProfileSyncService::OnSyncCycleCompleted() {
void ProfileSyncService::OnExperimentsChanged(
const syncer::Experiments& experiments) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (current_experiments_.Matches(experiments))
return;
@@ -1095,6 +1084,7 @@ AuthError ConnectionStatusToAuthError(syncer::ConnectionStatus status) {
void ProfileSyncService::OnConnectionStatusChange(
syncer::ConnectionStatus status) {
+ DCHECK(thread_checker_.CalledOnValidThread());
connection_status_update_time_ = base::Time::Now();
connection_status_ = status;
if (status == syncer::CONNECTION_AUTH_ERROR) {
@@ -1121,7 +1111,7 @@ void ProfileSyncService::OnConnectionStatusChange(
// further needs to be done at this point.
} else if (request_access_token_backoff_.failure_count() == 0) {
// First time request without delay. Currently invalid token is used
- // to initialize sync backend and we'll always end up here. We don't
+ // to initialize sync engine and we'll always end up here. We don't
// want to delay initialization.
request_access_token_backoff_.InformOfRequest(false);
RequestAccessToken();
@@ -1154,13 +1144,14 @@ void ProfileSyncService::OnConnectionStatusChange(
void ProfileSyncService::OnPassphraseRequired(
syncer::PassphraseRequiredReason reason,
const sync_pb::EncryptedData& pending_keys) {
- DCHECK(backend_.get());
- DCHECK(backend_->IsNigoriEnabled());
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(engine_);
+ DCHECK(engine_->IsNigoriEnabled());
// TODO(lipalani) : add this check to other locations as well.
if (HasUnrecoverableError()) {
// When unrecoverable error is detected we post a task to shutdown the
- // backend. The task might not have executed yet.
+ // engine. The task might not have executed yet.
return;
}
@@ -1180,6 +1171,7 @@ void ProfileSyncService::OnPassphraseRequired(
}
void ProfileSyncService::OnPassphraseAccepted() {
+ DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << "Received OnPassphraseAccepted.";
// If the pending keys were resolved via keystore, it's possible we never
@@ -1188,9 +1180,7 @@ void ProfileSyncService::OnPassphraseAccepted() {
cached_passphrase_.clear();
// Reset passphrase_required_reason_ since we know we no longer require the
- // passphrase. We do this here rather than down in ResolvePassphraseRequired()
- // because that can be called by OnPassphraseRequired() if no encrypted data
- // types are enabled, and we don't want to clobber the true passphrase error.
+ // passphrase.
passphrase_required_reason_ = syncer::REASON_PASSPHRASE_NOT_REQUIRED;
// Make sure the data types that depend on the passphrase are started at
@@ -1207,6 +1197,7 @@ void ProfileSyncService::OnPassphraseAccepted() {
void ProfileSyncService::OnEncryptedTypesChanged(
syncer::ModelTypeSet encrypted_types,
bool encrypt_everything) {
+ DCHECK(thread_checker_.CalledOnValidThread());
encrypted_types_ = encrypted_types;
encrypt_everything_ = encrypt_everything;
DCHECK(encrypt_everything_allowed_ || !encrypt_everything_);
@@ -1220,6 +1211,7 @@ void ProfileSyncService::OnEncryptedTypesChanged(
}
void ProfileSyncService::OnEncryptionComplete() {
+ DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << "Encryption complete";
if (encryption_pending_ && encrypt_everything_) {
encryption_pending_ = false;
@@ -1230,8 +1222,9 @@ void ProfileSyncService::OnEncryptionComplete() {
}
void ProfileSyncService::OnMigrationNeededForTypes(syncer::ModelTypeSet types) {
- DCHECK(backend_initialized_);
- DCHECK(data_type_manager_.get());
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(engine_initialized_);
+ DCHECK(data_type_manager_);
// Migrator must be valid, because we don't sync until it is created and this
// callback originates from a sync cycle.
@@ -1239,6 +1232,7 @@ void ProfileSyncService::OnMigrationNeededForTypes(syncer::ModelTypeSet types) {
}
void ProfileSyncService::OnActionableError(const SyncProtocolError& error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
last_actionable_error_ = error;
DCHECK_NE(last_actionable_error_.action, syncer::UNKNOWN_ACTION);
switch (error.action) {
@@ -1281,6 +1275,9 @@ void ProfileSyncService::OnActionableError(const SyncProtocolError& error) {
case syncer::RESET_LOCAL_SYNC_DATA:
ShutdownImpl(syncer::DISABLE_SYNC);
startup_controller_->TryStart();
+ UMA_HISTOGRAM_ENUMERATION(kClearServerDataEventsHistogramName,
+ CLEAR_SERVER_DATA_RESET_LOCAL_DATA_RECEIVED,
+ CLEAR_SERVER_DATA_MAX);
break;
default:
NOTREACHED();
@@ -1298,6 +1295,8 @@ void ProfileSyncService::OnLocalSetPassphraseEncryption(
// At this point the user has set a custom passphrase and we have received the
// updated nigori state. Time to cache the nigori state, and catch up the
// active data types.
+ UMA_HISTOGRAM_ENUMERATION(kClearServerDataEventsHistogramName,
+ CLEAR_SERVER_DATA_STARTED, CLEAR_SERVER_DATA_MAX);
sync_prefs_.SetNigoriSpecificsForPassphraseTransition(
nigori_state.nigori_specifics);
sync_prefs_.SetPassphraseEncryptionTransitionInProgress(true);
@@ -1318,7 +1317,7 @@ void ProfileSyncService::BeginConfigureCatchUpBeforeClear() {
void ProfileSyncService::ClearAndRestartSyncForPassphraseEncryption() {
DCHECK(thread_checker_.CalledOnValidThread());
- backend_->ClearServerData(
+ engine_->ClearServerData(
base::Bind(&ProfileSyncService::OnClearServerDataDone,
sync_enabled_weak_factory_.GetWeakPtr()));
}
@@ -1335,19 +1334,22 @@ void ProfileSyncService::OnClearServerDataDone() {
// nigori state.
ShutdownImpl(syncer::DISABLE_SYNC);
startup_controller_->TryStart();
+ UMA_HISTOGRAM_ENUMERATION(kClearServerDataEventsHistogramName,
+ CLEAR_SERVER_DATA_SUCCEEDED, CLEAR_SERVER_DATA_MAX);
}
void ProfileSyncService::OnConfigureDone(
const DataTypeManager::ConfigureResult& result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
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
- // OnBackendInitialized()).
+ // OnEngineInitialized()).
DCHECK(cached_passphrase_.empty());
if (!sync_configure_start_time_.is_null()) {
- if (result.status == DataTypeManager::OK) {
+ if (configure_status_ == DataTypeManager::OK) {
base::Time sync_configure_stop_time = base::Time::Now();
base::TimeDelta delta =
sync_configure_stop_time - sync_configure_start_time_;
@@ -1381,6 +1383,12 @@ void ProfileSyncService::OnConfigureDone(
// Handle unrecoverable error.
if (configure_status_ != DataTypeManager::OK) {
+ if (catch_up_configure_in_progress_) {
+ // Record catchup configuration failure.
+ UMA_HISTOGRAM_ENUMERATION(kClearServerDataEventsHistogramName,
+ CLEAR_SERVER_DATA_CATCHUP_FAILED,
+ CLEAR_SERVER_DATA_MAX);
+ }
// Something catastrophic had happened. We should only have one
// error representing it.
syncer::SyncError error = data_type_status_table_.GetUnrecoverableError();
@@ -1408,7 +1416,7 @@ void ProfileSyncService::OnConfigureDone(
// This must be done before we start syncing with the server to avoid
// sending unencrypted data up on a first time sync.
if (encryption_pending_)
- backend_->EnableEncryptEverything();
+ engine_->EnableEncryptEverything();
NotifyObservers();
if (migrator_.get() && migrator_->state() != BackendMigrator::IDLE) {
@@ -1429,19 +1437,21 @@ void ProfileSyncService::OnConfigureDone(
}
void ProfileSyncService::OnConfigureStart() {
+ DCHECK(thread_checker_.CalledOnValidThread());
sync_configure_start_time_ = base::Time::Now();
NotifyObservers();
}
ProfileSyncService::SyncStatusSummary
ProfileSyncService::QuerySyncStatusSummary() {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (HasUnrecoverableError()) {
return UNRECOVERABLE_ERROR;
- } else if (!backend_) {
+ } else if (!engine_) {
return NOT_ENABLED;
- } else if (backend_.get() && !IsFirstSetupComplete()) {
+ } else if (engine_ && !IsFirstSetupComplete()) {
return SETUP_INCOMPLETE;
- } else if (backend_ && IsFirstSetupComplete() && data_type_manager_ &&
+ } else if (engine_ && IsFirstSetupComplete() && data_type_manager_ &&
data_type_manager_->state() == DataTypeManager::STOPPED) {
return DATATYPES_NOT_INITIALIZED;
} else if (IsSyncActive()) {
@@ -1451,6 +1461,7 @@ ProfileSyncService::QuerySyncStatusSummary() {
}
std::string ProfileSyncService::QuerySyncStatusSummaryString() {
+ DCHECK(thread_checker_.CalledOnValidThread());
SyncStatusSummary status = QuerySyncStatusSummary();
std::string config_status_str =
@@ -1474,21 +1485,23 @@ std::string ProfileSyncService::QuerySyncStatusSummaryString() {
}
}
-std::string ProfileSyncService::GetBackendInitializationStateString() const {
- return startup_controller_->GetBackendInitializationStateString();
+std::string ProfileSyncService::GetEngineInitializationStateString() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return startup_controller_->GetEngineInitializationStateString();
}
bool ProfileSyncService::IsSetupInProgress() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return startup_controller_->IsSetupInProgress();
}
-bool ProfileSyncService::QueryDetailedSyncStatus(
- SyncBackendHost::Status* result) {
- if (backend_.get() && backend_initialized_) {
- *result = backend_->GetDetailedStatus();
+bool ProfileSyncService::QueryDetailedSyncStatus(SyncEngine::Status* result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (engine_ && engine_initialized_) {
+ *result = engine_->GetDetailedStatus();
return true;
} else {
- SyncBackendHost::Status status;
+ SyncEngine::Status status;
status.sync_protocol_error = last_actionable_error_;
*result = status;
return false;
@@ -1496,6 +1509,7 @@ bool ProfileSyncService::QueryDetailedSyncStatus(
}
const AuthError& ProfileSyncService::GetAuthError() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return last_auth_error_;
}
@@ -1504,11 +1518,13 @@ bool ProfileSyncService::CanConfigureDataTypes() const {
}
bool ProfileSyncService::IsFirstSetupInProgress() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return !IsFirstSetupComplete() && startup_controller_->IsSetupInProgress();
}
std::unique_ptr<syncer::SyncSetupInProgressHandle>
ProfileSyncService::GetSetupInProgressHandle() {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (++outstanding_setup_in_progress_handles_ == 1) {
DCHECK(!startup_controller_->IsSetupInProgress());
startup_controller_->SetSetupInProgress(true);
@@ -1523,17 +1539,25 @@ ProfileSyncService::GetSetupInProgressHandle() {
}
bool ProfileSyncService::IsSyncAllowed() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return IsSyncAllowedByFlag() && !IsManaged() && IsSyncAllowedByPlatform();
}
bool ProfileSyncService::IsSyncActive() const {
- return backend_initialized_ && data_type_manager_ &&
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return engine_initialized_ && data_type_manager_ &&
data_type_manager_->state() != DataTypeManager::STOPPED;
}
+bool ProfileSyncService::IsLocalSyncEnabled() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return sync_prefs_.IsLocalSyncEnabled();
+}
+
void ProfileSyncService::TriggerRefresh(const syncer::ModelTypeSet& types) {
- if (backend_initialized_)
- backend_->TriggerRefresh(types);
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (engine_initialized_)
+ engine_->TriggerRefresh(types);
}
bool ProfileSyncService::IsSignedIn() const {
@@ -1541,38 +1565,47 @@ bool ProfileSyncService::IsSignedIn() const {
return !signin_->GetAccountIdToUse().empty();
}
-bool ProfileSyncService::CanBackendStart() const {
+bool ProfileSyncService::CanEngineStart() const {
+ if (IsLocalSyncEnabled())
+ return true;
return CanSyncStart() && oauth2_token_service_ &&
oauth2_token_service_->RefreshTokenIsAvailable(
signin_->GetAccountIdToUse());
}
-bool ProfileSyncService::IsBackendInitialized() const {
- return backend_initialized_;
+bool ProfileSyncService::IsEngineInitialized() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return engine_initialized_;
}
bool ProfileSyncService::ConfigurationDone() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return data_type_manager_ &&
data_type_manager_->state() == DataTypeManager::CONFIGURED;
}
bool ProfileSyncService::waiting_for_auth() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return is_auth_in_progress_;
}
const syncer::Experiments& ProfileSyncService::current_experiments() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return current_experiments_;
}
bool ProfileSyncService::HasUnrecoverableError() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return unrecoverable_error_reason_ != ERROR_REASON_UNSET;
}
bool ProfileSyncService::IsPassphraseRequired() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return passphrase_required_reason_ != syncer::REASON_PASSPHRASE_NOT_REQUIRED;
}
bool ProfileSyncService::IsPassphraseRequiredForDecryption() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
// If there is an encrypted datatype enabled and we don't have the proper
// passphrase, we must prompt the user for a passphrase. The only way for the
// user to avoid entering their passphrase is to disable the encrypted types.
@@ -1580,6 +1613,7 @@ bool ProfileSyncService::IsPassphraseRequiredForDecryption() const {
}
base::string16 ProfileSyncService::GetLastSyncedTimeString() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
const base::Time last_synced_time = sync_prefs_.GetLastSyncedTime();
if (last_synced_time.is_null())
return l10n_util::GetStringUTF16(IDS_SYNC_TIME_NEVER);
@@ -1607,15 +1641,18 @@ void ProfileSyncService::UpdateSelectedTypesHistogram(
// the respective types in ModelType
const syncer::user_selectable_type::UserSelectableSyncType
user_selectable_types[] = {
- syncer::user_selectable_type::BOOKMARKS,
- syncer::user_selectable_type::PREFERENCES,
- syncer::user_selectable_type::PASSWORDS,
- syncer::user_selectable_type::AUTOFILL,
- syncer::user_selectable_type::THEMES,
- syncer::user_selectable_type::TYPED_URLS,
- syncer::user_selectable_type::EXTENSIONS,
- syncer::user_selectable_type::APPS,
- syncer::user_selectable_type::PROXY_TABS,
+ syncer::user_selectable_type::BOOKMARKS,
+ syncer::user_selectable_type::PREFERENCES,
+ syncer::user_selectable_type::PASSWORDS,
+ syncer::user_selectable_type::AUTOFILL,
+ syncer::user_selectable_type::THEMES,
+ syncer::user_selectable_type::TYPED_URLS,
+ syncer::user_selectable_type::EXTENSIONS,
+ syncer::user_selectable_type::APPS,
+#if BUILDFLAG(ENABLE_READING_LIST)
+ syncer::user_selectable_type::READING_LIST,
+#endif
+ syncer::user_selectable_type::PROXY_TABS,
};
static_assert(39 == syncer::MODEL_TYPE_COUNT,
@@ -1663,9 +1700,10 @@ void ProfileSyncService::RefreshSpareBootstrapToken(
void ProfileSyncService::OnUserChoseDatatypes(
bool sync_everything,
syncer::ModelTypeSet chosen_types) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(syncer::UserSelectableTypes().HasAll(chosen_types));
- if (!backend_.get() && !HasUnrecoverableError()) {
+ if (!engine_ && !HasUnrecoverableError()) {
NOTREACHED();
return;
}
@@ -1680,6 +1718,7 @@ void ProfileSyncService::OnUserChoseDatatypes(
void ProfileSyncService::ChangePreferredDataTypes(
syncer::ModelTypeSet preferred_types) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << "ChangePreferredDataTypes invoked";
const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
// Will only enable those types that are registered and preferred.
@@ -1690,6 +1729,7 @@ void ProfileSyncService::ChangePreferredDataTypes(
}
syncer::ModelTypeSet ProfileSyncService::GetActiveDataTypes() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (!IsSyncActive() || !ConfigurationDone())
return syncer::ModelTypeSet();
const syncer::ModelTypeSet preferred_types = GetPreferredDataTypes();
@@ -1699,10 +1739,12 @@ syncer::ModelTypeSet ProfileSyncService::GetActiveDataTypes() const {
}
syncer::SyncClient* ProfileSyncService::GetSyncClient() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return sync_client_.get();
}
syncer::ModelTypeSet ProfileSyncService::GetPreferredDataTypes() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
const syncer::ModelTypeSet preferred_types =
Union(sync_prefs_.GetPreferredDataTypes(registered_types),
@@ -1713,6 +1755,7 @@ syncer::ModelTypeSet ProfileSyncService::GetPreferredDataTypes() const {
}
syncer::ModelTypeSet ProfileSyncService::GetForcedDataTypes() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
// TODO(treib,zea): When SyncPrefs also implements SyncTypePreferenceProvider,
// we'll need another way to distinguish user-choosable types from
// programmatically-enabled types.
@@ -1720,6 +1763,7 @@ syncer::ModelTypeSet ProfileSyncService::GetForcedDataTypes() const {
}
syncer::ModelTypeSet ProfileSyncService::GetRegisteredDataTypes() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
syncer::ModelTypeSet registered_types;
// The data_type_controllers_ are determined by command-line flags;
// that's effectively what controls the values returned here.
@@ -1732,6 +1776,7 @@ syncer::ModelTypeSet ProfileSyncService::GetRegisteredDataTypes() const {
}
bool ProfileSyncService::IsUsingSecondaryPassphrase() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
syncer::PassphraseType passphrase_type = GetPassphraseType();
return passphrase_type ==
syncer::PassphraseType::FROZEN_IMPLICIT_PASSPHRASE ||
@@ -1739,6 +1784,7 @@ bool ProfileSyncService::IsUsingSecondaryPassphrase() const {
}
std::string ProfileSyncService::GetCustomPassphraseKey() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
syncer::SystemEncryptor encryptor;
syncer::Cryptographer cryptographer(&encryptor);
cryptographer.Bootstrap(sync_prefs_.GetEncryptionBootstrapToken());
@@ -1746,23 +1792,35 @@ std::string ProfileSyncService::GetCustomPassphraseKey() const {
}
syncer::PassphraseType ProfileSyncService::GetPassphraseType() const {
- return backend_->GetPassphraseType();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return engine_->GetPassphraseType();
}
base::Time ProfileSyncService::GetExplicitPassphraseTime() const {
- return backend_->GetExplicitPassphraseTime();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return engine_->GetExplicitPassphraseTime();
}
bool ProfileSyncService::IsCryptographerReady(
const syncer::BaseTransaction* trans) const {
- return backend_.get() && backend_->IsCryptographerReady(trans);
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return engine_ && engine_->IsCryptographerReady(trans);
}
void ProfileSyncService::SetPlatformSyncAllowedProvider(
const PlatformSyncAllowedProvider& platform_sync_allowed_provider) {
+ DCHECK(thread_checker_.CalledOnValidThread());
platform_sync_allowed_provider_ = platform_sync_allowed_provider;
}
+syncer::ModelTypeStoreFactory ProfileSyncService::GetModelTypeStoreFactory(
+ ModelType type) {
+ return base::Bind(&ModelTypeStore::CreateStore, type,
+ sync_data_folder_.Append(base::FilePath(kLevelDBFolderName))
+ .AsUTF8Unsafe(),
+ blocking_task_runner_);
+}
+
void ProfileSyncService::ConfigureDataTypeManager() {
// Don't configure datatypes if the setup UI is still on the screen - this
// is to help multi-screen setting UIs (like iOS) where they don't want to
@@ -1777,12 +1835,8 @@ void ProfileSyncService::ConfigureDataTypeManager() {
}
bool restart = false;
- if (!data_type_manager_) {
+ if (!migrator_) {
restart = true;
- data_type_manager_.reset(
- sync_client_->GetSyncApiComponentFactory()->CreateDataTypeManager(
- debug_info_listener_, &data_type_controllers_, this, backend_.get(),
- this));
// We create the migrator at the same time.
migrator_ = base::MakeUnique<BackendMigrator>(
@@ -1810,45 +1864,49 @@ void ProfileSyncService::ConfigureDataTypeManager() {
}
syncer::UserShare* ProfileSyncService::GetUserShare() const {
- if (backend_.get() && backend_initialized_) {
- return backend_->GetUserShare();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (engine_ && engine_initialized_) {
+ return engine_->GetUserShare();
}
NOTREACHED();
return nullptr;
}
syncer::SyncCycleSnapshot ProfileSyncService::GetLastCycleSnapshot() const {
- if (backend_)
- return backend_->GetLastCycleSnapshot();
- return syncer::SyncCycleSnapshot();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return last_snapshot_;
}
bool ProfileSyncService::HasUnsyncedItems() const {
- if (HasSyncingBackend() && backend_initialized_) {
- return backend_->HasUnsyncedItems();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (HasSyncingEngine() && engine_initialized_) {
+ return engine_->HasUnsyncedItems();
}
NOTREACHED();
return false;
}
BackendMigrator* ProfileSyncService::GetBackendMigratorForTest() {
+ DCHECK(thread_checker_.CalledOnValidThread());
return migrator_.get();
}
void ProfileSyncService::GetModelSafeRoutingInfo(
syncer::ModelSafeRoutingInfo* out) const {
- if (backend_.get() && backend_initialized_) {
- backend_->GetModelSafeRoutingInfo(out);
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (engine_ && engine_initialized_) {
+ engine_->GetModelSafeRoutingInfo(out);
} else {
NOTREACHED();
}
}
-base::Value* ProfileSyncService::GetTypeStatusMap() {
- std::unique_ptr<base::ListValue> result(new base::ListValue());
+std::unique_ptr<base::Value> ProfileSyncService::GetTypeStatusMap() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ auto result = base::MakeUnique<base::ListValue>();
- if (!backend_.get() || !backend_initialized_) {
- return result.release();
+ if (!engine_ || !engine_initialized_) {
+ return std::move(result);
}
DataTypeStatusTable::TypeErrorMap error_map =
@@ -1856,7 +1914,7 @@ base::Value* ProfileSyncService::GetTypeStatusMap() {
ModelTypeSet active_types;
ModelTypeSet passive_types;
ModelSafeRoutingInfo routing_info;
- backend_->GetModelSafeRoutingInfo(&routing_info);
+ engine_->GetModelSafeRoutingInfo(&routing_info);
for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin();
it != routing_info.end(); ++it) {
if (it->second == syncer::GROUP_PASSIVE) {
@@ -1866,7 +1924,7 @@ base::Value* ProfileSyncService::GetTypeStatusMap() {
}
}
- SyncBackendHost::Status detailed_status = backend_->GetDetailedStatus();
+ SyncEngine::Status detailed_status = engine_->GetDetailedStatus();
ModelTypeSet& throttled_types(detailed_status.throttled_types);
ModelTypeSet& backed_off_types(detailed_status.backed_off_types);
ModelTypeSet registered = GetRegisteredDataTypes();
@@ -1946,17 +2004,17 @@ base::Value* ProfileSyncService::GetTypeStatusMap() {
result->Append(std::move(type_status));
}
- return result.release();
+ return std::move(result);
}
void ProfileSyncService::ConsumeCachedPassphraseIfPossible() {
- // If no cached passphrase, or sync backend hasn't started up yet, just exit.
- // If the backend isn't running yet, OnBackendInitialized() will call this
- // method again after the backend starts up.
- if (cached_passphrase_.empty() || !IsBackendInitialized())
+ // If no cached passphrase, or sync engine hasn't started up yet, just exit.
+ // If the engine isn't running yet, OnEngineInitialized() will call this
+ // method again after the engine starts up.
+ if (cached_passphrase_.empty() || !IsEngineInitialized())
return;
- // Backend is up and running, so we can consume the cached passphrase.
+ // Engine is up and running, so we can consume the cached passphrase.
std::string passphrase = cached_passphrase_;
cached_passphrase_.clear();
@@ -2002,8 +2060,9 @@ void ProfileSyncService::RequestAccessToken() {
void ProfileSyncService::SetEncryptionPassphrase(const std::string& passphrase,
PassphraseType type) {
- // This should only be called when the backend has been initialized.
- DCHECK(IsBackendInitialized());
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // This should only be called when the engine has been initialized.
+ DCHECK(IsEngineInitialized());
DCHECK(!(type == IMPLICIT && IsUsingSecondaryPassphrase()))
<< "Data is already encrypted using an explicit passphrase";
DCHECK(!(type == EXPLICIT &&
@@ -2021,14 +2080,15 @@ void ProfileSyncService::SetEncryptionPassphrase(const std::string& passphrase,
passphrase_required_reason_ = syncer::REASON_PASSPHRASE_NOT_REQUIRED;
NotifyObservers();
}
- backend_->SetEncryptionPassphrase(passphrase, type == EXPLICIT);
+ engine_->SetEncryptionPassphrase(passphrase, type == EXPLICIT);
}
bool ProfileSyncService::SetDecryptionPassphrase(
const std::string& passphrase) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (IsPassphraseRequired()) {
DVLOG(1) << "Setting passphrase for decryption.";
- bool result = backend_->SetDecryptionPassphrase(passphrase);
+ bool result = engine_->SetDecryptionPassphrase(passphrase);
UMA_HISTOGRAM_BOOLEAN("Sync.PassphraseDecryptionSucceeded", result);
return result;
} else {
@@ -2039,21 +2099,24 @@ bool ProfileSyncService::SetDecryptionPassphrase(
}
bool ProfileSyncService::IsEncryptEverythingAllowed() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return encrypt_everything_allowed_;
}
void ProfileSyncService::SetEncryptEverythingAllowed(bool allowed) {
- DCHECK(allowed || !IsBackendInitialized() || !IsEncryptEverythingEnabled());
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(allowed || !IsEngineInitialized() || !IsEncryptEverythingEnabled());
encrypt_everything_allowed_ = allowed;
}
void ProfileSyncService::EnableEncryptEverything() {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(IsEncryptEverythingAllowed());
- // Tests override IsBackendInitialized() to always return true, so we
- // must check that instead of |backend_initialized_|.
+ // Tests override IsEngineInitialized() to always return true, so we
+ // must check that instead of |engine_initialized_|.
// TODO(akalin): Fix the above. :/
- DCHECK(IsBackendInitialized());
+ DCHECK(IsEngineInitialized());
// TODO(atwilson): Persist the encryption_pending_ flag to address the various
// problems around cancelling encryption in the background (crbug.com/119649).
if (!encrypt_everything_)
@@ -2061,6 +2124,7 @@ void ProfileSyncService::EnableEncryptEverything() {
}
bool ProfileSyncService::encryption_pending() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
// We may be called during the setup process before we're
// initialized (via IsEncryptedDatatypeEnabled and
// IsPassphraseRequiredForDecryption).
@@ -2068,11 +2132,13 @@ bool ProfileSyncService::encryption_pending() const {
}
bool ProfileSyncService::IsEncryptEverythingEnabled() const {
- DCHECK(backend_initialized_);
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(engine_initialized_);
return encrypt_everything_ || encryption_pending_;
}
syncer::ModelTypeSet ProfileSyncService::GetEncryptedDataTypes() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(encrypted_types_.Has(syncer::PASSWORDS));
// We may be called during the setup process before we're
// initialized. In this case, we default to the sensitive types.
@@ -2080,6 +2146,7 @@ syncer::ModelTypeSet ProfileSyncService::GetEncryptedDataTypes() const {
}
void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (is_sync_managed) {
StopImpl(CLEAR_DATA);
} else {
@@ -2091,17 +2158,18 @@ void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) {
void ProfileSyncService::GoogleSigninSucceeded(const std::string& account_id,
const std::string& username,
const std::string& password) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (IsSyncRequested() && !password.empty()) {
cached_passphrase_ = password;
- // Try to consume the passphrase we just cached. If the sync backend
+ // Try to consume the passphrase we just cached. If the sync engine
// is not running yet, the passphrase will remain cached until the
- // backend starts up.
+ // engine starts up.
ConsumeCachedPassphraseIfPossible();
}
#if defined(OS_CHROMEOS)
RefreshSpareBootstrapToken(password);
#endif
- if (!IsBackendInitialized() || GetAuthError().state() != AuthError::NONE) {
+ if (!IsEngineInitialized() || GetAuthError().state() != AuthError::NONE) {
// Track the fact that we're still waiting for auth to complete.
is_auth_in_progress_ = true;
}
@@ -2109,6 +2177,7 @@ void ProfileSyncService::GoogleSigninSucceeded(const std::string& account_id,
void ProfileSyncService::GoogleSignedOut(const std::string& account_id,
const std::string& username) {
+ DCHECK(thread_checker_.CalledOnValidThread());
sync_disabled_by_admin_ = false;
UMA_HISTOGRAM_ENUMERATION("Sync.StopSource", syncer::SIGN_OUT,
syncer::STOP_SOURCE_LIMIT);
@@ -2119,7 +2188,8 @@ void ProfileSyncService::OnGaiaAccountsInCookieUpdated(
const std::vector<gaia::ListedAccount>& accounts,
const std::vector<gaia::ListedAccount>& signed_out_accounts,
const GoogleServiceAuthError& error) {
- if (!IsBackendInitialized())
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!IsEngineInitialized())
return;
bool cookie_jar_mismatch = true;
@@ -2136,54 +2206,60 @@ void ProfileSyncService::OnGaiaAccountsInCookieUpdated(
DVLOG(1) << "Cookie jar mismatch: " << cookie_jar_mismatch;
DVLOG(1) << "Cookie jar empty: " << cookie_jar_empty;
- backend_->OnCookieJarChanged(cookie_jar_mismatch, cookie_jar_empty);
+ engine_->OnCookieJarChanged(cookie_jar_mismatch, cookie_jar_empty);
}
void ProfileSyncService::AddObserver(syncer::SyncServiceObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
observers_.AddObserver(observer);
}
void ProfileSyncService::RemoveObserver(syncer::SyncServiceObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
observers_.RemoveObserver(observer);
}
void ProfileSyncService::AddProtocolEventObserver(
ProtocolEventObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
protocol_event_observers_.AddObserver(observer);
- if (HasSyncingBackend()) {
- backend_->RequestBufferedProtocolEventsAndEnableForwarding();
+ if (HasSyncingEngine()) {
+ engine_->RequestBufferedProtocolEventsAndEnableForwarding();
}
}
void ProfileSyncService::RemoveProtocolEventObserver(
ProtocolEventObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
protocol_event_observers_.RemoveObserver(observer);
- if (HasSyncingBackend() &&
- !protocol_event_observers_.might_have_observers()) {
- backend_->DisableProtocolEventForwarding();
+ if (HasSyncingEngine() && !protocol_event_observers_.might_have_observers()) {
+ engine_->DisableProtocolEventForwarding();
}
}
void ProfileSyncService::AddTypeDebugInfoObserver(
syncer::TypeDebugInfoObserver* type_debug_info_observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
type_debug_info_observers_.AddObserver(type_debug_info_observer);
if (type_debug_info_observers_.might_have_observers() &&
- backend_initialized_) {
- backend_->EnableDirectoryTypeDebugInfoForwarding();
+ engine_initialized_) {
+ engine_->EnableDirectoryTypeDebugInfoForwarding();
}
}
void ProfileSyncService::RemoveTypeDebugInfoObserver(
syncer::TypeDebugInfoObserver* type_debug_info_observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
type_debug_info_observers_.RemoveObserver(type_debug_info_observer);
if (!type_debug_info_observers_.might_have_observers() &&
- backend_initialized_) {
- backend_->DisableDirectoryTypeDebugInfoForwarding();
+ engine_initialized_) {
+ engine_->DisableDirectoryTypeDebugInfoForwarding();
}
}
void ProfileSyncService::AddPreferenceProvider(
syncer::SyncTypePreferenceProvider* provider) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!HasPreferenceProvider(provider))
<< "Providers may only be added once!";
preference_providers_.insert(provider);
@@ -2191,6 +2267,7 @@ void ProfileSyncService::AddPreferenceProvider(
void ProfileSyncService::RemovePreferenceProvider(
syncer::SyncTypePreferenceProvider* provider) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(HasPreferenceProvider(provider))
<< "Only providers that have been added before can be removed!";
preference_providers_.erase(provider);
@@ -2198,6 +2275,7 @@ void ProfileSyncService::RemovePreferenceProvider(
bool ProfileSyncService::HasPreferenceProvider(
syncer::SyncTypePreferenceProvider* provider) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return preference_providers_.count(provider) > 0;
}
@@ -2262,13 +2340,14 @@ void GetAllNodesRequestHelper::OnReceivedNodesForType(
void ProfileSyncService::GetAllNodes(
const base::Callback<void(std::unique_ptr<base::ListValue>)>& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
ModelTypeSet all_types = GetActiveDataTypes();
all_types.PutAll(syncer::ControlTypes());
scoped_refptr<GetAllNodesRequestHelper> helper =
new GetAllNodesRequestHelper(all_types, callback);
- if (!backend_initialized_) {
- // If there's no backend available to fulfill the request, handle it here.
+ if (!engine_initialized_) {
+ // If there's no engine available to fulfill the request, handle it here.
for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
helper->OnReceivedNodesForType(it.Get(),
base::MakeUnique<base::ListValue>());
@@ -2293,13 +2372,16 @@ void ProfileSyncService::GetAllNodes(
bool ProfileSyncService::HasObserver(
const syncer::SyncServiceObserver* observer) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return observers_.HasObserver(observer);
}
base::WeakPtr<syncer::JsController> ProfileSyncService::GetJsController() {
+ DCHECK(thread_checker_.CalledOnValidThread());
return sync_js_controller_.AsWeakPtr();
}
+// static
void ProfileSyncService::SyncEvent(SyncEventCodes code) {
UMA_HISTOGRAM_ENUMERATION("Sync.EventCodes", code, MAX_SYNC_EVENT_CODE);
}
@@ -2311,30 +2393,36 @@ bool ProfileSyncService::IsSyncAllowedByFlag() {
}
bool ProfileSyncService::IsSyncAllowedByPlatform() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return platform_sync_allowed_provider_.is_null() ||
platform_sync_allowed_provider_.Run();
}
bool ProfileSyncService::IsManaged() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return sync_prefs_.IsManaged() || sync_disabled_by_admin_;
}
void ProfileSyncService::RequestStop(SyncStopDataFate data_fate) {
+ DCHECK(thread_checker_.CalledOnValidThread());
sync_prefs_.SetSyncRequested(false);
StopImpl(data_fate);
}
bool ProfileSyncService::IsSyncRequested() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return sync_prefs_.IsSyncRequested();
}
SigninManagerBase* ProfileSyncService::signin() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (!signin_)
return nullptr;
return signin_->GetOriginal();
}
void ProfileSyncService::RequestStart() {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (!IsSyncAllowed()) {
// Sync cannot be requested if it's not allowed.
return;
@@ -2348,10 +2436,11 @@ void ProfileSyncService::RequestStart() {
}
void ProfileSyncService::ReconfigureDatatypeManager() {
+ DCHECK(thread_checker_.CalledOnValidThread());
// If we haven't initialized yet, don't configure the DTM as it could cause
// association to start before a Directory has even been created.
- if (backend_initialized_) {
- DCHECK(backend_.get());
+ if (engine_initialized_) {
+ DCHECK(engine_);
ConfigureDataTypeManager();
} else if (HasUnrecoverableError()) {
// There is nothing more to configure. So inform the listeners,
@@ -2360,7 +2449,7 @@ void ProfileSyncService::ReconfigureDatatypeManager() {
DVLOG(1) << "ConfigureDataTypeManager not invoked because of an "
<< "Unrecoverable error.";
} else {
- DVLOG(0) << "ConfigureDataTypeManager not invoked because backend is not "
+ DVLOG(0) << "ConfigureDataTypeManager not invoked because engine is not "
<< "initialized";
}
}
@@ -2377,6 +2466,7 @@ syncer::ModelTypeSet ProfileSyncService::GetDataTypesFromPreferenceProviders()
}
const DataTypeStatusTable& ProfileSyncService::data_type_status_table() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return data_type_status_table_;
}
@@ -2391,31 +2481,33 @@ void ProfileSyncService::OnInternalUnrecoverableError(
}
bool ProfileSyncService::IsRetryingAccessTokenFetchForTest() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return request_access_token_retry_timer_.IsRunning();
}
std::string ProfileSyncService::GetAccessTokenForTest() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return access_token_;
}
-WeakHandle<syncer::JsEventHandler> ProfileSyncService::GetJsEventHandler() {
- return MakeWeakHandle(sync_js_controller_.AsWeakPtr());
-}
-
syncer::SyncableService* ProfileSyncService::GetSessionsSyncableService() {
+ DCHECK(thread_checker_.CalledOnValidThread());
return sessions_sync_manager_.get();
}
syncer::SyncableService* ProfileSyncService::GetDeviceInfoSyncableService() {
+ DCHECK(thread_checker_.CalledOnValidThread());
return device_info_sync_service_.get();
}
syncer::ModelTypeSyncBridge* ProfileSyncService::GetDeviceInfoSyncBridge() {
- return device_info_service_.get();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return device_info_sync_bridge_.get();
}
syncer::SyncService::SyncTokenStatus ProfileSyncService::GetSyncTokenStatus()
const {
+ DCHECK(thread_checker_.CalledOnValidThread());
SyncTokenStatus status;
status.connection_status_update_time = connection_status_update_time_;
status.connection_status = connection_status_;
@@ -2429,15 +2521,16 @@ syncer::SyncService::SyncTokenStatus ProfileSyncService::GetSyncTokenStatus()
void ProfileSyncService::OverrideNetworkResourcesForTest(
std::unique_ptr<syncer::NetworkResources> network_resources) {
+ DCHECK(thread_checker_.CalledOnValidThread());
network_resources_ = std::move(network_resources);
}
-bool ProfileSyncService::HasSyncingBackend() const {
- return backend_ != nullptr;
+bool ProfileSyncService::HasSyncingEngine() const {
+ return engine_ != nullptr;
}
void ProfileSyncService::UpdateFirstSyncTimePref() {
- if (!IsSignedIn()) {
+ if (!IsLocalSyncEnabled() && !IsSignedIn()) {
sync_prefs_.ClearFirstSyncTime();
} else if (sync_prefs_.GetFirstSyncTime().is_null()) {
// Set if not set before and it's syncing now.
@@ -2446,17 +2539,20 @@ void ProfileSyncService::UpdateFirstSyncTimePref() {
}
void ProfileSyncService::FlushDirectory() const {
- // backend_initialized_ implies backend_ isn't null and the manager exists.
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // engine_initialized_ implies engine_ isn't null and the manager exists.
// If sync is not initialized yet, we fail silently.
- if (backend_initialized_)
- backend_->FlushDirectory();
+ if (engine_initialized_)
+ engine_->FlushDirectory();
}
base::FilePath ProfileSyncService::GetDirectoryPathForTest() const {
- return directory_path_;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return sync_data_folder_;
}
base::MessageLoop* ProfileSyncService::GetSyncLoopForTest() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (sync_thread_) {
return sync_thread_->message_loop();
} else {
@@ -2465,12 +2561,13 @@ base::MessageLoop* ProfileSyncService::GetSyncLoopForTest() const {
}
void ProfileSyncService::RefreshTypesForTest(syncer::ModelTypeSet types) {
- if (backend_initialized_)
- backend_->RefreshTypesForTest(types);
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (engine_initialized_)
+ engine_->RefreshTypesForTest(types);
}
void ProfileSyncService::RemoveClientFromServer() const {
- if (!backend_initialized_)
+ if (!engine_initialized_)
return;
const std::string cache_guid = local_device_->GetLocalSyncCacheGUID();
std::string birthday;
@@ -2513,15 +2610,18 @@ void ProfileSyncService::ReportPreviousSessionMemoryWarningCount() {
}
const GURL& ProfileSyncService::sync_service_url() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return sync_service_url_;
}
std::string ProfileSyncService::unrecoverable_error_message() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return unrecoverable_error_message_;
}
tracked_objects::Location ProfileSyncService::unrecoverable_error_location()
const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return unrecoverable_error_location_;
}
@@ -2535,7 +2635,7 @@ void ProfileSyncService::OnSetupInProgressHandleDestroyed() {
DCHECK(startup_controller_->IsSetupInProgress());
startup_controller_->SetSetupInProgress(false);
- if (IsBackendInitialized())
+ if (IsEngineInitialized())
ReconfigureDatatypeManager();
NotifyObservers();
}
diff --git a/chromium/components/browser_sync/profile_sync_service.h b/chromium/components/browser_sync/profile_sync_service.h
index 9ef6d1869fc..9d7c709fc13 100644
--- a/chromium/components/browser_sync/profile_sync_service.h
+++ b/chromium/components/browser_sync/profile_sync_service.h
@@ -17,8 +17,10 @@
#include "base/macros.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
#include "base/observer_list.h"
#include "base/strings/string16.h"
+#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
@@ -35,18 +37,19 @@
#include "components/sync/driver/data_type_manager.h"
#include "components/sync/driver/data_type_manager_observer.h"
#include "components/sync/driver/data_type_status_table.h"
-#include "components/sync/driver/glue/sync_backend_host.h"
#include "components/sync/driver/startup_controller.h"
#include "components/sync/driver/sync_client.h"
-#include "components/sync/driver/sync_frontend.h"
-#include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/sync_service_base.h"
#include "components/sync/driver/sync_stopped_reporter.h"
#include "components/sync/engine/events/protocol_event_observer.h"
#include "components/sync/engine/model_safe_worker.h"
#include "components/sync/engine/net/network_time_update_callback.h"
#include "components/sync/engine/shutdown_reason.h"
+#include "components/sync/engine/sync_engine.h"
+#include "components/sync/engine/sync_engine_host.h"
#include "components/sync/engine/sync_manager_factory.h"
#include "components/sync/js/sync_js_controller.h"
+#include "components/sync/model/model_type_store.h"
#include "components/sync/syncable/user_share.h"
#include "components/version_info/version_info.h"
#include "google_apis/gaia/google_service_auth_error.h"
@@ -77,7 +80,6 @@ class DeviceInfoSyncService;
class DeviceInfoTracker;
class LocalDeviceInfoProvider;
class NetworkResources;
-class SyncApiComponentFactory;
class SyncClient;
class SyncErrorController;
class SyncTypePreferenceProvider;
@@ -92,11 +94,10 @@ struct UserShare;
namespace browser_sync {
// ProfileSyncService is the layer between browser subsystems like bookmarks,
-// and the sync backend. Each subsystem is logically thought of as being
-// a sync datatype.
-//
-// Individual datatypes can, at any point, be in a variety of stages of being
-// "enabled". Here are some specific terms for concepts used in this class:
+// and the sync engine. Each subsystem is logically thought of as being a sync
+// datatype. Individual datatypes can, at any point, be in a variety of stages
+// of being "enabled". Here are some specific terms for concepts used in this
+// class:
//
// 'Registered' (feature suppression for a datatype)
//
@@ -126,7 +127,7 @@ namespace browser_sync {
// synchronized: the syncer has been instructed to querying the server
// for this datatype, first-time merges have finished, and there is an
// actively installed ChangeProcessor that listens for changes to this
-// datatype, propagating such changes into and out of the sync backend
+// datatype, propagating such changes into and out of the sync engine
// as necessary.
//
// When a datatype is in the process of becoming active, it may be
@@ -170,8 +171,7 @@ namespace browser_sync {
// Once first setup has completed and there are no outstanding
// setup-in-progress handles, CanConfigureDataTypes() will return true and
// datatype configuration can begin.
-class ProfileSyncService : public syncer::SyncService,
- public syncer::SyncFrontend,
+class ProfileSyncService : public syncer::SyncServiceBase,
public syncer::SyncPrefObserver,
public syncer::DataTypeManagerObserver,
public syncer::UnrecoverableErrorHandler,
@@ -181,7 +181,7 @@ class ProfileSyncService : public syncer::SyncService,
public SigninManagerBase::Observer,
public GaiaCookieManagerService::Observer {
public:
- typedef syncer::SyncBackendHost::Status Status;
+ typedef syncer::SyncEngine::Status Status;
typedef base::Callback<bool(void)> PlatformSyncAllowedProvider;
enum SyncEventCodes {
@@ -232,8 +232,8 @@ class ProfileSyncService : public syncer::SyncService,
// explicitly defined.
struct InitParams {
InitParams();
+ InitParams(InitParams&& other);
~InitParams();
- InitParams(InitParams&& other); // NOLINT
std::unique_ptr<syncer::SyncClient> sync_client;
std::unique_ptr<SigninManagerWrapper> signin_wrapper;
@@ -245,7 +245,8 @@ class ProfileSyncService : public syncer::SyncService,
scoped_refptr<net::URLRequestContextGetter> url_request_context;
std::string debug_identifier;
version_info::Channel channel = version_info::Channel::UNKNOWN;
- base::SequencedWorkerPool* blocking_pool = nullptr;
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
+ base::FilePath local_sync_backend_folder;
private:
DISALLOW_COPY_AND_ASSIGN(InitParams);
@@ -263,6 +264,7 @@ class ProfileSyncService : public syncer::SyncService,
bool IsFirstSetupComplete() const override;
bool IsSyncAllowed() const override;
bool IsSyncActive() const override;
+ bool IsLocalSyncEnabled() const override;
void TriggerRefresh(const syncer::ModelTypeSet& types) override;
void OnDataTypeRequestsSyncStartup(syncer::ModelType type) override;
bool CanSyncStart() const override;
@@ -281,7 +283,7 @@ class ProfileSyncService : public syncer::SyncService,
bool ConfigurationDone() const override;
const GoogleServiceAuthError& GetAuthError() const override;
bool HasUnrecoverableError() const override;
- bool IsBackendInitialized() const override;
+ bool IsEngineInitialized() const override;
sync_sessions::OpenTabsUIDelegate* GetOpenTabsUIDelegate() override;
bool IsPassphraseRequiredForDecryption() const override;
base::Time GetExplicitPassphraseTime() const override;
@@ -306,9 +308,9 @@ class ProfileSyncService : public syncer::SyncService,
std::string QuerySyncStatusSummaryString() override;
bool QueryDetailedSyncStatus(syncer::SyncStatus* result) override;
base::string16 GetLastSyncedTimeString() const override;
- std::string GetBackendInitializationStateString() const override;
+ std::string GetEngineInitializationStateString() const override;
syncer::SyncCycleSnapshot GetLastCycleSnapshot() const override;
- base::Value* GetTypeStatusMap() override;
+ std::unique_ptr<base::Value> GetTypeStatusMap() override;
const GURL& sync_service_url() const override;
std::string unrecoverable_error_message() const override;
tracked_objects::Location unrecoverable_error_location() const override;
@@ -357,14 +359,15 @@ class ProfileSyncService : public syncer::SyncService,
// Called when asynchronous session restore has completed.
void OnSessionRestoreComplete();
- // SyncFrontend implementation.
- void OnBackendInitialized(
+ // SyncEngineHost implementation.
+ void OnEngineInitialized(
+ syncer::ModelTypeSet initial_types,
const syncer::WeakHandle<syncer::JsBackend>& js_backend,
const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
debug_info_listener,
const std::string& cache_guid,
bool success) override;
- void OnSyncCycleCompleted() override;
+ void OnSyncCycleCompleted(const syncer::SyncCycleSnapshot& snapshot) override;
void OnProtocolEvent(const syncer::ProtocolEvent& event) override;
void OnDirectoryTypeCommitCounterUpdated(
syncer::ModelType type,
@@ -415,10 +418,10 @@ class ProfileSyncService : public syncer::SyncService,
SyncStatusSummary QuerySyncStatusSummary();
// Reconfigures the data type manager with the latest enabled types.
- // Note: Does not initialize the backend if it is not already initialized.
+ // Note: Does not initialize the engine if it is not already initialized.
// This function needs to be called only after sync has been initialized
// (i.e.,only for reconfigurations). The reason we don't initialize the
- // backend is because if we had encountered an unrecoverable error we don't
+ // engine is because if we had encountered an unrecoverable error we don't
// want to startup once more.
// This function is called by |SetSetupInProgress|.
virtual void ReconfigureDatatypeManager();
@@ -455,7 +458,7 @@ class ProfileSyncService : public syncer::SyncService,
const std::string& message) override;
// The functions below (until ActivateDataType()) should only be
- // called if IsBackendInitialized() is true.
+ // called if IsEngineInitialized() is true.
// TODO(akalin): These two functions are used only by
// ProfileSyncServiceHarness. Figure out a different way to expose
@@ -483,7 +486,7 @@ class ProfileSyncService : public syncer::SyncService,
// Changes which data types we're going to be syncing to |preferred_types|.
// If it is running, the DataTypeManager will be instructed to reconfigure
- // the sync backend so that exactly these datatypes are actively synced. See
+ // the sync engine so that exactly these datatypes are actively synced. See
// class comment for more on what it means for a datatype to be Preferred.
virtual void ChangePreferredDataTypes(syncer::ModelTypeSet preferred_types);
@@ -578,6 +581,11 @@ class ProfileSyncService : public syncer::SyncService,
void SetPlatformSyncAllowedProvider(
const PlatformSyncAllowedProvider& platform_sync_allowed_provider);
+ // Returns a function for |type| that will create a ModelTypeStore that shares
+ // the sync LevelDB backend.
+ syncer::ModelTypeStoreFactory GetModelTypeStoreFactory(
+ syncer::ModelType type);
+
// Needed to test whether the directory is deleted properly.
base::FilePath GetDirectoryPathForTest() const;
@@ -588,48 +596,21 @@ class ProfileSyncService : public syncer::SyncService,
void RefreshTypesForTest(syncer::ModelTypeSet types);
protected:
- // Helper to install and configure a data type manager.
- void ConfigureDataTypeManager();
-
- // Shuts down the backend sync components.
- // |reason| dictates if syncing is being disabled or not, and whether
- // to claim ownership of sync thread from backend.
- void ShutdownImpl(syncer::ShutdownReason reason);
-
- // Return SyncCredentials from the OAuth2TokenService.
- syncer::SyncCredentials GetCredentials();
-
- virtual syncer::WeakHandle<syncer::JsEventHandler> GetJsEventHandler();
-
- // Helper method for managing encryption UI.
- bool IsEncryptedDatatypeEnabled() const;
-
- // Helper for OnUnrecoverableError.
- // TODO(tim): Use an enum for |delete_sync_database| here, in ShutdownImpl,
- // and in SyncBackendHost::Shutdown.
- void OnUnrecoverableErrorImpl(const tracked_objects::Location& from_here,
- const std::string& message,
- bool delete_sync_database);
-
- // This is a cache of the last authentication response we received from the
- // sync server. The UI queries this to display appropriate messaging to the
- // user.
- GoogleServiceAuthError last_auth_error_;
-
- // Our asynchronous backend to communicate with sync components living on
- // other threads.
- std::unique_ptr<syncer::SyncBackendHost> backend_;
-
- // Was the last SYNC_PASSPHRASE_REQUIRED notification sent because it
- // was required for encryption, decryption with a cached passphrase, or
- // because a new passphrase is required?
- syncer::PassphraseRequiredReason passphrase_required_reason_;
+ // SyncServiceBase implementation.
+ syncer::SyncCredentials GetCredentials() override;
+ syncer::WeakHandle<syncer::JsEventHandler> GetJsEventHandler() override;
+ syncer::SyncEngine::HttpPostProviderFactoryGetter
+ MakeHttpPostProviderFactoryGetter() override;
+ std::unique_ptr<syncer::SyncEncryptionHandler::NigoriState>
+ MoveSavedNigoriState() override;
+ syncer::WeakHandle<syncer::UnrecoverableErrorHandler>
+ GetUnrecoverableErrorHandler() override;
private:
enum UnrecoverableErrorReason {
ERROR_REASON_UNSET,
ERROR_REASON_SYNCER,
- ERROR_REASON_BACKEND_INIT_FAILURE,
+ ERROR_REASON_ENGINE_INIT_FAILURE,
ERROR_REASON_CONFIGURATION_RETRY,
ERROR_REASON_CONFIGURATION_FAILURE,
ERROR_REASON_ACTIONABLE_ERROR,
@@ -661,6 +642,24 @@ class ProfileSyncService : public syncer::SyncService,
friend class TestProfileSyncService;
+ // Helper to install and configure a data type manager.
+ void ConfigureDataTypeManager();
+
+ // Shuts down the engine sync components.
+ // |reason| dictates if syncing is being disabled or not, and whether
+ // to claim ownership of sync thread from engine.
+ void ShutdownImpl(syncer::ShutdownReason reason);
+
+ // Helper method for managing encryption UI.
+ bool IsEncryptedDatatypeEnabled() const;
+
+ // Helper for OnUnrecoverableError.
+ // TODO(tim): Use an enum for |delete_sync_database| here, in ShutdownImpl,
+ // and in SyncEngine::Shutdown.
+ void OnUnrecoverableErrorImpl(const tracked_objects::Location& from_here,
+ const std::string& message,
+ bool delete_sync_database);
+
// Stops the sync engine. Does NOT set IsSyncRequested to false. Use
// RequestStop for that. |data_fate| controls whether the local sync data is
// deleted or kept when the engine shuts down.
@@ -669,18 +668,13 @@ class ProfileSyncService : public syncer::SyncService,
// Update the last auth error and notify observers of error state.
void UpdateAuthErrorState(const GoogleServiceAuthError& error);
- // Puts the backend's sync scheduler into NORMAL mode.
+ // Puts the engine's sync scheduler into NORMAL mode.
// Called when configuration is complete.
void StartSyncingWithServer();
- // Called when we've determined that we don't need a passphrase (either
- // because OnPassphraseAccepted() was called, or because we've gotten a
- // OnPassphraseRequired() but no data types are enabled).
- void ResolvePassphraseRequired();
-
// During initial signin, ProfileSyncService caches the user's signin
// passphrase so it can be used to encrypt/decrypt data after sync starts up.
- // This routine is invoked once the backend has started up to use the
+ // This routine is invoked once the engine has started up to use the
// cached passphrase and clear it out when it is done.
void ConsumeCachedPassphraseIfPossible();
@@ -688,17 +682,7 @@ class ProfileSyncService : public syncer::SyncService,
// refresh token. This happens when a new OAuth2 login token is loaded and
// when sync server returns AUTH_ERROR which indicates it is time to refresh
// token.
- virtual void RequestAccessToken();
-
- // Return true if backend should start from a fresh sync DB.
- bool ShouldDeleteSyncFolder();
-
- // If |delete_sync_data_folder| is true, then this method will delete all
- // previous "Sync Data" folders. (useful if the folder is partial/corrupt).
- void InitializeBackend(bool delete_sync_data_folder);
-
- // Initializes the various settings from the command line.
- void InitSettings();
+ void RequestAccessToken();
// Sets the last synced time to the current time.
void UpdateLastSyncedTime();
@@ -711,8 +695,8 @@ class ProfileSyncService : public syncer::SyncService,
void ClearUnrecoverableError();
- // Starts up the backend sync components.
- virtual void StartUpSlowBackendComponents();
+ // Starts up the engine sync components.
+ virtual void StartUpSlowEngineComponents();
// Collects preferred sync data types from |preference_providers_|.
syncer::ModelTypeSet GetDataTypesFromPreferenceProviders() const;
@@ -736,24 +720,21 @@ class ProfileSyncService : public syncer::SyncService,
bool delete_sync_database,
UnrecoverableErrorReason reason);
- // Update UMA for syncing backend.
- void UpdateBackendInitUMA(bool success);
-
- // Various setup following backend initialization, mostly for syncing backend.
- void PostBackendInitialization();
+ // Update UMA for syncing engine.
+ void UpdateEngineInitUMA(bool success);
// Whether sync has been authenticated with an account ID.
bool IsSignedIn() const;
- // The backend can only start if sync can start and has an auth token. This is
- // different fron CanSyncStart because it represents whether the backend can
+ // The engine can only start if sync can start and has an auth token. This is
+ // different fron CanSyncStart because it represents whether the engine can
// be started at this moment, whereas CanSyncStart represents whether sync can
// conceptually start without further user action (acquiring a token is an
// automatic process).
- bool CanBackendStart() const;
+ bool CanEngineStart() const;
- // True if a syncing backend exists.
- bool HasSyncingBackend() const;
+ // True if a syncing engine exists.
+ bool HasSyncingEngine() const;
// Update first sync time stored in preferences
void UpdateFirstSyncTimePref();
@@ -778,7 +759,7 @@ class ProfileSyncService : public syncer::SyncService,
// Calls data type manager to start catch up configure.
void BeginConfigureCatchUpBeforeClear();
- // Calls sync backend to send ClearServerDataMessage to server.
+ // Calls sync engine to send ClearServerDataMessage to server.
void ClearAndRestartSyncForPassphraseEncryption();
// Restarts sync clearing directory in the process.
@@ -790,13 +771,18 @@ class ProfileSyncService : public syncer::SyncService,
// Called when a SetupInProgressHandle issued by this instance is destroyed.
virtual void OnSetupInProgressHandleDestroyed();
- // This profile's SyncClient, which abstracts away non-Sync dependencies and
- // the Sync API component factory.
- std::unique_ptr<syncer::SyncClient> sync_client_;
+ // This is a cache of the last authentication response we received from the
+ // sync server. The UI queries this to display appropriate messaging to the
+ // user.
+ GoogleServiceAuthError last_auth_error_;
+
+ // Cache of the last SyncCycleSnapshot received from the sync engine.
+ syncer::SyncCycleSnapshot last_snapshot_;
- // The class that handles getting, setting, and persisting sync
- // preferences.
- syncer::SyncPrefs sync_prefs_;
+ // Was the last SYNC_PASSPHRASE_REQUIRED notification sent because it
+ // was required for encryption, decryption with a cached passphrase, or
+ // because a new passphrase is required?
+ syncer::PassphraseRequiredReason passphrase_required_reason_;
// TODO(ncarter): Put this in a profile, once there is UI for it.
// This specifies where to find the sync server.
@@ -807,27 +793,17 @@ class ProfileSyncService : public syncer::SyncService,
// OnConfigureDone is called.
base::Time sync_configure_start_time_;
- // Callback to update the network time; used for initializing the backend.
+ // Callback to update the network time; used for initializing the engine.
syncer::NetworkTimeUpdateCallback network_time_update_callback_;
- // The path to the base directory under which sync should store its
- // information.
- base::FilePath base_directory_;
-
// The request context in which sync should operate.
scoped_refptr<net::URLRequestContextGetter> url_request_context_;
- // An identifier representing this instance for debugging purposes.
- std::string debug_identifier_;
-
- // The product channel of the embedder.
- version_info::Channel channel_;
-
- // Threading context.
- base::SequencedWorkerPool* blocking_pool_;
+ // The task runner to use for blocking IO operations.
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
// Indicates if this is the first time sync is being configured. This value
- // is equal to !IsFirstSetupComplete() at the time of OnBackendInitialized().
+ // is equal to !IsFirstSetupComplete() at the time of OnEngineInitialized().
bool is_first_time_sync_configure_;
// Number of UIs currently configuring the Sync service. When this number
@@ -837,21 +813,20 @@ class ProfileSyncService : public syncer::SyncService,
// List of available data type controllers.
syncer::DataTypeController::TypeMap data_type_controllers_;
- // Whether the SyncBackendHost has been initialized.
- bool backend_initialized_;
+ // Whether the SyncEngine has been initialized.
+ bool engine_initialized_;
// Set when sync receives DISABLED_BY_ADMIN error from server. Prevents
- // ProfileSyncService from starting backend till browser restarted or user
+ // ProfileSyncService from starting engine till browser restarted or user
// signed out.
bool sync_disabled_by_admin_;
// Set to true if a signin has completed but we're still waiting for the
- // backend to refresh its credentials.
+ // engine to refresh its credentials.
bool is_auth_in_progress_;
- // Encapsulates user signin - used to set/get the user's authenticated
- // email address.
- const std::unique_ptr<SigninManagerWrapper> signin_;
+ // The location where the local sync backend stores its data.
+ base::FilePath local_sync_backend_folder_;
// Information describing an unrecoverable error.
UnrecoverableErrorReason unrecoverable_error_reason_;
@@ -875,7 +850,7 @@ class ProfileSyncService : public syncer::SyncService,
bool expect_sync_configuration_aborted_;
// Sometimes we need to temporarily hold on to a passphrase because we don't
- // yet have a backend to send it to. This happens during initialization as
+ // yet have a engine to send it to. This happens during initialization as
// we don't StartUp until we have a valid token, which happens after valid
// credentials were provided.
std::string cached_passphrase_;
@@ -917,12 +892,6 @@ class ProfileSyncService : public syncer::SyncService,
// and association information.
syncer::WeakHandle<syncer::DataTypeDebugInfoListener> debug_info_listener_;
- // The thread where all the sync operations happen. This thread is kept alive
- // until browser shutdown and reused if sync is turned off and on again. It is
- // joined during the shutdown process, but there is an abort mechanism in
- // place to prevent slow HTTP requests from blocking browser shutdown.
- std::unique_ptr<base::Thread> sync_thread_;
-
// ProfileSyncService uses this service to get access tokens.
ProfileOAuth2TokenService* const oauth2_token_service_;
@@ -956,23 +925,20 @@ class ProfileSyncService : public syncer::SyncService,
// Locally owned SyncableService and ModelTypeSyncBridge implementations.
std::unique_ptr<sync_sessions::SessionsSyncManager> sessions_sync_manager_;
std::unique_ptr<syncer::DeviceInfoSyncService> device_info_sync_service_;
- std::unique_ptr<syncer::DeviceInfoSyncBridge> device_info_service_;
+ std::unique_ptr<syncer::DeviceInfoSyncBridge> device_info_sync_bridge_;
std::unique_ptr<syncer::NetworkResources> network_resources_;
StartBehavior start_behavior_;
std::unique_ptr<syncer::StartupController> startup_controller_;
- // The full path to the sync data directory.
- base::FilePath directory_path_;
-
std::unique_ptr<syncer::SyncStoppedReporter> sync_stopped_reporter_;
// Listens for the system being under memory pressure.
std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
// Nigori state after user switching to custom passphrase, saved until
- // transition steps complete. It will be injected into new backend after sync
+ // transition steps complete. It will be injected into new engine after sync
// restart.
std::unique_ptr<syncer::SyncEncryptionHandler::NigoriState>
saved_nigori_state_;
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 7304cdae651..6a37f1da985 100644
--- a/chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc
@@ -369,9 +369,11 @@ class WebDataServiceFake : public AutofillWebDataService {
DISALLOW_COPY_AND_ASSIGN(WebDataServiceFake);
};
-ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) {
- return new syncer::DataTypeManagerImpl(debug_listener, arg1, arg2, arg3,
- arg4);
+ACTION_P2(ReturnNewDataTypeManagerWithDebugListener,
+ sync_client,
+ debug_listener) {
+ return new syncer::DataTypeManagerImpl(sync_client, arg0, debug_listener,
+ arg2, arg3, arg4, arg5);
}
class MockPersonalDataManager : public PersonalDataManager {
@@ -476,8 +478,9 @@ class ProfileSyncServiceAutofillTest
CreateSyncService(std::move(sync_client_owned_), callback);
EXPECT_CALL(*profile_sync_service_bundle()->component_factory(),
- CreateDataTypeManager(_, _, _, _, _))
+ CreateDataTypeManager(_, _, _, _, _, _))
.WillOnce(ReturnNewDataTypeManagerWithDebugListener(
+ sync_client_,
syncer::MakeWeakHandle(debug_ptr_factory_.GetWeakPtr())));
EXPECT_CALL(personal_data_manager(), IsDataLoaded())
diff --git a/chromium/components/browser_sync/profile_sync_service_bookmark_unittest.cc b/chromium/components/browser_sync/profile_sync_service_bookmark_unittest.cc
index caecac85917..c593d78fa0f 100644
--- a/chromium/components/browser_sync/profile_sync_service_bookmark_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_bookmark_unittest.cc
@@ -28,6 +28,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_message_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/bookmarks/browser/base_bookmark_model_observer.h"
diff --git a/chromium/components/browser_sync/profile_sync_service_mock.h b/chromium/components/browser_sync/profile_sync_service_mock.h
index eea90410608..29c1591cde4 100644
--- a/chromium/components/browser_sync/profile_sync_service_mock.h
+++ b/chromium/components/browser_sync/profile_sync_service_mock.h
@@ -33,13 +33,14 @@ class ProfileSyncServiceMock : public ProfileSyncService {
virtual ~ProfileSyncServiceMock();
- MOCK_METHOD4(
- OnBackendInitialized,
- void(const syncer::WeakHandle<syncer::JsBackend>&,
+ MOCK_METHOD5(
+ OnEngineInitialized,
+ void(syncer::ModelTypeSet initial_types,
+ const syncer::WeakHandle<syncer::JsBackend>&,
const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&,
const std::string&,
bool));
- MOCK_METHOD0(OnSyncCycleCompleted, void());
+ MOCK_METHOD1(OnSyncCycleCompleted, void(const syncer::SyncCycleSnapshot&));
MOCK_METHOD2(OnUserChoseDatatypes,
void(bool sync_everything, syncer::ModelTypeSet chosen_types));
@@ -67,13 +68,13 @@ class ProfileSyncServiceMock : public ProfileSyncService {
MOCK_CONST_METHOD0(GetLastCycleSnapshot, syncer::SyncCycleSnapshot());
MOCK_METHOD1(QueryDetailedSyncStatus,
- bool(syncer::SyncBackendHost::Status* result));
+ bool(syncer::SyncEngine::Status* result));
MOCK_CONST_METHOD0(GetAuthError, const GoogleServiceAuthError&());
MOCK_CONST_METHOD0(IsFirstSetupInProgress, bool());
MOCK_CONST_METHOD0(GetLastSyncedTimeString, base::string16());
MOCK_CONST_METHOD0(HasUnrecoverableError, bool());
MOCK_CONST_METHOD0(IsSyncActive, bool());
- MOCK_CONST_METHOD0(IsBackendInitialized, bool());
+ MOCK_CONST_METHOD0(IsEngineInitialized, bool());
MOCK_CONST_METHOD0(IsSyncRequested, bool());
MOCK_CONST_METHOD0(waiting_for_auth, bool());
MOCK_METHOD1(OnActionableError, void(const syncer::SyncProtocolError&));
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 8a421875e11..dae10f171f1 100644
--- a/chromium/components/browser_sync/profile_sync_service_startup_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_startup_unittest.cc
@@ -18,9 +18,9 @@
#include "components/sync/base/pref_names.h"
#include "components/sync/driver/data_type_manager_mock.h"
#include "components/sync/driver/fake_data_type_controller.h"
-#include "components/sync/driver/glue/sync_backend_host_mock.h"
#include "components/sync/driver/sync_api_component_factory_mock.h"
#include "components/sync/driver/sync_service_observer.h"
+#include "components/sync/engine/fake_sync_engine.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "google_apis/gaia/gaia_auth_consumer.h"
#include "google_apis/gaia/gaia_constants.h"
@@ -30,7 +30,7 @@
using syncer::DataTypeManager;
using syncer::DataTypeManagerMock;
-using syncer::SyncBackendHostMock;
+using syncer::FakeSyncEngine;
using testing::_;
using testing::AnyNumber;
using testing::DoAll;
@@ -131,14 +131,14 @@ class ProfileSyncServiceStartupTest : public testing::Test {
DataTypeManagerMock* SetUpDataTypeManager() {
DataTypeManagerMock* data_type_manager = new DataTypeManagerMock();
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+ EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
.WillOnce(Return(data_type_manager));
return data_type_manager;
}
- SyncBackendHostMock* SetUpSyncBackendHost() {
- SyncBackendHostMock* sync_backend_host = new SyncBackendHostMock();
- EXPECT_CALL(*component_factory_, CreateSyncBackendHost(_, _, _, _))
+ FakeSyncEngine* SetUpSyncEngine() {
+ FakeSyncEngine* sync_backend_host = new FakeSyncEngine();
+ EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _))
.WillOnce(Return(sync_backend_host));
return sync_backend_host;
}
@@ -169,7 +169,7 @@ TEST_F(ProfileSyncServiceStartupTest, StartFirstTime) {
// We've never completed startup.
pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
CreateSyncService(ProfileSyncService::MANUAL_START);
- SetUpSyncBackendHost();
+ SetUpSyncEngine();
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(0);
@@ -213,7 +213,7 @@ TEST_F(ProfileSyncServiceStartupTest, DISABLED_StartNoCredentials) {
// Should not actually start, rather just clean things up and wait
// to be enabled.
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+ EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
.Times(0);
EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
sync_service_->Initialize();
@@ -243,7 +243,7 @@ TEST_F(ProfileSyncServiceStartupTest, DISABLED_StartNoCredentials) {
TEST_F(ProfileSyncServiceStartupTest, DISABLED_StartInvalidCredentials) {
CreateSyncService(ProfileSyncService::MANUAL_START);
std::string account_id = SimulateTestUserSignin(sync_service_.get());
- SyncBackendHostMock* mock_sbh = SetUpSyncBackendHost();
+ FakeSyncEngine* mock_sbh = SetUpSyncEngine();
// Tell the backend to stall while downloading control types (simulating an
// auth error).
@@ -275,9 +275,9 @@ TEST_F(ProfileSyncServiceStartupTest, DISABLED_StartInvalidCredentials) {
}
TEST_F(ProfileSyncServiceStartupCrosTest, StartCrosNoCredentials) {
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+ EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
.Times(0);
- EXPECT_CALL(*component_factory_, CreateSyncBackendHost(_, _, _, _)).Times(0);
+ EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _)).Times(0);
pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
@@ -291,7 +291,7 @@ TEST_F(ProfileSyncServiceStartupCrosTest, StartCrosNoCredentials) {
}
TEST_F(ProfileSyncServiceStartupCrosTest, StartFirstTime) {
- SetUpSyncBackendHost();
+ SetUpSyncEngine();
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
EXPECT_CALL(*data_type_manager, Configure(_, _));
@@ -312,7 +312,7 @@ TEST_F(ProfileSyncServiceStartupTest, StartNormal) {
CreateSyncService(ProfileSyncService::MANUAL_START);
std::string account_id = SimulateTestUserSignin(sync_service_.get());
sync_service_->SetFirstSetupComplete();
- SetUpSyncBackendHost();
+ SetUpSyncEngine();
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
EXPECT_CALL(*data_type_manager, Configure(_, _));
EXPECT_CALL(*data_type_manager, state())
@@ -342,7 +342,7 @@ TEST_F(ProfileSyncServiceStartupTest, StartRecoverDatatypePrefs) {
CreateSyncService(ProfileSyncService::MANUAL_START);
std::string account_id = SimulateTestUserSignin(sync_service_.get());
sync_service_->SetFirstSetupComplete();
- SetUpSyncBackendHost();
+ SetUpSyncEngine();
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
EXPECT_CALL(*data_type_manager, Configure(_, _));
EXPECT_CALL(*data_type_manager, state())
@@ -368,7 +368,7 @@ TEST_F(ProfileSyncServiceStartupTest, StartDontRecoverDatatypePrefs) {
CreateSyncService(ProfileSyncService::MANUAL_START);
std::string account_id = SimulateTestUserSignin(sync_service_.get());
sync_service_->SetFirstSetupComplete();
- SetUpSyncBackendHost();
+ SetUpSyncEngine();
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
EXPECT_CALL(*data_type_manager, Configure(_, _));
EXPECT_CALL(*data_type_manager, state())
@@ -389,7 +389,7 @@ TEST_F(ProfileSyncServiceStartupTest, ManagedStartup) {
// Disable sync through policy.
pref_service()->SetBoolean(syncer::prefs::kSyncManaged, true);
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+ EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
.Times(0);
EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
@@ -400,7 +400,7 @@ TEST_F(ProfileSyncServiceStartupTest, SwitchManaged) {
CreateSyncService(ProfileSyncService::MANUAL_START);
std::string account_id = SimulateTestUserSignin(sync_service_.get());
sync_service_->SetFirstSetupComplete();
- SetUpSyncBackendHost();
+ SetUpSyncEngine();
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
EXPECT_CALL(*data_type_manager, Configure(_, _));
EXPECT_CALL(*data_type_manager, state())
@@ -408,7 +408,7 @@ TEST_F(ProfileSyncServiceStartupTest, SwitchManaged) {
EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
IssueTestTokens(account_id);
sync_service_->Initialize();
- EXPECT_TRUE(sync_service_->IsBackendInitialized());
+ EXPECT_TRUE(sync_service_->IsEngineInitialized());
EXPECT_TRUE(sync_service_->IsSyncActive());
// The service should stop when switching to managed mode.
@@ -417,17 +417,17 @@ TEST_F(ProfileSyncServiceStartupTest, SwitchManaged) {
.WillOnce(Return(DataTypeManager::CONFIGURED));
EXPECT_CALL(*data_type_manager, Stop()).Times(1);
pref_service()->SetBoolean(syncer::prefs::kSyncManaged, true);
- EXPECT_FALSE(sync_service_->IsBackendInitialized());
+ EXPECT_FALSE(sync_service_->IsEngineInitialized());
// Note that PSS no longer references |data_type_manager| after stopping.
// When switching back to unmanaged, the state should change but sync should
// not start automatically because IsFirstSetupComplete() will be false.
// A new DataTypeManager should not be created.
Mock::VerifyAndClearExpectations(data_type_manager);
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+ EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
.Times(0);
pref_service()->ClearPref(syncer::prefs::kSyncManaged);
- EXPECT_FALSE(sync_service_->IsBackendInitialized());
+ EXPECT_FALSE(sync_service_->IsEngineInitialized());
EXPECT_FALSE(sync_service_->IsSyncActive());
}
@@ -435,7 +435,7 @@ TEST_F(ProfileSyncServiceStartupTest, StartFailure) {
CreateSyncService(ProfileSyncService::MANUAL_START);
std::string account_id = SimulateTestUserSignin(sync_service_.get());
sync_service_->SetFirstSetupComplete();
- SetUpSyncBackendHost();
+ SetUpSyncEngine();
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
DataTypeManager::ConfigureStatus status = DataTypeManager::ABORTED;
DataTypeManager::ConfigureResult result(status, syncer::ModelTypeSet());
@@ -459,7 +459,7 @@ TEST_F(ProfileSyncServiceStartupTest, StartDownloadFailed) {
// Pre load the tokens
CreateSyncService(ProfileSyncService::MANUAL_START);
std::string account_id = SimulateTestUserSignin(sync_service_.get());
- SyncBackendHostMock* mock_sbh = SetUpSyncBackendHost();
+ FakeSyncEngine* mock_sbh = SetUpSyncEngine();
mock_sbh->set_fail_initial_download(true);
pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
diff --git a/chromium/components/browser_sync/profile_sync_service_typed_url_unittest.cc b/chromium/components/browser_sync/profile_sync_service_typed_url_unittest.cc
index bab7b5be9de..440cd85be7e 100644
--- a/chromium/components/browser_sync/profile_sync_service_typed_url_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_typed_url_unittest.cc
@@ -60,8 +60,9 @@ const char kDummySavingBrowserHistoryDisabled[] = "dummyPref";
// Visits with this timestamp are treated as expired.
static const int EXPIRED_VISIT = -1;
-ACTION(ReturnNewDataTypeManager) {
- return new syncer::DataTypeManagerImpl(arg0, arg1, arg2, arg3, arg4);
+ACTION_P(ReturnNewDataTypeManager, sync_client) {
+ return new syncer::DataTypeManagerImpl(sync_client, arg0, arg1, arg2, arg3,
+ arg4, arg5);
}
class HistoryBackendMock : public HistoryBackend {
@@ -247,10 +248,10 @@ class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest {
SigninManagerBase* signin =
profile_sync_service_bundle()->signin_manager();
signin->SetAuthenticatedAccountInfo("gaia_id", "test");
- CreateSyncService(std::move(sync_client_), callback);
EXPECT_CALL(*profile_sync_service_bundle()->component_factory(),
- CreateDataTypeManager(_, _, _, _, _))
- .WillOnce(ReturnNewDataTypeManager());
+ CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(ReturnNewDataTypeManager(sync_client_.get()));
+ CreateSyncService(std::move(sync_client_), callback);
profile_sync_service_bundle()->auth_service()->UpdateCredentials(
account_id, "oauth2_login_token");
diff --git a/chromium/components/browser_sync/profile_sync_service_unittest.cc b/chromium/components/browser_sync/profile_sync_service_unittest.cc
index 6832d2cc173..916d3b3e98f 100644
--- a/chromium/components/browser_sync/profile_sync_service_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_unittest.cc
@@ -27,11 +27,11 @@
#include "components/strings/grit/components_strings.h"
#include "components/sync/base/pref_names.h"
#include "components/sync/driver/fake_data_type_controller.h"
-#include "components/sync/driver/glue/sync_backend_host_mock.h"
#include "components/sync/driver/sync_api_component_factory_mock.h"
#include "components/sync/driver/sync_driver_switches.h"
#include "components/sync/driver/sync_service_observer.h"
#include "components/sync/driver/sync_util.h"
+#include "components/sync/engine/fake_sync_engine.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/version_info/version_info_values.h"
#include "google_apis/gaia/gaia_constants.h"
@@ -40,7 +40,7 @@
#include "ui/base/l10n/l10n_util.h"
using syncer::DataTypeController;
-using syncer::SyncBackendHostMock;
+using syncer::FakeSyncEngine;
using syncer::SyncMergeResult;
using testing::Return;
@@ -99,73 +99,36 @@ class TestSyncServiceObserver : public syncer::SyncServiceObserver {
bool setup_in_progress_;
};
-// A variant of the SyncBackendHostMock that won't automatically
-// call back when asked to initialized. Allows us to test things
-// that could happen while backend init is in progress.
-class SyncBackendHostNoReturn : public SyncBackendHostMock {
- void Initialize(
- syncer::SyncFrontend* frontend,
- base::Thread* sync_thread,
- const syncer::WeakHandle<syncer::JsEventHandler>& event_handler,
- const GURL& service_url,
- const std::string& sync_user_agent,
- const syncer::SyncCredentials& credentials,
- bool delete_sync_data_folder,
- bool enable_local_sync_backend,
- const base::FilePath& local_sync_backend_folder,
- std::unique_ptr<syncer::SyncManagerFactory> sync_manager_factory,
- const syncer::WeakHandle<syncer::UnrecoverableErrorHandler>&
- unrecoverable_error_handler,
- const base::Closure& report_unrecoverable_error_function,
- const HttpPostProviderFactoryGetter& http_post_provider_factory_getter,
- std::unique_ptr<syncer::SyncEncryptionHandler::NigoriState>
- saved_nigori_state) override {}
+// A variant of the FakeSyncEngine that won't automatically call back when asked
+// to initialized. Allows us to test things that could happen while backend init
+// is in progress.
+class SyncEngineNoReturn : public FakeSyncEngine {
+ void Initialize(InitParams params) override {}
};
-class SyncBackendHostMockCollectDeleteDirParam : public SyncBackendHostMock {
+class FakeSyncEngineCollectDeleteDirParam : public FakeSyncEngine {
public:
- explicit SyncBackendHostMockCollectDeleteDirParam(
+ explicit FakeSyncEngineCollectDeleteDirParam(
std::vector<bool>* delete_dir_param)
: delete_dir_param_(delete_dir_param) {}
- void Initialize(
- syncer::SyncFrontend* frontend,
- base::Thread* sync_thread,
- const syncer::WeakHandle<syncer::JsEventHandler>& event_handler,
- const GURL& service_url,
- const std::string& sync_user_agent,
- const syncer::SyncCredentials& credentials,
- bool delete_sync_data_folder,
- bool enable_local_sync_backend,
- const base::FilePath& local_sync_backend_folder,
- std::unique_ptr<syncer::SyncManagerFactory> sync_manager_factory,
- const syncer::WeakHandle<syncer::UnrecoverableErrorHandler>&
- unrecoverable_error_handler,
- const base::Closure& report_unrecoverable_error_function,
- const HttpPostProviderFactoryGetter& http_post_provider_factory_getter,
- std::unique_ptr<syncer::SyncEncryptionHandler::NigoriState>
- saved_nigori_state) override {
- delete_dir_param_->push_back(delete_sync_data_folder);
- SyncBackendHostMock::Initialize(
- frontend, sync_thread, event_handler, service_url, sync_user_agent,
- credentials, delete_sync_data_folder, enable_local_sync_backend,
- local_sync_backend_folder, std::move(sync_manager_factory),
- unrecoverable_error_handler, report_unrecoverable_error_function,
- http_post_provider_factory_getter, std::move(saved_nigori_state));
+ void Initialize(InitParams params) override {
+ delete_dir_param_->push_back(params.delete_sync_data_folder);
+ FakeSyncEngine::Initialize(std::move(params));
}
private:
std::vector<bool>* delete_dir_param_;
};
-// SyncBackendHostMock that calls an external callback when ClearServerData is
+// FakeSyncEngine that calls an external callback when ClearServerData is
// called.
-class SyncBackendHostCaptureClearServerData : public SyncBackendHostMock {
+class SyncEngineCaptureClearServerData : public FakeSyncEngine {
public:
typedef base::Callback<void(
const syncer::SyncManager::ClearServerDataCallback&)>
ClearServerDataCalled;
- explicit SyncBackendHostCaptureClearServerData(
+ explicit SyncEngineCaptureClearServerData(
const ClearServerDataCalled& clear_server_data_called)
: clear_server_data_called_(clear_server_data_called) {}
@@ -178,16 +141,16 @@ class SyncBackendHostCaptureClearServerData : public SyncBackendHostMock {
ClearServerDataCalled clear_server_data_called_;
};
-ACTION(ReturnNewSyncBackendHostMock) {
- return new SyncBackendHostMock();
+ACTION(ReturnNewFakeSyncEngine) {
+ return new FakeSyncEngine();
}
-ACTION(ReturnNewSyncBackendHostNoReturn) {
- return new SyncBackendHostNoReturn();
+ACTION(ReturnNewSyncEngineNoReturn) {
+ return new SyncEngineNoReturn();
}
ACTION_P(ReturnNewMockHostCollectDeleteDirParam, delete_dir_param) {
- return new SyncBackendHostMockCollectDeleteDirParam(delete_dir_param);
+ return new FakeSyncEngineCollectDeleteDirParam(delete_dir_param);
}
void OnClearServerDataCalled(
@@ -197,7 +160,7 @@ void OnClearServerDataCalled(
}
ACTION_P(ReturnNewMockHostCaptureClearServerData, captured_callback) {
- return new SyncBackendHostCaptureClearServerData(base::Bind(
+ return new SyncEngineCaptureClearServerData(base::Bind(
&OnClearServerDataCalled, base::Unretained(captured_callback)));
}
@@ -206,10 +169,10 @@ void DoNothing(DataTypeController::ConfigureResult ignored1,
const SyncMergeResult& ignored3) {}
// A test harness that uses a real ProfileSyncService and in most cases a
-// MockSyncBackendHost.
+// MockSyncEngine.
//
// This is useful if we want to test the ProfileSyncService and don't care about
-// testing the SyncBackendHost.
+// testing the SyncEngine.
class ProfileSyncServiceTest : public ::testing::Test {
protected:
ProfileSyncServiceTest() : component_factory_(nullptr) {}
@@ -248,14 +211,24 @@ class ProfileSyncServiceTest : public ::testing::Test {
base::MakeUnique<syncer::FakeDataTypeController>(syncer::BOOKMARKS));
}
-#if defined(OS_WIN) || defined(OS_MACOSX) || \
- (defined(OS_LINUX) && !defined(OS_CHROMEOS))
- void CreateServiceWithoutSignIn() {
- CreateService(ProfileSyncService::AUTO_START);
- signin_manager()->SignOut(signin_metrics::SIGNOUT_TEST,
- signin_metrics::SignoutDelete::IGNORE_METRIC);
+ void CreateServiceWithLocalSyncBackend() {
+ component_factory_ = profile_sync_service_bundle_.component_factory();
+ ProfileSyncServiceBundle::SyncClientBuilder builder(
+ &profile_sync_service_bundle_);
+ ProfileSyncService::InitParams init_params =
+ profile_sync_service_bundle_.CreateBasicInitParams(
+ ProfileSyncService::AUTO_START, builder.Build());
+
+ prefs()->SetBoolean(syncer::prefs::kEnableLocalSyncBackend, true);
+ init_params.local_sync_backend_folder =
+ base::FilePath(FILE_PATH_LITERAL("dummyPath"));
+ init_params.oauth2_token_service = nullptr;
+ init_params.gaia_cookie_manager_service = nullptr;
+
+ service_ = base::MakeUnique<ProfileSyncService>(std::move(init_params));
+ service_->RegisterDataTypeController(
+ base::MakeUnique<syncer::FakeDataTypeController>(syncer::BOOKMARKS));
}
-#endif
void ShutdownAndDeleteService() {
if (service_)
@@ -309,36 +282,36 @@ class ProfileSyncServiceTest : public ::testing::Test {
void ExpectDataTypeManagerCreation(
int times,
const FakeDataTypeManager::ConfigureCalled& callback) {
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+ EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
.Times(times)
.WillRepeatedly(ReturnNewDataTypeManager(callback));
}
- void ExpectSyncBackendHostCreation(int times) {
- EXPECT_CALL(*component_factory_, CreateSyncBackendHost(_, _, _, _))
+ void ExpectSyncEngineCreation(int times) {
+ EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _))
.Times(times)
- .WillRepeatedly(ReturnNewSyncBackendHostMock());
+ .WillRepeatedly(ReturnNewFakeSyncEngine());
}
- void ExpectSyncBackendHostCreationCollectDeleteDir(
+ void ExpectSyncEngineCreationCollectDeleteDir(
int times,
std::vector<bool>* delete_dir_param) {
- EXPECT_CALL(*component_factory_, CreateSyncBackendHost(_, _, _, _))
+ EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _))
.Times(times)
.WillRepeatedly(
ReturnNewMockHostCollectDeleteDirParam(delete_dir_param));
}
- void ExpectSyncBackendHostCreationCaptureClearServerData(
+ void ExpectSyncEngineCreationCaptureClearServerData(
syncer::SyncManager::ClearServerDataCallback* captured_callback) {
- EXPECT_CALL(*component_factory_, CreateSyncBackendHost(_, _, _, _))
+ EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _))
.Times(1)
.WillOnce(ReturnNewMockHostCaptureClearServerData(captured_callback));
}
- void PrepareDelayedInitSyncBackendHost() {
- EXPECT_CALL(*component_factory_, CreateSyncBackendHost(_, _, _, _))
- .WillOnce(ReturnNewSyncBackendHostNoReturn());
+ void PrepareDelayedInitSyncEngine() {
+ EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _))
+ .WillOnce(ReturnNewSyncEngineNoReturn());
}
AccountTrackerService* account_tracker() {
@@ -403,7 +376,20 @@ TEST_F(ProfileSyncServiceTest, SuccessfulInitialization) {
IssueTestTokens();
CreateService(ProfileSyncService::AUTO_START);
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
+ InitializeForNthSync();
+ EXPECT_FALSE(service()->IsManaged());
+ EXPECT_TRUE(service()->IsSyncActive());
+}
+
+// Verify a successful initialization.
+TEST_F(ProfileSyncServiceTest, SuccessfulLocalBackendInitialization) {
+ prefs()->SetManagedPref(syncer::prefs::kSyncManaged,
+ new base::FundamentalValue(false));
+ IssueTestTokens();
+ CreateServiceWithLocalSyncBackend();
+ ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
+ ExpectSyncEngineCreation(1);
InitializeForNthSync();
EXPECT_FALSE(service()->IsManaged());
EXPECT_TRUE(service()->IsSyncActive());
@@ -463,7 +449,7 @@ TEST_F(ProfileSyncServiceTest, DisabledByPolicyAfterInit) {
IssueTestTokens();
CreateService(ProfileSyncService::AUTO_START);
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
InitializeForNthSync();
EXPECT_FALSE(service()->IsManaged());
@@ -480,7 +466,7 @@ TEST_F(ProfileSyncServiceTest, DisabledByPolicyAfterInit) {
// before the backend initialize call returns.
TEST_F(ProfileSyncServiceTest, AbortedByShutdown) {
CreateService(ProfileSyncService::AUTO_START);
- PrepareDelayedInitSyncBackendHost();
+ PrepareDelayedInitSyncEngine();
IssueTestTokens();
InitializeForNthSync();
@@ -494,7 +480,7 @@ TEST_F(ProfileSyncServiceTest, EarlyRequestStop) {
CreateService(ProfileSyncService::AUTO_START);
IssueTestTokens();
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
service()->RequestStop(ProfileSyncService::KEEP_DATA);
EXPECT_FALSE(service()->IsSyncRequested());
@@ -515,7 +501,7 @@ TEST_F(ProfileSyncServiceTest, DisableAndEnableSyncTemporarily) {
CreateService(ProfileSyncService::AUTO_START);
IssueTestTokens();
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
InitializeForNthSync();
EXPECT_TRUE(service()->IsSyncActive());
@@ -528,7 +514,7 @@ TEST_F(ProfileSyncServiceTest, DisableAndEnableSyncTemporarily) {
EXPECT_TRUE(prefs()->GetBoolean(syncer::prefs::kSyncSuppressStart));
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
service()->RequestStart();
EXPECT_TRUE(service()->IsSyncActive());
@@ -541,7 +527,7 @@ TEST_F(ProfileSyncServiceTest, DisableAndEnableSyncTemporarily) {
TEST_F(ProfileSyncServiceTest, EnableSyncAndSignOut) {
CreateService(ProfileSyncService::AUTO_START);
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
IssueTestTokens();
InitializeForNthSync();
@@ -558,7 +544,7 @@ TEST_F(ProfileSyncServiceTest, GetSyncTokenStatus) {
CreateService(ProfileSyncService::AUTO_START);
IssueTestTokens();
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
InitializeForNthSync();
// Initial status.
@@ -595,7 +581,7 @@ TEST_F(ProfileSyncServiceTest, RevokeAccessTokenFromTokenService) {
CreateService(ProfileSyncService::AUTO_START);
IssueTestTokens();
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
InitializeForNthSync();
EXPECT_TRUE(service()->IsSyncActive());
@@ -624,7 +610,7 @@ TEST_F(ProfileSyncServiceTest, SignOutRevokeAccessToken) {
CreateService(ProfileSyncService::AUTO_START);
IssueTestTokens();
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
InitializeForNthSync();
EXPECT_TRUE(service()->IsSyncActive());
@@ -645,7 +631,7 @@ TEST_F(ProfileSyncServiceTest, ClearDataOnSignOut) {
IssueTestTokens();
CreateService(ProfileSyncService::AUTO_START);
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
InitializeForNthSync();
EXPECT_TRUE(service()->IsSyncActive());
EXPECT_EQ(l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW),
@@ -677,7 +663,7 @@ TEST_F(ProfileSyncServiceTest, MemoryPressureRecording) {
CreateService(ProfileSyncService::AUTO_START);
IssueTestTokens();
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
InitializeForNthSync();
EXPECT_TRUE(service()->IsSyncActive());
@@ -725,10 +711,10 @@ TEST_F(ProfileSyncServiceTest, OnLocalSetPassphraseEncryption) {
syncer::SyncManager::ClearServerDataCallback captured_callback;
syncer::ConfigureReason configure_reason = syncer::CONFIGURE_REASON_UNKNOWN;
- // Initialize sync, ensure that both DataTypeManager and SyncBackendHost are
+ // Initialize sync, ensure that both DataTypeManager and SyncEngine are
// initialized and DTM::Configure is called with
// CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE.
- ExpectSyncBackendHostCreationCaptureClearServerData(&captured_callback);
+ ExpectSyncEngineCreationCaptureClearServerData(&captured_callback);
ExpectDataTypeManagerCreation(
1, GetRecordingConfigureCalledCallback(&configure_reason));
InitializeForNthSync();
@@ -754,7 +740,7 @@ TEST_F(ProfileSyncServiceTest, OnLocalSetPassphraseEncryption) {
// Once SBH::ClearServerData finishes successfully ensure that sync is
// restarted.
configure_reason = syncer::CONFIGURE_REASON_UNKNOWN;
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
ExpectDataTypeManagerCreation(
1, GetRecordingConfigureCalledCallback(&configure_reason));
captured_callback.Run();
@@ -773,7 +759,7 @@ TEST_F(ProfileSyncServiceTest,
switches::kSyncClearDataOnPassphraseEncryption);
IssueTestTokens();
CreateService(ProfileSyncService::AUTO_START);
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
ExpectDataTypeManagerCreation(
1, GetRecordingConfigureCalledCallback(&configure_reason));
InitializeForNthSync();
@@ -792,7 +778,7 @@ TEST_F(ProfileSyncServiceTest,
// Simulate browser restart. First configuration is a regular one.
service()->Shutdown();
syncer::SyncManager::ClearServerDataCallback captured_callback;
- ExpectSyncBackendHostCreationCaptureClearServerData(&captured_callback);
+ ExpectSyncEngineCreationCaptureClearServerData(&captured_callback);
ExpectDataTypeManagerCreation(
1, GetRecordingConfigureCalledCallback(&configure_reason));
service()->RequestStart();
@@ -810,7 +796,7 @@ TEST_F(ProfileSyncServiceTest,
service()->OnConfigureDone(result);
EXPECT_FALSE(captured_callback.is_null());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
ExpectDataTypeManagerCreation(
1, GetRecordingConfigureCalledCallback(&configure_reason));
captured_callback.Run();
@@ -829,7 +815,7 @@ TEST_F(ProfileSyncServiceTest,
switches::kSyncClearDataOnPassphraseEncryption);
IssueTestTokens();
CreateService(ProfileSyncService::AUTO_START);
- ExpectSyncBackendHostCreationCaptureClearServerData(&captured_callback);
+ ExpectSyncEngineCreationCaptureClearServerData(&captured_callback);
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
InitializeForNthSync();
testing::Mock::VerifyAndClearExpectations(component_factory());
@@ -842,7 +828,7 @@ TEST_F(ProfileSyncServiceTest,
// Simulate browser restart. First configuration is a regular one.
service()->Shutdown();
- ExpectSyncBackendHostCreationCaptureClearServerData(&captured_callback);
+ ExpectSyncEngineCreationCaptureClearServerData(&captured_callback);
ExpectDataTypeManagerCreation(
1, GetRecordingConfigureCalledCallback(&configure_reason));
service()->RequestStart();
@@ -862,7 +848,7 @@ TEST_F(ProfileSyncServiceTest,
service()->OnConfigureDone(result);
EXPECT_FALSE(captured_callback.is_null());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
ExpectDataTypeManagerCreation(
1, GetRecordingConfigureCalledCallback(&configure_reason));
captured_callback.Run();
@@ -876,7 +862,7 @@ TEST_F(ProfileSyncServiceTest, PassphrasePromptDueToVersion) {
IssueTestTokens();
CreateService(ProfileSyncService::AUTO_START);
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
InitializeForNthSync();
syncer::SyncPrefs sync_prefs(service()->GetSyncClient()->GetPrefService());
@@ -909,7 +895,7 @@ TEST_F(ProfileSyncServiceTest, ResetSyncData) {
// Backend should get initialized two times: once during initialization and
// once when handling actionable error.
ExpectDataTypeManagerCreation(2, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(2);
+ ExpectSyncEngineCreation(2);
InitializeForNthSync();
syncer::SyncProtocolError client_cmd;
@@ -923,7 +909,7 @@ TEST_F(ProfileSyncServiceTest, DisableSyncOnClient) {
IssueTestTokens();
CreateService(ProfileSyncService::AUTO_START);
ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncBackendHostCreation(1);
+ ExpectSyncEngineCreation(1);
InitializeForNthSync();
EXPECT_TRUE(service()->IsSyncActive());
@@ -955,7 +941,7 @@ TEST_F(ProfileSyncServiceTest, DisableSyncOnClient) {
TEST_F(ProfileSyncServiceTest, ValidPointersInDTCMap) {
CreateService(ProfileSyncService::AUTO_START);
service()->OnSessionRestoreComplete();
- service()->OnSyncCycleCompleted();
+ service()->OnSyncCycleCompleted(syncer::SyncCycleSnapshot());
}
// The OpenTabsUIDelegate should only be accessable when PROXY_TABS is enabled.
diff --git a/chromium/components/browser_sync/profile_sync_test_util.cc b/chromium/components/browser_sync/profile_sync_test_util.cc
index abf769319b8..c6d34511b40 100644
--- a/chromium/components/browser_sync/profile_sync_test_util.cc
+++ b/chromium/components/browser_sync/profile_sync_test_util.cc
@@ -7,16 +7,17 @@
#include <utility>
#include "base/memory/ptr_util.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/history/core/browser/history_model_worker.h"
#include "components/history/core/browser/history_service.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/signin/core/browser/signin_manager_base.h"
#include "components/sync/base/sync_prefs.h"
-#include "components/sync/driver/glue/browser_thread_model_worker.h"
-#include "components/sync/driver/glue/ui_model_worker.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/ui_model_worker.h"
#include "net/url_request/url_request_test_util.h"
namespace browser_sync {
@@ -260,7 +261,10 @@ ProfileSyncService::InitParams ProfileSyncServiceBundle::CreateBasicInitParams(
init_params.url_request_context = url_request_context();
init_params.debug_identifier = "dummyDebugName";
init_params.channel = version_info::Channel::UNKNOWN;
- init_params.blocking_pool = worker_pool_owner_.pool().get();
+ init_params.blocking_task_runner =
+ worker_pool_owner_.pool()->GetSequencedTaskRunnerWithShutdownBehavior(
+ worker_pool_owner_.pool()->GetSequenceToken(),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
return init_params;
}
diff --git a/chromium/components/browser_sync/test_profile_sync_service.cc b/chromium/components/browser_sync/test_profile_sync_service.cc
index c7222ae5ea0..a4bc8d080bd 100644
--- a/chromium/components/browser_sync/test_profile_sync_service.cc
+++ b/chromium/components/browser_sync/test_profile_sync_service.cc
@@ -30,7 +30,7 @@ void TestProfileSyncService::OnConfigureDone(
}
syncer::UserShare* TestProfileSyncService::GetUserShare() const {
- return backend_->GetUserShare();
+ return engine_->GetUserShare();
}
} // namespace browser_sync
diff --git a/chromium/components/browser_watcher/BUILD.gn b/chromium/components/browser_watcher/BUILD.gn
index fede06b927f..95b431e4cff 100644
--- a/chromium/components/browser_watcher/BUILD.gn
+++ b/chromium/components/browser_watcher/BUILD.gn
@@ -4,123 +4,134 @@
import("//third_party/protobuf/proto_library.gni")
-proto_library("stability_report_proto") {
- sources = [
- "stability_report.proto",
- ]
-}
+if (is_win) {
+ proto_library("stability_report_proto") {
+ sources = [
+ "stability_report.proto",
+ ]
+ }
-static_library("browser_watcher") {
- # This is a separate lib to minimize the dependencies for its
- # hosting binary "chrome_watcher.dll".
- sources = [
- "endsession_watcher_window_win.cc",
- "endsession_watcher_window_win.h",
- "exit_code_watcher_win.cc",
- "exit_code_watcher_win.h",
- "window_hang_monitor_win.cc",
- "window_hang_monitor_win.h",
- ]
- deps = [
- "//base",
- ]
-}
+ static_library("browser_watcher") {
+ # This is a separate lib to minimize the dependencies for its
+ # hosting binary "chrome_watcher.dll".
+ sources = [
+ "endsession_watcher_window_win.cc",
+ "endsession_watcher_window_win.h",
+ "exit_code_watcher_win.cc",
+ "exit_code_watcher_win.h",
+ "window_hang_monitor_win.cc",
+ "window_hang_monitor_win.h",
+ ]
+ deps = [
+ "//base",
+ ]
+ }
-static_library("browser_watcher_client") {
- sources = [
- "watcher_client_win.cc",
- "watcher_client_win.h",
- "watcher_metrics_provider_win.cc",
- "watcher_metrics_provider_win.h",
- ]
- deps = [
- ":postmortem_report_collector",
- ":stability",
- "//base",
- "//components/metrics",
- "//third_party/crashpad/crashpad/client",
- ]
-}
+ static_library("browser_watcher_client") {
+ sources = [
+ "watcher_client_win.cc",
+ "watcher_client_win.h",
+ "watcher_metrics_provider_win.cc",
+ "watcher_metrics_provider_win.h",
+ ]
+ deps = [
+ ":postmortem_report_collector",
+ ":stability",
+ "//base",
+ "//components/metrics",
+ "//third_party/crashpad/crashpad/client",
+ ]
+ }
-static_library("postmortem_minidump_writer") {
- # TODO(manzagop): remove this lib once Crashpad writes the minidumps.
- sources = [
- "postmortem_minidump_writer.h",
- "postmortem_minidump_writer_win.cc",
- ]
- deps = [
- ":stability_report_proto",
- "//base",
- "//third_party/crashpad/crashpad/client",
- "//third_party/crashpad/crashpad/minidump",
- "//third_party/crashpad/crashpad/util",
- ]
-}
+ static_library("postmortem_minidump_writer") {
+ # TODO(manzagop): remove this lib once Crashpad writes the minidumps.
+ sources = [
+ "postmortem_minidump_writer.h",
+ "postmortem_minidump_writer_win.cc",
+ ]
+ deps = [
+ ":stability_report_proto",
+ "//base",
+ "//third_party/crashpad/crashpad/client",
+ "//third_party/crashpad/crashpad/minidump",
+ "//third_party/crashpad/crashpad/util",
+ ]
+ }
-static_library("postmortem_report_collector") {
- sources = [
- "postmortem_report_collector.cc",
- "postmortem_report_collector.h",
- ]
- deps = [
- ":postmortem_minidump_writer",
- ":stability_report_proto",
- "//base",
- "//third_party/crashpad/crashpad/client",
- "//third_party/crashpad/crashpad/util",
- ]
+ static_library("postmortem_report_collector") {
+ sources = [
+ "postmortem_report_collector.cc",
+ "postmortem_report_collector.h",
+ ]
+ deps = [
+ ":postmortem_minidump_writer",
+ ":stability_report_proto",
+ "//base",
+ "//third_party/crashpad/crashpad/client",
+ "//third_party/crashpad/crashpad/util",
+ ]
+ }
}
static_library("stability") {
sources = [
"features.cc",
"features.h",
- "stability_debugging_win.cc",
- "stability_debugging_win.h",
+ "stability_debugging.cc",
+ "stability_debugging.h",
]
deps = [
"//base",
]
}
-source_set("unit_tests") {
- testonly = true
+static_library("stability_data") {
sources = [
- "endsession_watcher_window_win_unittest.cc",
- "exit_code_watcher_win_unittest.cc",
- "postmortem_minidump_writer_win_unittest.cc",
- "postmortem_report_collector_unittest.cc",
- "watcher_client_win_unittest.cc",
- "watcher_metrics_provider_win_unittest.cc",
- "window_hang_monitor_win_unittest.cc",
- ]
- configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
- deps = [
- ":browser_watcher",
- ":browser_watcher_client",
- ":postmortem_minidump_writer",
- ":postmortem_report_collector",
- ":stability_report_proto",
- "//base",
- "//base/test:test_support",
- "//testing/gmock",
- "//testing/gtest",
- "//third_party/crashpad/crashpad/client",
-
- # TODO(manzagop): remove this lib once Crashpad writes the minidumps.
- "//third_party/crashpad/crashpad/compat",
- "//third_party/crashpad/crashpad/minidump",
- "//third_party/crashpad/crashpad/snapshot",
- "//third_party/crashpad/crashpad/util",
+ "stability_data_names.cc",
+ "stability_data_names.h",
]
}
-executable("dump_postmortem") {
- sources = [
- "dump_postmortem_minidump_main_win.cc",
- ]
- deps = [
- ":stability_report_proto",
- "//base",
- ]
+if (is_win) {
+ source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "endsession_watcher_window_win_unittest.cc",
+ "exit_code_watcher_win_unittest.cc",
+ "postmortem_minidump_writer_win_unittest.cc",
+ "postmortem_report_collector_unittest.cc",
+ "watcher_client_win_unittest.cc",
+ "watcher_metrics_provider_win_unittest.cc",
+ "window_hang_monitor_win_unittest.cc",
+ ]
+ configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+ deps = [
+ ":browser_watcher",
+ ":browser_watcher_client",
+ ":postmortem_minidump_writer",
+ ":postmortem_report_collector",
+ ":stability_report_proto",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/crashpad/crashpad/client",
+
+ # TODO(manzagop): remove this lib once Crashpad writes the minidumps.
+ "//third_party/crashpad/crashpad/compat",
+ "//third_party/crashpad/crashpad/minidump",
+ "//third_party/crashpad/crashpad/snapshot",
+ "//third_party/crashpad/crashpad/util",
+ ]
+ }
+
+ executable("dump_postmortem") {
+ sources = [
+ "dump_postmortem_minidump_main_win.cc",
+ ]
+ deps = [
+ ":stability_report_proto",
+ "//base",
+ ]
+ }
}
diff --git a/chromium/components/browser_watcher/dump_postmortem_minidump_main_win.cc b/chromium/components/browser_watcher/dump_postmortem_minidump_main_win.cc
index 1b9aabe4d99..246a06a56b0 100644
--- a/chromium/components/browser_watcher/dump_postmortem_minidump_main_win.cc
+++ b/chromium/components/browser_watcher/dump_postmortem_minidump_main_win.cc
@@ -33,12 +33,58 @@ bool ParseCommandLine(const base::CommandLine* cmd,
return true;
}
+void Indent(FILE* out, size_t indent_level) {
+ DCHECK(out);
+ for (size_t i = 0; i < indent_level; ++i)
+ fprintf(out, " ");
+}
+
+void PrintActivity(FILE* out,
+ size_t indent_level,
+ const browser_watcher::Activity& activity) {
+ DCHECK(out);
+ Indent(out, indent_level);
+ fprintf(out, "Activity\n");
+ Indent(out, indent_level + 1);
+ fprintf(out, "type: %d\n", activity.type());
+ Indent(out, indent_level + 1);
+ fprintf(out, "time: %lld\n", activity.time());
+ switch (activity.type()) {
+ case browser_watcher::Activity::UNKNOWN:
+ break;
+ case browser_watcher::Activity::ACT_TASK_RUN:
+ Indent(out, indent_level + 1);
+ fprintf(out, "origin_address: %llx\n", activity.origin_address());
+ fprintf(out, "task_sequence_id: %lld\n", activity.task_sequence_id());
+ break;
+ case browser_watcher::Activity::ACT_LOCK_ACQUIRE:
+ Indent(out, indent_level + 1);
+ fprintf(out, "lock_address: %llx\n", activity.lock_address());
+ break;
+ case browser_watcher::Activity::ACT_EVENT_WAIT:
+ Indent(out, indent_level + 1);
+ fprintf(out, "event_address: %llx\n", activity.event_address());
+ break;
+ case browser_watcher::Activity::ACT_THREAD_JOIN:
+ Indent(out, indent_level + 1);
+ fprintf(out, "thread_id: %lld\n", activity.thread_id());
+ break;
+ case browser_watcher::Activity::ACT_PROCESS_WAIT:
+ Indent(out, indent_level + 1);
+ fprintf(out, "process_id: %lld\n", activity.process_id());
+ break;
+ }
+}
+
void PrintProcessState(FILE* out,
const browser_watcher::ProcessState& process) {
- fprintf(out, "%s", "Process:\n");
- for (int i = 0; i < process.threads_size(); ++i) {
- const browser_watcher::ThreadState thread = process.threads(i);
- fprintf(out, "%s\n", thread.thread_name().c_str());
+ fprintf(out, "Process %lld (%d threads)\n", process.process_id(),
+ process.threads_size());
+ for (const browser_watcher::ThreadState& thread : process.threads()) {
+ fprintf(out, "Thread %lld (%s) : %d activities\n", thread.thread_id(),
+ thread.thread_name().c_str(), thread.activity_count());
+ for (const browser_watcher::Activity& activity : thread.activities())
+ PrintActivity(out, 1, activity);
}
}
diff --git a/chromium/components/browser_watcher/postmortem_minidump_writer_win_unittest.cc b/chromium/components/browser_watcher/postmortem_minidump_writer_win_unittest.cc
index 7acbfb1b3fc..645caa1e1cd 100644
--- a/chromium/components/browser_watcher/postmortem_minidump_writer_win_unittest.cc
+++ b/chromium/components/browser_watcher/postmortem_minidump_writer_win_unittest.cc
@@ -33,8 +33,8 @@ class WritePostmortemDumpTest : public testing::Test {
void SetUp() override {
testing::Test::SetUp();
- expected_client_id_ = UUID(UUID::InitializeWithNewTag{});
- expected_report_id_ = UUID(UUID::InitializeWithNewTag{});
+ expected_client_id_.InitializeWithNew();
+ expected_report_id_.InitializeWithNew();
// Create a stability report.
// TODO(manzagop): flesh out the report once proto is more detailed.
diff --git a/chromium/components/browser_watcher/postmortem_report_collector.cc b/chromium/components/browser_watcher/postmortem_report_collector.cc
index 24cb0b67c01..2b3d9d5a185 100644
--- a/chromium/components/browser_watcher/postmortem_report_collector.cc
+++ b/chromium/components/browser_watcher/postmortem_report_collector.cc
@@ -12,6 +12,7 @@
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
+#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "components/browser_watcher/postmortem_minidump_writer.h"
#include "third_party/crashpad/crashpad/client/settings.h"
@@ -21,11 +22,75 @@ using base::FilePath;
namespace browser_watcher {
-using base::debug::ActivitySnapshot;
+using ActivitySnapshot = base::debug::ThreadActivityAnalyzer::Snapshot;
+using base::debug::ActivityUserData;
using base::debug::GlobalActivityAnalyzer;
using base::debug::ThreadActivityAnalyzer;
using crashpad::CrashReportDatabase;
+namespace {
+
+// Collects stability user data from the recorded format to the collected
+// format.
+void CollectUserData(
+ const ActivityUserData::Snapshot& recorded_map,
+ google::protobuf::Map<std::string, TypedValue>* collected_map) {
+ DCHECK(collected_map);
+
+ for (const auto& name_and_value : recorded_map) {
+ const ActivityUserData::TypedValue& recorded_value = name_and_value.second;
+ TypedValue collected_value;
+
+ switch (recorded_value.type()) {
+ case ActivityUserData::END_OF_VALUES:
+ NOTREACHED();
+ break;
+ case ActivityUserData::RAW_VALUE:
+ collected_value.set_bytes_value(recorded_value.Get().as_string());
+ break;
+ case ActivityUserData::RAW_VALUE_REFERENCE: {
+ base::StringPiece recorded_ref = recorded_value.GetReference();
+ TypedValue::Reference* collected_ref =
+ collected_value.mutable_bytes_reference();
+ collected_ref->set_address(
+ reinterpret_cast<uintptr_t>(recorded_ref.data()));
+ collected_ref->set_size(recorded_ref.size());
+ break;
+ }
+ case ActivityUserData::STRING_VALUE:
+ collected_value.set_string_value(
+ recorded_value.GetString().as_string());
+ break;
+ case ActivityUserData::STRING_VALUE_REFERENCE: {
+ base::StringPiece recorded_ref = recorded_value.GetStringReference();
+ TypedValue::Reference* collected_ref =
+ collected_value.mutable_string_reference();
+ collected_ref->set_address(
+ reinterpret_cast<uintptr_t>(recorded_ref.data()));
+ collected_ref->set_size(recorded_ref.size());
+ break;
+ }
+ case ActivityUserData::CHAR_VALUE:
+ collected_value.set_char_value(
+ std::string(1, recorded_value.GetChar()));
+ break;
+ case ActivityUserData::BOOL_VALUE:
+ collected_value.set_bool_value(recorded_value.GetBool());
+ break;
+ case ActivityUserData::SIGNED_VALUE:
+ collected_value.set_signed_value(recorded_value.GetInt());
+ break;
+ case ActivityUserData::UNSIGNED_VALUE:
+ collected_value.set_unsigned_value(recorded_value.GetUint());
+ break;
+ }
+
+ (*collected_map)[name_and_value.first].Swap(&collected_value);
+ }
+}
+
+} // namespace
+
PostmortemReportCollector::PostmortemReportCollector(
const std::string& product_name,
const std::string& version_number,
@@ -100,7 +165,8 @@ PostmortemReportCollector::CollectAndSubmit(
// 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.
+ // Collect the data from the debug file to a proto. Note: a non-empty report
+ // is interpreted here as an unclean exit.
std::unique_ptr<StabilityReport> report_proto;
CollectionStatus status = Collect(file, &report_proto);
if (status != SUCCESS) {
@@ -167,17 +233,29 @@ PostmortemReportCollector::CollectionStatus PostmortemReportCollector::Collect(
return ANALYZER_CREATION_FAILED;
// Early exit if there is no data.
+ std::vector<std::string> log_messages = global_analyzer->GetLogMessages();
+ ActivityUserData::Snapshot global_data_snapshot =
+ global_analyzer->GetGlobalUserDataSnapshot();
ThreadActivityAnalyzer* thread_analyzer = global_analyzer->GetFirstAnalyzer();
- if (!thread_analyzer) {
- // No data. This case happens in the case of a clean exit.
+ if (log_messages.empty() && global_data_snapshot.empty() &&
+ !thread_analyzer) {
return DEBUG_FILE_NO_DATA;
}
- // Iterate through the thread analyzers, fleshing out the report.
+ // Create the report, then flesh it out.
report->reset(new StabilityReport());
+
+ // Collect log messages.
+ for (const std::string& message : log_messages) {
+ (*report)->add_log_messages(message);
+ }
+
+ // Collect global user data.
+ CollectUserData(global_data_snapshot, (*report)->mutable_global_data());
+
+ // Collect thread activity data.
// Note: a single process is instrumented.
ProcessState* process_state = (*report)->add_process_states();
-
for (; thread_analyzer != nullptr;
thread_analyzer = global_analyzer->GetNextAnalyzer()) {
// Only valid analyzers are expected per contract of GetFirstAnalyzer /
@@ -199,7 +277,7 @@ PostmortemReportCollector::CollectionStatus PostmortemReportCollector::Collect(
}
void PostmortemReportCollector::CollectThread(
- const base::debug::ActivitySnapshot& snapshot,
+ const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
ThreadState* thread_state) {
DCHECK(thread_state);
@@ -207,8 +285,11 @@ void PostmortemReportCollector::CollectThread(
thread_state->set_thread_id(snapshot.thread_id);
thread_state->set_activity_count(snapshot.activity_stack_depth);
- for (const base::debug::Activity& recorded : snapshot.activity_stack) {
+ for (size_t i = 0; i < snapshot.activity_stack.size(); ++i) {
+ const base::debug::Activity& recorded = snapshot.activity_stack[i];
Activity* collected = thread_state->add_activities();
+
+ // Collect activity
switch (recorded.activity_type) {
case base::debug::Activity::ACT_TASK_RUN:
collected->set_type(Activity::ACT_TASK_RUN);
@@ -234,6 +315,12 @@ void PostmortemReportCollector::CollectThread(
default:
break;
}
+
+ // Collect user data
+ if (i < snapshot.user_data_stack.size()) {
+ CollectUserData(snapshot.user_data_stack[i],
+ collected->mutable_user_data());
+ }
}
}
diff --git a/chromium/components/browser_watcher/postmortem_report_collector.h b/chromium/components/browser_watcher/postmortem_report_collector.h
index d776a50ccb2..e1bac13297f 100644
--- a/chromium/components/browser_watcher/postmortem_report_collector.h
+++ b/chromium/components/browser_watcher/postmortem_report_collector.h
@@ -15,7 +15,7 @@
#include <string>
#include <vector>
-#include "base/debug/activity_tracker.h"
+#include "base/debug/activity_analyzer.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
@@ -72,6 +72,12 @@ class PostmortemReportCollector {
FRIEND_TEST_ALL_PREFIXES(PostmortemReportCollectorTest, CollectRandomFile);
FRIEND_TEST_ALL_PREFIXES(PostmortemReportCollectorCollectionTest,
CollectSuccess);
+ FRIEND_TEST_ALL_PREFIXES(
+ PostmortemReportCollectorCollectionFromGlobalTrackerTest,
+ LogCollection);
+ FRIEND_TEST_ALL_PREFIXES(
+ PostmortemReportCollectorCollectionFromGlobalTrackerTest,
+ GlobalUserDataCollection);
// Virtual for unittesting.
virtual std::vector<base::FilePath> GetDebugStateFilePaths(
@@ -88,8 +94,9 @@ class PostmortemReportCollector {
// TODO(manzagop): move this for reuse in live scenario.
virtual CollectionStatus Collect(const base::FilePath& debug_state_file,
std::unique_ptr<StabilityReport>* report);
- void CollectThread(const base::debug::ActivitySnapshot& snapshot,
- ThreadState* thread_state);
+ void CollectThread(
+ const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
+ ThreadState* thread_state);
virtual bool WriteReportToMinidump(const StabilityReport& report,
const crashpad::UUID& client_id,
diff --git a/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc b/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc
index 846412bc8b9..d7c2307ce33 100644
--- a/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc
+++ b/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc
@@ -12,6 +12,7 @@
#include <utility>
#include <vector>
+#include "base/debug/activity_analyzer.h"
#include "base/debug/activity_tracker.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
@@ -22,6 +23,7 @@
#include "base/memory/ptr_util.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/process/process_handle.h"
+#include "base/stl_util.h"
#include "base/threading/platform_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -30,6 +32,8 @@
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;
@@ -178,8 +182,9 @@ class PostmortemReportCollectorCollectAndSubmitTest : public testing::Test {
base::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_report_ = {minidump_file.GetPlatformFile(),
- crashpad::UUID(UUID::InitializeWithNewTag{}),
+ crashpad::UUID new_report_uuid;
+ new_report_uuid.InitializeWithNew();
+ crashpad_report_ = {minidump_file.GetPlatformFile(), new_report_uuid,
minidump_path};
EXPECT_CALL(database_, PrepareNewCrashReport(_))
.Times(1)
@@ -339,6 +344,9 @@ const int kAnotherThreadId = 45;
} // 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.
@@ -389,8 +397,9 @@ class PostmortemReportCollectorCollectionTest : public testing::Test {
return nullptr;
// Get the memory's base address.
- void* mem_base = allocator->GetAsObject<char>(
- mem_reference, GlobalActivityTracker::kTypeIdActivityTracker);
+ void* mem_base = allocator->GetAsArray<char>(
+ mem_reference, GlobalActivityTracker::kTypeIdActivityTracker,
+ PersistentMemoryAllocator::kSizeAny);
if (mem_base == nullptr)
return nullptr;
@@ -418,7 +427,7 @@ TEST_F(PostmortemReportCollectorCollectionTest, CollectSuccess) {
tracker_->PushActivity(
nullptr, base::debug::Activity::ACT_LOCK_ACQUIRE,
ActivityData::ForLock(reinterpret_cast<void*>(kLockAddress)));
- tracker_->PushActivity(
+ 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,
@@ -429,6 +438,14 @@ TEST_F(PostmortemReportCollectorCollectionTest, CollectSuccess) {
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);
@@ -459,27 +476,156 @@ TEST_F(PostmortemReportCollectorCollectionTest, CollectSuccess) {
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());
+ }
+}
+
+class PostmortemReportCollectorCollectionFromGlobalTrackerTest
+ : public testing::Test {
+ public:
+ const int kMemorySize = 1 << 20; // 1MiB
+
+ PostmortemReportCollectorCollectionFromGlobalTrackerTest() {}
+ ~PostmortemReportCollectorCollectionFromGlobalTrackerTest() 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 base::FilePath& debug_file_path() { return debug_file_path_; }
+
+ protected:
+ base::ScopedTempDir temp_dir_;
+ base::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);
+ std::unique_ptr<StabilityReport> report;
+ ASSERT_EQ(PostmortemReportCollector::SUCCESS,
+ collector.Collect(debug_file_path(), &report));
+ ASSERT_NE(nullptr, 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,
+ GlobalUserDataCollection) {
+ const char string1[] = "foo";
+ const char string2[] = "bar";
+
+ // Record some global user data.
+ GlobalActivityTracker::CreateWithFile(debug_file_path(), kMemorySize, 0ULL,
+ "", 3);
+ ActivityUserData& global_data = GlobalActivityTracker::Get()->user_data();
+ global_data.Set("raw", "foo", 3);
+ global_data.SetString("string", "bar");
+ global_data.SetChar("char", '9');
+ global_data.SetInt("int", -9999);
+ global_data.SetUint("uint", 9999);
+ global_data.SetBool("bool", true);
+ global_data.SetReference("ref", string1, strlen(string1));
+ global_data.SetStringReference("sref", string2);
+
+ // Collect the stability report.
+ PostmortemReportCollector collector(kProductName, kVersionNumber,
+ kChannelName);
+ std::unique_ptr<StabilityReport> report;
+ ASSERT_EQ(PostmortemReportCollector::SUCCESS,
+ collector.Collect(debug_file_path(), &report));
+ ASSERT_NE(nullptr, report);
+
+ // Validate the report's log content.
+ const auto& collected_data = report->global_data();
+ ASSERT_EQ(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()));
}
} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/stability_data_names.cc b/chromium/components/browser_watcher/stability_data_names.cc
new file mode 100644
index 00000000000..03cb921b9fc
--- /dev/null
+++ b/chromium/components/browser_watcher/stability_data_names.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/browser_watcher/stability_data_names.h"
+
+namespace browser_watcher {
+
+const char kStabilityChannel[] = "channel";
+const char kStabilityExecutionPhase[] = "stability-execution-phase";
+const char kStabilityModuleAddress[] = "module-address";
+const char kStabilityModuleSize[] = "module-size";
+const char kStabilityModuleTimestamp[] = "module-timestamp";
+const char kStabilityPlatform[] = "platform";
+const char kStabilityProduct[] = "product";
+const char kStabilitySpecialBuild[] = "special-build";
+const char kStabilityVersion[] = "version";
+
+} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/stability_data_names.h b/chromium/components/browser_watcher/stability_data_names.h
new file mode 100644
index 00000000000..32fb6406d7a
--- /dev/null
+++ b/chromium/components/browser_watcher/stability_data_names.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_BROWSER_WATCHER_STABILITY_DATA_NAMES_H_
+#define COMPONENTS_BROWSER_WATCHER_STABILITY_DATA_NAMES_H_
+
+namespace browser_watcher {
+
+// Alphabetical list of stability data names.
+extern const char kStabilityChannel[];
+extern const char kStabilityExecutionPhase[];
+extern const char kStabilityModuleAddress[];
+extern const char kStabilityModuleSize[];
+extern const char kStabilityModuleTimestamp[];
+extern const char kStabilityPlatform[];
+extern const char kStabilityProduct[];
+extern const char kStabilitySpecialBuild[];
+extern const char kStabilityVersion[];
+
+} // namespace browser_watcher
+
+#endif // COMPONENTS_BROWSER_WATCHER_STABILITY_DATA_NAMES_H_
diff --git a/chromium/components/browser_watcher/stability_debugging_win.cc b/chromium/components/browser_watcher/stability_debugging.cc
index 804bf88d5da..4341ffe7d3f 100644
--- a/chromium/components/browser_watcher/stability_debugging_win.cc
+++ b/chromium/components/browser_watcher/stability_debugging.cc
@@ -2,19 +2,25 @@
// 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_debugging_win.h"
+#include "components/browser_watcher/stability_debugging.h"
#include <string>
+#include "base/debug/activity_tracker.h"
+#include "base/feature_list.h"
+#include "base/files/file.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/path_service.h"
+#include "base/process/process.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
+#include "components/browser_watcher/features.h"
namespace browser_watcher {
namespace {
+#if defined(OS_WIN)
bool GetCreationTime(const base::Process& process, base::Time* time) {
DCHECK(time);
@@ -29,9 +35,12 @@ bool GetCreationTime(const base::Process& process, base::Time* time) {
*time = base::Time::FromFileTime(creation_time);
return true;
}
+#endif // defined(OS_WIN)
} // namespace
+#if defined(OS_WIN)
+
base::FilePath GetStabilityDir(const base::FilePath& user_data_dir) {
return user_data_dir.AppendASCII("Stability");
}
@@ -60,4 +69,35 @@ base::FilePath::StringType GetStabilityFilePattern() {
base::PersistentMemoryAllocator::kFileExtension;
}
+void MarkStabilityFileForDeletion(const base::FilePath& user_data_dir) {
+ if (!base::FeatureList::IsEnabled(
+ browser_watcher::kStabilityDebuggingFeature)) {
+ return;
+ }
+
+ base::FilePath stability_file;
+ if (!GetStabilityFileForProcess(base::Process::Current(), user_data_dir,
+ &stability_file)) {
+ // TODO(manzagop): add a metric for this.
+ return;
+ }
+
+ // 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);
+}
+#endif // defined(OS_WIN)
+
+void SetStabilityDataInt(base::StringPiece name, int64_t value) {
+ base::debug::GlobalActivityTracker* global_tracker =
+ base::debug::GlobalActivityTracker::Get();
+ if (!global_tracker)
+ return; // Activity tracking isn't enabled.
+
+ global_tracker->user_data().SetInt(name, value);
+}
+
} // namespace browser_watcher
diff --git a/chromium/components/browser_watcher/stability_debugging_win.h b/chromium/components/browser_watcher/stability_debugging.h
index 08edd48b386..e8569417482 100644
--- a/chromium/components/browser_watcher/stability_debugging_win.h
+++ b/chromium/components/browser_watcher/stability_debugging.h
@@ -2,15 +2,20 @@
// 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_DEBUGGING_WIN_H_
-#define COMPONENTS_BROWSER_WATCHER_STABILITY_DEBUGGING_WIN_H_
+#ifndef COMPONENTS_BROWSER_WATCHER_STABILITY_DEBUGGING_H_
+#define COMPONENTS_BROWSER_WATCHER_STABILITY_DEBUGGING_H_
+
+#include <stdint.h>
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/process/process.h"
+#include "base/strings/string_piece.h"
namespace browser_watcher {
+#if defined(OS_WIN)
+
// Returns the the stability debugging directory.
base::FilePath GetStabilityDir(const base::FilePath& user_data_dir);
@@ -23,6 +28,14 @@ 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);
+
+#endif // defined(OS_WIN)
+
+// Adds or updates the global stability user data.
+void SetStabilityDataInt(base::StringPiece name, int64_t value);
+
} // namespace browser_watcher
-#endif // COMPONENTS_BROWSER_WATCHER_STABILITY_DEBUGGING_WIN_H_
+#endif // COMPONENTS_BROWSER_WATCHER_STABILITY_DEBUGGING_H_
diff --git a/chromium/components/browser_watcher/stability_report.proto b/chromium/components/browser_watcher/stability_report.proto
index 7e1de9e4b1d..1d1897fb7ef 100644
--- a/chromium/components/browser_watcher/stability_report.proto
+++ b/chromium/components/browser_watcher/stability_report.proto
@@ -10,10 +10,12 @@ package browser_watcher;
// The state of the system on which Chrome is running (shutting down, battery
// level, load, etc.).
+// Next id: 1
message SystemState {
// TODO(manzagop): flesh out.
}
+// Next id: 8
message CodeModule {
// The base address of this code module as it was loaded by the process.
optional int64 base_address = 1;
@@ -49,11 +51,32 @@ message CodeModule {
optional string version = 7;
}
-// An activity represents information about something of interest on a thread.
+// A typed value holds values of interest or references to such values.
// Next id: 9
+message TypedValue {
+ // A reference to a value of interest.
+ message Reference {
+ optional uint64 address = 1;
+ optional int64 size = 2;
+ }
+
+ oneof value {
+ bytes bytes_value = 1;
+ Reference bytes_reference = 2;
+ string string_value = 3;
+ Reference string_reference = 4;
+ string char_value = 5;
+ bool bool_value = 6;
+ int64 signed_value = 7;
+ uint64 unsigned_value = 8;
+ }
+}
+
+// An activity represents information about something of interest on a thread.
+// Next id: 10
message Activity {
enum Type {
- NONE = 0;
+ UNKNOWN = 0;
ACT_TASK_RUN = 1;
ACT_LOCK_ACQUIRE = 2;
ACT_EVENT_WAIT = 3;
@@ -87,6 +110,9 @@ message Activity {
// A unique identifier for a process.
optional int64 process_id = 8;
+
+ // A key-value store.
+ map<string, TypedValue> user_data = 9;
}
// The state of a thread.
@@ -120,6 +146,7 @@ message ProcessState {
// A stability report contains information pertaining to the execution of a
// single logical instance of a "chrome browser". It is comprised of information
// about the system state and about the chrome browser's processes.
+// Next id: 5
message StabilityReport {
optional SystemState system_state = 1;
// TODO(manzagop): revisit whether a single repeated field should contain all
@@ -128,4 +155,8 @@ message StabilityReport {
// times (e.g. start time), hierarchical relationships (e.g. parent pid),
// command line, etc.
repeated ProcessState process_states = 2;
+ // TODO(manzagop): if/when reports contain multiple processes, attribute and
+ // relocate these to their process (and perhaps thread).
+ repeated string log_messages = 3;
+ map<string, TypedValue> global_data = 4;
}
diff --git a/chromium/components/browser_watcher/watcher_metrics_provider_win.cc b/chromium/components/browser_watcher/watcher_metrics_provider_win.cc
index 57a1d28e50f..74db4f5c837 100644
--- a/chromium/components/browser_watcher/watcher_metrics_provider_win.cc
+++ b/chromium/components/browser_watcher/watcher_metrics_provider_win.cc
@@ -24,7 +24,7 @@
#include "base/win/registry.h"
#include "components/browser_watcher/features.h"
#include "components/browser_watcher/postmortem_report_collector.h"
-#include "components/browser_watcher/stability_debugging_win.h"
+#include "components/browser_watcher/stability_debugging.h"
#include "third_party/crashpad/crashpad/client/crash_report_database.h"
namespace browser_watcher {
diff --git a/chromium/components/browsing_data/content/BUILD.gn b/chromium/components/browsing_data/content/BUILD.gn
index 6229e1b59fb..aa613cff35e 100644
--- a/chromium/components/browsing_data/content/BUILD.gn
+++ b/chromium/components/browsing_data/content/BUILD.gn
@@ -4,6 +4,8 @@
static_library("content") {
sources = [
+ "conditional_cache_counting_helper.cc",
+ "conditional_cache_counting_helper.h",
"conditional_cache_deletion_helper.cc",
"conditional_cache_deletion_helper.h",
"storage_partition_http_cache_data_remover.cc",
diff --git a/chromium/components/browsing_data/content/conditional_cache_counting_helper.cc b/chromium/components/browsing_data/content/conditional_cache_counting_helper.cc
new file mode 100644
index 00000000000..05f4f17d493
--- /dev/null
+++ b/chromium/components/browsing_data/content/conditional_cache_counting_helper.cc
@@ -0,0 +1,178 @@
+// 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/browsing_data/content/conditional_cache_counting_helper.h"
+
+#include "base/callback.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
+#include "net/disk_cache/disk_cache.h"
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using content::BrowserThread;
+
+namespace browsing_data {
+
+// static.
+ConditionalCacheCountingHelper* ConditionalCacheCountingHelper::CreateForRange(
+ content::StoragePartition* storage_partition,
+ base::Time begin_time,
+ base::Time end_time) {
+ return new ConditionalCacheCountingHelper(
+ begin_time, end_time, storage_partition->GetURLRequestContext(),
+ storage_partition->GetMediaURLRequestContext());
+}
+
+ConditionalCacheCountingHelper::ConditionalCacheCountingHelper(
+ base::Time begin_time,
+ base::Time end_time,
+ net::URLRequestContextGetter* main_context_getter,
+ net::URLRequestContextGetter* media_context_getter)
+ : calculation_result_(0),
+ begin_time_(begin_time),
+ end_time_(end_time),
+ is_finished_(false),
+ main_context_getter_(main_context_getter),
+ media_context_getter_(media_context_getter),
+ next_cache_state_(CacheState::NONE),
+ cache_(nullptr),
+ iterator_(nullptr),
+ weak_ptr_factory_(this) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+ConditionalCacheCountingHelper::~ConditionalCacheCountingHelper() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+base::WeakPtr<ConditionalCacheCountingHelper>
+ConditionalCacheCountingHelper::CountAndDestroySelfWhenFinished(
+ const CacheCountCallback& result_callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(!result_callback.is_null());
+ result_callback_ = result_callback;
+ calculation_result_ = 0;
+ is_upper_limit_ = false;
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&ConditionalCacheCountingHelper::CountHttpCacheOnIOThread,
+ base::Unretained(this)));
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
+bool ConditionalCacheCountingHelper::IsFinished() {
+ return is_finished_;
+}
+
+void ConditionalCacheCountingHelper::Finished() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(!is_finished_);
+ is_finished_ = true;
+ result_callback_.Run(calculation_result_, is_upper_limit_);
+ base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
+void ConditionalCacheCountingHelper::CountHttpCacheOnIOThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ next_cache_state_ = CacheState::NONE;
+ DCHECK_EQ(CacheState::NONE, next_cache_state_);
+ DCHECK(main_context_getter_.get());
+ DCHECK(media_context_getter_.get());
+
+ next_cache_state_ = CacheState::CREATE_MAIN;
+ DoCountCache(net::OK);
+}
+
+// The expected state sequence is CacheState::NONE --> CacheState::CREATE_MAIN
+// --> CacheState::PROCESS_MAIN --> CacheState::CREATE_MEDIA -->
+// CacheState::PROCESS_MEDIA --> CacheState::DONE.
+// On error, we jump directly to CacheState::DONE.
+void ConditionalCacheCountingHelper::DoCountCache(int rv) {
+ DCHECK_NE(CacheState::NONE, next_cache_state_);
+ while (rv != net::ERR_IO_PENDING && next_cache_state_ != CacheState::NONE) {
+ // On error, finish and return the error code. A valid result value might
+ // be of two types - either net::OK from the CREATE states, or the result
+ // of calculation from the PROCESS states. Since net::OK == 0, it is valid
+ // to simply add the value to the final calculation result.
+ if (rv < 0) {
+ calculation_result_ = rv;
+ next_cache_state_ = CacheState::DONE;
+ } else {
+ DCHECK_EQ(0, net::OK);
+ calculation_result_ += rv;
+ }
+
+ switch (next_cache_state_) {
+ case CacheState::CREATE_MAIN:
+ case CacheState::CREATE_MEDIA: {
+ // Get a pointer to the cache.
+ net::URLRequestContextGetter* getter =
+ (next_cache_state_ == CacheState::CREATE_MAIN)
+ ? main_context_getter_.get()
+ : media_context_getter_.get();
+ net::HttpCache* http_cache = getter->GetURLRequestContext()
+ ->http_transaction_factory()
+ ->GetCache();
+
+ next_cache_state_ = (next_cache_state_ == CacheState::CREATE_MAIN)
+ ? CacheState::COUNT_MAIN
+ : CacheState::COUNT_MEDIA;
+
+ rv = http_cache->GetBackend(
+ &cache_, base::Bind(&ConditionalCacheCountingHelper::DoCountCache,
+ base::Unretained(this)));
+ break;
+ }
+ case CacheState::COUNT_MAIN:
+ case CacheState::COUNT_MEDIA: {
+ next_cache_state_ = (next_cache_state_ == CacheState::COUNT_MAIN)
+ ? CacheState::CREATE_MEDIA
+ : CacheState::DONE;
+
+ // |cache_| can be null if it cannot be initialized.
+ if (cache_) {
+ if (begin_time_.is_null() && end_time_.is_max()) {
+ rv = cache_->CalculateSizeOfAllEntries(
+ base::Bind(&ConditionalCacheCountingHelper::DoCountCache,
+ base::Unretained(this)));
+ } else {
+ rv = cache_->CalculateSizeOfEntriesBetween(
+ begin_time_, end_time_,
+ base::Bind(&ConditionalCacheCountingHelper::DoCountCache,
+ base::Unretained(this)));
+ if (rv == net::ERR_NOT_IMPLEMENTED) {
+ is_upper_limit_ = true;
+ rv = cache_->CalculateSizeOfAllEntries(
+ base::Bind(&ConditionalCacheCountingHelper::DoCountCache,
+ base::Unretained(this)));
+ }
+ }
+ cache_ = NULL;
+ }
+ break;
+ }
+ case CacheState::DONE: {
+ cache_ = NULL;
+ next_cache_state_ = CacheState::NONE;
+ // Notify the UI thread that we are done.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&ConditionalCacheCountingHelper::Finished,
+ base::Unretained(this)));
+ return;
+ }
+ case CacheState::NONE: {
+ NOTREACHED() << "bad state";
+ return;
+ }
+ }
+ }
+}
+
+} // namespace browsing_data
diff --git a/chromium/components/browsing_data/content/conditional_cache_counting_helper.h b/chromium/components/browsing_data/content/conditional_cache_counting_helper.h
new file mode 100644
index 00000000000..dff721d2c00
--- /dev/null
+++ b/chromium/components/browsing_data/content/conditional_cache_counting_helper.h
@@ -0,0 +1,97 @@
+// 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_BROWSING_DATA_CONTENT_CONDITIONAL_CACHE_COUNTING_HELPER_H_
+#define COMPONENTS_BROWSING_DATA_CONTENT_CONDITIONAL_CACHE_COUNTING_HELPER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/disk_cache.h"
+
+namespace content {
+class StoragePartition;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace browsing_data {
+
+// Helper to count the size of the http cache data from a StoragePartition.
+class ConditionalCacheCountingHelper {
+ public:
+ // Returns the number bytes in the selected range and if this value is an
+ // upper estimate.
+ typedef base::Callback<void(int64_t, bool)> CacheCountCallback;
+
+ static ConditionalCacheCountingHelper* CreateForRange(
+ content::StoragePartition* storage_partition,
+ base::Time begin_time,
+ base::Time end_time);
+
+ // Count the cache entries according to the specified time range. Destroys
+ // this instance of ConditionalCacheCountingHelper when finished.
+ // Must be called on the UI thread!
+ //
+ // The |completion_callback| will be invoked when the operation completes.
+ base::WeakPtr<ConditionalCacheCountingHelper> CountAndDestroySelfWhenFinished(
+ const CacheCountCallback& result_callback);
+
+ bool IsFinished();
+
+ private:
+ enum class CacheState {
+ NONE,
+ CREATE_MAIN,
+ CREATE_MEDIA,
+ COUNT_MAIN,
+ COUNT_MEDIA,
+ DONE
+ };
+
+ friend class base::DeleteHelper<ConditionalCacheCountingHelper>;
+
+ ConditionalCacheCountingHelper(
+ base::Time begin_time,
+ base::Time end_time,
+ net::URLRequestContextGetter* main_context_getter,
+ net::URLRequestContextGetter* media_context_getter);
+ ~ConditionalCacheCountingHelper();
+
+ void Finished();
+
+ void CountHttpCacheOnIOThread();
+ void DoCountCache(int rv);
+
+ // Stores the cache size computation result before it can be returned
+ // via a callback. This is either the sum of size of the the two cache
+ // backends, or an error code if the calculation failed.
+ int64_t calculation_result_;
+ bool is_upper_limit_;
+
+ CacheCountCallback result_callback_;
+ const base::Time begin_time_;
+ const base::Time end_time_;
+
+ bool is_finished_;
+
+ const scoped_refptr<net::URLRequestContextGetter> main_context_getter_;
+ const scoped_refptr<net::URLRequestContextGetter> media_context_getter_;
+
+ CacheState next_cache_state_;
+ disk_cache::Backend* cache_;
+
+ std::unique_ptr<disk_cache::Backend::Iterator> iterator_;
+
+ base::WeakPtrFactory<ConditionalCacheCountingHelper> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConditionalCacheCountingHelper);
+};
+
+} // namespace browsing_data
+
+#endif // COMPONENTS_BROWSING_DATA_CONTENT_CONDITIONAL_CACHE_COUNTING_HELPER_H_
diff --git a/chromium/components/browsing_data/content/conditional_cache_deletion_helper.cc b/chromium/components/browsing_data/content/conditional_cache_deletion_helper.cc
index 416ad0ce8e7..f0f500e9633 100644
--- a/chromium/components/browsing_data/content/conditional_cache_deletion_helper.cc
+++ b/chromium/components/browsing_data/content/conditional_cache_deletion_helper.cc
@@ -17,8 +17,8 @@ bool EntryPredicateFromURLsAndTime(
const base::Time& begin_time,
const base::Time& end_time,
const disk_cache::Entry* entry) {
- return (entry->GetLastModified() >= begin_time &&
- entry->GetLastModified() < end_time &&
+ return (entry->GetLastUsed() >= begin_time &&
+ entry->GetLastUsed() < end_time &&
url_predicate.Run(GURL(entry->GetKey())));
}
@@ -76,7 +76,7 @@ void ConditionalCacheDeletionHelper::IterateOverEntries(int error) {
}
if (error == net::ERR_FAILED) {
- // The iteration finished successfuly or we can no longer iterate
+ // The iteration finished successfully or we can no longer iterate
// (e.g. the cache was destroyed). We cannot distinguish between the two,
// but we know that there is nothing more that we can do, so we return OK.
base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chromium/components/browsing_data/content/storage_partition_http_cache_data_remover.cc b/chromium/components/browsing_data/content/storage_partition_http_cache_data_remover.cc
index c0f20519de2..5eeff79f1ec 100644
--- a/chromium/components/browsing_data/content/storage_partition_http_cache_data_remover.cc
+++ b/chromium/components/browsing_data/content/storage_partition_http_cache_data_remover.cc
@@ -34,10 +34,8 @@ StoragePartitionHttpCacheDataRemover::StoragePartitionHttpCacheDataRemover(
delete_end_(delete_end),
main_context_getter_(main_context_getter),
media_context_getter_(media_context_getter),
- next_cache_state_(STATE_NONE),
- cache_(nullptr),
- calculation_result_(0) {
-}
+ next_cache_state_(CacheState::NONE),
+ cache_(nullptr) {}
StoragePartitionHttpCacheDataRemover::~StoragePartitionHttpCacheDataRemover() {
}
@@ -81,76 +79,45 @@ void StoragePartitionHttpCacheDataRemover::Remove(
base::Unretained(this)));
}
-void StoragePartitionHttpCacheDataRemover::Count(
- const net::Int64CompletionCallback& result_callback) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- DCHECK(!result_callback.is_null());
- result_callback_ = result_callback;
- calculation_result_ = 0;
-
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(
- &StoragePartitionHttpCacheDataRemover::CountHttpCacheOnIOThread,
- base::Unretained(this)));
-}
-
void StoragePartitionHttpCacheDataRemover::ClearHttpCacheOnIOThread() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
- next_cache_state_ = STATE_NONE;
- DCHECK_EQ(STATE_NONE, next_cache_state_);
+ next_cache_state_ = CacheState::NONE;
+ DCHECK_EQ(CacheState::NONE, next_cache_state_);
DCHECK(main_context_getter_.get());
DCHECK(media_context_getter_.get());
- next_cache_state_ = STATE_CREATE_MAIN;
+ next_cache_state_ = CacheState::CREATE_MAIN;
DoClearCache(net::OK);
}
-void StoragePartitionHttpCacheDataRemover::CountHttpCacheOnIOThread() {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
- next_cache_state_ = STATE_NONE;
- DCHECK_EQ(STATE_NONE, next_cache_state_);
- DCHECK(main_context_getter_.get());
- DCHECK(media_context_getter_.get());
-
- next_cache_state_ = STATE_CREATE_MAIN;
- DoCountCache(net::OK);
-}
-
void StoragePartitionHttpCacheDataRemover::ClearedHttpCache() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
done_callback_.Run();
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
}
-void StoragePartitionHttpCacheDataRemover::CountedHttpCache() {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- result_callback_.Run(calculation_result_);
- base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
-}
-
-// The expected state sequence is STATE_NONE --> STATE_CREATE_MAIN -->
-// STATE_PROCESS_MAIN --> STATE_CREATE_MEDIA --> STATE_PROCESS_MEDIA -->
-// STATE_DONE, and any errors are ignored.
+// The expected state sequence is CacheState::NONE --> CacheState::CREATE_MAIN
+// --> CacheState::PROCESS_MAIN --> CacheState::CREATE_MEDIA -->
+// CacheState::PROCESS_MEDIA --> CacheState::DONE, and any errors are ignored.
void StoragePartitionHttpCacheDataRemover::DoClearCache(int rv) {
- DCHECK_NE(STATE_NONE, next_cache_state_);
+ DCHECK_NE(CacheState::NONE, next_cache_state_);
- while (rv != net::ERR_IO_PENDING && next_cache_state_ != STATE_NONE) {
+ while (rv != net::ERR_IO_PENDING && next_cache_state_ != CacheState::NONE) {
switch (next_cache_state_) {
- case STATE_CREATE_MAIN:
- case STATE_CREATE_MEDIA: {
+ case CacheState::CREATE_MAIN:
+ case CacheState::CREATE_MEDIA: {
// Get a pointer to the cache.
net::URLRequestContextGetter* getter =
- (next_cache_state_ == STATE_CREATE_MAIN)
+ (next_cache_state_ == CacheState::CREATE_MAIN)
? main_context_getter_.get()
: media_context_getter_.get();
net::HttpCache* http_cache = getter->GetURLRequestContext()
->http_transaction_factory()
->GetCache();
- next_cache_state_ = (next_cache_state_ == STATE_CREATE_MAIN)
- ? STATE_PROCESS_MAIN
- : STATE_PROCESS_MEDIA;
+ next_cache_state_ = (next_cache_state_ == CacheState::CREATE_MAIN)
+ ? CacheState::DELETE_MAIN
+ : CacheState::DELETE_MEDIA;
// Clear QUIC server information from memory and the disk cache.
http_cache->GetSession()
@@ -172,11 +139,11 @@ void StoragePartitionHttpCacheDataRemover::DoClearCache(int rv) {
base::Unretained(this)));
break;
}
- case STATE_PROCESS_MAIN:
- case STATE_PROCESS_MEDIA: {
- next_cache_state_ = (next_cache_state_ == STATE_PROCESS_MAIN)
- ? STATE_CREATE_MEDIA
- : STATE_DONE;
+ case CacheState::DELETE_MAIN:
+ case CacheState::DELETE_MEDIA: {
+ next_cache_state_ = (next_cache_state_ == CacheState::DELETE_MAIN)
+ ? CacheState::CREATE_MEDIA
+ : CacheState::DONE;
// |cache_| can be null if it cannot be initialized.
if (cache_) {
@@ -205,9 +172,9 @@ void StoragePartitionHttpCacheDataRemover::DoClearCache(int rv) {
}
break;
}
- case STATE_DONE: {
+ case CacheState::DONE: {
cache_ = NULL;
- next_cache_state_ = STATE_NONE;
+ next_cache_state_ = CacheState::NONE;
// Notify the UI thread that we are done.
BrowserThread::PostTask(
@@ -216,91 +183,8 @@ void StoragePartitionHttpCacheDataRemover::DoClearCache(int rv) {
base::Unretained(this)));
return;
}
- default: {
- NOTREACHED() << "bad state";
- next_cache_state_ = STATE_NONE; // Stop looping.
- return;
- }
- }
- }
-}
-
-// The expected state sequence is STATE_NONE --> STATE_CREATE_MAIN -->
-// STATE_PROCESS_MAIN --> STATE_CREATE_MEDIA --> STATE_PROCESS_MEDIA -->
-// STATE_DONE. On error, we jump directly to STATE_DONE.
-void StoragePartitionHttpCacheDataRemover::DoCountCache(int rv) {
- DCHECK_NE(STATE_NONE, next_cache_state_);
-
- while (rv != net::ERR_IO_PENDING && next_cache_state_ != STATE_NONE) {
- // On error, finish and return the error code. A valid result value might
- // be of two types - either net::OK from the CREATE states, or the result
- // of calculation from the PROCESS states. Since net::OK == 0, it is valid
- // to simply add the value to the final calculation result.
- if (rv < 0) {
- calculation_result_ = rv;
- next_cache_state_ = STATE_DONE;
- } else {
- DCHECK_EQ(0, net::OK);
- calculation_result_ += rv;
- }
-
- switch (next_cache_state_) {
- case STATE_CREATE_MAIN:
- case STATE_CREATE_MEDIA: {
- // Get a pointer to the cache.
- net::URLRequestContextGetter* getter =
- (next_cache_state_ == STATE_CREATE_MAIN)
- ? main_context_getter_.get()
- : media_context_getter_.get();
- net::HttpCache* http_cache = getter->GetURLRequestContext()
- ->http_transaction_factory()
- ->GetCache();
-
- next_cache_state_ = (next_cache_state_ == STATE_CREATE_MAIN)
- ? STATE_PROCESS_MAIN
- : STATE_PROCESS_MEDIA;
-
- rv = http_cache->GetBackend(
- &cache_,
- base::Bind(&StoragePartitionHttpCacheDataRemover::DoCountCache,
- base::Unretained(this)));
- break;
- }
- case STATE_PROCESS_MAIN:
- case STATE_PROCESS_MEDIA: {
- next_cache_state_ = (next_cache_state_ == STATE_PROCESS_MAIN)
- ? STATE_CREATE_MEDIA
- : STATE_DONE;
-
- // |cache_| can be null if it cannot be initialized.
- if (cache_) {
- if (delete_begin_.is_null() && delete_end_.is_max()) {
- rv = cache_->CalculateSizeOfAllEntries(
- base::Bind(
- &StoragePartitionHttpCacheDataRemover::DoCountCache,
- base::Unretained(this)));
- } else {
- // TODO(msramek): Implement this when we need it.
- DoCountCache(net::ERR_NOT_IMPLEMENTED);
- }
- cache_ = NULL;
- }
- break;
- }
- case STATE_DONE: {
- cache_ = NULL;
- next_cache_state_ = STATE_NONE;
-
- // Notify the UI thread that we are done.
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&StoragePartitionHttpCacheDataRemover::CountedHttpCache,
- base::Unretained(this)));
- return;
- }
- default: {
+ case CacheState::NONE: {
NOTREACHED() << "bad state";
- next_cache_state_ = STATE_NONE; // Stop looping.
return;
}
}
diff --git a/chromium/components/browsing_data/content/storage_partition_http_cache_data_remover.h b/chromium/components/browsing_data/content/storage_partition_http_cache_data_remover.h
index ee4d581fbb7..8d11a4ffa6f 100644
--- a/chromium/components/browsing_data/content/storage_partition_http_cache_data_remover.h
+++ b/chromium/components/browsing_data/content/storage_partition_http_cache_data_remover.h
@@ -51,18 +51,14 @@ class StoragePartitionHttpCacheDataRemover {
// Calls |done_callback| upon completion and also destroys itself.
void Remove(const base::Closure& done_callback);
- // Counts the total size of entries that would be removed by calling |Remove|.
- // Reports it via |result_callback| and then destroys itself.
- void Count(const net::Int64CompletionCallback& result_callback);
-
private:
enum CacheState {
- STATE_NONE,
- STATE_CREATE_MAIN,
- STATE_CREATE_MEDIA,
- STATE_PROCESS_MAIN,
- STATE_PROCESS_MEDIA,
- STATE_DONE
+ NONE,
+ CREATE_MAIN,
+ CREATE_MEDIA,
+ DELETE_MAIN,
+ DELETE_MEDIA,
+ DONE
};
StoragePartitionHttpCacheDataRemover(
@@ -80,14 +76,9 @@ class StoragePartitionHttpCacheDataRemover {
~StoragePartitionHttpCacheDataRemover();
void ClearHttpCacheOnIOThread();
- void CountHttpCacheOnIOThread();
-
void ClearedHttpCache();
- void CountedHttpCache();
-
// Performs the actual work to delete or count the cache.
void DoClearCache(int rv);
- void DoCountCache(int rv);
base::Callback<bool(const GURL&)> url_predicate_;
const base::Time delete_begin_;
@@ -97,17 +88,11 @@ class StoragePartitionHttpCacheDataRemover {
const scoped_refptr<net::URLRequestContextGetter> media_context_getter_;
base::Closure done_callback_;
- net::Int64CompletionCallback result_callback_;
// IO.
int next_cache_state_;
disk_cache::Backend* cache_;
- // Stores the cache size computation result before it can be returned
- // via a callback. This is either the sum of size of the the two cache
- // backends, or an error code if the calculation failed.
- int64_t calculation_result_;
-
DISALLOW_COPY_AND_ASSIGN(StoragePartitionHttpCacheDataRemover);
};
diff --git a/chromium/components/browsing_data/core/browsing_data_utils.cc b/chromium/components/browsing_data/core/browsing_data_utils.cc
index b5b472bc19c..dfd25405592 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/user_metrics.h"
#include "components/browsing_data/core/counters/autofill_counter.h"
#include "components/browsing_data/core/counters/history_counter.h"
#include "components/browsing_data/core/counters/passwords_counter.h"
@@ -36,6 +37,33 @@ base::Time CalculateBeginDeleteTime(TimePeriod time_period) {
return delete_begin_time - diff;
}
+base::Time CalculateEndDeleteTime(TimePeriod time_period) {
+ // No TimePeriod currently supports the second time bound.
+ return base::Time::Max();
+}
+
+void RecordDeletionForPeriod(TimePeriod period) {
+ switch (period) {
+ case browsing_data::LAST_HOUR:
+ base::RecordAction(base::UserMetricsAction("ClearBrowsingData_LastHour"));
+ break;
+ case browsing_data::LAST_DAY:
+ base::RecordAction(base::UserMetricsAction("ClearBrowsingData_LastDay"));
+ break;
+ case browsing_data::LAST_WEEK:
+ base::RecordAction(base::UserMetricsAction("ClearBrowsingData_LastWeek"));
+ break;
+ case browsing_data::FOUR_WEEKS:
+ base::RecordAction(
+ base::UserMetricsAction("ClearBrowsingData_LastMonth"));
+ break;
+ case browsing_data::ALL_TIME:
+ base::RecordAction(
+ base::UserMetricsAction("ClearBrowsingData_Everything"));
+ break;
+ }
+}
+
base::string16 GetCounterTextFromResult(
const browsing_data::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 368dc60c84b..db0d7c2ce91 100644
--- a/chromium/components/browsing_data/core/browsing_data_utils.h
+++ b/chromium/components/browsing_data/core/browsing_data_utils.h
@@ -43,6 +43,12 @@ enum TimePeriod {
// Calculate the begin time for the deletion range specified by |time_period|.
base::Time CalculateBeginDeleteTime(TimePeriod time_period);
+// Calculate the end time for the deletion range specified by |time_period|.
+base::Time CalculateEndDeleteTime(TimePeriod time_period);
+
+// Records the UMA action of UI-triggered data deletion for |time_period|.
+void RecordDeletionForPeriod(TimePeriod time_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/counters/autofill_counter.cc b/chromium/components/browsing_data/core/counters/autofill_counter.cc
index b0e55d0f7a6..6e40f26cb13 100644
--- a/chromium/components/browsing_data/core/counters/autofill_counter.cc
+++ b/chromium/components/browsing_data/core/counters/autofill_counter.cc
@@ -147,7 +147,7 @@ void AutofillCounter::OnWebDataServiceRequestDone(
}
// If we still have pending queries, do not report data yet.
- if (HasPendingQuery())
+ if (suggestions_query_ || credit_cards_query_ || addresses_query_)
return;
std::unique_ptr<Result> reported_result(new AutofillResult(
diff --git a/chromium/components/browsing_data/core/counters/autofill_counter.h b/chromium/components/browsing_data/core/counters/autofill_counter.h
index ee9f98605d9..b732b1cbd9d 100644
--- a/chromium/components/browsing_data/core/counters/autofill_counter.h
+++ b/chromium/components/browsing_data/core/counters/autofill_counter.h
@@ -46,11 +46,6 @@ class AutofillCounter : public browsing_data::BrowsingDataCounter,
// BrowsingDataCounter implementation.
void OnInitialized() override;
- // Whether the counting is in progress.
- bool HasPendingQuery() {
- return suggestions_query_ || credit_cards_query_ || addresses_query_;
- }
-
const char* GetPrefName() const override;
// Set the beginning of the time period for testing. AutofillTable does not
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 1af879c6576..ec67193687b 100644
--- a/chromium/components/browsing_data/core/counters/browsing_data_counter.cc
+++ b/chromium/components/browsing_data/core/counters/browsing_data_counter.cc
@@ -40,13 +40,7 @@ base::Time BrowsingDataCounter::GetPeriodStart() {
void BrowsingDataCounter::Restart() {
DCHECK(initialized_);
-
- // If this data type was unchecked for deletion, we do not need to count it.
- if (!pref_service_->GetBoolean(GetPrefName()))
- return;
-
callback_.Run(base::MakeUnique<Result>(this));
-
Count();
}
diff --git a/chromium/components/browsing_data/core/history_notice_utils_unittest.cc b/chromium/components/browsing_data/core/history_notice_utils_unittest.cc
index 6c4363c02d1..2701c85ed77 100644
--- a/chromium/components/browsing_data/core/history_notice_utils_unittest.cc
+++ b/chromium/components/browsing_data/core/history_notice_utils_unittest.cc
@@ -28,9 +28,9 @@ namespace {
class TestSyncService : public syncer::FakeSyncService {
public:
// Getters (FakeSyncService implementation). ---------------------------------
- bool IsSyncActive() const override {
- return sync_active_;
- }
+ bool IsSyncActive() const override { return sync_active_; }
+
+ bool IsLocalSyncEnabled() const override { return false; }
syncer::ModelTypeSet GetActiveDataTypes() const override {
return active_data_types_;
diff --git a/chromium/components/cast_certificate/OWNERS b/chromium/components/cast_certificate/OWNERS
index 46f798b1caf..2a6f7a58665 100644
--- a/chromium/components/cast_certificate/OWNERS
+++ b/chromium/components/cast_certificate/OWNERS
@@ -1,3 +1,2 @@
file://chromecast/OWNERS
-sheretov@chromium.org
-vadimgo@chromium.org
+dougsteed@chromium.org
diff --git a/chromium/components/cast_certificate/cast_cert_validator.cc b/chromium/components/cast_certificate/cast_cert_validator.cc
index bcd0a862887..2a48ca3e931 100644
--- a/chromium/components/cast_certificate/cast_cert_validator.cc
+++ b/chromium/components/cast_certificate/cast_cert_validator.cc
@@ -310,8 +310,7 @@ bool VerifyDeviceCertUsingCustomTrustStore(
signature_policy.get(), verification_time,
&result);
path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
- net::CompletionStatus rv = path_builder.Run(base::Closure());
- DCHECK_EQ(rv, net::CompletionStatus::SYNC);
+ path_builder.Run();
if (!result.HasValidPath()) {
// TODO(crbug.com/634443): Log error information.
return false;
diff --git a/chromium/components/cast_certificate/cast_crl.cc b/chromium/components/cast_certificate/cast_crl.cc
index eb0b8d1d49d..d805f7f8b8a 100644
--- a/chromium/components/cast_certificate/cast_crl.cc
+++ b/chromium/components/cast_certificate/cast_crl.cc
@@ -145,8 +145,7 @@ bool VerifyCRL(const Crl& crl,
net::CertPathBuilder path_builder(parsed_cert.get(), trust_store,
signature_policy.get(), verification_time,
&result);
- net::CompletionStatus rv = path_builder.Run(base::Closure());
- DCHECK_EQ(rv, net::CompletionStatus::SYNC);
+ path_builder.Run();
if (!result.HasValidPath()) {
VLOG(2) << "CRL - Issuer certificate verification failed.";
// TODO(crbug.com/634443): Log the error information.
diff --git a/chromium/components/cdm/renderer/widevine_key_system_properties.cc b/chromium/components/cdm/renderer/widevine_key_system_properties.cc
index 47b7103b044..276f6238efa 100644
--- a/chromium/components/cdm/renderer/widevine_key_system_properties.cc
+++ b/chromium/components/cdm/renderer/widevine_key_system_properties.cc
@@ -119,17 +119,13 @@ EmeConfigRule WidevineKeySystemProperties::GetRobustnessConfigRule(
}
#if defined(OS_CHROMEOS)
- // TODO(ddorwin): Remove this once we have confirmed it is not necessary.
- // See https://crbug.com/482277
- if (robustness == Robustness::EMPTY)
- return EmeConfigRule::SUPPORTED;
-
// Hardware security requires remote attestation.
if (robustness >= Robustness::HW_SECURE_CRYPTO)
return EmeConfigRule::IDENTIFIER_REQUIRED;
// For video, recommend remote attestation if HW_SECURE_ALL is available,
- // because it enables hardware accelerated decoding.
+ // regardless of the value of |robustness|, because it enables hardware
+ // accelerated decoding.
// TODO(sandersd): Only do this when hardware accelerated decoding is
// available for the requested codecs.
if (media_type == EmeMediaType::VIDEO &&
diff --git a/chromium/components/certificate_reporting/cert_logger.proto b/chromium/components/certificate_reporting/cert_logger.proto
index 452ec03c22c..e049841408a 100644
--- a/chromium/components/certificate_reporting/cert_logger.proto
+++ b/chromium/components/certificate_reporting/cert_logger.proto
@@ -126,4 +126,8 @@ message CertLoggerRequest {
// Information about features that were enabled or disabled for the
// user that might affect certificate validation.
optional CertLoggerFeaturesInfo features_info = 10;
+
+ // False when the report is attempted to be uploaded for the first time. True
+ // in all other uploads.
+ optional bool is_retry_upload = 11;
};
diff --git a/chromium/components/certificate_reporting/error_report.cc b/chromium/components/certificate_reporting/error_report.cc
index fac5e4bfdf0..86d345f4cc0 100644
--- a/chromium/components/certificate_reporting/error_report.cc
+++ b/chromium/components/certificate_reporting/error_report.cc
@@ -160,8 +160,16 @@ void ErrorReport::AddNetworkTimeInfo(
network_time_info->set_network_time_query_behavior(report_behavior);
}
+void ErrorReport::SetIsRetryUpload(bool is_retry_upload) {
+ cert_report_->set_is_retry_upload(is_retry_upload);
+}
+
const std::string& ErrorReport::hostname() const {
return cert_report_->hostname();
}
+bool ErrorReport::is_retry_upload() const {
+ return cert_report_->is_retry_upload();
+}
+
} // namespace certificate_reporting
diff --git a/chromium/components/certificate_reporting/error_report.h b/chromium/components/certificate_reporting/error_report.h
index 92ce5a50fc0..f9236e1aa77 100644
--- a/chromium/components/certificate_reporting/error_report.h
+++ b/chromium/components/certificate_reporting/error_report.h
@@ -72,9 +72,15 @@ class ErrorReport {
void AddNetworkTimeInfo(
const network_time::NetworkTimeTracker* network_time_tracker);
+ // 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;
+ // Returns true if the report has been retried.
+ bool is_retry_upload() const;
+
private:
std::unique_ptr<CertLoggerRequest> cert_report_;
};
diff --git a/chromium/components/certificate_reporting/error_report_unittest.cc b/chromium/components/certificate_reporting/error_report_unittest.cc
index 0ecd92277cc..9f68364b571 100644
--- a/chromium/components/certificate_reporting/error_report_unittest.cc
+++ b/chromium/components/certificate_reporting/error_report_unittest.cc
@@ -9,6 +9,7 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
+#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "base/threading/thread.h"
#include "base/time/default_clock.h"
diff --git a/chromium/components/certificate_reporting/error_reporter.h b/chromium/components/certificate_reporting/error_reporter.h
index 7b2e1167ade..a02610862e2 100644
--- a/chromium/components/certificate_reporting/error_reporter.h
+++ b/chromium/components/certificate_reporting/error_reporter.h
@@ -11,13 +11,13 @@
#include <set>
#include <string>
+#include "base/callback.h"
#include "base/macros.h"
#include "net/url_request/report_sender.h"
#include "url/gurl.h"
namespace net {
class URLRequestContext;
-class SSLInfo;
}
namespace certificate_reporting {
diff --git a/chromium/components/certificate_transparency/log_proof_fetcher.h b/chromium/components/certificate_transparency/log_proof_fetcher.h
index 7c760a330a8..53a52af659f 100644
--- a/chromium/components/certificate_transparency/log_proof_fetcher.h
+++ b/chromium/components/certificate_transparency/log_proof_fetcher.h
@@ -16,10 +16,6 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-namespace base {
-class Value;
-} // namespace base
-
namespace net {
class URLRequestContext;
diff --git a/chromium/components/component_updater/component_updater_service.h b/chromium/components/component_updater/component_updater_service.h
index 72bc13d2865..38961e2ec3e 100644
--- a/chromium/components/component_updater/component_updater_service.h
+++ b/chromium/components/component_updater/component_updater_service.h
@@ -20,23 +20,11 @@
class ComponentsUI;
class PluginObserver;
-class SupervisedUserWhitelistService;
namespace base {
-class DictionaryValue;
-class FilePath;
class SequencedTaskRunner;
}
-namespace content {
-class ResourceThrottle;
-}
-
-namespace net {
-class URLRequestContextGetter;
-class URLRequest;
-}
-
namespace policy {
class ComponentUpdaterPolicyTest;
}
diff --git a/chromium/components/components_google_chrome_strings.grd b/chromium/components/components_google_chrome_strings.grd
index d4f3b0f582d..4033f6e7e07 100644
--- a/chromium/components/components_google_chrome_strings.grd
+++ b/chromium/components/components_google_chrome_strings.grd
@@ -188,12 +188,12 @@
<!-- About Flags UI -->
<if expr="not chromeos">
- <message name="IDS_FLAGS_UI_RELAUNCH_NOTICE" desc="Notifies the user that he needs to relaunch Chrome. Shown next to a button that says 'Relaunch Now'.">
+ <message name="IDS_FLAGS_UI_RELAUNCH_NOTICE" desc="Notifies the user that they need to relaunch Chrome. Shown next to a button that says 'Relaunch Now'.">
Your changes will take effect the next time you relaunch Google Chrome.
</message>
</if>
<if expr="chromeos">
- <message name="IDS_FLAGS_UI_RELAUNCH_NOTICE" desc="Notifies the user that he needs to restart Chrome OS. Shown next to a button that says 'Restart Now'.">
+ <message name="IDS_FLAGS_UI_RELAUNCH_NOTICE" desc="Notifies the user that they need to restart Chrome OS. Shown next to a button that says 'Restart Now'.">
Your changes will take effect the next time you restart your device.
</message>
</if>
@@ -222,7 +222,7 @@
This page includes a password or credit card input over HTTP. A warning will appear in the URL bar starting in Chrome 56 (Jan 2017).
</message>
<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 the URL bar.
+ This page includes a password or credit card input over HTTP. A warning has been added to the URL bar.
</message>
</messages>
</release>
diff --git a/chromium/components/components_strings.grd b/chromium/components/components_strings.grd
index f955b3c3e86..3bce74be7a5 100644
--- a/chromium/components/components_strings.grd
+++ b/chromium/components/components_strings.grd
@@ -278,6 +278,9 @@
<message name="IDS_ACCNAME_FORWARD" desc="The accessible name for the forward button.">
Forward
</message>
+ <message name="IDS_ACCNAME_CANCEL" desc="The accessible name for the Cancel button.">
+ Cancel
+ </message>
<message name="IDS_ACCNAME_CLOSE" desc="The accessible name for the Close button.">
Close
</message>
diff --git a/chromium/components/constrained_window/BUILD.gn b/chromium/components/constrained_window/BUILD.gn
index 8cff9755f0c..cc02308279a 100644
--- a/chromium/components/constrained_window/BUILD.gn
+++ b/chromium/components/constrained_window/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/config/ui.gni")
+import("//ui/base/ui_features.gni")
static_library("constrained_window") {
sources = [
@@ -24,6 +25,7 @@ static_library("constrained_window") {
"//components/guest_view/browser",
"//components/web_modal",
"//content/public/browser",
+ "//ui/display",
"//ui/views",
]
public_deps = [
diff --git a/chromium/components/constrained_window/DEPS b/chromium/components/constrained_window/DEPS
index 4950d97c958..b4fd76f3ab4 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/display",
"+ui/gfx",
"+ui/views",
"+ui/wm",
diff --git a/chromium/components/constrained_window/constrained_window_views.cc b/chromium/components/constrained_window/constrained_window_views.cc
index 3ed86ce0685..e493eaa7085 100644
--- a/chromium/components/constrained_window/constrained_window_views.cc
+++ b/chromium/components/constrained_window/constrained_window_views.cc
@@ -13,6 +13,8 @@
#include "components/web_modal/web_contents_modal_dialog_host.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
#include "ui/views/border.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
@@ -108,8 +110,20 @@ void UpdateModalDialogPosition(views::Widget* widget,
position.set_y(position.y() - border->GetInsets().top());
}
- if (widget->is_top_level())
+ if (widget->is_top_level()) {
position += host_widget->GetClientAreaBoundsInScreen().OffsetFromOrigin();
+ // If the dialog extends partially off any display, clamp its position to
+ // be fully visible within that display. If the dialog doesn't intersect
+ // with any display clamp its position to be fully on the nearest display.
+ gfx::Rect display_rect = gfx::Rect(position, size);
+ const display::Display display =
+ display::Screen::GetScreen()->GetDisplayNearestWindow(
+ dialog_host->GetHostView());
+ const gfx::Rect work_area = display.work_area();
+ if (!work_area.Contains(display_rect))
+ display_rect.AdjustToFit(work_area);
+ position = display_rect.origin();
+ }
widget->SetBounds(gfx::Rect(position, size));
}
diff --git a/chromium/components/constrained_window/constrained_window_views_client.h b/chromium/components/constrained_window/constrained_window_views_client.h
index de58b2cb0f4..739f7aaadf6 100644
--- a/chromium/components/constrained_window/constrained_window_views_client.h
+++ b/chromium/components/constrained_window/constrained_window_views_client.h
@@ -7,10 +7,6 @@
#include "ui/gfx/native_widget_types.h"
-namespace content {
-class WebContents;
-}
-
namespace web_modal {
class ModalDialogHost;
}
diff --git a/chromium/components/constrained_window/constrained_window_views_unittest.cc b/chromium/components/constrained_window/constrained_window_views_unittest.cc
index a0dd9dfddc3..26a4b6a21b7 100644
--- a/chromium/components/constrained_window/constrained_window_views_unittest.cc
+++ b/chromium/components/constrained_window/constrained_window_views_unittest.cc
@@ -5,10 +5,14 @@
#include "components/constrained_window/constrained_window_views.h"
#include <memory>
+#include <vector>
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "components/constrained_window/constrained_window_views_client.h"
#include "components/web_modal/test_web_contents_modal_dialog_host.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
@@ -228,4 +232,38 @@ TEST_F(ConstrainedWindowViewsTest, NullModalParent) {
widget->CloseNow();
}
+// Make sure dialogs presented off-screen are properly clamped to the nearest
+// screen.
+TEST_F(ConstrainedWindowViewsTest, ClampDialogToNearestDisplay) {
+ // Make sure the dialog will fit fully on the display
+ contents()->set_preferred_size(gfx::Size(200, 100));
+
+ // First, make sure the host and dialog are sized and positioned.
+ UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
+
+ const display::Screen* screen = display::Screen::GetScreen();
+ const display::Display display = screen->GetPrimaryDisplay();
+ // Within the tests there is only 1 display. Error if that ever changes.
+ EXPECT_EQ(screen->GetNumDisplays(), 1);
+ const gfx::Rect extents = display.work_area();
+
+ // Move the host completely off the screen.
+ views::Widget* host_widget =
+ views::Widget::GetWidgetForNativeView(dialog_host()->GetHostView());
+ gfx::Rect host_bounds = host_widget->GetWindowBoundsInScreen();
+ host_bounds.set_origin(gfx::Point(extents.right(), extents.bottom()));
+ host_widget->SetBounds(host_bounds);
+
+ // Make sure the host is fully off the screen.
+ EXPECT_FALSE(extents.Intersects(host_widget->GetWindowBoundsInScreen()));
+
+ // Now reposition the modal dialog into the display.
+ UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
+
+ const gfx::Rect dialog_bounds = dialog()->GetRootView()->GetBoundsInScreen();
+
+ // The dialog should now be fully on the display.
+ EXPECT_TRUE(extents.Contains(dialog_bounds));
+}
+
} // namespace constrained_window
diff --git a/chromium/components/content_settings/core/browser/BUILD.gn b/chromium/components/content_settings/core/browser/BUILD.gn
index 15d412ac5d8..3e4fa6688f5 100644
--- a/chromium/components/content_settings/core/browser/BUILD.gn
+++ b/chromium/components/content_settings/core/browser/BUILD.gn
@@ -3,15 +3,16 @@
# found in the LICENSE file.
import("//build/config/features.gni")
+import("//ppapi/features/features.gni")
static_library("browser") {
sources = [
- "content_settings_binary_value_map.cc",
- "content_settings_binary_value_map.h",
"content_settings_default_provider.cc",
"content_settings_default_provider.h",
"content_settings_details.cc",
"content_settings_details.h",
+ "content_settings_global_value_map.cc",
+ "content_settings_global_value_map.h",
"content_settings_info.cc",
"content_settings_info.h",
"content_settings_observable_provider.cc",
@@ -82,8 +83,9 @@ source_set("unit_tests") {
"//base/test:test_support",
"//components/content_settings/core/common",
"//components/content_settings/core/test:test_support",
- "//components/pref_registry:test_support",
+ "//components/pref_registry:pref_registry",
"//components/prefs",
+ "//components/sync_preferences:test_support",
"//extensions/features",
"//testing/gtest",
"//url",
diff --git a/chromium/components/content_settings/core/browser/DEPS b/chromium/components/content_settings/core/browser/DEPS
index dc52ba406c1..eddae7bcb63 100644
--- a/chromium/components/content_settings/core/browser/DEPS
+++ b/chromium/components/content_settings/core/browser/DEPS
@@ -3,6 +3,7 @@ include_rules = [
"+components/content_settings/core/test",
"+components/keyed_service/core",
"+components/pref_registry",
+ "+components/sync_preferences",
"+components/url_formatter",
"+extensions/features",
"+net/base",
diff --git a/chromium/components/content_settings/core/browser/content_settings_binary_value_map.cc b/chromium/components/content_settings/core/browser/content_settings_binary_value_map.cc
deleted file mode 100644
index ddfb7326c12..00000000000
--- a/chromium/components/content_settings/core/browser/content_settings_binary_value_map.cc
+++ /dev/null
@@ -1,64 +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/content_settings/core/browser/content_settings_binary_value_map.h"
-
-#include <utility>
-
-#include "base/synchronization/lock.h"
-#include "components/content_settings/core/browser/content_settings_rule.h"
-#include "components/content_settings/core/common/content_settings.h"
-
-namespace content_settings {
-
-namespace {
-
-class RuleIteratorBinary : public RuleIterator {
- public:
- RuleIteratorBinary(bool is_enabled, std::unique_ptr<base::AutoLock> auto_lock)
- : is_done_(is_enabled), auto_lock_(std::move(auto_lock)) {}
-
- bool HasNext() const override { return !is_done_; }
-
- Rule Next() override {
- DCHECK(HasNext());
- is_done_ = true;
- return Rule(ContentSettingsPattern::Wildcard(),
- ContentSettingsPattern::Wildcard(),
- new base::FundamentalValue(CONTENT_SETTING_BLOCK));
- }
-
- private:
- bool is_done_;
- std::unique_ptr<base::AutoLock> auto_lock_;
-};
-
-} // namespace
-
-BinaryValueMap::BinaryValueMap() {}
-
-BinaryValueMap::~BinaryValueMap() {}
-
-std::unique_ptr<RuleIterator> BinaryValueMap::GetRuleIterator(
- ContentSettingsType content_type,
- const ResourceIdentifier& resource_identifier,
- std::unique_ptr<base::AutoLock> auto_lock) const {
- if (!resource_identifier.empty())
- return nullptr;
- return std::unique_ptr<RuleIterator>(new RuleIteratorBinary(
- IsContentSettingEnabled(content_type), std::move(auto_lock)));
-}
-
-void BinaryValueMap::SetContentSettingDisabled(ContentSettingsType content_type,
- bool is_disabled) {
- is_enabled_[content_type] = !is_disabled;
-}
-
-bool BinaryValueMap::IsContentSettingEnabled(
- ContentSettingsType content_type) const {
- auto it = is_enabled_.find(content_type);
- return it == is_enabled_.end() || it->second;
-}
-
-} // namespace content_settings
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 f68ac5a5654..261fedb9d6b 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
@@ -177,11 +177,6 @@ DefaultProvider::DefaultProvider(PrefService* prefs, bool incognito)
GetPrefName(CONTENT_SETTINGS_TYPE_MIDI_SYSEX))),
CONTENT_SETTING_NUM_SETTINGS);
UMA_HISTOGRAM_ENUMERATION(
- "ContentSettings.DefaultKeygenSetting",
- IntToContentSetting(prefs_->GetInteger(
- GetPrefName(CONTENT_SETTINGS_TYPE_KEYGEN))),
- CONTENT_SETTING_NUM_SETTINGS);
- UMA_HISTOGRAM_ENUMERATION(
"ContentSettings.DefaultWebBluetoothGuardSetting",
IntToContentSetting(prefs_->GetInteger(
GetPrefName(CONTENT_SETTINGS_TYPE_BLUETOOTH_GUARD))),
diff --git a/chromium/components/content_settings/core/browser/content_settings_global_value_map.cc b/chromium/components/content_settings/core/browser/content_settings_global_value_map.cc
new file mode 100644
index 00000000000..94744fd7574
--- /dev/null
+++ b/chromium/components/content_settings/core/browser/content_settings_global_value_map.cc
@@ -0,0 +1,72 @@
+// 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/content_settings/core/browser/content_settings_global_value_map.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/synchronization/lock.h"
+#include "components/content_settings/core/browser/content_settings_rule.h"
+#include "components/content_settings/core/common/content_settings.h"
+
+namespace content_settings {
+
+namespace {
+
+class RuleIteratorSimple : public RuleIterator {
+ public:
+ RuleIteratorSimple(ContentSetting setting) : setting_(setting) {}
+
+ bool HasNext() const override { return !is_done_; }
+
+ Rule Next() override {
+ DCHECK(HasNext());
+ is_done_ = true;
+ return Rule(ContentSettingsPattern::Wildcard(),
+ ContentSettingsPattern::Wildcard(),
+ new base::FundamentalValue(setting_));
+ }
+
+ private:
+ const ContentSetting setting_;
+ bool is_done_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(RuleIteratorSimple);
+};
+
+} // namespace
+
+GlobalValueMap::GlobalValueMap() {}
+
+GlobalValueMap::~GlobalValueMap() {}
+
+std::unique_ptr<RuleIterator> GlobalValueMap::GetRuleIterator(
+ ContentSettingsType content_type,
+ const ResourceIdentifier& resource_identifier) const {
+ if (!resource_identifier.empty())
+ return nullptr;
+
+ auto it = settings_.find(content_type);
+ if (it == settings_.end())
+ return nullptr;
+
+ return base::MakeUnique<RuleIteratorSimple>(it->second);
+}
+
+void GlobalValueMap::SetContentSetting(ContentSettingsType content_type,
+ ContentSetting setting) {
+ if (setting == CONTENT_SETTING_DEFAULT)
+ settings_.erase(content_type);
+ else
+ settings_[content_type] = setting;
+}
+
+ContentSetting GlobalValueMap::GetContentSetting(
+ ContentSettingsType content_type) const {
+ auto it = settings_.find(content_type);
+ return it == settings_.end() ? CONTENT_SETTING_DEFAULT : it->second;
+}
+
+} // namespace content_settings
diff --git a/chromium/components/content_settings/core/browser/content_settings_binary_value_map.h b/chromium/components/content_settings/core/browser/content_settings_global_value_map.h
index dbce8982bc1..99407a1ab08 100644
--- a/chromium/components/content_settings/core/browser/content_settings_binary_value_map.h
+++ b/chromium/components/content_settings/core/browser/content_settings_global_value_map.h
@@ -2,43 +2,42 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_BINARY_VALUE_MAP_H_
-#define COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_BINARY_VALUE_MAP_H_
+#ifndef COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_GLOBAL_VALUE_MAP_H_
+#define COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_GLOBAL_VALUE_MAP_H_
#include <map>
#include "components/content_settings/core/browser/content_settings_provider.h"
#include "components/content_settings/core/common/content_settings_types.h"
-namespace base {
-class AutoLock;
-} // namespace base
-
namespace content_settings {
class RuleIterator;
-// A simplified value map that can be used to disable or enable the entire
-// Content Setting. The default behaviour is enabling the Content Setting if
-// it is not set explicitly.
-class BinaryValueMap {
+// A simplified value map that sets global content settings, i.e. applying to
+// all sites.
+// Note that this class does not do any synchronization. As content settings are
+// accessed from multiple threads, it's the responsibility of the client to
+// prevent concurrent access.
+class GlobalValueMap {
public:
- BinaryValueMap();
- ~BinaryValueMap();
+ GlobalValueMap();
+ ~GlobalValueMap();
// Returns nullptr to indicate the RuleIterator is empty.
std::unique_ptr<RuleIterator> GetRuleIterator(
ContentSettingsType content_type,
- const ResourceIdentifier& resource_identifier,
- std::unique_ptr<base::AutoLock> lock) const;
- void SetContentSettingDisabled(ContentSettingsType content_type,
- bool disabled);
- bool IsContentSettingEnabled(ContentSettingsType content_type) const;
+ const ResourceIdentifier& resource_identifier) const;
+ void SetContentSetting(ContentSettingsType content_type,
+ ContentSetting setting);
+ ContentSetting GetContentSetting(ContentSettingsType content_type) const;
private:
- std::map<ContentSettingsType, bool> is_enabled_;
+ std::map<ContentSettingsType, ContentSetting> settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(GlobalValueMap);
};
} // namespace content_settings
-#endif // COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_BINARY_VALUE_MAP_H_
+#endif // COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_GLOBAL_VALUE_MAP_H_
diff --git a/chromium/components/content_settings/core/browser/content_settings_info.h b/chromium/components/content_settings/core/browser/content_settings_info.h
index b7018dfff52..d1eb3a58248 100644
--- a/chromium/components/content_settings/core/browser/content_settings_info.h
+++ b/chromium/components/content_settings/core/browser/content_settings_info.h
@@ -21,12 +21,14 @@ class ContentSettingsInfo {
enum IncognitoBehavior {
// Content setting will be inherited from regular to incognito profiles
// as usual.
+ // TODO(dullweber): Remove as soon as INHERIT_IF_LESS_PERMISSIVE was tested.
INHERIT_IN_INCOGNITO,
- // Content setting will only partially inherit from regular to incognito
- // profiles: BLOCK will inherit as usual, but ALLOW will become ASK.
- // This is unusual, so seek privacy review before using this.
- INHERIT_IN_INCOGNITO_EXCEPT_ALLOW
+ // Content settings can be inherited if the setting is less permissive
+ // than the initial default value of the content setting. Example: A setting
+ // with an initial value of ASK will be inherited if it is set to BLOCK or
+ // ASK but ALLOW will become ASK in incognito mode.
+ INHERIT_IF_LESS_PERMISSIVE
};
// This object does not take ownership of |website_settings_info|.
diff --git a/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc b/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc
index 78a12a27a91..81a059b6e0d 100644
--- a/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc
@@ -55,11 +55,7 @@ const PrefsForManagedContentSettingsMapEntry
{prefs::kManagedPopupsAllowedForUrls, CONTENT_SETTINGS_TYPE_POPUPS,
CONTENT_SETTING_ALLOW},
{prefs::kManagedPopupsBlockedForUrls, CONTENT_SETTINGS_TYPE_POPUPS,
- CONTENT_SETTING_BLOCK},
- {prefs::kManagedKeygenAllowedForUrls,
- CONTENT_SETTINGS_TYPE_KEYGEN, CONTENT_SETTING_ALLOW},
- {prefs::kManagedKeygenBlockedForUrls,
- CONTENT_SETTINGS_TYPE_KEYGEN, CONTENT_SETTING_BLOCK}};
+ CONTENT_SETTING_BLOCK}};
} // namespace
@@ -89,7 +85,6 @@ const PolicyProvider::PrefsForManagedDefaultMapEntry
prefs::kManagedDefaultNotificationsSetting},
{CONTENT_SETTINGS_TYPE_PLUGINS, prefs::kManagedDefaultPluginsSetting},
{CONTENT_SETTINGS_TYPE_POPUPS, prefs::kManagedDefaultPopupsSetting},
- {CONTENT_SETTINGS_TYPE_KEYGEN, prefs::kManagedDefaultKeygenSetting},
{CONTENT_SETTINGS_TYPE_BLUETOOTH_GUARD,
prefs::kManagedDefaultWebBluetoothGuardSetting},
};
@@ -111,8 +106,6 @@ void PolicyProvider::RegisterProfilePrefs(
registry->RegisterListPref(prefs::kManagedPluginsBlockedForUrls);
registry->RegisterListPref(prefs::kManagedPopupsAllowedForUrls);
registry->RegisterListPref(prefs::kManagedPopupsBlockedForUrls);
- registry->RegisterListPref(prefs::kManagedKeygenAllowedForUrls);
- registry->RegisterListPref(prefs::kManagedKeygenBlockedForUrls);
// Preferences for default content setting policies. If a policy is not set of
// the corresponding preferences below is set to CONTENT_SETTING_DEFAULT.
registry->RegisterIntegerPref(prefs::kManagedDefaultCookiesSetting,
@@ -131,8 +124,6 @@ void PolicyProvider::RegisterProfilePrefs(
CONTENT_SETTING_DEFAULT);
registry->RegisterIntegerPref(prefs::kManagedDefaultPopupsSetting,
CONTENT_SETTING_DEFAULT);
- registry->RegisterIntegerPref(prefs::kManagedDefaultKeygenSetting,
- CONTENT_SETTING_DEFAULT);
registry->RegisterIntegerPref(prefs::kManagedDefaultWebBluetoothGuardSetting,
CONTENT_SETTING_DEFAULT);
}
@@ -162,8 +153,6 @@ PolicyProvider::PolicyProvider(PrefService* prefs) : prefs_(prefs) {
pref_change_registrar_.Add(prefs::kManagedPluginsBlockedForUrls, callback);
pref_change_registrar_.Add(prefs::kManagedPopupsAllowedForUrls, callback);
pref_change_registrar_.Add(prefs::kManagedPopupsBlockedForUrls, callback);
- pref_change_registrar_.Add(prefs::kManagedKeygenAllowedForUrls, callback);
- pref_change_registrar_.Add(prefs::kManagedKeygenBlockedForUrls, callback);
// The following preferences are only used to indicate if a default content
// setting is managed and to hold the managed default setting value. If the
// value for any of the following preferences is set then the corresponding
@@ -182,7 +171,6 @@ PolicyProvider::PolicyProvider(PrefService* prefs) : prefs_(prefs) {
prefs::kManagedDefaultMediaStreamSetting, callback);
pref_change_registrar_.Add(prefs::kManagedDefaultPluginsSetting, callback);
pref_change_registrar_.Add(prefs::kManagedDefaultPopupsSetting, callback);
- pref_change_registrar_.Add(prefs::kManagedDefaultKeygenSetting, callback);
pref_change_registrar_.Add(prefs::kManagedDefaultWebBluetoothGuardSetting,
callback);
}
@@ -300,7 +288,7 @@ void PolicyProvider::GetAutoSelectCertificateSettingsFromPreferences(
std::unique_ptr<base::Value> value = base::JSONReader::Read(
pattern_filter_json, base::JSON_ALLOW_TRAILING_COMMAS);
- if (!value || !value->IsType(base::Value::TYPE_DICTIONARY)) {
+ if (!value || !value->IsType(base::Value::Type::DICTIONARY)) {
VLOG(1) << "Ignoring invalid certificate auto select setting. Reason:"
" Invalid JSON object: " << pattern_filter_json;
continue;
@@ -428,9 +416,7 @@ void PolicyProvider::OnPreferenceChanged(const std::string& name) {
name == prefs::kManagedPluginsAllowedForUrls ||
name == prefs::kManagedPluginsBlockedForUrls ||
name == prefs::kManagedPopupsAllowedForUrls ||
- name == prefs::kManagedPopupsBlockedForUrls ||
- name == prefs::kManagedKeygenAllowedForUrls ||
- name == prefs::kManagedKeygenBlockedForUrls) {
+ name == prefs::kManagedPopupsBlockedForUrls) {
ReadManagedContentSettings(true);
ReadManagedDefaultSettings();
}
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 e19199368aa..0306c586032 100644
--- a/chromium/components/content_settings/core/browser/content_settings_pref.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_pref.cc
@@ -50,7 +50,7 @@ bool IsValueAllowedForType(const base::Value* value, ContentSettingsType type) {
// TODO(raymes): We should permit different types of base::Value for
// website settings.
- return value->GetType() == base::Value::TYPE_DICTIONARY;
+ return value->GetType() == base::Value::Type::DICTIONARY;
}
} // namespace
diff --git a/chromium/components/content_settings/core/browser/content_settings_pref.h b/chromium/components/content_settings/core/browser/content_settings_pref.h
index aa2ab178e3d..d06fe01e28b 100644
--- a/chromium/components/content_settings/core/browser/content_settings_pref.h
+++ b/chromium/components/content_settings/core/browser/content_settings_pref.h
@@ -27,10 +27,6 @@ class Clock;
class DictionaryValue;
}
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
namespace content_settings {
class RuleIterator;
@@ -78,10 +74,6 @@ class ContentSettingsPref {
bool TryLockForTesting() const;
private:
- // TODO(msramek): Currently only needed in the unittest to get the
- // corresponding pref name. Remove once pref names are in WebsiteSettingsInfo.
- friend class DeadlockCheckerObserver;
-
// Reads all content settings exceptions from the preference and loads them
// into the |value_map_|. The |value_map_| is cleared first.
void ReadContentSettingsFromPref();
diff --git a/chromium/components/content_settings/core/browser/content_settings_pref_provider.h b/chromium/components/content_settings/core/browser/content_settings_pref_provider.h
index 16c88610fa3..0c08dfc1657 100644
--- a/chromium/components/content_settings/core/browser/content_settings_pref_provider.h
+++ b/chromium/components/content_settings/core/browser/content_settings_pref_provider.h
@@ -20,7 +20,6 @@ class PrefService;
namespace base {
class Clock;
-class DictionaryValue;
}
namespace user_prefs {
diff --git a/chromium/components/content_settings/core/browser/content_settings_provider.h b/chromium/components/content_settings/core/browser/content_settings_provider.h
index 057ed81ba7e..3ee28c7d929 100644
--- a/chromium/components/content_settings/core/browser/content_settings_provider.h
+++ b/chromium/components/content_settings/core/browser/content_settings_provider.h
@@ -18,7 +18,6 @@ class ContentSettingsPattern;
namespace content_settings {
-struct Rule;
class RuleIterator;
class ProviderInterface {
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 b147dca2b92..917c6cc3348 100644
--- a/chromium/components/content_settings/core/browser/content_settings_registry.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_registry.cc
@@ -125,7 +125,7 @@ void ContentSettingsRegistry::Init() {
CONTENT_SETTING_SESSION_ONLY),
WebsiteSettingsInfo::REQUESTING_DOMAIN_ONLY_SCOPE,
WebsiteSettingsRegistry::ALL_PLATFORMS,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_IMAGES, "images", CONTENT_SETTING_ALLOW,
WebsiteSettingsInfo::SYNCABLE,
@@ -134,7 +134,7 @@ void ContentSettingsRegistry::Init() {
ValidSettings(CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK),
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_JAVASCRIPT, "javascript",
CONTENT_SETTING_ALLOW, WebsiteSettingsInfo::SYNCABLE,
@@ -144,7 +144,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_PLUGINS, "plugins",
CONTENT_SETTING_DETECT_IMPORTANT_CONTENT,
@@ -155,7 +155,7 @@ void ContentSettingsRegistry::Init() {
CONTENT_SETTING_DETECT_IMPORTANT_CONTENT),
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_POPUPS, "popups", CONTENT_SETTING_BLOCK,
WebsiteSettingsInfo::SYNCABLE,
@@ -164,7 +164,7 @@ void ContentSettingsRegistry::Init() {
ValidSettings(CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK),
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::ALL_PLATFORMS,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_GEOLOCATION, "geolocation",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -174,7 +174,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, "notifications",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -186,7 +186,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsRegistry::PLATFORM_ANDROID,
// See also NotificationPermissionContext::DecidePermission which
// implements additional incognito exceptions.
- ContentSettingsInfo::INHERIT_IN_INCOGNITO_EXCEPT_ALLOW);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, "media-stream-mic",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -196,7 +196,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, "media-stream-camera",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -206,7 +206,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_PPAPI_BROKER, "ppapi-broker",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -215,7 +215,7 @@ void ContentSettingsRegistry::Init() {
CONTENT_SETTING_ASK),
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, "automatic-downloads",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::SYNCABLE,
@@ -226,7 +226,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_MIDI_SYSEX, "midi-sysex", CONTENT_SETTING_ASK,
WebsiteSettingsInfo::SYNCABLE, WhitelistedSchemes(),
@@ -235,7 +235,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER,
"protected-media-identifier", CONTENT_SETTING_ASK,
@@ -245,7 +245,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE,
WebsiteSettingsRegistry::PLATFORM_ANDROID |
WebsiteSettingsRegistry::PLATFORM_CHROMEOS,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_DURABLE_STORAGE, "durable-storage",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -254,15 +254,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
-
- Register(CONTENT_SETTINGS_TYPE_KEYGEN, "keygen", CONTENT_SETTING_BLOCK,
- WebsiteSettingsInfo::SYNCABLE, WhitelistedSchemes(),
- ValidSettings(CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK),
- WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
- WebsiteSettingsRegistry::DESKTOP |
- WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC, "background-sync",
CONTENT_SETTING_ALLOW, WebsiteSettingsInfo::UNSYNCABLE,
@@ -271,7 +263,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_AUTOPLAY, "autoplay", CONTENT_SETTING_ALLOW,
WebsiteSettingsInfo::UNSYNCABLE, WhitelistedSchemes(),
@@ -279,7 +271,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
// Content settings that aren't used to store any data. TODO(raymes): use a
// different mechanism rather than content settings to represent these.
@@ -290,14 +282,14 @@ void ContentSettingsRegistry::Init() {
WhitelistedSchemes(), ValidSettings(),
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_MIXEDSCRIPT, "mixed-script",
CONTENT_SETTING_DEFAULT, WebsiteSettingsInfo::UNSYNCABLE,
WhitelistedSchemes(), ValidSettings(),
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
Register(CONTENT_SETTINGS_TYPE_BLUETOOTH_GUARD, "bluetooth-guard",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -306,7 +298,7 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
}
void ContentSettingsRegistry::Register(
@@ -321,11 +313,6 @@ void ContentSettingsRegistry::Register(
ContentSettingsInfo::IncognitoBehavior incognito_behavior) {
// Ensure that nothing has been registered yet for the given type.
DCHECK(!website_settings_registry_->Get(type));
- DCHECK(incognito_behavior !=
- ContentSettingsInfo::INHERIT_IN_INCOGNITO_EXCEPT_ALLOW ||
- base::ContainsKey(valid_settings, CONTENT_SETTING_ASK))
- << "If INHERIT_IN_INCOGNITO_EXCEPT_ALLOW is set, ASK must be listed as a "
- "valid setting.";
std::unique_ptr<base::Value> default_value(
new base::FundamentalValue(static_cast<int>(initial_default_value)));
const WebsiteSettingsInfo* website_settings_info =
diff --git a/chromium/components/content_settings/core/browser/content_settings_registry_unittest.cc b/chromium/components/content_settings/core/browser/content_settings_registry_unittest.cc
index bf595c44e03..d63d180e0bf 100644
--- a/chromium/components/content_settings/core/browser/content_settings_registry_unittest.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_registry_unittest.cc
@@ -71,7 +71,7 @@ TEST_F(ContentSettingsRegistryTest, Properties) {
// Check the other properties are populated correctly.
EXPECT_TRUE(info->IsSettingValid(CONTENT_SETTING_SESSION_ONLY));
EXPECT_FALSE(info->IsSettingValid(CONTENT_SETTING_ASK));
- EXPECT_EQ(ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ EXPECT_EQ(ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
info->incognito_behavior());
// Check the WebsiteSettingsInfo is populated correctly.
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 176c866beeb..9458bf5007e 100644
--- a/chromium/components/content_settings/core/browser/content_settings_utils.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_utils.cc
@@ -37,6 +37,25 @@ static_assert(arraysize(kContentSettingsStringMapping) ==
"kContentSettingsToFromString should have "
"CONTENT_SETTING_NUM_SETTINGS elements");
+// Content settings sorted from most to least permissive. The order is chosen
+// to check if a permission grants more rights than another. This is intuitive
+// for ALLOW, ASK and BLOCK. SESSION_ONLY and DETECT_IMPORTANT_CONTENT are never
+// used in the same setting so their respective order is not important but both
+// belong between ALLOW and ASK. DEFAULT should never be used and is therefore
+// not part of this array.
+const ContentSetting kContentSettingOrder[] = {
+ CONTENT_SETTING_ALLOW,
+ CONTENT_SETTING_SESSION_ONLY,
+ CONTENT_SETTING_DETECT_IMPORTANT_CONTENT,
+ CONTENT_SETTING_ASK,
+ CONTENT_SETTING_BLOCK
+};
+
+static_assert(arraysize(kContentSettingOrder) ==
+ CONTENT_SETTING_NUM_SETTINGS - 1,
+ "kContentSettingOrder should have CONTENT_SETTING_NUM_SETTINGS-1"
+ "entries");
+
} // namespace
namespace content_settings {
@@ -96,10 +115,8 @@ PatternPair ParsePatternString(const std::string& pattern_str) {
}
PatternPair pattern_pair;
- pattern_pair.first =
- ContentSettingsPattern::FromString(pattern_str_list[0]);
- pattern_pair.second =
- ContentSettingsPattern::FromString(pattern_str_list[1]);
+ pattern_pair.first = ContentSettingsPattern::FromString(pattern_str_list[0]);
+ pattern_pair.second = ContentSettingsPattern::FromString(pattern_str_list[1]);
return pattern_pair;
}
@@ -158,4 +175,17 @@ void GetRendererContentSettingRules(const HostContentSettingsMap* map,
&(rules->autoplay_rules));
}
+bool IsMorePermissive(ContentSetting a, ContentSetting b) {
+ // Check whether |a| or |b| is reached first in kContentSettingOrder.
+ // If |a| is first, it means that |a| is more permissive than |b|.
+ for (ContentSetting setting : kContentSettingOrder) {
+ if (setting == b)
+ return false;
+ if (setting == a)
+ return true;
+ }
+ NOTREACHED();
+ return true;
+}
+
} // namespace content_settings
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 e3d5a4a171a..fdcaf0bc6fa 100644
--- a/chromium/components/content_settings/core/browser/content_settings_utils.h
+++ b/chromium/components/content_settings/core/browser/content_settings_utils.h
@@ -18,14 +18,10 @@ namespace base {
class Value;
}
-class GURL;
class HostContentSettingsMap;
namespace content_settings {
-class ProviderInterface;
-class RuleIterator;
-
typedef std::pair<ContentSettingsPattern, ContentSettingsPattern> PatternPair;
// Helper class to iterate over only the values in a map.
@@ -86,6 +82,9 @@ std::unique_ptr<base::Value> ContentSettingToValue(ContentSetting setting);
void GetRendererContentSettingRules(const HostContentSettingsMap* map,
RendererContentSettingRules* rules);
+// Returns true if setting |a| is more permissive than setting |b|.
+bool IsMorePermissive(ContentSetting a, ContentSetting b);
+
} // namespace content_settings
#endif // COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_UTILS_H_
diff --git a/chromium/components/content_settings/core/browser/content_settings_utils_unittest.cc b/chromium/components/content_settings/core/browser/content_settings_utils_unittest.cc
index f51e46148e2..f38264b8b9a 100644
--- a/chromium/components/content_settings/core/browser/content_settings_utils_unittest.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_utils_unittest.cc
@@ -12,6 +12,8 @@
#include "components/content_settings/core/test/content_settings_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace content_settings {
+
namespace {
const char* const kContentSettingNames[] = {
@@ -28,36 +30,34 @@ static_assert(arraysize(kContentSettingNames) == CONTENT_SETTING_NUM_SETTINGS,
} // namespace
TEST(ContentSettingsUtilsTest, ParsePatternString) {
- content_settings::PatternPair pattern_pair;
+ PatternPair pattern_pair;
- pattern_pair = content_settings::ParsePatternString(std::string());
+ pattern_pair = ParsePatternString(std::string());
EXPECT_FALSE(pattern_pair.first.IsValid());
EXPECT_FALSE(pattern_pair.second.IsValid());
- pattern_pair = content_settings::ParsePatternString(",");
+ pattern_pair = ParsePatternString(",");
EXPECT_FALSE(pattern_pair.first.IsValid());
EXPECT_FALSE(pattern_pair.second.IsValid());
- pattern_pair = content_settings::ParsePatternString("http://www.foo.com");
+ pattern_pair = ParsePatternString("http://www.foo.com");
EXPECT_TRUE(pattern_pair.first.IsValid());
EXPECT_EQ(pattern_pair.second, ContentSettingsPattern::Wildcard());
// This inconsistency is to recover from some broken code.
- pattern_pair = content_settings::ParsePatternString("http://www.foo.com,");
+ pattern_pair = ParsePatternString("http://www.foo.com,");
EXPECT_TRUE(pattern_pair.first.IsValid());
EXPECT_FALSE(pattern_pair.second.IsValid());
- pattern_pair = content_settings::ParsePatternString(
- "http://www.foo.com,http://www.bar.com");
+ pattern_pair = ParsePatternString("http://www.foo.com,http://www.bar.com");
EXPECT_TRUE(pattern_pair.first.IsValid());
EXPECT_TRUE(pattern_pair.second.IsValid());
- pattern_pair = content_settings::ParsePatternString(
- "http://www.foo.com,http://www.bar.com,");
+ pattern_pair = ParsePatternString("http://www.foo.com,http://www.bar.com,");
EXPECT_FALSE(pattern_pair.first.IsValid());
EXPECT_FALSE(pattern_pair.second.IsValid());
- pattern_pair = content_settings::ParsePatternString(
+ pattern_pair = ParsePatternString(
"http://www.foo.com,http://www.bar.com,http://www.error.com");
EXPECT_FALSE(pattern_pair.first.IsValid());
EXPECT_FALSE(pattern_pair.second.IsValid());
@@ -65,22 +65,77 @@ TEST(ContentSettingsUtilsTest, ParsePatternString) {
TEST(ContentSettingsUtilsTest, ContentSettingsStringMap) {
std::string setting_string =
- content_settings::ContentSettingToString(CONTENT_SETTING_NUM_SETTINGS);
+ ContentSettingToString(CONTENT_SETTING_NUM_SETTINGS);
EXPECT_TRUE(setting_string.empty());
for (size_t i = 0; i < arraysize(kContentSettingNames); ++i) {
ContentSetting setting = static_cast<ContentSetting>(i);
- setting_string = content_settings::ContentSettingToString(setting);
+ setting_string = ContentSettingToString(setting);
EXPECT_EQ(kContentSettingNames[i], setting_string);
ContentSetting converted_setting;
if (i == 0) {
- EXPECT_FALSE(content_settings::ContentSettingFromString(
+ EXPECT_FALSE(ContentSettingFromString(
kContentSettingNames[i], &converted_setting));
} else {
- EXPECT_TRUE(content_settings::ContentSettingFromString(
+ EXPECT_TRUE(ContentSettingFromString(
kContentSettingNames[i], &converted_setting));
}
EXPECT_EQ(setting, converted_setting);
}
}
+
+TEST(ContentSettingsUtilsTest, IsMorePermissive) {
+ EXPECT_TRUE(IsMorePermissive(
+ CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK));
+ EXPECT_TRUE(IsMorePermissive(
+ CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK));
+ EXPECT_TRUE(IsMorePermissive(
+ CONTENT_SETTING_ALLOW, CONTENT_SETTING_DETECT_IMPORTANT_CONTENT));
+ EXPECT_TRUE(IsMorePermissive(
+ CONTENT_SETTING_ALLOW, CONTENT_SETTING_SESSION_ONLY));
+
+ EXPECT_TRUE(IsMorePermissive(
+ CONTENT_SETTING_SESSION_ONLY, CONTENT_SETTING_ASK));
+ EXPECT_TRUE(IsMorePermissive(
+ CONTENT_SETTING_SESSION_ONLY, CONTENT_SETTING_BLOCK));
+
+ EXPECT_TRUE(IsMorePermissive(
+ CONTENT_SETTING_DETECT_IMPORTANT_CONTENT, CONTENT_SETTING_ASK));
+ EXPECT_TRUE(IsMorePermissive(
+ CONTENT_SETTING_DETECT_IMPORTANT_CONTENT, CONTENT_SETTING_BLOCK));
+
+ EXPECT_TRUE(IsMorePermissive(CONTENT_SETTING_ASK, CONTENT_SETTING_BLOCK));
+
+ EXPECT_FALSE(IsMorePermissive(
+ CONTENT_SETTING_BLOCK, CONTENT_SETTING_ALLOW));
+ EXPECT_FALSE(IsMorePermissive(
+ CONTENT_SETTING_BLOCK, CONTENT_SETTING_DETECT_IMPORTANT_CONTENT));
+ EXPECT_FALSE(IsMorePermissive(
+ CONTENT_SETTING_BLOCK, CONTENT_SETTING_SESSION_ONLY));
+ EXPECT_FALSE(IsMorePermissive(CONTENT_SETTING_BLOCK, CONTENT_SETTING_ASK));
+
+ EXPECT_FALSE(IsMorePermissive(
+ CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW));
+ EXPECT_FALSE(IsMorePermissive(
+ CONTENT_SETTING_ASK, CONTENT_SETTING_SESSION_ONLY));
+ EXPECT_FALSE(IsMorePermissive(
+ CONTENT_SETTING_ASK, CONTENT_SETTING_DETECT_IMPORTANT_CONTENT));
+
+ EXPECT_FALSE(IsMorePermissive(
+ CONTENT_SETTING_SESSION_ONLY, CONTENT_SETTING_ALLOW));
+ EXPECT_FALSE(IsMorePermissive(
+ CONTENT_SETTING_DETECT_IMPORTANT_CONTENT, CONTENT_SETTING_ALLOW));
+
+ EXPECT_FALSE(IsMorePermissive(
+ CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW));
+
+ // Check that all possible ContentSettings except CONTENT_SETTING_DEFAULT are
+ // handled.
+ for (int i = 1; i < CONTENT_SETTING_NUM_SETTINGS; ++i) {
+ auto s = static_cast<ContentSetting>(i);
+ EXPECT_FALSE(IsMorePermissive(s, s));
+ }
+}
+
+} // namespace content_settings \ No newline at end of file
diff --git a/chromium/components/content_settings/core/browser/cookie_settings.cc b/chromium/components/content_settings/core/browser/cookie_settings.cc
index b5db52bd258..9f616f4bbc9 100644
--- a/chromium/components/content_settings/core/browser/cookie_settings.cc
+++ b/chromium/components/content_settings/core/browser/cookie_settings.cc
@@ -58,18 +58,36 @@ ContentSetting CookieSettings::GetDefaultCookieSetting(
bool CookieSettings::IsReadingCookieAllowed(const GURL& url,
const GURL& first_party_url) const {
- ContentSetting setting = GetCookieSetting(url, first_party_url, false, NULL);
- return IsAllowed(setting);
+ ContentSetting reading_setting;
+ GetCookieSetting(url, first_party_url, nullptr, &reading_setting,
+ nullptr /* setting_cookie */);
+ return IsAllowed(reading_setting);
}
bool CookieSettings::IsSettingCookieAllowed(const GURL& url,
const GURL& first_party_url) const {
- ContentSetting setting = GetCookieSetting(url, first_party_url, true, NULL);
- return IsAllowed(setting);
+ ContentSetting setting_setting;
+ GetCookieSetting(url, first_party_url, nullptr, nullptr /* reading_cookie */,
+ &setting_setting);
+ return IsAllowed(setting_setting);
+}
+
+void CookieSettings::GetReadingAndSettingCookieAllowed(
+ const GURL& url,
+ const GURL& first_party_url,
+ bool* reading_cookie_allowed,
+ bool* setting_cookie_allowed) const {
+ ContentSetting reading_setting;
+ ContentSetting setting_setting;
+ GetCookieSetting(url, first_party_url, nullptr, &reading_setting,
+ &setting_setting);
+ *reading_cookie_allowed = IsAllowed(reading_setting);
+ *setting_cookie_allowed = IsAllowed(setting_setting);
}
bool CookieSettings::IsCookieSessionOnly(const GURL& origin) const {
- ContentSetting setting = GetCookieSetting(origin, origin, true, NULL);
+ ContentSetting setting;
+ GetCookieSetting(origin, origin, nullptr, nullptr, &setting);
DCHECK(IsValidSetting(setting));
return (setting == CONTENT_SETTING_SESSION_ONLY);
}
@@ -122,18 +140,29 @@ void CookieSettings::ShutdownOnUIThread() {
pref_change_registrar_.RemoveAll();
}
-ContentSetting CookieSettings::GetCookieSetting(const GURL& url,
- const GURL& first_party_url,
- bool setting_cookie,
- SettingSource* source) const {
+void CookieSettings::GetCookieSetting(const GURL& url,
+ const GURL& first_party_url,
+ content_settings::SettingSource* source,
+ ContentSetting* reading_cookie,
+ ContentSetting* setting_cookie) const {
// Auto-allow in extensions or for WebUI embedded in a secure origin.
- if (url.SchemeIsCryptographic() && first_party_url.SchemeIs(kChromeUIScheme))
- return CONTENT_SETTING_ALLOW;
+ if (first_party_url.SchemeIs(kChromeUIScheme) &&
+ url.SchemeIsCryptographic()) {
+ if (reading_cookie)
+ *reading_cookie = CONTENT_SETTING_ALLOW;
+ if (setting_cookie)
+ *setting_cookie = CONTENT_SETTING_ALLOW;
+ return;
+ }
#if BUILDFLAG(ENABLE_EXTENSIONS)
- if (url.SchemeIs(kExtensionScheme) &&
- first_party_url.SchemeIs(kExtensionScheme)) {
- return CONTENT_SETTING_ALLOW;
+ if (url.SchemeIs(extension_scheme_) &&
+ first_party_url.SchemeIs(extension_scheme_)) {
+ if (reading_cookie)
+ *reading_cookie = CONTENT_SETTING_ALLOW;
+ if (setting_cookie)
+ *setting_cookie = CONTENT_SETTING_ALLOW;
+ return;
}
#endif
@@ -147,26 +176,27 @@ ContentSetting CookieSettings::GetCookieSetting(const GURL& url,
*source = info.source;
// If no explicit exception has been made and third-party cookies are blocked
- // by default, apply that rule.
- if (info.primary_pattern.MatchesAllHosts() &&
- info.secondary_pattern.MatchesAllHosts() &&
- ShouldBlockThirdPartyCookies() &&
- !first_party_url.SchemeIs(extension_scheme_)) {
- net::StaticCookiePolicy policy(
- net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES);
- int rv;
- if (setting_cookie)
- rv = policy.CanSetCookie(url, first_party_url);
- else
- rv = policy.CanGetCookies(url, first_party_url);
- DCHECK_NE(net::ERR_IO_PENDING, rv);
- if (rv != net::OK)
- return CONTENT_SETTING_BLOCK;
- }
+ // by default, apply CONTENT_SETTING_BLOCKED.
+ bool block_third = info.primary_pattern.MatchesAllHosts() &&
+ info.secondary_pattern.MatchesAllHosts() &&
+ ShouldBlockThirdPartyCookies() &&
+ !first_party_url.SchemeIs(extension_scheme_);
+ net::StaticCookiePolicy policy(
+ net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES);
// We should always have a value, at least from the default provider.
DCHECK(value.get());
- return ValueToContentSetting(value.get());
+ ContentSetting setting = ValueToContentSetting(value.get());
+ if (reading_cookie) {
+ bool block =
+ block_third && policy.CanGetCookies(url, first_party_url) != net::OK;
+ *reading_cookie = block ? CONTENT_SETTING_BLOCK : setting;
+ }
+ if (setting_cookie) {
+ bool block =
+ block_third && policy.CanSetCookie(url, first_party_url) != net::OK;
+ *setting_cookie = block ? CONTENT_SETTING_BLOCK : setting;
+ }
}
CookieSettings::~CookieSettings() {
diff --git a/chromium/components/content_settings/core/browser/cookie_settings.h b/chromium/components/content_settings/core/browser/cookie_settings.h
index 26ffa8068ba..b1653f37a5a 100644
--- a/chromium/components/content_settings/core/browser/cookie_settings.h
+++ b/chromium/components/content_settings/core/browser/cookie_settings.h
@@ -17,7 +17,6 @@
#include "components/keyed_service/core/refcounted_keyed_service.h"
#include "components/prefs/pref_change_registrar.h"
-class ContentSettingsPattern;
class GURL;
class PrefService;
@@ -37,7 +36,7 @@ class CookieSettings : public RefcountedKeyedService {
// Returns the default content setting (CONTENT_SETTING_ALLOW,
// CONTENT_SETTING_BLOCK, or CONTENT_SETTING_SESSION_ONLY) for cookies. If
- // |provider_id| is not NULL, the id of the provider which provided the
+ // |provider_id| is not nullptr, the id of the provider which provided the
// default setting is assigned to it.
//
// This may be called on any thread.
@@ -57,6 +56,15 @@ class CookieSettings : public RefcountedKeyedService {
bool IsSettingCookieAllowed(const GURL& url,
const GURL& first_party_url) const;
+ // Gets the results from IsReadingCookieAllowed and IsSettingCookieAllowed in
+ // a performance efficient way.
+ //
+ // This may be called on any thread.
+ void GetReadingAndSettingCookieAllowed(const GURL& url,
+ const GURL& first_party_url,
+ bool* reading_cookie_allowed,
+ bool* setting_cookie_allowed) const;
+
// Returns true if the cookie set by a page identified by |url| should be
// session only. Querying this only makes sense if |IsSettingCookieAllowed|
// has returned true.
@@ -66,7 +74,7 @@ class CookieSettings : public RefcountedKeyedService {
// Returns all patterns with a non-default cookie setting, mapped to their
// actual settings, in the precedence order of the setting rules. |settings|
- // must be a non-NULL outparam.
+ // must be a non-nullptr outparam.
//
// This may be called on any thread.
void GetCookieSettings(ContentSettingsForOneType* settings) const;
@@ -95,11 +103,11 @@ class CookieSettings : public RefcountedKeyedService {
void ShutdownOnUIThread() override;
// A helper for applying third party cookie blocking rules.
- ContentSetting GetCookieSetting(
- const GURL& url,
- const GURL& first_party_url,
- bool setting_cookie,
- content_settings::SettingSource* source) const;
+ void GetCookieSetting(const GURL& url,
+ const GURL& first_party_url,
+ content_settings::SettingSource* source,
+ ContentSetting* reading_cookie,
+ ContentSetting* setting_cookie) const;
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
diff --git a/chromium/components/content_settings/core/browser/cookie_settings_unittest.cc b/chromium/components/content_settings/core/browser/cookie_settings_unittest.cc
index 79a2f7383c8..2299b95802a 100644
--- a/chromium/components/content_settings/core/browser/cookie_settings_unittest.cc
+++ b/chromium/components/content_settings/core/browser/cookie_settings_unittest.cc
@@ -4,10 +4,11 @@
#include "components/content_settings/core/browser/cookie_settings.h"
+#include "base/message_loop/message_loop.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/pref_names.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "extensions/features/features.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -38,7 +39,11 @@ class CookieSettingsTest : public testing::Test {
~CookieSettingsTest() override { settings_map_->ShutdownOnUIThread(); }
protected:
- user_prefs::TestingPrefServiceSyncable prefs_;
+ // There must be a valid ThreadTaskRunnerHandle in HostContentSettingsMap's
+ // scope.
+ base::MessageLoop message_loop_;
+
+ sync_preferences::TestingPrefServiceSyncable prefs_;
scoped_refptr<HostContentSettingsMap> settings_map_;
scoped_refptr<CookieSettings> cookie_settings_;
const GURL kBlockedSite;
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 1f29b5b4d15..4ebb241f384 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
@@ -14,6 +14,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/time/clock.h"
#include "build/build_config.h"
#include "components/content_settings/core/browser/content_settings_default_provider.h"
@@ -36,6 +37,7 @@
#include "url/gurl.h"
using content_settings::WebsiteSettingsInfo;
+using content_settings::ContentSettingsInfo;
namespace {
@@ -97,30 +99,37 @@ std::unique_ptr<base::Value> ProcessIncognitoInheritanceBehavior(
ContentSettingsType content_type,
std::unique_ptr<base::Value> value) {
// Website setting inheritance can be completely disallowed.
- const content_settings::WebsiteSettingsInfo* website_settings_info =
+ const WebsiteSettingsInfo* website_settings_info =
content_settings::WebsiteSettingsRegistry::GetInstance()->Get(
content_type);
if (website_settings_info &&
website_settings_info->incognito_behavior() ==
- content_settings::WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO) {
+ WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO) {
return nullptr;
}
- // Content setting inheritance can be disabled for CONTENT_SETTING_ALLOW.
- const content_settings::ContentSettingsInfo* content_settings_info =
+ // Content setting inheritance can be for settings, that are more permissive
+ // than the initial value of a content setting.
+ const ContentSettingsInfo* content_settings_info =
content_settings::ContentSettingsRegistry::GetInstance()->Get(
content_type);
if (content_settings_info) {
- if (content_settings_info->incognito_behavior() !=
- content_settings::ContentSettingsInfo::
- INHERIT_IN_INCOGNITO_EXCEPT_ALLOW)
- return value;
- ContentSetting setting =
- content_settings::ValueToContentSetting(value.get());
- if (setting != CONTENT_SETTING_ALLOW)
- return value;
- DCHECK(content_settings_info->IsSettingValid(CONTENT_SETTING_ASK));
- return content_settings::ContentSettingToValue(CONTENT_SETTING_ASK);
+ ContentSettingsInfo::IncognitoBehavior behaviour =
+ content_settings_info->incognito_behavior();
+ switch (behaviour) {
+ case ContentSettingsInfo::INHERIT_IN_INCOGNITO:
+ return value;
+ case ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE:
+ ContentSetting setting =
+ content_settings::ValueToContentSetting(value.get());
+ const base::Value* initial_value = content_settings_info
+ ->website_settings_info()->initial_default_value();
+ ContentSetting initial_setting =
+ content_settings::ValueToContentSetting(initial_value);
+ if (content_settings::IsMorePermissive(setting, initial_setting))
+ return content_settings::ContentSettingToValue(initial_setting);
+ return value;
+ }
}
return value;
@@ -153,12 +162,24 @@ content_settings::PatternPair GetPatternsFromScopingType(
return patterns;
}
+content_settings::PatternPair GetPatternsForContentSettingsType(
+ const GURL& primary_url,
+ const GURL& secondary_url,
+ ContentSettingsType type) {
+ const WebsiteSettingsInfo* website_settings_info =
+ content_settings::WebsiteSettingsRegistry::GetInstance()->Get(type);
+ DCHECK(website_settings_info);
+ content_settings::PatternPair patterns = GetPatternsFromScopingType(
+ website_settings_info->scoping_type(), primary_url, secondary_url);
+ return patterns;
+}
+
} // namespace
HostContentSettingsMap::HostContentSettingsMap(PrefService* prefs,
bool is_incognito_profile,
bool is_guest_profile)
- :
+ : RefcountedKeyedService(base::ThreadTaskRunnerHandle::Get()),
#ifndef NDEBUG
used_from_thread_id_(base::PlatformThread::CurrentId()),
#endif
@@ -189,7 +210,6 @@ HostContentSettingsMap::HostContentSettingsMap(PrefService* prefs,
default_provider->AddObserver(this);
content_settings_providers_[DEFAULT_PROVIDER] = std::move(default_provider);
- MigrateKeygenSettings();
MigrateDomainScopedSettings(false);
RecordExceptionMetrics();
}
@@ -341,11 +361,8 @@ void HostContentSettingsMap::SetWebsiteSettingDefaultScope(
ContentSettingsType content_type,
const std::string& resource_identifier,
std::unique_ptr<base::Value> value) {
- const WebsiteSettingsInfo* info =
- content_settings::WebsiteSettingsRegistry::GetInstance()->Get(
- content_type);
- content_settings::PatternPair patterns = GetPatternsFromScopingType(
- info->scoping_type(), primary_url, secondary_url);
+ content_settings::PatternPair patterns = GetPatternsForContentSettingsType(
+ primary_url, secondary_url, content_type);
ContentSettingsPattern primary_pattern = patterns.first;
ContentSettingsPattern secondary_pattern = patterns.second;
if (!primary_pattern.IsValid() || !secondary_pattern.IsValid())
@@ -377,11 +394,34 @@ void HostContentSettingsMap::SetWebsiteSettingCustomScope(
NOTREACHED();
}
+bool HostContentSettingsMap::CanSetNarrowestContentSetting(
+ const GURL& primary_url,
+ const GURL& secondary_url,
+ ContentSettingsType type) const {
+ content_settings::PatternPair patterns =
+ GetNarrowestPatterns(primary_url, secondary_url, type);
+ return patterns.first.IsValid() && patterns.second.IsValid();
+}
+
void HostContentSettingsMap::SetNarrowestContentSetting(
const GURL& primary_url,
const GURL& secondary_url,
ContentSettingsType type,
ContentSetting setting) {
+ content_settings::PatternPair patterns =
+ GetNarrowestPatterns(primary_url, secondary_url, type);
+
+ if (!patterns.first.IsValid() || !patterns.second.IsValid())
+ return;
+
+ SetContentSettingCustomScope(patterns.first, patterns.second, type,
+ std::string(), setting);
+}
+
+content_settings::PatternPair HostContentSettingsMap::GetNarrowestPatterns (
+ const GURL& primary_url,
+ const GURL& secondary_url,
+ ContentSettingsType type) const {
// Permission settings are specified via rules. There exists always at least
// one rule for the default setting. Get the rule that currently defines
// the permission for the given permission |type|. Then test whether the
@@ -391,31 +431,29 @@ void HostContentSettingsMap::SetNarrowestContentSetting(
content_settings::SettingInfo info;
std::unique_ptr<base::Value> v = GetWebsiteSettingInternal(
primary_url, secondary_url, type, std::string(), &info);
- DCHECK_EQ(content_settings::SETTING_SOURCE_USER, info.source);
-
- const WebsiteSettingsInfo* website_settings_info =
- content_settings::WebsiteSettingsRegistry::GetInstance()->Get(type);
- content_settings::PatternPair patterns = GetPatternsFromScopingType(
- website_settings_info->scoping_type(), primary_url, secondary_url);
+ if (info.source != content_settings::SETTING_SOURCE_USER) {
+ // Return an invalid pattern if the current setting is not a user setting
+ // and thus can't be changed.
+ return content_settings::PatternPair();
+ }
- ContentSettingsPattern narrow_primary = patterns.first;
- ContentSettingsPattern narrow_secondary = patterns.second;
+ content_settings::PatternPair patterns = GetPatternsForContentSettingsType(
+ primary_url, secondary_url, type);
ContentSettingsPattern::Relation r1 =
info.primary_pattern.Compare(patterns.first);
if (r1 == ContentSettingsPattern::PREDECESSOR) {
- narrow_primary = info.primary_pattern;
+ patterns.first = info.primary_pattern;
} else if (r1 == ContentSettingsPattern::IDENTITY) {
ContentSettingsPattern::Relation r2 =
info.secondary_pattern.Compare(patterns.second);
DCHECK(r2 != ContentSettingsPattern::DISJOINT_ORDER_POST &&
r2 != ContentSettingsPattern::DISJOINT_ORDER_PRE);
if (r2 == ContentSettingsPattern::PREDECESSOR)
- narrow_secondary = info.secondary_pattern;
+ patterns.second = info.secondary_pattern;
}
- SetContentSettingCustomScope(narrow_primary, narrow_secondary, type,
- std::string(), setting);
+ return patterns;
}
void HostContentSettingsMap::SetContentSettingCustomScope(
@@ -450,15 +488,9 @@ void HostContentSettingsMap::SetContentSettingDefaultScope(
ContentSettingsType content_type,
const std::string& resource_identifier,
ContentSetting setting) {
- using content_settings::ContentSettingsInfo;
- const ContentSettingsInfo* info =
- content_settings::ContentSettingsRegistry::GetInstance()->Get(
- content_type);
- DCHECK(info);
+ content_settings::PatternPair patterns = GetPatternsForContentSettingsType(
+ primary_url, secondary_url, content_type);
- content_settings::PatternPair patterns =
- GetPatternsFromScopingType(info->website_settings_info()->scoping_type(),
- primary_url, secondary_url);
ContentSettingsPattern primary_pattern = patterns.first;
ContentSettingsPattern secondary_pattern = patterns.second;
if (!primary_pattern.IsValid() || !secondary_pattern.IsValid())
@@ -468,45 +500,6 @@ void HostContentSettingsMap::SetContentSettingDefaultScope(
resource_identifier, setting);
}
-void HostContentSettingsMap::MigrateKeygenSettings() {
- const content_settings::ContentSettingsInfo* info =
- content_settings::ContentSettingsRegistry::GetInstance()->Get(
- CONTENT_SETTINGS_TYPE_KEYGEN);
- if (info) {
- ContentSettingsForOneType settings;
- GetSettingsForOneType(CONTENT_SETTINGS_TYPE_KEYGEN, std::string(),
- &settings);
-
- for (const ContentSettingPatternSource& setting_entry : settings) {
- // Migrate user preference settings only.
- if (setting_entry.source != "preference")
- continue;
- // Migrate old-format settings only.
- if (setting_entry.secondary_pattern !=
- ContentSettingsPattern::Wildcard()) {
- GURL url(setting_entry.primary_pattern.ToString());
- // Pull out the value of the old-format setting. Only do this if the
- // patterns are as we expect them to be, otherwise the setting will just
- // be removed for safety.
- ContentSetting content_setting = CONTENT_SETTING_DEFAULT;
- if (setting_entry.primary_pattern == setting_entry.secondary_pattern &&
- url.is_valid()) {
- content_setting = GetContentSetting(
- url, url, CONTENT_SETTINGS_TYPE_KEYGEN, std::string());
- }
- // Remove the old pattern.
- SetContentSettingCustomScope(setting_entry.primary_pattern,
- setting_entry.secondary_pattern,
- CONTENT_SETTINGS_TYPE_KEYGEN,
- std::string(), CONTENT_SETTING_DEFAULT);
- // Set the new pattern.
- SetContentSettingDefaultScope(url, GURL(), CONTENT_SETTINGS_TYPE_KEYGEN,
- std::string(), content_setting);
- }
- }
- }
-}
-
void HostContentSettingsMap::MigrateDomainScopedSettings(bool after_sync) {
DomainToOriginMigrationStatus status =
static_cast<DomainToOriginMigrationStatus>(
@@ -795,6 +788,7 @@ void HostContentSettingsMap::OnContentSettingChanged(
}
HostContentSettingsMap::~HostContentSettingsMap() {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!prefs_);
}
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 b3eea7fff6e..b74865acb39 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
@@ -26,7 +26,6 @@
#include "components/keyed_service/core/refcounted_keyed_service.h"
#include "components/prefs/pref_change_registrar.h"
-class ExtensionService;
class GURL;
class PrefService;
@@ -39,6 +38,7 @@ namespace content_settings {
class ObservableProvider;
class ProviderInterface;
class PrefProvider;
+class RuleIterator;
class TestUtils;
}
@@ -196,6 +196,13 @@ class HostContentSettingsMap : public content_settings::Observer,
const std::string& resource_identifier,
std::unique_ptr<base::Value> value);
+ // Check if a call to SetNarrowestContentSetting would succeed or if it would
+ // fail because of an invalid pattern.
+ bool CanSetNarrowestContentSetting(
+ const GURL& primary_url,
+ const GURL& secondary_url,
+ ContentSettingsType type) const;
+
// Sets the most specific rule that currently defines the setting for the
// given content type. TODO(raymes): Remove this once all content settings
// are scoped to origin scope. There is no scope more narrow than origin
@@ -307,7 +314,6 @@ class HostContentSettingsMap : public content_settings::Observer,
DomainToOriginMigrationStatus);
FRIEND_TEST_ALL_PREFIXES(HostContentSettingsMapTest,
MigrateDomainScopedSettings);
- FRIEND_TEST_ALL_PREFIXES(HostContentSettingsMapTest, MigrateKeygenSettings);
friend class content_settings::TestUtils;
@@ -323,16 +329,6 @@ class HostContentSettingsMap : public content_settings::Observer,
ContentSettingsType content_type,
ProviderType* provider_type) const;
- // Migrate Keygen settings which only use a primary pattern. Settings which
- // only used a primary pattern were inconsistent in what they did with the
- // secondary pattern. Some stored a ContentSettingsPattern::Wildcard() whereas
- // others stored the same pattern twice. This function migrates all such
- // settings to use ContentSettingsPattern::Wildcard(). This allows us to make
- // the scoping code consistent across different settings.
- // TODO(lshang): Remove this when clients have migrated (~M53). We should
- // leave in some code to remove old-format settings for a long time.
- void MigrateKeygenSettings();
-
// Collect UMA data of exceptions.
void RecordExceptionMetrics();
@@ -364,6 +360,11 @@ class HostContentSettingsMap : public content_settings::Observer,
const std::string& resource_identifier,
content_settings::SettingInfo* info) const;
+ content_settings::PatternPair GetNarrowestPatterns(
+ const GURL& primary_url,
+ const GURL& secondary_url,
+ ContentSettingsType type) const;
+
static std::unique_ptr<base::Value> GetContentSettingValueAndPatterns(
const content_settings::ProviderInterface* provider,
const GURL& primary_url,
diff --git a/chromium/components/content_settings/core/browser/website_settings_info.cc b/chromium/components/content_settings/core/browser/website_settings_info.cc
index 8ee4a329a27..e7932081bac 100644
--- a/chromium/components/content_settings/core/browser/website_settings_info.cc
+++ b/chromium/components/content_settings/core/browser/website_settings_info.cc
@@ -48,7 +48,7 @@ WebsiteSettingsInfo::WebsiteSettingsInfo(
// TODO(raymes): We should migrate the underlying pref to be a dictionary
// rather than an int.
DCHECK(!initial_default_value_ ||
- initial_default_value_->IsType(base::Value::TYPE_INTEGER));
+ initial_default_value_->IsType(base::Value::Type::INTEGER));
}
WebsiteSettingsInfo::~WebsiteSettingsInfo() {}
diff --git a/chromium/components/content_settings/core/common/BUILD.gn b/chromium/components/content_settings/core/common/BUILD.gn
index a73d49ed145..42404221620 100644
--- a/chromium/components/content_settings/core/common/BUILD.gn
+++ b/chromium/components/content_settings/core/common/BUILD.gn
@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//mojo/public/tools/bindings/mojom.gni")
+
static_library("common") {
sources = [
"content_settings.cc",
@@ -19,6 +21,7 @@ static_library("common") {
deps = [
"//base",
+ "//mojo/public/cpp/bindings:struct_traits",
"//net",
"//url",
]
@@ -39,3 +42,9 @@ source_set("unit_tests") {
"//url",
]
}
+
+mojom("mojo_bindings") {
+ sources = [
+ "content_settings.mojom",
+ ]
+}
diff --git a/chromium/components/content_settings/core/common/DEPS b/chromium/components/content_settings/core/common/DEPS
index 201d8c82fc0..4e501b277a1 100644
--- a/chromium/components/content_settings/core/common/DEPS
+++ b/chromium/components/content_settings/core/common/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+mojo/public/cpp/bindings",
"+net/base",
"+testing",
"+url",
diff --git a/chromium/components/content_settings/core/common/OWNERS b/chromium/components/content_settings/core/common/OWNERS
new file mode 100644
index 00000000000..ac44cd00686
--- /dev/null
+++ b/chromium/components/content_settings/core/common/OWNERS
@@ -0,0 +1,5 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/content_settings/core/common/content_settings.cc b/chromium/components/content_settings/core/common/content_settings.cc
index 5e38200208a..5139efc6c54 100644
--- a/chromium/components/content_settings/core/common/content_settings.cc
+++ b/chromium/components/content_settings/core/common/content_settings.cc
@@ -52,7 +52,7 @@ ContentSettingsType kHistogramOrder[] = {
CONTENT_SETTINGS_TYPE_APP_BANNER,
CONTENT_SETTINGS_TYPE_SITE_ENGAGEMENT,
CONTENT_SETTINGS_TYPE_DURABLE_STORAGE,
- CONTENT_SETTINGS_TYPE_KEYGEN,
+ CONTENT_SETTINGS_TYPE_DEFAULT, // KEYGEN (removed)
CONTENT_SETTINGS_TYPE_BLUETOOTH_GUARD,
CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC,
CONTENT_SETTINGS_TYPE_AUTOPLAY,
diff --git a/chromium/components/content_settings/core/common/content_settings.h b/chromium/components/content_settings/core/common/content_settings.h
index 3179ef324bf..c441e280eaa 100644
--- a/chromium/components/content_settings/core/common/content_settings.h
+++ b/chromium/components/content_settings/core/common/content_settings.h
@@ -18,6 +18,9 @@
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.preferences.website
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: ContentSettingValues
+//
+// TODO(nigeltao): migrate the Java users of this enum to the mojom-generated
+// enum.
enum ContentSetting {
CONTENT_SETTING_DEFAULT = 0,
CONTENT_SETTING_ALLOW,
diff --git a/chromium/components/content_settings/core/common/content_settings.mojom b/chromium/components/content_settings/core/common/content_settings.mojom
new file mode 100644
index 00000000000..5c4c5d92a92
--- /dev/null
+++ b/chromium/components/content_settings/core/common/content_settings.mojom
@@ -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.
+
+module content_settings.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
+ // |is_scheme_wildcard| flag is set.
+ string scheme;
+
+ // True if the scheme wildcard is set.
+ bool is_scheme_wildcard;
+
+ // Normalized string that is either of the following:
+ // - IPv4 or IPv6
+ // - hostname
+ // - domain
+ // - empty string if the |is_host_wildcard flag is set.
+ string host;
+
+ // True if the domain wildcard is set.
+ bool has_domain_wildcard;
+
+ // String with the port to match. This string is empty if the
+ // |is_port_wildcard| flag is set.
+ string port;
+
+ // True if the port wildcard is set.
+ bool is_port_wildcard;
+
+ // TODO(markusheintz): Needed for legacy reasons. Remove. Path
+ // specification. Only used for content settings pattern with a "file"
+ // scheme part.
+ string path;
+
+ // True if the path wildcard is set.
+ bool is_path_wildcard;
+};
+
+// This mirrors the C++ type in content_settings_pattern.h.
+struct ContentSettingsPattern {
+ PatternParts parts;
+ bool is_valid;
+};
+
+// This mirrors the C++ type in content_settings.h.
+enum ContentSetting {
+ DEFAULT = 0,
+ ALLOW,
+ BLOCK,
+ ASK,
+ SESSION_ONLY,
+ DETECT_IMPORTANT_CONTENT
+};
+
+// This mirrors the C++ type in content_settings.h.
+struct ContentSettingPatternSource {
+ ContentSettingsPattern primary_pattern;
+ ContentSettingsPattern secondary_pattern;
+ ContentSetting setting;
+ string source;
+ bool incognito;
+};
+
+// This mirrors the C++ type in content_settings.h.
+struct RendererContentSettingRules {
+ array<ContentSettingPatternSource> image_rules;
+ array<ContentSettingPatternSource> script_rules;
+ array<ContentSettingPatternSource> autoplay_rules;
+};
diff --git a/chromium/components/content_settings/core/common/content_settings.typemap b/chromium/components/content_settings/core/common/content_settings.typemap
new file mode 100644
index 00000000000..200cd73767d
--- /dev/null
+++ b/chromium/components/content_settings/core/common/content_settings.typemap
@@ -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.
+
+deps = [
+ "//components/content_settings/core/common",
+ "//mojo/public/cpp/bindings",
+]
+mojom = "//components/content_settings/core/common/content_settings.mojom"
+public_deps = [
+ "//components/content_settings/core/common",
+]
+public_headers = [
+ "//components/content_settings/core/common/content_settings.h",
+ "//components/content_settings/core/common/content_settings_pattern.h",
+]
+sources = [
+ "//components/content_settings/core/common/content_settings_struct_traits.cc",
+]
+traits_headers = [ "//components/content_settings/core/common/content_settings_struct_traits.h" ]
+type_mappings = [
+ "content_settings.mojom.PatternParts=::ContentSettingsPattern::PatternParts",
+ "content_settings.mojom.ContentSettingsPattern=::ContentSettingsPattern",
+ "content_settings.mojom.ContentSetting=::ContentSetting",
+ "content_settings.mojom.ContentSettingPatternSource=::ContentSettingPatternSource",
+ "content_settings.mojom.RendererContentSettingRules=::RendererContentSettingRules",
+]
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 f0432f59930..7d6fc8d00dd 100644
--- a/chromium/components/content_settings/core/common/content_settings_pattern.h
+++ b/chromium/components/content_settings/core/common/content_settings_pattern.h
@@ -10,11 +10,16 @@
#include <string>
#include "base/gtest_prod_util.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
class GURL;
namespace content_settings {
class PatternParser;
+
+namespace mojom {
+class ContentSettingsPatternDataView;
+}
}
// A pattern used in content setting rules. See |IsValid| for a description of
@@ -216,7 +221,9 @@ class ContentSettingsPattern {
private:
friend class content_settings::PatternParser;
- friend class ContentSettingsPatternSerializer;
+ friend struct mojo::StructTraits<
+ content_settings::mojom::ContentSettingsPatternDataView,
+ ContentSettingsPattern>;
FRIEND_TEST_ALL_PREFIXES(ContentSettingsPatternParserTest, SerializePatterns);
class Builder;
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
new file mode 100644
index 00000000000..11ad9c92ed6
--- /dev/null
+++ b/chromium/components/content_settings/core/common/content_settings_struct_traits.cc
@@ -0,0 +1,104 @@
+// 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/content_settings/core/common/content_settings_struct_traits.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<content_settings::mojom::PatternPartsDataView,
+ ContentSettingsPattern::PatternParts>::
+ Read(content_settings::mojom::PatternPartsDataView data,
+ ContentSettingsPattern::PatternParts* out) {
+ out->is_scheme_wildcard = data.is_scheme_wildcard();
+ out->has_domain_wildcard = data.has_domain_wildcard();
+ out->is_port_wildcard = data.is_port_wildcard();
+ out->is_path_wildcard = data.is_path_wildcard();
+ return data.ReadScheme(&out->scheme) && data.ReadHost(&out->host) &&
+ data.ReadPort(&out->port) && data.ReadPath(&out->path);
+}
+
+// static
+bool StructTraits<content_settings::mojom::ContentSettingsPatternDataView,
+ ContentSettingsPattern>::
+ Read(content_settings::mojom::ContentSettingsPatternDataView data,
+ ContentSettingsPattern* out) {
+ out->is_valid_ = data.is_valid();
+ return data.ReadParts(&out->parts_);
+}
+
+// static
+content_settings::mojom::ContentSetting
+EnumTraits<content_settings::mojom::ContentSetting, ContentSetting>::ToMojom(
+ ContentSetting setting) {
+ switch (setting) {
+ case CONTENT_SETTING_DEFAULT:
+ return content_settings::mojom::ContentSetting::DEFAULT;
+ case CONTENT_SETTING_ALLOW:
+ return content_settings::mojom::ContentSetting::ALLOW;
+ case CONTENT_SETTING_BLOCK:
+ return content_settings::mojom::ContentSetting::BLOCK;
+ case CONTENT_SETTING_ASK:
+ return content_settings::mojom::ContentSetting::ASK;
+ case CONTENT_SETTING_SESSION_ONLY:
+ return content_settings::mojom::ContentSetting::SESSION_ONLY;
+ case CONTENT_SETTING_DETECT_IMPORTANT_CONTENT:
+ return content_settings::mojom::ContentSetting::DETECT_IMPORTANT_CONTENT;
+ case CONTENT_SETTING_NUM_SETTINGS:
+ // CONTENT_SETTING_NUM_SETTINGS is a dummy enum value.
+ break;
+ }
+ NOTREACHED();
+ return content_settings::mojom::ContentSetting::DEFAULT;
+}
+
+// static
+bool EnumTraits<content_settings::mojom::ContentSetting, ContentSetting>::
+ FromMojom(content_settings::mojom::ContentSetting setting,
+ ContentSetting* out) {
+ switch (setting) {
+ case content_settings::mojom::ContentSetting::DEFAULT:
+ *out = CONTENT_SETTING_DEFAULT;
+ return true;
+ case content_settings::mojom::ContentSetting::ALLOW:
+ *out = CONTENT_SETTING_ALLOW;
+ return true;
+ case content_settings::mojom::ContentSetting::BLOCK:
+ *out = CONTENT_SETTING_BLOCK;
+ return true;
+ case content_settings::mojom::ContentSetting::ASK:
+ *out = CONTENT_SETTING_ASK;
+ return true;
+ case content_settings::mojom::ContentSetting::SESSION_ONLY:
+ *out = CONTENT_SETTING_SESSION_ONLY;
+ return true;
+ case content_settings::mojom::ContentSetting::DETECT_IMPORTANT_CONTENT:
+ *out = CONTENT_SETTING_DETECT_IMPORTANT_CONTENT;
+ return true;
+ }
+ return false;
+}
+
+// static
+bool StructTraits<content_settings::mojom::ContentSettingPatternSourceDataView,
+ ContentSettingPatternSource>::
+ Read(content_settings::mojom::ContentSettingPatternSourceDataView data,
+ ContentSettingPatternSource* out) {
+ out->incognito = data.incognito();
+ return data.ReadPrimaryPattern(&out->primary_pattern) &&
+ data.ReadSecondaryPattern(&out->secondary_pattern) &&
+ data.ReadSetting(&out->setting) && data.ReadSource(&out->source);
+}
+
+// static
+bool StructTraits<content_settings::mojom::RendererContentSettingRulesDataView,
+ RendererContentSettingRules>::
+ Read(content_settings::mojom::RendererContentSettingRulesDataView data,
+ RendererContentSettingRules* out) {
+ return data.ReadImageRules(&out->image_rules) &&
+ data.ReadScriptRules(&out->script_rules) &&
+ data.ReadAutoplayRules(&out->autoplay_rules);
+}
+
+} // namespace mojo
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
new file mode 100644
index 00000000000..10c78efeafd
--- /dev/null
+++ b/chromium/components/content_settings/core/common/content_settings_struct_traits.h
@@ -0,0 +1,142 @@
+// 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_CONTENT_SETTINGS_CORE_COMMON_CONTENT_SETTINGS_STRUCT_TRAITS_H
+#define COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_CONTENT_SETTINGS_STRUCT_TRAITS_H
+
+#include <string>
+
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings.mojom.h"
+#include "mojo/public/cpp/bindings/enum_traits.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<content_settings::mojom::PatternPartsDataView,
+ ContentSettingsPattern::PatternParts> {
+ static const std::string& scheme(
+ const ContentSettingsPattern::PatternParts& r) {
+ return r.scheme;
+ }
+
+ static bool is_scheme_wildcard(
+ const ContentSettingsPattern::PatternParts& r) {
+ return r.is_scheme_wildcard;
+ }
+
+ static const std::string& host(
+ const ContentSettingsPattern::PatternParts& r) {
+ return r.host;
+ }
+
+ static bool has_domain_wildcard(
+ const ContentSettingsPattern::PatternParts& r) {
+ return r.has_domain_wildcard;
+ }
+
+ static const std::string& port(
+ const ContentSettingsPattern::PatternParts& r) {
+ return r.port;
+ }
+
+ static bool is_port_wildcard(const ContentSettingsPattern::PatternParts& r) {
+ return r.is_port_wildcard;
+ }
+
+ static const std::string& path(
+ const ContentSettingsPattern::PatternParts& r) {
+ return r.path;
+ }
+
+ static bool is_path_wildcard(const ContentSettingsPattern::PatternParts& r) {
+ return r.is_path_wildcard;
+ }
+
+ static bool Read(content_settings::mojom::PatternPartsDataView data,
+ ContentSettingsPattern::PatternParts* out);
+};
+
+template <>
+struct StructTraits<content_settings::mojom::ContentSettingsPatternDataView,
+ ContentSettingsPattern> {
+ static const ContentSettingsPattern::PatternParts& parts(
+ const ContentSettingsPattern& r) {
+ return r.parts_;
+ }
+
+ static bool is_valid(const ContentSettingsPattern& r) { return r.is_valid_; }
+
+ static bool Read(content_settings::mojom::ContentSettingsPatternDataView data,
+ ContentSettingsPattern* out);
+};
+
+template <>
+struct EnumTraits<content_settings::mojom::ContentSetting, ContentSetting> {
+ static content_settings::mojom::ContentSetting ToMojom(
+ ContentSetting setting);
+
+ static bool FromMojom(content_settings::mojom::ContentSetting setting,
+ ContentSetting* out);
+};
+
+template <>
+struct StructTraits<
+ content_settings::mojom::ContentSettingPatternSourceDataView,
+ ContentSettingPatternSource> {
+ static const ContentSettingsPattern& primary_pattern(
+ const ContentSettingPatternSource& r) {
+ return r.primary_pattern;
+ }
+
+ static const ContentSettingsPattern& secondary_pattern(
+ const ContentSettingPatternSource& r) {
+ return r.secondary_pattern;
+ }
+
+ static ContentSetting setting(const ContentSettingPatternSource& r) {
+ return r.setting;
+ }
+
+ static const std::string& source(const ContentSettingPatternSource& r) {
+ return r.source;
+ }
+
+ static bool incognito(const ContentSettingPatternSource& r) {
+ return r.incognito;
+ }
+
+ static bool Read(
+ content_settings::mojom::ContentSettingPatternSourceDataView data,
+ ContentSettingPatternSource* out);
+};
+
+template <>
+struct StructTraits<
+ content_settings::mojom::RendererContentSettingRulesDataView,
+ RendererContentSettingRules> {
+ static const std::vector<ContentSettingPatternSource>& image_rules(
+ const RendererContentSettingRules& r) {
+ return r.image_rules;
+ }
+
+ static const std::vector<ContentSettingPatternSource>& script_rules(
+ const RendererContentSettingRules& r) {
+ return r.script_rules;
+ }
+
+ static const std::vector<ContentSettingPatternSource>& autoplay_rules(
+ const RendererContentSettingRules& r) {
+ return r.autoplay_rules;
+ }
+
+ static bool Read(
+ content_settings::mojom::RendererContentSettingRulesDataView data,
+ RendererContentSettingRules* out);
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_CONTENT_SETTINGS_STRUCT_TRAITS_H
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 f4c8d73964d..f6814643385 100644
--- a/chromium/components/content_settings/core/common/content_settings_types.h
+++ b/chromium/components/content_settings/core/common/content_settings_types.h
@@ -40,7 +40,6 @@ enum ContentSettingsType {
CONTENT_SETTINGS_TYPE_DURABLE_STORAGE,
CONTENT_SETTINGS_TYPE_USB_CHOOSER_DATA,
CONTENT_SETTINGS_TYPE_BLUETOOTH_GUARD,
- CONTENT_SETTINGS_TYPE_KEYGEN,
CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC,
CONTENT_SETTINGS_TYPE_AUTOPLAY,
CONTENT_SETTINGS_TYPE_PROMPT_NO_DECISION_COUNT,
diff --git a/chromium/components/content_settings/core/common/pref_names.cc b/chromium/components/content_settings/core/common/pref_names.cc
index 36a7e628b32..08bb3112b88 100644
--- a/chromium/components/content_settings/core/common/pref_names.cc
+++ b/chromium/components/content_settings/core/common/pref_names.cc
@@ -42,8 +42,6 @@ const char kManagedDefaultPluginsSetting[] =
"profile.managed_default_content_settings.plugins";
const char kManagedDefaultPopupsSetting[] =
"profile.managed_default_content_settings.popups";
-const char kManagedDefaultKeygenSetting[] =
- "profile.managed_default_content_settings.keygen";
const char kManagedDefaultWebBluetoothGuardSetting[] =
"profile.managed_default_content_settings.web_bluetooth_guard";
@@ -77,9 +75,4 @@ const char kManagedPopupsAllowedForUrls[] =
"profile.managed_popups_allowed_for_urls";
const char kManagedPopupsBlockedForUrls[] =
"profile.managed_popups_blocked_for_urls";
-const char kManagedKeygenAllowedForUrls[] =
- "profile.managed_keygen_allowed_for_urls";
-const char kManagedKeygenBlockedForUrls[] =
- "profile.managed_keygen_blocked_for_urls";
-
} // namespace prefs
diff --git a/chromium/components/content_settings/core/common/pref_names.h b/chromium/components/content_settings/core/common/pref_names.h
index 9ddbd4c882d..daf1b6cf1ad 100644
--- a/chromium/components/content_settings/core/common/pref_names.h
+++ b/chromium/components/content_settings/core/common/pref_names.h
@@ -26,7 +26,6 @@ extern const char kManagedDefaultPopupsSetting[];
extern const char kManagedDefaultGeolocationSetting[];
extern const char kManagedDefaultNotificationsSetting[];
extern const char kManagedDefaultMediaStreamSetting[];
-extern const char kManagedDefaultKeygenSetting[];
extern const char kManagedDefaultWebBluetoothGuardSetting[];
extern const char kManagedCookiesAllowedForUrls[];
@@ -43,8 +42,6 @@ extern const char kManagedPopupsBlockedForUrls[];
extern const char kManagedNotificationsAllowedForUrls[];
extern const char kManagedNotificationsBlockedForUrls[];
extern const char kManagedAutoSelectCertificateForUrls[];
-extern const char kManagedKeygenAllowedForUrls[];
-extern const char kManagedKeygenBlockedForUrls[];
} // namespace prefs
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 e070500ffa3..c724c2ecde9 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
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/memory/ptr_util.h"
#include "components/contextual_search/browser/contextual_search_js_api_handler.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
diff --git a/chromium/components/contextual_search/renderer/contextual_search_wrapper.h b/chromium/components/contextual_search/renderer/contextual_search_wrapper.h
index 9f0c2598fc1..23162f2a0a8 100644
--- a/chromium/components/contextual_search/renderer/contextual_search_wrapper.h
+++ b/chromium/components/contextual_search/renderer/contextual_search_wrapper.h
@@ -16,10 +16,6 @@ namespace blink {
class WebFrame;
}
-namespace gin {
-class Arguments;
-}
-
namespace contextual_search {
// Wrapper for injecting Contextual Search JavaScript
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 573f5d35122..ae397056b9f 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
@@ -7,6 +7,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/memory/ptr_util.h"
#include "components/contextual_search/renderer/contextual_search_wrapper.h"
#include "components/contextual_search/renderer/overlay_page_notifier_service_impl.h"
#include "content/public/renderer/render_frame.h"
diff --git a/chromium/components/crash/content/app/BUILD.gn b/chromium/components/crash/content/app/BUILD.gn
index 4bdcc707212..0cf0c6ca109 100644
--- a/chromium/components/crash/content/app/BUILD.gn
+++ b/chromium/components/crash/content/app/BUILD.gn
@@ -66,13 +66,22 @@ if (is_win) {
sources = [
"crash_switches.cc",
"crash_switches.h",
+ "fallback_crash_handler_launcher_win.cc",
+ "fallback_crash_handler_launcher_win.h",
+ "fallback_crash_handler_win.cc",
+ "fallback_crash_handler_win.h",
+ "fallback_crash_handling_win.cc",
+ "fallback_crash_handling_win.h",
"run_as_crashpad_handler_win.cc",
"run_as_crashpad_handler_win.h",
]
deps = [
"//base",
+ "//chrome/install_static:install_static_util",
+ "//third_party/crashpad/crashpad/client",
"//third_party/crashpad/crashpad/handler:handler_lib",
+ "//third_party/crashpad/crashpad/minidump",
]
}
}
@@ -213,15 +222,26 @@ source_set("unit_tests") {
testonly = true
sources = [
"crash_keys_win_unittest.cc",
+ "fallback_crash_handler_launcher_win_unittest.cc",
+ "fallback_crash_handler_win_unittest.cc",
+ "fallback_crash_handling_win_unittest.cc",
]
deps = [
":lib",
"//base",
+ "//base/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
if (is_win) {
- deps += [ "//breakpad:client" ]
+ deps += [
+ ":run_as_crashpad_handler",
+ "//breakpad:client",
+ "//third_party/crashpad/crashpad/client:client",
+ "//third_party/crashpad/crashpad/compat",
+ "//third_party/crashpad/crashpad/snapshot:snapshot",
+ "//third_party/crashpad/crashpad/util",
+ ]
}
}
diff --git a/chromium/components/crash/content/app/breakpad_linux.cc b/chromium/components/crash/content/app/breakpad_linux.cc
index 9ebc33f4f01..6e5058e9563 100644
--- a/chromium/components/crash/content/app/breakpad_linux.cc
+++ b/chromium/components/crash/content/app/breakpad_linux.cc
@@ -101,6 +101,8 @@ ExceptionHandler* g_breakpad = nullptr;
const char* g_asan_report_str = nullptr;
#endif
#if defined(OS_ANDROID)
+#define G_DUMPS_SUPPRESSED_MAGIC 0x5AFECEDE
+uint32_t g_dumps_suppressed = 0;
char* g_process_type = nullptr;
ExceptionHandler* g_microdump = nullptr;
int g_signal_code_pipe_fd = -1;
@@ -553,16 +555,27 @@ void CrashReporterWriter::AddFileContents(const char* filename_msg,
}
#endif // defined(OS_CHROMEOS)
-void DumpProcess() {
- if (g_breakpad)
- g_breakpad->WriteMinidump();
-
#if defined(OS_ANDROID)
- // If microdumps are enabled write also a microdump on the system log.
- if (g_microdump)
- g_microdump->WriteMinidump();
-#endif
+// Writes the "package" field, which is in the format:
+// $PACKAGE_NAME v$VERSION_CODE ($VERSION_NAME)
+void WriteAndroidPackage(MimeWriter& writer,
+ base::android::BuildInfo* android_build_info) {
+ // The actual size limits on packageId and versionName are quite generous.
+ // Limit to a reasonable size rather than allocating theoretical limits.
+ const int kMaxSize = 1024;
+ char buf[kMaxSize];
+
+ // Not using sprintf to ensure no heap allocations.
+ my_strlcpy(buf, android_build_info->package_name(), kMaxSize);
+ my_strlcat(buf, " v", kMaxSize);
+ my_strlcat(buf, android_build_info->package_version_code(), kMaxSize);
+ my_strlcat(buf, " (", kMaxSize);
+ my_strlcat(buf, android_build_info->package_version_name(), kMaxSize);
+ my_strlcat(buf, ")", kMaxSize);
+
+ writer.AddPairString("package", buf);
}
+#endif // defined(OS_ANDROID)
#if defined(OS_ANDROID)
const char kGoogleBreakpad[] = "google-breakpad";
@@ -581,6 +594,10 @@ size_t WriteNewline() {
}
#if defined(OS_ANDROID)
+bool ShouldGenerateDump(void *context) {
+ return g_dumps_suppressed != G_DUMPS_SUPPRESSED_MAGIC;
+}
+
void AndroidLogWriteHorizontalRule() {
__android_log_write(ANDROID_LOG_WARN, kGoogleBreakpad,
"### ### ### ### ### ### ### ### ### ### ### ### ###");
@@ -622,7 +639,22 @@ bool FinalizeCrashDoneAndroid(bool is_browser_process) {
}
return false;
}
-#endif
+
+bool MicrodumpCrashDone(const MinidumpDescriptor& minidump,
+ void* context,
+ bool succeeded) {
+ // WARNING: this code runs in a compromised context. It may not call into
+ // libc nor allocate memory normally.
+ if (!succeeded) {
+ static const char msg[] = "Microdump crash handler failed.\n";
+ WriteLog(msg, sizeof(msg) - 1);
+ return false;
+ }
+
+ const bool is_browser_process = (context != nullptr);
+ return FinalizeCrashDoneAndroid(is_browser_process);
+}
+#endif // defined(OS_ANDROID)
bool CrashDone(const MinidumpDescriptor& minidump,
const bool upload,
@@ -693,6 +725,31 @@ bool CrashDoneUpload(const MinidumpDescriptor& minidump,
}
#endif
+void DumpProcess() {
+#if defined(OS_ANDROID)
+ // Don't use g_breakpad and g_microdump directly here, because their
+ // output might currently be suppressed.
+ if (g_breakpad) {
+ ExceptionHandler(g_breakpad->minidump_descriptor(),
+ nullptr,
+ CrashDoneNoUpload,
+ nullptr,
+ false, -1).WriteMinidump();
+ }
+ // If microdumps are enabled write also a microdump on the system log.
+ if (g_microdump) {
+ ExceptionHandler(g_microdump->minidump_descriptor(),
+ nullptr,
+ MicrodumpCrashDone,
+ nullptr,
+ false, -1).WriteMinidump();
+ }
+#else
+ if (g_breakpad)
+ g_breakpad->WriteMinidump();
+#endif
+}
+
#if defined(ADDRESS_SANITIZER)
extern "C"
void __asan_set_error_report_callback(void (*cb)(const char*));
@@ -734,7 +791,11 @@ void EnableCrashDumping(bool unattended) {
if (unattended) {
g_breakpad = new ExceptionHandler(
minidump_descriptor,
+#if defined(OS_ANDROID)
+ ShouldGenerateDump,
+#else
nullptr,
+#endif
CrashDoneNoUpload,
nullptr,
true, // Install handlers.
@@ -755,21 +816,6 @@ void EnableCrashDumping(bool unattended) {
}
#if defined(OS_ANDROID)
-bool MicrodumpCrashDone(const MinidumpDescriptor& minidump,
- void* context,
- bool succeeded) {
- // WARNING: this code runs in a compromised context. It may not call into
- // libc nor allocate memory normally.
- if (!succeeded) {
- static const char msg[] = "Microdump crash handler failed.\n";
- WriteLog(msg, sizeof(msg) - 1);
- return false;
- }
-
- const bool is_browser_process = (context != nullptr);
- return FinalizeCrashDoneAndroid(is_browser_process);
-}
-
bool WriteSignalCodeToPipe(const void* crash_context,
size_t crash_context_size,
void* context) {
@@ -843,19 +889,8 @@ void EnableNonBrowserCrashDumping(const std::string& process_type,
const size_t process_type_len = process_type.size() + 1;
g_process_type = new char[process_type_len];
strncpy(g_process_type, process_type.c_str(), process_type_len);
- new google_breakpad::ExceptionHandler(MinidumpDescriptor(minidump_fd),
- nullptr, CrashDoneInProcessNoUpload, nullptr, true, -1);
-}
-
-void GenerateMinidumpOnDemandForAndroid() {
- int dump_fd = GetCrashReporterClient()->GetAndroidMinidumpDescriptor();
- if (dump_fd >= 0) {
- MinidumpDescriptor minidump_descriptor(dump_fd);
- minidump_descriptor.set_size_limit(-1);
- ExceptionHandler(minidump_descriptor, nullptr, MinidumpGenerated, nullptr,
- false, -1)
- .WriteMinidump();
- }
+ new ExceptionHandler(MinidumpDescriptor(minidump_fd), ShouldGenerateDump,
+ CrashDoneInProcessNoUpload, nullptr, true, -1);
}
void MicrodumpInfo::SetGpuFingerprint(const std::string& gpu_fingerprint) {
@@ -912,25 +947,14 @@ void MicrodumpInfo::Initialize(const std::string& process_type,
}
g_microdump =
- new ExceptionHandler(descriptor, nullptr, MicrodumpCrashDone,
+ new ExceptionHandler(descriptor, ShouldGenerateDump, MicrodumpCrashDone,
reinterpret_cast<void*>(is_browser_process),
true, // Install handlers.
-1); // Server file descriptor. -1 for in-process.
- if (process_type == kWebViewSingleProcessType ||
- process_type == kBrowserProcessType) {
- // TODO(tobiasjs): figure out what to do with on demand minidump on the
- // renderer process of webview.
- // We do not use |DumpProcess()| for handling programatically
- // generated dumps for WebView because we only know the file
- // descriptor to which we are dumping at the time of the call to
- // |DumpWithoutCrashing()|. Therefore we need to construct the
- // |MinidumpDescriptor| and |ExceptionHandler| instances as
- // needed, instead of setting up |g_breakpad| at initialization
- // time.
- base::debug::SetDumpWithoutCrashingFunction(
- &GenerateMinidumpOnDemandForAndroid);
- } else if (!process_type.empty()) {
+ if (process_type != kWebViewSingleProcessType &&
+ process_type != kBrowserProcessType &&
+ !process_type.empty()) {
g_signal_code_pipe_fd =
GetCrashReporterClient()->GetAndroidCrashSignalFD();
if (g_signal_code_pipe_fd != -1)
@@ -1628,6 +1652,8 @@ void HandleCrashDump(const BreakpadInfo& info) {
writer.AddPairString(gms_core_version,
android_build_info->gms_version_code());
writer.AddBoundary();
+ WriteAndroidPackage(writer, android_build_info);
+ writer.AddBoundary();
if (android_build_info->java_exception_info() != nullptr) {
writer.AddPairString(exception_info,
android_build_info->java_exception_info());
@@ -1934,6 +1960,20 @@ void AddGpuFingerprintToMicrodumpCrashHandler(
const std::string& gpu_fingerprint) {
g_microdump_info.Get().SetGpuFingerprint(gpu_fingerprint);
}
+
+void GenerateMinidumpOnDemandForAndroid(int dump_fd) {
+ if (dump_fd >= 0) {
+ MinidumpDescriptor minidump_descriptor(dump_fd);
+ minidump_descriptor.set_size_limit(-1);
+ ExceptionHandler(minidump_descriptor, nullptr, MinidumpGenerated, nullptr,
+ false, -1)
+ .WriteMinidump();
+ }
+}
+
+void SuppressDumpGeneration() {
+ g_dumps_suppressed = G_DUMPS_SUPPRESSED_MAGIC;
+}
#endif // OS_ANDROID
bool IsCrashReporterEnabled() {
diff --git a/chromium/components/crash/content/app/breakpad_linux.h b/chromium/components/crash/content/app/breakpad_linux.h
index 3316fa0cbe6..bbeb208b68e 100644
--- a/chromium/components/crash/content/app/breakpad_linux.h
+++ b/chromium/components/crash/content/app/breakpad_linux.h
@@ -31,6 +31,11 @@ extern void InitMicrodumpCrashHandlerIfNecessary(
extern void AddGpuFingerprintToMicrodumpCrashHandler(
const std::string& gpu_fingerprint);
+
+// Calling SuppressDumpGeneration causes subsequent crashes to not
+// generate dumps. Calling base::debug::DumpWithoutCrashing will still
+// generate a dump.
+extern void SuppressDumpGeneration();
#endif
// Checks if crash reporting is enabled. Note that this is not the same as
@@ -38,6 +43,8 @@ extern void AddGpuFingerprintToMicrodumpCrashHandler(
// whether InitCrashReporter() is called.
bool IsCrashReporterEnabled();
+// Generates a minidump on demand for this process, writing it to |dump_fd|.
+void GenerateMinidumpOnDemandForAndroid(int dump_fd);
} // 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 61455c95912..a5fb543e651 100644
--- a/chromium/components/crash/content/app/breakpad_win.cc
+++ b/chromium/components/crash/content/app/breakpad_win.cc
@@ -151,6 +151,12 @@ MSVC_ENABLE_OPTIMIZE()
// 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) {
// |serialized_crash_keys| is not propagated in breakpad but is in crashpad
@@ -159,6 +165,15 @@ extern "C" HANDLE __declspec(dllexport) __cdecl InjectDumpProcessWithoutCrash(
0, 0, NULL);
}
+extern "C" HANDLE __declspec(
+ dllexport) __cdecl InjectDumpForHungInputNoCrashKeys(HANDLE process,
+ int reason) {
+ // |reason| is not propagated in breakpad but is in crashpad since breakpad
+ // is deprecated.
+ return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread, 0,
+ 0, NULL);
+}
+
extern "C" HANDLE __declspec(dllexport) __cdecl
InjectDumpForHangDebugging(HANDLE process) {
return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread,
diff --git a/chromium/components/crash/content/app/crashpad_win.cc b/chromium/components/crash/content/app/crashpad_win.cc
index a22af313a62..bdbd94ee575 100644
--- a/chromium/components/crash/content/app/crashpad_win.cc
+++ b/chromium/components/crash/content/app/crashpad_win.cc
@@ -151,8 +151,9 @@ extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() {
namespace {
-// We need to prevent ICF from folding DumpForHangDebuggingThread() and
-// DumpProcessWithoutCrashThread() together, since that makes them
+// We need to prevent ICF from folding DumpForHangDebuggingThread(),
+// DumpProcessForHungInputThread(), DumpProcessForHungInputNoCrashKeysThread()
+// 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()
@@ -160,7 +161,14 @@ 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* crash_keys_str) {
+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) {
base::StringPairs crash_keys;
if (crash_keys_str && base::SplitStringIntoKeyValuePairs(
reinterpret_cast<const char*>(crash_keys_str), ':',
@@ -173,9 +181,19 @@ DWORD WINAPI DumpProcessWithoutCrashThread(void* crash_keys_str) {
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(dtapuska): Remove when enough information is gathered where the crash
+// reports without crash keys come from.
+DWORD WINAPI DumpProcessForHungInputNoCrashKeysThread(void* reason) {
+#pragma warning(push)
+#pragma warning(disable : 4311 4302)
+ base::debug::SetCrashKeyValue(
+ "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*) {
@@ -207,19 +225,40 @@ 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
-// registered as crash keys.
-HANDLE __declspec(dllexport) __cdecl InjectDumpProcessWithoutCrash(
+// registered as crash keys. This method is used solely to classify hung input.
+HANDLE __declspec(dllexport) __cdecl InjectDumpForHungInput(
HANDLE process,
void* serialized_crash_keys) {
return CreateRemoteThread(
process, nullptr, 0,
- crash_reporter::internal::DumpProcessWithoutCrashThread,
+ crash_reporter::internal::DumpProcessForHungInputThread,
serialized_crash_keys, 0, nullptr);
}
+// Injects a thread into a remote process to dump state when there is no crash.
+// This method provides |reason| which will interpreted as an integer and logged
+// as a crash key.
+HANDLE __declspec(dllexport) __cdecl InjectDumpForHungInputNoCrashKeys(
+ HANDLE process,
+ int reason) {
+ return CreateRemoteThread(
+ process, nullptr, 0,
+ crash_reporter::internal::DumpProcessForHungInputNoCrashKeysThread,
+ reinterpret_cast<void*>(reason), 0, nullptr);
+}
+
HANDLE __declspec(dllexport) __cdecl InjectDumpForHangDebugging(
HANDLE process) {
return CreateRemoteThread(
diff --git a/chromium/components/crash/content/app/fallback_crash_handler_launcher_win.cc b/chromium/components/crash/content/app/fallback_crash_handler_launcher_win.cc
new file mode 100644
index 00000000000..45c8c83db29
--- /dev/null
+++ b/chromium/components/crash/content/app/fallback_crash_handler_launcher_win.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/crash/content/app/fallback_crash_handler_launcher_win.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/win/win_util.h"
+
+namespace crash_reporter {
+
+namespace {
+
+// The number of characters reserved at the tail of the command line for the
+// thread ID parameter.
+const size_t kCommandLineTailSize = 32;
+
+} // namespace
+
+FallbackCrashHandlerLauncher::FallbackCrashHandlerLauncher() {
+ memset(&exception_pointers_, 0, sizeof(exception_pointers_));
+}
+
+FallbackCrashHandlerLauncher::~FallbackCrashHandlerLauncher() {}
+
+bool FallbackCrashHandlerLauncher::Initialize(
+ const base::CommandLine& program,
+ const base::FilePath& crashpad_database) {
+ // Open an inheritable handle to self. This will be inherited to the handler.
+ const DWORD kAccessMask = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ |
+ PROCESS_DUP_HANDLE | PROCESS_TERMINATE;
+ self_process_handle_.Set(
+ OpenProcess(kAccessMask, TRUE, ::GetCurrentProcessId()));
+ if (!self_process_handle_.IsValid())
+ return false;
+
+ // Setup the startup info for inheriting the self process handle into the
+ // fallback crash handler.
+ if (!startup_info_.InitializeProcThreadAttributeList(1))
+ return false;
+
+ HANDLE raw_self_process_handle = self_process_handle_.Get();
+ if (!startup_info_.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &raw_self_process_handle,
+ sizeof(raw_self_process_handle))) {
+ return false;
+ }
+
+ // Build the command line from a copy of the command line passed in.
+ base::CommandLine cmd_line(program);
+ cmd_line.AppendSwitchPath("database", crashpad_database);
+ cmd_line.AppendSwitchASCII(
+ "exception-pointers",
+ base::Uint64ToString(reinterpret_cast<uintptr_t>(&exception_pointers_)));
+ cmd_line.AppendSwitchASCII(
+ "process", base::UintToString(
+ base::win::HandleToUint32(self_process_handle_.Get())));
+
+ std::wstring str_cmd_line = cmd_line.GetCommandLineString();
+
+ // Append the - for now abortive - thread argument manually.
+ str_cmd_line.append(L" --thread=");
+ // Store the command line string for easy use later.
+ cmd_line_.assign(str_cmd_line.begin(), str_cmd_line.end());
+
+ // Resize the vector to reserve space for the thread ID.
+ cmd_line_.resize(cmd_line_.size() + kCommandLineTailSize, '\0');
+
+ return true;
+}
+
+DWORD FallbackCrashHandlerLauncher::LaunchAndWaitForHandler(
+ EXCEPTION_POINTERS* exception_pointers) {
+ DCHECK(!cmd_line_.empty());
+ DCHECK_EQ('=', cmd_line_[cmd_line_.size() - kCommandLineTailSize - 1]);
+ // This program has crashed. Try and not use anything but the stack.
+
+ // Append the current thread's ID to the command line in-place.
+ int chars_appended = wsprintf(&cmd_line_.back() - kCommandLineTailSize + 1,
+ L"%d", GetCurrentThreadId());
+ DCHECK_GT(static_cast<int>(kCommandLineTailSize), chars_appended);
+
+ // Copy the exception pointers to our member variable, whose address is
+ // already baked into the command line.
+ exception_pointers_ = *exception_pointers;
+
+ // Launch the pre-cooked command line.
+
+ PROCESS_INFORMATION process_info = {};
+ if (!CreateProcess(nullptr, // Application name.
+ &cmd_line_[0], // Command line.
+ nullptr, // Process attributes.
+ nullptr, // Thread attributes.
+ true, // Inherit handles.
+ 0, // Creation flags.
+ nullptr, // Environment.
+ nullptr, // Current directory.
+ startup_info_.startup_info(), // Startup info.
+ &process_info)) {
+ return GetLastError();
+ }
+
+ // Wait on the fallback crash handler process. The expectation is that this
+ // will never return, as the fallback crash handler will terminate this
+ // process. For testing, and full-on belt and suspenders, cover for this
+ // returning.
+ DWORD error = WaitForSingleObject(process_info.hProcess, INFINITE);
+ if (error != WAIT_OBJECT_0) {
+ // This should never happen, barring handle abuse.
+ // TODO(siggi): Record an UMA metric here.
+ NOTREACHED();
+ error = GetLastError();
+ } else {
+ // On successful wait, return the exit code of the fallback crash handler
+ // process.
+ if (!GetExitCodeProcess(process_info.hProcess, &error)) {
+ // This should never happen, barring handle abuse.
+ NOTREACHED();
+ error = GetLastError();
+ }
+ }
+
+ // Close the handles returned from CreateProcess.
+ CloseHandle(process_info.hProcess);
+ CloseHandle(process_info.hThread);
+
+ return error;
+}
+
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/app/fallback_crash_handler_launcher_win.h b/chromium/components/crash/content/app/fallback_crash_handler_launcher_win.h
new file mode 100644
index 00000000000..96ba9c09be1
--- /dev/null
+++ b/chromium/components/crash/content/app/fallback_crash_handler_launcher_win.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_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_LAUNCHER_WIN_H_
+#define COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_LAUNCHER_WIN_H_
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/startup_information.h"
+
+#include <windows.h>
+
+namespace crash_reporter {
+
+// This class is a last-ditch crash handler for the Crashpad handler process.
+// It prepares and stores a command line, ready for dispatching when
+// an exception occurs. Everything needed to call CreateProcess is pre-allocated
+// to minimize the odds of re-faulting due to e.g. tripping over the same issue
+// that caused the initial crash.
+// This is still very much best-effort, as doing anything at all inside a
+// process that's crashed is always going to be iffy^2.
+class FallbackCrashHandlerLauncher {
+ public:
+ FallbackCrashHandlerLauncher();
+ ~FallbackCrashHandlerLauncher();
+
+ // Initializes everything that's needed in LaunchAndWaitForHandler.
+ bool Initialize(const base::CommandLine& program,
+ const base::FilePath& crashpad_database);
+
+ // Launch the pre-computed command line for the fallback error handler.
+ // The expectation is that this function will never return, as the fallback
+ // error handler should terminate it with the exception code as the process
+ // exit code. The return value from this function is therefore academic in
+ // the normal case.
+ // However, for completeness, this function returns one of:
+ // - The error from CreateProcess if it fails to launch the fallback handler.
+ // - The error from waiting on the fallback crash handler process if it
+ // fails to wait for that process to exit.
+ // - The exit code from the fallback crash handler process.
+ // - The error encountered in retrieving the crash handler process' exit code.
+ // Note that the return value is used in testing.
+ DWORD LaunchAndWaitForHandler(EXCEPTION_POINTERS* pointers);
+
+ private:
+ // A copy of the actual exception pointers made at time of exception.
+ EXCEPTION_POINTERS exception_pointers_;
+
+ // The precomputed startup info and command line for launching the fallback
+ // handler.
+ base::win::StartupInformation startup_info_;
+ // Stores the pre-cooked command line, with an allotment of zeros at the back
+ // sufficient for writing in the thread id, just before launch.
+ std::vector<wchar_t> cmd_line_;
+
+ // An inheritable handle to our own process, the raw handle is necessary
+ // for pre-computing the startup info.
+ base::win::ScopedHandle self_process_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(FallbackCrashHandlerLauncher);
+};
+
+} // namespace crash_reporter
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_LAUNCHER_WIN_H_
diff --git a/chromium/components/crash/content/app/fallback_crash_handler_launcher_win_unittest.cc b/chromium/components/crash/content/app/fallback_crash_handler_launcher_win_unittest.cc
new file mode 100644
index 00000000000..c19ae79d17d
--- /dev/null
+++ b/chromium/components/crash/content/app/fallback_crash_handler_launcher_win_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/crash/content/app/fallback_crash_handler_launcher_win.h"
+
+#include <dbghelp.h>
+
+#include "base/base_switches.h"
+#include "base/command_line.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/macros.h"
+#include "base/process/process.h"
+#include "base/process/process_handle.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/multiprocess_test.h"
+#include "base/win/win_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace crash_reporter {
+
+namespace {
+
+const wchar_t kFileName[] = L"crash.dmp";
+
+// This function is called in the fallback handler process instance.
+MULTIPROCESS_TEST_MAIN(FallbackCrashHandlerLauncherMain) {
+ base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+
+ // Check for presence of the "process" argument.
+ CHECK(cmd_line->HasSwitch("process"));
+
+ // Retrieve and check the handle to verify that it was inherited in.
+ unsigned int uint_process = 0;
+ CHECK(base::StringToUint(cmd_line->GetSwitchValueASCII("process"),
+ &uint_process));
+ base::Process process(base::win::Uint32ToHandle(uint_process));
+
+ // Verify that the process handle points to our parent.
+ CHECK_EQ(base::GetParentProcessId(base::GetCurrentProcessHandle()),
+ process.Pid());
+
+ // Check the "thread" argument.
+ CHECK(cmd_line->HasSwitch("thread"));
+
+ // Retrieve the thread id.
+ unsigned int thread_id = 0;
+ CHECK(
+ base::StringToUint(cmd_line->GetSwitchValueASCII("thread"), &thread_id));
+
+ // Check the "exception-pointers" argument.
+ CHECK(cmd_line->HasSwitch("exception-pointers"));
+ uint64_t uint_exc_ptrs = 0;
+ CHECK(base::StringToUint64(
+ cmd_line->GetSwitchValueASCII("exception-pointers"), &uint_exc_ptrs));
+
+ EXCEPTION_POINTERS* exc_ptrs = reinterpret_cast<EXCEPTION_POINTERS*>(
+ static_cast<uintptr_t>(uint_exc_ptrs));
+
+ // Check the "database" argument.
+ CHECK(cmd_line->HasSwitch("database"));
+ base::FilePath database_dir = cmd_line->GetSwitchValuePath("database");
+
+ base::FilePath dump_path = database_dir.Append(kFileName);
+
+ // Create a dump file in the directory.
+ base::File dump(dump_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+
+ CHECK(dump.IsValid());
+
+ const MINIDUMP_TYPE kMinidumpType = static_cast<MINIDUMP_TYPE>(
+ MiniDumpWithHandleData | MiniDumpWithUnloadedModules |
+ MiniDumpWithProcessThreadData | MiniDumpWithThreadInfo |
+ MiniDumpWithTokenInformation);
+
+ MINIDUMP_EXCEPTION_INFORMATION exc_info = {};
+ exc_info.ThreadId = thread_id;
+ exc_info.ExceptionPointers = exc_ptrs;
+ exc_info.ClientPointers = TRUE; // ExceptionPointers in client.
+
+ // Write the minidump as a direct test of the validity and permissions on the
+ // parent process handle.
+ if (!MiniDumpWriteDump(process.Handle(), // Process handle.
+ process.Pid(), // Process Id.
+ dump.GetPlatformFile(), // File handle.
+ kMinidumpType, // Minidump type.
+ &exc_info, // Exception Param
+ nullptr, // UserStreamParam,
+ nullptr)) { // CallbackParam
+ DWORD error = GetLastError();
+
+ dump.Close();
+ CHECK(base::DeleteFile(dump_path, false));
+ CHECK(false) << "Unable to write dump, error " << error;
+ }
+
+ return 0;
+}
+
+MULTIPROCESS_TEST_MAIN(TestCrashHandlerLauncherMain) {
+ base::ScopedTempDir database_dir;
+ CHECK(database_dir.CreateUniqueTempDir());
+
+ // Construct the base command line that diverts to the main function above.
+ base::CommandLine base_cmdline(
+ base::GetMultiProcessTestChildBaseCommandLine());
+
+ base_cmdline.AppendSwitchASCII(switches::kTestChildProcess,
+ "FallbackCrashHandlerLauncherMain");
+
+ FallbackCrashHandlerLauncher launcher;
+ CHECK(launcher.Initialize(base_cmdline, database_dir.GetPath()));
+
+ // Make like an access violation at the current place.
+ EXCEPTION_RECORD exc_record = {};
+ exc_record.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
+ CONTEXT ctx = {};
+ RtlCaptureContext(&ctx);
+ CHECK_NE(0UL, ctx.ContextFlags);
+
+ EXCEPTION_POINTERS exc_ptrs = {&exc_record, &ctx};
+
+ CHECK_EQ(0UL, launcher.LaunchAndWaitForHandler(&exc_ptrs));
+
+ // Check that the dump was written, e.g. it's an existing file with non-zero
+ // size.
+ base::File dump(database_dir.GetPath().Append(kFileName),
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
+ CHECK(dump.IsValid());
+ CHECK_NE(0, dump.GetLength());
+ return 0;
+}
+
+class FallbackCrashHandlerLauncherTest : public base::MultiProcessTest {};
+
+} // namespace
+
+TEST_F(FallbackCrashHandlerLauncherTest, LaunchAndWaitForHandler) {
+ // Because this process is heavily multithreaded it's going to be flaky
+ // and generally fraught with peril to try and grab a minidump of it.
+ // Instead, fire off a sacrificial process to do the testing.
+ base::Process test_process = SpawnChild("TestCrashHandlerLauncherMain");
+ int exit_code = 0;
+ ASSERT_TRUE(test_process.WaitForExit(&exit_code));
+ ASSERT_EQ(0, exit_code);
+}
+
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/app/fallback_crash_handler_win.cc b/chromium/components/crash/content/app/fallback_crash_handler_win.cc
new file mode 100644
index 00000000000..f96d47a3c3a
--- /dev/null
+++ b/chromium/components/crash/content/app/fallback_crash_handler_win.cc
@@ -0,0 +1,464 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/app/fallback_crash_handler_win.h"
+
+#include <dbghelp.h>
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/process/process_handle.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/win_util.h"
+#include "third_party/crashpad/crashpad/client/crash_report_database.h"
+#include "third_party/crashpad/crashpad/client/settings.h"
+#include "third_party/crashpad/crashpad/minidump/minidump_extensions.h"
+
+namespace crash_reporter {
+
+namespace {
+
+using FilePosition = uint32_t;
+const FilePosition kInvalidFilePos = static_cast<FilePosition>(-1);
+
+using StringStringMap = std::map<std::string, std::string>;
+
+// This class is a helper to edit minidump files written by MiniDumpWriteDump.
+// It assumes the minidump file it operates on has a directory entry pointing to
+// a CrashpadInfo entry, which it updates to point to the SimpleDictionary data
+// it appends to the file contents.
+class MinidumpUpdater {
+ public:
+ MinidumpUpdater();
+
+ // Reads the existing directory from |file|.
+ bool Initialize(base::File* file);
+
+ // Appends the simple dictionary with |crash_keys| to the file, and updates
+ // the CrashpadInfo with its location.
+ bool AppendSimpleDictionary(const StringStringMap& crash_keys);
+
+ private:
+ // Writes |data_len| bytes from |data| to the file at the current location.
+ bool WriteData(const void* data, size_t data_len);
+ bool WriteAndAdvance(const void* data,
+ size_t data_len,
+ FilePosition* position);
+
+ base::File* file_;
+ std::vector<MINIDUMP_DIRECTORY> directory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpUpdater);
+};
+
+MinidumpUpdater::MinidumpUpdater() : file_(nullptr) {}
+
+bool MinidumpUpdater::Initialize(base::File* file) {
+ DCHECK(file && file->IsValid());
+ DCHECK(!file_);
+
+ // Read the file header.
+ MINIDUMP_HEADER header = {};
+ int bytes_read =
+ file->Read(0, reinterpret_cast<char*>(&header), sizeof(header));
+ if (bytes_read != sizeof(header))
+ return false;
+ if (header.Signature != MINIDUMP_SIGNATURE || header.NumberOfStreams == 0)
+ return false;
+
+ // Read the stream directory.
+ directory_.resize(header.NumberOfStreams);
+ int bytes_to_read = header.NumberOfStreams * sizeof(directory_[0]);
+ bytes_read =
+ file->Read(header.StreamDirectoryRva,
+ reinterpret_cast<char*>(&directory_[0]), bytes_to_read);
+ if (bytes_read != bytes_to_read)
+ return false;
+
+ // Crashpad has some fairly unreasonable checking on the minidump header and
+ // directory. Match with those checks for now to allow Crashpad to read the
+ // CrashpadInfo and upload these dumps.
+
+ // Start by removing any unused directory entries.
+ // TODO(siggi): Fix Crashpad to ignore unused streams.
+ directory_.erase(std::remove_if(directory_.begin(), directory_.end(),
+ [](const MINIDUMP_DIRECTORY& entry) {
+ return entry.StreamType == UnusedStream;
+ }),
+ directory_.end());
+
+ // Update the header.
+ // TODO(siggi): Fix Crashpad's version checking.
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = base::saturated_cast<ULONG32>(directory_.size());
+
+ // Write back the potentially shortened and packed dictionary.
+ int bytes_to_write = header.NumberOfStreams * sizeof(directory_[0]);
+ int bytes_written = file->Write(header.StreamDirectoryRva,
+ reinterpret_cast<const char*>(&directory_[0]),
+ bytes_to_write);
+ if (bytes_written != bytes_to_write)
+ return false;
+
+ // Write back the header.
+ bytes_written =
+ file->Write(0, reinterpret_cast<const char*>(&header), sizeof(header));
+ if (bytes_written != sizeof(header))
+ return false;
+
+ // Success, stash the file.
+ file_ = file;
+
+ return true;
+}
+
+bool MinidumpUpdater::AppendSimpleDictionary(
+ const StringStringMap& crash_keys) {
+ DCHECK(file_);
+
+ // Start by finding the Crashpad directory entry and reading the CrashpadInfo.
+ FilePosition crashpad_info_pos = 0;
+ crashpad::MinidumpCrashpadInfo crashpad_info;
+ for (const auto& entry : directory_) {
+ if (entry.StreamType == crashpad::kMinidumpStreamTypeCrashpadInfo) {
+ // This file is freshly written, so it must contain the same version
+ // CrashpadInfo structure this code compiled against.
+ if (entry.Location.DataSize != sizeof(crashpad_info))
+ return false;
+
+ crashpad_info_pos = entry.Location.Rva;
+ break;
+ }
+ }
+
+ // No CrashpadInfo directory entry found.
+ if (crashpad_info_pos == 0)
+ return false;
+
+ int bytes_read =
+ file_->Read(crashpad_info_pos, reinterpret_cast<char*>(&crashpad_info),
+ sizeof(crashpad_info));
+ if (bytes_read != sizeof(crashpad_info))
+ return false;
+
+ if (crashpad_info.version != crashpad::MinidumpCrashpadInfo::kVersion)
+ return false;
+
+ // Seek to the tail of the file, where we're going to extend it.
+ FilePosition next_available_byte = file_->Seek(base::File::FROM_END, 0);
+ if (next_available_byte == kInvalidFilePos)
+ return false;
+
+ // Write the key/value pairs and collect their locations.
+ std::vector<crashpad::MinidumpSimpleStringDictionaryEntry> entries;
+ for (const auto& kv : crash_keys) {
+ // The key of a key/value pair should never be empty.
+ DCHECK(!kv.first.empty());
+
+ crashpad::MinidumpSimpleStringDictionaryEntry entry = {0};
+
+ // Skip this key/value if the value is empty.
+ if (!kv.second.empty()) {
+ entry.key = next_available_byte;
+ uint32_t key_len = base::saturated_cast<uint32_t>(kv.first.size());
+ if (!WriteAndAdvance(&key_len, sizeof(key_len), &next_available_byte) ||
+ !WriteAndAdvance(&kv.first[0], key_len, &next_available_byte)) {
+ return false;
+ }
+
+ entry.value = next_available_byte;
+ uint32_t value_len = base::saturated_cast<uint32_t>(kv.second.size());
+ if (!WriteAndAdvance(&value_len, sizeof(value_len),
+ &next_available_byte) ||
+ !WriteAndAdvance(&kv.second[0], value_len, &next_available_byte)) {
+ return false;
+ }
+
+ entries.push_back(entry);
+ }
+ }
+
+ // Write the dictionary array itself - note the array is count-prefixed.
+ FilePosition dict_pos = next_available_byte;
+ uint32_t entry_count = base::saturated_cast<uint32_t>(entries.size());
+ if (!WriteAndAdvance(&entry_count, sizeof(entry_count),
+ &next_available_byte) ||
+ !WriteAndAdvance(&entries[0], entry_count * sizeof(entries[0]),
+ &next_available_byte)) {
+ return false;
+ }
+
+ // Touch up the CrashpadInfo and write it back to the file.
+ crashpad_info.simple_annotations.DataSize = next_available_byte - dict_pos;
+ crashpad_info.simple_annotations.Rva = dict_pos;
+
+ int bytes_written = file_->Write(
+ crashpad_info_pos, reinterpret_cast<const char*>(&crashpad_info),
+ sizeof(crashpad_info));
+ if (bytes_written != sizeof(crashpad_info))
+ return false;
+
+ return true;
+}
+
+bool MinidumpUpdater::WriteData(const void* data, size_t data_len) {
+ DCHECK(file_);
+ DCHECK(data);
+ DCHECK_NE(0U, data_len);
+
+ if (data_len > INT_MAX)
+ return false;
+
+ int bytes_to_write = static_cast<int>(data_len);
+ int written_bytes = file_->WriteAtCurrentPos(
+ reinterpret_cast<const char*>(data), bytes_to_write);
+ if (written_bytes == -1)
+ return false;
+
+ return true;
+}
+
+bool MinidumpUpdater::WriteAndAdvance(const void* data,
+ size_t data_len,
+ FilePosition* position) {
+ DCHECK(position);
+ DCHECK_EQ(file_->Seek(base::File::FROM_CURRENT, 0), *position);
+
+ if (!WriteData(data, data_len))
+ return false;
+
+ *position += base::saturated_cast<FilePosition>(data_len);
+ return true;
+}
+
+// Writes a minidump file for |process| to |dump_file| with embedded
+// CrashpadInfo, containing |crash_keys|, |client_id| and |report_id|.
+// The |dump_file| must be open for read as well as write.
+bool MiniDumpWriteDumpWithCrashpadInfo(const base::Process& process,
+ uint32_t minidump_type,
+ MINIDUMP_EXCEPTION_INFORMATION* exc_info,
+ const StringStringMap& crash_keys,
+ const crashpad::UUID& client_id,
+ const crashpad::UUID& report_id,
+ base::File* dump_file) {
+ DCHECK(process.IsValid());
+ DCHECK(exc_info);
+ DCHECK(dump_file && dump_file->IsValid());
+
+ // The CrashpadInfo structure and its associated directory entry are injected
+ // into the minidump, to minimize the work to patching up the dump.
+ crashpad::MinidumpCrashpadInfo crashpad_info;
+ crashpad_info.version = crashpad::MinidumpCrashpadInfo::kVersion;
+ crashpad_info.client_id = client_id;
+ crashpad_info.report_id = report_id;
+
+ MINIDUMP_USER_STREAM crashpad_info_stream = {
+ crashpad::kMinidumpStreamTypeCrashpadInfo, // Type
+ sizeof(crashpad_info), // BufferSize
+ &crashpad_info // Buffer
+ };
+ MINIDUMP_USER_STREAM_INFORMATION user_stream_info = {
+ 1, // UserStreamCount
+ &crashpad_info_stream // UserStreamArray
+ };
+
+ // Write the minidump to the provided dump file.
+ if (!MiniDumpWriteDump(
+ process.Handle(), // Process handle.
+ process.Pid(), // Process Id.
+ dump_file->GetPlatformFile(), // File handle.
+ static_cast<MINIDUMP_TYPE>(minidump_type), // Minidump type.
+ exc_info, // Exception Param
+ &user_stream_info, // UserStreamParam,
+ nullptr)) { // CallbackParam
+ return false;
+ }
+
+ // Retouch the minidump to make it Crashpad compatible.
+ MinidumpUpdater updater;
+ if (!updater.Initialize(dump_file))
+ return false;
+ if (!updater.AppendSimpleDictionary(crash_keys))
+ return false;
+
+ return true;
+}
+
+// Appends the full contents of |source| to |dest| from the current position
+// of |dest|.
+bool AppendFileContents(base::File* source, base::PlatformFile dest) {
+ DCHECK(source && source->IsValid());
+ DCHECK_NE(base::kInvalidPlatformFile, dest);
+
+ // Rewind the source.
+ if (source->Seek(base::File::FROM_BEGIN, 0) == kInvalidFilePos)
+ return false;
+
+ std::vector<char> buf;
+ buf.resize(1024);
+ while (true) {
+ int bytes_read =
+ source->ReadAtCurrentPos(&buf[0], static_cast<int>(buf.size()));
+ if (bytes_read == -1)
+ return false;
+ if (bytes_read == 0)
+ break;
+
+ DWORD bytes_written = 0;
+ // Due to handle instrumentation, the destination can't be wrapped in
+ // a base::File, so we go basic Win32 API here.
+ if (!WriteFile(dest, &buf[0], bytes_read, &bytes_written, nullptr) ||
+ static_cast<int>(bytes_written) != bytes_read) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+FallbackCrashHandler::FallbackCrashHandler()
+ : thread_id_(base::kInvalidThreadId), exception_ptrs_(0UL) {}
+
+FallbackCrashHandler::~FallbackCrashHandler() {}
+
+bool FallbackCrashHandler::ParseCommandLine(const base::CommandLine& cmd_line) {
+ // Retrieve the handle to the process to dump.
+ unsigned int uint_process;
+ if (!base::StringToUint(cmd_line.GetSwitchValueASCII("process"),
+ &uint_process)) {
+ return false;
+ }
+
+ // Before taking ownership of the supposed handle, see whether it's really
+ // a process handle.
+ base::ProcessHandle process_handle = base::win::Uint32ToHandle(uint_process);
+ if (base::GetProcId(process_handle) == base::kNullProcessId)
+ return false;
+
+ // Retrieve the thread id argument.
+ unsigned int thread_id = 0;
+ if (!base::StringToUint(cmd_line.GetSwitchValueASCII("thread"), &thread_id)) {
+ return false;
+ }
+ thread_id_ = thread_id;
+
+ // Retrieve the "exception-pointers" argument.
+ uint64_t uint_exc_ptrs = 0;
+ if (!base::StringToUint64(cmd_line.GetSwitchValueASCII("exception-pointers"),
+ &uint_exc_ptrs)) {
+ return false;
+ }
+ exception_ptrs_ = static_cast<uintptr_t>(uint_exc_ptrs);
+
+ // Retrieve the "database" argument.
+ database_dir_ = cmd_line.GetSwitchValuePath("database");
+ if (database_dir_.empty())
+ return false;
+
+ // Everything checks out, take ownership of the process handle.
+ process_ = base::Process(process_handle);
+
+ return true;
+}
+
+bool FallbackCrashHandler::GenerateCrashDump(const std::string& product,
+ const std::string& version,
+ const std::string& channel,
+ const std::string& process_type) {
+ std::unique_ptr<crashpad::CrashReportDatabase> database =
+ crashpad::CrashReportDatabase::InitializeWithoutCreating(database_dir_);
+
+ if (!database)
+ return false;
+
+ crashpad::CrashReportDatabase::NewReport* report = nullptr;
+ crashpad::CrashReportDatabase::OperationStatus status =
+ database->PrepareNewCrashReport(&report);
+ if (status != crashpad::CrashReportDatabase::kNoError)
+ return false;
+
+ // Make sure we release the report on early exit.
+ crashpad::CrashReportDatabase::CallErrorWritingCrashReport on_error(
+ database.get(), report);
+
+ MINIDUMP_EXCEPTION_INFORMATION exc_info = {};
+ exc_info.ThreadId = thread_id_;
+ exc_info.ExceptionPointers =
+ reinterpret_cast<EXCEPTION_POINTERS*>(exception_ptrs_);
+ exc_info.ClientPointers = TRUE; // ExceptionPointers in client.
+
+// Mandatory crash keys. These will be read by Crashpad and used as
+// http request parameters for the upload. Keys and values need to match
+// server side configuration.
+#if defined(ARCH_CPU_64_BITS)
+ const char* platform = "Win64";
+#else
+ const char* platform = "Win32";
+#endif
+ std::map<std::string, std::string> crash_keys = {{"prod", product},
+ {"ver", version},
+ {"channel", channel},
+ {"plat", platform},
+ {"ptype", process_type}};
+
+ crashpad::UUID client_id;
+ crashpad::Settings* settings = 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);
+ }
+
+ base::FilePath dump_file_path;
+ if (!base::CreateTemporaryFile(&dump_file_path))
+ return false;
+
+ // Open the file with delete on close, to try and ensure it's cleaned up on
+ // any kind of failure.
+ base::File dump_file(dump_file_path, base::File::FLAG_OPEN |
+ base::File::FLAG_READ |
+ base::File::FLAG_WRITE |
+ base::File::FLAG_DELETE_ON_CLOSE);
+ if (!dump_file.IsValid())
+ return false;
+
+ uint32_t minidump_type = MiniDumpWithUnloadedModules |
+ MiniDumpWithProcessThreadData |
+ MiniDumpWithThreadInfo;
+
+ // Capture more detail for canary and dev channels. The prefix search caters
+ // for the soon to be outdated "-m" suffixed multi-install channels.
+ if (channel.find("canary") == 0 || channel.find("dev") == 0)
+ minidump_type |= MiniDumpWithIndirectlyReferencedMemory;
+
+ // Write the minidump to the temp file, and then copy the data to the
+ // Crashpad-provided handle, as the latter is only open for write.
+ if (!MiniDumpWriteDumpWithCrashpadInfo(process_, minidump_type, &exc_info,
+ crash_keys, client_id, report->uuid,
+ &dump_file) ||
+ !AppendFileContents(&dump_file, report->handle)) {
+ return false;
+ }
+
+ on_error.Disarm();
+
+ crashpad::UUID report_id = {};
+ status = database->FinishedWritingCrashReport(report, &report_id);
+ if (status != crashpad::CrashReportDatabase::kNoError)
+ return false;
+
+ return true;
+}
+
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/app/fallback_crash_handler_win.h b/chromium/components/crash/content/app/fallback_crash_handler_win.h
new file mode 100644
index 00000000000..579bcb5b57a
--- /dev/null
+++ b/chromium/components/crash/content/app/fallback_crash_handler_win.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_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_WIN_H_
+#define COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_WIN_H_
+
+#include <windows.h>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/process/process.h"
+#include "base/threading/platform_thread.h"
+
+namespace crash_reporter {
+
+// In the fallback crash handler, this invokes the system crash machinery
+// (MiniDumpWriteDump) to generate the crash report, then adds the report to
+// the Crashpad database for upload.
+class FallbackCrashHandler {
+ public:
+ FallbackCrashHandler();
+ ~FallbackCrashHandler();
+
+ // Parses |cmd_line| for the following arguments:
+ // --database=<path>
+ // Path to the Crashpad database where the minidump will be stored.
+ // --exception-pointers=<address>
+ // Address of exception pointers in the crashed process.
+ // --process=<handle>
+ // Handle to the process to dump.
+ // --thread=<id>
+ // ID of the crashing thread.
+ bool ParseCommandLine(const base::CommandLine& cmd_line);
+
+ // Generates a crashdump with Crashpad properties containing:
+ // prod: |product|
+ // ver: |version|
+ // channel: |cannel|
+ // plat: "Win32" or "Win64", depending on bitness.
+ // ptype: |process_type|.
+ bool GenerateCrashDump(const std::string& product,
+ const std::string& version,
+ const std::string& channel,
+ const std::string& process_type);
+
+ const base::Process& process() const { return process_; }
+
+ private:
+ base::Process process_;
+ base::PlatformThreadId thread_id_;
+ // This is a pointer in process_, which is hopefully not this process.
+ uintptr_t exception_ptrs_;
+ base::FilePath database_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(FallbackCrashHandler);
+};
+
+} // namespace crash_reporter
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLER_WIN_H_
diff --git a/chromium/components/crash/content/app/fallback_crash_handler_win_unittest.cc b/chromium/components/crash/content/app/fallback_crash_handler_win_unittest.cc
new file mode 100644
index 00000000000..f490a287da7
--- /dev/null
+++ b/chromium/components/crash/content/app/fallback_crash_handler_win_unittest.cc
@@ -0,0 +1,232 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/content/app/fallback_crash_handler_win.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/process/process_handle.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/multiprocess_test.h"
+#include "base/threading/platform_thread.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/win_util.h"
+#include "components/crash/content/app/fallback_crash_handler_launcher_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+#include "third_party/crashpad/crashpad/client/crash_report_database.h"
+#include "third_party/crashpad/crashpad/client/settings.h"
+#include "third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h"
+#include "third_party/crashpad/crashpad/util/file/file_reader.h"
+#include "third_party/crashpad/crashpad/util/misc/uuid.h"
+
+namespace crash_reporter {
+
+namespace {
+
+class ExceptionPointers {
+ public:
+ ExceptionPointers() {
+ RtlCaptureContext(&context_);
+ memset(&exception_, 0, sizeof(exception_));
+ exception_.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
+
+ exception_ptrs_.ExceptionRecord = &exception_;
+ exception_ptrs_.ContextRecord = &context_;
+ }
+
+ EXCEPTION_POINTERS* exception_ptrs() { return &exception_ptrs_; }
+ std::string AsString() {
+ return base::Uint64ToString(reinterpret_cast<uintptr_t>(exception_ptrs()));
+ }
+
+ private:
+ CONTEXT context_;
+ EXCEPTION_RECORD exception_;
+ EXCEPTION_POINTERS exception_ptrs_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExceptionPointers);
+};
+
+const char kProduct[] = "SomeProduct";
+const char kVersion[] = "1.2.3.6";
+const char kChannel[] = "canary";
+const char kProcessType[] = "Test";
+
+// This main function runs the handler to test.
+MULTIPROCESS_TEST_MAIN(FallbackCrashHandlerWinRunHandler) {
+ base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+
+ FallbackCrashHandler handler;
+ CHECK(handler.ParseCommandLine(*cmd_line));
+ CHECK(handler.GenerateCrashDump(kProduct, kVersion, kChannel, kProcessType));
+
+ return 0;
+}
+
+// This main function runs the setup and generates the simulated crash.
+MULTIPROCESS_TEST_MAIN(FallbackCrashHandlerWinMain) {
+ base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+
+ base::FilePath directory = cmd_line->GetSwitchValuePath("directory");
+ CHECK(!directory.empty());
+
+ base::CommandLine base_cmdline =
+ base::GetMultiProcessTestChildBaseCommandLine();
+ base_cmdline.AppendSwitchASCII(switches::kTestChildProcess,
+ "FallbackCrashHandlerWinRunHandler");
+
+ FallbackCrashHandlerLauncher launcher;
+ CHECK(launcher.Initialize(base_cmdline, directory));
+
+ ExceptionPointers exception_ptrs;
+ CHECK_EQ(0U,
+ launcher.LaunchAndWaitForHandler(exception_ptrs.exception_ptrs()));
+
+ return 0;
+}
+
+class FallbackCrashHandlerWinTest : public testing::Test {
+ public:
+ FallbackCrashHandlerWinTest() : self_handle_(base::kNullProcessHandle) {}
+
+ void SetUp() override {
+ ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
+
+ // Open a handle to our own process.
+ const DWORD kAccessMask =
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_DUP_HANDLE;
+ self_handle_ = OpenProcess(kAccessMask, FALSE, base::GetCurrentProcId());
+ DWORD err = GetLastError();
+ EXPECT_NE(base::kNullProcessHandle, self_handle_) << "GetLastError: "
+ << err;
+ }
+
+ void TearDown() override {
+ if (self_handle_ != base::kNullProcessHandle) {
+ CloseHandle(self_handle_);
+ self_handle_ = base::kNullProcessHandle;
+ }
+ }
+
+ std::string SelfHandleAsString() const {
+ return base::UintToString(base::win::HandleToUint32(self_handle_));
+ };
+
+ void CreateDatabase() {
+ std::unique_ptr<crashpad::CrashReportDatabase> database =
+ crashpad::CrashReportDatabase::InitializeWithoutCreating(
+ database_dir_.GetPath());
+ }
+
+ protected:
+ base::ProcessHandle self_handle_;
+ base::ScopedTempDir database_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(FallbackCrashHandlerWinTest);
+};
+
+} // namespace
+
+TEST_F(FallbackCrashHandlerWinTest, ParseCommandLine) {
+ FallbackCrashHandler handler;
+
+ // An empty command line shouldn't work.
+ base::CommandLine cmd_line(base::FilePath(L"empty"));
+ ASSERT_FALSE(handler.ParseCommandLine(cmd_line));
+
+ ExceptionPointers exception_ptrs;
+ cmd_line.AppendSwitchPath("database", database_dir_.GetPath());
+ cmd_line.AppendSwitchASCII("exception-pointers", exception_ptrs.AsString());
+ cmd_line.AppendSwitchASCII("process", SelfHandleAsString());
+
+ // Thread missing, still should fail.
+ ASSERT_FALSE(handler.ParseCommandLine(cmd_line));
+
+ cmd_line.AppendSwitchASCII(
+ "thread", base::UintToString(base::PlatformThread::CurrentId()));
+
+ // Should succeed with a fully populated command line.
+ // Because of how handle ownership is guarded, we have to "disown" it before
+ // the handler takes it over.
+ EXPECT_TRUE(handler.ParseCommandLine(cmd_line));
+ self_handle_ = base::kNullProcessHandle;
+}
+
+TEST_F(FallbackCrashHandlerWinTest, GenerateCrashDump) {
+ base::CommandLine cmd_line = base::GetMultiProcessTestChildBaseCommandLine();
+ cmd_line.AppendSwitchPath("directory", database_dir_.GetPath());
+ base::LaunchOptions options;
+ options.start_hidden = true;
+ base::Process test_child = base::SpawnMultiProcessTestChild(
+ "FallbackCrashHandlerWinMain", cmd_line, options);
+
+ ASSERT_TRUE(test_child.IsValid());
+ int exit_code = -1;
+ ASSERT_TRUE(test_child.WaitForExit(&exit_code));
+ ASSERT_EQ(0, exit_code);
+
+ // Validate that the database contains one valid crash dump.
+ std::unique_ptr<crashpad::CrashReportDatabase> database =
+ crashpad::CrashReportDatabase::InitializeWithoutCreating(
+ database_dir_.GetPath());
+
+ std::vector<crashpad::CrashReportDatabase::Report> reports;
+ ASSERT_EQ(crashpad::CrashReportDatabase::kNoError,
+ database->GetPendingReports(&reports));
+
+ EXPECT_EQ(1U, reports.size());
+
+ // Validate crashpad can read the produced minidump.
+ crashpad::FileReader minidump_file_reader;
+ ASSERT_TRUE(minidump_file_reader.Open(reports[0].file_path));
+
+ crashpad::ProcessSnapshotMinidump minidump_process_snapshot;
+ ASSERT_TRUE(minidump_process_snapshot.Initialize(&minidump_file_reader));
+
+ crashpad::UUID expected_client_id;
+ ASSERT_TRUE(database->GetSettings()->GetClientID(&expected_client_id));
+
+ // Validate that the CrashpadInfo in the report contains the same basic
+ // info, as does the database.
+ crashpad::UUID client_id;
+ minidump_process_snapshot.ClientID(&client_id);
+ EXPECT_EQ(expected_client_id, client_id);
+
+ crashpad::UUID report_id;
+ minidump_process_snapshot.ReportID(&report_id);
+ EXPECT_EQ(reports[0].uuid, report_id);
+
+ std::map<std::string, std::string> parameters =
+ minidump_process_snapshot.AnnotationsSimpleMap();
+ auto it = parameters.find("prod");
+ EXPECT_NE(parameters.end(), it);
+ EXPECT_EQ(kProduct, it->second);
+
+ it = parameters.find("ver");
+ EXPECT_NE(parameters.end(), it);
+ EXPECT_EQ(kVersion, it->second);
+
+ it = parameters.find("channel");
+ EXPECT_NE(parameters.end(), it);
+ EXPECT_EQ(kChannel, it->second);
+
+ it = parameters.find("plat");
+ EXPECT_NE(parameters.end(), it);
+#if defined(ARCH_CPU_64_BITS)
+ EXPECT_EQ("Win64", it->second);
+#else
+ EXPECT_EQ("Win32", it->second);
+#endif
+
+ it = parameters.find("ptype");
+ EXPECT_NE(parameters.end(), it);
+ EXPECT_EQ(kProcessType, it->second);
+}
+
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/app/fallback_crash_handling_win.cc b/chromium/components/crash/content/app/fallback_crash_handling_win.cc
new file mode 100644
index 00000000000..e83be6275ea
--- /dev/null
+++ b/chromium/components/crash/content/app/fallback_crash_handling_win.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/crash/content/app/fallback_crash_handling_win.h"
+
+#include <memory>
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "components/crash/content/app/crash_switches.h"
+#include "components/crash/content/app/fallback_crash_handler_launcher_win.h"
+#include "components/crash/content/app/fallback_crash_handler_win.h"
+
+namespace crash_reporter {
+
+namespace switches {
+const char kFallbackCrashHandler[] = "fallback-handler";
+const char kPrefetchArgument[] = "/prefetch:7";
+}
+
+const uint32_t kFallbackCrashTerminationCode = 0xFFFF8001;
+
+namespace {
+
+std::unique_ptr<FallbackCrashHandlerLauncher> g_fallback_crash_handler_launcher;
+
+LONG WINAPI FallbackUnhandledExceptionFilter(EXCEPTION_POINTERS* exc_ptrs) {
+ if (!g_fallback_crash_handler_launcher)
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ return g_fallback_crash_handler_launcher->LaunchAndWaitForHandler(exc_ptrs);
+}
+
+} // namespace
+
+bool SetupFallbackCrashHandling(const base::CommandLine& command_line) {
+ DCHECK(!g_fallback_crash_handler_launcher);
+
+ // Run the same program.
+ base::CommandLine base_command_line(command_line.GetProgram());
+ base_command_line.AppendSwitchASCII("type", switches::kFallbackCrashHandler);
+
+ // This is to support testing under gtest.
+ if (command_line.HasSwitch(::switches::kTestChildProcess)) {
+ base_command_line.AppendSwitchASCII(
+ ::switches::kTestChildProcess,
+ command_line.GetSwitchValueASCII(::switches::kTestChildProcess));
+ }
+
+ // All Chrome processes need a prefetch argument.
+ base_command_line.AppendArg(switches::kPrefetchArgument);
+
+ // Get the database path.
+ base::FilePath database_path = command_line.GetSwitchValuePath("database");
+ if (database_path.empty()) {
+ NOTREACHED();
+ return false;
+ }
+
+ std::unique_ptr<FallbackCrashHandlerLauncher> fallback_launcher(
+ new FallbackCrashHandlerLauncher());
+
+ if (!fallback_launcher->Initialize(base_command_line, database_path)) {
+ NOTREACHED();
+ return false;
+ }
+
+ // This is necessary because chrome_elf stubs out the
+ // SetUnhandledExceptionFilter in the IAT of chrome.exe.
+ typedef PTOP_LEVEL_EXCEPTION_FILTER(WINAPI *
+ SetUnhandledExceptionFilterFunction)(
+ PTOP_LEVEL_EXCEPTION_FILTER filter);
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ if (!kernel32)
+ return false;
+
+ SetUnhandledExceptionFilterFunction set_unhandled_exception_filter =
+ reinterpret_cast<SetUnhandledExceptionFilterFunction>(
+ GetProcAddress(kernel32, "SetUnhandledExceptionFilter"));
+ if (!set_unhandled_exception_filter)
+ return false;
+
+ // Success, pass ownership to the global.
+ g_fallback_crash_handler_launcher = std::move(fallback_launcher);
+
+ set_unhandled_exception_filter(&FallbackUnhandledExceptionFilter);
+
+ return true;
+}
+
+int RunAsFallbackCrashHandler(const base::CommandLine& command_line,
+ std::string product_name,
+ std::string version,
+ std::string channel_name) {
+ FallbackCrashHandler fallback_handler;
+
+ if (!fallback_handler.ParseCommandLine(command_line)) {
+ // TODO(siggi): Figure out how to UMA from this process, if need be.
+ return 1;
+ }
+
+ if (!fallback_handler.GenerateCrashDump(
+ product_name, version, channel_name,
+ crash_reporter::switches::kCrashpadHandler)) {
+ // TODO(siggi): Figure out how to UMA from this process, if need be.
+ return 2;
+ }
+
+ if (!fallback_handler.process().Terminate(kFallbackCrashTerminationCode,
+ false)) {
+ return 3;
+ }
+
+ return 0;
+}
+
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/app/fallback_crash_handling_win.h b/chromium/components/crash/content/app/fallback_crash_handling_win.h
new file mode 100644
index 00000000000..ebf5d9f0781
--- /dev/null
+++ b/chromium/components/crash/content/app/fallback_crash_handling_win.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_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLING_WIN_H_
+#define COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLING_WIN_H_
+
+#include <stdint.h>
+#include <string>
+
+#include "base/command_line.h"
+
+namespace crash_reporter {
+
+namespace switches {
+extern const char kFallbackCrashHandler[];
+// The fallback handler uses the same prefetch ID as the Crashpad handler.
+extern const char kPrefetchArgument[];
+}
+
+// A somewhat unique exit code for the crashed process. This is mostly
+// for testing, as there won't likely be anyone around to record the exit
+// code of the Crashpad handler.
+extern const uint32_t kFallbackCrashTerminationCode;
+
+// Sets up fallback crash handling for this process.
+// Note that this installs an unhandled exception filter, and requires
+// that the executable call the "RunAsFallbackCrashHandler" function, when
+// it's passed a --type switch with the value |kFallbackCrashHandlerType|.
+bool SetupFallbackCrashHandling(const base::CommandLine& command_line);
+
+// Runs the fallback crash handler process, to handle a crash in a process
+// that's previously called SetupFallbackCrashHandling.
+// The |product_name|, |version| and |channel_name| parameters will be used
+// as properties of the crash for the purposes of upload.
+int RunAsFallbackCrashHandler(const base::CommandLine& command_line,
+ std::string product_name,
+ std::string version,
+ std::string channel_name);
+
+} // namespace crash_reporter
+
+#endif // COMPONENTS_CRASH_CONTENT_APP_FALLBACK_CRASH_HANDLING_WIN_H_
diff --git a/chromium/components/crash/content/app/fallback_crash_handling_win_unittest.cc b/chromium/components/crash/content/app/fallback_crash_handling_win_unittest.cc
new file mode 100644
index 00000000000..7df6b20262a
--- /dev/null
+++ b/chromium/components/crash/content/app/fallback_crash_handling_win_unittest.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/crash/content/app/fallback_crash_handling_win.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/test/multiprocess_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+#include "third_party/crashpad/crashpad/client/crash_report_database.h"
+
+namespace crash_reporter {
+
+namespace {
+
+const DWORD kExceptionCode = 0xCAFEBABE;
+
+// This main function runs in two modes, first as a faux-crashpad handler,
+// and then with --type=fallback-handler to handle the crash in the first
+// instance.
+MULTIPROCESS_TEST_MAIN(FallbackCrashHandlingWinRunHandler) {
+ base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+ if (cmd_line->GetSwitchValueASCII("type") ==
+ switches::kFallbackCrashHandler) {
+ return RunAsFallbackCrashHandler(*cmd_line,
+ "FallbackCrashHandlingWinRunHandler",
+ "1.2.3.4", "FakeChannel");
+ }
+
+ CHECK(SetupFallbackCrashHandling(*cmd_line));
+
+ // Provoke a crash with a well-defined exception code.
+ // The process shouldn't terminate with this exception code.
+ RaiseException(kExceptionCode, 0, 0, nullptr);
+
+ // This process should never return from the exception.
+ CHECK(false) << "Unexpected return from RaiseException";
+
+ // Should never get here.
+ return 0;
+}
+
+class FallbackCrashHandlingTest : public base::MultiProcessTest {
+ public:
+ void SetUp() override { ASSERT_TRUE(database_dir_.CreateUniqueTempDir()); }
+
+ protected:
+ base::ScopedTempDir database_dir_;
+};
+
+} // namespace
+
+TEST_F(FallbackCrashHandlingTest, SetupAndRunAsFallbackCrashHandler) {
+ // Launch a subprocess to test the fallback handling implementation.
+ base::CommandLine cmd_line = base::GetMultiProcessTestChildBaseCommandLine();
+ cmd_line.AppendSwitchPath("database", database_dir_.GetPath());
+
+ base::LaunchOptions options;
+ options.start_hidden = true;
+ base::Process test_child = base::SpawnMultiProcessTestChild(
+ "FallbackCrashHandlingWinRunHandler", cmd_line, options);
+
+ ASSERT_TRUE(test_child.IsValid());
+ int exit_code = -1;
+ ASSERT_TRUE(test_child.WaitForExit(&exit_code));
+ ASSERT_EQ(kFallbackCrashTerminationCode, static_cast<uint32_t>(exit_code));
+
+ // Validate that the database contains one valid crash dump.
+ std::unique_ptr<crashpad::CrashReportDatabase> database =
+ crashpad::CrashReportDatabase::InitializeWithoutCreating(
+ database_dir_.GetPath());
+
+ std::vector<crashpad::CrashReportDatabase::Report> reports;
+ ASSERT_EQ(crashpad::CrashReportDatabase::kNoError,
+ database->GetPendingReports(&reports));
+
+ EXPECT_EQ(1U, reports.size());
+}
+
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/browser/BUILD.gn b/chromium/components/crash/content/browser/BUILD.gn
index 6eecf543f61..20da935224c 100644
--- a/chromium/components/crash/content/browser/BUILD.gn
+++ b/chromium/components/crash/content/browser/BUILD.gn
@@ -10,8 +10,8 @@ source_set("browser") {
sources = [
"crash_dump_manager_android.cc",
"crash_dump_manager_android.h",
- "crash_micro_dump_manager_android.cc",
- "crash_micro_dump_manager_android.h",
+ "crash_dump_observer_android.cc",
+ "crash_dump_observer_android.h",
]
deps = [
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 6fc9b2d5211..bec13bdd5f4 100644
--- a/chromium/components/crash/content/browser/crash_dump_manager_android.cc
+++ b/chromium/components/crash/content/browser/crash_dump_manager_android.cc
@@ -16,6 +16,7 @@
#include "base/rand_util.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.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"
@@ -27,43 +28,25 @@ using content::BrowserThread;
namespace breakpad {
-// static
-CrashDumpManager* CrashDumpManager::instance_ = NULL;
-
-// static
-CrashDumpManager* CrashDumpManager::GetInstance() {
- CHECK(instance_);
- return instance_;
-}
-
-CrashDumpManager::CrashDumpManager(const base::FilePath& crash_dump_dir)
- : crash_dump_dir_(crash_dump_dir) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- DCHECK(!instance_);
-
- instance_ = this;
-
- notification_registrar_.Add(this,
- content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
- content::NotificationService::AllSources());
- notification_registrar_.Add(this,
- content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
- content::NotificationService::AllSources());
-
- BrowserChildProcessObserver::Add(this);
-}
+CrashDumpManager::CrashDumpManager(const base::FilePath& crash_dump_dir,
+ int descriptor_id)
+ : crash_dump_dir_(crash_dump_dir), descriptor_id_(descriptor_id) {}
CrashDumpManager::~CrashDumpManager() {
- instance_ = NULL;
-
- BrowserChildProcessObserver::Remove(this);
}
-base::File CrashDumpManager::CreateMinidumpFile(int child_process_id) {
+void CrashDumpManager::OnChildStart(int child_process_id,
+ content::FileDescriptorInfo* mappings) {
DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+
+ if (!breakpad::IsCrashReporterEnabled())
+ return;
+
base::FilePath minidump_path;
- if (!base::CreateTemporaryFile(&minidump_path))
- return base::File();
+ if (!base::CreateTemporaryFile(&minidump_path)) {
+ LOG(ERROR) << "Failed to create temporary file, crash won't be reported.";
+ return;
+ }
// We need read permission as the minidump is generated in several phases
// and needs to be read at some point.
@@ -71,8 +54,8 @@ base::File CrashDumpManager::CreateMinidumpFile(int child_process_id) {
base::File::FLAG_WRITE;
base::File minidump_file(minidump_path, flags);
if (!minidump_file.IsValid()) {
- LOG(ERROR) << "Failed to create temporary file, crash won't be reported.";
- return base::File();
+ LOG(ERROR) << "Failed to open temporary file, crash won't be reported.";
+ return;
}
{
@@ -81,18 +64,19 @@ base::File CrashDumpManager::CreateMinidumpFile(int child_process_id) {
child_process_id));
child_process_id_to_minidump_path_[child_process_id] = minidump_path;
}
- return minidump_file;
+ mappings->Transfer(descriptor_id_,
+ base::ScopedFD(minidump_file.TakePlatformFile()));
}
// static
void CrashDumpManager::ProcessMinidump(
const base::FilePath& minidump_path,
+ const base::FilePath& crash_dump_dir,
base::ProcessHandle pid,
content::ProcessType process_type,
base::TerminationStatus termination_status,
base::android::ApplicationState app_state) {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- CHECK(instance_);
int64_t file_size = 0;
int r = base::GetFileSize(minidump_path, &file_size);
DCHECK(r) << "Failed to retrieve size for minidump "
@@ -153,7 +137,7 @@ void CrashDumpManager::ProcessMinidump(
// We are dealing with a valid minidump. Copy it to the crash report
// directory from where Java code will upload it later on.
- if (instance_->crash_dump_dir_.empty()) {
+ if (crash_dump_dir.empty()) {
NOTREACHED() << "Failed to retrieve the crash dump directory.";
return;
}
@@ -161,7 +145,7 @@ void CrashDumpManager::ProcessMinidump(
const std::string filename =
base::StringPrintf("chromium-renderer-minidump-%016" PRIx64 ".dmp%d",
rand, pid);
- base::FilePath dest_path = instance_->crash_dump_dir_.Append(filename);
+ base::FilePath dest_path = crash_dump_dir.Append(filename);
r = base::Move(minidump_path, dest_path);
if (!r) {
LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value()
@@ -169,67 +153,8 @@ void CrashDumpManager::ProcessMinidump(
base::DeleteFile(minidump_path, false);
return;
}
- VLOG(1) << "Crash minidump successfully generated: " <<
- instance_->crash_dump_dir_.Append(filename).value();
-}
-
-void CrashDumpManager::BrowserChildProcessHostDisconnected(
- const content::ChildProcessData& data) {
- OnChildExit(data.id,
- data.handle,
- static_cast<content::ProcessType>(data.process_type),
- base::TERMINATION_STATUS_MAX_ENUM,
- base::android::ApplicationStatusListener::GetState());
-}
-
-void CrashDumpManager::BrowserChildProcessCrashed(
- const content::ChildProcessData& data,
- int exit_code) {
- OnChildExit(data.id,
- data.handle,
- static_cast<content::ProcessType>(data.process_type),
- base::TERMINATION_STATUS_ABNORMAL_TERMINATION,
- base::android::APPLICATION_STATE_UNKNOWN);
-}
-
-void CrashDumpManager::Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
- content::RenderProcessHost* rph =
- content::Source<content::RenderProcessHost>(source).ptr();
- base::TerminationStatus term_status = base::TERMINATION_STATUS_MAX_ENUM;
- base::android::ApplicationState app_state =
- base::android::APPLICATION_STATE_UNKNOWN;
- switch (type) {
- case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
- // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer
- // process is cleanly shutdown. However, we still need to close the
- // minidump_fd we kept open.
- term_status = base::TERMINATION_STATUS_NORMAL_TERMINATION;
- break;
- }
- case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
- // We do not care about android fast shutdowns as it is a known case where
- // the renderer is intentionally killed when we are done with it.
- if (rph->FastShutdownStarted()) {
- break;
- }
- content::RenderProcessHost::RendererClosedDetails* process_details =
- content::Details<content::RenderProcessHost::RendererClosedDetails>(
- details).ptr();
- term_status = process_details->status;
- app_state = base::android::ApplicationStatusListener::GetState();
- break;
- }
- default:
- NOTREACHED();
- return;
- }
- OnChildExit(rph->GetID(),
- rph->GetHandle(),
- content::PROCESS_TYPE_RENDERER,
- term_status,
- app_state);
+ VLOG(1) << "Crash minidump successfully generated: "
+ << crash_dump_dir.Append(filename).value();
}
void CrashDumpManager::OnChildExit(int child_process_id,
@@ -252,11 +177,8 @@ void CrashDumpManager::OnChildExit(int child_process_id,
}
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
- base::Bind(&CrashDumpManager::ProcessMinidump,
- minidump_path,
- pid,
- process_type,
- termination_status,
+ 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_manager_android.h b/chromium/components/crash/content/browser/crash_dump_manager_android.h
index 79930f6a979..ad711936d7f 100644
--- a/chromium/components/crash/content/browser/crash_dump_manager_android.h
+++ b/chromium/components/crash/content/browser/crash_dump_manager_android.h
@@ -7,21 +7,9 @@
#include <map>
-#include "base/android/application_status_listener.h"
-#include "base/files/file.h"
#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/process/kill.h"
-#include "base/process/process.h"
#include "base/synchronization/lock.h"
-#include "content/public/browser/browser_child_process_observer.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-#include "content/public/common/process_type.h"
-
-namespace content {
-class RenderProcessHost;
-}
+#include "components/crash/content/browser/crash_dump_observer_android.h"
namespace breakpad {
@@ -31,23 +19,23 @@ namespace breakpad {
// (as the browser process does not have access to some system files for the
// crashed process). So the minidump is generated in the renderer process.
// Since the isolated process cannot open files, we provide it on creation with
-// a file descriptor where to write the minidump in the event of a crash.
+// a file descriptor into which to write the minidump in the event of a crash.
// This class creates these file descriptors and associates them with render
-// processes and take the appropriate action when the render process terminates.
-class CrashDumpManager : public content::BrowserChildProcessObserver,
- public content::NotificationObserver {
+// processes and takes the appropriate action when the render process
+// terminates.
+class CrashDumpManager : public breakpad::CrashDumpObserver::Client {
public:
- // The embedder should create a single instance of the CrashDumpManager.
- static CrashDumpManager* GetInstance();
-
- // Should be created on the UI thread.
- explicit CrashDumpManager(const base::FilePath& crash_dump_dir);
-
+ CrashDumpManager(const base::FilePath& crash_dump_dir, int descriptor_id);
~CrashDumpManager() override;
- // Returns a file that should be used to generate a minidump for the process
- // |child_process_id|.
- base::File CreateMinidumpFile(int child_process_id);
+ // breakpad::CrashDumpObserver::Client implementation:
+ void OnChildStart(int child_process_id,
+ content::FileDescriptorInfo* mappings) override;
+ void OnChildExit(int child_process_id,
+ base::ProcessHandle pid,
+ content::ProcessType process_type,
+ base::TerminationStatus termination_status,
+ base::android::ApplicationState app_state) override;
private:
typedef std::map<int, base::FilePath> ChildProcessIDToMinidumpPath;
@@ -65,40 +53,23 @@ class CrashDumpManager : public content::BrowserChildProcessObserver,
};
static void ProcessMinidump(const base::FilePath& minidump_path,
+ const base::FilePath& crash_dump_dir,
base::ProcessHandle pid,
content::ProcessType process_type,
base::TerminationStatus termination_status,
base::android::ApplicationState app_state);
- // content::BrowserChildProcessObserver implementation:
- void BrowserChildProcessHostDisconnected(
- const content::ChildProcessData& data) override;
- void BrowserChildProcessCrashed(
- const content::ChildProcessData& data,
- int exit_code) override;
-
- // NotificationObserver implementation:
- void Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) override;
-
- // Called on child process exit (including crash).
- void OnChildExit(int child_process_id,
- base::ProcessHandle pid,
- content::ProcessType process_type,
- base::TerminationStatus termination_status,
- base::android::ApplicationState app_state);
-
- content::NotificationRegistrar notification_registrar_;
-
// This map should only be accessed with its lock aquired as it is accessed
// from the PROCESS_LAUNCHER and UI threads.
base::Lock child_process_id_to_minidump_path_lock_;
ChildProcessIDToMinidumpPath child_process_id_to_minidump_path_;
+ // The directory in which temporary minidump files should be created.
base::FilePath crash_dump_dir_;
- static CrashDumpManager* instance_;
+ // The id used to identify the file descriptor in the set of file
+ // descriptor mappings passed to the child process.
+ int descriptor_id_;
DISALLOW_COPY_AND_ASSIGN(CrashDumpManager);
};
diff --git a/chromium/components/crash/content/browser/crash_dump_observer_android.cc b/chromium/components/crash/content/browser/crash_dump_observer_android.cc
new file mode 100644
index 00000000000..9a784b6d14b
--- /dev/null
+++ b/chromium/components/crash/content/browser/crash_dump_observer_android.cc
@@ -0,0 +1,154 @@
+// 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/crash/content/browser/crash_dump_observer_android.h"
+
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+
+using content::BrowserThread;
+
+namespace breakpad {
+
+namespace {
+base::LazyInstance<CrashDumpObserver> g_instance = LAZY_INSTANCE_INITIALIZER;
+}
+
+// static
+void CrashDumpObserver::Create() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ // If this DCHECK fails in a unit test then a previously executing
+ // test that makes use of CrashDumpObserver forgot to create a
+ // ShadowingAtExitManager.
+ DCHECK(g_instance == nullptr);
+ g_instance.Get();
+}
+
+// static
+CrashDumpObserver* CrashDumpObserver::GetInstance() {
+ DCHECK(!(g_instance == nullptr));
+ return g_instance.Pointer();
+}
+
+CrashDumpObserver::CrashDumpObserver() {
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
+ content::NotificationService::AllSources());
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
+ content::NotificationService::AllSources());
+ BrowserChildProcessObserver::Add(this);
+}
+
+CrashDumpObserver::~CrashDumpObserver() {
+ BrowserChildProcessObserver::Remove(this);
+}
+
+void CrashDumpObserver::RegisterClient(std::unique_ptr<Client> client) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ base::AutoLock auto_lock(registered_clients_lock_);
+ registered_clients_.push_back(std::move(client));
+}
+
+void CrashDumpObserver::OnChildExit(int child_process_id,
+ base::ProcessHandle pid,
+ content::ProcessType process_type,
+ base::TerminationStatus termination_status,
+ base::android::ApplicationState app_state) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ std::vector<Client*> registered_clients_copy;
+ {
+ base::AutoLock auto_lock(registered_clients_lock_);
+ for (auto& client : registered_clients_)
+ registered_clients_copy.push_back(client.get());
+ }
+ for (auto& client : registered_clients_copy) {
+ client->OnChildExit(child_process_id, pid, process_type, termination_status,
+ app_state);
+ }
+}
+
+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_);
+ for (auto& client : registered_clients_)
+ registered_clients_copy.push_back(client.get());
+ }
+ for (auto& client : registered_clients_copy) {
+ client->OnChildStart(child_process_id, mappings);
+ }
+}
+
+void CrashDumpObserver::BrowserChildProcessHostDisconnected(
+ const content::ChildProcessData& data) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ OnChildExit(data.id, data.handle,
+ static_cast<content::ProcessType>(data.process_type),
+ base::TERMINATION_STATUS_MAX_ENUM,
+ base::android::ApplicationStatusListener::GetState());
+}
+
+void CrashDumpObserver::BrowserChildProcessCrashed(
+ const content::ChildProcessData& data,
+ int exit_code) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ OnChildExit(data.id, data.handle,
+ static_cast<content::ProcessType>(data.process_type),
+ base::TERMINATION_STATUS_ABNORMAL_TERMINATION,
+ base::android::APPLICATION_STATE_UNKNOWN);
+}
+
+void CrashDumpObserver::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ content::RenderProcessHost* rph =
+ content::Source<content::RenderProcessHost>(source).ptr();
+ base::TerminationStatus term_status = base::TERMINATION_STATUS_MAX_ENUM;
+ base::android::ApplicationState app_state =
+ base::android::APPLICATION_STATE_UNKNOWN;
+ switch (type) {
+ case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
+ // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer
+ // process is cleanly shutdown.
+ term_status = base::TERMINATION_STATUS_NORMAL_TERMINATION;
+ break;
+ }
+ case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
+ // We do not care about android fast shutdowns as it is a known case where
+ // the renderer is intentionally killed when we are done with it.
+ if (rph->FastShutdownStarted()) {
+ break;
+ }
+ content::RenderProcessHost::RendererClosedDetails* process_details =
+ content::Details<content::RenderProcessHost::RendererClosedDetails>(
+ details)
+ .ptr();
+ term_status = process_details->status;
+ app_state = base::android::ApplicationStatusListener::GetState();
+ break;
+ }
+ default:
+ NOTREACHED();
+ return;
+ }
+
+ OnChildExit(rph->GetID(), rph->GetHandle(), content::PROCESS_TYPE_RENDERER,
+ term_status, app_state);
+}
+
+} // namespace breakpad
diff --git a/chromium/components/crash/content/browser/crash_dump_observer_android.h b/chromium/components/crash/content/browser/crash_dump_observer_android.h
new file mode 100644
index 00000000000..42c5e4f21c2
--- /dev/null
+++ b/chromium/components/crash/content/browser/crash_dump_observer_android.h
@@ -0,0 +1,109 @@
+// 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_CRASH_CONTENT_BROWSER_CRASH_DUMP_OBSERVER_ANDROID_H_
+#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_OBSERVER_ANDROID_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/android/application_status_listener.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ref_counted.h"
+#include "base/process/kill.h"
+#include "base/process/process.h"
+#include "content/public/browser/browser_child_process_observer.h"
+#include "content/public/browser/file_descriptor_info.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/common/process_type.h"
+
+namespace breakpad {
+
+// This class centralises the observation of child processes for the
+// purpose of reacting to child process crashes.
+// The CrashDumpObserver instance exists on the browser main thread.
+class CrashDumpObserver : public content::BrowserChildProcessObserver,
+ public content::NotificationObserver {
+ public:
+ // CrashDumpObserver client interface.
+ // Client methods will be called synchronously in the order in which
+ // clients were registered. It is the implementer's responsibility
+ // to post tasks to the appropriate threads if required (and be
+ // aware that this may break ordering guarantees).
+ class Client {
+ public:
+ // OnChildStart is called on the launcher thread.
+ virtual void OnChildStart(int child_process_id,
+ content::FileDescriptorInfo* mappings) = 0;
+ // OnChildExit is called on the UI thread.
+ // OnChildExit may be called twice (once for the child process
+ // termination, and once for the IPC channel disconnection).
+ virtual void OnChildExit(int child_process_id,
+ base::ProcessHandle pid,
+ content::ProcessType process_type,
+ base::TerminationStatus termination_status,
+ base::android::ApplicationState app_state) = 0;
+
+ virtual ~Client() {}
+ };
+
+ // The global CrashDumpObserver instance is created by calling
+ // Create (on the UI thread), and lives until process exit. Tests
+ // making use of this class should register an AtExitManager.
+ static void Create();
+
+ // Fetch a pointer to the global CrashDumpObserver instance. The
+ // global instance must have been created by the time GetInstance is
+ // called.
+ static CrashDumpObserver* GetInstance();
+
+ void RegisterClient(std::unique_ptr<Client> client);
+
+ // BrowserChildProcessStarted must be called from
+ // ContentBrowserClient::GetAdditionalMappedFilesForChildProcess
+ // overrides, to notify the CrashDumpObserver of child process
+ // creation, and to allow clients to register any fd mappings they
+ // need.
+ void BrowserChildProcessStarted(int child_process_id,
+ content::FileDescriptorInfo* mappings);
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<CrashDumpObserver>;
+
+ CrashDumpObserver();
+ ~CrashDumpObserver() override;
+
+ // content::BrowserChildProcessObserver implementation:
+ void BrowserChildProcessHostDisconnected(
+ const content::ChildProcessData& data) override;
+ void BrowserChildProcessCrashed(const content::ChildProcessData& data,
+ int exit_code) override;
+ // On Android we will never observe BrowserChildProcessCrashed
+ // because we do not receive exit codes from zygote spawned
+ // processes.
+
+ // NotificationObserver implementation:
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // Called on child process exit (including crash).
+ void OnChildExit(int child_process_id,
+ base::ProcessHandle pid,
+ content::ProcessType process_type,
+ base::TerminationStatus termination_status,
+ base::android::ApplicationState app_state);
+
+ content::NotificationRegistrar notification_registrar_;
+
+ base::Lock registered_clients_lock_;
+ std::vector<std::unique_ptr<Client>> registered_clients_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashDumpObserver);
+};
+
+} // namespace breakpad
+
+#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_OBSERVER_ANDROID_H_
diff --git a/chromium/components/crash/content/browser/crash_micro_dump_manager_android.cc b/chromium/components/crash/content/browser/crash_micro_dump_manager_android.cc
deleted file mode 100644
index cfb4b1c2d75..00000000000
--- a/chromium/components/crash/content/browser/crash_micro_dump_manager_android.cc
+++ /dev/null
@@ -1,129 +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/crash/content/browser/crash_micro_dump_manager_android.h"
-
-#include <unistd.h>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/child_process_data.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/browser/render_process_host.h"
-
-using content::BrowserThread;
-
-namespace breakpad {
-
-// static
-CrashMicroDumpManager* CrashMicroDumpManager::GetInstance() {
- return base::Singleton<CrashMicroDumpManager>::get();
-}
-
-CrashMicroDumpManager::CrashMicroDumpManager() : weak_factory_(this) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
- notification_registrar_.Add(this,
- content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
- content::NotificationService::AllSources());
- notification_registrar_.Add(this,
- content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
- content::NotificationService::AllSources());
-}
-
-CrashMicroDumpManager::~CrashMicroDumpManager() {
- base::AutoLock auto_lock(child_process_id_to_pipe_lock_);
- child_process_id_to_pipe_.clear();
-}
-
-base::ScopedFD CrashMicroDumpManager::CreateCrashInfoChannel(
- int child_process_id) {
- DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
- std::unique_ptr<base::SyncSocket> local_pipe(new base::SyncSocket());
- std::unique_ptr<base::SyncSocket> child_pipe(new base::SyncSocket());
- if (!base::SyncSocket::CreatePair(local_pipe.get(), child_pipe.get()))
- return base::ScopedFD();
- {
- base::AutoLock auto_lock(child_process_id_to_pipe_lock_);
- DCHECK(!ContainsKey(child_process_id_to_pipe_, child_process_id));
- child_process_id_to_pipe_[child_process_id] = std::move(local_pipe);
- }
- return base::ScopedFD(dup(child_pipe->handle()));
-}
-
-void CrashMicroDumpManager::HandleChildTerminationOnFileThread(
- int child_process_id,
- base::TerminationStatus termination_status) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-
- std::unique_ptr<base::SyncSocket> pipe;
- {
- base::AutoLock auto_lock(child_process_id_to_pipe_lock_);
- const auto& iter = child_process_id_to_pipe_.find(child_process_id);
- if (iter == child_process_id_to_pipe_.end()) {
- // We might get a NOTIFICATION_RENDERER_PROCESS_TERMINATED and a
- // NOTIFICATION_RENDERER_PROCESS_CLOSED.
- return;
- }
- pipe = std::move(iter->second);
- child_process_id_to_pipe_.erase(iter);
- }
- DCHECK(pipe->handle() != base::SyncSocket::kInvalidHandle);
-
- if (termination_status == base::TERMINATION_STATUS_NORMAL_TERMINATION)
- return;
- if (pipe->Peek() >= sizeof(int)) {
- int exit_code;
- pipe->Receive(&exit_code, sizeof(exit_code));
- LOG(FATAL) << "Renderer process crash detected (code " << exit_code
- << "). Terminating browser.";
- } else {
- // The child process hasn't written anything into the pipe. This implies
- // that it was terminated via SIGKILL by the low memory killer, and thus we
- // need to perform a clean exit.
- exit(0);
- }
-}
-
-void CrashMicroDumpManager::Observe(
- int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
- content::RenderProcessHost* rph =
- content::Source<content::RenderProcessHost>(source).ptr();
- // In case of a normal termination we just need to close the pipe_fd we kept
- // open.
- base::TerminationStatus termination_status =
- base::TERMINATION_STATUS_NORMAL_TERMINATION;
- switch (type) {
- case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
- // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer
- // process is cleanly shutdown.
- break;
- }
- case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
- // Android fast shutdowns is a known case where the renderer is
- // intentionally killed when we are done with it.
- if (!rph->FastShutdownStarted()) {
- content::RenderProcessHost::RendererClosedDetails* process_details =
- content::Details<content::RenderProcessHost::RendererClosedDetails>(
- details).ptr();
- termination_status = process_details->status;
- }
- break;
- }
- default:
- NOTREACHED();
- return;
- }
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- base::Bind(&CrashMicroDumpManager::HandleChildTerminationOnFileThread,
- weak_factory_.GetWeakPtr(), rph->GetID(),
- termination_status));
-}
-
-} // namespace breakpad
diff --git a/chromium/components/crash/content/browser/crash_micro_dump_manager_android.h b/chromium/components/crash/content/browser/crash_micro_dump_manager_android.h
deleted file mode 100644
index 753b344030a..00000000000
--- a/chromium/components/crash/content/browser/crash_micro_dump_manager_android.h
+++ /dev/null
@@ -1,69 +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_CRASH_CONTENT_BROWSER_CRASH_MICRO_DUMP_MANAGER_ANDROID_H_
-#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_MICRO_DUMP_MANAGER_ANDROID_H_
-
-#include <map>
-#include <memory>
-
-#include "base/files/scoped_file.h"
-#include "base/memory/singleton.h"
-#include "base/memory/weak_ptr.h"
-#include "base/process/kill.h"
-#include "base/sync_socket.h"
-#include "base/synchronization/lock.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-
-namespace breakpad {
-
-// This class manages behavior of the browser on renderer crashes when
-// microdumps are used for capturing the crash stack. Normally, in this case
-// the browser doesn't need to do much, because a microdump is written into
-// Android log by the renderer process itself. However, the browser may need to
-// crash itself on a renderer crash. Since on Android renderers are not child
-// processes of the browser, it can't access the exit code. Instead, the browser
-// uses a dedicated pipe in order to receive the information about the renderer
-// crash status.
-class CrashMicroDumpManager : public content::NotificationObserver {
- public:
- // There is only a single instance of CrashMicroDumpManager per browser
- // process. It needs to be created on the UI thread.
- static CrashMicroDumpManager* GetInstance();
-
- // Returns a pipe that should be used to transfer termination cause of
- // |child_process_id|.
- base::ScopedFD CreateCrashInfoChannel(int child_process_id);
-
- private:
- friend struct base::DefaultSingletonTraits<CrashMicroDumpManager>;
-
- CrashMicroDumpManager();
- ~CrashMicroDumpManager() override;
-
- // NotificationObserver implementation:
- void Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) override;
-
- // Called on child process exit (including crash).
- void HandleChildTerminationOnFileThread(
- int child_process_id,
- base::TerminationStatus termination_status);
-
- content::NotificationRegistrar notification_registrar_;
-
- // This map should only be accessed with its lock aquired as it is accessed
- // from the PROCESS_LAUNCHER, FILE, and UI threads.
- base::Lock child_process_id_to_pipe_lock_;
- std::map<int, std::unique_ptr<base::SyncSocket>> child_process_id_to_pipe_;
- base::WeakPtrFactory<CrashMicroDumpManager> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(CrashMicroDumpManager);
-};
-
-} // namespace breakpad
-
-#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_MICRO_DUMP_MANAGER_ANDROID_H_
diff --git a/chromium/components/crash/core/browser/crashes_ui_util.cc b/chromium/components/crash/core/browser/crashes_ui_util.cc
index c83361c0de9..b9651dd46a2 100644
--- a/chromium/components/crash/core/browser/crashes_ui_util.cc
+++ b/chromium/components/crash/core/browser/crashes_ui_util.cc
@@ -22,7 +22,9 @@ const CrashesUILocalizedString kCrashesUILocalizedStrings[] = {
{"crashCountFormat", IDS_CRASH_CRASH_COUNT_BANNER_FORMAT},
{"crashHeaderFormat", IDS_CRASH_CRASH_HEADER_FORMAT},
{"crashHeaderFormatLocalOnly", IDS_CRASH_CRASH_HEADER_FORMAT_LOCAL_ONLY},
- {"crashTimeFormat", IDS_CRASH_CRASH_TIME_FORMAT},
+ {"crashUploadTimeFormat", IDS_CRASH_UPLOAD_TIME_FORMAT},
+ {"crashCaptureAndUploadTimeFormat",
+ IDS_CRASH_CAPTURE_AND_UPLOAD_TIME_FORMAT},
{"crashNotUploaded", IDS_CRASH_CRASH_NOT_UPLOADED},
{"crashUserRequested", IDS_CRASH_CRASH_USER_REQUESTED},
{"crashPending", IDS_CRASH_CRASH_PENDING},
@@ -69,10 +71,11 @@ void UploadListToValue(UploadList* upload_list, base::ListValue* out_value) {
std::unique_ptr<base::DictionaryValue> crash(new base::DictionaryValue());
crash->SetString("id", info.upload_id);
if (info.state == UploadList::UploadInfo::State::Uploaded) {
- crash->SetString("time",
+ crash->SetString("upload_time",
base::TimeFormatFriendlyDateAndTime(info.upload_time));
- } else {
- crash->SetString("time",
+ }
+ if (!info.capture_time.is_null()) {
+ crash->SetString("capture_time",
base::TimeFormatFriendlyDateAndTime(info.capture_time));
}
crash->SetString("local_id", info.local_id);
diff --git a/chromium/components/crash/core/browser/resources/crashes.js b/chromium/components/crash/core/browser/resources/crashes.js
index 8894d32fdc7..dbd2ae8df59 100644
--- a/chromium/components/crash/core/browser/resources/crashes.js
+++ b/chromium/components/crash/core/browser/resources/crashes.js
@@ -33,6 +33,9 @@ function updateCrashList(
$('disabledMode').hidden = enabled;
$('crashUploadStatus').hidden = !enabled || !dynamicBackend;
+ // Make the height fixed while clearing the
+ // element in order to maintain scroll position.
+ crashSection.style.height = getComputedStyle(crashSection).height;
// Clear any previous list.
crashSection.textContent = '';
@@ -46,6 +49,7 @@ function updateCrashList(
var crashBlock = document.createElement('div');
if (crash.state != 'uploaded')
crashBlock.className = 'notUploaded';
+
var title = document.createElement('h3');
var uploaded = crash.state == 'uploaded';
if (uploaded) {
@@ -57,11 +61,20 @@ function updateCrashList(
crash.local_id);
}
crashBlock.appendChild(title);
+
if (uploaded) {
var date = document.createElement('p');
- date.textContent = loadTimeData.getStringF('crashTimeFormat',
- crash.time);
+ date.textContent = ""
+ if (crash.capture_time) {
+ date.textContent += loadTimeData.getStringF(
+ 'crashCaptureAndUploadTimeFormat', crash.capture_time,
+ crash.upload_time);
+ } else {
+ date.textContent += loadTimeData.getStringF('crashUploadTimeFormat',
+ crash.upload_time);
+ }
crashBlock.appendChild(date);
+
var linkBlock = document.createElement('p');
var link = document.createElement('a');
var commentLines = [
@@ -113,7 +126,7 @@ function updateCrashList(
var crashText = document.createElement('p');
crashText.textContent = loadTimeData.getStringF(textContentKey,
- crash.time);
+ crash.capture_time);
crashBlock.appendChild(crashText);
if (crash.file_size != '') {
@@ -140,6 +153,8 @@ function updateCrashList(
crashSection.appendChild(crashBlock);
}
+ // Reset the height, in order to accommodate for the new content.
+ crashSection.style.height = "";
$('noCrashes').hidden = crashes.length != 0;
}
diff --git a/chromium/components/crash_strings.grdp b/chromium/components/crash_strings.grdp
index c38d7752a26..ab130d69648 100644
--- a/chromium/components/crash_strings.grdp
+++ b/chromium/components/crash_strings.grdp
@@ -13,8 +13,11 @@
<message name="IDS_CRASH_CRASH_HEADER_FORMAT_LOCAL_ONLY" desc="Format for crash entry headings on chrome://crashes that have not been uploaded">
Crash ID <ph name="CRASH_LOCAL_ID">$1<ex>123456-789789</ex></ph>
</message>
- <message name="IDS_CRASH_CRASH_TIME_FORMAT" desc="Format for crash entry occurrence time on chrome://crashes">
- Automatically reported <ph name="CRASH_TIME">$1<ex>Tuesday, January 25, 2011 2:58:02 PM</ex></ph>
+ <message name="IDS_CRASH_UPLOAD_TIME_FORMAT" desc="Format for crash report upload time displayed on chrome://crashes">
+ Crash report uploaded on <ph name="UPLOAD_TIME">$1<ex>Tuesday, January 25, 2011 2:58:02 PM</ex></ph>
+ </message>
+ <message name="IDS_CRASH_CAPTURE_AND_UPLOAD_TIME_FORMAT" desc="Format for the time and date at which the crash report was captured, and subsequently uploaded, to display on chrome://crashes">
+ Crash report captured on <ph name="CRASH_TIME">$1<ex>Tuesday, January 25, 2011 2:58:02 PM</ex></ph>, uploaded on <ph name="UPLOAD_TIME">$2<ex>Tuesday, January 25, 2011 2:58:02 PM</ex></ph>
</message>
<message name="IDS_CRASH_CRASH_NOT_UPLOADED" desc="Format for crash entry occurrence on chrome://crashes that was not uploaded to the server">
Crash report captured on <ph name="CRASH_TIME">$1<ex>Tuesday, January 25, 2011 2:58:02 PM</ex></ph> was not uploaded
diff --git a/chromium/components/cronet/android/BUILD.gn b/chromium/components/cronet/android/BUILD.gn
index 81a70dfc148..b399a563ed7 100644
--- a/chromium/components/cronet/android/BUILD.gn
+++ b/chromium/components/cronet/android/BUILD.gn
@@ -8,6 +8,7 @@ import("//build/config/android/rules.gni")
import("//build/util/process_version.gni")
import("//build/util/version.gni")
import("//testing/test.gni")
+import("//third_party/netty4/netty4.gni")
import("//third_party/protobuf/proto_library.gni")
import("//url/features.gni")
@@ -80,11 +81,16 @@ _generated_api_version_java =
"$_generated_api_version_java_dir/org/chromium/net/ApiVersion.java"
process_version("cronet_api_version_java") {
+ _api_level = read_file("api_version.txt", "value")
template_file = "api/src/org/chromium/net/ApiVersion.template"
sources = [
"//build/util/LASTCHANGE",
"//chrome/VERSION",
]
+ extra_args = [
+ "-e",
+ "API_LEVEL=$_api_level",
+ ]
output = _generated_api_version_java
}
@@ -105,11 +111,16 @@ _generated_impl_version_java =
"$_generated_impl_version_java_dir/org/chromium/net/impl/ImplVersion.java"
process_version("cronet_impl_version_java") {
+ _api_level = read_file("api_version.txt", "value")
template_file = "java/src/org/chromium/net/impl/ImplVersion.template"
sources = [
"//build/util/LASTCHANGE",
"//chrome/VERSION",
]
+ extra_args = [
+ "-e",
+ "API_LEVEL=$_api_level",
+ ]
output = _generated_impl_version_java
}
@@ -270,13 +281,15 @@ android_library("cronet_api_java") {
java_files = [
"api/src/org/chromium/net/BidirectionalStream.java",
"api/src/org/chromium/net/CronetEngine.java",
+ "api/src/org/chromium/net/CallbackException.java",
"api/src/org/chromium/net/CronetException.java",
- "api/src/org/chromium/net/ICronetEngineBuilder.java",
- "api/src/org/chromium/net/ImplLoader.java",
+ "api/src/org/chromium/net/CronetProvider.java",
"api/src/org/chromium/net/ExperimentalBidirectionalStream.java",
"api/src/org/chromium/net/ExperimentalCronetEngine.java",
"api/src/org/chromium/net/ExperimentalUrlRequest.java",
+ "api/src/org/chromium/net/ICronetEngineBuilder.java",
"api/src/org/chromium/net/InlineExecutionProhibitedException.java",
+ "api/src/org/chromium/net/NetworkException.java",
"api/src/org/chromium/net/NetworkQualityRttListener.java",
"api/src/org/chromium/net/NetworkQualityThroughputListener.java",
"api/src/org/chromium/net/QuicException.java",
@@ -285,7 +298,6 @@ android_library("cronet_api_java") {
"api/src/org/chromium/net/UploadDataProviders.java",
"api/src/org/chromium/net/UploadDataSink.java",
"api/src/org/chromium/net/UrlRequest.java",
- "api/src/org/chromium/net/UrlRequestException.java",
"api/src/org/chromium/net/UrlResponseInfo.java",
]
@@ -310,14 +322,19 @@ cronet_impl_common_java_srcjar_deps = [
# by all Cronet engine implementations.
android_library("cronet_impl_common_java") {
java_files = [
+ "java/src/org/chromium/net/impl/CallbackExceptionImpl.java",
+ "java/src/org/chromium/net/impl/CronetExceptionImpl.java",
"java/src/org/chromium/net/impl/CronetEngineBase.java",
"java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java",
+ "java/src/org/chromium/net/impl/NetworkExceptionImpl.java",
"java/src/org/chromium/net/impl/Preconditions.java",
+ "java/src/org/chromium/net/impl/QuicExceptionImpl.java",
"java/src/org/chromium/net/impl/RequestFinishedInfoImpl.java",
"java/src/org/chromium/net/impl/UrlRequestBase.java",
"java/src/org/chromium/net/impl/UrlRequestBuilderImpl.java",
"java/src/org/chromium/net/impl/UrlResponseInfoImpl.java",
"java/src/org/chromium/net/impl/UserAgent.java",
+ "java/src/org/chromium/net/impl/VersionSafeCallbacks.java",
]
deps = [
@@ -333,12 +350,15 @@ android_library("cronet_impl_platform_java") {
java_files = [
"java/src/org/chromium/net/impl/InputStreamChannel.java",
"java/src/org/chromium/net/impl/JavaCronetEngine.java",
+ "java/src/org/chromium/net/impl/JavaCronetEngineBuilderImpl.java",
+ "java/src/org/chromium/net/impl/JavaCronetProvider.java",
"java/src/org/chromium/net/impl/JavaUrlRequest.java",
]
deps = [
":cronet_api_java",
":cronet_impl_common_java",
+ "//third_party/jsr-305:jsr_305_javalib",
]
}
@@ -358,6 +378,8 @@ android_library("cronet_impl_native_java") {
"java/src/org/chromium/net/impl/CronetUploadDataStream.java",
"java/src/org/chromium/net/impl/CronetUrlRequest.java",
"java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
+ "java/src/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java",
+ "java/src/org/chromium/net/impl/NativeCronetProvider.java",
"java/src/org/chromium/net/urlconnection/CronetBufferedOutputStream.java",
"java/src/org/chromium/net/urlconnection/CronetChunkedOutputStream.java",
"java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java",
@@ -421,6 +443,7 @@ android_apk("cronet_sample_apk") {
shared_libraries = [ ":cronet" ]
deps = [
+ ":cronet_combine_proguard_flags",
":cronet_sample_apk_java",
":cronet_sample_apk_resources",
"//base:base_java",
@@ -428,15 +451,13 @@ android_apk("cronet_sample_apk") {
]
run_findbugs_override = true
- if (!is_java_debug) {
- proguard_enabled = true
- proguard_configs = [
- "proguard.cfg",
- "sample/javatests/proguard.cfg",
- "//base/android/proguard/chromium_apk.flags",
- "//base/android/proguard/chromium_code.flags",
- ]
- }
+ proguard_enabled = true
+ proguard_configs = [
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
+ "cronet_impl_common_proguard.cfg",
+ "sample/javatests/proguard.cfg",
+ "//base/android/proguard/chromium_apk.flags",
+ ]
}
# cronet_sample_test_apk_resources is identical to
@@ -465,11 +486,12 @@ instrumentation_test_apk("cronet_sample_test_apk") {
"//base:base_java",
"//base:base_java_test_support",
"//net/android:net_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
]
additional_apks = [ "//net/android:net_test_support_apk" ]
run_findbugs_override = true
- proguard_enabled = !is_java_debug
+ proguard_enabled = true
}
generate_jni("cronet_tests_jni_headers") {
@@ -480,7 +502,6 @@ generate_jni("cronet_tests_jni_headers") {
"test/src/org/chromium/net/MockCertVerifier.java",
"test/src/org/chromium/net/MockUrlRequestJobFactory.java",
"test/src/org/chromium/net/NativeTestServer.java",
- "test/src/org/chromium/net/NetworkChangeNotifierUtil.java",
"test/src/org/chromium/net/QuicTestServer.java",
"test/src/org/chromium/net/SdchObserver.java",
"test/src/org/chromium/net/TestUploadDataStreamHandler.java",
@@ -491,6 +512,12 @@ generate_jni("cronet_tests_jni_headers") {
shared_library("cronet_tests") {
testonly = true
sources = [
+ # While "cronet_tests" cannot depend on "cronet_static", and hence cannot
+ # call any Cronet functions, it can access fields of Cronet objects, so add
+ # Cronet header files to facilitate accessing these fields.
+ "//components/cronet/android/cronet_url_request_adapter.h",
+ "//components/cronet/android/cronet_url_request_context_adapter.h",
+ "//components/cronet/url_request_context_config.h",
"test/cronet_test_jni.cc",
"test/cronet_test_util.cc",
"test/cronet_test_util.h",
@@ -502,8 +529,6 @@ shared_library("cronet_tests") {
"test/mock_url_request_job_factory.h",
"test/native_test_server.cc",
"test/native_test_server.h",
- "test/network_change_notifier_util.cc",
- "test/network_change_notifier_util.h",
"test/quic_test_server.cc",
"test/quic_test_server.h",
"test/sdch_test_util.cc",
@@ -513,12 +538,12 @@ shared_library("cronet_tests") {
]
deps = [
- ":cronet_static",
":cronet_tests_jni_headers",
":cronet_version_header",
"//base",
"//base:i18n",
"//base/test:test_support",
+ "//components/prefs",
"//net",
"//net:simple_quic_tools",
"//net:test_support",
@@ -542,7 +567,10 @@ shared_library("cronet_tests") {
android_resources("cronet_test_apk_resources") {
testonly = true
- resource_dirs = [ "test/res" ]
+ resource_dirs = [
+ "test/res",
+ "test/smoketests/res/native",
+ ]
android_manifest = "test/AndroidManifest.xml"
}
@@ -558,7 +586,6 @@ android_library("cronet_test_apk_java") {
"test/src/org/chromium/net/MockCertVerifier.java",
"test/src/org/chromium/net/MockUrlRequestJobFactory.java",
"test/src/org/chromium/net/NativeTestServer.java",
- "test/src/org/chromium/net/NetworkChangeNotifierUtil.java",
"test/src/org/chromium/net/QuicTestServer.java",
"test/src/org/chromium/net/SdchObserver.java",
"test/src/org/chromium/net/TestFilesInstaller.java",
@@ -578,6 +605,35 @@ android_library("cronet_test_apk_java") {
run_findbugs_override = true
}
+cronet_smoketests_platform_only_common_srcs = [
+ "test/smoketests/src/org/chromium/net/smoke/ChromiumPlatformOnlyTestSupport.java",
+ "test/smoketests/src/org/chromium/net/smoke/CronetSmokeTestCase.java",
+ "test/smoketests/src/org/chromium/net/smoke/HttpTestServer.java",
+ "test/smoketests/src/org/chromium/net/smoke/SmokeTestRequestCallback.java",
+ "test/smoketests/src/org/chromium/net/smoke/TestSupport.java",
+]
+
+cronet_smoketests_native_common_srcs = cronet_smoketests_platform_only_common_srcs + [
+ "test/smoketests/src/org/chromium/net/smoke/ChromiumNativeTestSupport.java",
+ "test/smoketests/src/org/chromium/net/smoke/NativeCronetTestCase.java",
+ ]
+
+android_library("cronet_smoketests_native_java") {
+ testonly = true
+ java_files = [
+ "test/smoketests/src/org/chromium/net/smoke/Http2Test.java",
+ "test/smoketests/src/org/chromium/net/smoke/QuicTest.java",
+ ] + cronet_smoketests_native_common_srcs
+
+ deps = [
+ ":cronet_api_java",
+ ":cronet_test_apk_java",
+ "//base:base_java",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/netty4:netty_all_java",
+ ]
+}
+
android_assets("cronet_test_apk_assets") {
testonly = true
@@ -622,17 +678,28 @@ android_apk("cronet_test_apk") {
testonly = true
apk_name = "CronetTest"
android_manifest = "test/AndroidManifest.xml"
- shared_libraries = [ ":cronet_tests" ]
+ shared_libraries = [
+ ":cronet",
+ ":cronet_tests",
+ ]
loadable_modules = [ "$root_out_dir/libnetty-tcnative.so" ]
deps = [
+ ":cronet_combine_proguard_flags",
":cronet_test_apk_assets",
- ":cronet_test_apk_java",
":cronet_test_apk_resources",
"//base:base_java",
"//third_party/netty-tcnative:netty-tcnative-so",
]
+ proguard_enabled = true
+
+ proguard_configs = [
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
+ "cronet_impl_common_proguard.cfg",
+ "cronet_impl_platform_proguard.cfg",
+ ]
+
run_findbugs_override = true
}
@@ -643,6 +710,7 @@ android_library("cronet_javatests") {
"test/javatests/src/org/chromium/net/BidirectionalStreamQuicTest.java",
"test/javatests/src/org/chromium/net/BidirectionalStreamTest.java",
"test/javatests/src/org/chromium/net/Criteria.java",
+ "test/javatests/src/org/chromium/net/CronetEngineBuilderTest.java",
"test/javatests/src/org/chromium/net/CronetTestBase.java",
"test/javatests/src/org/chromium/net/CronetUploadTest.java",
"test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java",
@@ -682,6 +750,7 @@ android_library("cronet_javatests") {
"//base:base_java_test_support",
"//net/android:net_java",
"//net/android:net_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
]
run_findbugs_override = true
@@ -696,11 +765,13 @@ instrumentation_test_apk("cronet_test_instrumentation_apk") {
":cronet_api_java",
":cronet_impl_all_java",
":cronet_javatests",
+ ":cronet_smoketests_native_java",
":cronet_test_apk_java",
"//base:base_java",
"//base:base_java_test_support",
"//net/android:net_java",
"//net/android:net_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
]
additional_apks = [ "//net/android:net_test_support_apk" ]
@@ -708,6 +779,113 @@ instrumentation_test_apk("cronet_test_instrumentation_apk") {
"//net:test_support",
]
+ proguard_enabled = true
+
+ proguard_configs = [ "test/proguard.cfg" ]
+
+ run_findbugs_override = true
+}
+
+android_resources("cronet_smoketests_platform_only_apk_resources") {
+ testonly = true
+ resource_dirs = [ "test/smoketests/res/platform_only" ]
+ android_manifest = "test/AndroidManifest.xml"
+}
+
+android_library("cronet_smoketests_platform_only_java") {
+ testonly = true
+ java_files = [ "test/smoketests/src/org/chromium/net/smoke/PlatformOnlyEngineTest.java" ] + cronet_smoketests_platform_only_common_srcs
+ deps = [
+ ":cronet_api_java",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/netty4:netty_all_java",
+ ]
+}
+
+android_apk("cronet_smoketests_platform_only_apk") {
+ testonly = true
+ apk_name = "PlatformOnlyEngineSmokeTest"
+ android_manifest = "test/AndroidManifest.xml"
+ java_files = [ "test/src/org/chromium/net/CronetTestApplication.java" ]
+
+ proguard_enabled = true
+ proguard_configs = [
+ "cronet_impl_common_proguard.cfg",
+ "cronet_impl_platform_proguard.cfg",
+ ]
+
+ deps = [
+ ":cronet_api_java",
+ ":cronet_impl_common_java",
+ ":cronet_impl_platform_java",
+ ":cronet_smoketests_platform_only_apk_resources",
+ ]
+ run_findbugs_override = true
+}
+
+instrumentation_test_apk(
+ "cronet_smoketests_platform_only_instrumentation_apk") {
+ apk_name = "PlatformOnlyEngineSmokeTestInstrumentation"
+ apk_under_test = ":cronet_smoketests_platform_only_apk"
+ android_manifest = "test/javatests/AndroidManifest.xml"
+ deps = [
+ ":cronet_smoketests_platform_only_java",
+ ]
+
+ proguard_enabled = true
+
+ proguard_configs = [ "test/proguard.cfg" ]
+ run_findbugs_override = true
+}
+
+android_library("cronet_smoketests_missing_native_library_java") {
+ testonly = true
+ java_files = [ "test/smoketests/src/org/chromium/net/smoke/MissingNativeLibraryTest.java" ] + cronet_smoketests_native_common_srcs
+ deps = [
+ ":cronet_api_java",
+ ":cronet_test_apk_java",
+ "//base:base_java",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/netty4:netty_all_java",
+ ]
+}
+
+android_apk("cronet_smoketests_missing_native_library_apk") {
+ testonly = true
+ apk_name = "MissingNativeLibrarySmokeTest"
+ android_manifest = "test/AndroidManifest.xml"
+ deps = [
+ ":cronet_api_java",
+ ":cronet_combine_proguard_flags",
+ ":cronet_impl_common_java",
+ ":cronet_impl_platform_java",
+ ":cronet_test_apk_resources",
+ ]
+
+ proguard_enabled = true
+ proguard_configs = [
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
+ "cronet_impl_common_proguard.cfg",
+ "cronet_impl_platform_proguard.cfg",
+ ]
+
+ run_findbugs_override = true
+}
+
+instrumentation_test_apk(
+ "cronet_smoketests_missing_native_library_instrumentation_apk") {
+ apk_name = "MissingNativeLibrarySmokeTestInstrumentation"
+ apk_under_test = ":cronet_smoketests_missing_native_library_apk"
+ android_manifest = "test/javatests/AndroidManifest.xml"
+
+ deps = [
+ ":cronet_smoketests_missing_native_library_java",
+ ]
+
+ proguard_enabled = true
+
+ proguard_configs = [ "test/proguard.cfg" ]
+
run_findbugs_override = true
}
@@ -731,9 +909,13 @@ android_apk("cronet_perf_test_apk") {
testonly = true
apk_name = "CronetPerfTest"
android_manifest = "test/javaperftests/AndroidManifest.xml"
- shared_libraries = [ ":cronet_tests" ]
+ shared_libraries = [
+ ":cronet",
+ ":cronet_tests",
+ ]
deps = [
+ ":cronet_combine_proguard_flags",
":cronet_perf_test_apk_java",
":cronet_test_apk_java",
"//base:base_java",
@@ -742,10 +924,10 @@ android_apk("cronet_perf_test_apk") {
run_findbugs_override = true
proguard_enabled = true
proguard_configs = [
- "proguard.cfg",
- "test/javaperftests/proguard.cfg",
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
+ "cronet_impl_common_proguard.cfg",
+ "test/proguard.cfg",
"//base/android/proguard/chromium_apk.flags",
- "//base/android/proguard/chromium_code.flags",
]
}
@@ -775,7 +957,9 @@ test("cronet_unittests") {
}
_package_dir = "$root_out_dir/cronet"
+_test_package_dir = "$root_out_dir/cronet/test"
_extract_cronet_jars_dir = "$target_gen_dir/cronet_jar_extract"
+_extract_cronet_test_jars_dir = "$target_gen_dir/cronet_test_jar_extract"
action("extract_cronet_jars") {
# extract_from_jars.py deletes the target directory before extracting.
@@ -834,6 +1018,75 @@ action("repackage_extracted_jars") {
]
}
+action("extract_cronet_test_jars") {
+ # extract_from_jars.py deletes the target directory before extracting.
+ script = "//components/cronet/tools/extract_from_jars.py"
+ depfile = "$target_gen_dir/$target_name.d"
+ testonly = true
+
+ sources = [
+ "$root_out_dir/lib.java/base/base_java.jar",
+ "$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/net_java.jar",
+ "$root_out_dir/lib.java/net/android/net_java_test_support.jar",
+ "$root_out_dir/lib.java/third_party/netty-tcnative/netty-tcnative_java.jar",
+ "$root_out_dir/lib.java/url/url_java.jar",
+ NETTY4_JAR_FILE,
+ ]
+
+ _stamp_file = "$target_gen_dir/$target_name.stamp"
+ outputs = [
+ _stamp_file,
+ ]
+
+ _rebased_sources = rebase_path(sources, root_build_dir)
+
+ args = [
+ "--classes-dir",
+ rebase_path(_extract_cronet_test_jars_dir, root_build_dir),
+ "--jars=${_rebased_sources}",
+ "--depfile",
+ rebase_path(depfile, root_build_dir),
+ "--stamp",
+ rebase_path(_stamp_file, root_build_dir),
+ ]
+
+ deps = [
+ ":cronet_javatests",
+ ":cronet_test_apk_java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//net/android:net_java",
+ "//net/android:net_java_test_support",
+ "//third_party/netty-tcnative:netty-tcnative_java",
+ "//third_party/netty4:netty_all_java",
+ "//url:url_java",
+ ]
+}
+
+action("repackage_extracted_test_jars") {
+ _output_jar = "$_test_package_dir/cronet_tests_java.jar"
+ testonly = true
+
+ script = "//build/android/gyp/jar.py"
+ outputs = [
+ _output_jar,
+ ]
+
+ args = [
+ "--classes-dir",
+ rebase_path(_extract_cronet_test_jars_dir, root_build_dir),
+ "--jar-path",
+ rebase_path(_output_jar, root_build_dir),
+ ]
+
+ deps = [
+ ":extract_cronet_test_jars",
+ ]
+}
+
template("jar_src") {
action(target_name) {
_rebased_src_search_dirs =
@@ -1012,8 +1265,11 @@ copy("cronet_package_copy") {
"$root_out_dir/lib.java/components/cronet/android/cronet_api.jar",
"$root_out_dir/lib.java/components/cronet/android/cronet_impl_common_java.jar",
"$root_out_dir/lib.java/components/cronet/android/cronet_impl_platform_java.jar",
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
"//AUTHORS",
"//chrome/VERSION",
+ "cronet_impl_common_proguard.cfg",
+ "cronet_impl_platform_proguard.cfg",
]
outputs = [
"$_package_dir/{{source_file_part}}",
@@ -1021,6 +1277,7 @@ copy("cronet_package_copy") {
deps = [
":cronet_api_java",
+ ":cronet_combine_proguard_flags",
":cronet_impl_common_java",
":cronet_impl_platform_java",
]
@@ -1029,14 +1286,15 @@ copy("cronet_package_copy") {
action("cronet_combine_proguard_flags") {
script = "//components/cronet/tools/generate_proguard_file.py"
outputs = [
- "$_package_dir/proguard.cfg",
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
]
-
args = [
"--output-file",
- rebase_path("$_package_dir/proguard.cfg", root_build_dir),
+ rebase_path("$target_gen_dir/cronet_impl_native_proguard.cfg",
+ root_build_dir),
+ rebase_path("//components/cronet/android/cronet_impl_native_proguard.cfg",
+ root_build_dir),
rebase_path("//base/android/proguard/chromium_code.flags", root_build_dir),
- rebase_path("//components/cronet/android/proguard.cfg", root_build_dir),
]
}
@@ -1064,6 +1322,78 @@ copy("cronet_package_copy_native_lib_unstripped") {
]
}
+copy("cronet_package_copy_native_test_lib") {
+ testonly = true
+ sources = [
+ "$root_out_dir/libcronet_tests.so",
+ "$root_out_dir/libnetty-tcnative.so",
+ ]
+ outputs = [
+ "$_test_package_dir/libs/${android_app_abi}/{{source_file_part}}",
+ ]
+ deps = [
+ ":cronet_tests",
+ "//third_party/netty-tcnative:netty-tcnative-so",
+ ]
+}
+
+copy("cronet_package_copy_native_test_lib_unstripped") {
+ testonly = true
+ sources = [
+ "$root_out_dir/lib.unstripped/libcronet_tests.so",
+ "$root_out_dir/lib.unstripped/libnetty-tcnative.so",
+ ]
+ outputs = [
+ "$_test_package_dir/symbols/${android_app_abi}/{{source_file_part}}",
+ ]
+ deps = [
+ ":cronet_tests",
+ "//third_party/netty-tcnative:netty-tcnative-so",
+ ]
+}
+
+copy("cronet_package_copy_test_assets") {
+ testonly = true
+ sources = [
+ "test/assets",
+ ]
+ outputs = [
+ "$_test_package_dir/assets",
+ ]
+}
+
+copy("cronet_package_copy_test_support_apks") {
+ testonly = true
+ sources = [
+ # Provides EmbeddedTestServer.
+ "$root_out_dir/apks/ChromiumNetTestSupport.apk",
+ ]
+ outputs = [
+ "$_test_package_dir/apks/${android_app_abi}/{{source_file_part}}",
+ ]
+ deps = [
+ "//net/android:net_test_support_apk",
+ ]
+}
+
+copy("cronet_package_copy_test_files") {
+ testonly = true
+ sources = [
+ "//net/data/ssl/certificates/quic_test.example.com.crt",
+ "//net/data/ssl/certificates/quic_test.example.com.key",
+ "//net/data/ssl/certificates/quic_test.example.com.key.pkcs8",
+ "//net/data/ssl/certificates/quic_test.example.com.key.sct",
+ ]
+ outputs = [
+ "$_test_package_dir/assets/test_files/net/data/ssl/certificates/{{source_file_part}}",
+ ]
+ deps = [
+ # Not really dependent, but builds can fail if these two targets attempt
+ # to create the "assets" subdirectory simultaneously.
+ ":cronet_package_copy_test_assets",
+ ]
+}
+
# Enforce that ARM Neon is not used when building for ARMv7
if (target_cpu == "arm" && arm_version == 7 && !arm_use_neon) {
action("enforce_no_neon") {
@@ -1088,15 +1418,62 @@ if (target_cpu == "arm" && arm_version == 7 && !arm_use_neon) {
}
}
+# Enforce restrictions for API<->impl boundary.
+action("api_static_checks") {
+ script = "//components/cronet/tools/api_static_checks.py"
+ outputs = [
+ "$target_gen_dir/$target_name.stamp",
+ ]
+ args = [
+ "--api_jar",
+ rebase_path(
+ "$root_out_dir/lib.java/components/cronet/android/cronet_api.jar",
+ root_build_dir),
+ "--impl_jar",
+ rebase_path(
+ "$root_out_dir/lib.java/components/cronet/android/cronet_impl_common_java.jar",
+ root_build_dir),
+ "--impl_jar",
+ rebase_path(
+ "$root_out_dir/lib.java/components/cronet/android/cronet_impl_platform_java.jar",
+ root_build_dir),
+ "--impl_jar",
+ rebase_path(
+ "$root_out_dir/lib.java/components/cronet/android/cronet_impl_native_java.jar",
+ root_build_dir),
+ "--stamp",
+ rebase_path(outputs[0], root_build_dir),
+ ]
+ deps = [
+ ":cronet_api_java",
+ ":cronet_impl_common_java",
+ ":cronet_impl_native_java",
+ ":cronet_impl_platform_java",
+ ]
+ inputs = [
+ "//components/cronet/tools/update_api.py",
+ ]
+ sources = [
+ "//components/cronet/android/api.txt",
+ "//components/cronet/android/api_version.txt",
+ ]
+}
+
group("cronet_package") {
+ # Marked as testonly as it contains test-only targets too.
+ testonly = true
+
+ # Enforce building with ICU alternatives, crbug.com/611621.
# Enforce that arm_use_neon==false when building for ARMv7 by
# not including any deps in cronet_package target otherwise.
- if (!(target_cpu == "arm" && arm_version == 7) || !arm_use_neon) {
+ if (use_platform_icu_alternatives &&
+ (!(target_cpu == "arm" && arm_version == 7) || !arm_use_neon)) {
deps = [
- ":cronet_combine_proguard_flags",
+ ":api_static_checks",
":cronet_package_copy",
":cronet_package_copy_native_lib",
":cronet_package_copy_native_lib_unstripped",
+ ":cronet_test_package",
":generate_javadoc",
":generate_licenses",
":jar_cronet_api_source",
@@ -1111,3 +1488,19 @@ group("cronet_package") {
}
}
}
+
+group("cronet_test_package") {
+ testonly = true
+
+ # Don't build for MIPS where tests aren't run.
+ if (current_cpu != "mipsel" && current_cpu != "mips64el") {
+ deps = [
+ ":cronet_package_copy_native_test_lib",
+ ":cronet_package_copy_native_test_lib_unstripped",
+ ":cronet_package_copy_test_assets",
+ ":cronet_package_copy_test_files",
+ ":cronet_package_copy_test_support_apks",
+ ":repackage_extracted_test_jars",
+ ]
+ }
+}
diff --git a/chromium/components/cronet/ios/BUILD.gn b/chromium/components/cronet/ios/BUILD.gn
index 4de541fb921..a449ceba8af 100644
--- a/chromium/components/cronet/ios/BUILD.gn
+++ b/chromium/components/cronet/ios/BUILD.gn
@@ -53,8 +53,9 @@ source_set("cronet_sources") {
"../url_request_context_config.h",
"Cronet.h",
"Cronet.mm",
- "cronet_environment.cc",
+ "cronet_c_for_grpc.h",
"cronet_environment.h",
+ "cronet_environment.mm",
]
include_dirs = [ "//components/grpc_support/include" ]
@@ -153,7 +154,10 @@ ios_framework_bundle("cronet_framework") {
"//components/grpc_support",
]
- public_headers = [ "Cronet.h" ]
+ public_headers = [
+ "Cronet.h",
+ "cronet_c_for_grpc.h",
+ ]
public_headers += grpc_public_headers
sources = [
diff --git a/chromium/components/cronet/ios/test/BUILD.gn b/chromium/components/cronet/ios/test/BUILD.gn
index 8bab274084c..db793b56b22 100644
--- a/chromium/components/cronet/ios/test/BUILD.gn
+++ b/chromium/components/cronet/ios/test/BUILD.gn
@@ -9,6 +9,7 @@ test("cronet_test") {
testonly = true
sources = [
"cronet_http_test.mm",
+ "cronet_netlog_test.mm",
"cronet_test_runner.mm",
"get_stream_engine.mm",
"start_cronet.h",
diff --git a/chromium/components/proximity_auth/cryptauth/BUILD.gn b/chromium/components/cryptauth/BUILD.gn
index 6791187ea8b..e8e1479896e 100644
--- a/chromium/components/proximity_auth/cryptauth/BUILD.gn
+++ b/chromium/components/cryptauth/BUILD.gn
@@ -2,8 +2,17 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+# CryptAuth is a component which manages data about devices associated with a
+# user's account. This component both sends data about the current device and
+# requets data about associated devices.
+
static_library("cryptauth") {
sources = [
+ "bluetooth_throttler.h",
+ "bluetooth_throttler_impl.cc",
+ "bluetooth_throttler_impl.h",
+ "connection.cc",
+ "connection.h",
"cryptauth_access_token_fetcher.h",
"cryptauth_access_token_fetcher_impl.cc",
"cryptauth_access_token_fetcher_impl.h",
@@ -25,8 +34,14 @@ static_library("cryptauth") {
"cryptauth_gcm_manager.h",
"cryptauth_gcm_manager_impl.cc",
"cryptauth_gcm_manager_impl.h",
+ "eid_generator.cc",
+ "eid_generator.h",
"pref_names.cc",
"pref_names.h",
+ "remote_beacon_seed_fetcher.cc",
+ "remote_beacon_seed_fetcher.h",
+ "remote_device.cc",
+ "remote_device.h",
"secure_message_delegate.cc",
"secure_message_delegate.h",
"switches.cc",
@@ -35,10 +50,13 @@ static_library("cryptauth") {
"sync_scheduler.h",
"sync_scheduler_impl.cc",
"sync_scheduler_impl.h",
+ "wire_message.cc",
+ "wire_message.h",
]
deps = [
"//base",
+ "//components/cryptauth/ble",
"//components/gcm_driver",
"//components/gcm_driver/common",
"//components/prefs",
@@ -49,27 +67,40 @@ static_library("cryptauth") {
]
public_deps = [
- "//components/proximity_auth/cryptauth/proto",
+ "//components/cryptauth/proto",
]
+
+ # TODO (hansberry): Resolve this.
+ allow_circular_includes_from = [ "//components/cryptauth/ble" ]
}
static_library("test_support") {
testonly = true
sources = [
+ "cryptauth_test_util.cc",
+ "cryptauth_test_util.h",
+ "fake_connection.cc",
+ "fake_connection.h",
"fake_cryptauth_gcm_manager.cc",
"fake_cryptauth_gcm_manager.h",
"fake_secure_message_delegate.cc",
"fake_secure_message_delegate.h",
"mock_cryptauth_client.cc",
"mock_cryptauth_client.h",
+ "mock_eid_generator.cc",
+ "mock_eid_generator.h",
+ "mock_remote_beacon_seed_fetcher.cc",
+ "mock_remote_beacon_seed_fetcher.h",
"mock_sync_scheduler.cc",
"mock_sync_scheduler.h",
+ "remote_device_test_util.cc",
+ "remote_device_test_util.h",
]
public_deps = [
":cryptauth",
- "//components/proximity_auth/cryptauth/proto",
+ "//components/cryptauth/proto",
]
deps = [
@@ -81,6 +112,8 @@ static_library("test_support") {
source_set("unit_tests") {
testonly = true
sources = [
+ "bluetooth_throttler_impl_unittest.cc",
+ "connection_unittest.cc",
"cryptauth_access_token_fetcher_impl_unittest.cc",
"cryptauth_api_call_flow_unittest.cc",
"cryptauth_client_impl_unittest.cc",
@@ -88,17 +121,20 @@ source_set("unit_tests") {
"cryptauth_enroller_impl_unittest.cc",
"cryptauth_enrollment_manager_unittest.cc",
"cryptauth_gcm_manager_impl_unittest.cc",
+ "eid_generator_unittest.cc",
"fake_secure_message_delegate_unittest.cc",
+ "remote_beacon_seed_fetcher_unittest.cc",
"sync_scheduler_impl_unittest.cc",
+ "wire_message_unittest.cc",
]
deps = [
":cryptauth",
":test_support",
"//base/test:test_support",
+ "//components/cryptauth/ble:unit_tests",
"//components/gcm_driver:test_support",
"//components/prefs:test_support",
- "//components/proximity_auth",
"//google_apis:test_support",
"//net:test_support",
"//testing/gtest",
diff --git a/chromium/components/cryptauth/DEPS b/chromium/components/cryptauth/DEPS
new file mode 100644
index 00000000000..af0b1cb4cea
--- /dev/null
+++ b/chromium/components/cryptauth/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+ "+components/gcm_driver",
+ "+components/prefs",
+ "+components/proximity_auth/logging",
+ "+crypto",
+ "+device/bluetooth",
+ "+google_apis",
+ "+net",
+]
diff --git a/chromium/components/cryptauth/OWNERS b/chromium/components/cryptauth/OWNERS
new file mode 100644
index 00000000000..b330adf2413
--- /dev/null
+++ b/chromium/components/cryptauth/OWNERS
@@ -0,0 +1,5 @@
+tbarzic@chromium.org
+tengs@chromium.org
+xiyuan@chromium.org
+khorimoto@chromium.org
+hansberry@chromium.org
diff --git a/chromium/components/cryptauth/README b/chromium/components/cryptauth/README
new file mode 100644
index 00000000000..5a0fdf7aa62
--- /dev/null
+++ b/chromium/components/cryptauth/README
@@ -0,0 +1,3 @@
+Cryptauth provides cryptographic protocols involving user devices. In
+particular, it uses known device public keys to verify cryptographic
+assertions made by corresponding private keys. \ No newline at end of file
diff --git a/chromium/components/cryptauth/ble/BUILD.gn b/chromium/components/cryptauth/ble/BUILD.gn
new file mode 100644
index 00000000000..a1de6a07ebf
--- /dev/null
+++ b/chromium/components/cryptauth/ble/BUILD.gn
@@ -0,0 +1,65 @@
+# 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("//testing/test.gni")
+
+static_library("ble") {
+ sources = [
+ "bluetooth_low_energy_characteristics_finder.cc",
+ "bluetooth_low_energy_characteristics_finder.h",
+ "bluetooth_low_energy_weave_client_connection.cc",
+ "bluetooth_low_energy_weave_client_connection.h",
+ "bluetooth_low_energy_weave_defines.h",
+ "bluetooth_low_energy_weave_packet_generator.cc",
+ "bluetooth_low_energy_weave_packet_generator.h",
+ "bluetooth_low_energy_weave_packet_receiver.cc",
+ "bluetooth_low_energy_weave_packet_receiver.h",
+ "fake_wire_message.cc",
+ "fake_wire_message.h",
+ "remote_attribute.h",
+ ]
+
+ deps = [
+ "//base",
+
+ # TODO (hansberry): This component has a circular dependency
+ # with the root cryptauth target. It is whitelisted in that target for
+ # includes.
+ "//components/prefs",
+ "//components/proximity_auth/logging",
+ "//device/bluetooth",
+ "//net",
+ ]
+
+ public_deps = [
+ "//components/cryptauth/proto",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "bluetooth_low_energy_characteristics_finder_unittest.cc",
+ "bluetooth_low_energy_weave_client_connection_unittest.cc",
+ "bluetooth_low_energy_weave_packet_generator_unittest.cc",
+ "bluetooth_low_energy_weave_packet_receiver_unittest.cc",
+ ]
+
+ configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
+ deps = [
+ ":ble",
+ "//base/test:test_support",
+ "//components/cryptauth",
+ "//components/cryptauth:test_support",
+ "//components/prefs:test_support",
+ "//device/bluetooth:mocks",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+
+ public_deps = [
+ "//components/cryptauth/proto",
+ ]
+}
diff --git a/chromium/components/cryptauth/ble/OWNERS b/chromium/components/cryptauth/ble/OWNERS
new file mode 100644
index 00000000000..067b4517ba4
--- /dev/null
+++ b/chromium/components/cryptauth/ble/OWNERS
@@ -0,0 +1,5 @@
+msarda@chromium.org
+sacomoto@chromium.org
+tengs@chromium.org
+khorimoto@chromium.org
+hansberry@chromium.org
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.cc b/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.cc
new file mode 100644
index 00000000000..be036e45e61
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.cc
@@ -0,0 +1,142 @@
+// 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/ble/bluetooth_low_energy_characteristics_finder.h"
+
+#include "components/proximity_auth/logging/logging.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothDevice;
+using device::BluetoothRemoteGattCharacteristic;
+using device::BluetoothRemoteGattService;
+using device::BluetoothUUID;
+
+namespace cryptauth {
+
+BluetoothLowEnergyCharacteristicsFinder::
+ BluetoothLowEnergyCharacteristicsFinder(
+ scoped_refptr<BluetoothAdapter> adapter,
+ BluetoothDevice* device,
+ const RemoteAttribute& remote_service,
+ const RemoteAttribute& to_peripheral_char,
+ const RemoteAttribute& from_peripheral_char,
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback)
+ : adapter_(adapter),
+ remote_service_(remote_service),
+ to_peripheral_char_(to_peripheral_char),
+ from_peripheral_char_(from_peripheral_char),
+ success_callback_(success_callback),
+ error_callback_(error_callback) {
+ if (!adapter_) {
+ error_callback_.Run(to_peripheral_char_, from_peripheral_char_);
+ ResetCallbacks();
+ return;
+ }
+
+ adapter_->AddObserver(this);
+ ScanRemoteCharacteristics(device, remote_service_.uuid);
+
+ // TODO(sacomoto): implement a timeout for characteristic discovery.
+}
+
+BluetoothLowEnergyCharacteristicsFinder::
+ 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();
+ }
+ }
+ }
+}
+
+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;
+ }
+ }
+ }
+}
+
+void 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();
+ }
+}
+
+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();
+ }
+}
+
+void BluetoothLowEnergyCharacteristicsFinder::ResetCallbacks() {
+ success_callback_.Reset();
+ error_callback_.Reset();
+}
+
+} // 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
new file mode 100644
index 00000000000..8741765a2d8
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h
@@ -0,0 +1,119 @@
+// 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_CHARACTERISTICS_FINDER_H_
+#define COMPONENTS_CRYPTAUTH_BLE_BLUETOOTH_LOW_ENERGY_CHARACTERISTICS_FINDER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/cryptauth/ble/remote_attribute.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace cryptauth {
+
+// Looks for given characteristics in a remote device, for which a GATT
+// connection was already established. In the current BLE connection protocol
+// (device::BluetoothDevice::CreateGattConnection), remote characteristic
+// discovery starts immediatelly after a GATT connection was established. So,
+// this class simply adds an observer for a characteristic discovery event and
+// call |success_callback_| once all necessary characteristics were discovered.
+class BluetoothLowEnergyCharacteristicsFinder
+ : public device::BluetoothAdapter::Observer {
+ public:
+ // This callbacks takes as arguments (in this order): |remote_service_|,
+ // |to_peripheral_char_| and |from_peripheral_char_|. Note that, since this is
+ // called after the characteristics were discovered, their id field (e.g.
+ // to_peripheral_char_.id) will be non-blank.
+ typedef base::Callback<void(const RemoteAttribute&,
+ const RemoteAttribute&,
+ const RemoteAttribute&)>
+ SuccessCallback;
+
+ // This callback takes as arguments (in this order): |to_peripheral_char_| and
+ // |from_peripheral_char_|. A blank id field in the characteristics indicate
+ // that the characteristics was not found in the remote service.
+ // TODO(sacomoto): Remove RemoteAttributes and add an error message instead.
+ // The caller of this object should not care if only a subset of the
+ // characteristics was found. See crbug.com/495511.
+ typedef base::Callback<void(const RemoteAttribute&, const RemoteAttribute&)>
+ ErrorCallback;
+
+ // Constructs the object and registers itself as an observer for |adapter|,
+ // waiting for |to_peripheral_char| and |from_peripheral_char| to be found.
+ // When both characteristics were found |success_callback| is called. After
+ // all characteristics of |service| were discovered, if |from_periphral_char|
+ // or |to_peripheral| was not found, it calls |error_callback|. The object
+ // will perform at most one call of the callbacks.
+ BluetoothLowEnergyCharacteristicsFinder(
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ device::BluetoothDevice* device,
+ const RemoteAttribute& remote_service,
+ const RemoteAttribute& to_peripheral_char,
+ const RemoteAttribute& from_peripheral_char,
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback);
+
+ ~BluetoothLowEnergyCharacteristicsFinder() override;
+
+ protected:
+ // device::BluetoothAdapter::Observer:
+ void GattDiscoveryCompleteForService(
+ device::BluetoothAdapter* adapter,
+ device::BluetoothRemoteGattService* service) override;
+ void GattCharacteristicAdded(
+ device::BluetoothAdapter* adapter,
+ device::BluetoothRemoteGattCharacteristic* characteristic) override;
+
+ // For testing. Used to mock this class.
+ BluetoothLowEnergyCharacteristicsFinder();
+
+ private:
+ // Handles the discovery of a new characteristic.
+ void HandleCharacteristicUpdate(
+ device::BluetoothRemoteGattCharacteristic* characteristic);
+
+ // Scans the remote chracteristics of the service with |uuid| in |device|
+ // calling HandleCharacteristicUpdate() for each of them.
+ void ScanRemoteCharacteristics(device::BluetoothDevice* device,
+ const device::BluetoothUUID& uuid);
+
+ // Updates the value of |to_peripheral_char_| and
+ // |from_peripheral_char_|
+ // when |characteristic| was found.
+ 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_;
+
+ // Remote service the |connection_| was established with.
+ RemoteAttribute remote_service_;
+
+ // Characteristic used to receive data from the remote device.
+ RemoteAttribute to_peripheral_char_;
+
+ // Characteristic used to receive data from the remote device.
+ RemoteAttribute from_peripheral_char_;
+
+ // Called when all characteristics were found.
+ SuccessCallback success_callback_;
+
+ // Called when there is an error.
+ ErrorCallback error_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothLowEnergyCharacteristicsFinder);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_BLE_BLUETOOTH_CHARACTERISTICS_FINDER_H_
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder_unittest.cc b/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder_unittest.cc
new file mode 100644
index 00000000000..58ac22f58bf
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_characteristics_finder_unittest.cc
@@ -0,0 +1,295 @@
+// 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/ble/bluetooth_low_energy_characteristics_finder.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "components/cryptauth/ble/remote_attribute.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/bluetooth/test/mock_bluetooth_device.h"
+#include "device/bluetooth/test/mock_bluetooth_gatt_characteristic.h"
+#include "device/bluetooth/test/mock_bluetooth_gatt_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::AtLeast;
+using testing::NiceMock;
+using testing::Return;
+using testing::StrictMock;
+using testing::SaveArg;
+
+namespace cryptauth {
+namespace {
+
+const char kDeviceName[] = "Device name";
+const char kBluetoothAddress[] = "11:22:33:44:55:66";
+
+const char kServiceUUID[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEEF";
+const char kToPeripheralCharUUID[] = "FBAE09F2-0482-11E5-8418-1697F925EC7B";
+const char kFromPeripheralCharUUID[] = "5539ED10-0483-11E5-8418-1697F925EC7B";
+
+const char kToPeripheralCharID[] = "to peripheral id";
+const char kFromPeripheralCharID[] = "from peripheral id";
+
+const device::BluetoothRemoteGattCharacteristic::Properties
+ kCharacteristicProperties =
+ device::BluetoothRemoteGattCharacteristic::PROPERTY_BROADCAST |
+ device::BluetoothRemoteGattCharacteristic::PROPERTY_READ |
+ device::BluetoothRemoteGattCharacteristic::
+ PROPERTY_WRITE_WITHOUT_RESPONSE |
+ device::BluetoothRemoteGattCharacteristic::PROPERTY_INDICATE;
+
+const char kOtherCharUUID[] = "09731422-048A-11E5-8418-1697F925EC7B";
+const char kOtherCharID[] = "other id";
+} // namespace
+
+class CryptAuthBluetoothLowEnergyCharacteristicFinderTest
+ : public testing::Test {
+ protected:
+ CryptAuthBluetoothLowEnergyCharacteristicFinderTest()
+ : adapter_(new NiceMock<device::MockBluetoothAdapter>),
+ success_callback_(base::Bind(
+ &CryptAuthBluetoothLowEnergyCharacteristicFinderTest::
+ OnCharacteristicsFound,
+ base::Unretained(this))),
+ error_callback_(base::Bind(
+ &CryptAuthBluetoothLowEnergyCharacteristicFinderTest::
+ OnCharacteristicsFinderError,
+ base::Unretained(this))),
+ device_(new NiceMock<device::MockBluetoothDevice>(adapter_.get(),
+ 0,
+ kDeviceName,
+ kBluetoothAddress,
+ false,
+ false)),
+ service_(new NiceMock<device::MockBluetoothGattService>(
+ device_.get(),
+ "",
+ device::BluetoothUUID(kServiceUUID),
+ true,
+ false)),
+ remote_service_({device::BluetoothUUID(kServiceUUID), ""}),
+ to_peripheral_char_({device::BluetoothUUID(kToPeripheralCharUUID), ""}),
+ from_peripheral_char_(
+ {device::BluetoothUUID(kFromPeripheralCharUUID), ""}) {
+ device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
+
+ // The default behavior for |device_| is to have no services discovered. Can
+ // be overrided later.
+ ON_CALL(*device_, GetGattServices())
+ .WillByDefault(
+ Return(std::vector<device::BluetoothRemoteGattService*>()));
+ }
+
+ void SetUp() {
+ EXPECT_CALL(*adapter_, AddObserver(_));
+ EXPECT_CALL(*adapter_, RemoveObserver(_));
+ }
+
+ MOCK_METHOD3(OnCharacteristicsFound,
+ void(const RemoteAttribute&,
+ const RemoteAttribute&,
+ const RemoteAttribute&));
+ MOCK_METHOD2(OnCharacteristicsFinderError,
+ void(const RemoteAttribute&, const RemoteAttribute&));
+
+ std::unique_ptr<device::MockBluetoothGattCharacteristic>
+ ExpectToFindCharacteristic(const device::BluetoothUUID& uuid,
+ const std::string& id,
+ bool valid) {
+ std::unique_ptr<device::MockBluetoothGattCharacteristic> characteristic(
+ new NiceMock<device::MockBluetoothGattCharacteristic>(
+ service_.get(), id, uuid, true, kCharacteristicProperties,
+ device::BluetoothRemoteGattCharacteristic::PERMISSION_NONE));
+
+ ON_CALL(*characteristic.get(), GetUUID()).WillByDefault(Return(uuid));
+ if (valid)
+ ON_CALL(*characteristic.get(), GetIdentifier()).WillByDefault(Return(id));
+ ON_CALL(*characteristic.get(), GetService())
+ .WillByDefault(Return(service_.get()));
+ return characteristic;
+ }
+
+ scoped_refptr<device::MockBluetoothAdapter> adapter_;
+ BluetoothLowEnergyCharacteristicsFinder::SuccessCallback success_callback_;
+ BluetoothLowEnergyCharacteristicsFinder::ErrorCallback error_callback_;
+ std::unique_ptr<device::MockBluetoothDevice> device_;
+ std::unique_ptr<device::MockBluetoothGattService> service_;
+ RemoteAttribute remote_service_;
+ RemoteAttribute to_peripheral_char_;
+ RemoteAttribute from_peripheral_char_;
+};
+
+TEST_F(CryptAuthBluetoothLowEnergyCharacteristicFinderTest,
+ ConstructAndDestroyDontCrash) {
+ BluetoothLowEnergyCharacteristicsFinder characteristic_finder(
+ adapter_, device_.get(), remote_service_, to_peripheral_char_,
+ from_peripheral_char_, success_callback_, error_callback_);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyCharacteristicFinderTest,
+ FindRightCharacteristics) {
+ BluetoothLowEnergyCharacteristicsFinder characteristic_finder(
+ adapter_, device_.get(), remote_service_, to_peripheral_char_,
+ from_peripheral_char_, success_callback_, error_callback_);
+ // Upcasting |characteristic_finder| to access the virtual protected methods
+ // from Observer: GattCharacteristicAdded() and
+ // GattDiscoveryCompleteForService().
+ device::BluetoothAdapter::Observer* observer =
+ static_cast<device::BluetoothAdapter::Observer*>(&characteristic_finder);
+
+ RemoteAttribute found_to_char, found_from_char;
+ EXPECT_CALL(*this, OnCharacteristicsFound(_, _, _))
+ .WillOnce(
+ DoAll(SaveArg<1>(&found_to_char), SaveArg<2>(&found_from_char)));
+ EXPECT_CALL(*this, OnCharacteristicsFinderError(_, _)).Times(0);
+
+ std::unique_ptr<device::MockBluetoothGattCharacteristic> from_char =
+ ExpectToFindCharacteristic(device::BluetoothUUID(kFromPeripheralCharUUID),
+ kFromPeripheralCharID, true);
+ observer->GattCharacteristicAdded(adapter_.get(), from_char.get());
+
+ std::unique_ptr<device::MockBluetoothGattCharacteristic> to_char =
+ ExpectToFindCharacteristic(device::BluetoothUUID(kToPeripheralCharUUID),
+ kToPeripheralCharID, true);
+ observer->GattCharacteristicAdded(adapter_.get(), to_char.get());
+
+ EXPECT_EQ(kToPeripheralCharID, found_to_char.id);
+ EXPECT_EQ(kFromPeripheralCharID, found_from_char.id);
+
+ EXPECT_CALL(*service_, GetUUID())
+ .WillOnce(Return(device::BluetoothUUID(kServiceUUID)));
+ observer->GattDiscoveryCompleteForService(adapter_.get(), service_.get());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyCharacteristicFinderTest,
+ DidntFindRightCharacteristics) {
+ BluetoothLowEnergyCharacteristicsFinder characteristic_finder(
+ adapter_, device_.get(), remote_service_, to_peripheral_char_,
+ from_peripheral_char_, success_callback_, error_callback_);
+ device::BluetoothAdapter::Observer* observer =
+ static_cast<device::BluetoothAdapter::Observer*>(&characteristic_finder);
+
+ EXPECT_CALL(*this, OnCharacteristicsFound(_, _, _)).Times(0);
+ EXPECT_CALL(*this, OnCharacteristicsFinderError(_, _));
+
+ std::unique_ptr<device::MockBluetoothGattCharacteristic> other_char =
+ ExpectToFindCharacteristic(device::BluetoothUUID(kOtherCharUUID),
+ kOtherCharID, false);
+ observer->GattCharacteristicAdded(adapter_.get(), other_char.get());
+
+ EXPECT_CALL(*service_, GetUUID())
+ .WillOnce(Return(device::BluetoothUUID(kServiceUUID)));
+ observer->GattDiscoveryCompleteForService(adapter_.get(), service_.get());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyCharacteristicFinderTest,
+ FindOnlyOneRightCharacteristic) {
+ BluetoothLowEnergyCharacteristicsFinder characteristic_finder(
+ adapter_, device_.get(), remote_service_, to_peripheral_char_,
+ from_peripheral_char_, success_callback_, error_callback_);
+ device::BluetoothAdapter::Observer* observer =
+ static_cast<device::BluetoothAdapter::Observer*>(&characteristic_finder);
+
+ RemoteAttribute found_to_char, found_from_char;
+ EXPECT_CALL(*this, OnCharacteristicsFound(_, _, _)).Times(0);
+ EXPECT_CALL(*this, OnCharacteristicsFinderError(_, _))
+ .WillOnce(
+ DoAll(SaveArg<0>(&found_to_char), SaveArg<1>(&found_from_char)));
+
+ std::unique_ptr<device::MockBluetoothGattCharacteristic> from_char =
+ ExpectToFindCharacteristic(device::BluetoothUUID(kFromPeripheralCharUUID),
+ kFromPeripheralCharID, true);
+ observer->GattCharacteristicAdded(adapter_.get(), from_char.get());
+
+ EXPECT_CALL(*service_, GetUUID())
+ .WillOnce(Return(device::BluetoothUUID(kServiceUUID)));
+ observer->GattDiscoveryCompleteForService(adapter_.get(), service_.get());
+ EXPECT_EQ(kFromPeripheralCharID, found_from_char.id);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyCharacteristicFinderTest,
+ FindWrongCharacteristic_FindRightCharacteristics) {
+ BluetoothLowEnergyCharacteristicsFinder characteristic_finder(
+ adapter_, device_.get(), remote_service_, to_peripheral_char_,
+ from_peripheral_char_, success_callback_, error_callback_);
+ device::BluetoothAdapter::Observer* observer =
+ static_cast<device::BluetoothAdapter::Observer*>(&characteristic_finder);
+
+ RemoteAttribute found_to_char, found_from_char;
+ EXPECT_CALL(*this, OnCharacteristicsFound(_, _, _))
+ .WillOnce(
+ DoAll(SaveArg<1>(&found_to_char), SaveArg<2>(&found_from_char)));
+ EXPECT_CALL(*this, OnCharacteristicsFinderError(_, _)).Times(0);
+
+ std::unique_ptr<device::MockBluetoothGattCharacteristic> other_char =
+ ExpectToFindCharacteristic(device::BluetoothUUID(kOtherCharUUID),
+ kOtherCharID, false);
+ observer->GattCharacteristicAdded(adapter_.get(), other_char.get());
+
+ std::unique_ptr<device::MockBluetoothGattCharacteristic> from_char =
+ ExpectToFindCharacteristic(device::BluetoothUUID(kFromPeripheralCharUUID),
+ kFromPeripheralCharID, true);
+ observer->GattCharacteristicAdded(adapter_.get(), from_char.get());
+
+ std::unique_ptr<device::MockBluetoothGattCharacteristic> to_char =
+ ExpectToFindCharacteristic(device::BluetoothUUID(kToPeripheralCharUUID),
+ kToPeripheralCharID, true);
+ observer->GattCharacteristicAdded(adapter_.get(), to_char.get());
+
+ EXPECT_EQ(kToPeripheralCharID, found_to_char.id);
+ EXPECT_EQ(kFromPeripheralCharID, found_from_char.id);
+
+ EXPECT_CALL(*service_, GetUUID())
+ .WillOnce(Return(device::BluetoothUUID(kServiceUUID)));
+ observer->GattDiscoveryCompleteForService(adapter_.get(), service_.get());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyCharacteristicFinderTest,
+ RightCharacteristicsAlreadyPresent) {
+ RemoteAttribute found_to_char, found_from_char;
+ EXPECT_CALL(*this, OnCharacteristicsFound(_, _, _))
+ .WillOnce(
+ DoAll(SaveArg<1>(&found_to_char), SaveArg<2>(&found_from_char)));
+ EXPECT_CALL(*this, OnCharacteristicsFinderError(_, _)).Times(0);
+
+ std::unique_ptr<device::MockBluetoothGattCharacteristic> from_char =
+ ExpectToFindCharacteristic(device::BluetoothUUID(kFromPeripheralCharUUID),
+ kFromPeripheralCharID, true);
+
+ std::unique_ptr<device::MockBluetoothGattCharacteristic> to_char =
+ ExpectToFindCharacteristic(device::BluetoothUUID(kToPeripheralCharUUID),
+ kToPeripheralCharID, true);
+
+ std::vector<device::BluetoothRemoteGattService*> services;
+ services.push_back(service_.get());
+ ON_CALL(*device_, GetGattServices()).WillByDefault(Return(services));
+
+ std::vector<device::BluetoothRemoteGattCharacteristic*> characteristics;
+ characteristics.push_back(from_char.get());
+ characteristics.push_back(to_char.get());
+ ON_CALL(*service_, GetCharacteristics())
+ .WillByDefault(Return(characteristics));
+
+ BluetoothLowEnergyCharacteristicsFinder characteristic_finder(
+ adapter_, device_.get(), remote_service_, to_peripheral_char_,
+ from_peripheral_char_, success_callback_, error_callback_);
+ device::BluetoothAdapter::Observer* observer =
+ static_cast<device::BluetoothAdapter::Observer*>(&characteristic_finder);
+
+ EXPECT_EQ(kToPeripheralCharID, found_to_char.id);
+ EXPECT_EQ(kFromPeripheralCharID, found_from_char.id);
+
+ EXPECT_CALL(*service_, GetUUID())
+ .WillOnce(Return(device::BluetoothUUID(kServiceUUID)));
+ observer->GattDiscoveryCompleteForService(adapter_.get(), service_.get());
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc
new file mode 100644
index 00000000000..fac07bc77a8
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc
@@ -0,0 +1,639 @@
+// 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/ble/bluetooth_low_energy_weave_client_connection.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/cryptauth/bluetooth_throttler.h"
+#include "components/cryptauth/connection_finder.h"
+#include "components/cryptauth/wire_message.h"
+#include "components/proximity_auth/logging/logging.h"
+#include "device/bluetooth/bluetooth_gatt_connection.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothDevice;
+using device::BluetoothGattConnection;
+using device::BluetoothRemoteGattService;
+using device::BluetoothRemoteGattCharacteristic;
+using device::BluetoothGattNotifySession;
+using device::BluetoothUUID;
+
+namespace cryptauth {
+namespace weave {
+namespace {
+
+typedef BluetoothLowEnergyWeavePacketReceiver::State ReceiverState;
+
+// The UUID of the TX characteristic used to transmit data to the server.
+const char kTXCharacteristicUUID[] = "00000100-0004-1000-8000-001A11000101";
+
+// The UUID of the RX characteristic used to receive data from the server.
+const char kRXCharacteristicUUID[] = "00000100-0004-1000-8000-001A11000102";
+
+} // namespace
+
+BluetoothLowEnergyWeaveClientConnection::
+ BluetoothLowEnergyWeaveClientConnection(
+ const RemoteDevice& device,
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ const BluetoothUUID remote_service_uuid,
+ BluetoothThrottler* bluetooth_throttler,
+ int max_number_of_write_attempts)
+ : Connection(device),
+ adapter_(adapter),
+ remote_service_({remote_service_uuid, ""}),
+ packet_generator_(
+ BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance()),
+ packet_receiver_(
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ BluetoothLowEnergyWeavePacketReceiver::ReceiverType::CLIENT)),
+ tx_characteristic_({BluetoothUUID(kTXCharacteristicUUID), ""}),
+ rx_characteristic_({BluetoothUUID(kRXCharacteristicUUID), ""}),
+ bluetooth_throttler_(bluetooth_throttler),
+ task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ sub_status_(SubStatus::DISCONNECTED),
+ write_remote_characteristic_pending_(false),
+ max_number_of_write_attempts_(max_number_of_write_attempts),
+ 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_ = NULL;
+ }
+}
+
+void BluetoothLowEnergyWeaveClientConnection::Connect() {
+ DCHECK(sub_status() == SubStatus::DISCONNECTED);
+
+ SetSubStatus(SubStatus::WAITING_GATT_CONNECTION);
+ 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::CreateGattConnection,
+ weak_ptr_factory_.GetWeakPtr()),
+ throttler_delay);
+ return;
+ }
+
+ CreateGattConnection();
+}
+
+void BluetoothLowEnergyWeaveClientConnection::CreateGattConnection() {
+ DCHECK(sub_status() == SubStatus::WAITING_GATT_CONNECTION);
+
+ BluetoothDevice* remote_device = GetRemoteDevice();
+ if (remote_device) {
+ PA_LOG(INFO) << "Creating GATT connection with "
+ << remote_device->GetAddress();
+
+ remote_device->CreateGattConnection(
+ base::Bind(
+ &BluetoothLowEnergyWeaveClientConnection::OnGattConnectionCreated,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BluetoothLowEnergyWeaveClientConnection::
+ OnCreateGattConnectionError,
+ weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ SetSubStatus(SubStatus::DISCONNECTED);
+ }
+}
+
+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();
+ }
+}
+
+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();
+ }
+
+ // 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);
+ }
+}
+
+void BluetoothLowEnergyWeaveClientConnection::SetSubStatus(
+ SubStatus new_sub_status) {
+ sub_status_ = new_sub_status;
+
+ // Sets the status of parent class Connection accordingly.
+ if (new_sub_status == SubStatus::CONNECTED) {
+ SetStatus(Status::CONNECTED);
+ } else if (new_sub_status == SubStatus::DISCONNECTED) {
+ SetStatus(Status::DISCONNECTED);
+ } else {
+ SetStatus(Status::IN_PROGRESS);
+ }
+}
+
+void BluetoothLowEnergyWeaveClientConnection::SetTaskRunnerForTesting(
+ scoped_refptr<base::TaskRunner> task_runner) {
+ task_runner_ = task_runner;
+}
+
+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);
+
+ std::shared_ptr<WireMessage> request_message(message.release());
+
+ 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);
+ }
+}
+
+// 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(
+ BluetoothAdapter* adapter,
+ BluetoothDevice* device) {
+ DCHECK(device);
+ if (sub_status() == SubStatus::DISCONNECTED ||
+ device->GetAddress() != GetDeviceAddress())
+ return;
+
+ if (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();
+ }
+}
+
+void BluetoothLowEnergyWeaveClientConnection::DeviceRemoved(
+ BluetoothAdapter* adapter,
+ BluetoothDevice* device) {
+ DCHECK(device);
+ if (sub_status_ == SubStatus::DISCONNECTED ||
+ device->GetAddress() != GetDeviceAddress())
+ return;
+
+ PA_LOG(INFO) << "Device removed " << GetDeviceAddress();
+ DestroyConnection();
+}
+
+void BluetoothLowEnergyWeaveClientConnection::GattCharacteristicValueChanged(
+ BluetoothAdapter* adapter,
+ 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();
+
+ if (characteristic->GetIdentifier() == rx_characteristic_.id) {
+ ReceiverState state = packet_receiver_->ReceivePacket(value);
+
+ 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();
+ }
+ }
+}
+
+void BluetoothLowEnergyWeaveClientConnection::CompleteConnection() {
+ PA_LOG(INFO) << "Connection completed. Time elapsed: "
+ << base::TimeTicks::Now() - start_time_;
+ SetSubStatus(SubStatus::CONNECTED);
+}
+
+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;
+ 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);
+ characteristic_finder_.reset(CreateCharacteristicsFinder(
+ base::Bind(
+ &BluetoothLowEnergyWeaveClientConnection::OnCharacteristicsFound,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BluetoothLowEnergyWeaveClientConnection::
+ OnCharacteristicsFinderError,
+ weak_ptr_factory_.GetWeakPtr())));
+}
+
+BluetoothLowEnergyCharacteristicsFinder*
+BluetoothLowEnergyWeaveClientConnection::CreateCharacteristicsFinder(
+ const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
+ success_callback,
+ const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
+ error_callback) {
+ return new BluetoothLowEnergyCharacteristicsFinder(
+ adapter_, GetRemoteDevice(), remote_service_, tx_characteristic_,
+ rx_characteristic_, success_callback, error_callback);
+}
+
+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;
+
+ SetSubStatus(SubStatus::CHARACTERISTICS_FOUND);
+ StartNotifySession();
+}
+
+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.";
+
+ DestroyConnection();
+}
+
+void BluetoothLowEnergyWeaveClientConnection::StartNotifySession() {
+ DCHECK(sub_status() == SubStatus::CHARACTERISTICS_FOUND);
+ BluetoothRemoteGattCharacteristic* characteristic =
+ GetGattCharacteristic(rx_characteristic_.id);
+ CHECK(characteristic);
+
+ // This is a workaround for crbug.com/507325. If |characteristic| is already
+ // notifying |characteristic->StartNotifySession()| will fail 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);
+ characteristic->StartNotifySession(
+ base::Bind(
+ &BluetoothLowEnergyWeaveClientConnection::OnNotifySessionStarted,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BluetoothLowEnergyWeaveClientConnection::OnNotifySessionError,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BluetoothLowEnergyWeaveClientConnection::OnNotifySessionStarted(
+ std::unique_ptr<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);
+
+ SendConnectionRequest();
+}
+
+void BluetoothLowEnergyWeaveClientConnection::OnNotifySessionError(
+ BluetoothRemoteGattService::GattErrorCode error) {
+ DCHECK(sub_status() == SubStatus::WAITING_NOTIFY_SESSION);
+ PA_LOG(WARNING) << "Error starting notification session: " << 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);
+
+ WriteRequest write_request(packet_generator_->CreateConnectionRequest(),
+ WriteRequestType::CONNECTION_REQUEST);
+
+ WriteRemoteCharacteristic(write_request);
+ }
+}
+
+void BluetoothLowEnergyWeaveClientConnection::WriteRemoteCharacteristic(
+ const WriteRequest& request) {
+ write_requests_queue_.push(request);
+ PA_LOG(ERROR) << "you got " << request.message.get();
+ ProcessNextWriteRequest();
+}
+
+void BluetoothLowEnergyWeaveClientConnection::ProcessNextWriteRequest() {
+ 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()));
+ }
+}
+
+void BluetoothLowEnergyWeaveClientConnection::OnRemoteCharacteristicWritten() {
+ PA_LOG(INFO) << "Characteristic written.";
+
+ DCHECK(!write_requests_queue_.empty());
+
+ const WriteRequest& request = write_requests_queue_.front();
+
+ 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:
+ DestroyConnection();
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // Removes the top of queue (already processed) and process the next request.
+ write_requests_queue_.pop();
+ write_remote_characteristic_pending_ = false;
+ ProcessNextWriteRequest();
+}
+
+void BluetoothLowEnergyWeaveClientConnection::OnWriteRemoteCharacteristicError(
+ BluetoothRemoteGattService::GattErrorCode error) {
+ PA_LOG(WARNING) << "Error " << error << " writing characteristic: "
+ << tx_characteristic_.uuid.canonical_value();
+
+ write_remote_characteristic_pending_ = false;
+
+ DCHECK(!write_requests_queue_.empty());
+ WriteRequest* request = &write_requests_queue_.front();
+
+ // Increases the number of failed attempts and retry.
+
+ if (++request->number_of_failed_attempts >= max_number_of_write_attempts_) {
+ 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 the previous write failed that many times, probably can't write a
+ // connection close either, so just destroy the connection.
+ DestroyConnection();
+ } else {
+ ProcessNextWriteRequest();
+ }
+}
+
+void BluetoothLowEnergyWeaveClientConnection::OnPacketReceiverError() {
+ PA_LOG(ERROR) << "Received erroneous packet. Closing connection.";
+
+ WriteRequest request(packet_generator_->CreateConnectionClose(
+ packet_receiver_->GetReasonToClose()),
+ WriteRequestType::CONNECTION_CLOSE);
+
+ // Skip all other writes that's not in progress.
+
+ // 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);
+
+ if (write_remote_characteristic_pending_) {
+ // Add the in progress write back to the queue.
+ write_requests_queue_.push(empty.front());
+ }
+
+ WriteRemoteCharacteristic(request);
+}
+
+void BluetoothLowEnergyWeaveClientConnection::PrintTimeElapsed() {
+ PA_LOG(INFO) << "Time elapsed: " << base::TimeTicks::Now() - start_time_;
+}
+
+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.
+ return gatt_connection_ ? gatt_connection_->GetDeviceAddress()
+ : remote_device().bluetooth_address;
+}
+
+BluetoothDevice* BluetoothLowEnergyWeaveClientConnection::GetRemoteDevice() {
+ // It's not possible to simply use
+ // |adapter_->GetDevice(GetDeviceAddress())| to find the device with MAC
+ // address |GetDeviceAddress()|. For paired devices,
+ // BluetoothAdapter::GetDevice(XXX) searches for the temporary MAC address
+ // XXX, whereas |GetDeviceAddress()| is the real MAC address. This is a
+ // bug in the way device::BluetoothAdapter is storing the devices (see
+ // crbug.com/497841).
+ std::vector<BluetoothDevice*> devices = adapter_->GetDevices();
+ for (const auto& device : devices) {
+ if (device->GetAddress() == GetDeviceAddress())
+ return device;
+ }
+
+ return nullptr;
+}
+
+BluetoothRemoteGattService*
+BluetoothLowEnergyWeaveClientConnection::GetRemoteService() {
+ BluetoothDevice* remote_device = GetRemoteDevice();
+ if (!remote_device) {
+ PA_LOG(WARNING) << "Remote device not found.";
+ return NULL;
+ }
+ if (remote_service_.id.empty()) {
+ std::vector<BluetoothRemoteGattService*> services =
+ remote_device->GetGattServices();
+ for (const auto& service : services)
+ if (service->GetUUID() == remote_service_.uuid) {
+ remote_service_.id = service->GetIdentifier();
+ break;
+ }
+ }
+ return remote_device->GetGattService(remote_service_.id);
+}
+
+BluetoothRemoteGattCharacteristic*
+BluetoothLowEnergyWeaveClientConnection::GetGattCharacteristic(
+ const std::string& gatt_characteristic) {
+ BluetoothRemoteGattService* remote_service = GetRemoteService();
+ if (!remote_service) {
+ PA_LOG(WARNING) << "Remote service not found.";
+ return NULL;
+ }
+ return remote_service->GetCharacteristic(gatt_characteristic);
+}
+
+std::string BluetoothLowEnergyWeaveClientConnection::GetReasonForClose() {
+ switch (packet_receiver_->GetReasonForClose()) {
+ case ReasonForClose::CLOSE_WITHOUT_ERROR:
+ return "CLOSE_WITHOUT_ERROR";
+ case ReasonForClose::UNKNOWN_ERROR:
+ return "UNKNOWN_ERROR";
+ case ReasonForClose::NO_COMMON_VERSION_SUPPORTED:
+ return "NO_COMMON_VERSION_SUPPORTED";
+ case ReasonForClose::RECEIVED_PACKET_OUT_OF_SEQUENCE:
+ return "RECEIVED_PACKET_OUT_OF_SEQUENCE";
+ case ReasonForClose::APPLICATION_ERROR:
+ return "APPLICATION_ERROR";
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+BluetoothLowEnergyWeaveClientConnection::WriteRequest::WriteRequest(
+ const Packet& val,
+ WriteRequestType request_type,
+ std::shared_ptr<WireMessage> message)
+ : value(val),
+ request_type(request_type),
+ message(message),
+ number_of_failed_attempts(0) {}
+
+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;
+
+BluetoothLowEnergyWeaveClientConnection::WriteRequest::~WriteRequest() {}
+
+} // namespace weave
+
+} // namespace cryptauth
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
new file mode 100644
index 00000000000..797c7324129
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h
@@ -0,0 +1,316 @@
+// 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_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <queue>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.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"
+#include "device/bluetooth/bluetooth_gatt_notify_session.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace base {
+class TaskRunner;
+}
+
+namespace cryptauth {
+
+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 |
+// | receive connection request
+// | send connection response
+// receive connection response |
+// opt: send data | opt: send data
+// receive data | receive data
+// opt: close connection | opt: close connection
+class BluetoothLowEnergyWeaveClientConnection
+ : public Connection,
+ public device::BluetoothAdapter::Observer {
+ public:
+ class Factory {
+ public:
+ static std::unique_ptr<BluetoothLowEnergyWeaveClientConnection>
+ NewInstance();
+
+ // Exposed for testing.
+ static void SetInstanceForTesting(Factory* factory);
+
+ protected:
+ // Exposed for testing.
+ virtual std::unique_ptr<BluetoothLowEnergyWeaveClientConnection>
+ BuildInstance();
+
+ private:
+ static Factory* factory_instance_;
+ };
+
+ // The sub-state of a BluetoothLowEnergyWeaveClientConnection
+ // extends the IN_PROGRESS state of Connection::Status.
+ enum SubStatus {
+ DISCONNECTED,
+ WAITING_GATT_CONNECTION,
+ WAITING_CHARACTERISTICS,
+ CHARACTERISTICS_FOUND,
+ WAITING_NOTIFY_SESSION,
+ NOTIFY_SESSION_READY,
+ WAITING_CONNECTION_RESPONSE,
+ 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.
+ BluetoothLowEnergyWeaveClientConnection(
+ const RemoteDevice& remote_device,
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ const device::BluetoothUUID remote_service_uuid,
+ BluetoothThrottler* bluetooth_throttler,
+ int max_number_of_write_attempts);
+
+ ~BluetoothLowEnergyWeaveClientConnection() override;
+
+ // namespace Connection:
+ void Connect() override;
+ void Disconnect() override;
+ std::string GetDeviceAddress() override;
+
+ protected:
+ // Exposed for testing.
+ void DestroyConnection();
+
+ // Exposed for testing.
+ SubStatus sub_status() { return sub_status_; }
+
+ // Sets |task_runner_| for testing.
+ void SetTaskRunnerForTesting(scoped_refptr<base::TaskRunner> task_runner);
+
+ // Virtual for testing.
+ virtual BluetoothLowEnergyCharacteristicsFinder* CreateCharacteristicsFinder(
+ const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
+ success_callback,
+ const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
+ error_callback);
+
+ // namespace Connection:
+ void SendMessageImpl(std::unique_ptr<WireMessage> message) override;
+
+ // device::BluetoothAdapter::Observer:
+ void DeviceChanged(device::BluetoothAdapter* adapter,
+ device::BluetoothDevice* device) override;
+ void DeviceRemoved(device::BluetoothAdapter* adapter,
+ device::BluetoothDevice* device) override;
+ void GattCharacteristicValueChanged(
+ device::BluetoothAdapter* adapter,
+ device::BluetoothRemoteGattCharacteristic* characteristic,
+ const Packet& value) override;
+
+ private:
+ enum WriteRequestType {
+ REGULAR,
+ MESSAGE_COMPLETE,
+ CONNECTION_REQUEST,
+ CONNECTION_CLOSE
+ };
+
+ // Represents a request to write |value| to a some characteristic.
+ // |is_last_write_for_wire_messsage| indicates whether this request
+ // corresponds to the last write request for some wire message.
+ struct WriteRequest {
+ WriteRequest(const Packet& val,
+ WriteRequestType request_type,
+ std::shared_ptr<WireMessage> message);
+ WriteRequest(const Packet& val, WriteRequestType request_type);
+ WriteRequest(const WriteRequest& other);
+ ~WriteRequest();
+
+ Packet value;
+ WriteRequestType request_type;
+ std::shared_ptr<WireMessage> message;
+ int number_of_failed_attempts;
+ };
+
+ void SetSubStatus(SubStatus status);
+
+ // 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 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).
+ 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_|.
+ void ProcessNextWriteRequest();
+
+ // Called when the
+ // BluetoothRemoteGattCharacteristic::RemoteCharacteristicWrite() is
+ // successfully complete.
+ void OnRemoteCharacteristicWritten();
+
+ // Called when there is an error writing to the remote characteristic
+ // |tx_characteristic_|.
+ void OnWriteRemoteCharacteristicError(
+ device::BluetoothRemoteGattService::GattErrorCode error);
+
+ void OnPacketReceiverError();
+
+ // Prints the time elapsed since |Connect()| was called.
+ void PrintTimeElapsed();
+
+ // Returns the device corresponding to |remote_device_address_|.
+ device::BluetoothDevice* GetRemoteDevice();
+
+ // Returns the service corresponding to |remote_service_| in the current
+ // device.
+ device::BluetoothRemoteGattService* GetRemoteService();
+
+ // Returns the characteristic corresponding to |identifier| in the current
+ // service.
+ device::BluetoothRemoteGattCharacteristic* GetGattCharacteristic(
+ const std::string& identifier);
+
+ // 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();
+
+ // 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_;
+
+ // The GATT connection with the remote device.
+ 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_;
+
+ // Maximum number of tries to send any write request.
+ int max_number_of_write_attempts_;
+
+ // Stores when the instace was created.
+ base::TimeTicks start_time_;
+
+ base::WeakPtrFactory<BluetoothLowEnergyWeaveClientConnection>
+ weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothLowEnergyWeaveClientConnection);
+};
+
+} // namespace weave
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_CLIENT_CONNECTION_H_
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
new file mode 100644
index 00000000000..cf49aa0d0c1
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc
@@ -0,0 +1,1061 @@
+// 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/ble/bluetooth_low_energy_weave_client_connection.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#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 "components/cryptauth/connection_finder.h"
+#include "components/cryptauth/connection_observer.h"
+#include "components/cryptauth/cryptauth_test_util.h"
+#include "components/cryptauth/remote_device.h"
+#include "components/cryptauth/wire_message.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/bluetooth/test/mock_bluetooth_device.h"
+#include "device/bluetooth/test/mock_bluetooth_discovery_session.h"
+#include "device/bluetooth/test/mock_bluetooth_gatt_characteristic.h"
+#include "device/bluetooth/test/mock_bluetooth_gatt_connection.h"
+#include "device/bluetooth/test/mock_bluetooth_gatt_notify_session.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::AtLeast;
+using testing::NiceMock;
+using testing::Return;
+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) {}
+
+ void OnConnectionStatusChanged(Connection* connection,
+ Connection::Status old_status,
+ Connection::Status new_status) override {}
+
+ 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_; }
+
+ private:
+ std::string last_deserialized_message_;
+ bool last_send_success_;
+ int num_send_completed_;
+};
+
+} // namespace
+
+namespace weave {
+namespace {
+
+typedef BluetoothLowEnergyWeaveClientConnection::SubStatus SubStatus;
+typedef BluetoothLowEnergyWeavePacketReceiver::State ReceiverState;
+typedef BluetoothLowEnergyWeavePacketReceiver::ReceiverError ReceiverError;
+typedef BluetoothLowEnergyWeavePacketReceiver::ReceiverType ReceiverType;
+
+const char kServiceUUID[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEEF";
+const char kTXCharacteristicUUID[] = "977c6674-1239-4e72-993b-502369b8bb5a";
+const char kRXCharacteristicUUID[] = "f4b904a2-a030-43b3-98a8-221c536c03cb";
+
+const char kServiceID[] = "service id";
+const char kTXCharacteristicID[] = "TX characteristic id";
+const char kRXCharacteristicID[] = "RX characteristic id";
+
+const device::BluetoothRemoteGattCharacteristic::Properties
+ kCharacteristicProperties =
+ device::BluetoothRemoteGattCharacteristic::PROPERTY_BROADCAST |
+ device::BluetoothRemoteGattCharacteristic::PROPERTY_READ |
+ device::BluetoothRemoteGattCharacteristic::
+ PROPERTY_WRITE_WITHOUT_RESPONSE |
+ device::BluetoothRemoteGattCharacteristic::PROPERTY_INDICATE;
+
+const int kMaxNumberOfTries = 3;
+const uint16_t kLargeMaxPacketSize = 30;
+
+const uint8_t kDataHeader = 0;
+const uint8_t kConnectionRequestHeader = 1;
+const uint8_t kSmallConnectionResponseHeader = 2;
+const uint8_t kLargeConnectionResponseHeader = 3;
+const uint8_t kConnectionCloseHeader = 4;
+const uint8_t kErroneousHeader = 5;
+
+const std::string kSmallMessage = "bb";
+const std::string kLargeMessage = "aaabbb";
+const std::string kLargeMessage0 = "aaa";
+const std::string kLargeMessage1 = "bbb";
+
+const Packet kConnectionRequest{kConnectionRequestHeader};
+const Packet kSmallConnectionResponse{kSmallConnectionResponseHeader};
+const Packet kLargeConnectionResponse{kLargeConnectionResponseHeader};
+const Packet kConnectionCloseSuccess{kConnectionCloseHeader,
+ ReasonForClose::CLOSE_WITHOUT_ERROR};
+const Packet kConnectionCloseUnknownError{kConnectionCloseHeader,
+ ReasonForClose::UNKNOWN_ERROR};
+const Packet kConnectionCloseApplicationError{
+ kConnectionCloseHeader, ReasonForClose::APPLICATION_ERROR};
+
+const Packet kSmallPackets0 = Packet{kDataHeader, 'b', 'b'};
+const Packet kLargePackets0 = Packet{kDataHeader, 'a', 'a', 'a'};
+const Packet kLargePackets1 = Packet{kDataHeader, 'b', 'b', 'b'};
+const Packet kErroneousPacket = Packet{kErroneousHeader};
+
+const std::vector<Packet> kSmallPackets{kSmallPackets0};
+const std::vector<Packet> kLargePackets{kLargePackets0, kLargePackets1};
+
+class MockBluetoothLowEnergyWeavePacketGenerator
+ : public BluetoothLowEnergyWeavePacketGenerator {
+ public:
+ MockBluetoothLowEnergyWeavePacketGenerator()
+ : max_packet_size_(kDefaultMaxPacketSize) {}
+
+ Packet CreateConnectionRequest() override { return kConnectionRequest; }
+
+ Packet CreateConnectionResponse() override {
+ NOTIMPLEMENTED();
+ return Packet();
+ }
+
+ Packet CreateConnectionClose(ReasonForClose reason_for_close) override {
+ return Packet{kConnectionCloseHeader,
+ static_cast<uint8_t>(reason_for_close)};
+ }
+
+ 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) {
+ return kSmallPackets;
+ } else if (message == (kTestFeature + "," + kLargeMessage)
+ && max_packet_size_ == kLargeMaxPacketSize) {
+ return kLargePackets;
+ } else {
+ NOTREACHED();
+ return std::vector<Packet>();
+ }
+ }
+
+ uint16_t GetMaxPacketSize() { return max_packet_size_; }
+
+ private:
+ uint16_t max_packet_size_;
+};
+
+class MockBluetoothLowEnergyWeavePacketReceiver
+ : public BluetoothLowEnergyWeavePacketReceiver {
+ public:
+ MockBluetoothLowEnergyWeavePacketReceiver()
+ : BluetoothLowEnergyWeavePacketReceiver(ReceiverType::CLIENT),
+ state_(State::CONNECTING),
+ max_packet_size_(kDefaultMaxPacketSize),
+ reason_for_close_(ReasonForClose::CLOSE_WITHOUT_ERROR),
+ reason_to_close_(ReasonForClose::CLOSE_WITHOUT_ERROR) {}
+
+ ReceiverState GetState() override { return state_; }
+
+ uint16_t GetMaxPacketSize() override { return max_packet_size_; }
+
+ ReasonForClose GetReasonForClose() override { return reason_for_close_; }
+
+ ReasonForClose GetReasonToClose() override { return reason_to_close_; }
+
+ std::string GetDataMessage() override {
+ if (max_packet_size_ == kDefaultMaxPacketSize) {
+ return kSmallMessage;
+ } else {
+ return kLargeMessage;
+ }
+ }
+
+ ReceiverError GetReceiverError() override {
+ return ReceiverError::NO_ERROR_DETECTED;
+ }
+
+ ReceiverState ReceivePacket(const Packet& packet) override {
+ switch (packet[0]) {
+ case kSmallConnectionResponseHeader:
+ max_packet_size_ = kDefaultMaxPacketSize;
+ state_ = ReceiverState::WAITING;
+ break;
+ case kLargeConnectionResponseHeader:
+ max_packet_size_ = kLargeMaxPacketSize;
+ state_ = ReceiverState::WAITING;
+ break;
+ case kConnectionCloseHeader:
+ state_ = ReceiverState::CONNECTION_CLOSED;
+ reason_for_close_ = static_cast<ReasonForClose>(packet[1]);
+ break;
+ case kDataHeader:
+ if (packet == kSmallPackets0 || packet == kLargePackets1) {
+ state_ = ReceiverState::DATA_READY;
+ } else {
+ state_ = ReceiverState::RECEIVING_DATA;
+ }
+ break;
+ default:
+ reason_to_close_ = ReasonForClose::APPLICATION_ERROR;
+ state_ = ReceiverState::ERROR_DETECTED;
+ }
+ return state_;
+ }
+
+ private:
+ ReceiverState state_;
+ uint16_t max_packet_size_;
+ ReasonForClose reason_for_close_;
+ 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:
+ TestBluetoothLowEnergyWeaveClientConnection(
+ const RemoteDevice& remote_device,
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ const device::BluetoothUUID remote_service_uuid,
+ BluetoothThrottler* bluetooth_throttler,
+ int max_number_of_write_attempts)
+ : BluetoothLowEnergyWeaveClientConnection(remote_device,
+ adapter,
+ remote_service_uuid,
+ bluetooth_throttler,
+ max_number_of_write_attempts) {}
+
+ ~TestBluetoothLowEnergyWeaveClientConnection() override {}
+
+ MOCK_METHOD2(
+ CreateCharacteristicsFinder,
+ BluetoothLowEnergyCharacteristicsFinder*(
+ const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
+ success,
+ const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback& error));
+
+ MOCK_METHOD1(OnBytesReceived, void(const std::string& bytes));
+
+ // Exposing inherited protected methods for testing.
+ using BluetoothLowEnergyWeaveClientConnection::GattCharacteristicValueChanged;
+ using BluetoothLowEnergyWeaveClientConnection::SetTaskRunnerForTesting;
+ using BluetoothLowEnergyWeaveClientConnection::DestroyConnection;
+
+ // Exposing inherited protected fields for testing.
+ using BluetoothLowEnergyWeaveClientConnection::status;
+ using BluetoothLowEnergyWeaveClientConnection::sub_status;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestBluetoothLowEnergyWeaveClientConnection);
+};
+
+} // namespace
+
+class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
+ : public testing::Test {
+ public:
+ CryptAuthBluetoothLowEnergyWeaveClientConnectionTest()
+ : adapter_(new NiceMock<device::MockBluetoothAdapter>),
+ 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_);
+ BluetoothLowEnergyWeavePacketReceiver::Factory::SetInstanceForTesting(
+ receiver_factory_);
+ }
+
+ ~CryptAuthBluetoothLowEnergyWeaveClientConnectionTest() override {
+ BluetoothLowEnergyWeavePacketGenerator::Factory::SetInstanceForTesting(
+ nullptr);
+ BluetoothLowEnergyWeavePacketReceiver::Factory::SetInstanceForTesting(
+ nullptr);
+ }
+
+ void SetUp() override {
+ device_ = base::MakeUnique<NiceMock<device::MockBluetoothDevice>>(
+ adapter_.get(), 0, kTestRemoteDeviceName,
+ kTestRemoteDeviceBluetoothAddress, false, false);
+
+ service_ = base::MakeUnique<NiceMock<device::MockBluetoothGattService>>(
+ device_.get(), kServiceID, service_uuid_, true, false);
+ tx_characteristic_ =
+ base::MakeUnique<NiceMock<device::MockBluetoothGattCharacteristic>>(
+ 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(device_.get());
+ ON_CALL(*adapter_, GetDevices()).WillByDefault(Return(devices));
+ ON_CALL(*adapter_, GetDevice(kTestRemoteDeviceBluetoothAddress))
+ .WillByDefault(Return(device_.get()));
+ ON_CALL(*device_, GetGattService(kServiceID))
+ .WillByDefault(Return(service_.get()));
+ ON_CALL(*service_, GetCharacteristic(kRXCharacteristicID))
+ .WillByDefault(Return(rx_characteristic_.get()));
+ ON_CALL(*service_, GetCharacteristic(kTXCharacteristicID))
+ .WillByDefault(Return(tx_characteristic_.get()));
+ }
+
+ // Creates a BluetoothLowEnergyWeaveClientConnection and verifies it's in
+ // DISCONNECTED state.
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection>
+ CreateConnection() {
+ EXPECT_CALL(*adapter_, AddObserver(_));
+ EXPECT_CALL(*adapter_, RemoveObserver(_));
+
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ new TestBluetoothLowEnergyWeaveClientConnection(
+ remote_device_, adapter_, service_uuid_, bluetooth_throttler_.get(),
+ kMaxNumberOfTries));
+
+ 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_);
+
+ return connection;
+ }
+
+ // Transitions |connection| from DISCONNECTED to WAITING_CHARACTERISTICS
+ // state, without an existing GATT connection.
+ void ConnectGatt(TestBluetoothLowEnergyWeaveClientConnection* connection) {
+ // Preparing |connection| for a CreateGattConnection call.
+ EXPECT_CALL(*device_, CreateGattConnection(_, _))
+ .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();
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::WAITING_GATT_CONNECTION);
+ EXPECT_EQ(connection->status(), Connection::IN_PROGRESS);
+
+ // Preparing |connection| to run |create_gatt_connection_success_callback_|.
+ EXPECT_FALSE(create_gatt_connection_error_callback_.is_null());
+ ASSERT_FALSE(create_gatt_connection_success_callback_.is_null());
+ EXPECT_CALL(*connection, CreateCharacteristicsFinder(_, _))
+ .WillOnce(DoAll(
+ SaveArg<0>(&characteristics_finder_success_callback_),
+ SaveArg<1>(&characteristics_finder_error_callback_),
+ Return(new NiceMock<MockBluetoothLowEnergyCharacteristicsFinder>)));
+
+ create_gatt_connection_success_callback_.Run(
+ base::MakeUnique<NiceMock<device::MockBluetoothGattConnection>>(
+ adapter_, kTestRemoteDeviceBluetoothAddress));
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::WAITING_CHARACTERISTICS);
+ EXPECT_EQ(connection->status(), Connection::IN_PROGRESS);
+ }
+
+ // Transitions |connection| from WAITING_CHARACTERISTICS to
+ // WAITING_NOTIFY_SESSION state.
+ void CharacteristicsFound(
+ TestBluetoothLowEnergyWeaveClientConnection* connection) {
+ EXPECT_CALL(*rx_characteristic_, StartNotifySession(_, _))
+ .WillOnce(DoAll(SaveArg<0>(&notify_session_success_callback_),
+ SaveArg<1>(&notify_session_error_callback_)));
+ 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::WAITING_NOTIFY_SESSION);
+ EXPECT_EQ(connection->status(), Connection::IN_PROGRESS);
+ }
+
+ // Transitions |connection| from WAITING_NOTIFY_SESSION to
+ // WAITING_CONNECTION_RESPONSE state.
+ void NotifySessionStarted(
+ TestBluetoothLowEnergyWeaveClientConnection* connection) {
+ 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_)));
+ EXPECT_FALSE(notify_session_error_callback_.is_null());
+ ASSERT_FALSE(notify_session_success_callback_.is_null());
+
+ // Store an alias for the notify session passed |connection|.
+ 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();
+
+ // Written value contains only the mock Connection Request.
+ EXPECT_EQ(last_value_written_on_tx_characteristic_, kConnectionRequest);
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::WAITING_CONNECTION_RESPONSE);
+ EXPECT_EQ(connection->status(), Connection::IN_PROGRESS);
+ }
+
+ // Transitions |connection| from WAITING_CONNECTION_RESPONSE to CONNECTED.
+ void ConnectionResponseReceived(
+ TestBluetoothLowEnergyWeaveClientConnection* connection,
+ uint16_t selected_packet_size) {
+ // Written value contains only the mock Connection Request.
+ EXPECT_EQ(last_value_written_on_tx_characteristic_, kConnectionRequest);
+
+ // OnDidSendMessage is not called.
+ EXPECT_EQ(0, connection_observer_.GetNumSendCompleted());
+
+ RunWriteCharacteristicSuccessCallback();
+
+ // Received Connection Response.
+ 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);
+ } 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);
+ } else {
+ NOTREACHED();
+ }
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::CONNECTED);
+ EXPECT_EQ(connection->status(), Connection::CONNECTED);
+ }
+
+ // 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(
+ DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
+ SaveArg<1>(&write_remote_characteristic_success_callback_),
+ SaveArg<2>(&write_remote_characteristic_error_callback_)));
+ }
+
+ connection->Disconnect();
+
+ if (connection->sub_status() == SubStatus::CONNECTED) {
+ connection->DestroyConnection();
+ EXPECT_EQ(last_value_written_on_tx_characteristic_,
+ kConnectionCloseSuccess);
+ RunWriteCharacteristicSuccessCallback();
+ }
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+ }
+
+ void InitializeConnection(
+ TestBluetoothLowEnergyWeaveClientConnection* connection,
+ uint32_t selected_packet_size) {
+ ConnectGatt(connection);
+ CharacteristicsFound(connection);
+ NotifySessionStarted(connection);
+ ConnectionResponseReceived(connection, selected_packet_size);
+ }
+
+ void RunWriteCharacteristicSuccessCallback() {
+ EXPECT_FALSE(write_remote_characteristic_error_callback_.is_null());
+ ASSERT_FALSE(write_remote_characteristic_success_callback_.is_null());
+ write_remote_characteristic_success_callback_.Run();
+ }
+
+ protected:
+ scoped_refptr<device::MockBluetoothAdapter> adapter_;
+ RemoteDevice remote_device_;
+ device::BluetoothUUID service_uuid_;
+ device::BluetoothUUID tx_characteristic_uuid_;
+ device::BluetoothUUID rx_characteristic_uuid_;
+ std::unique_ptr<device::MockBluetoothDevice> 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::shared_ptr<MockBluetoothLowEnergyWeavePacketGeneratorFactory>
+ generator_factory_;
+ std::shared_ptr<MockBluetoothLowEnergyWeavePacketReceiverFactory>
+ receiver_factory_;
+ MockConnectionObserver connection_observer_;
+
+ // Callbacks
+ device::BluetoothDevice::GattConnectionCallback
+ create_gatt_connection_success_callback_;
+ device::BluetoothDevice::ConnectErrorCallback
+ create_gatt_connection_error_callback_;
+
+ BluetoothLowEnergyCharacteristicsFinder::SuccessCallback
+ characteristics_finder_success_callback_;
+ BluetoothLowEnergyCharacteristicsFinder::ErrorCallback
+ characteristics_finder_error_callback_;
+
+ device::BluetoothRemoteGattCharacteristic::NotifySessionCallback
+ notify_session_success_callback_;
+ device::BluetoothRemoteGattCharacteristic::ErrorCallback
+ notify_session_error_callback_;
+
+ base::Closure write_remote_characteristic_success_callback_;
+ device::BluetoothRemoteGattCharacteristic::ErrorCallback
+ write_remote_characteristic_error_callback_;
+};
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ CreateAndDestroyWithoutConnectCallDoesntCrash) {
+ BluetoothLowEnergyWeaveClientConnection connection(
+ remote_device_, adapter_, service_uuid_, bluetooth_throttler_.get(),
+ kMaxNumberOfTries);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ DisconnectWithoutConnectDoesntCrash) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ Disconnect(connection.get());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ConnectSuccess) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ ConnectGatt(connection.get());
+ CharacteristicsFound(connection.get());
+ NotifySessionStarted(connection.get());
+ ConnectionResponseReceived(connection.get(), kDefaultMaxPacketSize);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ConnectSuccessDisconnect) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ InitializeConnection(connection.get(), kDefaultMaxPacketSize);
+ EXPECT_EQ(connection->sub_status(), SubStatus::CONNECTED);
+ Disconnect(connection.get());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ConnectIncompleteDisconnectFromWaitingCharacteristicsState) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ ConnectGatt(connection.get());
+ Disconnect(connection.get());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ConnectIncompleteDisconnectFromWaitingNotifySessionState) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ ConnectGatt(connection.get());
+ CharacteristicsFound(connection.get());
+ Disconnect(connection.get());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ConnectIncompleteDisconnectFromWaitingConnectionResponseState) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ ConnectGatt(connection.get());
+ CharacteristicsFound(connection.get());
+ NotifySessionStarted(connection.get());
+ Disconnect(connection.get());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ConnectFailsCharacteristicsNotFound) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ ConnectGatt(connection.get());
+
+ EXPECT_CALL(*rx_characteristic_, StartNotifySession(_, _)).Times(0);
+ EXPECT_FALSE(characteristics_finder_success_callback_.is_null());
+ ASSERT_FALSE(characteristics_finder_error_callback_.is_null());
+
+ characteristics_finder_error_callback_.Run(
+ {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());
+ ConnectGatt(connection.get());
+ CharacteristicsFound(connection.get());
+
+ EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic(_, _, _)).Times(0);
+ EXPECT_FALSE(notify_session_success_callback_.is_null());
+ ASSERT_FALSE(notify_session_error_callback_.is_null());
+
+ notify_session_error_callback_.Run(
+ device::BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ConnectFailsErrorSendingConnectionRequest) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ ConnectGatt(connection.get());
+ CharacteristicsFound(connection.get());
+ NotifySessionStarted(connection.get());
+
+ // |connection| will call WriteRemoteCharacteristics(_,_) to try to send the
+ // message |kMaxNumberOfTries| times. There is alredy one EXPECTA_CALL for
+ // WriteRemoteCharacteristic(_,_,_) in NotifySessionStated, that's why we use
+ // |kMaxNumberOfTries-1| in the EXPECT_CALL statement.
+ EXPECT_EQ(0, connection_observer_.GetNumSendCompleted());
+ EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic(_, _, _))
+ .Times(kMaxNumberOfTries - 1)
+ .WillRepeatedly(
+ DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
+ SaveArg<1>(&write_remote_characteristic_success_callback_),
+ SaveArg<2>(&write_remote_characteristic_error_callback_)));
+
+ for (int i = 0; i < kMaxNumberOfTries; i++) {
+ EXPECT_EQ(last_value_written_on_tx_characteristic_, kConnectionRequest);
+ ASSERT_FALSE(write_remote_characteristic_error_callback_.is_null());
+ EXPECT_FALSE(write_remote_characteristic_success_callback_.is_null());
+ write_remote_characteristic_error_callback_.Run(
+ device::BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
+ }
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ReceiveMessageSmallerThanCharacteristicSize) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ InitializeConnection(connection.get(), kDefaultMaxPacketSize);
+
+ std::string received_bytes;
+ EXPECT_CALL(*connection, OnBytesReceived(_))
+ .WillOnce(SaveArg<0>(&received_bytes));
+
+ connection->GattCharacteristicValueChanged(
+ adapter_.get(), rx_characteristic_.get(), kSmallPackets0);
+
+ EXPECT_EQ(received_bytes, kSmallMessage);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ReceiveMessageLargerThanCharacteristicSize) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+
+ InitializeConnection(connection.get(), kLargeMaxPacketSize);
+
+ std::string received_bytes;
+ EXPECT_CALL(*connection, OnBytesReceived(_))
+ .WillOnce(SaveArg<0>(&received_bytes));
+
+ std::vector<Packet> packets = kLargePackets;
+
+ for (auto packet : packets) {
+ connection->GattCharacteristicValueChanged(
+ adapter_.get(), rx_characteristic_.get(), packet);
+ }
+ EXPECT_EQ(received_bytes, kLargeMessage);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ SendMessageSmallerThanCharacteristicSize) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ InitializeConnection(connection.get(), kDefaultMaxPacketSize);
+
+ // Expecting a first call of WriteRemoteCharacteristic, after SendMessage is
+ // called.
+ 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();
+
+ EXPECT_EQ(1, connection_observer_.GetNumSendCompleted());
+ EXPECT_EQ(kSmallMessage, connection_observer_.GetLastDeserializedMessage());
+ EXPECT_TRUE(connection_observer_.GetLastSendSuccess());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ SendMessageLargerThanCharacteristicSize) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+
+ InitializeConnection(connection.get(), kLargeMaxPacketSize);
+
+ // Expecting a first call of WriteRemoteCharacteristic, after SendMessage is
+ // called.
+ 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>(kLargeMessage, kTestFeature));
+
+ EXPECT_EQ(last_value_written_on_tx_characteristic_, kLargePackets0);
+ std::vector<uint8_t> bytes_received(
+ last_value_written_on_tx_characteristic_.begin() + 1,
+ last_value_written_on_tx_characteristic_.end());
+
+ 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_)));
+
+ RunWriteCharacteristicSuccessCallback();
+ bytes_received.insert(bytes_received.end(),
+ last_value_written_on_tx_characteristic_.begin() + 1,
+ last_value_written_on_tx_characteristic_.end());
+
+ std::vector<uint8_t> expected(kLargeMessage.begin(), kLargeMessage.end());
+ EXPECT_EQ(expected, bytes_received);
+
+ RunWriteCharacteristicSuccessCallback();
+
+ EXPECT_EQ(1, connection_observer_.GetNumSendCompleted());
+ EXPECT_EQ(kLargeMessage, connection_observer_.GetLastDeserializedMessage());
+ EXPECT_TRUE(connection_observer_.GetLastSendSuccess());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ SendMessageKeepsFailing) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ InitializeConnection(connection.get(), kDefaultMaxPacketSize);
+
+ EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic(_, _, _))
+ .Times(kMaxNumberOfTries)
+ .WillRepeatedly(
+ 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));
+
+ for (int i = 0; i < kMaxNumberOfTries; i++) {
+ EXPECT_EQ(last_value_written_on_tx_characteristic_, kSmallPackets0);
+ ASSERT_FALSE(write_remote_characteristic_error_callback_.is_null());
+ EXPECT_FALSE(write_remote_characteristic_success_callback_.is_null());
+ write_remote_characteristic_error_callback_.Run(
+ device::BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
+ if (i == kMaxNumberOfTries - 1) {
+ EXPECT_EQ(1, connection_observer_.GetNumSendCompleted());
+ EXPECT_EQ(kSmallMessage,
+ connection_observer_.GetLastDeserializedMessage());
+ EXPECT_FALSE(connection_observer_.GetLastSendSuccess());
+ } else {
+ EXPECT_EQ(0, connection_observer_.GetNumSendCompleted());
+ }
+ }
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ReceiveCloseConnectionTest) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+ InitializeConnection(connection.get(), kDefaultMaxPacketSize);
+
+ connection->GattCharacteristicValueChanged(
+ adapter_.get(), rx_characteristic_.get(), kConnectionCloseUnknownError);
+
+ EXPECT_EQ(receiver_factory_->GetMostRecentInstance()->GetReasonForClose(),
+ ReasonForClose::UNKNOWN_ERROR);
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ReceiverErrorTest) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+
+ InitializeConnection(connection.get(), 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->GattCharacteristicValueChanged(
+ adapter_.get(), rx_characteristic_.get(), kErroneousPacket);
+
+ EXPECT_EQ(last_value_written_on_tx_characteristic_,
+ kConnectionCloseApplicationError);
+ EXPECT_EQ(receiver_factory_->GetMostRecentInstance()->GetReasonToClose(),
+ ReasonForClose::APPLICATION_ERROR);
+
+ RunWriteCharacteristicSuccessCallback();
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ReceiverErrorWithPendingWritesTest) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+
+ InitializeConnection(connection.get(), kLargeMaxPacketSize);
+
+ 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>(kLargeMessage, kTestFeature));
+
+ connection->GattCharacteristicValueChanged(
+ adapter_.get(), rx_characteristic_.get(), kErroneousPacket);
+
+ EXPECT_EQ(last_value_written_on_tx_characteristic_, kLargePackets0);
+
+ 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_)));
+
+ RunWriteCharacteristicSuccessCallback();
+
+ EXPECT_EQ(last_value_written_on_tx_characteristic_,
+ kConnectionCloseApplicationError);
+ EXPECT_EQ(receiver_factory_->GetMostRecentInstance()->GetReasonToClose(),
+ ReasonForClose::APPLICATION_ERROR);
+
+ RunWriteCharacteristicSuccessCallback();
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ WriteConnectionCloseMaxNumberOfTimes) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+
+ InitializeConnection(connection.get(), kDefaultMaxPacketSize);
+ EXPECT_EQ(connection->sub_status(), SubStatus::CONNECTED);
+
+ 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->Disconnect();
+ EXPECT_EQ(connection->sub_status(), SubStatus::CONNECTED);
+
+ for (int i = 0; i < kMaxNumberOfTries; i++) {
+ EXPECT_EQ(last_value_written_on_tx_characteristic_,
+ kConnectionCloseSuccess);
+ ASSERT_FALSE(write_remote_characteristic_error_callback_.is_null());
+ EXPECT_FALSE(write_remote_characteristic_success_callback_.is_null());
+
+ if (i != kMaxNumberOfTries - 1) {
+ 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_)));
+ }
+
+ write_remote_characteristic_error_callback_.Run(
+ device::BluetoothRemoteGattService::GATT_ERROR_UNKNOWN);
+ }
+
+ EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
+ EXPECT_EQ(connection->status(), Connection::DISCONNECTED);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
+ ConnectAfterADelayWhenThrottled) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection());
+
+ EXPECT_CALL(*bluetooth_throttler_, GetDelay())
+ .WillOnce(Return(base::TimeDelta(base::TimeDelta::FromSeconds(1))));
+ EXPECT_CALL(*device_, CreateGattConnection(_, _))
+ .WillOnce(DoAll(SaveArg<0>(&create_gatt_connection_success_callback_),
+ SaveArg<1>(&create_gatt_connection_error_callback_)));
+
+ // No GATT connection should be created before the delay.
+ connection->Connect();
+ EXPECT_EQ(connection->sub_status(), SubStatus::WAITING_GATT_CONNECTION);
+ EXPECT_EQ(connection->status(), Connection::IN_PROGRESS);
+ EXPECT_TRUE(create_gatt_connection_error_callback_.is_null());
+ EXPECT_TRUE(create_gatt_connection_success_callback_.is_null());
+
+ // A GATT connection should be created after the delay.
+ task_runner_->RunUntilIdle();
+ EXPECT_FALSE(create_gatt_connection_error_callback_.is_null());
+ ASSERT_FALSE(create_gatt_connection_success_callback_.is_null());
+
+ // Preparing |connection| to run |create_gatt_connection_success_callback_|.
+ EXPECT_CALL(*connection, CreateCharacteristicsFinder(_, _))
+ .WillOnce(DoAll(
+ SaveArg<0>(&characteristics_finder_success_callback_),
+ SaveArg<1>(&characteristics_finder_error_callback_),
+ Return(new NiceMock<MockBluetoothLowEnergyCharacteristicsFinder>)));
+
+ create_gatt_connection_success_callback_.Run(
+ base::MakeUnique<NiceMock<device::MockBluetoothGattConnection>>(
+ adapter_, kTestRemoteDeviceBluetoothAddress));
+
+ CharacteristicsFound(connection.get());
+ NotifySessionStarted(connection.get());
+ ConnectionResponseReceived(connection.get(), kDefaultMaxPacketSize);
+}
+
+} // namespace weave
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_defines.h b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_defines.h
new file mode 100644
index 00000000000..e1d561d1fad
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_defines.h
@@ -0,0 +1,55 @@
+// 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_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_DEFINES_H_
+#define COMPONENTS_CRYPTAUTH_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_DEFINES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+namespace cryptauth {
+namespace weave {
+
+enum PacketType { DATA = 0x00, CONTROL = 0x01 };
+
+// Identify the action intended by the control packet.
+enum ControlCommand {
+ CONNECTION_REQUEST = 0x00,
+ CONNECTION_RESPONSE = 0x01,
+ CONNECTION_CLOSE = 0x02
+};
+
+// Sent with the ConnectionClose control packet.
+// Identify why the client/server wished to close the connection.
+enum ReasonForClose {
+ CLOSE_WITHOUT_ERROR = 0x00,
+ UNKNOWN_ERROR = 0x01,
+ NO_COMMON_VERSION_SUPPORTED = 0x02,
+ RECEIVED_PACKET_OUT_OF_SEQUENCE = 0x03,
+ APPLICATION_ERROR = 0x80
+};
+
+typedef std::vector<uint8_t> Packet;
+
+const uint16_t kMinConnectionRequestSize = 7;
+const uint16_t kMinConnectionResponseSize = 5;
+const uint16_t kMaxConnectionCloseSize = 3;
+const uint16_t kDefaultMaxPacketSize = 20;
+const uint16_t kWeaveVersion = 1;
+// Defer selecting the max packet size to the server.
+const uint16_t kSelectMaxPacketSize = 0;
+const uint8_t kMaxPacketCounter = 8;
+
+// Used only for tests.
+const uint8_t kByteDefaultMaxPacketSize = 20;
+const uint8_t kByteWeaveVersion = 1;
+const uint8_t kByteSelectMaxPacketSize = 0;
+const uint8_t kEmptyUpperByte = 0;
+
+} // namespace weave
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_DEFINES_H_
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
new file mode 100644
index 00000000000..164a47b65de
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.cc
@@ -0,0 +1,201 @@
+// 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/ble/bluetooth_low_energy_weave_packet_generator.h"
+
+#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.
+std::shared_ptr<BluetoothLowEnergyWeavePacketGenerator::Factory>
+ BluetoothLowEnergyWeavePacketGenerator::Factory::factory_instance_ =
+ nullptr;
+
+// static.
+std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator>
+BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance() {
+ if (!factory_instance_) {
+ factory_instance_.reset(new Factory());
+ }
+ return factory_instance_->BuildInstance();
+}
+
+// static.
+void BluetoothLowEnergyWeavePacketGenerator::Factory::SetInstanceForTesting(
+ std::shared_ptr<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) {}
+
+Packet BluetoothLowEnergyWeavePacketGenerator::CreateConnectionRequest() {
+ Packet packet(kMinConnectionRequestSize, 0);
+
+ SetPacketTypeBit(PacketType::CONTROL, &packet);
+ // Since it only make sense for connection request to be the 0th packet,
+ // resets the packet counter.
+ next_packet_counter_ = 1;
+ SetControlCommand(ControlCommand::CONNECTION_REQUEST, &packet);
+ SetShortField(1, kWeaveVersion, &packet);
+ SetShortField(3, kWeaveVersion, &packet);
+ SetShortField(5, kSelectMaxPacketSize, &packet);
+
+ return packet;
+}
+
+Packet BluetoothLowEnergyWeavePacketGenerator::CreateConnectionResponse() {
+ Packet packet(kMinConnectionResponseSize, 0);
+
+ SetPacketTypeBit(PacketType::CONTROL, &packet);
+ // Since it only make sense for connection response to be the 0th packet,
+ // resets the next packet counter.
+ next_packet_counter_ = 1;
+ SetControlCommand(ControlCommand::CONNECTION_RESPONSE, &packet);
+ SetShortField(1, kWeaveVersion, &packet);
+ SetShortField(3, max_packet_size_, &packet);
+
+ return packet;
+}
+
+Packet BluetoothLowEnergyWeavePacketGenerator::CreateConnectionClose(
+ ReasonForClose reason_for_close) {
+ Packet packet(kMaxConnectionCloseSize, 0);
+
+ SetPacketTypeBit(PacketType::CONTROL, &packet);
+ SetPacketCounter(&packet);
+ SetControlCommand(ControlCommand::CONNECTION_CLOSE, &packet);
+ SetShortField(1, reason_for_close, &packet);
+
+ return packet;
+}
+
+void BluetoothLowEnergyWeavePacketGenerator::SetMaxPacketSize(uint16_t size) {
+ DCHECK(size >= kDefaultMaxPacketSize);
+ max_packet_size_ = size;
+}
+
+std::vector<Packet> BluetoothLowEnergyWeavePacketGenerator::EncodeDataMessage(
+ std::string message) {
+ DCHECK(!message.empty());
+
+ // The first byte of a packet is used by the uWeave protocol,
+ // hence the payload is 1 byte smaller than the packet size.
+ uint32_t packet_payload_size = max_packet_size_ - 1;
+
+ uint32_t message_length = message.length();
+ // (packet_payload_size - 1) is used to enforce rounding up.
+ uint32_t num_packets =
+ (message_length + (packet_payload_size - 1)) / packet_payload_size;
+
+ std::vector<Packet> weave_message(num_packets);
+
+ const char* byte_message = message.c_str();
+
+ for (uint32_t i = 0; i < num_packets; ++i) {
+ Packet* packet = &weave_message[i];
+ uint32_t begin = packet_payload_size * i;
+ uint32_t end = std::min(begin + packet_payload_size, message_length);
+
+ packet->push_back(0);
+
+ SetPacketTypeBit(PacketType::DATA, packet);
+ SetPacketCounter(packet);
+
+ for (uint32_t j = begin; j < end; ++j) {
+ packet->push_back(byte_message[j]);
+ }
+ }
+
+ // Guaranteed to have at least one packet since message is not empty.
+ SetDataFirstBit(&weave_message[0]);
+ SetDataLastBit(&weave_message[num_packets - 1]);
+
+ return weave_message;
+}
+
+void BluetoothLowEnergyWeavePacketGenerator::SetShortField(uint32_t byte_offset,
+ uint16_t val,
+ Packet* packet) {
+ DCHECK(packet);
+ DCHECK_LT(byte_offset, packet->size());
+ DCHECK_LT(byte_offset + 1, packet->size());
+
+ uint16_t network_val = htons(val);
+ uint8_t* network_val_ptr = reinterpret_cast<uint8_t*>(&network_val);
+
+ packet->at(byte_offset) = network_val_ptr[0];
+ packet->at(byte_offset + 1) = network_val_ptr[1];
+}
+
+void BluetoothLowEnergyWeavePacketGenerator::SetPacketTypeBit(PacketType type,
+ Packet* packet) {
+ DCHECK(packet);
+ DCHECK(!packet->empty());
+
+ // Type bit is the highest bit of the first byte of the packet.
+ // So clear the highest bit and set it according to val.
+ packet->at(0) = (packet->at(0) & 0x7F) | (type << 7);
+}
+
+void BluetoothLowEnergyWeavePacketGenerator::SetControlCommand(
+ ControlCommand command,
+ Packet* packet) {
+ DCHECK(packet);
+ DCHECK(!packet->empty());
+
+ // Control Command is the lower 4 bits of the packet's first byte.
+ // So clear the lower 4 bites and set it according to val.
+ packet->at(0) = (packet->at(0) & 0xF0) | command;
+}
+
+void BluetoothLowEnergyWeavePacketGenerator::SetPacketCounter(Packet* packet) {
+ DCHECK(packet);
+ DCHECK(!packet->empty());
+ uint8_t counter = next_packet_counter_ % kMaxPacketCounter;
+
+ // Packet counter is the bits 4, 5, and 6 of the packet's first byte.
+ // So clear those bits and set them according to current packet counter
+ // modular max packet counter.
+ packet->at(0) = (packet->at(0) & 0x8F) | (counter << 4);
+ next_packet_counter_++;
+}
+
+void BluetoothLowEnergyWeavePacketGenerator::SetDataFirstBit(Packet* packet) {
+ DCHECK(packet);
+ DCHECK(!packet->empty());
+
+ // First bit is bit 3 of the packet's first byte and set it to 1.
+ packet->at(0) = packet->at(0) | (1 << 3);
+}
+
+void BluetoothLowEnergyWeavePacketGenerator::SetDataLastBit(Packet* packet) {
+ DCHECK(packet);
+ DCHECK(!packet->empty());
+
+ // Last bit is the bit 2 of the packet's first byte and set it to 1.
+ packet->at(0) = packet->at(0) | (1 << 2);
+}
+
+} // namespace weave
+
+} // namespace cryptauth
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
new file mode 100644
index 00000000000..b59cfa43187
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator.h
@@ -0,0 +1,74 @@
+// 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_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_PACKET_GENERATOR_H_
+#define COMPONENTS_CRYPTAUTH_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_PACKET_GENERATOR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/cryptauth/ble/bluetooth_low_energy_weave_defines.h"
+#include "build/build_config.h"
+
+namespace cryptauth {
+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(std::shared_ptr<Factory> factory);
+
+ protected:
+ // Exposed for testing.
+ virtual std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator>
+ BuildInstance();
+
+ private:
+ static std::shared_ptr<Factory> factory_instance_;
+ };
+
+ virtual Packet CreateConnectionRequest();
+ virtual Packet CreateConnectionResponse();
+ virtual Packet CreateConnectionClose(ReasonForClose reason_for_close);
+
+ // Packet size must be greater than or equal to 20.
+ virtual void SetMaxPacketSize(uint16_t size);
+
+ // 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);
+ void SetControlCommand(ControlCommand val, Packet* packet);
+ void SetPacketCounter(Packet* packet);
+ void SetDataFirstBit(Packet* packet);
+ void SetDataLastBit(Packet* packet);
+
+ // The default max packet length is 20 unless SetDataPacketLength() is called
+ // and specified otherwise.
+ uint16_t max_packet_size_;
+
+ // Counter for the number of packets sent, starting at 0.
+ uint8_t next_packet_counter_;
+};
+
+} // namespace weave
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_PACKET_GENERATOR_H_
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
new file mode 100644
index 00000000000..7e1b4ed56ae
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_generator_unittest.cc
@@ -0,0 +1,264 @@
+// 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/ble/bluetooth_low_energy_weave_packet_generator.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cryptauth {
+namespace weave {
+
+class CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest
+ : public testing::Test {
+ protected:
+ CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest() {}
+
+ void TestConnectionCloseWithReason(ReasonForClose reason_for_close,
+ uint8_t expected_reason_for_close) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
+ BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+
+ Packet packet = generator->CreateConnectionClose(reason_for_close);
+
+ const uint16_t kCloseSize = 3;
+ Packet expected(kCloseSize, 0);
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -000 ---- : counter = 0
+ // ---- 0010 : command = 2 (close)
+ // 1000 0010 = 0x82
+ expected = {0x82, kEmptyUpperByte, expected_reason_for_close};
+
+ EXPECT_EQ(expected, packet);
+ }
+
+ uint8_t GetCounterFromHeader(uint8_t header) { return (header >> 4) & 7; }
+
+ uint8_t GetPacketType(uint8_t header) { return (header >> 7) & 1; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(
+ CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest);
+};
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
+ CreateConnectionRequestTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
+ BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+
+ Packet packet = generator->CreateConnectionRequest();
+
+ const uint16_t kRequestSize = 7;
+ Packet expected(kRequestSize, 0);
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -000 ---- : counter = 0
+ // ---- 0000 : command = 0 (request)
+ // 1000 0000 = 0x80
+ expected = {0x80,
+ kEmptyUpperByte,
+ kByteWeaveVersion,
+ kEmptyUpperByte,
+ kByteWeaveVersion,
+ kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+
+ EXPECT_EQ(expected, packet);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
+ CreateConnectionResponseWithDefaultPacketSizeTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
+ BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+
+ Packet packet = generator->CreateConnectionResponse();
+
+ const uint16_t kResponseSize = 5;
+ Packet expected_default(kResponseSize, 0);
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -000 ---- : counter = 0
+ // ---- 0001 : command = 1 (response)
+ // 1000 0001 = 0x81
+ expected_default = {0x81, kEmptyUpperByte, kByteWeaveVersion, kEmptyUpperByte,
+ kByteDefaultMaxPacketSize};
+
+ EXPECT_EQ(expected_default, packet);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
+ CreateConnectionResponseWithSelectedPacketSizeTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
+ BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+
+ const uint8_t kSelectedPacketSize = 30;
+ const uint16_t kResponseSize = 5;
+
+ generator->SetMaxPacketSize(kSelectedPacketSize);
+
+ Packet packet = generator->CreateConnectionResponse();
+
+ Packet expected_selected(kResponseSize, 0);
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -000 ---- : counter = 0
+ // ---- 0001 : command = 1 (response)
+ // 1000 0001 = 0x81
+ expected_selected = {0x81, kEmptyUpperByte, kByteWeaveVersion,
+ kEmptyUpperByte, kSelectedPacketSize};
+ EXPECT_EQ(expected_selected, packet);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
+ CreateConnectionCloseTest) {
+ // Reason for close spec of uWeave.
+ // 0x00: Close without error
+ // 0x01: Unknown error
+ // 0x02: No common version supported
+ // 0x03: Received packet out of sequence
+ // 0x80: Application error
+
+ TestConnectionCloseWithReason(ReasonForClose::CLOSE_WITHOUT_ERROR, 0x00);
+ TestConnectionCloseWithReason(ReasonForClose::UNKNOWN_ERROR, 0x01);
+ TestConnectionCloseWithReason(ReasonForClose::NO_COMMON_VERSION_SUPPORTED,
+ 0x02);
+ TestConnectionCloseWithReason(ReasonForClose::RECEIVED_PACKET_OUT_OF_SEQUENCE,
+ 0x03);
+ TestConnectionCloseWithReason(ReasonForClose::APPLICATION_ERROR, 0x80);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
+ EncodeDataMessageWithDefaultPacketSizeTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
+ BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+
+ std::string data = "abcdefghijklmnopqrstuvwxyz";
+
+ std::vector<Packet> packets = generator->EncodeDataMessage(data);
+
+ std::vector<Packet> expected(2);
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -000 ---- : counter = 0
+ // ---- 1--- : first packet = true
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0000 1000 = 0x08
+ expected[0] = {0x08, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+ 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's'};
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 0--- : first packet = false
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 0100 = 0x14
+ expected[1] = {0x14, 't', 'u', 'v', 'w', 'x', 'y', 'z'};
+
+ EXPECT_EQ(expected, packets);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
+ EncodeDataMessageWithSelectedPacketSizeTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
+ BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+
+ const uint32_t packet_size = 30;
+ const uint32_t residual_packet_size = 2;
+ std::string a(packet_size - 1, 'a');
+ std::string b(packet_size - 1, 'b');
+ std::string c(residual_packet_size - 1, 'c');
+
+ std::string data = a + b + c;
+
+ generator->SetMaxPacketSize(packet_size);
+
+ std::vector<Packet> packets = generator->EncodeDataMessage(data);
+
+ std::vector<Packet> expected(3);
+
+ expected[0].assign(packet_size, 'a');
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -000 ---- : counter = 0
+ // ---- 1--- : first packet = true
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0000 1000 = 0x08
+ expected[0][0] = 0x08;
+
+ expected[1].assign(packet_size, 'b');
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 0--- : first packet = false
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 0000 = 0x10
+ expected[1][0] = 0x10;
+
+ expected[2].assign(residual_packet_size, 'c');
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -010 ---- : counter = 2
+ // ---- 0--- : first packet = false
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0010 0100 = 0x24
+ expected[2][0] = 0x24;
+
+ EXPECT_EQ(expected, packets);
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
+ PacketCounterForMixedPacketTypesTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
+ BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+
+ Packet packet = generator->CreateConnectionRequest();
+
+ EXPECT_EQ(0, GetCounterFromHeader(packet[0]));
+
+ std::string data = "a";
+ std::vector<Packet> packets = generator->EncodeDataMessage(data);
+
+ EXPECT_EQ(1, GetCounterFromHeader(packets[0][0]));
+
+ packet = generator->CreateConnectionClose(ReasonForClose::UNKNOWN_ERROR);
+
+ EXPECT_EQ(2, GetCounterFromHeader(packet[0]));
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketGeneratorTest,
+ PacketCounterWrappedAroundTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> generator =
+ BluetoothLowEnergyWeavePacketGenerator::Factory::NewInstance();
+
+ const uint8_t kNumPackets = 100;
+ std::string data(kNumPackets * kByteDefaultMaxPacketSize, 'a');
+
+ std::vector<Packet> packets = generator->EncodeDataMessage(data);
+
+ std::vector<Packet> expected(kNumPackets);
+
+ const uint8_t kDataType = 0;
+
+ for (uint8_t i = 0; i < kNumPackets; ++i) {
+ uint8_t header = packets[i][0];
+ EXPECT_EQ(i % kMaxPacketCounter, GetCounterFromHeader(header));
+ EXPECT_EQ(kDataType, GetPacketType(header));
+ }
+}
+
+} // namespace weave
+
+} // namespace cryptauth
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
new file mode 100644
index 00000000000..c29bb1a393f
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.cc
@@ -0,0 +1,451 @@
+// 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/ble/bluetooth_low_energy_weave_packet_receiver.h"
+
+#ifdef OS_WIN
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#endif
+
+#include "components/proximity_auth/logging/logging.h"
+
+namespace cryptauth {
+namespace weave {
+namespace {
+
+const uint16_t kMaxInitControlPacketSize = 20;
+const uint16_t kMaxPacketSizeLowerBound = 20;
+
+} // namespace
+
+std::shared_ptr<BluetoothLowEnergyWeavePacketReceiver::Factory>
+ BluetoothLowEnergyWeavePacketReceiver::Factory::factory_instance_ = nullptr;
+
+// static
+std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver>
+BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType receiver_type) {
+ if (!factory_instance_) {
+ factory_instance_.reset(new Factory());
+ }
+ return factory_instance_->BuildInstance(receiver_type);
+}
+
+// static
+void BluetoothLowEnergyWeavePacketReceiver::Factory::SetInstanceForTesting(
+ std::shared_ptr<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),
+ next_packet_counter_(0),
+ state_(State::CONNECTING),
+ reason_for_close_(ReasonForClose::CLOSE_WITHOUT_ERROR),
+ reason_to_close_(ReasonForClose::CLOSE_WITHOUT_ERROR),
+ receiver_error_(ReceiverError::NO_ERROR_DETECTED) {
+ SetMaxPacketSize(kMaxPacketSizeLowerBound);
+}
+
+BluetoothLowEnergyWeavePacketReceiver::
+ ~BluetoothLowEnergyWeavePacketReceiver() {}
+
+BluetoothLowEnergyWeavePacketReceiver::State
+BluetoothLowEnergyWeavePacketReceiver::GetState() {
+ return state_;
+}
+
+uint16_t BluetoothLowEnergyWeavePacketReceiver::GetMaxPacketSize() {
+ // max_packet_size_ is well defined in every state.
+ return max_packet_size_;
+}
+
+ReasonForClose BluetoothLowEnergyWeavePacketReceiver::GetReasonForClose() {
+ DCHECK(state_ == State::CONNECTION_CLOSED);
+ return reason_for_close_;
+}
+
+ReasonForClose BluetoothLowEnergyWeavePacketReceiver::GetReasonToClose() {
+ DCHECK(state_ == State::ERROR_DETECTED);
+ return reason_to_close_;
+}
+
+std::string BluetoothLowEnergyWeavePacketReceiver::GetDataMessage() {
+ DCHECK(state_ == State::DATA_READY);
+ return std::string(data_message_.begin(), data_message_.end());
+}
+
+BluetoothLowEnergyWeavePacketReceiver::ReceiverError
+BluetoothLowEnergyWeavePacketReceiver::GetReceiverError() {
+ return receiver_error_;
+}
+
+BluetoothLowEnergyWeavePacketReceiver::State
+BluetoothLowEnergyWeavePacketReceiver::ReceivePacket(const Packet& packet) {
+ if (state_ == State::ERROR_DETECTED) {
+ PA_LOG(ERROR) << "Received message in ERROR state.";
+ } else if (packet.empty()) {
+ PA_LOG(ERROR) << "Received empty packet. Empty packet is not a valid uWeave"
+ << " packet.";
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::EMPTY_PACKET);
+ } else {
+ VerifyPacketCounter(packet);
+
+ switch (state_) {
+ case State::CONNECTING:
+ ReceiveFirstPacket(packet);
+ break;
+ case State::WAITING:
+ case State::RECEIVING_DATA:
+ ReceiveNonFirstPacket(packet);
+ break;
+ case State::DATA_READY:
+ data_message_.clear();
+ ReceiveNonFirstPacket(packet);
+ break;
+ case State::CONNECTION_CLOSED:
+ PA_LOG(ERROR) << "Received message in ConnectionClosed state.";
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::RECEIVED_PACKET_IN_CONNECTION_CLOSED);
+ break;
+ case State::ERROR_DETECTED:
+ // Counter not verified.
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+ return state_;
+}
+
+void BluetoothLowEnergyWeavePacketReceiver::ReceiveFirstPacket(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+ DCHECK(state_ == State::CONNECTING);
+
+ if (GetPacketType(packet) != PacketType::CONTROL) {
+ PA_LOG(ERROR) << "Received data packets when not connected.";
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::RECEIVED_DATA_IN_CONNECTING);
+ return;
+ }
+
+ uint8_t command = GetControlCommand(packet);
+ switch (command) {
+ case ControlCommand::CONNECTION_REQUEST:
+ if (receiver_type_ == ReceiverType::SERVER) {
+ ReceiveConnectionRequest(packet);
+ } else {
+ PA_LOG(ERROR) << "Server received connection response instead of "
+ << "request.";
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::SERVER_RECEIVED_CONNECTION_RESPONSE);
+ }
+ break;
+ case ControlCommand::CONNECTION_RESPONSE:
+ if (receiver_type_ == ReceiverType::CLIENT) {
+ ReceiveConnectionResponse(packet);
+ } else {
+ PA_LOG(ERROR) << "Client received connection request instead of "
+ << "response.";
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::CLIENT_RECEIVED_CONNECTION_REQUEST);
+ }
+ break;
+ case ControlCommand::CONNECTION_CLOSE:
+ PA_LOG(ERROR) << "Received connection close when not even connected.";
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::RECEIVED_CONNECTION_CLOSE_IN_CONNECTING);
+ break;
+ default:
+ PA_LOG(ERROR) << "Received unrecognized control packet command: "
+ << std::to_string(command);
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::UNRECOGNIZED_CONTROL_COMMAND);
+ break;
+ }
+}
+
+void BluetoothLowEnergyWeavePacketReceiver::ReceiveNonFirstPacket(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+
+ uint8_t command;
+ bool expect_first_packet = state_ != State::RECEIVING_DATA;
+
+ switch (GetPacketType(packet)) {
+ case PacketType::CONTROL:
+ command = GetControlCommand(packet);
+ if (command == ControlCommand::CONNECTION_CLOSE) {
+ ReceiveConnectionClose(packet);
+ } else {
+ PA_LOG(ERROR) << "Received invalid command " << std::to_string(command)
+ << " during data transaction";
+ MoveToErrorState(
+ ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::INVALID_CONTROL_COMMAND_IN_DATA_TRANSACTION);
+ }
+ break;
+ case PacketType::DATA:
+ if (packet.size() > GetConceptualMaxPacketSize()) {
+ PA_LOG(ERROR) << "Received packet with size: " << packet.size()
+ << ". It is greater than maximum packet size "
+ << GetConceptualMaxPacketSize();
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::INVALID_DATA_PACKET_SIZE);
+ } else if (!AreLowerTwoBitsCleared(packet)) {
+ PA_LOG(ERROR) << "Lower two bits of data packet header are not clear "
+ << "as expected.";
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::DATA_HEADER_LOW_BITS_NOT_CLEARED);
+ } else if (expect_first_packet != IsFirstDataPacket(packet)) {
+ PA_LOG(ERROR) << "First bit of data packet is set incorrectly to: "
+ << IsFirstDataPacket(packet);
+ MoveToErrorState(ReasonForClose::RECEIVED_PACKET_OUT_OF_SEQUENCE,
+ ReceiverError::INCORRECT_DATA_FIRST_BIT);
+ } else {
+ AppendData(packet, 1);
+ if (IsLastDataPacket(packet)) {
+ state_ = State::DATA_READY;
+ } else {
+ state_ = State::RECEIVING_DATA;
+ }
+ }
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+void BluetoothLowEnergyWeavePacketReceiver::ReceiveConnectionRequest(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+ DCHECK(state_ == State::CONNECTING);
+
+ if (packet.size() < kMinConnectionRequestSize ||
+ packet.size() > kMaxInitControlPacketSize) {
+ PA_LOG(ERROR) << "Received invalid connection request packet size: "
+ << packet.size();
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::INVALID_CONNECTION_REQUEST_SIZE);
+ return;
+ }
+
+ uint16_t packet_size = GetShortField(packet, 5);
+ // Packet size of 0 means the server can observe the ATT_MTU and select an
+ // appropriate packet size;
+ if (packet_size != kSelectMaxPacketSize &&
+ packet_size < kMaxPacketSizeLowerBound) {
+ PA_LOG(ERROR) << "Received requested max packet size of: " << packet_size
+ << ". Client must support at least "
+ << kMaxPacketSizeLowerBound << " bytes per packet.";
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::INVALID_REQUESTED_MAX_PACKET_SIZE);
+ return;
+ }
+ SetMaxPacketSize(packet_size);
+
+ uint16_t min_version = GetShortField(packet, 1);
+ uint16_t max_version = GetShortField(packet, 3);
+ if (kWeaveVersion < min_version || kWeaveVersion > max_version) {
+ PA_LOG(ERROR) << "Server does not support client version range.";
+ MoveToErrorState(ReasonForClose::NO_COMMON_VERSION_SUPPORTED,
+ ReceiverError::NOT_SUPPORTED_REQUESTED_VERSION);
+ return;
+ }
+
+ if (packet.size() > kMinConnectionRequestSize) {
+ AppendData(packet, kMinConnectionRequestSize);
+ state_ = State::DATA_READY;
+ } else {
+ state_ = State::WAITING;
+ }
+}
+
+void BluetoothLowEnergyWeavePacketReceiver::ReceiveConnectionResponse(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+ DCHECK(state_ == State::CONNECTING);
+
+ if (packet.size() < kMinConnectionResponseSize ||
+ packet.size() > kMaxInitControlPacketSize) {
+ PA_LOG(ERROR) << "Received invalid connection response packet size: "
+ << packet.size();
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::INVALID_CONNECTION_RESPONSE_SIZE);
+ return;
+ }
+
+ uint16_t selected_packet_size = GetShortField(packet, 3);
+ if (selected_packet_size < kMaxPacketSizeLowerBound) {
+ PA_LOG(ERROR) << "Received selected max packet size of: "
+ << selected_packet_size << ". Server must support at least "
+ << kMaxPacketSizeLowerBound << " bytes per packet.";
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::INVALID_SELECTED_MAX_PACKET_SIZE);
+ return;
+ }
+ SetMaxPacketSize(selected_packet_size);
+
+ uint16_t selected_version = GetShortField(packet, 1);
+ if (selected_version != kWeaveVersion) {
+ PA_LOG(ERROR) << "Client does not support server selected version.";
+ MoveToErrorState(ReasonForClose::NO_COMMON_VERSION_SUPPORTED,
+ ReceiverError::NOT_SUPPORTED_SELECTED_VERSION);
+ return;
+ }
+
+ if (packet.size() > kMinConnectionResponseSize) {
+ AppendData(packet, kMinConnectionResponseSize);
+ state_ = State::DATA_READY;
+ } else {
+ state_ = State::WAITING;
+ }
+}
+
+void BluetoothLowEnergyWeavePacketReceiver::ReceiveConnectionClose(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+
+ uint16_t reason;
+
+ if (packet.size() > kMaxConnectionCloseSize) {
+ PA_LOG(ERROR) << "Received invalid connection close packet size: "
+ << packet.size();
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::INVALID_CONNECTION_CLOSE_SIZE);
+ return;
+ } else if (packet.size() < kMaxConnectionCloseSize) {
+ reason = ReasonForClose::UNKNOWN_ERROR;
+ } else {
+ reason = GetShortField(packet, 1);
+ }
+
+ switch (reason) {
+ case ReasonForClose::CLOSE_WITHOUT_ERROR:
+ case ReasonForClose::UNKNOWN_ERROR:
+ case ReasonForClose::NO_COMMON_VERSION_SUPPORTED:
+ case ReasonForClose::RECEIVED_PACKET_OUT_OF_SEQUENCE:
+ case ReasonForClose::APPLICATION_ERROR:
+ reason_for_close_ = static_cast<ReasonForClose>(reason);
+ state_ = State::CONNECTION_CLOSED;
+ break;
+ default:
+ PA_LOG(ERROR) << "Received invalid reason for close: " << reason;
+ MoveToErrorState(ReasonForClose::UNKNOWN_ERROR,
+ ReceiverError::UNRECOGNIZED_REASON_FOR_CLOSE);
+ break;
+ }
+}
+
+void BluetoothLowEnergyWeavePacketReceiver::AppendData(const Packet& packet,
+ uint32_t byte_offset) {
+ DCHECK(!packet.empty());
+
+ // Append to data_message_ bytes 1 through end of the packet.
+ data_message_.insert(data_message_.end(), packet.begin() + byte_offset,
+ packet.end());
+}
+
+uint16_t BluetoothLowEnergyWeavePacketReceiver::GetShortField(
+ const Packet& packet,
+ uint32_t byte_offset) {
+ DCHECK_LT(byte_offset, packet.size());
+ DCHECK_LT(byte_offset + 1, packet.size());
+
+ uint16_t received;
+ uint8_t* received_ptr = reinterpret_cast<uint8_t*>(&received);
+ received_ptr[0] = packet[byte_offset];
+ received_ptr[1] = packet[byte_offset + 1];
+
+ return ntohs(received);
+}
+
+uint8_t BluetoothLowEnergyWeavePacketReceiver::GetPacketType(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+ // Packet type is stored in the highest bit of the first byte.
+ return (packet[0] >> 7) & 1;
+}
+
+uint8_t BluetoothLowEnergyWeavePacketReceiver::GetControlCommand(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+ // Control command is stored in the lower 4 bits of the first byte.
+ return packet[0] & 0x0F;
+}
+
+void BluetoothLowEnergyWeavePacketReceiver::VerifyPacketCounter(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+ DCHECK(state_ != State::ERROR_DETECTED);
+
+ // Packet counter is bits 4, 5, and 6 of the first byte.
+ uint8_t count = (packet[0] >> 4) & 7;
+
+ if (count == (next_packet_counter_ % kMaxPacketCounter)) {
+ next_packet_counter_++;
+ } else {
+ PA_LOG(ERROR) << "Received invalid packet counter: "
+ << std::to_string(count);
+ MoveToErrorState(ReasonForClose::RECEIVED_PACKET_OUT_OF_SEQUENCE,
+ ReceiverError::PACKET_OUT_OF_SEQUENCE);
+ }
+}
+
+bool BluetoothLowEnergyWeavePacketReceiver::IsFirstDataPacket(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+ // Bit 3 determines whether the packet is the first packet of the message.
+ return (packet[0] >> 3) & 1;
+}
+
+bool BluetoothLowEnergyWeavePacketReceiver::IsLastDataPacket(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+ // Bit 2 determines whether the packet is the last packet of the message.
+ return (packet[0] >> 2) & 1;
+}
+
+bool BluetoothLowEnergyWeavePacketReceiver::AreLowerTwoBitsCleared(
+ const Packet& packet) {
+ DCHECK(!packet.empty());
+ return (packet[0] & 3) == 0;
+}
+
+void BluetoothLowEnergyWeavePacketReceiver::MoveToErrorState(
+ ReasonForClose reason_to_close,
+ ReceiverError receiver_error) {
+ state_ = State::ERROR_DETECTED;
+ reason_to_close_ = reason_to_close;
+ receiver_error_ = receiver_error;
+}
+
+void BluetoothLowEnergyWeavePacketReceiver::SetMaxPacketSize(
+ uint16_t packet_size) {
+ DCHECK(packet_size == kSelectMaxPacketSize ||
+ packet_size >= kMaxPacketSizeLowerBound);
+ max_packet_size_ = packet_size;
+}
+
+uint16_t BluetoothLowEnergyWeavePacketReceiver::GetConceptualMaxPacketSize() {
+ if (!max_packet_size_)
+ return kMaxPacketSizeLowerBound;
+ return max_packet_size_;
+}
+
+} // namespace weave
+
+} // namespace cryptauth
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
new file mode 100644
index 00000000000..147963f1ff7
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver.h
@@ -0,0 +1,222 @@
+// 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_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_PACKET_RECEIVER_H_
+#define COMPONENTS_CRYPTAUTH_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_PACKET_RECEIVER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "build/build_config.h"
+#include "components/cryptauth/ble/bluetooth_low_energy_weave_defines.h"
+
+namespace cryptauth {
+namespace weave {
+
+// Receive the messages sent with uWeave protocol.
+// Example Usage:
+// State state = ReceivePacket(packet);
+// switch (state) {
+// case ReceiverState::DATA_READY:
+// OnBytesReceived(GetDataMessage());
+// break;
+// case ReceiverState::CONNECTION_CLOSED:
+// Disconnect(GetReasonForClose());
+// break;
+// case ReceiverState::ERROR:
+// HandleError(GetReasonToClose());
+// break;
+// case ReceiverState::CONNECTING:
+// case ReceiverState::WAITING:
+// case ReceiverState::RECEIVING_DATA:
+// break;
+// default:
+// FoundABugInReceiver();
+// break;
+// }
+class BluetoothLowEnergyWeavePacketReceiver {
+ public:
+ enum ReceiverType { CLIENT, SERVER };
+
+ // CONNECTING:
+ // The connection hasn't been estabalished. Accept a CONNECTION_REQUEST if
+ // the receiver is a SERVER. Accept a CONNECTION_RESPONSE if the receiver is
+ // a CLIENT. All other packets cause the receiver to move into ERROR state.
+ // The state will transition to WAITING after a request/response if they
+ // do not have extra data. The state will transition to DATA_READY if the
+ // request/response have extra data since the extra data is treated as a
+ // complete data message. This state is never reentered.
+ // WAITING:
+ // The reciever is ready but doesn't have any data. It's waiting for packet
+ // to arrive. Will accept all but connection request/response packets. The
+ // first data packet will move the receiver to the RECEIVING_DATA state. A
+ // connection close packet will move the receiver to the CONNECTION_CLOSED
+ // state. This state is also never reentered.
+ // RECEIVING_DATA:
+ // The receiver is in middle of receiving a data message consisted of
+ // multiple packets. Will receive only data packets. The last data packet
+ // will move the receiver into DATA_READY state. This state can be entered
+ // once from WAITING and unlimited number of times from DATA_READY.
+ // DATA_READY:
+ // The data message is ready to be retrieved. If the data is not retrieved
+ // before the next packet which will cause a transition, the data will be
+ // lost. Move to RECEIVING_DATA on receiving first data packet. Move to
+ // CONNECTION_CLOSED on receiving close. This state can be entered once from
+ // CONNECTING and unlimited number of times from RECEIVING_DATA.
+ // CONNECTION_CLOSED:
+ // The connection is closed. Refuse any further messages. Allow the reason
+ // for close to be retrieved.
+ // ERROR:
+ // Something bad happened along the way. Allow a reason to close be
+ // retrieved. The reason to close tells the receiver's user what reason to
+ // close the connection in case the user wants to send a CONNECTION_CLOSE.
+ enum State {
+ CONNECTING = 0x00,
+ WAITING = 0x01,
+ RECEIVING_DATA = 0x02,
+ DATA_READY = 0x03,
+ CONNECTION_CLOSED = 0x04,
+ ERROR_DETECTED = 0x05
+ };
+
+ // The specific error that caused the receiver to move to ERROR state.
+ enum ReceiverError {
+ NO_ERROR_DETECTED,
+ EMPTY_PACKET,
+ RECEIVED_PACKET_IN_CONNECTION_CLOSED,
+ RECEIVED_DATA_IN_CONNECTING,
+ SERVER_RECEIVED_CONNECTION_RESPONSE,
+ CLIENT_RECEIVED_CONNECTION_REQUEST,
+ RECEIVED_CONNECTION_CLOSE_IN_CONNECTING,
+ UNRECOGNIZED_CONTROL_COMMAND,
+ INVALID_CONTROL_COMMAND_IN_DATA_TRANSACTION,
+ INVALID_DATA_PACKET_SIZE,
+ DATA_HEADER_LOW_BITS_NOT_CLEARED,
+ INCORRECT_DATA_FIRST_BIT,
+ INVALID_CONNECTION_REQUEST_SIZE,
+ INVALID_REQUESTED_MAX_PACKET_SIZE,
+ NOT_SUPPORTED_REQUESTED_VERSION,
+ INVALID_CONNECTION_RESPONSE_SIZE,
+ INVALID_SELECTED_MAX_PACKET_SIZE,
+ NOT_SUPPORTED_SELECTED_VERSION,
+ INVALID_CONNECTION_CLOSE_SIZE,
+ UNRECOGNIZED_REASON_FOR_CLOSE,
+ PACKET_OUT_OF_SEQUENCE
+ };
+
+ class Factory {
+ public:
+ static std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> NewInstance(
+ ReceiverType receiver_type);
+
+ // Exposed for testing.
+ static void SetInstanceForTesting(std::shared_ptr<Factory> factory);
+
+ protected:
+ // Exposed for testing.
+ virtual std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver>
+ BuildInstance(ReceiverType receiver_type);
+
+ private:
+ static std::shared_ptr<Factory> factory_instance_;
+ };
+
+ ~BluetoothLowEnergyWeavePacketReceiver();
+
+ typedef std::vector<uint8_t> Packet;
+
+ // Get the receiver’s state.
+ virtual State GetState();
+
+ // Return the packet size that the receiver parsed out of request/response.
+ virtual uint16_t GetMaxPacketSize();
+
+ // Get the reason that receiver received in a connection close packet.
+ // It's only defined in CONNECTION_CLOSED state.
+ // Will crash unless receiver is in State::CONNECTION_CLOSED.
+ virtual ReasonForClose GetReasonForClose();
+
+ // The reason that the receiver decided to enter the ERROR state.
+ // This would be the reason that the receiver's want to send a connection
+ // close to the other side of the connection.
+ // Will crash unless receiver is in State::ERROR.
+ virtual ReasonForClose GetReasonToClose();
+
+ // Get a complete data message that's yet received.
+ // Will crash unless receiver is in State::DATA_READY.
+ // NOTE: if this function is not called in DATA_READY state and the receiver
+ // transitions out of that state, the data will be gone!
+ virtual std::string GetDataMessage();
+
+ // Get the specific error that caused the receiver to jump into ERROR state.
+ // Can be called from any state. Will return NO_ERROR if no error occurred.
+ virtual ReceiverError GetReceiverError();
+
+ // 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);
+
+ void ReceiveConnectionRequest(const Packet& packet);
+ void ReceiveConnectionResponse(const Packet& packet);
+ void ReceiveConnectionClose(const Packet& packet);
+ void AppendData(const Packet& packet, uint32_t byte_offset);
+
+ uint16_t GetShortField(const Packet& packet, uint32_t byte_offset);
+ uint8_t GetPacketType(const Packet& packet);
+ uint8_t GetControlCommand(const Packet& packet);
+ void VerifyPacketCounter(const Packet& packet);
+ bool IsFirstDataPacket(const Packet& packet);
+ bool IsLastDataPacket(const Packet& packet);
+ bool AreLowerTwoBitsCleared(const Packet& packet);
+
+ void MoveToErrorState(ReasonForClose reason_to_close,
+ ReceiverError receiver_error);
+
+ void SetMaxPacketSize(uint16_t packet_size);
+ uint16_t GetConceptualMaxPacketSize();
+
+ // Identify whether the receiver is for a client or a server.
+ ReceiverType receiver_type_;
+
+ // Max packet size of the connection.
+ // Default is 0 which means the server will determine the size by observing
+ // ATT_MTU of the client.
+ uint16_t max_packet_size_;
+
+ // Expected counter of the next packet received, starting at 0.
+ uint8_t next_packet_counter_;
+
+ // Current state of the receiver.
+ // Certain functions will only return valid value if the receiver is in the
+ // appropriate state.
+ State state_;
+
+ // The reason why the connection was closed by the sender if any.
+ ReasonForClose reason_for_close_;
+
+ // The reason why the receiver is in an erronous state if any.
+ ReasonForClose reason_to_close_;
+
+ // The data message if there is one.
+ Packet data_message_;
+
+ // The error the receiver encountered while processing packets.
+ // Used for debugging purproses.
+ ReceiverError receiver_error_;
+};
+
+} // namespace weave
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_BLE_BLUETOOTH_LOW_ENERGY_WEAVE_PACKET_RECEIVER_H_
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
new file mode 100644
index 00000000000..827e88b5916
--- /dev/null
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_packet_receiver_unittest.cc
@@ -0,0 +1,1026 @@
+// 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/ble/bluetooth_low_energy_weave_packet_receiver.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cryptauth {
+namespace weave {
+namespace {
+
+typedef BluetoothLowEnergyWeavePacketReceiver::ReceiverType ReceiverType;
+typedef BluetoothLowEnergyWeavePacketReceiver::State State;
+typedef BluetoothLowEnergyWeavePacketReceiver::ReceiverError ReceiverError;
+
+const uint8_t kCloseWithoutError = 0;
+
+// uWeave Header:
+// 1--- ---- : type = 1 (control packet)
+// -000 ---- : counter = 0
+// ---- 0000 : command = 0 (request)
+// 1000 0000 = 0x80
+const uint8_t kControlRequestHeader = 0x80;
+
+// uWeave Header:
+// 1--- ---- : type = 1 (control packet)
+// -000 ---- : counter = 0
+// ---- 0001 : command = 1 (response)
+// 1000 0001 = 0x81
+const uint8_t kControlResponseHeader = 0x81;
+} // namespace
+
+class CryptAuthBluetoothLowEnergyWeavePacketReceiverTest
+ : public testing::Test {
+ protected:
+ CryptAuthBluetoothLowEnergyWeavePacketReceiverTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(
+ CryptAuthBluetoothLowEnergyWeavePacketReceiverTest);
+};
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ WellBehavingServerPacketsNoControlDataTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ std::vector<uint8_t> p1(kByteDefaultMaxPacketSize, 'a');
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 1000 = 0x18
+ p1[0] = 0x18;
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::RECEIVING_DATA, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -010 ---- : counter = 2
+ // ---- 0--- : first packet = false
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0010 0100 = 0x24
+ std::vector<uint8_t> p2{0x24, 'c', 'd'};
+ receiver->ReceivePacket(p2);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("aaaaaaaaaaaaaaaaaaacd", receiver->GetDataMessage());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -011 ---- : counter = 3
+ // ---- 1--- : first packet = true
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0011 1100 = 0x3C
+ std::vector<uint8_t> p3{0x3C, 'g', 'o', 'o', 'g', 'l', 'e'};
+ receiver->ReceivePacket(p3);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("google", receiver->GetDataMessage());
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -100 ---- : counter = 4
+ // ---- 0010 : command = 2 (close)
+ // 1100 0010 = 0xC2
+ // 0x80 is the hex value for APPLICATION_ERROR
+ std::vector<uint8_t> p4{0xC2, kEmptyUpperByte, 0x80};
+ receiver->ReceivePacket(p4);
+ EXPECT_EQ(State::CONNECTION_CLOSED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::APPLICATION_ERROR, receiver->GetReasonForClose());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ WellBehavingServerPacketsWithFullControlDataTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader,
+ kEmptyUpperByte,
+ kByteWeaveVersion,
+ kEmptyUpperByte,
+ kByteWeaveVersion,
+ kEmptyUpperByte,
+ kByteSelectMaxPacketSize,
+ 'a',
+ 'b',
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i',
+ 'j',
+ 'k',
+ 'l',
+ 'm'};
+
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("abcdefghijklm", receiver->GetDataMessage());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 1000 = 0x18
+ std::vector<uint8_t> p1(kByteDefaultMaxPacketSize, 'o');
+ p1[0] = 0x18;
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::RECEIVING_DATA, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -010 ---- : counter = 2
+ // ---- 0--- : first packet = false
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0010 0100 = 0x24
+ std::vector<uint8_t> p2{0x24, 'p', 'q'};
+ receiver->ReceivePacket(p2);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("ooooooooooooooooooopq", receiver->GetDataMessage());
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -011 ---- : counter = 3
+ // ---- 0010 : command = 2 (close)
+ // 1011 0010 = 0xB2
+ std::vector<uint8_t> p3{0xB2, kEmptyUpperByte, kCloseWithoutError};
+ receiver->ReceivePacket(p3);
+ EXPECT_EQ(State::CONNECTION_CLOSED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::CLOSE_WITHOUT_ERROR, receiver->GetReasonForClose());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ WellBehavingServerPacketsWithSomeControlDataTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize, 'a'};
+
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("a", receiver->GetDataMessage());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 1000 = 0x18
+ std::vector<uint8_t> p1(kByteDefaultMaxPacketSize, 'o');
+ p1[0] = 0x18;
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::RECEIVING_DATA, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -010 ---- : counter = 2
+ // ---- 0--- : first packet = false
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0010 0100 = 0x24
+ std::vector<uint8_t> p2{0x24, 'p', 'q'};
+ receiver->ReceivePacket(p2);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("ooooooooooooooooooopq", receiver->GetDataMessage());
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -011 ---- : counter = 3
+ // ---- 0010 : command = 2 (close)
+ // 1011 0010 = 0xB2
+ std::vector<uint8_t> p3{0xB2, kEmptyUpperByte, kCloseWithoutError};
+ receiver->ReceivePacket(p3);
+ EXPECT_EQ(State::CONNECTION_CLOSED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::CLOSE_WITHOUT_ERROR, receiver->GetReasonForClose());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ WellBehavingClientPacketsNoControlDataTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ const uint8_t kSelectedPacketSize = 30;
+ std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kSelectedPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+ EXPECT_EQ(kSelectedPacketSize, receiver->GetMaxPacketSize());
+
+ std::vector<uint8_t> p1(kSelectedPacketSize, 'o');
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 1100 = 0x1C
+ p1[0] = 0x1C;
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("ooooooooooooooooooooooooooooo", receiver->GetDataMessage());
+
+ const uint8_t kApplicationError = 0x80;
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -010 ---- : counter = 2
+ // ---- 0010 : command = 2 (close)
+ // 1010 0010 = 0xA2
+ std::vector<uint8_t> p2{0xA2, kEmptyUpperByte, kApplicationError};
+ receiver->ReceivePacket(p2);
+ EXPECT_EQ(State::CONNECTION_CLOSED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::APPLICATION_ERROR, receiver->GetReasonForClose());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ WellBehavingClientPacketsWithFullControlDataTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0{kControlResponseHeader,
+ kEmptyUpperByte,
+ kByteWeaveVersion,
+ kEmptyUpperByte,
+ kByteDefaultMaxPacketSize,
+ 'a',
+ 'b',
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i',
+ 'j',
+ 'k',
+ 'l',
+ 'm',
+ 'n',
+ 'o'};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("abcdefghijklmno", receiver->GetDataMessage());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 1100 = 0x1C
+ std::vector<uint8_t> p1{0x1C, 'g', 'o', 'o', 'g', 'l', 'e'};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("google", receiver->GetDataMessage());
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -010 ---- : counter = 2
+ // ---- 0010 : command = 2 (close)
+ // 1010 0010 = 0xA2
+ std::vector<uint8_t> p2{0xA2, kEmptyUpperByte, kCloseWithoutError};
+ receiver->ReceivePacket(p2);
+ EXPECT_EQ(State::CONNECTION_CLOSED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::CLOSE_WITHOUT_ERROR, receiver->GetReasonForClose());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ WellBehavingClientPacketsWithSomeControlDataTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0{kControlResponseHeader,
+ kEmptyUpperByte,
+ kByteWeaveVersion,
+ kEmptyUpperByte,
+ kByteDefaultMaxPacketSize,
+ 'a',
+ 'b',
+ 'c'};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("abc", receiver->GetDataMessage());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 1100 = 0x1C
+ std::vector<uint8_t> p1{0x1C, 'g', 'o', 'o', 'g', 'l', 'e'};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("google", receiver->GetDataMessage());
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -010 ---- : counter = 2
+ // ---- 0010 : command = 2 (close)
+ // 1010 0010 = 0xA2
+ std::vector<uint8_t> p2{0xA2, kEmptyUpperByte, kCloseWithoutError};
+ receiver->ReceivePacket(p2);
+ EXPECT_EQ(State::CONNECTION_CLOSED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::CLOSE_WITHOUT_ERROR, receiver->GetReasonForClose());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ LegacyCloseWithoutReasonTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -001 ---- : counter = 1
+ // ---- 0010 : command = 2 (close)
+ // 1001 0010 = 0x92
+ std::vector<uint8_t> p1{0x92};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::CONNECTION_CLOSED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonForClose());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ OneBytePacketTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteDefaultMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 1100 = 0x1C
+ std::vector<uint8_t> p1{0x1C};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::DATA_READY, receiver->GetState());
+ EXPECT_EQ("", receiver->GetDataMessage());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ EmptyPacketTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0;
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::EMPTY_PACKET, receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ServerReceivingConnectionResponseTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+ std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteDefaultMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::CLIENT_RECEIVED_CONNECTION_REQUEST,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ClientReceivingConnectionRequestTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::SERVER_RECEIVED_CONNECTION_RESPONSE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ReceiveConnectionCloseInConnecting) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -000 ---- : counter = 0
+ // ---- 0010 : command = 2 (close)
+ // 1000 0010 = 0x82
+ std::vector<uint8_t> p0{0x82, kEmptyUpperByte, kCloseWithoutError};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::RECEIVED_CONNECTION_CLOSE_IN_CONNECTING,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ReceiveDataInConnecting) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -000 ---- : counter = 0
+ // ---- 1--- : first packet = true
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0000 1000 = 0x08
+ std::vector<uint8_t> p3{0x08, 'a', 'b', 'c', 'd'};
+ receiver->ReceivePacket(p3);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::RECEIVED_DATA_IN_CONNECTING,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ConnectionRequestTooSmallTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INVALID_CONNECTION_REQUEST_SIZE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ConnectionRequestTooLargeTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0(kByteDefaultMaxPacketSize + 1, 0);
+ p0[0] = kControlRequestHeader;
+ p0[2] = kByteWeaveVersion;
+ p0[4] = kByteWeaveVersion;
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INVALID_CONNECTION_REQUEST_SIZE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ConnectionResponseTooSmallTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INVALID_CONNECTION_RESPONSE_SIZE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ConnectionResponseTooLargeTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0(kByteDefaultMaxPacketSize + 1, 0);
+ p0[0] = kControlResponseHeader;
+ p0[2] = kByteWeaveVersion;
+ p0[4] = kByteDefaultMaxPacketSize;
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INVALID_CONNECTION_RESPONSE_SIZE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ConnectionCloseTooLargeTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -001 ---- : counter = 1
+ // ---- 0010 : command = 2 (close)
+ // 1001 0010 = 0x92
+ std::vector<uint8_t> p1{0x92, kEmptyUpperByte, kCloseWithoutError, 'a'};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReceiverError::INVALID_CONNECTION_CLOSE_SIZE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ DataPacketTooLargeTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 1100 = 0x1C
+ std::vector<uint8_t> p1(kByteDefaultMaxPacketSize + 1, 'a');
+ p1[0] = 0x1C;
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INVALID_DATA_PACKET_SIZE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ FirstPacketNoFirstNorLastBitTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 0--- : first packet = false
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 0000 = 0x10
+ std::vector<uint8_t> p1{0x10};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::RECEIVED_PACKET_OUT_OF_SEQUENCE,
+ receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INCORRECT_DATA_FIRST_BIT,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ FirstPacketNoFirstYesLastBitTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 0--- : first packet = false
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 0100 = 0x14
+ std::vector<uint8_t> p1{0x14};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::RECEIVED_PACKET_OUT_OF_SEQUENCE,
+ receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INCORRECT_DATA_FIRST_BIT,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ NonFirstPacketYesFirstBitTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0001 1000 = 0x18
+ std::vector<uint8_t> p1{0x18};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::RECEIVING_DATA, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -010 ---- : counter = 2
+ // ---- 1--- : first packet = true
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0010 1000 = 0x28
+ std::vector<uint8_t> p2{0x28};
+ receiver->ReceivePacket(p2);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::RECEIVED_PACKET_OUT_OF_SEQUENCE,
+ receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INCORRECT_DATA_FIRST_BIT,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ OutOfOrderPacketTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -010 ---- : counter = 2
+ // ---- 0--- : first packet = false
+ // ---- -0-- : last packet = false
+ // ---- --00 : defined by uWeave to be 0
+ // 0010 0000 = 0x20
+ std::vector<uint8_t> p1{0x20};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::RECEIVED_PACKET_OUT_OF_SEQUENCE,
+ receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::PACKET_OUT_OF_SEQUENCE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ InvalidVersionInConnectionRequestTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ const uint8_t kWrongVersion = 2;
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kWrongVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::NO_COMMON_VERSION_SUPPORTED,
+ receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::NOT_SUPPORTED_REQUESTED_VERSION,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ InvalidMaxPacketSizeInConnectionRequestTest) {
+ const uint8_t kSmallMaxPacketSize = 19;
+
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kSmallMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INVALID_REQUESTED_MAX_PACKET_SIZE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ InvalidSelectedVersionInConnectionResponseTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0{kControlResponseHeader, kByteWeaveVersion,
+ kEmptyUpperByte, kEmptyUpperByte,
+ kByteDefaultMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::NO_COMMON_VERSION_SUPPORTED,
+ receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::NOT_SUPPORTED_SELECTED_VERSION,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ InvalidSelectedMaxPacketSizeInConnectionResponseTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ const uint8_t kSmallMaxPacketSize = 19;
+ std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kSmallMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INVALID_SELECTED_MAX_PACKET_SIZE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ UnrecognizedReasonForCloseInConnectionCloseTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteDefaultMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ const uint8_t kInvalidReasonForClose = 5;
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -001 ---- : counter = 1
+ // ---- 0010 : command = 2 (close)
+ // 1001 0010 = 0x92
+ std::vector<uint8_t> p1{0x92, kEmptyUpperByte, kInvalidReasonForClose};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::UNRECOGNIZED_REASON_FOR_CLOSE,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ UnrecognizedControlCommandBitTwoTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -000 ---- : counter = 0
+ // ---- 0100 : command = 4 (INVALID)
+ // 1000 0100 = 0x84
+ std::vector<uint8_t> p0{0x84,
+ kEmptyUpperByte,
+ kByteWeaveVersion,
+ kEmptyUpperByte,
+ kByteWeaveVersion,
+ kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::UNRECOGNIZED_CONTROL_COMMAND,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ InvalidControlCommandBitThreeTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -000 ---- : counter = 0
+ // ---- 1000 : command = 8 (INVALID)
+ // 1000 1000 = 0x88
+ std::vector<uint8_t> p0{0x88, kEmptyUpperByte, kByteWeaveVersion,
+ kEmptyUpperByte, kByteDefaultMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::UNRECOGNIZED_CONTROL_COMMAND,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ InvalidBitOneInDataPacketHeaderTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteDefaultMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -1-- : last packet = true
+ // ---- --10 : defined by uWeave to be 0, but bit 1 is not
+ // 0001 1110 = 0x1E
+ std::vector<uint8_t> p1{0x1E, 'a'};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::DATA_HEADER_LOW_BITS_NOT_CLEARED,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ InvalidBitZeroInDataPacketHeaderTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0{kControlResponseHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteDefaultMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -001 ---- : counter = 1
+ // ---- 1--- : first packet = true
+ // ---- -1-- : last packet = true
+ // ---- --01 : defined by uWeave to be 0, but bit 0 is not
+ // 0001 1101 = 0x1D
+ std::vector<uint8_t> p1{0x1D, 'a'};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::DATA_HEADER_LOW_BITS_NOT_CLEARED,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ReceivedPacketInErrorState) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::CLIENT);
+
+ std::vector<uint8_t> p0;
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+
+ std::vector<uint8_t> p1{kControlResponseHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::EMPTY_PACKET, receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ ReceivedPacketInConnectionClosedStateTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -001 ---- : counter = 1
+ // ---- 0010 : command = 2 (close)
+ // 1001 0010 = 0x92
+ std::vector<uint8_t> p1{0x92, kEmptyUpperByte, kCloseWithoutError};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::CONNECTION_CLOSED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::CLOSE_WITHOUT_ERROR, receiver->GetReasonForClose());
+
+ // uWeave Header:
+ // 0--- ---- : type = 0 (data packet)
+ // -010 ---- : counter = 2
+ // ---- 1--- : first packet = true
+ // ---- -1-- : last packet = true
+ // ---- --00 : defined by uWeave to be 0
+ // 0010 1100 = 0x2C
+ std::vector<uint8_t> p2{0x2C, 'a'};
+ receiver->ReceivePacket(p2);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::RECEIVED_PACKET_IN_CONNECTION_CLOSED,
+ receiver->GetReceiverError());
+}
+
+TEST_F(CryptAuthBluetoothLowEnergyWeavePacketReceiverTest,
+ MultipleControlPacketTest) {
+ std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> receiver =
+ BluetoothLowEnergyWeavePacketReceiver::Factory::NewInstance(
+ ReceiverType::SERVER);
+
+ std::vector<uint8_t> p0{kControlRequestHeader, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteWeaveVersion, kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p0);
+ EXPECT_EQ(State::WAITING, receiver->GetState());
+
+ // uWeave Header:
+ // 1--- ---- : type = 1 (control packet)
+ // -001 ---- : counter = 1
+ // ---- 0000 : command = 0 (request)
+ // 1001 0000 = 0x90
+ std::vector<uint8_t> p1{0x90,
+ kEmptyUpperByte,
+ kByteWeaveVersion,
+ kEmptyUpperByte,
+ kByteWeaveVersion,
+ kEmptyUpperByte,
+ kByteSelectMaxPacketSize};
+ receiver->ReceivePacket(p1);
+ EXPECT_EQ(State::ERROR_DETECTED, receiver->GetState());
+ EXPECT_EQ(ReasonForClose::UNKNOWN_ERROR, receiver->GetReasonToClose());
+ EXPECT_EQ(ReceiverError::INVALID_CONTROL_COMMAND_IN_DATA_TRANSACTION,
+ receiver->GetReceiverError());
+}
+
+} // namespace weave
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/ble/fake_wire_message.cc b/chromium/components/cryptauth/ble/fake_wire_message.cc
new file mode 100644
index 00000000000..8853299d2e6
--- /dev/null
+++ b/chromium/components/cryptauth/ble/fake_wire_message.cc
@@ -0,0 +1,23 @@
+// 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/ble/fake_wire_message.h"
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "components/cryptauth/wire_message.h"
+
+namespace cryptauth {
+
+FakeWireMessage::FakeWireMessage(
+ const std::string& payload, const std::string& feature)
+ : WireMessage(payload, feature) {}
+
+std::string FakeWireMessage::Serialize() const {
+ return feature() + "," + payload();
+}
+
+}
diff --git a/chromium/components/cryptauth/ble/fake_wire_message.h b/chromium/components/cryptauth/ble/fake_wire_message.h
new file mode 100644
index 00000000000..8eb951669fe
--- /dev/null
+++ b/chromium/components/cryptauth/ble/fake_wire_message.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_CRYPTAUTH_FAKE_WIRE_MESSAGE_H_
+#define COMPONENTS_CRYPTAUTH_FAKE_WIRE_MESSAGE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "components/cryptauth/wire_message.h"
+
+namespace cryptauth {
+
+class FakeWireMessage : public WireMessage {
+ public:
+ FakeWireMessage(const std::string& payload, const std::string& feature);
+
+ // WireMessage:
+ std::string Serialize() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FakeWireMessage);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_FAKE_WIRE_MESSAGE_H_
diff --git a/chromium/components/cryptauth/ble/remote_attribute.h b/chromium/components/cryptauth/ble/remote_attribute.h
new file mode 100644
index 00000000000..1bf1218dd4f
--- /dev/null
+++ b/chromium/components/cryptauth/ble/remote_attribute.h
@@ -0,0 +1,22 @@
+// 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_REMOTE_ATTRIBUTE_H_
+#define COMPONENTS_CRYPTAUTH_BLE_REMOTE_ATTRIBUTE_H_
+
+#include <string>
+
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace cryptauth {
+
+// Represents an attribute in the peripheral (service or characteristic).
+struct RemoteAttribute {
+ device::BluetoothUUID uuid;
+ std::string id;
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_BLE_REMOTE_ATTRIBUTE_H_
diff --git a/chromium/components/cryptauth/bluetooth_throttler.h b/chromium/components/cryptauth/bluetooth_throttler.h
new file mode 100644
index 00000000000..90eed117951
--- /dev/null
+++ b/chromium/components/cryptauth/bluetooth_throttler.h
@@ -0,0 +1,38 @@
+// 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
new file mode 100644
index 00000000000..d72337ef593
--- /dev/null
+++ b/chromium/components/cryptauth/bluetooth_throttler_impl.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/cryptauth/bluetooth_throttler_impl.h"
+
+#include <utility>
+
+#include "base/stl_util.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 = 7;
+
+} // namespace
+
+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
new file mode 100644
index 00000000000..2933f168119
--- /dev/null
+++ b/chromium/components/cryptauth/bluetooth_throttler_impl.h
@@ -0,0 +1,69 @@
+// 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/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:
+ // Creates a throttler for connections to a remote device, using the |clock|
+ // as a time source.
+ explicit BluetoothThrottlerImpl(std::unique_ptr<base::TickClock> clock);
+ ~BluetoothThrottlerImpl() override;
+
+ // BluetoothThrottler:
+ base::TimeDelta GetDelay() const override;
+ void OnConnection(Connection* connection) override;
+
+ protected:
+ // Returns the duration to wait, after disconnecting, before reattempting a
+ // connection to the remote device. Exposed for testing.
+ base::TimeDelta GetCooldownTimeDelta() const;
+
+ private:
+ // 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
new file mode 100644
index 00000000000..7a3a028f738
--- /dev/null
+++ b/chromium/components/cryptauth/bluetooth_throttler_impl_unittest.cc
@@ -0,0 +1,101 @@
+// 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
new file mode 100644
index 00000000000..ac0b2c318b2
--- /dev/null
+++ b/chromium/components/cryptauth/connection.cc
@@ -0,0 +1,105 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/connection.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "components/cryptauth/connection_observer.h"
+#include "components/cryptauth/wire_message.h"
+
+namespace cryptauth {
+
+Connection::Connection(const RemoteDevice& remote_device)
+ : remote_device_(remote_device),
+ status_(DISCONNECTED),
+ is_sending_message_(false) {}
+
+Connection::~Connection() {}
+
+bool Connection::IsConnected() const {
+ return status_ == CONNECTED;
+}
+
+void Connection::SendMessage(std::unique_ptr<WireMessage> message) {
+ if (!IsConnected()) {
+ VLOG(1) << "Cannot send message when disconnected.";
+ return;
+ }
+
+ if (is_sending_message_) {
+ VLOG(1) << "Another message is currently in progress.";
+ return;
+ }
+
+ is_sending_message_ = true;
+ SendMessageImpl(std::move(message));
+}
+
+void Connection::AddObserver(ConnectionObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void Connection::RemoveObserver(ConnectionObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+std::string Connection::GetDeviceAddress() {
+ return remote_device_.bluetooth_address;
+}
+
+void Connection::SetStatus(Status status) {
+ if (status_ == status)
+ return;
+
+ received_bytes_.clear();
+
+ Status old_status = status_;
+ status_ = status;
+ for (auto& observer : observers_)
+ observer.OnConnectionStatusChanged(this, old_status, status_);
+}
+
+void Connection::OnDidSendMessage(const WireMessage& message, bool success) {
+ if (!is_sending_message_) {
+ VLOG(1) << "Send completed, but no message in progress.";
+ return;
+ }
+
+ is_sending_message_ = false;
+ for (auto& observer : observers_)
+ observer.OnSendCompleted(*this, message, success);
+}
+
+void Connection::OnBytesReceived(const std::string& bytes) {
+ if (!IsConnected()) {
+ VLOG(1) << "Received bytes, but not connected.";
+ return;
+ }
+
+ received_bytes_ += bytes;
+
+ bool is_incomplete_message;
+ std::unique_ptr<WireMessage> message =
+ DeserializeWireMessage(&is_incomplete_message);
+ if (is_incomplete_message)
+ return;
+
+ if (message) {
+ for (auto& observer : observers_)
+ observer.OnMessageReceived(*this, *message);
+ }
+
+ // Whether the message was parsed successfully or not, clear the
+ // |received_bytes_| buffer.
+ received_bytes_.clear();
+}
+
+std::unique_ptr<WireMessage> Connection::DeserializeWireMessage(
+ bool* is_incomplete_message) {
+ return WireMessage::Deserialize(received_bytes_, is_incomplete_message);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/connection.h b/chromium/components/cryptauth/connection.h
new file mode 100644
index 00000000000..c134fbf4d27
--- /dev/null
+++ b/chromium/components/cryptauth/connection.h
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_CONNECTION_H_
+#define COMPONENTS_CRYPTAUTH_CONNECTION_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "components/cryptauth/remote_device.h"
+
+namespace cryptauth {
+
+class ConnectionObserver;
+class WireMessage;
+
+// Base class representing a connection with a remote device, which is a
+// persistent bidirectional channel for sending and receiving wire messages.
+class Connection {
+ public:
+ enum Status {
+ DISCONNECTED,
+ IN_PROGRESS,
+ CONNECTED,
+ };
+
+ // Constructs a connection to the given |remote_device|.
+ explicit Connection(const RemoteDevice& remote_device);
+ virtual ~Connection();
+
+ // Returns true iff the connection's status is CONNECTED.
+ bool IsConnected() const;
+
+ // Returns true iff the connection is currently sending a message.
+ bool is_sending_message() const { return is_sending_message_; }
+
+ // Sends a message to the remote device.
+ // |OnSendCompleted()| will be called for all observers upon completion with
+ // either success or failure.
+ void SendMessage(std::unique_ptr<WireMessage> message);
+
+ void AddObserver(ConnectionObserver* observer);
+ void RemoveObserver(ConnectionObserver* observer);
+
+ const RemoteDevice& remote_device() const {
+ return remote_device_;
+ }
+
+ // Abstract methods that subclasses should implement:
+
+ // Attempts to connect to the remote device if not already connected.
+ virtual void Connect() = 0;
+
+ // Disconnects from the remote device.
+ virtual void Disconnect() = 0;
+
+ // The bluetooth address of the connected device.
+ virtual std::string GetDeviceAddress();
+
+ Status status() const { return status_; }
+
+ protected:
+ // Sets the connection's status to |status|. If this is different from the
+ // previous status, notifies observers of the change in status.
+ // Virtual for testing.
+ virtual void SetStatus(Status status);
+
+ // Called after attempting to send bytes over the connection, whether the
+ // message was successfully sent or not.
+ // Virtual for testing.
+ virtual void OnDidSendMessage(const WireMessage& message, bool success);
+
+ // Called when bytes are read from the connection. There should not be a send
+ // in progress when this function is called.
+ // Virtual for testing.
+ virtual void OnBytesReceived(const std::string& bytes);
+
+ // Sends bytes over the connection. The implementing class should call
+ // OnDidSendMessage() once the send succeeds or fails. At most one send will
+ // be
+ // in progress.
+ virtual void SendMessageImpl(std::unique_ptr<WireMessage> message) = 0;
+
+ // Deserializes the |recieved_bytes_| and returns the resulting WireMessage,
+ // or NULL if the message is malformed. Sets |is_incomplete_message| to true
+ // if the |serialized_message| does not have enough data to parse the header,
+ // or if the message length encoded in the message header exceeds the size of
+ // the |serialized_message|. Exposed for testing.
+ virtual std::unique_ptr<WireMessage> DeserializeWireMessage(
+ bool* is_incomplete_message);
+
+ private:
+ // The remote device corresponding to this connection.
+ const RemoteDevice remote_device_;
+
+ // The current status of the connection.
+ Status status_;
+
+ // The registered observers of the connection.
+ base::ObserverList<ConnectionObserver> observers_;
+
+ // A temporary buffer storing bytes received before a received message can be
+ // fully constructed.
+ std::string received_bytes_;
+
+ // Whether a message is currently in the process of being sent.
+ bool is_sending_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(Connection);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CONNECTION_H_
diff --git a/chromium/components/cryptauth/connection_finder.h b/chromium/components/cryptauth/connection_finder.h
new file mode 100644
index 00000000000..f7873dd17df
--- /dev/null
+++ b/chromium/components/cryptauth/connection_finder.h
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_CONNECTION_FINDER_H_
+#define COMPONENTS_CRYPTAUTH_CONNECTION_FINDER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "components/cryptauth/connection.h"
+
+namespace cryptauth {
+
+// Interface for finding a connection to a remote device.
+class ConnectionFinder {
+ public:
+ virtual ~ConnectionFinder() {}
+
+ // Attempts to find a connection to a remote device. The finder will try to
+ // find the connection indefinitely until the finder is destroyed. Calls
+ // |connection_callback| with the open connection once the remote device is
+ // connected.
+ // TODO(isherman): Can this just be done as part of the constructor?
+ typedef base::Callback<void(std::unique_ptr<Connection> connection)>
+ ConnectionCallback;
+ virtual void Find(const ConnectionCallback& connection_callback) = 0;
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CONNECTION_FINDER_H_
diff --git a/chromium/components/cryptauth/connection_observer.h b/chromium/components/cryptauth/connection_observer.h
new file mode 100644
index 00000000000..3ac1a99c4ef
--- /dev/null
+++ b/chromium/components/cryptauth/connection_observer.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_CONNECTION_OBSERVER_H_
+#define COMPONENTS_CRYPTAUTH_CONNECTION_OBSERVER_H_
+
+#include "components/cryptauth/connection.h"
+
+namespace cryptauth {
+
+class WireMessage;
+
+// An interface for observing events that happen on a Connection.
+class ConnectionObserver {
+ public:
+ virtual ~ConnectionObserver() {}
+
+ // Called when the |connection|'s status changes from |old_status| to
+ // |new_status|. The |connectoin| is guaranteed to be non-null.
+ virtual void OnConnectionStatusChanged(Connection* connection,
+ Connection::Status old_status,
+ Connection::Status new_status) {}
+
+ // Called when a |message| is received from a remote device over the
+ // |connection|.
+ virtual void OnMessageReceived(const Connection& connection,
+ const WireMessage& message) {}
+
+ // Called after a |message| is sent to the remote device over the
+ // |connection|. |success| is |true| iff the message is sent successfully.
+ virtual void OnSendCompleted(const Connection& connection,
+ const WireMessage& message,
+ bool success) {}
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CONNECTION_OBSERVER_H_
diff --git a/chromium/components/cryptauth/connection_unittest.cc b/chromium/components/cryptauth/connection_unittest.cc
new file mode 100644
index 00000000000..d33146a6ac2
--- /dev/null
+++ b/chromium/components/cryptauth/connection_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/connection.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/connection_observer.h"
+#include "components/cryptauth/remote_device.h"
+#include "components/cryptauth/wire_message.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::NiceMock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::StrictMock;
+
+namespace cryptauth {
+namespace {
+
+class MockConnection : public Connection {
+ public:
+ MockConnection() : Connection(RemoteDevice()) {}
+ ~MockConnection() {}
+
+ MOCK_METHOD1(SetPaused, void(bool paused));
+ MOCK_METHOD0(Connect, void());
+ MOCK_METHOD0(Disconnect, void());
+ MOCK_METHOD0(CancelConnectionAttempt, void());
+ MOCK_METHOD1(SendMessageImplProxy, void(WireMessage* message));
+ MOCK_METHOD1(DeserializeWireMessageProxy,
+ WireMessage*(bool* is_incomplete_message));
+
+ // Gmock only supports copyable types, so create simple wrapper methods for
+ // ease of mocking.
+ void SendMessageImpl(std::unique_ptr<WireMessage> message) override {
+ SendMessageImplProxy(message.get());
+ }
+
+ std::unique_ptr<WireMessage> DeserializeWireMessage(
+ bool* is_incomplete_message) override {
+ return base::WrapUnique(DeserializeWireMessageProxy(is_incomplete_message));
+ }
+
+ using Connection::status;
+ using Connection::SetStatus;
+ using Connection::OnDidSendMessage;
+ using Connection::OnBytesReceived;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockConnection);
+};
+
+class MockConnectionObserver : public ConnectionObserver {
+ public:
+ MockConnectionObserver() {}
+ virtual ~MockConnectionObserver() {}
+
+ MOCK_METHOD3(OnConnectionStatusChanged,
+ void(Connection* connection,
+ Connection::Status old_status,
+ Connection::Status new_status));
+ MOCK_METHOD2(OnMessageReceived,
+ void(const Connection& connection, const WireMessage& message));
+ MOCK_METHOD3(OnSendCompleted,
+ void(const Connection& connection,
+ const WireMessage& message,
+ bool success));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockConnectionObserver);
+};
+
+// Unlike WireMessage, offers a public constructor.
+class TestWireMessage : public WireMessage {
+ public:
+ TestWireMessage() : WireMessage("payload", "feature") {}
+ ~TestWireMessage() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestWireMessage);
+};
+
+} // namespace
+
+class CryptAuthConnectionTest : public testing::Test {
+ protected:
+ CryptAuthConnectionTest() {}
+ ~CryptAuthConnectionTest() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthConnectionTest);
+};
+
+TEST(CryptAuthConnectionTest, IsConnected) {
+ StrictMock<MockConnection> connection;
+ EXPECT_FALSE(connection.IsConnected());
+
+ connection.SetStatus(Connection::CONNECTED);
+ EXPECT_TRUE(connection.IsConnected());
+
+ connection.SetStatus(Connection::DISCONNECTED);
+ EXPECT_FALSE(connection.IsConnected());
+
+ connection.SetStatus(Connection::IN_PROGRESS);
+ EXPECT_FALSE(connection.IsConnected());
+}
+
+TEST(CryptAuthConnectionTest, SendMessage_FailsWhenNotConnected) {
+ StrictMock<MockConnection> connection;
+ connection.SetStatus(Connection::IN_PROGRESS);
+
+ EXPECT_CALL(connection, SendMessageImplProxy(_)).Times(0);
+ connection.SendMessage(std::unique_ptr<WireMessage>());
+}
+
+TEST(CryptAuthConnectionTest,
+ SendMessage_FailsWhenAnotherMessageSendIsInProgress) {
+ NiceMock<MockConnection> connection;
+ connection.SetStatus(Connection::CONNECTED);
+ connection.SendMessage(std::unique_ptr<WireMessage>());
+
+ EXPECT_CALL(connection, SendMessageImplProxy(_)).Times(0);
+ connection.SendMessage(std::unique_ptr<WireMessage>());
+}
+
+TEST(CryptAuthConnectionTest, SendMessage_SucceedsWhenConnected) {
+ StrictMock<MockConnection> connection;
+ connection.SetStatus(Connection::CONNECTED);
+
+ EXPECT_CALL(connection, SendMessageImplProxy(_));
+ connection.SendMessage(std::unique_ptr<WireMessage>());
+}
+
+TEST(CryptAuthConnectionTest,
+ SendMessage_SucceedsAfterPreviousMessageSendCompletes) {
+ NiceMock<MockConnection> connection;
+ connection.SetStatus(Connection::CONNECTED);
+ connection.SendMessage(std::unique_ptr<WireMessage>());
+ connection.OnDidSendMessage(TestWireMessage(), true /* success */);
+
+ EXPECT_CALL(connection, SendMessageImplProxy(_));
+ connection.SendMessage(std::unique_ptr<WireMessage>());
+}
+
+TEST(CryptAuthConnectionTest, SetStatus_NotifiesObserversOfStatusChange) {
+ StrictMock<MockConnection> connection;
+ EXPECT_EQ(Connection::DISCONNECTED, connection.status());
+
+ StrictMock<MockConnectionObserver> observer;
+ connection.AddObserver(&observer);
+
+ EXPECT_CALL(observer,
+ OnConnectionStatusChanged(&connection, Connection::DISCONNECTED,
+ Connection::CONNECTED));
+ connection.SetStatus(Connection::CONNECTED);
+}
+
+TEST(CryptAuthConnectionTest,
+ SetStatus_DoesntNotifyObserversIfStatusUnchanged) {
+ StrictMock<MockConnection> connection;
+ EXPECT_EQ(Connection::DISCONNECTED, connection.status());
+
+ StrictMock<MockConnectionObserver> observer;
+ connection.AddObserver(&observer);
+
+ EXPECT_CALL(observer, OnConnectionStatusChanged(_, _, _)).Times(0);
+ connection.SetStatus(Connection::DISCONNECTED);
+}
+
+TEST(CryptAuthConnectionTest,
+ OnDidSendMessage_NotifiesObserversIfMessageSendInProgress) {
+ NiceMock<MockConnection> connection;
+ connection.SetStatus(Connection::CONNECTED);
+ connection.SendMessage(std::unique_ptr<WireMessage>());
+
+ StrictMock<MockConnectionObserver> observer;
+ connection.AddObserver(&observer);
+
+ EXPECT_CALL(observer, OnSendCompleted(Ref(connection), _, true));
+ connection.OnDidSendMessage(TestWireMessage(), true /* success */);
+}
+
+TEST(CryptAuthConnectionTest,
+ OnDidSendMessage_DoesntNotifyObserversIfNoMessageSendInProgress) {
+ NiceMock<MockConnection> connection;
+ connection.SetStatus(Connection::CONNECTED);
+
+ StrictMock<MockConnectionObserver> observer;
+ connection.AddObserver(&observer);
+
+ EXPECT_CALL(observer, OnSendCompleted(_, _, _)).Times(0);
+ connection.OnDidSendMessage(TestWireMessage(), true /* success */);
+}
+
+TEST(CryptAuthConnectionTest,
+ OnBytesReceived_NotifiesObserversOnValidMessage) {
+ NiceMock<MockConnection> connection;
+ connection.SetStatus(Connection::CONNECTED);
+
+ StrictMock<MockConnectionObserver> observer;
+ connection.AddObserver(&observer);
+
+ ON_CALL(connection, DeserializeWireMessageProxy(_))
+ .WillByDefault(
+ DoAll(SetArgPointee<0>(false), Return(new TestWireMessage)));
+ EXPECT_CALL(observer, OnMessageReceived(Ref(connection), _));
+ connection.OnBytesReceived(std::string());
+}
+
+TEST(CryptAuthConnectionTest,
+ OnBytesReceived_DoesntNotifyObserversIfNotConnected) {
+ StrictMock<MockConnection> connection;
+ connection.SetStatus(Connection::IN_PROGRESS);
+
+ StrictMock<MockConnectionObserver> observer;
+ connection.AddObserver(&observer);
+
+ EXPECT_CALL(observer, OnMessageReceived(_, _)).Times(0);
+ connection.OnBytesReceived(std::string());
+}
+
+TEST(CryptAuthConnectionTest,
+ OnBytesReceived_DoesntNotifyObserversIfMessageIsIncomplete) {
+ NiceMock<MockConnection> connection;
+ connection.SetStatus(Connection::CONNECTED);
+
+ StrictMock<MockConnectionObserver> observer;
+ connection.AddObserver(&observer);
+
+ ON_CALL(connection, DeserializeWireMessageProxy(_))
+ .WillByDefault(DoAll(SetArgPointee<0>(true), Return(nullptr)));
+ EXPECT_CALL(observer, OnMessageReceived(_, _)).Times(0);
+ connection.OnBytesReceived(std::string());
+}
+
+TEST(CryptAuthConnectionTest,
+ OnBytesReceived_DoesntNotifyObserversIfMessageIsInvalid) {
+ NiceMock<MockConnection> connection;
+ connection.SetStatus(Connection::CONNECTED);
+
+ StrictMock<MockConnectionObserver> observer;
+ connection.AddObserver(&observer);
+
+ ON_CALL(connection, DeserializeWireMessageProxy(_))
+ .WillByDefault(DoAll(SetArgPointee<0>(false), Return(nullptr)));
+ EXPECT_CALL(observer, OnMessageReceived(_, _)).Times(0);
+ connection.OnBytesReceived(std::string());
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_access_token_fetcher.h b/chromium/components/cryptauth/cryptauth_access_token_fetcher.h
new file mode 100644
index 00000000000..d79e7bdad02
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_access_token_fetcher.h
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+
+namespace cryptauth {
+
+// Simple interface for fetching the OAuth2 access token that authorizes
+// CryptAuth API calls. Do not reuse this after calling FetchAccessToken();
+// instead, create a new instance.
+class CryptAuthAccessTokenFetcher {
+ public:
+ virtual ~CryptAuthAccessTokenFetcher() {}
+
+ // Fetches the access token asynchronously, invoking the callback upon
+ // completion. If the fetch fails, the callback will be invoked with an empty
+ // string.
+ typedef base::Callback<void(const std::string&)> AccessTokenCallback;
+ virtual void FetchAccessToken(const AccessTokenCallback& callback) = 0;
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_H_
diff --git a/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.cc b/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.cc
new file mode 100644
index 00000000000..f9ba9260088
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/cryptauth_access_token_fetcher_impl.h"
+
+namespace cryptauth {
+
+namespace {
+
+// Returns the set of OAuth2 scopes that CryptAuth uses.
+OAuth2TokenService::ScopeSet GetScopes() {
+ OAuth2TokenService::ScopeSet scopes;
+ scopes.insert("https://www.googleapis.com/auth/cryptauth");
+ return scopes;
+}
+
+} // namespace
+
+CryptAuthAccessTokenFetcherImpl::CryptAuthAccessTokenFetcherImpl(
+ OAuth2TokenService* token_service,
+ const std::string& account_id)
+ : OAuth2TokenService::Consumer("cryptauth_access_token_fetcher"),
+ token_service_(token_service),
+ account_id_(account_id),
+ fetch_started_(false) {
+}
+
+CryptAuthAccessTokenFetcherImpl::~CryptAuthAccessTokenFetcherImpl() {
+}
+
+void CryptAuthAccessTokenFetcherImpl::FetchAccessToken(
+ const AccessTokenCallback& callback) {
+ if (fetch_started_) {
+ LOG(WARNING) << "Create an instance for each token fetched. Do not reuse.";
+ callback.Run(std::string());
+ return;
+ }
+
+ fetch_started_ = true;
+ callback_ = callback;
+ // This request will return a cached result if it is available, saving a
+ // network round trip every time we fetch the access token.
+ token_request_ = token_service_->StartRequest(account_id_, GetScopes(), this);
+}
+
+void CryptAuthAccessTokenFetcherImpl::OnGetTokenSuccess(
+ const OAuth2TokenService::Request* request,
+ const std::string& access_token,
+ const base::Time& expiration_time) {
+ callback_.Run(access_token);
+}
+
+void CryptAuthAccessTokenFetcherImpl::OnGetTokenFailure(
+ const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) {
+ callback_.Run(std::string());
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.h b/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.h
new file mode 100644
index 00000000000..f683b54c038
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.h
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_IMPL_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_IMPL_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/cryptauth/cryptauth_access_token_fetcher.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+
+namespace cryptauth {
+
+// Implementation of CryptAuthAccessTokenFetcher fetching an access token for a
+// given account using the provided OAuth2TokenService.
+class CryptAuthAccessTokenFetcherImpl : public CryptAuthAccessTokenFetcher,
+ public OAuth2TokenService::Consumer {
+ public:
+ // |token_service| is not owned, and must outlive this object.
+ CryptAuthAccessTokenFetcherImpl(OAuth2TokenService* token_service,
+ const std::string& account_id);
+ ~CryptAuthAccessTokenFetcherImpl() override;
+
+ // CryptAuthAccessTokenFetcher:
+ void FetchAccessToken(const AccessTokenCallback& callback) override;
+
+ private:
+ // OAuth2TokenService::Consumer:
+ void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+ const std::string& access_token,
+ const base::Time& expiration_time) override;
+ void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) override;
+
+ // System service that caches and fetches tokens for a given account.
+ // Not owned.
+ OAuth2TokenService* token_service_;
+
+ // The account id for whom to mint the token.
+ std::string account_id_;
+
+ // True if FetchAccessToken() has been called.
+ bool fetch_started_;
+
+ // Stores the request from |token_service_| to mint the token.
+ std::unique_ptr<OAuth2TokenService::Request> token_request_;
+
+ // Callback to invoke when the token fetch succeeds or fails.
+ AccessTokenCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthAccessTokenFetcherImpl);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_IMPL_H_
diff --git a/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc b/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc
new file mode 100644
index 00000000000..3e72c230cc0
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/cryptauth_access_token_fetcher_impl.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "google_apis/gaia/fake_oauth2_token_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cryptauth {
+
+namespace {
+
+const char kAccountId[] = "account_id";
+const char kAccessToken[] = "access_token";
+const char kInvalidResult[] = "invalid_result";
+
+// Callback that saves the fetched access token to the first argument.
+void SaveAccessToken(std::string* out_token, const std::string& in_token) {
+ *out_token = in_token;
+}
+
+} // namespace
+
+class CryptAuthAccessTokenFetcherTest : public testing::Test {
+ protected:
+ CryptAuthAccessTokenFetcherTest()
+ : fetcher_(&token_service_, kAccountId) {
+ token_service_.AddAccount(kAccountId);
+ }
+
+ FakeOAuth2TokenService token_service_;
+ CryptAuthAccessTokenFetcherImpl fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthAccessTokenFetcherTest);
+};
+
+TEST_F(CryptAuthAccessTokenFetcherTest, FetchSuccess) {
+ std::string result;
+ fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result));
+ token_service_.IssueAllTokensForAccount(kAccountId, kAccessToken,
+ base::Time::Max());
+
+ EXPECT_EQ(kAccessToken, result);
+}
+
+TEST_F(CryptAuthAccessTokenFetcherTest, FetchFailure) {
+ std::string result(kInvalidResult);
+ fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result));
+ token_service_.IssueErrorForAllPendingRequestsForAccount(
+ kAccountId,
+ GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR));
+
+ EXPECT_EQ(std::string(), result);
+}
+
+TEST_F(CryptAuthAccessTokenFetcherTest, FetcherReuse) {
+ std::string result1;
+ fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result1));
+
+ {
+ std::string result2(kInvalidResult);
+ fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result2));
+ EXPECT_EQ(std::string(), result2);
+ }
+
+ token_service_.IssueAllTokensForAccount(kAccountId, kAccessToken,
+ base::Time::Max());
+ EXPECT_EQ(kAccessToken, result1);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_api_call_flow.cc b/chromium/components/cryptauth/cryptauth_api_call_flow.cc
new file mode 100644
index 00000000000..cfacd8992bb
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_api_call_flow.cc
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/cryptauth_api_call_flow.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "components/proximity_auth/logging/logging.h"
+#include "net/url_request/url_fetcher.h"
+
+namespace cryptauth {
+
+namespace {
+
+const char kResponseBodyError[] = "Failed to get response body";
+const char kRequestFailedError[] = "Request failed";
+const char kHttpStatusErrorPrefix[] = "HTTP status: ";
+
+} // namespace
+
+CryptAuthApiCallFlow::CryptAuthApiCallFlow() {
+}
+
+CryptAuthApiCallFlow::~CryptAuthApiCallFlow() {
+}
+
+void CryptAuthApiCallFlow::Start(const GURL& request_url,
+ net::URLRequestContextGetter* context,
+ const std::string& access_token,
+ const std::string& serialized_request,
+ const ResultCallback& result_callback,
+ const ErrorCallback& error_callback) {
+ request_url_ = request_url;
+ serialized_request_ = serialized_request;
+ result_callback_ = result_callback;
+ error_callback_ = error_callback;
+ OAuth2ApiCallFlow::Start(context, access_token);
+}
+
+GURL CryptAuthApiCallFlow::CreateApiCallUrl() {
+ return request_url_;
+}
+
+std::string CryptAuthApiCallFlow::CreateApiCallBody() {
+ return serialized_request_;
+}
+
+std::string CryptAuthApiCallFlow::CreateApiCallBodyContentType() {
+ return "application/x-protobuf";
+}
+
+net::URLFetcher::RequestType CryptAuthApiCallFlow::GetRequestTypeForBody(
+ const std::string& body) {
+ return net::URLFetcher::POST;
+}
+
+void CryptAuthApiCallFlow::ProcessApiCallSuccess(
+ const net::URLFetcher* source) {
+ std::string serialized_response;
+ if (!source->GetResponseAsString(&serialized_response)) {
+ error_callback_.Run(kResponseBodyError);
+ return;
+ }
+ result_callback_.Run(serialized_response);
+}
+
+void CryptAuthApiCallFlow::ProcessApiCallFailure(
+ const net::URLFetcher* source) {
+ std::string error_message;
+ if (source->GetStatus().status() == net::URLRequestStatus::SUCCESS) {
+ error_message =
+ kHttpStatusErrorPrefix + base::IntToString(source->GetResponseCode());
+ } else {
+ error_message = kRequestFailedError;
+ }
+
+ std::string response;
+ source->GetResponseAsString(&response);
+ PA_LOG(INFO) << "API call failed:\n" << response;
+ error_callback_.Run(error_message);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_api_call_flow.h b/chromium/components/cryptauth/cryptauth_api_call_flow.h
new file mode 100644
index 00000000000..07a487edca6
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_api_call_flow.h
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_CRYPTAUTH_API_CALL_FLOW_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_API_CALL_FLOW_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "google_apis/gaia/oauth2_api_call_flow.h"
+
+namespace cryptauth {
+
+// Google API call flow implementation underlying all CryptAuth API calls.
+// CryptAuth is a Google service that manages authorization and cryptographic
+// credentials for users' devices (eg. public keys).
+class CryptAuthApiCallFlow : public OAuth2ApiCallFlow {
+ public:
+ typedef base::Callback<void(const std::string& serialized_response)>
+ ResultCallback;
+ typedef base::Callback<void(const std::string& error_message)> ErrorCallback;
+
+ CryptAuthApiCallFlow();
+ ~CryptAuthApiCallFlow() override;
+
+ // Starts the API call.
+ // request_url: The URL endpoint of the API request.
+ // context: The URL context used to make the request.
+ // access_token: The access token for whom to make the to make the request.
+ // serialized_request: A serialized proto containing the request data.
+ // result_callback: Called when the flow completes successfully with a
+ // serialized response proto.
+ // error_callback: Called when the flow completes with an error.
+ virtual void Start(const GURL& request_url,
+ net::URLRequestContextGetter* context,
+ const std::string& access_token,
+ const std::string& serialized_request,
+ const ResultCallback& result_callback,
+ const ErrorCallback& error_callback);
+
+ protected:
+ // Reduce the visibility of OAuth2ApiCallFlow::Start() to avoid exposing
+ // overloaded methods.
+ using OAuth2ApiCallFlow::Start;
+
+ // google_apis::OAuth2ApiCallFlow:
+ GURL CreateApiCallUrl() override;
+ std::string CreateApiCallBody() override;
+ std::string CreateApiCallBodyContentType() override;
+ net::URLFetcher::RequestType GetRequestTypeForBody(
+ const std::string& body) override;
+ void ProcessApiCallSuccess(const net::URLFetcher* source) override;
+ void ProcessApiCallFailure(const net::URLFetcher* source) override;
+
+ private:
+ // The URL of the CryptAuth endpoint serving the request.
+ GURL request_url_;
+
+ // Serialized request message proto that will be sent in the API request.
+ std::string serialized_request_;
+
+ // Callback invoked with the serialized response message proto when the flow
+ // completes successfully.
+ ResultCallback result_callback_;
+
+ // Callback invoked with an error message when the flow fails.
+ ErrorCallback error_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthApiCallFlow);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_API_CALL_FLOW_H_
diff --git a/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc b/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc
new file mode 100644
index 00000000000..cbc6701e4f8
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc
@@ -0,0 +1,153 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/cryptauth_api_call_flow.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/test/test_simple_task_runner.h"
+#include "net/base/net_errors.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/gtest/include/gtest/gtest.h"
+
+namespace cryptauth {
+
+namespace {
+
+const char kSerializedRequestProto[] = "serialized_request_proto";
+const char kSerializedResponseProto[] = "result_proto";
+const char kRequestUrl[] = "https://googleapis.com/cryptauth/test";
+
+} // namespace
+
+class CryptAuthApiCallFlowTest
+ : public testing::Test,
+ public net::TestURLFetcherDelegateForTests {
+ protected:
+ CryptAuthApiCallFlowTest()
+ : url_request_context_getter_(new net::TestURLRequestContextGetter(
+ new base::TestSimpleTaskRunner())) {}
+
+ void SetUp() override {
+ // The TestURLFetcherFactory will override the global URLFetcherFactory for
+ // the entire test.
+ url_fetcher_factory_.reset(new net::TestURLFetcherFactory());
+ url_fetcher_factory_->SetDelegateForTests(this);
+ }
+
+ void StartApiCallFlow() {
+ StartApiCallFlowWithRequest(kSerializedRequestProto);
+ }
+
+ void StartApiCallFlowWithRequest(const std::string& serialized_request) {
+ flow_.Start(GURL(kRequestUrl), url_request_context_getter_.get(),
+ "access_token", serialized_request,
+ base::Bind(&CryptAuthApiCallFlowTest::OnResult,
+ base::Unretained(this)),
+ base::Bind(&CryptAuthApiCallFlowTest::OnError,
+ base::Unretained(this)));
+ // URLFetcher object for the API request should be created.
+ CheckCryptAuthHttpRequest(serialized_request);
+ }
+
+ void OnResult(const std::string& result) {
+ EXPECT_FALSE(result_);
+ result_.reset(new std::string(result));
+ }
+
+ void OnError(const std::string& error_message) {
+ EXPECT_FALSE(error_message_);
+ error_message_.reset(new std::string(error_message));
+ }
+
+ void CheckCryptAuthHttpRequest(const std::string& serialized_request) {
+ ASSERT_TRUE(url_fetcher_);
+ EXPECT_EQ(GURL(kRequestUrl), url_fetcher_->GetOriginalURL());
+ EXPECT_EQ(serialized_request, url_fetcher_->upload_data());
+
+ net::HttpRequestHeaders request_headers;
+ url_fetcher_->GetExtraRequestHeaders(&request_headers);
+
+ EXPECT_EQ("application/x-protobuf", url_fetcher_->upload_content_type());
+ }
+
+ // Responds to the current HTTP request. If the |error| is not |net::OK|, then
+ // the |response_code| and |response_string| arguments will be ignored.
+ void CompleteCurrentRequest(net::Error error,
+ int response_code,
+ const std::string& response_string) {
+ ASSERT_TRUE(url_fetcher_);
+ net::TestURLFetcher* url_fetcher = url_fetcher_;
+ url_fetcher_ = NULL;
+ url_fetcher->set_status(net::URLRequestStatus::FromError(error));
+ if (error == net::OK) {
+ url_fetcher->set_response_code(response_code);
+ url_fetcher->SetResponseString(response_string);
+ }
+ url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+ }
+
+ // net::TestURLFetcherDelegateForTests overrides.
+ void OnRequestStart(int fetcher_id) override {
+ url_fetcher_ = url_fetcher_factory_->GetFetcherByID(fetcher_id);
+ }
+
+ void OnChunkUpload(int fetcher_id) override {}
+
+ void OnRequestEnd(int fetcher_id) override {}
+
+ net::TestURLFetcher* url_fetcher_;
+ std::unique_ptr<std::string> result_;
+ std::unique_ptr<std::string> error_message_;
+
+ private:
+ scoped_refptr<net::TestURLRequestContextGetter> url_request_context_getter_;
+ std::unique_ptr<net::TestURLFetcherFactory> url_fetcher_factory_;
+ CryptAuthApiCallFlow flow_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthApiCallFlowTest);
+};
+
+TEST_F(CryptAuthApiCallFlowTest, RequestSuccess) {
+ StartApiCallFlow();
+ CompleteCurrentRequest(net::OK, net::HTTP_OK, kSerializedResponseProto);
+ EXPECT_EQ(kSerializedResponseProto, *result_);
+ EXPECT_FALSE(error_message_);
+}
+
+TEST_F(CryptAuthApiCallFlowTest, RequestFailure) {
+ StartApiCallFlow();
+ CompleteCurrentRequest(net::ERR_FAILED, 0, std::string());
+ EXPECT_FALSE(result_);
+ EXPECT_EQ("Request failed", *error_message_);
+}
+
+TEST_F(CryptAuthApiCallFlowTest, RequestStatus500) {
+ StartApiCallFlow();
+ CompleteCurrentRequest(net::OK, net::HTTP_INTERNAL_SERVER_ERROR,
+ "CryptAuth Meltdown.");
+ EXPECT_FALSE(result_);
+ EXPECT_EQ("HTTP status: 500", *error_message_);
+}
+
+// The empty string is a valid protocol buffer message serialization.
+TEST_F(CryptAuthApiCallFlowTest, RequestWithNoBody) {
+ StartApiCallFlowWithRequest(std::string());
+ CompleteCurrentRequest(net::OK, net::HTTP_OK, kSerializedResponseProto);
+ EXPECT_EQ(kSerializedResponseProto, *result_);
+ EXPECT_FALSE(error_message_);
+}
+
+// The empty string is a valid protocol buffer message serialization.
+TEST_F(CryptAuthApiCallFlowTest, ResponseWithNoBody) {
+ StartApiCallFlow();
+ CompleteCurrentRequest(net::OK, net::HTTP_OK, std::string());
+ EXPECT_EQ(std::string(), *result_);
+ EXPECT_FALSE(error_message_);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_client.h b/chromium/components/cryptauth/cryptauth_client.h
new file mode 100644
index 00000000000..98c83d417ba
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_client.h
@@ -0,0 +1,107 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_CRYPTAUTH_CLIENT_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_CLIENT_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+
+namespace cryptauth {
+class GetMyDevicesRequest;
+class GetMyDevicesResponse;
+class FindEligibleUnlockDevicesRequest;
+class FindEligibleUnlockDevicesResponse;
+class SendDeviceSyncTickleRequest;
+class SendDeviceSyncTickleResponse;
+class ToggleEasyUnlockRequest;
+class ToggleEasyUnlockResponse;
+class SetupEnrollmentRequest;
+class SetupEnrollmentResponse;
+class FinishEnrollmentRequest;
+class FinishEnrollmentResponse;
+}
+
+namespace cryptauth {
+
+// Interface for making API requests to the CryptAuth service, which
+// manages cryptographic credentials (ie. public keys) for a user's devices.
+// Implmentations shall only processes a single request, so create a new
+// instance for each request you make. DO NOT REUSE.
+// For documentation on each API call, see
+// components/cryptauth/proto/cryptauth_api.proto
+class CryptAuthClient {
+ public:
+ typedef base::Callback<void(const std::string&)> ErrorCallback;
+
+ virtual ~CryptAuthClient() {}
+
+ // GetMyDevices
+ typedef base::Callback<void(const GetMyDevicesResponse&)>
+ GetMyDevicesCallback;
+ virtual void GetMyDevices(const GetMyDevicesRequest& request,
+ const GetMyDevicesCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // FindEligibleUnlockDevices
+ typedef base::Callback<void(
+ const FindEligibleUnlockDevicesResponse&)>
+ FindEligibleUnlockDevicesCallback;
+ virtual void FindEligibleUnlockDevices(
+ const FindEligibleUnlockDevicesRequest& request,
+ const FindEligibleUnlockDevicesCallback& 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;
+
+ // ToggleEasyUnlock
+ typedef base::Callback<void(const ToggleEasyUnlockResponse&)>
+ ToggleEasyUnlockCallback;
+ virtual void ToggleEasyUnlock(
+ const ToggleEasyUnlockRequest& request,
+ const ToggleEasyUnlockCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // SetupEnrollment
+ typedef base::Callback<void(const SetupEnrollmentResponse&)>
+ SetupEnrollmentCallback;
+ virtual void SetupEnrollment(const SetupEnrollmentRequest& request,
+ const SetupEnrollmentCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // FinishEnrollment
+ typedef base::Callback<void(const FinishEnrollmentResponse&)>
+ FinishEnrollmentCallback;
+ virtual void FinishEnrollment(
+ const FinishEnrollmentRequest& request,
+ const FinishEnrollmentCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Returns the access token used to make the request. If no request has been
+ // made yet, this function will return an empty string.
+ virtual std::string GetAccessTokenUsed() = 0;
+};
+
+// Interface for creating CryptAuthClient instances. Because each
+// CryptAuthClient instance can only be used for one API call, a factory makes
+// it easier to make multiple requests in sequence or in parallel.
+class CryptAuthClientFactory {
+ public:
+ virtual ~CryptAuthClientFactory() {}
+
+ virtual std::unique_ptr<CryptAuthClient> CreateInstance() = 0;
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_CLIENT_H_
diff --git a/chromium/components/cryptauth/cryptauth_client_impl.cc b/chromium/components/cryptauth/cryptauth_client_impl.cc
new file mode 100644
index 00000000000..259838598ef
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_client_impl.cc
@@ -0,0 +1,203 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/cryptauth_client_impl.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/cryptauth_access_token_fetcher_impl.h"
+#include "components/cryptauth/switches.h"
+
+namespace cryptauth {
+
+namespace {
+
+// Default URL of Google APIs endpoint hosting CryptAuth.
+const char kDefaultCryptAuthHTTPHost[] = "https://www.googleapis.com";
+
+// URL subpath hosting the CryptAuth service.
+const char kCryptAuthPath[] = "cryptauth/v1/";
+
+// URL subpaths for each CryptAuth API.
+const char kGetMyDevicesPath[] = "deviceSync/getmydevices";
+const char kFindEligibleUnlockDevicesPath[] =
+ "deviceSync/findeligibleunlockdevices";
+const char kSendDeviceSyncTicklePath[] = "deviceSync/senddevicesynctickle";
+const char kToggleEasyUnlockPath[] = "deviceSync/toggleeasyunlock";
+const char kSetupEnrollmentPath[] = "enrollment/setup";
+const char kFinishEnrollmentPath[] = "enrollment/finish";
+
+// Query string of the API URL indicating that the response should be in a
+// serialized protobuf format.
+const char kQueryProtobuf[] = "?alt=proto";
+
+// Creates the full CryptAuth URL for endpoint to the API with |request_path|.
+GURL CreateRequestUrl(const std::string& request_path) {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ GURL google_apis_url =
+ GURL(command_line->HasSwitch(switches::kCryptAuthHTTPHost)
+ ? command_line->GetSwitchValueASCII(switches::kCryptAuthHTTPHost)
+ : kDefaultCryptAuthHTTPHost);
+ return google_apis_url.Resolve(kCryptAuthPath + request_path +
+ kQueryProtobuf);
+}
+
+} // namespace
+
+CryptAuthClientImpl::CryptAuthClientImpl(
+ std::unique_ptr<CryptAuthApiCallFlow> api_call_flow,
+ std::unique_ptr<CryptAuthAccessTokenFetcher> access_token_fetcher,
+ scoped_refptr<net::URLRequestContextGetter> url_request_context,
+ const DeviceClassifier& device_classifier)
+ : api_call_flow_(std::move(api_call_flow)),
+ access_token_fetcher_(std::move(access_token_fetcher)),
+ url_request_context_(url_request_context),
+ device_classifier_(device_classifier),
+ has_call_started_(false),
+ weak_ptr_factory_(this) {}
+
+CryptAuthClientImpl::~CryptAuthClientImpl() {
+}
+
+void CryptAuthClientImpl::GetMyDevices(
+ const GetMyDevicesRequest& request,
+ const GetMyDevicesCallback& callback,
+ const ErrorCallback& error_callback) {
+ MakeApiCall(kGetMyDevicesPath, request, callback, error_callback);
+}
+
+void CryptAuthClientImpl::FindEligibleUnlockDevices(
+ const FindEligibleUnlockDevicesRequest& request,
+ const FindEligibleUnlockDevicesCallback& callback,
+ const ErrorCallback& error_callback) {
+ MakeApiCall(kFindEligibleUnlockDevicesPath, request, callback,
+ error_callback);
+}
+
+void CryptAuthClientImpl::SendDeviceSyncTickle(
+ const SendDeviceSyncTickleRequest& request,
+ const SendDeviceSyncTickleCallback& callback,
+ const ErrorCallback& error_callback) {
+ MakeApiCall(kSendDeviceSyncTicklePath, request, callback, error_callback);
+}
+
+void CryptAuthClientImpl::ToggleEasyUnlock(
+ const ToggleEasyUnlockRequest& request,
+ const ToggleEasyUnlockCallback& callback,
+ const ErrorCallback& error_callback) {
+ MakeApiCall(kToggleEasyUnlockPath, request, callback, error_callback);
+}
+
+void CryptAuthClientImpl::SetupEnrollment(
+ const SetupEnrollmentRequest& request,
+ const SetupEnrollmentCallback& callback,
+ const ErrorCallback& error_callback) {
+ MakeApiCall(kSetupEnrollmentPath, request, callback, error_callback);
+}
+
+void CryptAuthClientImpl::FinishEnrollment(
+ const FinishEnrollmentRequest& request,
+ const FinishEnrollmentCallback& callback,
+ const ErrorCallback& error_callback) {
+ MakeApiCall(kFinishEnrollmentPath, request, callback, error_callback);
+}
+
+std::string CryptAuthClientImpl::GetAccessTokenUsed() {
+ return access_token_used_;
+}
+
+template <class RequestProto, class ResponseProto>
+void CryptAuthClientImpl::MakeApiCall(
+ const std::string& request_path,
+ const RequestProto& request_proto,
+ const base::Callback<void(const ResponseProto&)>& response_callback,
+ const ErrorCallback& error_callback) {
+ if (has_call_started_) {
+ error_callback.Run(
+ "Client has been used for another request. Do not reuse.");
+ return;
+ }
+ has_call_started_ = true;
+
+ // The |device_classifier| field must be present for all CryptAuth requests.
+ RequestProto request_copy(request_proto);
+ request_copy.mutable_device_classifier()->CopyFrom(device_classifier_);
+
+ std::string serialized_request;
+ if (!request_copy.SerializeToString(&serialized_request)) {
+ error_callback.Run(std::string("Failed to serialize ") +
+ request_proto.GetTypeName() + " proto.");
+ return;
+ }
+
+ request_path_ = request_path;
+ error_callback_ = error_callback;
+ access_token_fetcher_->FetchAccessToken(base::Bind(
+ &CryptAuthClientImpl::OnAccessTokenFetched<ResponseProto>,
+ weak_ptr_factory_.GetWeakPtr(), serialized_request, response_callback));
+}
+
+template <class ResponseProto>
+void CryptAuthClientImpl::OnAccessTokenFetched(
+ const std::string& serialized_request,
+ const base::Callback<void(const ResponseProto&)>& response_callback,
+ const std::string& access_token) {
+ if (access_token.empty()) {
+ OnApiCallFailed("Failed to get a valid access token.");
+ return;
+ }
+ access_token_used_ = access_token;
+
+ api_call_flow_->Start(
+ CreateRequestUrl(request_path_), url_request_context_.get(), access_token,
+ serialized_request,
+ base::Bind(&CryptAuthClientImpl::OnFlowSuccess<ResponseProto>,
+ weak_ptr_factory_.GetWeakPtr(), response_callback),
+ base::Bind(&CryptAuthClientImpl::OnApiCallFailed,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+template <class ResponseProto>
+void CryptAuthClientImpl::OnFlowSuccess(
+ const base::Callback<void(const ResponseProto&)>& result_callback,
+ const std::string& serialized_response) {
+ ResponseProto response;
+ if (!response.ParseFromString(serialized_response)) {
+ OnApiCallFailed("Failed to parse response proto.");
+ return;
+ }
+ result_callback.Run(response);
+};
+
+void CryptAuthClientImpl::OnApiCallFailed(const std::string& error_message) {
+ error_callback_.Run(error_message);
+}
+
+// CryptAuthClientFactoryImpl
+CryptAuthClientFactoryImpl::CryptAuthClientFactoryImpl(
+ OAuth2TokenService* token_service,
+ const std::string& account_id,
+ scoped_refptr<net::URLRequestContextGetter> url_request_context,
+ const DeviceClassifier& device_classifier)
+ : token_service_(token_service),
+ account_id_(account_id),
+ url_request_context_(url_request_context),
+ device_classifier_(device_classifier) {
+}
+
+CryptAuthClientFactoryImpl::~CryptAuthClientFactoryImpl() {
+}
+
+std::unique_ptr<CryptAuthClient> CryptAuthClientFactoryImpl::CreateInstance() {
+ return base::MakeUnique<CryptAuthClientImpl>(
+ base::WrapUnique(new CryptAuthApiCallFlow()),
+ base::WrapUnique(
+ new CryptAuthAccessTokenFetcherImpl(token_service_, account_id_)),
+ url_request_context_, device_classifier_);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_client_impl.h b/chromium/components/cryptauth/cryptauth_client_impl.h
new file mode 100644
index 00000000000..d880a118e24
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_client_impl.h
@@ -0,0 +1,150 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_CRYPTAUTH_CLIENT_IMPL_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_CLIENT_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/cryptauth/cryptauth_access_token_fetcher.h"
+#include "components/cryptauth/cryptauth_api_call_flow.h"
+#include "components/cryptauth/cryptauth_client.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "net/url_request/url_request_context_getter.h"
+
+class OAuth2TokenService;
+
+namespace cryptauth {
+
+// Implementation of CryptAuthClient.
+// Note: There is no need to set the |device_classifier| field in request
+// messages. CryptAuthClient will fill this field for all requests.
+class CryptAuthClientImpl : public CryptAuthClient {
+ public:
+ typedef base::Callback<void(const std::string&)> ErrorCallback;
+
+ // Creates the client using |url_request_context| to make the HTTP request
+ // through |api_call_flow|. CryptAuthClientImpl takes ownership of
+ // |access_token_fetcher|, which provides the access token authorizing
+ // CryptAuth requests. The |device_classifier| argument contains basic device
+ // information of the caller (e.g. version and device type).
+ CryptAuthClientImpl(
+ std::unique_ptr<CryptAuthApiCallFlow> api_call_flow,
+ std::unique_ptr<CryptAuthAccessTokenFetcher> access_token_fetcher,
+ scoped_refptr<net::URLRequestContextGetter> url_request_context,
+ const DeviceClassifier& device_classifier);
+ ~CryptAuthClientImpl() override;
+
+ // CryptAuthClient:
+ void GetMyDevices(const GetMyDevicesRequest& request,
+ const GetMyDevicesCallback& callback,
+ const ErrorCallback& error_callback) override;
+ void FindEligibleUnlockDevices(
+ const FindEligibleUnlockDevicesRequest& request,
+ const FindEligibleUnlockDevicesCallback& callback,
+ const ErrorCallback& error_callback) override;
+ void SendDeviceSyncTickle(
+ const SendDeviceSyncTickleRequest& request,
+ const SendDeviceSyncTickleCallback& callback,
+ const ErrorCallback& error_callback) override;
+ void ToggleEasyUnlock(const ToggleEasyUnlockRequest& request,
+ const ToggleEasyUnlockCallback& callback,
+ const ErrorCallback& error_callback) override;
+ void SetupEnrollment(const SetupEnrollmentRequest& request,
+ const SetupEnrollmentCallback& callback,
+ const ErrorCallback& error_callback) override;
+ void FinishEnrollment(const FinishEnrollmentRequest& request,
+ const FinishEnrollmentCallback& callback,
+ const ErrorCallback& error_callback) override;
+ std::string GetAccessTokenUsed() override;
+
+ private:
+ // Starts a call to the API given by |request_path|, with the templated
+ // request and response types. The client first fetches the access token and
+ // then makes the HTTP request.
+ template <class RequestProto, class ResponseProto>
+ void MakeApiCall(
+ const std::string& request_path,
+ const RequestProto& request_proto,
+ const base::Callback<void(const ResponseProto&)>& response_callback,
+ const ErrorCallback& error_callback);
+
+ // Called when the access token is obtained so the API request can be made.
+ template <class ResponseProto>
+ void OnAccessTokenFetched(
+ const std::string& serialized_request,
+ const base::Callback<void(const ResponseProto&)>& response_callback,
+ const std::string& access_token);
+
+ // Called with CryptAuthApiCallFlow completes successfully to deserialize and
+ // return the result.
+ template <class ResponseProto>
+ void OnFlowSuccess(
+ const base::Callback<void(const ResponseProto&)>& result_callback,
+ const std::string& serialized_response);
+
+ // Called when the current API call fails at any step.
+ void OnApiCallFailed(const std::string& error_message);
+
+ // Constructs and executes the actual HTTP request.
+ std::unique_ptr<CryptAuthApiCallFlow> api_call_flow_;
+
+ // Fetches the access token authorizing the API calls.
+ std::unique_ptr<CryptAuthAccessTokenFetcher> access_token_fetcher_;
+
+ // The context for network requests.
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_;
+
+ // Contains basic device info of the client making the request that is sent to
+ // CryptAuth with each API call.
+ const DeviceClassifier device_classifier_;
+
+ // True if an API call has been started. Remains true even after the API call
+ // completes.
+ bool has_call_started_;
+
+ // URL path of the current request.
+ std::string request_path_;
+
+ // The access token fetched by |access_token_fetcher_|.
+ std::string access_token_used_;
+
+ // Called when the current request fails.
+ ErrorCallback error_callback_;
+
+ base::WeakPtrFactory<CryptAuthClientImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthClientImpl);
+};
+
+// Implementation of CryptAuthClientFactory.
+class CryptAuthClientFactoryImpl : public CryptAuthClientFactory {
+ public:
+ // |token_service|: Gets the user's access token.
+ // Not owned, so |token_service| needs to outlive this object.
+ // |account_id|: The account id of the user.
+ // |url_request_context|: The request context to make the HTTP requests.
+ // |device_classifier|: Contains basic device information of the client.
+ CryptAuthClientFactoryImpl(
+ OAuth2TokenService* token_service,
+ const std::string& account_id,
+ scoped_refptr<net::URLRequestContextGetter> url_request_context,
+ const DeviceClassifier& device_classifier);
+ ~CryptAuthClientFactoryImpl() override;
+
+ // CryptAuthClientFactory:
+ std::unique_ptr<CryptAuthClient> CreateInstance() override;
+
+ private:
+ OAuth2TokenService* token_service_;
+ const std::string account_id_;
+ const scoped_refptr<net::URLRequestContextGetter> url_request_context_;
+ const DeviceClassifier device_classifier_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthClientFactoryImpl);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_CLIENT_IMPL_H_
diff --git a/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc b/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc
new file mode 100644
index 00000000000..20224c00295
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc
@@ -0,0 +1,562 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/cryptauth_client_impl.h"
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/null_task_runner.h"
+#include "components/cryptauth/cryptauth_access_token_fetcher.h"
+#include "components/cryptauth/cryptauth_api_call_flow.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/switches.h"
+#include "google_apis/gaia/fake_oauth2_token_service.h"
+#include "net/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"
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArg;
+using testing::StrictMock;
+
+namespace cryptauth {
+
+namespace {
+
+const char kTestGoogleApisUrl[] = "https://www.testgoogleapis.com";
+const char kAccessToken[] = "access_token";
+const char kPublicKey1[] = "public_key1";
+const char kPublicKey2[] = "public_key2";
+const char kBluetoothAddress1[] = "AA:AA:AA:AA:AA:AA";
+const char kBluetoothAddress2[] = "BB:BB:BB:BB:BB:BB";
+
+// Values for the DeviceClassifier field.
+const int kDeviceOsVersionCode = 100;
+const int kDeviceSoftwareVersionCode = 200;
+const char kDeviceSoftwarePackage[] = "cryptauth_client_unittest";
+const DeviceType kDeviceType = CHROME;
+
+// CryptAuthAccessTokenFetcher implementation simply returning a predetermined
+// access token.
+class FakeCryptAuthAccessTokenFetcher : public CryptAuthAccessTokenFetcher {
+ public:
+ FakeCryptAuthAccessTokenFetcher() : access_token_(kAccessToken) {}
+
+ void FetchAccessToken(const AccessTokenCallback& callback) override {
+ callback.Run(access_token_);
+ }
+
+ void set_access_token(const std::string& access_token) {
+ access_token_ = access_token;
+ };
+
+ private:
+ std::string access_token_;
+};
+
+// Mock CryptAuthApiCallFlow, which handles the HTTP requests to CryptAuth.
+class MockCryptAuthApiCallFlow : public CryptAuthApiCallFlow {
+ public:
+ MockCryptAuthApiCallFlow() : CryptAuthApiCallFlow() {}
+ virtual ~MockCryptAuthApiCallFlow() {}
+
+ MOCK_METHOD6(Start,
+ void(const GURL&,
+ net::URLRequestContextGetter* context,
+ const std::string& access_token,
+ const std::string& serialized_request,
+ const ResultCallback& result_callback,
+ const ErrorCallback& error_callback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockCryptAuthApiCallFlow);
+};
+
+// Callback that should never be invoked.
+template <class T>
+void NotCalled(const T& type) {
+ EXPECT_TRUE(false);
+}
+
+// Callback that saves the result returned by CryptAuthClient.
+template <class T>
+void SaveResult(T* out, const T& result) {
+ *out = result;
+}
+
+} // namespace
+
+class CryptAuthClientTest : public testing::Test {
+ protected:
+ CryptAuthClientTest()
+ : access_token_fetcher_(new FakeCryptAuthAccessTokenFetcher()),
+ api_call_flow_(new StrictMock<MockCryptAuthApiCallFlow>()),
+ url_request_context_(
+ new net::TestURLRequestContextGetter(new base::NullTaskRunner())),
+ serialized_request_(std::string()) {}
+
+ void SetUp() override {
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kCryptAuthHTTPHost, kTestGoogleApisUrl);
+
+ DeviceClassifier device_classifier;
+ device_classifier.set_device_os_version_code(kDeviceOsVersionCode);
+ device_classifier.set_device_software_version_code(
+ kDeviceSoftwareVersionCode);
+ device_classifier.set_device_software_package(kDeviceSoftwarePackage);
+ device_classifier.set_device_type(kDeviceType);
+
+ client_.reset(
+ new CryptAuthClientImpl(base::WrapUnique(api_call_flow_),
+ base::WrapUnique(access_token_fetcher_),
+ url_request_context_, device_classifier));
+ }
+
+ // Sets up an expectation and captures a CryptAuth API request to
+ // |request_url|.
+ void ExpectRequest(const std::string& request_url) {
+ GURL url(request_url);
+ EXPECT_CALL(*api_call_flow_,
+ Start(url, url_request_context_.get(), kAccessToken, _, _, _))
+ .WillOnce(DoAll(SaveArg<3>(&serialized_request_),
+ SaveArg<4>(&flow_result_callback_),
+ SaveArg<5>(&flow_error_callback_)));
+ }
+
+ // Returns |response_proto| as the result to the current API request.
+ // ExpectResult() must have been called first.
+ void FinishApiCallFlow(const google::protobuf::MessageLite* response_proto) {
+ flow_result_callback_.Run(response_proto->SerializeAsString());
+ }
+
+ // Ends the current API request with |error_message|. ExpectResult() must have
+ // been called first.
+ void FailApiCallFlow(const std::string& error_message) {
+ flow_error_callback_.Run(error_message);
+ }
+
+ protected:
+ // Owned by |client_|.
+ FakeCryptAuthAccessTokenFetcher* access_token_fetcher_;
+ // Owned by |client_|.
+ StrictMock<MockCryptAuthApiCallFlow>* api_call_flow_;
+
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_;
+ std::unique_ptr<CryptAuthClient> client_;
+
+ std::string serialized_request_;
+ CryptAuthApiCallFlow::ResultCallback flow_result_callback_;
+ CryptAuthApiCallFlow::ErrorCallback flow_error_callback_;
+};
+
+TEST_F(CryptAuthClientTest, GetMyDevicesSuccess) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "getmydevices?alt=proto");
+
+ GetMyDevicesResponse result_proto;
+ GetMyDevicesRequest request_proto;
+ request_proto.set_allow_stale_read(true);
+ client_->GetMyDevices(
+ request_proto,
+ base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
+ base::Bind(&NotCalled<std::string>));
+
+ GetMyDevicesRequest expected_request;
+ EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
+ EXPECT_TRUE(expected_request.allow_stale_read());
+
+ // Return two devices, one unlock key and one unlockable device.
+ {
+ GetMyDevicesResponse response_proto;
+ response_proto.add_devices();
+ response_proto.mutable_devices(0)->set_public_key(kPublicKey1);
+ response_proto.mutable_devices(0)->set_unlock_key(true);
+ response_proto.mutable_devices(0)
+ ->set_bluetooth_address(kBluetoothAddress1);
+ response_proto.add_devices();
+ response_proto.mutable_devices(1)->set_public_key(kPublicKey2);
+ response_proto.mutable_devices(1)->set_unlockable(true);
+ FinishApiCallFlow(&response_proto);
+ }
+
+ // Check that the result received in callback is the same as the response.
+ ASSERT_EQ(2, result_proto.devices_size());
+ EXPECT_EQ(kPublicKey1, result_proto.devices(0).public_key());
+ EXPECT_TRUE(result_proto.devices(0).unlock_key());
+ EXPECT_EQ(kBluetoothAddress1, result_proto.devices(0).bluetooth_address());
+ EXPECT_EQ(kPublicKey2, result_proto.devices(1).public_key());
+ EXPECT_TRUE(result_proto.devices(1).unlockable());
+}
+
+TEST_F(CryptAuthClientTest, GetMyDevicesFailure) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "getmydevices?alt=proto");
+
+ std::string error_message;
+ client_->GetMyDevices(GetMyDevicesRequest(),
+ base::Bind(&NotCalled<GetMyDevicesResponse>),
+ base::Bind(&SaveResult<std::string>, &error_message));
+
+ std::string kStatus500Error("HTTP status: 500");
+ FailApiCallFlow(kStatus500Error);
+ EXPECT_EQ(kStatus500Error, error_message);
+}
+
+TEST_F(CryptAuthClientTest, FindEligibleUnlockDevicesSuccess) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "findeligibleunlockdevices?alt=proto");
+
+ FindEligibleUnlockDevicesResponse result_proto;
+ FindEligibleUnlockDevicesRequest request_proto;
+ request_proto.set_callback_bluetooth_address(kBluetoothAddress2);
+ client_->FindEligibleUnlockDevices(
+ request_proto,
+ base::Bind(&SaveResult<FindEligibleUnlockDevicesResponse>,
+ &result_proto),
+ base::Bind(&NotCalled<std::string>));
+
+ FindEligibleUnlockDevicesRequest expected_request;
+ EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
+ EXPECT_EQ(kBluetoothAddress2, expected_request.callback_bluetooth_address());
+
+ // Return a response proto with one eligible and one ineligible device.
+ FindEligibleUnlockDevicesResponse response_proto;
+ response_proto.add_eligible_devices();
+ response_proto.mutable_eligible_devices(0)->set_public_key(kPublicKey1);
+
+ const std::string kIneligibilityReason = "You require more vespine gas.";
+ response_proto.add_ineligible_devices();
+ response_proto.mutable_ineligible_devices(0)
+ ->mutable_device()
+ ->set_public_key(kPublicKey2);
+ response_proto.mutable_ineligible_devices(0)
+ ->add_reasons(kIneligibilityReason);
+ FinishApiCallFlow(&response_proto);
+
+ // Check that the result received in callback is the same as the response.
+ ASSERT_EQ(1, result_proto.eligible_devices_size());
+ EXPECT_EQ(kPublicKey1, result_proto.eligible_devices(0).public_key());
+ ASSERT_EQ(1, result_proto.ineligible_devices_size());
+ EXPECT_EQ(kPublicKey2,
+ result_proto.ineligible_devices(0).device().public_key());
+ ASSERT_EQ(1, result_proto.ineligible_devices(0).reasons_size());
+ EXPECT_EQ(kIneligibilityReason,
+ result_proto.ineligible_devices(0).reasons(0));
+}
+
+TEST_F(CryptAuthClientTest, FindEligibleUnlockDevicesFailure) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "findeligibleunlockdevices?alt=proto");
+
+ std::string error_message;
+ FindEligibleUnlockDevicesRequest request_proto;
+ request_proto.set_callback_bluetooth_address(kBluetoothAddress1);
+ client_->FindEligibleUnlockDevices(
+ request_proto,
+ base::Bind(&NotCalled<FindEligibleUnlockDevicesResponse>),
+ base::Bind(&SaveResult<std::string>, &error_message));
+
+ std::string kStatus403Error("HTTP status: 403");
+ FailApiCallFlow(kStatus403Error);
+ EXPECT_EQ(kStatus403Error, error_message);
+}
+
+TEST_F(CryptAuthClientTest, SendDeviceSyncTickleSuccess) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "senddevicesynctickle?alt=proto");
+
+ SendDeviceSyncTickleResponse result_proto;
+ client_->SendDeviceSyncTickle(
+ SendDeviceSyncTickleRequest(),
+ base::Bind(&SaveResult<SendDeviceSyncTickleResponse>,
+ &result_proto),
+ base::Bind(&NotCalled<std::string>));
+
+ SendDeviceSyncTickleRequest expected_request;
+ EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
+
+ SendDeviceSyncTickleResponse response_proto;
+ FinishApiCallFlow(&response_proto);
+}
+
+TEST_F(CryptAuthClientTest, ToggleEasyUnlockSuccess) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "toggleeasyunlock?alt=proto");
+
+ ToggleEasyUnlockResponse result_proto;
+ ToggleEasyUnlockRequest request_proto;
+ request_proto.set_enable(true);
+ request_proto.set_apply_to_all(false);
+ request_proto.set_public_key(kPublicKey1);
+ client_->ToggleEasyUnlock(
+ request_proto,
+ base::Bind(&SaveResult<ToggleEasyUnlockResponse>,
+ &result_proto),
+ base::Bind(&NotCalled<std::string>));
+
+ ToggleEasyUnlockRequest expected_request;
+ EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
+ EXPECT_TRUE(expected_request.enable());
+ EXPECT_EQ(kPublicKey1, expected_request.public_key());
+ EXPECT_FALSE(expected_request.apply_to_all());
+
+ ToggleEasyUnlockResponse response_proto;
+ FinishApiCallFlow(&response_proto);
+}
+
+TEST_F(CryptAuthClientTest, SetupEnrollmentSuccess) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/enrollment/"
+ "setup?alt=proto");
+
+ std::string kApplicationId = "mkaes";
+ std::vector<std::string> supported_protocols;
+ supported_protocols.push_back("gcmV1");
+ supported_protocols.push_back("testProtocol");
+
+ SetupEnrollmentResponse result_proto;
+ SetupEnrollmentRequest request_proto;
+ request_proto.set_application_id(kApplicationId);
+ request_proto.add_types("gcmV1");
+ request_proto.add_types("testProtocol");
+ client_->SetupEnrollment(
+ request_proto, base::Bind(&SaveResult<SetupEnrollmentResponse>,
+ &result_proto),
+ base::Bind(&NotCalled<std::string>));
+
+ SetupEnrollmentRequest expected_request;
+ EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
+ EXPECT_EQ(kApplicationId, expected_request.application_id());
+ ASSERT_EQ(2, expected_request.types_size());
+ EXPECT_EQ("gcmV1", expected_request.types(0));
+ EXPECT_EQ("testProtocol", expected_request.types(1));
+
+ // Return a fake enrollment session.
+ {
+ SetupEnrollmentResponse response_proto;
+ response_proto.set_status("OK");
+ response_proto.add_infos();
+ response_proto.mutable_infos(0)->set_type("gcmV1");
+ response_proto.mutable_infos(0)->set_enrollment_session_id("session_id");
+ response_proto.mutable_infos(0)->set_server_ephemeral_key("ephemeral_key");
+ FinishApiCallFlow(&response_proto);
+ }
+
+ // Check that the returned proto is the same as the one just created.
+ EXPECT_EQ("OK", result_proto.status());
+ ASSERT_EQ(1, result_proto.infos_size());
+ EXPECT_EQ("gcmV1", result_proto.infos(0).type());
+ EXPECT_EQ("session_id", result_proto.infos(0).enrollment_session_id());
+ EXPECT_EQ("ephemeral_key", result_proto.infos(0).server_ephemeral_key());
+}
+
+TEST_F(CryptAuthClientTest, FinishEnrollmentSuccess) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/enrollment/"
+ "finish?alt=proto");
+
+ const char kEnrollmentSessionId[] = "enrollment_session_id";
+ const char kEnrollmentMessage[] = "enrollment_message";
+ const char kDeviceEphemeralKey[] = "device_ephermal_key";
+ FinishEnrollmentResponse result_proto;
+ FinishEnrollmentRequest request_proto;
+ request_proto.set_enrollment_session_id(kEnrollmentSessionId);
+ request_proto.set_enrollment_message(kEnrollmentMessage);
+ request_proto.set_device_ephemeral_key(kDeviceEphemeralKey);
+ client_->FinishEnrollment(
+ request_proto,
+ base::Bind(&SaveResult<FinishEnrollmentResponse>,
+ &result_proto),
+ base::Bind(&NotCalled<const std::string&>));
+
+ FinishEnrollmentRequest expected_request;
+ EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
+ EXPECT_EQ(kEnrollmentSessionId, expected_request.enrollment_session_id());
+ EXPECT_EQ(kEnrollmentMessage, expected_request.enrollment_message());
+ EXPECT_EQ(kDeviceEphemeralKey, expected_request.device_ephemeral_key());
+
+ {
+ FinishEnrollmentResponse response_proto;
+ response_proto.set_status("OK");
+ FinishApiCallFlow(&response_proto);
+ }
+ EXPECT_EQ("OK", result_proto.status());
+}
+
+TEST_F(CryptAuthClientTest, FetchAccessTokenFailure) {
+ access_token_fetcher_->set_access_token("");
+
+ std::string error_message;
+ client_->GetMyDevices(GetMyDevicesRequest(),
+ base::Bind(&NotCalled<GetMyDevicesResponse>),
+ base::Bind(&SaveResult<std::string>, &error_message));
+
+ EXPECT_EQ("Failed to get a valid access token.", error_message);
+}
+
+TEST_F(CryptAuthClientTest, ParseResponseProtoFailure) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "getmydevices?alt=proto");
+
+ std::string error_message;
+ client_->GetMyDevices(GetMyDevicesRequest(),
+ base::Bind(&NotCalled<GetMyDevicesResponse>),
+ base::Bind(&SaveResult<std::string>, &error_message));
+
+ flow_result_callback_.Run("Not a valid serialized response message.");
+ EXPECT_EQ("Failed to parse response proto.", error_message);
+}
+
+TEST_F(CryptAuthClientTest,
+ MakeSecondRequestBeforeFirstRequestSucceeds) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "getmydevices?alt=proto");
+
+ // Make first request.
+ GetMyDevicesResponse result_proto;
+ client_->GetMyDevices(
+ GetMyDevicesRequest(),
+ base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
+ base::Bind(&NotCalled<std::string>));
+
+ // With request pending, make second request.
+ {
+ std::string error_message;
+ client_->FindEligibleUnlockDevices(
+ FindEligibleUnlockDevicesRequest(),
+ base::Bind(&NotCalled<FindEligibleUnlockDevicesResponse>),
+ base::Bind(&SaveResult<std::string>, &error_message));
+ EXPECT_EQ("Client has been used for another request. Do not reuse.",
+ error_message);
+ }
+
+ // Complete first request.
+ {
+ GetMyDevicesResponse response_proto;
+ response_proto.add_devices();
+ response_proto.mutable_devices(0)->set_public_key(kPublicKey1);
+ FinishApiCallFlow(&response_proto);
+ }
+
+ ASSERT_EQ(1, result_proto.devices_size());
+ EXPECT_EQ(kPublicKey1, result_proto.devices(0).public_key());
+}
+
+TEST_F(CryptAuthClientTest,
+ MakeSecondRequestBeforeFirstRequestFails) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "getmydevices?alt=proto");
+
+ // Make first request.
+ std::string error_message;
+ client_->GetMyDevices(GetMyDevicesRequest(),
+ base::Bind(&NotCalled<GetMyDevicesResponse>),
+ base::Bind(&SaveResult<std::string>, &error_message));
+
+ // With request pending, make second request.
+ {
+ std::string error_message;
+ client_->FindEligibleUnlockDevices(
+ FindEligibleUnlockDevicesRequest(),
+ base::Bind(&NotCalled<FindEligibleUnlockDevicesResponse>),
+ base::Bind(&SaveResult<std::string>, &error_message));
+ EXPECT_EQ("Client has been used for another request. Do not reuse.",
+ error_message);
+ }
+
+ // Fail first request.
+ std::string kStatus429Error = "HTTP status: 429";
+ FailApiCallFlow(kStatus429Error);
+ EXPECT_EQ(kStatus429Error, error_message);
+}
+
+TEST_F(CryptAuthClientTest,
+ MakeSecondRequestAfterFirstRequestSucceeds) {
+ // Make first request successfully.
+ {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "getmydevices?alt=proto");
+ GetMyDevicesResponse result_proto;
+ client_->GetMyDevices(
+ GetMyDevicesRequest(),
+ base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
+ base::Bind(&NotCalled<std::string>));
+
+ GetMyDevicesResponse response_proto;
+ response_proto.add_devices();
+ response_proto.mutable_devices(0)->set_public_key(kPublicKey1);
+ FinishApiCallFlow(&response_proto);
+ ASSERT_EQ(1, result_proto.devices_size());
+ EXPECT_EQ(kPublicKey1, result_proto.devices(0).public_key());
+ }
+
+ // Second request fails.
+ {
+ std::string error_message;
+ client_->FindEligibleUnlockDevices(
+ FindEligibleUnlockDevicesRequest(),
+ base::Bind(&NotCalled<FindEligibleUnlockDevicesResponse>),
+ base::Bind(&SaveResult<std::string>, &error_message));
+ EXPECT_EQ("Client has been used for another request. Do not reuse.",
+ error_message);
+ }
+}
+
+TEST_F(CryptAuthClientTest, DeviceClassifierIsSet) {
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "getmydevices?alt=proto");
+
+ GetMyDevicesResponse result_proto;
+ GetMyDevicesRequest request_proto;
+ request_proto.set_allow_stale_read(true);
+ client_->GetMyDevices(
+ request_proto,
+ base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
+ base::Bind(&NotCalled<std::string>));
+ GetMyDevicesRequest expected_request;
+ EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
+
+ const DeviceClassifier& device_classifier =
+ expected_request.device_classifier();
+ EXPECT_EQ(kDeviceOsVersionCode, device_classifier.device_os_version_code());
+ EXPECT_EQ(kDeviceSoftwareVersionCode,
+ device_classifier.device_software_version_code());
+ EXPECT_EQ(kDeviceSoftwarePackage,
+ device_classifier.device_software_package());
+ EXPECT_EQ(kDeviceType, device_classifier.device_type());
+}
+
+TEST_F(CryptAuthClientTest, GetAccessTokenUsed) {
+ EXPECT_TRUE(client_->GetAccessTokenUsed().empty());
+ ExpectRequest(
+ "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
+ "getmydevices?alt=proto");
+
+ GetMyDevicesResponse result_proto;
+ GetMyDevicesRequest request_proto;
+ request_proto.set_allow_stale_read(true);
+ client_->GetMyDevices(
+ request_proto,
+ base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
+ base::Bind(&NotCalled<std::string>));
+ EXPECT_EQ(kAccessToken, client_->GetAccessTokenUsed());
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_device_manager.cc b/chromium/components/cryptauth/cryptauth_device_manager.cc
new file mode 100644
index 00000000000..3c7a3cf5789
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_device_manager.cc
@@ -0,0 +1,539 @@
+// 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/cryptauth_device_manager.h"
+
+#include <stddef.h>
+#include <stdexcept>
+#include <utility>
+
+#include "base/base64url.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/cryptauth/cryptauth_client.h"
+#include "components/cryptauth/pref_names.h"
+#include "components/cryptauth/sync_scheduler_impl.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace cryptauth {
+
+namespace {
+
+// The normal period between successful syncs, in hours.
+const int kRefreshPeriodHours = 24;
+
+// A more aggressive period between sync attempts to recover when the last
+// sync attempt fails, in minutes. This is a base time that increases for each
+// subsequent failure.
+const int kDeviceSyncBaseRecoveryPeriodMinutes = 10;
+
+// The bound on the amount to jitter the period between syncs.
+const double kDeviceSyncMaxJitterRatio = 0.2;
+
+// Keys for ExternalDeviceInfo dictionaries that are stored in the user's prefs.
+const char kExternalDeviceKeyPublicKey[] = "public_key";
+const char kExternalDeviceKeyDeviceName[] = "device_name";
+const char kExternalDeviceKeyBluetoothAddress[] = "bluetooth_address";
+const char kExternalDeviceKeyUnlockKey[] = "unlock_key";
+const char kExternalDeviceKeyUnlockable[] = "unlockable";
+const char kExternalDeviceKeyLastUpdateTimeMillis[] = "last_update_time_millis";
+const char kExternalDeviceKeyMobileHotspotSupported[] =
+ "mobile_hotspot_supported";
+const char kExternalDeviceKeyDeviceType[] = "device_type";
+const char kExternalDeviceKeyBeaconSeeds[] = "beacon_seeds";
+const char kExternalDeviceKeyBeaconSeedData[] = "beacon_seed_data";
+const char kExternalDeviceKeyBeaconSeedStartMs[] = "beacon_seed_start_ms";
+const char kExternalDeviceKeyBeaconSeedEndMs[] = "beacon_seed_end_ms";
+
+// Converts BeaconSeed protos to a list value that can be stored in user prefs.
+std::unique_ptr<base::ListValue> BeaconSeedsToListValue(
+ const google::protobuf::RepeatedPtrField<BeaconSeed>& seeds) {
+ std::unique_ptr<base::ListValue> list(new base::ListValue());
+
+ for (int i = 0; i < seeds.size(); i++) {
+ BeaconSeed seed = seeds.Get(i);
+
+ if (!seed.has_data()
+ || !seed.has_start_time_millis()
+ || !seed.has_end_time_millis()) {
+ PA_LOG(WARNING) << "Unable to serialize BeaconSeed due to missing data; "
+ << "skipping.";
+ continue;
+ }
+
+ std::unique_ptr<base::DictionaryValue> beacon_seed_value(
+ new base::DictionaryValue());
+
+ // Note that the |BeaconSeed|s' data is stored in Base64Url encoding because
+ // dictionary values must be valid UTF8 strings.
+ std::string seed_data_b64;
+ base::Base64UrlEncode(seed.data(),
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &seed_data_b64);
+ beacon_seed_value->SetString(kExternalDeviceKeyBeaconSeedData,
+ seed_data_b64);
+
+ // Set the timestamps as string representations of their numeric value
+ // since there is no notion of a base::LongValue.
+ beacon_seed_value->SetString(
+ kExternalDeviceKeyBeaconSeedStartMs,
+ std::to_string(seed.start_time_millis()));
+ beacon_seed_value->SetString(
+ kExternalDeviceKeyBeaconSeedEndMs,
+ std::to_string(seed.end_time_millis()));
+
+ list->Append(std::move(beacon_seed_value));
+ }
+
+ return list;
+}
+
+// Converts an unlock key proto to a dictionary that can be stored in user
+// prefs.
+std::unique_ptr<base::DictionaryValue> UnlockKeyToDictionary(
+ const ExternalDeviceInfo& device) {
+ // The device public key is a required value.
+ if (!device.has_public_key()) {
+ return nullptr;
+ }
+
+ std::unique_ptr<base::DictionaryValue> dictionary(
+ new base::DictionaryValue());
+
+ // Note that the device public key, name, and Bluetooth addresses are stored
+ // in Base64Url form because dictionary values must be valid UTF8 strings.
+
+ std::string public_key_b64;
+ base::Base64UrlEncode(device.public_key(),
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &public_key_b64);
+ dictionary->SetString(kExternalDeviceKeyPublicKey, public_key_b64);
+
+ if (device.has_friendly_device_name()) {
+ std::string device_name_b64;
+ base::Base64UrlEncode(device.friendly_device_name(),
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &device_name_b64);
+ dictionary->SetString(kExternalDeviceKeyDeviceName, device_name_b64);
+ }
+
+ if (device.has_bluetooth_address()) {
+ std::string bluetooth_address_b64;
+ base::Base64UrlEncode(device.bluetooth_address(),
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &bluetooth_address_b64);
+ dictionary->SetString(kExternalDeviceKeyBluetoothAddress,
+ bluetooth_address_b64);
+ }
+
+ if (device.has_unlock_key()) {
+ dictionary->SetBoolean(kExternalDeviceKeyUnlockKey,
+ device.unlock_key());
+ }
+
+ if (device.has_unlockable()) {
+ dictionary->SetBoolean(kExternalDeviceKeyUnlockable,
+ device.unlockable());
+ }
+
+ if (device.has_last_update_time_millis()) {
+ dictionary->SetString(kExternalDeviceKeyLastUpdateTimeMillis,
+ std::to_string(device.last_update_time_millis()));
+ }
+
+ if (device.has_mobile_hotspot_supported()) {
+ dictionary->SetBoolean(kExternalDeviceKeyMobileHotspotSupported,
+ device.mobile_hotspot_supported());
+ }
+
+ if (device.has_device_type() && DeviceType_IsValid(device.device_type())) {
+ dictionary->SetInteger(kExternalDeviceKeyDeviceType,
+ device.device_type());
+ }
+
+ std::unique_ptr<base::ListValue> beacon_seed_list =
+ BeaconSeedsToListValue(device.beacon_seeds());
+ dictionary->Set(kExternalDeviceKeyBeaconSeeds, std::move(beacon_seed_list));
+
+ return dictionary;
+}
+
+void AddBeaconSeedsToExternalDevice(
+ const base::ListValue& beacon_seeds,
+ ExternalDeviceInfo& external_device) {
+ for (size_t i = 0; i < beacon_seeds.GetSize(); i++) {
+ const base::DictionaryValue* seed_dictionary = nullptr;
+ if (!beacon_seeds.GetDictionary(i, &seed_dictionary)) {
+ PA_LOG(WARNING) << "Unable to retrieve BeaconSeed dictionary; "
+ << "skipping.";
+ continue;
+ }
+
+ std::string seed_data_b64, start_time_millis_str, end_time_millis_str;
+ if (!seed_dictionary->GetString(kExternalDeviceKeyBeaconSeedData,
+ &seed_data_b64) ||
+ !seed_dictionary->GetString(kExternalDeviceKeyBeaconSeedStartMs,
+ &start_time_millis_str) ||
+ !seed_dictionary->GetString(kExternalDeviceKeyBeaconSeedEndMs,
+ &end_time_millis_str)) {
+ PA_LOG(WARNING) << "Unable to deserialize BeaconSeed due to missing "
+ << "data; skipping.";
+ continue;
+ }
+
+ // Seed data is returned as raw data, not in Base64 encoding.
+ std::string seed_data;
+ if (!base::Base64UrlDecode(seed_data_b64,
+ base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+ &seed_data)) {
+ PA_LOG(WARNING) << "Decoding seed data failed.";
+ continue;
+ }
+
+ int64_t start_time_millis, end_time_millis;
+ if (!base::StringToInt64(start_time_millis_str, &start_time_millis)
+ || !base::StringToInt64(end_time_millis_str, &end_time_millis)) {
+ PA_LOG(WARNING) << "Unable to convert stored timestamp to int64_t: "
+ << start_time_millis_str << " or " << end_time_millis_str;
+ continue;
+ }
+
+ BeaconSeed* seed = external_device.add_beacon_seeds();
+ seed->set_data(seed_data);
+ seed->set_start_time_millis(start_time_millis);
+ seed->set_end_time_millis(end_time_millis);
+ }
+}
+
+// Converts an unlock key dictionary stored in user prefs to an
+// ExternalDeviceInfo proto. Returns true if the dictionary is valid, and the
+// parsed proto is written to |external_device|.
+bool DictionaryToUnlockKey(const base::DictionaryValue& dictionary,
+ ExternalDeviceInfo* external_device) {
+ std::string public_key_b64;
+ if (!dictionary.GetString(kExternalDeviceKeyPublicKey, &public_key_b64)) {
+ // The public key is a required field, so if it is absent, there is no
+ // valid data to return.
+ return false;
+ }
+
+ std::string public_key;
+ if (!base::Base64UrlDecode(public_key_b64,
+ base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+ &public_key)) {
+ // The public key is stored as a Base64Url, so if it cannot be decoded,
+ // there is no valid data to return.
+ return false;
+ }
+ external_device->set_public_key(public_key);
+
+ std::string device_name_b64;
+ if (dictionary.GetString(kExternalDeviceKeyDeviceName, &device_name_b64)) {
+ std::string device_name;
+ if (base::Base64UrlDecode(device_name_b64,
+ base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+ &device_name)) {
+ external_device->set_friendly_device_name(device_name);
+ }
+ }
+
+ std::string bluetooth_address_b64;
+ if (dictionary.GetString(
+ kExternalDeviceKeyBluetoothAddress, &bluetooth_address_b64)) {
+ std::string bluetooth_address;
+ if (base::Base64UrlDecode(bluetooth_address_b64,
+ base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+ &bluetooth_address)) {
+ external_device->set_bluetooth_address(bluetooth_address);
+ }
+ }
+
+ bool unlock_key;
+ if (dictionary.GetBoolean(kExternalDeviceKeyUnlockKey, &unlock_key)) {
+ external_device->set_unlock_key(unlock_key);
+ }
+
+ bool unlockable;
+ if (dictionary.GetBoolean(kExternalDeviceKeyUnlockable, &unlockable)) {
+ external_device->set_unlockable(unlockable);
+ }
+
+ std::string last_update_time_millis_str;
+ if (dictionary.GetString(
+ kExternalDeviceKeyLastUpdateTimeMillis, &last_update_time_millis_str)) {
+ int64_t last_update_time_millis;
+ if (base::StringToInt64(
+ last_update_time_millis_str, &last_update_time_millis)) {
+ external_device->set_last_update_time_millis(last_update_time_millis);
+ } else {
+ PA_LOG(WARNING) << "Unable to convert stored update time to int64_t: "
+ << last_update_time_millis_str;
+ }
+ }
+
+ bool mobile_hotspot_supported;
+ if (dictionary.GetBoolean(
+ kExternalDeviceKeyMobileHotspotSupported, &mobile_hotspot_supported)) {
+ external_device->set_mobile_hotspot_supported(mobile_hotspot_supported);
+ }
+
+ int device_type;
+ if (dictionary.GetInteger(kExternalDeviceKeyDeviceType, &device_type)) {
+ if (DeviceType_IsValid(device_type)) {
+ external_device->set_device_type(static_cast<DeviceType>(device_type));
+ }
+ }
+
+ const base::ListValue* beacon_seeds = nullptr;
+ dictionary.GetList(kExternalDeviceKeyBeaconSeeds, &beacon_seeds);
+ if (beacon_seeds) {
+ AddBeaconSeedsToExternalDevice(*beacon_seeds, *external_device);
+ }
+
+ return true;
+}
+
+} // namespace
+
+CryptAuthDeviceManager::CryptAuthDeviceManager(
+ std::unique_ptr<base::Clock> clock,
+ std::unique_ptr<CryptAuthClientFactory> client_factory,
+ CryptAuthGCMManager* gcm_manager,
+ PrefService* pref_service)
+ : clock_(std::move(clock)),
+ client_factory_(std::move(client_factory)),
+ gcm_manager_(gcm_manager),
+ pref_service_(pref_service),
+ weak_ptr_factory_(this) {
+ UpdateUnlockKeysFromPrefs();
+}
+
+// Test-only constructor.
+CryptAuthDeviceManager::CryptAuthDeviceManager()
+ : clock_(nullptr),
+ client_factory_(nullptr),
+ gcm_manager_(nullptr),
+ pref_service_(nullptr),
+ weak_ptr_factory_(this) {}
+
+CryptAuthDeviceManager::~CryptAuthDeviceManager() {
+ if (gcm_manager_) {
+ gcm_manager_->RemoveObserver(this);
+ }
+}
+
+// static
+void CryptAuthDeviceManager::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterDoublePref(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds,
+ 0.0);
+ registry->RegisterBooleanPref(
+ prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure, false);
+ registry->RegisterIntegerPref(prefs::kCryptAuthDeviceSyncReason,
+ INVOCATION_REASON_UNKNOWN);
+ registry->RegisterListPref(prefs::kCryptAuthDeviceSyncUnlockKeys);
+}
+
+void CryptAuthDeviceManager::Start() {
+ gcm_manager_->AddObserver(this);
+
+ base::Time last_successful_sync = GetLastSyncTime();
+ base::TimeDelta elapsed_time_since_last_sync =
+ clock_->Now() - last_successful_sync;
+
+ bool is_recovering_from_failure =
+ pref_service_->GetBoolean(
+ prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure) ||
+ last_successful_sync.is_null();
+
+ scheduler_ = CreateSyncScheduler();
+ scheduler_->Start(elapsed_time_since_last_sync,
+ is_recovering_from_failure
+ ? SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
+ : SyncScheduler::Strategy::PERIODIC_REFRESH);
+}
+
+void CryptAuthDeviceManager::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void CryptAuthDeviceManager::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void CryptAuthDeviceManager::ForceSyncNow(
+ InvocationReason invocation_reason) {
+ pref_service_->SetInteger(prefs::kCryptAuthDeviceSyncReason,
+ invocation_reason);
+ scheduler_->ForceSync();
+}
+
+base::Time CryptAuthDeviceManager::GetLastSyncTime() const {
+ return base::Time::FromDoubleT(
+ pref_service_->GetDouble(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds));
+}
+
+base::TimeDelta CryptAuthDeviceManager::GetTimeToNextAttempt() const {
+ return scheduler_->GetTimeToNextSync();
+}
+
+bool CryptAuthDeviceManager::IsSyncInProgress() const {
+ return scheduler_->GetSyncState() ==
+ SyncScheduler::SyncState::SYNC_IN_PROGRESS;
+}
+
+bool CryptAuthDeviceManager::IsRecoveringFromFailure() const {
+ return scheduler_->GetStrategy() ==
+ SyncScheduler::Strategy::AGGRESSIVE_RECOVERY;
+}
+
+std::vector<ExternalDeviceInfo>
+CryptAuthDeviceManager::GetSyncedDevices() const {
+ return synced_devices_;
+}
+
+std::vector<ExternalDeviceInfo> CryptAuthDeviceManager::GetUnlockKeys() const {
+ std::vector<ExternalDeviceInfo> unlock_keys;
+ for (const auto& device : synced_devices_) {
+ if (device.unlock_key()) {
+ 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_) {
+ if (device.mobile_hotspot_supported()) {
+ 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());
+ for (const auto& device : response.devices()) {
+ devices_as_list->Append(UnlockKeyToDictionary(device));
+ }
+ PA_LOG(INFO) << "Devices Synced:\n" << *devices_as_list;
+
+ bool unlock_keys_changed = !devices_as_list->Equals(
+ pref_service_->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys));
+ {
+ ListPrefUpdate update(pref_service_, prefs::kCryptAuthDeviceSyncUnlockKeys);
+ update.Get()->Swap(devices_as_list.get());
+ }
+ UpdateUnlockKeysFromPrefs();
+
+ // Reset metadata used for scheduling syncing.
+ pref_service_->SetBoolean(prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure,
+ false);
+ pref_service_->SetDouble(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds,
+ clock_->Now().ToDoubleT());
+ pref_service_->SetInteger(prefs::kCryptAuthDeviceSyncReason,
+ INVOCATION_REASON_UNKNOWN);
+
+ sync_request_->OnDidComplete(true);
+ cryptauth_client_.reset();
+ sync_request_.reset();
+ for (auto& observer : observers_) {
+ observer.OnSyncFinished(SyncResult::SUCCESS,
+ unlock_keys_changed
+ ? DeviceChangeResult::CHANGED
+ : DeviceChangeResult::UNCHANGED);
+ }
+}
+
+void CryptAuthDeviceManager::OnGetMyDevicesFailure(const std::string& error) {
+ PA_LOG(ERROR) << "GetMyDevices API failed: " << error;
+ pref_service_->SetBoolean(prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure,
+ true);
+ sync_request_->OnDidComplete(false);
+ cryptauth_client_.reset();
+ sync_request_.reset();
+ for (auto& observer : observers_)
+ 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);
+}
+
+void CryptAuthDeviceManager::UpdateUnlockKeysFromPrefs() {
+ const base::ListValue* unlock_key_list =
+ pref_service_->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys);
+ synced_devices_.clear();
+ for (size_t i = 0; i < unlock_key_list->GetSize(); ++i) {
+ const base::DictionaryValue* unlock_key_dictionary;
+ if (unlock_key_list->GetDictionary(i, &unlock_key_dictionary)) {
+ ExternalDeviceInfo unlock_key;
+ if (DictionaryToUnlockKey(*unlock_key_dictionary, &unlock_key)) {
+ synced_devices_.push_back(unlock_key);
+ } else {
+ PA_LOG(ERROR) << "Unable to deserialize unlock key dictionary "
+ << "(index=" << i << "):\n" << *unlock_key_dictionary;
+ }
+ } else {
+ PA_LOG(ERROR) << "Can not get dictionary in list of unlock keys "
+ << "(index=" << i << "):\n" << *unlock_key_list;
+ }
+ }
+}
+
+void CryptAuthDeviceManager::OnSyncRequested(
+ std::unique_ptr<SyncScheduler::SyncRequest> sync_request) {
+ for (auto& observer : observers_)
+ observer.OnSyncStarted();
+
+ sync_request_ = std::move(sync_request);
+ cryptauth_client_ = client_factory_->CreateInstance();
+
+ InvocationReason invocation_reason =
+ INVOCATION_REASON_UNKNOWN;
+
+ int reason_stored_in_prefs =
+ pref_service_->GetInteger(prefs::kCryptAuthDeviceSyncReason);
+
+ // If the sync attempt is not forced, it is acceptable for CryptAuth to return
+ // a cached copy of the user's devices, rather taking a database hit for the
+ // freshest data.
+ bool is_sync_speculative =
+ reason_stored_in_prefs != INVOCATION_REASON_UNKNOWN;
+
+ if (InvocationReason_IsValid(reason_stored_in_prefs) &&
+ reason_stored_in_prefs != INVOCATION_REASON_UNKNOWN) {
+ invocation_reason =
+ static_cast<InvocationReason>(reason_stored_in_prefs);
+ } else if (GetLastSyncTime().is_null()) {
+ invocation_reason = INVOCATION_REASON_INITIALIZATION;
+ } else if (IsRecoveringFromFailure()) {
+ invocation_reason = INVOCATION_REASON_FAILURE_RECOVERY;
+ } else {
+ invocation_reason = INVOCATION_REASON_PERIODIC;
+ }
+
+ GetMyDevicesRequest request;
+ request.set_invocation_reason(invocation_reason);
+ request.set_allow_stale_read(is_sync_speculative);
+ cryptauth_client_->GetMyDevices(
+ request, base::Bind(&CryptAuthDeviceManager::OnGetMyDevicesSuccess,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&CryptAuthDeviceManager::OnGetMyDevicesFailure,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_device_manager.h b/chromium/components/cryptauth/cryptauth_device_manager.h
new file mode 100644
index 00000000000..a728f247abf
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_device_manager.h
@@ -0,0 +1,179 @@
+// 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_CRYPTAUTH_DEVICE_MANAGER_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_DEVICE_MANAGER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "components/cryptauth/cryptauth_gcm_manager.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/sync_scheduler.h"
+
+class PrefService;
+class PrefRegistrySimple;
+
+namespace cryptauth {
+
+class CryptAuthClient;
+class CryptAuthClientFactory;
+
+// This class manages syncing and storing the user's phones that are registered
+// with CryptAuth and are capable of unlocking the user's other devices. These
+// phones are called "unlock keys".
+// The manager periodically syncs the user's devices from CryptAuth to keep the
+// list of unlock keys fresh. If a sync attempts fails, the manager will
+// schedule the next sync more aggressively to recover.
+class CryptAuthDeviceManager : public SyncScheduler::Delegate,
+ public CryptAuthGCMManager::Observer {
+ public:
+ // Respresents the success result of a sync attempt.
+ enum class SyncResult { SUCCESS, FAILURE };
+
+ // Represents whether the list of unlock keys has changed after a sync
+ // attempt completes.
+ enum class DeviceChangeResult {
+ UNCHANGED,
+ CHANGED,
+ };
+
+ class Observer {
+ public:
+ // Called when a sync attempt is started.
+ virtual void OnSyncStarted() {}
+
+ // Called when a sync attempt finishes with the |success| of the request.
+ // |devices_changed| specifies if the sync caused the stored unlock keys to
+ // change.
+ virtual void OnSyncFinished(SyncResult sync_result,
+ DeviceChangeResult device_change_result) {}
+
+ virtual ~Observer() {}
+ };
+
+ // Creates the manager:
+ // |clock|: Used to determine the time between sync attempts.
+ // |client_factory|: Creates CryptAuthClient instances to perform each sync.
+ // |gcm_manager|: Notifies when GCM push messages trigger device syncs.
+ // Not owned and must outlive this instance.
+ // |pref_service|: Stores syncing metadata and unlock key information to
+ // persist across browser restarts. Must already be registered
+ // with RegisterPrefs().
+ CryptAuthDeviceManager(std::unique_ptr<base::Clock> clock,
+ std::unique_ptr<CryptAuthClientFactory> client_factory,
+ CryptAuthGCMManager* gcm_manager,
+ PrefService* pref_service);
+
+ ~CryptAuthDeviceManager() override;
+
+ // Registers the prefs used by this class to the given |registry|.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ // Starts device manager to begin syncing devices.
+ void Start();
+
+ // Adds an observer.
+ void AddObserver(Observer* observer);
+
+ // Removes an observer.
+ void RemoveObserver(Observer* observer);
+
+ // Skips the waiting period and forces a sync immediately. If a
+ // sync attempt is already in progress, this function does nothing.
+ // |invocation_reason| specifies the reason that the sync was triggered,
+ // which is upload to the server.
+ void ForceSyncNow(InvocationReason invocation_reason);
+
+ // Returns the timestamp of the last successful sync. If no sync
+ // has ever been made, then returns a null base::Time object.
+ base::Time GetLastSyncTime() const;
+
+ // Returns the time to the next sync attempt.
+ base::TimeDelta GetTimeToNextAttempt() const;
+
+ // Returns true if a device sync attempt is currently in progress.
+ bool IsSyncInProgress() const;
+
+ // Returns true if the last device sync failed and the manager is now
+ // scheduling sync attempts more aggressively to recover. If no enrollment
+ // has ever been recorded, then this function will also return true.
+ bool IsRecoveringFromFailure() const;
+
+ // Returns a list of all remote devices that have been synced.
+ virtual std::vector<ExternalDeviceInfo> GetSyncedDevices() const;
+
+ // Returns a list of remote devices that can unlock the user's other devices.
+ virtual std::vector<ExternalDeviceInfo> GetUnlockKeys() const;
+
+ // Returns a list of remote devices that can host tether hotspots.
+ virtual std::vector<ExternalDeviceInfo> GetTetherHosts() 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();
+
+ private:
+ // CryptAuthGCMManager::Observer:
+ void OnResyncMessage() override;
+
+ // Updates |unlock_keys_| by fetching the list stored in |pref_service_|.
+ void UpdateUnlockKeysFromPrefs();
+
+ // SyncScheduler::Delegate:
+ void OnSyncRequested(
+ std::unique_ptr<SyncScheduler::SyncRequest> sync_request) override;
+
+ // Callback when |cryptauth_client_| completes with the response.
+ void OnGetMyDevicesSuccess(const GetMyDevicesResponse& response);
+ void OnGetMyDevicesFailure(const std::string& error);
+
+ // Used to determine the time.
+ std::unique_ptr<base::Clock> clock_;
+
+ // Creates CryptAuthClient instances for each sync attempt.
+ std::unique_ptr<CryptAuthClientFactory> client_factory_;
+
+ // Notifies when GCM push messages trigger device sync. Not owned and must
+ // outlive this instance.
+ CryptAuthGCMManager* gcm_manager_;
+
+ // Contains preferences that outlive the lifetime of this object and across
+ // process restarts. |pref_service_| must outlive the lifetime of this
+ // instance.
+ PrefService* const pref_service_;
+
+ // All devices currently synced from CryptAuth.
+ std::vector<ExternalDeviceInfo> synced_devices_;
+
+ // Schedules the time between device sync attempts.
+ std::unique_ptr<SyncScheduler> scheduler_;
+
+ // Contains the SyncRequest that |scheduler_| requests when a device sync
+ // attempt is made.
+ std::unique_ptr<SyncScheduler::SyncRequest> sync_request_;
+
+ // The CryptAuthEnroller instance for the current sync attempt. A new
+ // instance will be created for each individual attempt.
+ std::unique_ptr<CryptAuthClient> cryptauth_client_;
+
+ // List of observers.
+ base::ObserverList<Observer> observers_;
+
+ base::WeakPtrFactory<CryptAuthDeviceManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthDeviceManager);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_DEVICE_MANAGER_H_
diff --git a/chromium/components/cryptauth/cryptauth_device_manager_unittest.cc b/chromium/components/cryptauth/cryptauth_device_manager_unittest.cc
new file mode 100644
index 00000000000..c89f8261e43
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_device_manager_unittest.cc
@@ -0,0 +1,908 @@
+// 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/cryptauth_device_manager.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/base64url.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/simple_test_clock.h"
+#include "components/cryptauth/fake_cryptauth_gcm_manager.h"
+#include "components/cryptauth/mock_cryptauth_client.h"
+#include "components/cryptauth/mock_sync_scheduler.h"
+#include "components/cryptauth/pref_names.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::SaveArg;
+
+namespace cryptauth {
+namespace {
+
+// The initial "Now" time for testing.
+const double kInitialTimeNowSeconds = 20000000;
+
+// A later "Now" time for testing.
+const double kLaterTimeNowSeconds = kInitialTimeNowSeconds + 30;
+
+// The timestamp of a last successful sync in seconds.
+const double kLastSyncTimeSeconds = kInitialTimeNowSeconds - (60 * 60 * 5);
+
+// Unlock key fields originally stored in the user prefs.
+const char kStoredPublicKey[] = "AAPL";
+const char kStoredDeviceName[] = "iPhone 6";
+const char kStoredBluetoothAddress[] = "12:34:56:78:90:AB";
+const bool kStoredUnlockKey = true;
+const bool kStoredUnlockable = false;
+const bool kStoredMobileHotspotSupported = true;
+
+// ExternalDeviceInfo fields for the synced unlock key.
+const char kPublicKey1[] = "GOOG";
+const char kDeviceName1[] = "Nexus 5";
+const char kBluetoothAddress1[] = "aa:bb:cc:ee:dd:ff";
+const bool kUnlockKey1 = true;
+const bool kUnlockable1 = false;
+const bool kMobileHotspotSupported1 = true;
+const char kBeaconSeed1Data[] = "beaconSeed1Data";
+const int64_t kBeaconSeed1StartTime = 123456;
+const int64_t kBeaconSeed1EndTime = 123457;
+const char kBeaconSeed2Data[] = "beaconSeed2Data";
+const int64_t kBeaconSeed2StartTime = 234567;
+const int64_t kBeaconSeed2EndTime = 234568;
+
+// ExternalDeviceInfo fields for a non-synced unlockable device.
+const char kPublicKey2[] = "MSFT";
+const char kDeviceName2[] = "Surface Pro 3";
+const bool kUnlockKey2 = false;
+const bool kUnlockable2 = true;
+const bool kMobileHotspotSupported2 = false;
+const char kBeaconSeed3Data[] = "beaconSeed3Data";
+const int64_t kBeaconSeed3StartTime = 123456;
+const int64_t kBeaconSeed3EndTime = 123457;
+const char kBeaconSeed4Data[] = "beaconSeed4Data";
+const int64_t kBeaconSeed4StartTime = 234567;
+const int64_t kBeaconSeed4EndTime = 234568;
+
+// Validates that |devices| is equal to |expected_devices|.
+void ExpectSyncedDevicesAreEqual(
+ const std::vector<ExternalDeviceInfo>& expected_devices,
+ const std::vector<ExternalDeviceInfo>& devices) {
+ ASSERT_EQ(expected_devices.size(), devices.size());
+ for (size_t i = 0; i < devices.size(); ++i) {
+ SCOPED_TRACE(
+ base::StringPrintf("Compare protos at index=%d", static_cast<int>(i)));
+ const auto& expected_device = expected_devices[i];
+ const auto& device = devices.at(i);
+ EXPECT_TRUE(expected_device.has_public_key());
+ EXPECT_TRUE(device.has_public_key());
+ EXPECT_EQ(expected_device.public_key(),
+ device.public_key());
+
+ EXPECT_EQ(expected_device.has_friendly_device_name(),
+ device.has_friendly_device_name());
+ EXPECT_EQ(expected_device.friendly_device_name(),
+ device.friendly_device_name());
+
+ EXPECT_EQ(expected_device.has_bluetooth_address(),
+ device.has_bluetooth_address());
+ EXPECT_EQ(expected_device.bluetooth_address(),
+ device.bluetooth_address());
+
+ EXPECT_EQ(expected_device.has_unlock_key(),
+ device.has_unlock_key());
+ EXPECT_EQ(expected_device.unlock_key(),
+ device.unlock_key());
+
+ EXPECT_EQ(expected_device.has_unlockable(),
+ device.has_unlockable());
+ EXPECT_EQ(expected_device.unlockable(),
+ device.unlockable());
+
+ EXPECT_EQ(expected_device.has_last_update_time_millis(),
+ device.has_last_update_time_millis());
+ EXPECT_EQ(expected_device.last_update_time_millis(),
+ device.last_update_time_millis());
+
+ EXPECT_EQ(expected_device.has_mobile_hotspot_supported(),
+ device.has_mobile_hotspot_supported());
+ EXPECT_EQ(expected_device.mobile_hotspot_supported(),
+ device.mobile_hotspot_supported());
+
+ EXPECT_EQ(expected_device.has_device_type(),
+ device.has_device_type());
+ EXPECT_EQ(expected_device.device_type(),
+ device.device_type());
+
+ ASSERT_EQ(expected_device.beacon_seeds_size(),
+ device.beacon_seeds_size());
+ for (int i = 0; i < expected_device.beacon_seeds_size(); i++) {
+ const BeaconSeed expected_seed =
+ expected_device.beacon_seeds(i);
+ const BeaconSeed seed = device.beacon_seeds(i);
+ EXPECT_TRUE(expected_seed.has_data());
+ EXPECT_TRUE(seed.has_data());
+ EXPECT_EQ(expected_seed.data(), seed.data());
+
+ EXPECT_TRUE(expected_seed.has_start_time_millis());
+ EXPECT_TRUE(seed.has_start_time_millis());
+ EXPECT_EQ(expected_seed.start_time_millis(), seed.start_time_millis());
+
+ EXPECT_TRUE(expected_seed.has_end_time_millis());
+ EXPECT_TRUE(seed.has_end_time_millis());
+ EXPECT_EQ(expected_seed.end_time_millis(), seed.end_time_millis());
+ }
+ }
+}
+
+// Validates that |devices| and the corresponding preferences stored by
+// |pref_service| are equal to |expected_devices|.
+void ExpectSyncedDevicesAndPrefAreEqual(
+ const std::vector<ExternalDeviceInfo>& expected_devices,
+ const std::vector<ExternalDeviceInfo>& devices,
+ const PrefService& pref_service) {
+ ExpectSyncedDevicesAreEqual(expected_devices, devices);
+
+ const base::ListValue* synced_devices_pref =
+ pref_service.GetList(prefs::kCryptAuthDeviceSyncUnlockKeys);
+ ASSERT_EQ(expected_devices.size(), synced_devices_pref->GetSize());
+ for (size_t i = 0; i < synced_devices_pref->GetSize(); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Compare pref dictionary at index=%d",
+ static_cast<int>(i)));
+ const base::DictionaryValue* device_dictionary;
+ EXPECT_TRUE(synced_devices_pref->GetDictionary(i, &device_dictionary));
+
+ const auto& expected_device = expected_devices[i];
+
+ std::string public_key_b64, public_key;
+ EXPECT_TRUE(
+ device_dictionary->GetString("public_key", &public_key_b64));
+ EXPECT_TRUE(base::Base64UrlDecode(
+ public_key_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+ &public_key));
+ EXPECT_TRUE(expected_device.has_public_key());
+ EXPECT_EQ(expected_device.public_key(), public_key);
+
+ std::string device_name_b64, device_name;
+ if (device_dictionary->GetString("device_name", &device_name_b64)) {
+ EXPECT_TRUE(base::Base64UrlDecode(
+ device_name_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+ &device_name));
+ EXPECT_TRUE(expected_device.has_friendly_device_name());
+ EXPECT_EQ(expected_device.friendly_device_name(), device_name);
+ } else {
+ EXPECT_FALSE(expected_device.has_friendly_device_name());
+ }
+
+ std::string bluetooth_address_b64, bluetooth_address;
+ if (device_dictionary->GetString("bluetooth_address",
+ &bluetooth_address_b64)) {
+ EXPECT_TRUE(base::Base64UrlDecode(
+ bluetooth_address_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+ &bluetooth_address));
+ EXPECT_TRUE(expected_device.has_bluetooth_address());
+ EXPECT_EQ(expected_device.bluetooth_address(), bluetooth_address);
+ } else {
+ EXPECT_FALSE(expected_device.has_bluetooth_address());
+ }
+
+ bool unlock_key;
+ if (device_dictionary->GetBoolean("unlock_key", &unlock_key)) {
+ EXPECT_TRUE(expected_device.has_unlock_key());
+ EXPECT_EQ(expected_device.unlock_key(), unlock_key);
+ } else {
+ EXPECT_FALSE(expected_device.has_unlock_key());
+ }
+
+ bool unlockable;
+ if (device_dictionary->GetBoolean("unlockable", &unlockable)) {
+ EXPECT_TRUE(expected_device.has_unlockable());
+ EXPECT_EQ(expected_device.unlockable(), unlockable);
+ } else {
+ EXPECT_FALSE(expected_device.has_unlockable());
+ }
+
+ std::string last_update_time_millis_str;
+ if (device_dictionary->GetString("last_update_time_millis",
+ &last_update_time_millis_str)) {
+ int64_t last_update_time_millis;
+ EXPECT_TRUE(base::StringToInt64(last_update_time_millis_str,
+ &last_update_time_millis));
+ EXPECT_TRUE(expected_device.has_last_update_time_millis());
+ EXPECT_EQ(expected_device.last_update_time_millis(),
+ last_update_time_millis);
+ } else {
+ EXPECT_FALSE(expected_device.has_last_update_time_millis());
+ }
+
+ bool mobile_hotspot_supported;
+ if (device_dictionary->GetBoolean("mobile_hotspot_supported",
+ &mobile_hotspot_supported)) {
+ EXPECT_TRUE(expected_device.has_mobile_hotspot_supported());
+ EXPECT_EQ(expected_device.mobile_hotspot_supported(),
+ mobile_hotspot_supported);
+ } else {
+ EXPECT_FALSE(expected_device.has_mobile_hotspot_supported());
+ }
+
+ int device_type;
+ if (device_dictionary->GetInteger("device_type",
+ &device_type)) {
+ EXPECT_TRUE(expected_device.has_device_type());
+ EXPECT_EQ(expected_device.device_type(),
+ device_type);
+ } else {
+ EXPECT_FALSE(expected_device.has_device_type());
+ }
+
+ const base::ListValue* beacon_seeds_from_prefs;
+ if (device_dictionary->GetList("beacon_seeds",
+ &beacon_seeds_from_prefs)) {
+ ASSERT_EQ(
+ (size_t) expected_device.beacon_seeds_size(),
+ beacon_seeds_from_prefs->GetSize());
+ for (size_t i = 0; i < beacon_seeds_from_prefs->GetSize(); i++) {
+ const base::DictionaryValue* seed;
+ ASSERT_TRUE(beacon_seeds_from_prefs->GetDictionary(i, &seed));
+
+ std::string data_b64, start_ms, end_ms;
+ EXPECT_TRUE(seed->GetString("beacon_seed_data", &data_b64));
+ EXPECT_TRUE(seed->GetString("beacon_seed_start_ms", &start_ms));
+ EXPECT_TRUE(seed->GetString("beacon_seed_end_ms", &end_ms));
+
+ const BeaconSeed& expected_seed =
+ expected_device.beacon_seeds((int) i);
+
+ std::string data;
+ EXPECT_TRUE(base::Base64UrlDecode(
+ data_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING, &data));
+ EXPECT_TRUE(expected_seed.has_data());
+ EXPECT_EQ(expected_seed.data(), data);
+
+ EXPECT_TRUE(expected_seed.has_start_time_millis());
+ EXPECT_EQ(expected_seed.start_time_millis(), std::stol(start_ms));
+
+ EXPECT_TRUE(expected_seed.has_end_time_millis());
+ EXPECT_EQ(expected_seed.end_time_millis(), std::stol(end_ms));
+ }
+ } else {
+ EXPECT_FALSE(expected_device.beacon_seeds_size());
+ }
+ }
+}
+
+// Harness for testing CryptAuthDeviceManager.
+class TestCryptAuthDeviceManager : public CryptAuthDeviceManager {
+ public:
+ TestCryptAuthDeviceManager(
+ std::unique_ptr<base::Clock> clock,
+ std::unique_ptr<CryptAuthClientFactory> client_factory,
+ CryptAuthGCMManager* gcm_manager,
+ PrefService* pref_service)
+ : CryptAuthDeviceManager(std::move(clock),
+ std::move(client_factory),
+ gcm_manager,
+ pref_service),
+ scoped_sync_scheduler_(new NiceMock<MockSyncScheduler>()),
+ weak_sync_scheduler_factory_(scoped_sync_scheduler_.get()) {}
+
+ ~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_;
+
+ // Stores the pointer of |scoped_sync_scheduler_| after ownership is passed to
+ // the super class.
+ // This should be safe because the life-time this SyncScheduler will always be
+ // within the life of the TestCryptAuthDeviceManager object.
+ base::WeakPtrFactory<MockSyncScheduler> weak_sync_scheduler_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestCryptAuthDeviceManager);
+};
+
+} // namespace
+
+class CryptAuthDeviceManagerTest
+ : public testing::Test,
+ public CryptAuthDeviceManager::Observer,
+ public MockCryptAuthClientFactory::Observer {
+ protected:
+ CryptAuthDeviceManagerTest()
+ : clock_(new base::SimpleTestClock()),
+ client_factory_(new MockCryptAuthClientFactory(
+ MockCryptAuthClientFactory::MockType::MAKE_STRICT_MOCKS)),
+ gcm_manager_("existing gcm registration id") {
+ client_factory_->AddObserver(this);
+
+ ExternalDeviceInfo unlock_key;
+ unlock_key.set_public_key(kPublicKey1);
+ unlock_key.set_friendly_device_name(kDeviceName1);
+ unlock_key.set_bluetooth_address(kBluetoothAddress1);
+ unlock_key.set_unlock_key(kUnlockKey1);
+ unlock_key.set_unlockable(kUnlockable1);
+ unlock_key.set_mobile_hotspot_supported(kMobileHotspotSupported1);
+ BeaconSeed* seed1 = unlock_key.add_beacon_seeds();
+ seed1->set_data(kBeaconSeed1Data);
+ seed1->set_start_time_millis(kBeaconSeed1StartTime);
+ seed1->set_end_time_millis(kBeaconSeed1EndTime);
+ BeaconSeed* seed2 = unlock_key.add_beacon_seeds();
+ seed2->set_data(kBeaconSeed2Data);
+ seed2->set_start_time_millis(kBeaconSeed2StartTime);
+ seed2->set_end_time_millis(kBeaconSeed2EndTime);
+ devices_in_response_.push_back(unlock_key);
+
+ ExternalDeviceInfo unlockable_device;
+ unlockable_device.set_public_key(kPublicKey2);
+ unlockable_device.set_friendly_device_name(kDeviceName2);
+ unlockable_device.set_unlock_key(kUnlockKey2);
+ unlockable_device.set_unlockable(kUnlockable2);
+ unlockable_device.set_mobile_hotspot_supported(kMobileHotspotSupported2);
+ BeaconSeed* seed3 = unlockable_device.add_beacon_seeds();
+ seed3->set_data(kBeaconSeed3Data);
+ seed3->set_start_time_millis(kBeaconSeed3StartTime);
+ seed3->set_end_time_millis(kBeaconSeed3EndTime);
+ BeaconSeed* seed4 = unlockable_device.add_beacon_seeds();
+ seed4->set_data(kBeaconSeed4Data);
+ seed4->set_start_time_millis(kBeaconSeed4StartTime);
+ seed4->set_end_time_millis(kBeaconSeed4EndTime);
+ devices_in_response_.push_back(unlockable_device);
+ }
+
+ ~CryptAuthDeviceManagerTest() {
+ client_factory_->RemoveObserver(this);
+ }
+
+ // testing::Test:
+ void SetUp() override {
+ clock_->SetNow(base::Time::FromDoubleT(kInitialTimeNowSeconds));
+
+ CryptAuthDeviceManager::RegisterPrefs(pref_service_.registry());
+ pref_service_.SetUserPref(
+ prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure,
+ new base::FundamentalValue(false));
+ pref_service_.SetUserPref(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds,
+ new base::FundamentalValue(kLastSyncTimeSeconds));
+ pref_service_.SetUserPref(
+ prefs::kCryptAuthDeviceSyncReason,
+ new base::FundamentalValue(INVOCATION_REASON_UNKNOWN));
+
+ std::unique_ptr<base::DictionaryValue> device_dictionary(
+ new base::DictionaryValue());
+
+ std::string public_key_b64, device_name_b64, bluetooth_address_b64;
+ base::Base64UrlEncode(kStoredPublicKey,
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &public_key_b64);
+ base::Base64UrlEncode(kStoredDeviceName,
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &device_name_b64);
+ base::Base64UrlEncode(kStoredBluetoothAddress,
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &bluetooth_address_b64);
+
+ device_dictionary->SetString("public_key", public_key_b64);
+ device_dictionary->SetString("device_name", device_name_b64);
+ device_dictionary->SetString("bluetooth_address",
+ bluetooth_address_b64);
+ device_dictionary->SetBoolean("unlock_key", kStoredUnlockKey);
+ device_dictionary->SetBoolean("unlockable", kStoredUnlockable);
+ device_dictionary->Set("beacon_seeds",
+ base::WrapUnique(new base::ListValue()));
+ device_dictionary->SetBoolean("mobile_hotspot_supported",
+ kStoredMobileHotspotSupported);
+ {
+ ListPrefUpdate update(&pref_service_,
+ prefs::kCryptAuthDeviceSyncUnlockKeys);
+ update.Get()->Append(std::move(device_dictionary));
+ }
+
+ device_manager_.reset(new TestCryptAuthDeviceManager(
+ base::WrapUnique(clock_), base::WrapUnique(client_factory_),
+ &gcm_manager_, &pref_service_));
+ device_manager_->AddObserver(this);
+
+ get_my_devices_response_.add_devices()->CopyFrom(devices_in_response_[0]);
+ get_my_devices_response_.add_devices()->CopyFrom(devices_in_response_[1]);
+
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH));
+ }
+
+ void TearDown() override { device_manager_->RemoveObserver(this); }
+
+ // CryptAuthDeviceManager::Observer:
+ void OnSyncStarted() override { OnSyncStartedProxy(); }
+
+ void OnSyncFinished(CryptAuthDeviceManager::SyncResult sync_result,
+ CryptAuthDeviceManager::DeviceChangeResult
+ device_change_result) override {
+ OnSyncFinishedProxy(sync_result, device_change_result);
+ }
+
+ MOCK_METHOD0(OnSyncStartedProxy, void());
+ MOCK_METHOD2(OnSyncFinishedProxy,
+ void(CryptAuthDeviceManager::SyncResult,
+ CryptAuthDeviceManager::DeviceChangeResult));
+
+ // Simulates firing the SyncScheduler to trigger a device sync attempt.
+ void FireSchedulerForSync(
+ InvocationReason expected_invocation_reason) {
+ SyncScheduler::Delegate* delegate =
+ static_cast<SyncScheduler::Delegate*>(device_manager_.get());
+
+ std::unique_ptr<SyncScheduler::SyncRequest> sync_request =
+ base::MakeUnique<SyncScheduler::SyncRequest>(
+ device_manager_->GetSyncScheduler());
+ EXPECT_CALL(*this, OnSyncStartedProxy());
+ delegate->OnSyncRequested(std::move(sync_request));
+
+ EXPECT_EQ(expected_invocation_reason,
+ get_my_devices_request_.invocation_reason());
+
+ // The allow_stale_read flag is set if the sync was not forced.
+ bool allow_stale_read =
+ pref_service_.GetInteger(prefs::kCryptAuthDeviceSyncReason) !=
+ INVOCATION_REASON_UNKNOWN;
+ EXPECT_EQ(allow_stale_read, get_my_devices_request_.allow_stale_read());
+ }
+
+ // MockCryptAuthClientFactory::Observer:
+ void OnCryptAuthClientCreated(MockCryptAuthClient* client) override {
+ EXPECT_CALL(*client, GetMyDevices(_, _, _))
+ .WillOnce(DoAll(SaveArg<0>(&get_my_devices_request_),
+ SaveArg<1>(&success_callback_),
+ SaveArg<2>(&error_callback_)));
+ }
+
+ MockSyncScheduler* sync_scheduler() {
+ return device_manager_->GetSyncScheduler().get();
+ }
+
+ // Owned by |device_manager_|.
+ base::SimpleTestClock* clock_;
+
+ // Owned by |device_manager_|.
+ MockCryptAuthClientFactory* client_factory_;
+
+ TestingPrefServiceSimple pref_service_;
+
+ FakeCryptAuthGCMManager gcm_manager_;
+
+ std::unique_ptr<TestCryptAuthDeviceManager> device_manager_;
+
+ std::vector<ExternalDeviceInfo> devices_in_response_;
+
+ GetMyDevicesResponse get_my_devices_response_;
+
+ GetMyDevicesRequest get_my_devices_request_;
+
+ CryptAuthClient::GetMyDevicesCallback success_callback_;
+
+ CryptAuthClient::ErrorCallback error_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthDeviceManagerTest);
+};
+
+TEST_F(CryptAuthDeviceManagerTest, RegisterPrefs) {
+ TestingPrefServiceSimple pref_service;
+ CryptAuthDeviceManager::RegisterPrefs(pref_service.registry());
+ EXPECT_TRUE(pref_service.FindPreference(
+ prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds));
+ EXPECT_TRUE(pref_service.FindPreference(
+ prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure));
+ EXPECT_TRUE(pref_service.FindPreference(prefs::kCryptAuthDeviceSyncReason));
+ EXPECT_TRUE(
+ pref_service.FindPreference(prefs::kCryptAuthDeviceSyncUnlockKeys));
+}
+
+TEST_F(CryptAuthDeviceManagerTest, GetSyncState) {
+ device_manager_->Start();
+
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH));
+ EXPECT_FALSE(device_manager_->IsRecoveringFromFailure());
+
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::AGGRESSIVE_RECOVERY));
+ EXPECT_TRUE(device_manager_->IsRecoveringFromFailure());
+
+ base::TimeDelta time_to_next_sync = base::TimeDelta::FromMinutes(60);
+ ON_CALL(*sync_scheduler(), GetTimeToNextSync())
+ .WillByDefault(Return(time_to_next_sync));
+ EXPECT_EQ(time_to_next_sync, device_manager_->GetTimeToNextAttempt());
+
+ ON_CALL(*sync_scheduler(), GetSyncState())
+ .WillByDefault(Return(SyncScheduler::SyncState::SYNC_IN_PROGRESS));
+ EXPECT_TRUE(device_manager_->IsSyncInProgress());
+
+ ON_CALL(*sync_scheduler(), GetSyncState())
+ .WillByDefault(Return(SyncScheduler::SyncState::WAITING_FOR_REFRESH));
+ EXPECT_FALSE(device_manager_->IsSyncInProgress());
+}
+
+TEST_F(CryptAuthDeviceManagerTest, InitWithDefaultPrefs) {
+ std::unique_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock);
+ clock->SetNow(base::Time::FromDoubleT(kInitialTimeNowSeconds));
+ base::TimeDelta elapsed_time = clock->Now() - base::Time::FromDoubleT(0);
+
+ TestingPrefServiceSimple pref_service;
+ CryptAuthDeviceManager::RegisterPrefs(pref_service.registry());
+
+ TestCryptAuthDeviceManager device_manager(
+ std::move(clock),
+ base::MakeUnique<MockCryptAuthClientFactory>(
+ MockCryptAuthClientFactory::MockType::MAKE_STRICT_MOCKS),
+ &gcm_manager_, &pref_service);
+
+ EXPECT_CALL(
+ *(device_manager.GetSyncScheduler()),
+ Start(elapsed_time, SyncScheduler::Strategy::AGGRESSIVE_RECOVERY));
+ device_manager.Start();
+ EXPECT_TRUE(device_manager.GetLastSyncTime().is_null());
+ EXPECT_EQ(0u, device_manager.GetSyncedDevices().size());
+}
+
+TEST_F(CryptAuthDeviceManagerTest, InitWithExistingPrefs) {
+ EXPECT_CALL(
+ *sync_scheduler(),
+ Start(clock_->Now() - base::Time::FromDoubleT(kLastSyncTimeSeconds),
+ SyncScheduler::Strategy::PERIODIC_REFRESH));
+
+ device_manager_->Start();
+ EXPECT_EQ(base::Time::FromDoubleT(kLastSyncTimeSeconds),
+ device_manager_->GetLastSyncTime());
+
+ auto synced_devices = device_manager_->GetSyncedDevices();
+ ASSERT_EQ(1u, synced_devices.size());
+ EXPECT_EQ(kStoredPublicKey, synced_devices[0].public_key());
+ EXPECT_EQ(kStoredDeviceName, synced_devices[0].friendly_device_name());
+ EXPECT_EQ(kStoredBluetoothAddress, synced_devices[0].bluetooth_address());
+ EXPECT_EQ(kStoredUnlockKey, synced_devices[0].unlock_key());
+ EXPECT_EQ(kStoredUnlockable, synced_devices[0].unlockable());
+}
+
+TEST_F(CryptAuthDeviceManagerTest, SyncSucceedsForFirstTime) {
+ pref_service_.ClearPref(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds);
+ device_manager_->Start();
+
+ FireSchedulerForSync(INVOCATION_REASON_INITIALIZATION);
+ ASSERT_FALSE(success_callback_.is_null());
+
+ clock_->SetNow(base::Time::FromDoubleT(kLaterTimeNowSeconds));
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+
+ success_callback_.Run(get_my_devices_response_);
+ EXPECT_EQ(clock_->Now(), device_manager_->GetLastSyncTime());
+
+ ExpectSyncedDevicesAndPrefAreEqual(devices_in_response_,
+ device_manager_->GetSyncedDevices(),
+ pref_service_);
+}
+
+TEST_F(CryptAuthDeviceManagerTest, ForceSync) {
+ device_manager_->Start();
+
+ EXPECT_CALL(*sync_scheduler(), ForceSync());
+ device_manager_->ForceSyncNow(INVOCATION_REASON_MANUAL);
+
+ FireSchedulerForSync(INVOCATION_REASON_MANUAL);
+
+ clock_->SetNow(base::Time::FromDoubleT(kLaterTimeNowSeconds));
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(get_my_devices_response_);
+ EXPECT_EQ(clock_->Now(), device_manager_->GetLastSyncTime());
+
+ ExpectSyncedDevicesAndPrefAreEqual(devices_in_response_,
+ device_manager_->GetSyncedDevices(),
+ pref_service_);
+}
+
+TEST_F(CryptAuthDeviceManagerTest, ForceSyncFailsThenSucceeds) {
+ device_manager_->Start();
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure));
+ base::Time old_sync_time = device_manager_->GetLastSyncTime();
+
+ // The first force sync fails.
+ EXPECT_CALL(*sync_scheduler(), ForceSync());
+ device_manager_->ForceSyncNow(INVOCATION_REASON_MANUAL);
+ FireSchedulerForSync(INVOCATION_REASON_MANUAL);
+ clock_->SetNow(base::Time::FromDoubleT(kLaterTimeNowSeconds));
+ EXPECT_CALL(*this,
+ OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::FAILURE,
+ CryptAuthDeviceManager::DeviceChangeResult::UNCHANGED));
+ error_callback_.Run("404");
+ EXPECT_EQ(old_sync_time, device_manager_->GetLastSyncTime());
+ EXPECT_TRUE(pref_service_.GetBoolean(
+ prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure));
+ EXPECT_EQ(static_cast<int>(INVOCATION_REASON_MANUAL),
+ pref_service_.GetInteger(prefs::kCryptAuthDeviceSyncReason));
+
+ // The second recovery sync succeeds.
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::AGGRESSIVE_RECOVERY));
+ FireSchedulerForSync(INVOCATION_REASON_MANUAL);
+ clock_->SetNow(base::Time::FromDoubleT(kLaterTimeNowSeconds + 30));
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(get_my_devices_response_);
+ EXPECT_EQ(clock_->Now(), device_manager_->GetLastSyncTime());
+
+ ExpectSyncedDevicesAndPrefAreEqual(devices_in_response_,
+ device_manager_->GetSyncedDevices(),
+ pref_service_);
+
+ EXPECT_FLOAT_EQ(
+ clock_->Now().ToDoubleT(),
+ pref_service_.GetDouble(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds));
+ EXPECT_EQ(static_cast<int>(INVOCATION_REASON_UNKNOWN),
+ pref_service_.GetInteger(prefs::kCryptAuthDeviceSyncReason));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure));
+}
+
+TEST_F(CryptAuthDeviceManagerTest, PeriodicSyncFailsThenSucceeds) {
+ device_manager_->Start();
+ base::Time old_sync_time = device_manager_->GetLastSyncTime();
+
+ // The first periodic sync fails.
+ FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+ clock_->SetNow(base::Time::FromDoubleT(kLaterTimeNowSeconds));
+ EXPECT_CALL(*this,
+ OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::FAILURE,
+ CryptAuthDeviceManager::DeviceChangeResult::UNCHANGED));
+ error_callback_.Run("401");
+ EXPECT_EQ(old_sync_time, device_manager_->GetLastSyncTime());
+ EXPECT_TRUE(pref_service_.GetBoolean(
+ prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure));
+
+ // The second recovery sync succeeds.
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::AGGRESSIVE_RECOVERY));
+ FireSchedulerForSync(INVOCATION_REASON_FAILURE_RECOVERY);
+ clock_->SetNow(base::Time::FromDoubleT(kLaterTimeNowSeconds + 30));
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(get_my_devices_response_);
+ EXPECT_EQ(clock_->Now(), device_manager_->GetLastSyncTime());
+
+ ExpectSyncedDevicesAndPrefAreEqual(devices_in_response_,
+ device_manager_->GetSyncedDevices(),
+ pref_service_);
+
+ EXPECT_FLOAT_EQ(
+ clock_->Now().ToDoubleT(),
+ pref_service_.GetDouble(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure));
+}
+
+TEST_F(CryptAuthDeviceManagerTest, SyncSameDevice) {
+ device_manager_->Start();
+ auto original_devices = device_manager_->GetSyncedDevices();
+
+ // Sync new devices.
+ FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+ ASSERT_FALSE(success_callback_.is_null());
+ EXPECT_CALL(*this,
+ OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::UNCHANGED));
+
+ // Sync the same device.
+ ExternalDeviceInfo synced_device;
+ synced_device.set_public_key(kStoredPublicKey);
+ synced_device.set_friendly_device_name(kStoredDeviceName);
+ synced_device.set_bluetooth_address(kStoredBluetoothAddress);
+ synced_device.set_unlock_key(kStoredUnlockKey);
+ synced_device.set_unlockable(kStoredUnlockable);
+ synced_device.set_mobile_hotspot_supported(kStoredMobileHotspotSupported);
+ GetMyDevicesResponse get_my_devices_response;
+ get_my_devices_response.add_devices()->CopyFrom(synced_device);
+ success_callback_.Run(get_my_devices_response);
+
+ // Check that devices are still the same after sync.
+ ExpectSyncedDevicesAndPrefAreEqual(
+ original_devices, device_manager_->GetSyncedDevices(), pref_service_);
+}
+
+TEST_F(CryptAuthDeviceManagerTest, SyncEmptyDeviceList) {
+ GetMyDevicesResponse empty_response;
+
+ device_manager_->Start();
+ EXPECT_EQ(1u, device_manager_->GetSyncedDevices().size());
+
+ FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+ ASSERT_FALSE(success_callback_.is_null());
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(empty_response);
+
+ ExpectSyncedDevicesAndPrefAreEqual(
+ std::vector<ExternalDeviceInfo>(),
+ device_manager_->GetSyncedDevices(),
+ pref_service_);
+}
+
+TEST_F(CryptAuthDeviceManagerTest, SyncThreeDevices) {
+ GetMyDevicesResponse response(get_my_devices_response_);
+ ExternalDeviceInfo synced_device2;
+ synced_device2.set_public_key("new public key");
+ synced_device2.set_friendly_device_name("new device name");
+ synced_device2.set_bluetooth_address("aa:bb:cc:dd:ee:ff");
+ synced_device2.set_unlock_key(true);
+ response.add_devices()->CopyFrom(synced_device2);
+
+ std::vector<ExternalDeviceInfo> expected_devices;
+ expected_devices.push_back(devices_in_response_[0]);
+ expected_devices.push_back(devices_in_response_[1]);
+ expected_devices.push_back(synced_device2);
+
+ device_manager_->Start();
+ EXPECT_EQ(1u, device_manager_->GetSyncedDevices().size());
+ EXPECT_EQ(1u, pref_service_.GetList(prefs::kCryptAuthDeviceSyncUnlockKeys)
+ ->GetSize());
+
+ FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+ ASSERT_FALSE(success_callback_.is_null());
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(response);
+
+ ExpectSyncedDevicesAndPrefAreEqual(
+ expected_devices, device_manager_->GetSyncedDevices(), pref_service_);
+}
+
+TEST_F(CryptAuthDeviceManagerTest, SyncOnGCMPushMessage) {
+ device_manager_->Start();
+
+ EXPECT_CALL(*sync_scheduler(), ForceSync());
+ gcm_manager_.PushResyncMessage();
+
+ FireSchedulerForSync(INVOCATION_REASON_SERVER_INITIATED);
+
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(get_my_devices_response_);
+
+ ExpectSyncedDevicesAndPrefAreEqual(devices_in_response_,
+ device_manager_->GetSyncedDevices(),
+ pref_service_);
+}
+
+TEST_F(CryptAuthDeviceManagerTest, SyncDeviceWithNoContents) {
+ device_manager_->Start();
+
+ EXPECT_CALL(*sync_scheduler(), ForceSync());
+ gcm_manager_.PushResyncMessage();
+
+ FireSchedulerForSync(INVOCATION_REASON_SERVER_INITIATED);
+
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(get_my_devices_response_);
+
+ ExpectSyncedDevicesAndPrefAreEqual(devices_in_response_,
+ device_manager_->GetSyncedDevices(),
+ pref_service_);
+}
+
+TEST_F(CryptAuthDeviceManagerTest, SyncFullyDetailedExternalDeviceInfos) {
+ GetMyDevicesResponse response;
+
+ // First, use a device with only a public key (a public key is the only
+ // required field). This ensures devices work properly when they do not have
+ // all fields filled out.
+ ExternalDeviceInfo device_with_only_public_key;
+ device_with_only_public_key.set_public_key("publicKey1");
+ // Currently, CryptAuthDeviceManager only stores devices which are unlock
+ // keys, so set_unlock_key(true) must be called here for storage to work.
+ // TODO(khorimoto): Remove this when support for storing all types of devices
+ // is added.
+ device_with_only_public_key.set_unlock_key(true);
+ response.add_devices()->CopyFrom(device_with_only_public_key);
+
+ // Second, use a device with all fields filled out. This ensures that all
+ // device details are properly saved.
+ ExternalDeviceInfo device_with_all_fields;
+ device_with_all_fields.set_public_key("publicKey2");
+ device_with_all_fields.set_friendly_device_name("deviceName");
+ device_with_all_fields.set_bluetooth_address("aa:bb:cc:dd:ee:ff");
+ device_with_all_fields.set_unlock_key(true);
+ device_with_all_fields.set_unlockable(true);
+ device_with_all_fields.set_last_update_time_millis(123456789L);
+ device_with_all_fields.set_mobile_hotspot_supported(true);
+ device_with_all_fields.set_device_type(DeviceType::ANDROIDOS);
+ BeaconSeed seed1;
+ seed1.set_data(kBeaconSeed1Data);
+ seed1.set_start_time_millis(kBeaconSeed1StartTime);
+ seed1.set_end_time_millis(kBeaconSeed1EndTime);
+ device_with_all_fields.add_beacon_seeds()->CopyFrom(seed1);
+ BeaconSeed seed2;
+ seed2.set_data(kBeaconSeed2Data);
+ seed2.set_start_time_millis(kBeaconSeed2StartTime);
+ seed2.set_end_time_millis(kBeaconSeed2EndTime);
+ device_with_all_fields.add_beacon_seeds()->CopyFrom(seed2);
+ response.add_devices()->CopyFrom(device_with_all_fields);
+
+ std::vector<ExternalDeviceInfo> expected_devices;
+ expected_devices.push_back(device_with_only_public_key);
+ expected_devices.push_back(device_with_all_fields);
+
+ device_manager_->Start();
+ FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+ ASSERT_FALSE(success_callback_.is_null());
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(response);
+
+ ExpectSyncedDevicesAndPrefAreEqual(
+ expected_devices, device_manager_->GetSyncedDevices(), pref_service_);
+}
+
+TEST_F(CryptAuthDeviceManagerTest, SubsetsOfSyncedDevices) {
+ device_manager_->Start();
+
+ FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+ ASSERT_FALSE(success_callback_.is_null());
+ EXPECT_CALL(*this,
+ OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(get_my_devices_response_);
+
+ // All synced devices.
+ ExpectSyncedDevicesAndPrefAreEqual(
+ devices_in_response_,
+ device_manager_->GetSyncedDevices(),
+ pref_service_);
+
+ // Only unlock keys.
+ ExpectSyncedDevicesAreEqual(
+ std::vector<ExternalDeviceInfo>(1, devices_in_response_[0]),
+ device_manager_->GetUnlockKeys());
+
+ // Only tether hosts.
+ ExpectSyncedDevicesAreEqual(
+ std::vector<ExternalDeviceInfo>(1, devices_in_response_[0]),
+ device_manager_->GetTetherHosts());
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_enroller.h b/chromium/components/cryptauth/cryptauth_enroller.h
new file mode 100644
index 00000000000..9a72d33a8df
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_enroller.h
@@ -0,0 +1,47 @@
+// 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_CRYPTAUTH_ENROLLER_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_ENROLLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+
+namespace cryptauth {
+
+// Interface for enrolling a device with CryptAuth.
+class CryptAuthEnroller {
+ public:
+ virtual ~CryptAuthEnroller() {}
+
+ // Enrolls the device:
+ // |user_public_key|: The user's persistent public key identifying the device.
+ // |user_private_key|: The corresponding private key to |user_public_key|.
+ // |device_info|: Contains information about the local device. Note that the
+ // enroller may change fields in this proto before it is finally uploaded.
+ // |invocation_reason|: The reason why the enrollment occurred.
+ // |callback|: Called will be called with true if the enrollment
+ // succeeds and false otherwise.
+ typedef base::Callback<void(bool)> EnrollmentFinishedCallback;
+ virtual void Enroll(const std::string& user_public_key,
+ const std::string& user_private_key,
+ const GcmDeviceInfo& device_info,
+ InvocationReason invocation_reason,
+ const EnrollmentFinishedCallback& callback) = 0;
+};
+
+// Interface for creating CryptAuthEnroller instances.
+class CryptAuthEnrollerFactory {
+ public:
+ virtual ~CryptAuthEnrollerFactory() {}
+
+ virtual std::unique_ptr<CryptAuthEnroller> CreateInstance() = 0;
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_ENROLLER_H_
diff --git a/chromium/components/cryptauth/cryptauth_enroller_impl.cc b/chromium/components/cryptauth/cryptauth_enroller_impl.cc
new file mode 100644
index 00000000000..7ac514930f0
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_enroller_impl.cc
@@ -0,0 +1,229 @@
+// 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/cryptauth_enroller_impl.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "components/cryptauth/cryptauth_client_impl.h"
+#include "components/cryptauth/cryptauth_enrollment_utils.h"
+#include "components/cryptauth/secure_message_delegate.h"
+#include "components/proximity_auth/logging/logging.h"
+#include "crypto/sha2.h"
+
+namespace cryptauth {
+
+namespace {
+
+// A successful SetupEnrollment or FinishEnrollment response should contain this
+// status string.
+const char kResponseStatusOk[] = "ok";
+
+// The name of the "gcmV1" protocol that the enrolling device supports.
+const char kSupportedEnrollmentTypeGcmV1[] = "gcmV1";
+
+// The version field of the GcmMetadata message.
+const int kGCMMetadataVersion = 1;
+
+// Returns true if |device_info| contains the required fields for enrollment.
+bool ValidateDeviceInfo(const GcmDeviceInfo& device_info) {
+ if (!device_info.has_long_device_id()) {
+ PA_LOG(ERROR) << "Expected long_device_id field in GcmDeviceInfo.";
+ return false;
+ }
+
+ if (!device_info.has_device_type()) {
+ PA_LOG(ERROR) << "Expected device_type field in GcmDeviceInfo.";
+ return false;
+ }
+
+ return true;
+}
+
+// Creates the public metadata to put in the SecureMessage that is sent to the
+// server with the FinishEnrollment request.
+std::string CreateEnrollmentPublicMetadata() {
+ GcmMetadata metadata;
+ metadata.set_version(kGCMMetadataVersion);
+ metadata.set_type(MessageType::ENROLLMENT);
+ return metadata.SerializeAsString();
+}
+
+} // namespace
+
+CryptAuthEnrollerImpl::CryptAuthEnrollerImpl(
+ std::unique_ptr<CryptAuthClientFactory> client_factory,
+ std::unique_ptr<SecureMessageDelegate> secure_message_delegate)
+ : client_factory_(std::move(client_factory)),
+ secure_message_delegate_(std::move(secure_message_delegate)),
+ weak_ptr_factory_(this) {}
+
+CryptAuthEnrollerImpl::~CryptAuthEnrollerImpl() {
+}
+
+void CryptAuthEnrollerImpl::Enroll(
+ const std::string& user_public_key,
+ const std::string& user_private_key,
+ const GcmDeviceInfo& device_info,
+ InvocationReason invocation_reason,
+ const EnrollmentFinishedCallback& callback) {
+ if (!callback_.is_null()) {
+ PA_LOG(ERROR) << "Enroll() already called. Do not reuse.";
+ callback.Run(false);
+ return;
+ }
+
+ user_public_key_ = user_public_key;
+ user_private_key_ = user_private_key;
+ device_info_ = device_info;
+ invocation_reason_ = invocation_reason;
+ callback_ = callback;
+
+ if (!ValidateDeviceInfo(device_info)) {
+ callback.Run(false);
+ return;
+ }
+
+ secure_message_delegate_->GenerateKeyPair(
+ base::Bind(&CryptAuthEnrollerImpl::OnKeyPairGenerated,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnKeyPairGenerated(const std::string& public_key,
+ const std::string& private_key) {
+ PA_LOG(INFO) << "Ephemeral key pair generated, calling SetupEnrollment API.";
+ session_public_key_ = public_key;
+ session_private_key_ = private_key;
+
+ cryptauth_client_ = client_factory_->CreateInstance();
+ SetupEnrollmentRequest request;
+ request.add_types(kSupportedEnrollmentTypeGcmV1);
+ request.set_invocation_reason(invocation_reason_);
+ cryptauth_client_->SetupEnrollment(
+ request, base::Bind(&CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&CryptAuthEnrollerImpl::OnSetupEnrollmentFailure,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess(
+ const SetupEnrollmentResponse& response) {
+ if (response.status() != kResponseStatusOk) {
+ PA_LOG(WARNING) << "Unexpected status for SetupEnrollment: "
+ << response.status();
+ callback_.Run(false);
+ return;
+ }
+
+ if (response.infos_size() == 0) {
+ PA_LOG(ERROR) << "No response info returned by server for SetupEnrollment";
+ callback_.Run(false);
+ return;
+ }
+
+ PA_LOG(INFO) << "SetupEnrollment request succeeded: deriving symmetric key.";
+ setup_info_ = response.infos(0);
+ device_info_.set_enrollment_session_id(setup_info_.enrollment_session_id());
+
+ secure_message_delegate_->DeriveKey(
+ session_private_key_, setup_info_.server_ephemeral_key(),
+ base::Bind(&CryptAuthEnrollerImpl::OnKeyDerived,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnSetupEnrollmentFailure(const std::string& error) {
+ PA_LOG(WARNING) << "SetupEnrollment API failed with error: " << error;
+ callback_.Run(false);
+}
+
+void CryptAuthEnrollerImpl::OnKeyDerived(const std::string& symmetric_key) {
+ PA_LOG(INFO) << "Derived symmetric key, "
+ << "encrypting enrollment data for upload.";
+
+ // Make sure we're enrolling the same public key used below to sign the
+ // secure message.
+ device_info_.set_user_public_key(user_public_key_);
+ device_info_.set_key_handle(user_public_key_);
+
+ // Hash the symmetric key and add it to the |device_info_| to be uploaded.
+ device_info_.set_device_master_key_hash(
+ crypto::SHA256HashString(symmetric_key));
+
+ // The server verifies that the access token set here and in the header
+ // of the FinishEnrollment() request are the same.
+ device_info_.set_oauth_token(cryptauth_client_->GetAccessTokenUsed());
+ PA_LOG(INFO) << "Using access token: " << device_info_.oauth_token();
+
+ symmetric_key_ = symmetric_key;
+ SecureMessageDelegate::CreateOptions options;
+ options.encryption_scheme = securemessage::NONE;
+ options.signature_scheme = securemessage::ECDSA_P256_SHA256;
+ options.verification_key_id = user_public_key_;
+
+ // The inner message contains the signed device information that will be
+ // sent to CryptAuth.
+ secure_message_delegate_->CreateSecureMessage(
+ device_info_.SerializeAsString(), user_private_key_, options,
+ base::Bind(&CryptAuthEnrollerImpl::OnInnerSecureMessageCreated,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnInnerSecureMessageCreated(
+ const std::string& inner_message) {
+ if (inner_message.empty()) {
+ PA_LOG(ERROR) << "Error creating inner message";
+ callback_.Run(false);
+ return;
+ }
+
+ SecureMessageDelegate::CreateOptions options;
+ options.encryption_scheme = securemessage::AES_256_CBC;
+ options.signature_scheme = securemessage::HMAC_SHA256;
+ options.public_metadata = CreateEnrollmentPublicMetadata();
+
+ // The outer message encrypts and signs the inner message with the derived
+ // symmetric session key.
+ secure_message_delegate_->CreateSecureMessage(
+ inner_message, symmetric_key_, options,
+ base::Bind(&CryptAuthEnrollerImpl::OnOuterSecureMessageCreated,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnOuterSecureMessageCreated(
+ const std::string& outer_message) {
+ PA_LOG(INFO) << "SecureMessage created, calling FinishEnrollment API.";
+
+ FinishEnrollmentRequest request;
+ request.set_enrollment_session_id(setup_info_.enrollment_session_id());
+ request.set_enrollment_message(outer_message);
+ request.set_device_ephemeral_key(session_public_key_);
+ request.set_invocation_reason(invocation_reason_);
+
+ cryptauth_client_ = client_factory_->CreateInstance();
+ cryptauth_client_->FinishEnrollment(
+ request, base::Bind(&CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&CryptAuthEnrollerImpl::OnFinishEnrollmentFailure,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess(
+ const FinishEnrollmentResponse& response) {
+ if (response.status() != kResponseStatusOk) {
+ PA_LOG(WARNING) << "Unexpected status for FinishEnrollment: "
+ << response.status();
+ callback_.Run(false);
+ } else {
+ callback_.Run(true);
+ }
+}
+
+void CryptAuthEnrollerImpl::OnFinishEnrollmentFailure(
+ const std::string& error) {
+ PA_LOG(WARNING) << "FinishEnrollment API failed with error: " << error;
+ callback_.Run(false);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_enroller_impl.h b/chromium/components/cryptauth/cryptauth_enroller_impl.h
new file mode 100644
index 00000000000..c4cca8ea0a9
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_enroller_impl.h
@@ -0,0 +1,103 @@
+// 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_CRYPTAUTH_ENROLLER_IMPL_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_ENROLLER_IMPL_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/cryptauth/cryptauth_enroller.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+
+namespace cryptauth {
+
+class CryptAuthClient;
+class CryptAuthClientFactory;
+class SecureMessageDelegate;
+
+// Implementation of CryptAuthEnroller to perform enrollment in two steps:
+// 1. SetupEnrollment:
+// Obtain a session public key from CryptAuth used to encrypt enrollment
+// data. Generate an ephemeral public key and derive a session symmetric
+// key.
+// 2. FinishEnrollment:
+// Encrypt the enrollment data with the session symmetric key, and send the
+// payload and device's public key to CryptAuth.
+class CryptAuthEnrollerImpl : public CryptAuthEnroller {
+ public:
+ // |client_factory| creates CryptAuthClient instances for making API calls.
+ // |crypto_delegate| is responsible for SecureMessage operations.
+ CryptAuthEnrollerImpl(
+ std::unique_ptr<CryptAuthClientFactory> client_factory,
+ std::unique_ptr<SecureMessageDelegate> secure_message_delegate);
+ ~CryptAuthEnrollerImpl() override;
+
+ // CryptAuthEnroller:
+ void Enroll(const std::string& user_public_key,
+ const std::string& user_private_key,
+ const GcmDeviceInfo& device_info,
+ InvocationReason invocation_reason,
+ const EnrollmentFinishedCallback& callback) override;
+
+ private:
+ // Callbacks for SetupEnrollment.
+ void OnSetupEnrollmentSuccess(
+ const SetupEnrollmentResponse& response);
+ void OnSetupEnrollmentFailure(const std::string& error);
+
+ // Callbacks for FinishEnrollment.
+ void OnFinishEnrollmentSuccess(
+ const FinishEnrollmentResponse& response);
+ void OnFinishEnrollmentFailure(const std::string& error);
+
+ // Callbacks for SecureMessageDelegate operations.
+ void OnKeyPairGenerated(const std::string& public_key,
+ const std::string& private_key);
+ void OnKeyDerived(const std::string& symmetric_key);
+ void OnInnerSecureMessageCreated(const std::string& inner_message);
+ void OnOuterSecureMessageCreated(const std::string& outer_message);
+
+ // Creates the CryptAuthClient instances to make API requests.
+ std::unique_ptr<CryptAuthClientFactory> client_factory_;
+
+ // Handles SecureMessage operations.
+ std::unique_ptr<SecureMessageDelegate> secure_message_delegate_;
+
+ // The CryptAuthClient for the latest request.
+ std::unique_ptr<CryptAuthClient> cryptauth_client_;
+
+ // The ephemeral key-pair generated for a single enrollment.
+ std::string session_public_key_;
+ std::string session_private_key_;
+
+ // The user's persistent key-pair identifying the local device.
+ std::string user_public_key_;
+ std::string user_private_key_;
+
+ // Contains information of the device to enroll.
+ GcmDeviceInfo device_info_;
+
+ // The reason telling the server why the enrollment happened.
+ InvocationReason invocation_reason_;
+
+ // The setup information returned from the SetupEnrollment API call.
+ SetupEnrollmentInfo setup_info_;
+
+ // Callback invoked when the enrollment is done.
+ EnrollmentFinishedCallback callback_;
+
+ // The derived ephemeral symmetric key.
+ std::string symmetric_key_;
+
+ base::WeakPtrFactory<CryptAuthEnrollerImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthEnrollerImpl);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_ENROLLER_IMPL_H_
diff --git a/chromium/components/cryptauth/cryptauth_enroller_impl_unittest.cc b/chromium/components/cryptauth/cryptauth_enroller_impl_unittest.cc
new file mode 100644
index 00000000000..503b49381bc
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_enroller_impl_unittest.cc
@@ -0,0 +1,359 @@
+// 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/cryptauth_enroller_impl.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/cryptauth_enrollment_utils.h"
+#include "components/cryptauth/fake_secure_message_delegate.h"
+#include "components/cryptauth/mock_cryptauth_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace cryptauth {
+
+namespace {
+
+const char kAccessTokenUsed[] = "access token used by CryptAuthClient";
+
+const char kClientSessionPublicKey[] = "throw away after one use";
+const char kServerSessionPublicKey[] = "disposables are not eco-friendly";
+
+InvocationReason kInvocationReason =
+ INVOCATION_REASON_MANUAL;
+const int kGCMMetadataVersion = 1;
+const char kSupportedEnrollmentTypeGcmV1[] = "gcmV1";
+const char kResponseStatusOk[] = "ok";
+const char kResponseStatusNotOk[] = "Your key was too bland.";
+const char kEnrollmentSessionId[] = "0123456789876543210";
+const char kFinishEnrollmentError[] = "A hungry router ate all your packets.";
+
+const char kDeviceId[] = "2015 AD";
+const DeviceType kDeviceType = CHROME;
+const char kDeviceOsVersion[] = "41.0.0";
+
+// Creates and returns the GcmDeviceInfo message to be uploaded.
+GcmDeviceInfo GetDeviceInfo() {
+ GcmDeviceInfo device_info;
+ device_info.set_long_device_id(kDeviceId);
+ device_info.set_device_type(kDeviceType);
+ device_info.set_device_os_version(kDeviceOsVersion);
+ return device_info;
+}
+
+// Creates and returns the SetupEnrollmentResponse message to be returned to the
+// enroller with the session_. If |success| is false, then a bad response will
+// be returned.
+SetupEnrollmentResponse GetSetupEnrollmentResponse(bool success) {
+ SetupEnrollmentResponse response;
+ if (!success) {
+ response.set_status(kResponseStatusNotOk);
+ return response;
+ }
+
+ response.set_status(kResponseStatusOk);
+ SetupEnrollmentInfo* info = response.add_infos();
+ info->set_type(kSupportedEnrollmentTypeGcmV1);
+ info->set_enrollment_session_id(kEnrollmentSessionId);
+ info->set_server_ephemeral_key(kServerSessionPublicKey);
+ return response;
+}
+
+// Creates and returns the FinishEnrollmentResponse message to be returned to
+// the enroller with the session_. If |success| is false, then a bad response
+// will be returned.
+FinishEnrollmentResponse GetFinishEnrollmentResponse(bool success) {
+ FinishEnrollmentResponse response;
+ if (success) {
+ response.set_status(kResponseStatusOk);
+ } else {
+ response.set_status(kResponseStatusNotOk);
+ response.set_error_message(kFinishEnrollmentError);
+ }
+ return response;
+}
+
+// Callback that saves the key returned by SecureMessageDelegate::DeriveKey().
+void SaveDerivedKey(std::string* value_out, const std::string& value) {
+ *value_out = value;
+}
+
+// Callback that saves the results returned by
+// SecureMessageDelegate::UnwrapSecureMessage().
+void SaveUnwrapResults(bool* verified_out,
+ std::string* payload_out,
+ securemessage::Header* header_out,
+ bool verified,
+ const std::string& payload,
+ const securemessage::Header& header) {
+ *verified_out = verified;
+ *payload_out = payload;
+ *header_out = header;
+}
+
+} // namespace
+
+class CryptAuthEnrollerTest
+ : public testing::Test,
+ public MockCryptAuthClientFactory::Observer {
+ public:
+ CryptAuthEnrollerTest()
+ : client_factory_(new MockCryptAuthClientFactory(
+ MockCryptAuthClientFactory::MockType::MAKE_NICE_MOCKS)),
+ secure_message_delegate_(new FakeSecureMessageDelegate()),
+ enroller_(base::WrapUnique(client_factory_),
+ base::WrapUnique(secure_message_delegate_)) {
+ client_factory_->AddObserver(this);
+
+ // This call is actually synchronous.
+ secure_message_delegate_->GenerateKeyPair(
+ base::Bind(&CryptAuthEnrollerTest::OnKeyPairGenerated,
+ base::Unretained(this)));
+ }
+
+ // Starts the enroller.
+ void StartEnroller(const GcmDeviceInfo& device_info) {
+ secure_message_delegate_->set_next_public_key(kClientSessionPublicKey);
+ enroller_result_.reset();
+ enroller_.Enroll(
+ user_public_key_, user_private_key_, device_info, kInvocationReason,
+ base::Bind(&CryptAuthEnrollerTest::OnEnrollerCompleted,
+ base::Unretained(this)));
+ }
+
+ // Verifies that |serialized_message| is a valid SecureMessage sent with the
+ // FinishEnrollment API call.
+ void ValidateEnrollmentMessage(const std::string& serialized_message) {
+ // Derive the session symmetric key.
+ std::string server_session_private_key =
+ secure_message_delegate_->GetPrivateKeyForPublicKey(
+ kServerSessionPublicKey);
+ std::string symmetric_key;
+ secure_message_delegate_->DeriveKey(
+ server_session_private_key, kClientSessionPublicKey,
+ base::Bind(&SaveDerivedKey, &symmetric_key));
+
+ std::string inner_message;
+ std::string inner_payload;
+ {
+ // Unwrap the outer message.
+ bool verified;
+ securemessage::Header header;
+ SecureMessageDelegate::UnwrapOptions unwrap_options;
+ unwrap_options.encryption_scheme = securemessage::AES_256_CBC;
+ unwrap_options.signature_scheme = securemessage::HMAC_SHA256;
+ secure_message_delegate_->UnwrapSecureMessage(
+ serialized_message, symmetric_key, unwrap_options,
+ base::Bind(&SaveUnwrapResults, &verified, &inner_message, &header));
+ EXPECT_TRUE(verified);
+
+ GcmMetadata metadata;
+ ASSERT_TRUE(metadata.ParseFromString(header.public_metadata()));
+ EXPECT_EQ(kGCMMetadataVersion, metadata.version());
+ EXPECT_EQ(MessageType::ENROLLMENT, metadata.type());
+ }
+
+ {
+ // Unwrap inner message.
+ bool verified;
+ securemessage::Header header;
+ SecureMessageDelegate::UnwrapOptions unwrap_options;
+ unwrap_options.encryption_scheme = securemessage::NONE;
+ unwrap_options.signature_scheme = securemessage::ECDSA_P256_SHA256;
+ secure_message_delegate_->UnwrapSecureMessage(
+ inner_message, user_public_key_, unwrap_options,
+ base::Bind(&SaveUnwrapResults, &verified, &inner_payload, &header));
+ EXPECT_TRUE(verified);
+ EXPECT_EQ(user_public_key_, header.verification_key_id());
+ }
+
+ // Check that the decrypted GcmDeviceInfo is correct.
+ GcmDeviceInfo device_info;
+ ASSERT_TRUE(device_info.ParseFromString(inner_payload));
+ EXPECT_EQ(kDeviceId, device_info.long_device_id());
+ EXPECT_EQ(kDeviceType, device_info.device_type());
+ EXPECT_EQ(kDeviceOsVersion, device_info.device_os_version());
+ EXPECT_EQ(user_public_key_, device_info.user_public_key());
+ EXPECT_EQ(user_public_key_, device_info.key_handle());
+ EXPECT_EQ(kEnrollmentSessionId, device_info.enrollment_session_id());
+ }
+
+ protected:
+ // MockCryptAuthClientFactory::Observer:
+ void OnCryptAuthClientCreated(MockCryptAuthClient* client) override {
+ ON_CALL(*client, SetupEnrollment(_, _, _))
+ .WillByDefault(Invoke(
+ this, &CryptAuthEnrollerTest::OnSetupEnrollment));
+
+ ON_CALL(*client, FinishEnrollment(_, _, _))
+ .WillByDefault(Invoke(
+ this, &CryptAuthEnrollerTest::OnFinishEnrollment));
+
+ ON_CALL(*client, GetAccessTokenUsed())
+ .WillByDefault(Return(kAccessTokenUsed));
+ }
+
+ void OnKeyPairGenerated(const std::string& public_key,
+ const std::string& private_key) {
+ user_public_key_ = public_key;
+ user_private_key_ = private_key;
+ }
+
+ void OnEnrollerCompleted(bool success) {
+ EXPECT_FALSE(enroller_result_.get());
+ enroller_result_.reset(new bool(success));
+ }
+
+ void OnSetupEnrollment(
+ const SetupEnrollmentRequest& request,
+ const CryptAuthClient::SetupEnrollmentCallback& callback,
+ const CryptAuthClient::ErrorCallback& error_callback) {
+ // Check that SetupEnrollment is called before FinishEnrollment.
+ EXPECT_FALSE(setup_request_.get());
+ EXPECT_FALSE(finish_request_.get());
+ EXPECT_TRUE(setup_callback_.is_null());
+ EXPECT_TRUE(error_callback_.is_null());
+
+ setup_request_.reset(new SetupEnrollmentRequest(request));
+ setup_callback_ = callback;
+ error_callback_ = error_callback;
+ }
+
+ void OnFinishEnrollment(
+ const FinishEnrollmentRequest& request,
+ const CryptAuthClient::FinishEnrollmentCallback& callback,
+ const CryptAuthClient::ErrorCallback& error_callback) {
+ // Check that FinishEnrollment is called after SetupEnrollment.
+ EXPECT_TRUE(setup_request_.get());
+ EXPECT_FALSE(finish_request_.get());
+ EXPECT_TRUE(finish_callback_.is_null());
+
+ finish_request_.reset(new FinishEnrollmentRequest(request));
+ finish_callback_ = callback;
+ error_callback_ = error_callback;
+ }
+
+ // The persistent user key-pair.
+ std::string user_public_key_;
+ std::string user_private_key_;
+
+ // Owned by |enroller_|.
+ MockCryptAuthClientFactory* client_factory_;
+ // Owned by |enroller_|.
+ FakeSecureMessageDelegate* secure_message_delegate_;
+ // The CryptAuthEnroller under test.
+ CryptAuthEnrollerImpl enroller_;
+
+ // Stores the result of running |enroller_|.
+ std::unique_ptr<bool> enroller_result_;
+
+ // Stored callbacks and requests for SetupEnrollment and FinishEnrollment.
+ std::unique_ptr<SetupEnrollmentRequest> setup_request_;
+ std::unique_ptr<FinishEnrollmentRequest> finish_request_;
+ CryptAuthClient::SetupEnrollmentCallback setup_callback_;
+ CryptAuthClient::FinishEnrollmentCallback finish_callback_;
+ CryptAuthClient::ErrorCallback error_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthEnrollerTest);
+};
+
+TEST_F(CryptAuthEnrollerTest, EnrollmentSucceeds) {
+ StartEnroller(GetDeviceInfo());
+
+ // Handle SetupEnrollment request.
+ EXPECT_TRUE(setup_request_.get());
+ EXPECT_EQ(kInvocationReason, setup_request_->invocation_reason());
+ ASSERT_EQ(1, setup_request_->types_size());
+ EXPECT_EQ(kSupportedEnrollmentTypeGcmV1, setup_request_->types(0));
+ ASSERT_FALSE(setup_callback_.is_null());
+ setup_callback_.Run(GetSetupEnrollmentResponse(true));
+
+ // Handle FinishEnrollment request.
+ EXPECT_TRUE(finish_request_.get());
+ EXPECT_EQ(kEnrollmentSessionId, finish_request_->enrollment_session_id());
+ EXPECT_EQ(kClientSessionPublicKey, finish_request_->device_ephemeral_key());
+ ValidateEnrollmentMessage(finish_request_->enrollment_message());
+ EXPECT_EQ(kInvocationReason, finish_request_->invocation_reason());
+
+ ASSERT_FALSE(finish_callback_.is_null());
+ finish_callback_.Run(GetFinishEnrollmentResponse(true));
+
+ ASSERT_TRUE(enroller_result_.get());
+ EXPECT_TRUE(*enroller_result_);
+}
+
+TEST_F(CryptAuthEnrollerTest, SetupEnrollmentApiCallError) {
+ StartEnroller(GetDeviceInfo());
+
+ EXPECT_TRUE(setup_request_.get());
+ ASSERT_FALSE(error_callback_.is_null());
+ error_callback_.Run("Setup enrollment failed network");
+
+ EXPECT_TRUE(finish_callback_.is_null());
+ ASSERT_TRUE(enroller_result_.get());
+ EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(CryptAuthEnrollerTest, SetupEnrollmentBadStatus) {
+ StartEnroller(GetDeviceInfo());
+
+ EXPECT_TRUE(setup_request_.get());
+ setup_callback_.Run(GetSetupEnrollmentResponse(false));
+
+ EXPECT_TRUE(finish_callback_.is_null());
+ ASSERT_TRUE(enroller_result_.get());
+ EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(CryptAuthEnrollerTest, SetupEnrollmentNoInfosReturned) {
+ StartEnroller(GetDeviceInfo());
+ EXPECT_TRUE(setup_request_.get());
+ SetupEnrollmentResponse response;
+ response.set_status(kResponseStatusOk);
+ setup_callback_.Run(response);
+
+ EXPECT_TRUE(finish_callback_.is_null());
+ ASSERT_TRUE(enroller_result_.get());
+ EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(CryptAuthEnrollerTest, FinishEnrollmentApiCallError) {
+ StartEnroller(GetDeviceInfo());
+ setup_callback_.Run(GetSetupEnrollmentResponse(true));
+ ASSERT_FALSE(error_callback_.is_null());
+ error_callback_.Run("finish enrollment oauth error");
+ ASSERT_TRUE(enroller_result_.get());
+ EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(CryptAuthEnrollerTest, FinishEnrollmentBadStatus) {
+ StartEnroller(GetDeviceInfo());
+ setup_callback_.Run(GetSetupEnrollmentResponse(true));
+ ASSERT_FALSE(finish_callback_.is_null());
+ finish_callback_.Run(GetFinishEnrollmentResponse(false));
+ ASSERT_TRUE(enroller_result_.get());
+ EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(CryptAuthEnrollerTest, ReuseEnroller) {
+ StartEnroller(GetDeviceInfo());
+ setup_callback_.Run(GetSetupEnrollmentResponse(true));
+ finish_callback_.Run(GetFinishEnrollmentResponse(true));
+ EXPECT_TRUE(*enroller_result_);
+
+ StartEnroller(GetDeviceInfo());
+ EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(CryptAuthEnrollerTest, IncompleteDeviceInfo) {
+ StartEnroller(GcmDeviceInfo());
+ ASSERT_TRUE(enroller_result_.get());
+ EXPECT_FALSE(*enroller_result_);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_enrollment_manager.cc b/chromium/components/cryptauth/cryptauth_enrollment_manager.cc
new file mode 100644
index 00000000000..8381de24ccf
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_enrollment_manager.cc
@@ -0,0 +1,304 @@
+// 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/cryptauth_enrollment_manager.h"
+
+#include <utility>
+
+#include "base/base64url.h"
+#include "base/memory/ptr_util.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "components/cryptauth/cryptauth_enroller.h"
+#include "components/cryptauth/pref_names.h"
+#include "components/cryptauth/secure_message_delegate.h"
+#include "components/cryptauth/sync_scheduler_impl.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace cryptauth {
+
+namespace {
+
+// The number of days that an enrollment is valid. Note that we try to refresh
+// the enrollment well before this time elapses.
+const int kValidEnrollmentPeriodDays = 45;
+
+// The normal period between successful enrollments in days.
+const int kEnrollmentRefreshPeriodDays = 30;
+
+// A more aggressive period between enrollments to recover when the last
+// enrollment fails, in minutes. This is a base time that increases for each
+// subsequent failure.
+const int kEnrollmentBaseRecoveryPeriodMinutes = 10;
+
+// The bound on the amount to jitter the period between enrollments.
+const double kEnrollmentMaxJitterRatio = 0.2;
+
+// The value of the device_software_package field in the device info uploaded
+// during enrollment. This value must be the same as the app id used for GCM
+// registration.
+const char kDeviceSoftwarePackage[] = "com.google.chrome.cryptauth";
+
+} // namespace
+
+CryptAuthEnrollmentManager::CryptAuthEnrollmentManager(
+ std::unique_ptr<base::Clock> clock,
+ std::unique_ptr<CryptAuthEnrollerFactory> enroller_factory,
+ std::unique_ptr<SecureMessageDelegate> secure_message_delegate,
+ const GcmDeviceInfo& device_info,
+ CryptAuthGCMManager* gcm_manager,
+ PrefService* pref_service)
+ : clock_(std::move(clock)),
+ enroller_factory_(std::move(enroller_factory)),
+ secure_message_delegate_(std::move(secure_message_delegate)),
+ device_info_(device_info),
+ gcm_manager_(gcm_manager),
+ pref_service_(pref_service),
+ weak_ptr_factory_(this) {}
+
+CryptAuthEnrollmentManager::~CryptAuthEnrollmentManager() {
+ gcm_manager_->RemoveObserver(this);
+}
+
+// static
+void CryptAuthEnrollmentManager::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(
+ prefs::kCryptAuthEnrollmentIsRecoveringFromFailure, false);
+ registry->RegisterDoublePref(
+ prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds, 0.0);
+ registry->RegisterIntegerPref(prefs::kCryptAuthEnrollmentReason,
+ INVOCATION_REASON_UNKNOWN);
+ registry->RegisterStringPref(prefs::kCryptAuthEnrollmentUserPublicKey,
+ std::string());
+ registry->RegisterStringPref(prefs::kCryptAuthEnrollmentUserPrivateKey,
+ std::string());
+}
+
+void CryptAuthEnrollmentManager::Start() {
+ gcm_manager_->AddObserver(this);
+
+ bool is_recovering_from_failure =
+ pref_service_->GetBoolean(
+ prefs::kCryptAuthEnrollmentIsRecoveringFromFailure) ||
+ !IsEnrollmentValid();
+
+ base::Time last_successful_enrollment = GetLastEnrollmentTime();
+ 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
+ : SyncScheduler::Strategy::PERIODIC_REFRESH);
+}
+
+void CryptAuthEnrollmentManager::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void CryptAuthEnrollmentManager::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void CryptAuthEnrollmentManager::ForceEnrollmentNow(
+ InvocationReason invocation_reason) {
+ // We store the invocation reason in a preference so that it can persist
+ // across browser restarts. If the sync fails, the next retry should still use
+ // this original reason instead of INVOCATION_REASON_FAILURE_RECOVERY.
+ pref_service_->SetInteger(prefs::kCryptAuthEnrollmentReason,
+ invocation_reason);
+ scheduler_->ForceSync();
+}
+
+bool CryptAuthEnrollmentManager::IsEnrollmentValid() const {
+ base::Time last_enrollment_time = GetLastEnrollmentTime();
+ return !last_enrollment_time.is_null() &&
+ (clock_->Now() - last_enrollment_time) <
+ base::TimeDelta::FromDays(kValidEnrollmentPeriodDays);
+}
+
+base::Time CryptAuthEnrollmentManager::GetLastEnrollmentTime() const {
+ return base::Time::FromDoubleT(pref_service_->GetDouble(
+ prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds));
+}
+
+base::TimeDelta CryptAuthEnrollmentManager::GetTimeToNextAttempt() const {
+ return scheduler_->GetTimeToNextSync();
+}
+
+bool CryptAuthEnrollmentManager::IsEnrollmentInProgress() const {
+ return scheduler_->GetSyncState() ==
+ SyncScheduler::SyncState::SYNC_IN_PROGRESS;
+}
+
+bool CryptAuthEnrollmentManager::IsRecoveringFromFailure() const {
+ return scheduler_->GetStrategy() ==
+ SyncScheduler::Strategy::AGGRESSIVE_RECOVERY;
+}
+
+void CryptAuthEnrollmentManager::OnEnrollmentFinished(bool success) {
+ if (success) {
+ pref_service_->SetDouble(
+ prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds,
+ clock_->Now().ToDoubleT());
+ pref_service_->SetInteger(prefs::kCryptAuthEnrollmentReason,
+ INVOCATION_REASON_UNKNOWN);
+ }
+
+ pref_service_->SetBoolean(prefs::kCryptAuthEnrollmentIsRecoveringFromFailure,
+ !success);
+
+ sync_request_->OnDidComplete(success);
+ cryptauth_enroller_.reset();
+ sync_request_.reset();
+ for (auto& observer : observers_)
+ 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(
+ pref_service_->GetString(prefs::kCryptAuthEnrollmentUserPublicKey),
+ base::Base64UrlDecodePolicy::REQUIRE_PADDING, &public_key)) {
+ PA_LOG(ERROR) << "Invalid public key stored in user prefs.";
+ return std::string();
+ }
+ return public_key;
+}
+
+std::string CryptAuthEnrollmentManager::GetUserPrivateKey() const {
+ std::string private_key;
+ if (!base::Base64UrlDecode(
+ pref_service_->GetString(prefs::kCryptAuthEnrollmentUserPrivateKey),
+ base::Base64UrlDecodePolicy::REQUIRE_PADDING, &private_key)) {
+ PA_LOG(ERROR) << "Invalid private key stored in user prefs.";
+ return std::string();
+ }
+ return private_key;
+}
+
+void CryptAuthEnrollmentManager::OnGCMRegistrationResult(bool success) {
+ if (!sync_request_)
+ return;
+
+ PA_LOG(INFO) << "GCM registration for CryptAuth Enrollment completed: "
+ << success;
+ if (success)
+ DoCryptAuthEnrollment();
+ else
+ OnEnrollmentFinished(false);
+}
+
+void CryptAuthEnrollmentManager::OnKeyPairGenerated(
+ const std::string& public_key,
+ const std::string& private_key) {
+ if (!public_key.empty() && !private_key.empty()) {
+ PA_LOG(INFO) << "Key pair generated for CryptAuth enrollment";
+ // Store the keypair in Base64 format because pref values require readable
+ // string values.
+ std::string public_key_b64, private_key_b64;
+ base::Base64UrlEncode(public_key,
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &public_key_b64);
+ base::Base64UrlEncode(private_key,
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &private_key_b64);
+ pref_service_->SetString(prefs::kCryptAuthEnrollmentUserPublicKey,
+ public_key_b64);
+ pref_service_->SetString(prefs::kCryptAuthEnrollmentUserPrivateKey,
+ private_key_b64);
+ DoCryptAuthEnrollment();
+ } else {
+ OnEnrollmentFinished(false);
+ }
+}
+
+void CryptAuthEnrollmentManager::OnReenrollMessage() {
+ ForceEnrollmentNow(INVOCATION_REASON_SERVER_INITIATED);
+}
+
+void CryptAuthEnrollmentManager::OnSyncRequested(
+ std::unique_ptr<SyncScheduler::SyncRequest> sync_request) {
+ for (auto& observer : observers_)
+ observer.OnEnrollmentStarted();
+
+ sync_request_ = std::move(sync_request);
+ if (gcm_manager_->GetRegistrationId().empty() ||
+ pref_service_->GetInteger(prefs::kCryptAuthEnrollmentReason) ==
+ INVOCATION_REASON_MANUAL) {
+ gcm_manager_->RegisterWithGCM();
+ } else {
+ DoCryptAuthEnrollment();
+ }
+}
+
+void CryptAuthEnrollmentManager::DoCryptAuthEnrollment() {
+ if (GetUserPublicKey().empty() || GetUserPrivateKey().empty()) {
+ secure_message_delegate_->GenerateKeyPair(
+ base::Bind(&CryptAuthEnrollmentManager::OnKeyPairGenerated,
+ weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ DoCryptAuthEnrollmentWithKeys();
+ }
+}
+
+void CryptAuthEnrollmentManager::DoCryptAuthEnrollmentWithKeys() {
+ DCHECK(sync_request_);
+ InvocationReason invocation_reason =
+ INVOCATION_REASON_UNKNOWN;
+
+ int reason_stored_in_prefs =
+ pref_service_->GetInteger(prefs::kCryptAuthEnrollmentReason);
+
+ if (InvocationReason_IsValid(reason_stored_in_prefs) &&
+ reason_stored_in_prefs != INVOCATION_REASON_UNKNOWN) {
+ invocation_reason =
+ static_cast<InvocationReason>(reason_stored_in_prefs);
+ } else if (GetLastEnrollmentTime().is_null()) {
+ invocation_reason = INVOCATION_REASON_INITIALIZATION;
+ } else if (!IsEnrollmentValid()) {
+ invocation_reason = INVOCATION_REASON_EXPIRATION;
+ } else if (scheduler_->GetStrategy() ==
+ SyncScheduler::Strategy::PERIODIC_REFRESH) {
+ invocation_reason = INVOCATION_REASON_PERIODIC;
+ } else if (scheduler_->GetStrategy() ==
+ SyncScheduler::Strategy::AGGRESSIVE_RECOVERY) {
+ invocation_reason = INVOCATION_REASON_FAILURE_RECOVERY;
+ }
+
+ // Fill in the current GCM registration id before enrolling, and explicitly
+ // make sure that the software package is the same as the GCM app id.
+ GcmDeviceInfo device_info(device_info_);
+ device_info.set_gcm_registration_id(gcm_manager_->GetRegistrationId());
+ device_info.set_device_software_package(kDeviceSoftwarePackage);
+
+ std::string public_key_b64;
+ base::Base64UrlEncode(GetUserPublicKey(),
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &public_key_b64);
+ PA_LOG(INFO) << "Making enrollment:\n"
+ << " public_key: " << public_key_b64 << "\n"
+ << " invocation_reason: " << invocation_reason << "\n"
+ << " gcm_registration_id: "
+ << device_info.gcm_registration_id();
+
+ cryptauth_enroller_ = enroller_factory_->CreateInstance();
+ cryptauth_enroller_->Enroll(
+ GetUserPublicKey(), GetUserPrivateKey(), device_info, invocation_reason,
+ base::Bind(&CryptAuthEnrollmentManager::OnEnrollmentFinished,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_enrollment_manager.h b/chromium/components/cryptauth/cryptauth_enrollment_manager.h
new file mode 100644
index 00000000000..e621b9f5b4e
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_enrollment_manager.h
@@ -0,0 +1,189 @@
+// 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_CRYPTAUTH_ENROLLMENT_MANAGER_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_ENROLLMENT_MANAGER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/time/time.h"
+#include "components/cryptauth/cryptauth_gcm_manager.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/sync_scheduler.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace base {
+class Clock;
+class Time;
+}
+
+namespace cryptauth {
+
+class CryptAuthEnroller;
+class CryptAuthEnrollerFactory;
+class SecureMessageDelegate;
+
+// This class manages the device's enrollment with CryptAuth, periodically
+// re-enrolling to keep the state on the server fresh. If an enrollment fails,
+// the manager will schedule the next enrollment more aggressively to recover
+// from the failure.
+class CryptAuthEnrollmentManager : public SyncScheduler::Delegate,
+ public CryptAuthGCMManager::Observer {
+ public:
+ class Observer {
+ public:
+ // Called when an enrollment attempt is started.
+ virtual void OnEnrollmentStarted() = 0;
+
+ // Called when an enrollment attempt finishes with the |success| of the
+ // attempt.
+ virtual void OnEnrollmentFinished(bool success) = 0;
+
+ virtual ~Observer() {}
+ };
+
+ // Creates the manager:
+ // |clock|: Used to determine the time between sync attempts.
+ // |enroller_factory|: Creates CryptAuthEnroller instances to perform each
+ // enrollment attempt.
+ // |secure_message_delegate|: Used to generate the user's keypair if it does
+ // not exist.
+ // |device_info|: Contains information about the local device that will be
+ // uploaded to CryptAuth with each enrollment request.
+ // |gcm_manager|: Used to perform GCM registrations and also notifies when GCM
+ // push messages trigger re-enrollments.
+ // Not owned and must outlive this instance.
+ // |pref_service|: Contains preferences across browser restarts, and should
+ // have been registered through RegisterPrefs().
+ CryptAuthEnrollmentManager(
+ std::unique_ptr<base::Clock> clock,
+ std::unique_ptr<CryptAuthEnrollerFactory> enroller_factory,
+ std::unique_ptr<SecureMessageDelegate> secure_message_delegate,
+ const GcmDeviceInfo& device_info,
+ CryptAuthGCMManager* gcm_manager,
+ PrefService* pref_service);
+
+ ~CryptAuthEnrollmentManager() override;
+
+ // Registers the prefs used by this class to the given |pref_service|.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ // Begins scheduling periodic enrollment attempts.
+ void Start();
+
+ // Adds an observer.
+ void AddObserver(Observer* observer);
+
+ // Removes an observer.
+ void RemoveObserver(Observer* observer);
+
+ // Skips the waiting period and forces an enrollment immediately. If an
+ // enrollment is already in progress, this function does nothing.
+ // |invocation_reason| specifies the reason that the enrollment was triggered,
+ // which is upload to the server.
+ void ForceEnrollmentNow(InvocationReason invocation_reason);
+
+ // Returns true if a successful enrollment has been recorded and this
+ // enrollment has not expired.
+ bool IsEnrollmentValid() const;
+
+ // Returns the timestamp of the last successful enrollment. If no enrollment
+ // has ever been made, then a null base::Time object will be returned.
+ base::Time GetLastEnrollmentTime() const;
+
+ // Returns the time to the next enrollment attempt.
+ base::TimeDelta GetTimeToNextAttempt() const;
+
+ // Returns true if an enrollment attempt is currently in progress.
+ bool IsEnrollmentInProgress() const;
+
+ // Returns true if the last enrollment failed and the manager is now
+ // scheduling enrollments more aggressively to recover. If no enrollment has
+ // ever been recorded, then this function will also return true.
+ bool IsRecoveringFromFailure() const;
+
+ // Returns the keypair used to enroll with CryptAuth. If no enrollment has
+ // been completed, then an empty string will be returned.
+ // Note: These keys are really serialized protocol buffer messages, and should
+ // only be used by passing to SecureMessageDelegate.
+ std::string GetUserPublicKey() const;
+ std::string GetUserPrivateKey() const;
+
+ protected:
+ // Creates a new SyncScheduler instance. Exposed for testing.
+ virtual std::unique_ptr<SyncScheduler> CreateSyncScheduler();
+
+ private:
+ // CryptAuthGCMManager::Observer:
+ void OnGCMRegistrationResult(bool success) override;
+ void OnReenrollMessage() override;
+
+ // Callback when a new keypair is generated.
+ void OnKeyPairGenerated(const std::string& public_key,
+ const std::string& private_key);
+
+ // SyncScheduler::Delegate:
+ void OnSyncRequested(
+ std::unique_ptr<SyncScheduler::SyncRequest> sync_request) override;
+
+ // Starts a CryptAuth enrollment attempt, generating a new keypair if one is
+ // not already stored in the user prefs.
+ void DoCryptAuthEnrollment();
+
+ // Starts a CryptAuth enrollment attempt, after a key-pair is stored in the
+ // user prefs.
+ void DoCryptAuthEnrollmentWithKeys();
+
+ // Callback when |cryptauth_enroller_| completes.
+ void OnEnrollmentFinished(bool success);
+
+ // Used to determine the time.
+ std::unique_ptr<base::Clock> clock_;
+
+ // Creates CryptAuthEnroller instances for each enrollment attempt.
+ std::unique_ptr<CryptAuthEnrollerFactory> enroller_factory_;
+
+ // The SecureMessageDelegate used to generate the user's keypair if it does
+ // not already exist.
+ std::unique_ptr<SecureMessageDelegate> secure_message_delegate_;
+
+ // The local device information to upload to CryptAuth.
+ const GcmDeviceInfo device_info_;
+
+ // Used to perform GCM registrations and also notifies when GCM push messages
+ // trigger re-enrollments. Not owned and must outlive this instance.
+ CryptAuthGCMManager* gcm_manager_;
+
+ // Contains perferences that outlive the lifetime of this object and across
+ // process restarts.
+ // Not owned and must outlive this instance.
+ PrefService* pref_service_;
+
+ // Schedules the time between enrollment attempts.
+ std::unique_ptr<SyncScheduler> scheduler_;
+
+ // Contains the SyncRequest that |scheduler_| requests when an enrollment
+ // attempt is made.
+ std::unique_ptr<SyncScheduler::SyncRequest> sync_request_;
+
+ // The CryptAuthEnroller instance for the current enrollment attempt. A new
+ // instance will be created for each individual attempt.
+ std::unique_ptr<CryptAuthEnroller> cryptauth_enroller_;
+
+ // List of observers.
+ base::ObserverList<Observer> observers_;
+
+ base::WeakPtrFactory<CryptAuthEnrollmentManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthEnrollmentManager);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_ENROLLMENT_MANAGER_H_
diff --git a/chromium/components/cryptauth/cryptauth_enrollment_manager_unittest.cc b/chromium/components/cryptauth/cryptauth_enrollment_manager_unittest.cc
new file mode 100644
index 00000000000..01f9781a9e8
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_enrollment_manager_unittest.cc
@@ -0,0 +1,470 @@
+// 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/cryptauth_enrollment_manager.h"
+
+#include <utility>
+
+#include "base/base64url.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/test/simple_test_clock.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "components/cryptauth/cryptauth_enroller.h"
+#include "components/cryptauth/fake_cryptauth_gcm_manager.h"
+#include "components/cryptauth/fake_secure_message_delegate.h"
+#include "components/cryptauth/mock_sync_scheduler.h"
+#include "components/cryptauth/pref_names.h"
+#include "components/prefs/testing_pref_service.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 cryptauth {
+
+namespace {
+
+// The GCM registration id from a successful registration.
+const char kGCMRegistrationId[] = "new gcm registration id";
+
+// The user's persistent public key identifying the local device.
+const char kUserPublicKey[] = "user public key";
+
+// The initial "Now" time for testing.
+const double kInitialTimeNowSeconds = 20000000;
+
+// A later "Now" time for testing.
+const double kLaterTimeNow = kInitialTimeNowSeconds + 30;
+
+// The timestamp of a last successful enrollment that is still valid.
+const double kLastEnrollmentTimeSeconds =
+ kInitialTimeNowSeconds - (60 * 60 * 24 * 15);
+
+// The timestamp of a last successful enrollment that is expired.
+const double kLastExpiredEnrollmentTimeSeconds =
+ kInitialTimeNowSeconds - (60 * 60 * 24 * 100);
+
+// Mocks out the actual enrollment flow.
+class MockCryptAuthEnroller : public CryptAuthEnroller {
+ public:
+ MockCryptAuthEnroller() {}
+ ~MockCryptAuthEnroller() override {}
+
+ MOCK_METHOD5(Enroll,
+ void(const std::string& user_public_key,
+ const std::string& user_private_key,
+ const GcmDeviceInfo& device_info,
+ InvocationReason invocation_reason,
+ const EnrollmentFinishedCallback& callback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockCryptAuthEnroller);
+};
+
+// Creates MockCryptAuthEnroller instances, and allows expecations to be set
+// before they are returned.
+class MockCryptAuthEnrollerFactory : public CryptAuthEnrollerFactory {
+ public:
+ MockCryptAuthEnrollerFactory()
+ : next_cryptauth_enroller_(new NiceMock<MockCryptAuthEnroller>()) {}
+ ~MockCryptAuthEnrollerFactory() override {}
+
+ // CryptAuthEnrollerFactory:
+ std::unique_ptr<CryptAuthEnroller> CreateInstance() override {
+ auto passed_cryptauth_enroller = std::move(next_cryptauth_enroller_);
+ next_cryptauth_enroller_.reset(new NiceMock<MockCryptAuthEnroller>());
+ return std::move(passed_cryptauth_enroller);
+ }
+
+ MockCryptAuthEnroller* next_cryptauth_enroller() {
+ return next_cryptauth_enroller_.get();
+ }
+
+ private:
+ // Stores the next CryptAuthEnroller to be created.
+ // Ownership is passed to the caller of |CreateInstance()|.
+ std::unique_ptr<MockCryptAuthEnroller> next_cryptauth_enroller_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCryptAuthEnrollerFactory);
+};
+
+// Harness for testing CryptAuthEnrollmentManager.
+class TestCryptAuthEnrollmentManager : public CryptAuthEnrollmentManager {
+ public:
+ TestCryptAuthEnrollmentManager(
+ std::unique_ptr<base::Clock> clock,
+ std::unique_ptr<CryptAuthEnrollerFactory> enroller_factory,
+ std::unique_ptr<SecureMessageDelegate> secure_message_delegate,
+ const GcmDeviceInfo& device_info,
+ CryptAuthGCMManager* gcm_manager,
+ PrefService* pref_service)
+ : CryptAuthEnrollmentManager(std::move(clock),
+ std::move(enroller_factory),
+ std::move(secure_message_delegate),
+ device_info,
+ gcm_manager,
+ pref_service),
+ scoped_sync_scheduler_(new NiceMock<MockSyncScheduler>()),
+ weak_sync_scheduler_factory_(scoped_sync_scheduler_.get()) {}
+
+ ~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();
+ }
+
+ private:
+ // Ownership is passed to |CryptAuthEnrollmentManager| super class when
+ // |CreateSyncScheduler()| is called.
+ std::unique_ptr<MockSyncScheduler> scoped_sync_scheduler_;
+
+ // Stores the pointer of |scoped_sync_scheduler_| after ownership is passed to
+ // the super class.
+ // This should be safe because the life-time this SyncScheduler will always be
+ // within the life of the TestCryptAuthEnrollmentManager object.
+ base::WeakPtrFactory<MockSyncScheduler> weak_sync_scheduler_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestCryptAuthEnrollmentManager);
+};
+
+} // namespace
+
+class CryptAuthEnrollmentManagerTest
+ : public testing::Test,
+ public CryptAuthEnrollmentManager::Observer {
+ protected:
+ CryptAuthEnrollmentManagerTest()
+ : public_key_(kUserPublicKey),
+ clock_(new base::SimpleTestClock()),
+ enroller_factory_(new MockCryptAuthEnrollerFactory()),
+ secure_message_delegate_(new FakeSecureMessageDelegate()),
+ gcm_manager_(kGCMRegistrationId),
+ enrollment_manager_(base::WrapUnique(clock_),
+ base::WrapUnique(enroller_factory_),
+ base::WrapUnique(secure_message_delegate_),
+ device_info_,
+ &gcm_manager_,
+ &pref_service_) {}
+
+ // testing::Test:
+ void SetUp() override {
+ clock_->SetNow(base::Time::FromDoubleT(kInitialTimeNowSeconds));
+ enrollment_manager_.AddObserver(this);
+
+ private_key_ =
+ secure_message_delegate_->GetPrivateKeyForPublicKey(public_key_);
+ secure_message_delegate_->set_next_public_key(public_key_);
+
+ CryptAuthEnrollmentManager::RegisterPrefs(pref_service_.registry());
+ pref_service_.SetUserPref(
+ prefs::kCryptAuthEnrollmentIsRecoveringFromFailure,
+ new base::FundamentalValue(false));
+ pref_service_.SetUserPref(
+ prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds,
+ new base::FundamentalValue(kLastEnrollmentTimeSeconds));
+ pref_service_.SetUserPref(
+ prefs::kCryptAuthEnrollmentReason,
+ new base::FundamentalValue(INVOCATION_REASON_UNKNOWN));
+
+ std::string public_key_b64, private_key_b64;
+ base::Base64UrlEncode(public_key_,
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &public_key_b64);
+ base::Base64UrlEncode(private_key_,
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &private_key_b64);
+ pref_service_.SetString(prefs::kCryptAuthEnrollmentUserPublicKey,
+ public_key_b64);
+ pref_service_.SetString(prefs::kCryptAuthEnrollmentUserPrivateKey,
+ private_key_b64);
+
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH));
+ }
+
+ void TearDown() override { enrollment_manager_.RemoveObserver(this); }
+
+ // CryptAuthEnrollmentManager::Observer:
+ void OnEnrollmentStarted() override { OnEnrollmentStartedProxy(); }
+
+ void OnEnrollmentFinished(bool success) override {
+ // Simulate the scheduler changing strategies based on success or failure.
+ SyncScheduler::Strategy new_strategy =
+ SyncScheduler::Strategy::AGGRESSIVE_RECOVERY;
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(new_strategy));
+
+ OnEnrollmentFinishedProxy(success);
+ }
+
+ MOCK_METHOD0(OnEnrollmentStartedProxy, void());
+ MOCK_METHOD1(OnEnrollmentFinishedProxy, void(bool success));
+
+ // Simulates firing the SyncScheduler to trigger an enrollment attempt.
+ CryptAuthEnroller::EnrollmentFinishedCallback FireSchedulerForEnrollment(
+ InvocationReason expected_invocation_reason) {
+ CryptAuthEnroller::EnrollmentFinishedCallback completion_callback;
+ EXPECT_CALL(
+ *next_cryptauth_enroller(),
+ Enroll(public_key_, private_key_, _, expected_invocation_reason, _))
+ .WillOnce(SaveArg<4>(&completion_callback));
+
+ auto sync_request = base::MakeUnique<SyncScheduler::SyncRequest>(
+ enrollment_manager_.GetSyncScheduler());
+ EXPECT_CALL(*this, OnEnrollmentStartedProxy());
+
+ SyncScheduler::Delegate* delegate =
+ static_cast<SyncScheduler::Delegate*>(&enrollment_manager_);
+ delegate->OnSyncRequested(std::move(sync_request));
+
+ return completion_callback;
+ }
+
+ MockSyncScheduler* sync_scheduler() {
+ return enrollment_manager_.GetSyncScheduler().get();
+ }
+
+ MockCryptAuthEnroller* next_cryptauth_enroller() {
+ return enroller_factory_->next_cryptauth_enroller();
+ }
+
+ // The expected persistent keypair.
+ std::string public_key_;
+ std::string private_key_;
+
+ // Owned by |enrollment_manager_|.
+ base::SimpleTestClock* clock_;
+
+ // Owned by |enrollment_manager_|.
+ MockCryptAuthEnrollerFactory* enroller_factory_;
+
+ // Ownered by |enrollment_manager_|.
+ FakeSecureMessageDelegate* secure_message_delegate_;
+
+ GcmDeviceInfo device_info_;
+
+ TestingPrefServiceSimple pref_service_;
+
+ FakeCryptAuthGCMManager gcm_manager_;
+
+ TestCryptAuthEnrollmentManager enrollment_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthEnrollmentManagerTest);
+};
+
+TEST_F(CryptAuthEnrollmentManagerTest, RegisterPrefs) {
+ TestingPrefServiceSimple pref_service;
+ CryptAuthEnrollmentManager::RegisterPrefs(pref_service.registry());
+ EXPECT_TRUE(pref_service.FindPreference(
+ prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds));
+ EXPECT_TRUE(pref_service.FindPreference(
+ prefs::kCryptAuthEnrollmentIsRecoveringFromFailure));
+ EXPECT_TRUE(pref_service.FindPreference(prefs::kCryptAuthEnrollmentReason));
+}
+
+TEST_F(CryptAuthEnrollmentManagerTest, GetEnrollmentState) {
+ enrollment_manager_.Start();
+
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH));
+ EXPECT_FALSE(enrollment_manager_.IsRecoveringFromFailure());
+
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::AGGRESSIVE_RECOVERY));
+ EXPECT_TRUE(enrollment_manager_.IsRecoveringFromFailure());
+
+ base::TimeDelta time_to_next_sync = base::TimeDelta::FromMinutes(60);
+ ON_CALL(*sync_scheduler(), GetTimeToNextSync())
+ .WillByDefault(Return(time_to_next_sync));
+ EXPECT_EQ(time_to_next_sync, enrollment_manager_.GetTimeToNextAttempt());
+
+ ON_CALL(*sync_scheduler(), GetSyncState())
+ .WillByDefault(Return(SyncScheduler::SyncState::SYNC_IN_PROGRESS));
+ EXPECT_TRUE(enrollment_manager_.IsEnrollmentInProgress());
+
+ ON_CALL(*sync_scheduler(), GetSyncState())
+ .WillByDefault(Return(SyncScheduler::SyncState::WAITING_FOR_REFRESH));
+ EXPECT_FALSE(enrollment_manager_.IsEnrollmentInProgress());
+}
+
+TEST_F(CryptAuthEnrollmentManagerTest, InitWithDefaultPrefs) {
+ std::unique_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock());
+ clock->SetNow(base::Time::FromDoubleT(kInitialTimeNowSeconds));
+ base::TimeDelta elapsed_time = clock->Now() - base::Time::FromDoubleT(0);
+
+ TestingPrefServiceSimple pref_service;
+ CryptAuthEnrollmentManager::RegisterPrefs(pref_service.registry());
+
+ TestCryptAuthEnrollmentManager enrollment_manager(
+ std::move(clock), base::MakeUnique<MockCryptAuthEnrollerFactory>(),
+ base::MakeUnique<FakeSecureMessageDelegate>(), device_info_,
+ &gcm_manager_, &pref_service);
+
+ EXPECT_CALL(
+ *enrollment_manager.GetSyncScheduler(),
+ Start(elapsed_time, SyncScheduler::Strategy::AGGRESSIVE_RECOVERY));
+ enrollment_manager.Start();
+
+ EXPECT_FALSE(enrollment_manager.IsEnrollmentValid());
+ EXPECT_TRUE(enrollment_manager.GetLastEnrollmentTime().is_null());
+}
+
+TEST_F(CryptAuthEnrollmentManagerTest, InitWithExistingPrefs) {
+ EXPECT_CALL(
+ *sync_scheduler(),
+ Start(clock_->Now() - base::Time::FromDoubleT(kLastEnrollmentTimeSeconds),
+ SyncScheduler::Strategy::PERIODIC_REFRESH));
+
+ enrollment_manager_.Start();
+ EXPECT_TRUE(enrollment_manager_.IsEnrollmentValid());
+ EXPECT_EQ(base::Time::FromDoubleT(kLastEnrollmentTimeSeconds),
+ enrollment_manager_.GetLastEnrollmentTime());
+}
+
+TEST_F(CryptAuthEnrollmentManagerTest, InitWithExpiredEnrollment) {
+ pref_service_.SetUserPref(
+ prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds,
+ new base::FundamentalValue(kLastExpiredEnrollmentTimeSeconds));
+
+ EXPECT_CALL(*sync_scheduler(),
+ Start(clock_->Now() - base::Time::FromDoubleT(
+ kLastExpiredEnrollmentTimeSeconds),
+ SyncScheduler::Strategy::AGGRESSIVE_RECOVERY));
+
+ enrollment_manager_.Start();
+ EXPECT_FALSE(enrollment_manager_.IsEnrollmentValid());
+ EXPECT_EQ(base::Time::FromDoubleT(kLastExpiredEnrollmentTimeSeconds),
+ enrollment_manager_.GetLastEnrollmentTime());
+}
+
+TEST_F(CryptAuthEnrollmentManagerTest, ForceEnrollment) {
+ enrollment_manager_.Start();
+
+ EXPECT_CALL(*sync_scheduler(), ForceSync());
+ enrollment_manager_.ForceEnrollmentNow(
+ INVOCATION_REASON_SERVER_INITIATED);
+
+ auto completion_callback =
+ FireSchedulerForEnrollment(INVOCATION_REASON_SERVER_INITIATED);
+
+ clock_->SetNow(base::Time::FromDoubleT(kLaterTimeNow));
+ EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
+ completion_callback.Run(true);
+ EXPECT_EQ(clock_->Now(), enrollment_manager_.GetLastEnrollmentTime());
+}
+
+TEST_F(CryptAuthEnrollmentManagerTest,
+ EnrollmentFailsThenSucceeds) {
+ enrollment_manager_.Start();
+ base::Time old_enrollment_time = enrollment_manager_.GetLastEnrollmentTime();
+
+ // The first periodic enrollment fails.
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH));
+ auto completion_callback =
+ FireSchedulerForEnrollment(INVOCATION_REASON_PERIODIC);
+ clock_->SetNow(base::Time::FromDoubleT(kLaterTimeNow));
+ EXPECT_CALL(*this, OnEnrollmentFinishedProxy(false));
+ completion_callback.Run(false);
+ EXPECT_EQ(old_enrollment_time, enrollment_manager_.GetLastEnrollmentTime());
+ EXPECT_TRUE(pref_service_.GetBoolean(
+ prefs::kCryptAuthEnrollmentIsRecoveringFromFailure));
+
+ // The second recovery enrollment succeeds.
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::AGGRESSIVE_RECOVERY));
+ completion_callback =
+ FireSchedulerForEnrollment(INVOCATION_REASON_FAILURE_RECOVERY);
+ clock_->SetNow(base::Time::FromDoubleT(kLaterTimeNow + 30));
+ EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
+ completion_callback.Run(true);
+ EXPECT_EQ(clock_->Now(), enrollment_manager_.GetLastEnrollmentTime());
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kCryptAuthEnrollmentIsRecoveringFromFailure));
+}
+
+TEST_F(CryptAuthEnrollmentManagerTest,
+ EnrollmentSucceedsForFirstTime) {
+ // Initialize |enrollment_manager_|.
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH));
+ gcm_manager_.set_registration_id(std::string());
+ pref_service_.ClearPref(prefs::kCryptAuthEnrollmentUserPublicKey);
+ pref_service_.ClearPref(prefs::kCryptAuthEnrollmentUserPrivateKey);
+ pref_service_.ClearPref(prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds);
+ enrollment_manager_.Start();
+ EXPECT_FALSE(enrollment_manager_.IsEnrollmentValid());
+
+ // Trigger a sync request.
+ EXPECT_CALL(*this, OnEnrollmentStartedProxy());
+ auto sync_request = base::MakeUnique<SyncScheduler::SyncRequest>(
+ enrollment_manager_.GetSyncScheduler());
+ static_cast<SyncScheduler::Delegate*>(&enrollment_manager_)
+ ->OnSyncRequested(std::move(sync_request));
+
+ // Complete GCM registration successfully, and expect an enrollment.
+ CryptAuthEnroller::EnrollmentFinishedCallback enrollment_callback;
+ EXPECT_CALL(*next_cryptauth_enroller(),
+ Enroll(public_key_, private_key_, _,
+ INVOCATION_REASON_INITIALIZATION, _))
+ .WillOnce(SaveArg<4>(&enrollment_callback));
+ ASSERT_TRUE(gcm_manager_.registration_in_progress());
+ gcm_manager_.CompleteRegistration(kGCMRegistrationId);
+
+ // Complete CryptAuth enrollment.
+ ASSERT_FALSE(enrollment_callback.is_null());
+ clock_->SetNow(base::Time::FromDoubleT(kLaterTimeNow));
+ EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
+ enrollment_callback.Run(true);
+ EXPECT_EQ(clock_->Now(), enrollment_manager_.GetLastEnrollmentTime());
+ EXPECT_TRUE(enrollment_manager_.IsEnrollmentValid());
+
+ // Check that CryptAuthEnrollmentManager returns the expected key-pair.
+ EXPECT_EQ(public_key_, enrollment_manager_.GetUserPublicKey());
+ EXPECT_EQ(private_key_, enrollment_manager_.GetUserPrivateKey());
+}
+
+TEST_F(CryptAuthEnrollmentManagerTest, GCMRegistrationFails) {
+ // Initialize |enrollment_manager_|.
+ ON_CALL(*sync_scheduler(), GetStrategy())
+ .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH));
+ gcm_manager_.set_registration_id(std::string());
+ enrollment_manager_.Start();
+
+ // Trigger a sync request.
+ EXPECT_CALL(*this, OnEnrollmentStartedProxy());
+ auto sync_request = base::MakeUnique<SyncScheduler::SyncRequest>(
+ enrollment_manager_.GetSyncScheduler());
+ static_cast<SyncScheduler::Delegate*>(&enrollment_manager_)
+ ->OnSyncRequested(std::move(sync_request));
+
+ // Complete GCM registration with failure.
+ EXPECT_CALL(*this, OnEnrollmentFinishedProxy(false));
+ gcm_manager_.CompleteRegistration(std::string());
+}
+
+TEST_F(CryptAuthEnrollmentManagerTest, ReenrollOnGCMPushMessage) {
+ enrollment_manager_.Start();
+
+ // Simulate receiving a GCM push message, forcing the device to re-enroll.
+ gcm_manager_.PushReenrollMessage();
+ auto completion_callback =
+ FireSchedulerForEnrollment(INVOCATION_REASON_SERVER_INITIATED);
+
+ EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
+ completion_callback.Run(true);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_enrollment_utils.cc b/chromium/components/cryptauth/cryptauth_enrollment_utils.cc
new file mode 100644
index 00000000000..e8429532ffe
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_enrollment_utils.cc
@@ -0,0 +1,44 @@
+// 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/cryptauth_enrollment_utils.h"
+
+#include <math.h>
+#include <stddef.h>
+
+#include "base/base64url.h"
+#include "base/md5.h"
+#include "base/sha1.h"
+
+namespace cryptauth {
+
+int64_t HashStringToInt64(const std::string& string) {
+ base::MD5Context context;
+ base::MD5Init(&context);
+ base::MD5Update(&context, string);
+
+ base::MD5Digest digest;
+ base::MD5Final(&digest, &context);
+
+ // Fold the digest into an int64_t value. |digest.a| is a 16-byte array, so we
+ // sum the two 8-byte halves of the digest to create the hash.
+ int64_t hash = 0;
+ for (size_t i = 0; i < sizeof(digest.a); ++i) {
+ uint8_t byte = digest.a[i];
+ hash += static_cast<int64_t>(byte) << (i % sizeof(int64_t));
+ }
+
+ return hash;
+}
+
+std::string CalculateDeviceUserId(const std::string& device_id,
+ const std::string& user_id) {
+ std::string device_user_id;
+ base::Base64UrlEncode(base::SHA1HashString(device_id + "|" + user_id),
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &device_user_id);
+ return device_user_id;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_enrollment_utils.h b/chromium/components/cryptauth/cryptauth_enrollment_utils.h
new file mode 100644
index 00000000000..375eee3b3d8
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_enrollment_utils.h
@@ -0,0 +1,23 @@
+// 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_CRYPTAUTH_ENROLLMENT_UTILS_H
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_ENROLLMENT_UTILS_H
+
+#include <stdint.h>
+#include <string>
+
+namespace cryptauth {
+
+// Calculates an id for the specified user on the specified device.
+std::string CalculateDeviceUserId(const std::string& device_id,
+ const std::string& user_id);
+
+// Hashes |string| to an int64_t value. These int64_t hashes are used to fill
+// the GcmDeviceInfo proto to upload to CryptAuth.
+int64_t HashStringToInt64(const std::string& string);
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_ENROLLMENT_UTILS_H
diff --git a/chromium/components/cryptauth/cryptauth_gcm_manager.cc b/chromium/components/cryptauth/cryptauth_gcm_manager.cc
new file mode 100644
index 00000000000..d28930014e1
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_gcm_manager.cc
@@ -0,0 +1,30 @@
+// 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/cryptauth_gcm_manager.h"
+
+#include "components/cryptauth/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+
+namespace cryptauth {
+
+CryptAuthGCMManager::Observer::~Observer() {
+}
+
+void CryptAuthGCMManager::Observer::OnGCMRegistrationResult(bool success) {
+}
+
+void CryptAuthGCMManager::Observer::OnReenrollMessage() {
+}
+
+void CryptAuthGCMManager::Observer::OnResyncMessage() {
+}
+
+// static.
+void CryptAuthGCMManager::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterStringPref(prefs::kCryptAuthGCMRegistrationId,
+ std::string());
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_gcm_manager.h b/chromium/components/cryptauth/cryptauth_gcm_manager.h
new file mode 100644
index 00000000000..5be472da73f
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_gcm_manager.h
@@ -0,0 +1,64 @@
+// 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_CRYPTAUTH_GCM_MANAGER_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_GCM_MANAGER_H_
+
+#include <string>
+
+class PrefRegistrySimple;
+
+namespace cryptauth {
+
+// Interface for the manager controlling GCM registrations and handling GCM push
+// messages for CryptAuth. CryptAuth sends GCM messages to request the local
+// device to re-enroll to get the freshest device state, and to notify the
+// local device to resync the remote device list when this list changes.
+class CryptAuthGCMManager {
+ public:
+ class Observer {
+ public:
+ virtual ~Observer();
+
+ // Called when a gcm registration attempt finishes with the |success| of the
+ // attempt.
+ virtual void OnGCMRegistrationResult(bool success);
+
+ // Called when a GCM message is received to re-enroll the device with
+ // CryptAuth.
+ virtual void OnReenrollMessage();
+
+ // Called when a GCM message is received to sync down new devices from
+ // CryptAuth.
+ virtual void OnResyncMessage();
+ };
+
+ virtual ~CryptAuthGCMManager() {}
+
+ // Registers the prefs used by the manager to the given |pref_service|.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ // Starts listening to incoming GCM messages. If GCM registration is completed
+ // after this function is called, then messages will also be handled properly.
+ virtual void StartListening() = 0;
+
+ // Begins registration with GCM. The Observer::OnGCMRegistrationResult()
+ // observer function will be called when registration completes.
+ virtual void RegisterWithGCM() = 0;
+
+ // Returns the GCM registration id received from the last successful
+ // registration. If registration has not been performed, then an empty string
+ // will be returned.
+ virtual std::string GetRegistrationId() = 0;
+
+ // Adds an observer.
+ virtual void AddObserver(Observer* observer) = 0;
+
+ // Removes an observer.
+ virtual void RemoveObserver(Observer* observer) = 0;
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_GCM_MANAGER_H_
diff --git a/chromium/components/cryptauth/cryptauth_gcm_manager_impl.cc b/chromium/components/cryptauth/cryptauth_gcm_manager_impl.cc
new file mode 100644
index 00000000000..6178481438a
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_gcm_manager_impl.cc
@@ -0,0 +1,156 @@
+// 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/cryptauth_gcm_manager_impl.h"
+
+#include "base/bind.h"
+#include "base/strings/string_util.h"
+#include "components/cryptauth/pref_names.h"
+#include "components/gcm_driver/gcm_driver.h"
+#include "components/prefs/pref_service.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace cryptauth {
+
+namespace {
+
+// The GCM app id identifies the client.
+const char kCryptAuthGCMAppId[] = "com.google.chrome.cryptauth";
+
+// The GCM sender id identifies the CryptAuth server.
+const char kCryptAuthGCMSenderId[] = "381449029288";
+
+// The 'registrationTickleType' key-value pair is present in GCM push
+// messages. The values correspond to a server-side enum.
+const char kRegistrationTickleTypeKey[] = "registrationTickleType";
+const char kRegistrationTickleTypeForceEnrollment[] = "1";
+const char kRegistrationTickleTypeUpdateEnrollment[] = "2";
+const char kRegistrationTickleTypeDevicesSync[] = "3";
+
+} // namespace
+
+CryptAuthGCMManagerImpl::CryptAuthGCMManagerImpl(gcm::GCMDriver* gcm_driver,
+ PrefService* pref_service)
+ : gcm_driver_(gcm_driver),
+ pref_service_(pref_service),
+ registration_in_progress_(false),
+ weak_ptr_factory_(this) {
+}
+
+CryptAuthGCMManagerImpl::~CryptAuthGCMManagerImpl() {
+ if (gcm_driver_->GetAppHandler(kCryptAuthGCMAppId) == this)
+ gcm_driver_->RemoveAppHandler(kCryptAuthGCMAppId);
+}
+
+void CryptAuthGCMManagerImpl::StartListening() {
+ if (gcm_driver_->GetAppHandler(kCryptAuthGCMAppId) == this) {
+ PA_LOG(INFO) << "GCM app handler already added";
+ return;
+ }
+
+ gcm_driver_->AddAppHandler(kCryptAuthGCMAppId, this);
+}
+
+void CryptAuthGCMManagerImpl::RegisterWithGCM() {
+ if (registration_in_progress_) {
+ PA_LOG(INFO) << "GCM Registration is already in progress";
+ return;
+ }
+
+ PA_LOG(INFO) << "Beginning GCM registration...";
+ registration_in_progress_ = true;
+
+ std::vector<std::string> sender_ids(1, kCryptAuthGCMSenderId);
+ gcm_driver_->Register(
+ kCryptAuthGCMAppId, sender_ids,
+ base::Bind(&CryptAuthGCMManagerImpl::OnRegistrationCompleted,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+std::string CryptAuthGCMManagerImpl::GetRegistrationId() {
+ return pref_service_->GetString(prefs::kCryptAuthGCMRegistrationId);
+}
+
+void CryptAuthGCMManagerImpl::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void CryptAuthGCMManagerImpl::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void CryptAuthGCMManagerImpl::ShutdownHandler() {
+}
+
+void CryptAuthGCMManagerImpl::OnStoreReset() {
+ // We will automatically re-register to GCM and re-enroll the new registration
+ // ID to Cryptauth during the next scheduled sync.
+ pref_service_->ClearPref(prefs::kCryptAuthGCMRegistrationId);
+}
+
+void CryptAuthGCMManagerImpl::OnMessage(const std::string& app_id,
+ const gcm::IncomingMessage& message) {
+ std::vector<std::string> fields;
+ for (const auto& kv : message.data) {
+ fields.push_back(std::string(kv.first) + ": " + std::string(kv.second));
+ }
+
+ PA_LOG(INFO) << "GCM message received:\n"
+ << " sender_id: " << message.sender_id << "\n"
+ << " collapse_key: " << message.collapse_key << "\n"
+ << " data:\n " << base::JoinString(fields, "\n ");
+
+ if (message.data.find(kRegistrationTickleTypeKey) == message.data.end()) {
+ PA_LOG(WARNING) << "GCM message does not contain 'registrationTickleType'.";
+ } else {
+ std::string tickle_type = message.data.at(kRegistrationTickleTypeKey);
+ if (tickle_type == kRegistrationTickleTypeForceEnrollment ||
+ tickle_type == kRegistrationTickleTypeUpdateEnrollment) {
+ // These tickle types correspond to re-enrollment messages.
+ for (auto& observer : observers_)
+ observer.OnReenrollMessage();
+ } else if (tickle_type == kRegistrationTickleTypeDevicesSync) {
+ for (auto& observer : observers_)
+ observer.OnResyncMessage();
+ } else {
+ PA_LOG(WARNING) << "Unknown tickle type in GCM message.";
+ }
+ }
+}
+
+void CryptAuthGCMManagerImpl::OnMessagesDeleted(const std::string& app_id) {
+}
+
+void CryptAuthGCMManagerImpl::OnSendError(
+ const std::string& app_id,
+ const gcm::GCMClient::SendErrorDetails& details) {
+ NOTREACHED();
+}
+
+void CryptAuthGCMManagerImpl::OnSendAcknowledged(
+ const std::string& app_id,
+ const std::string& message_id) {
+ NOTREACHED();
+}
+
+void CryptAuthGCMManagerImpl::OnRegistrationCompleted(
+ const std::string& registration_id,
+ gcm::GCMClient::Result result) {
+ registration_in_progress_ = false;
+ if (result != gcm::GCMClient::SUCCESS) {
+ PA_LOG(WARNING) << "GCM registration failed with result="
+ << static_cast<int>(result);
+ for (auto& observer : observers_)
+ observer.OnGCMRegistrationResult(false);
+ return;
+ }
+
+ PA_LOG(INFO) << "GCM registration success, registration_id="
+ << registration_id;
+ pref_service_->SetString(prefs::kCryptAuthGCMRegistrationId, registration_id);
+ for (auto& observer : observers_)
+ observer.OnGCMRegistrationResult(true);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_gcm_manager_impl.h b/chromium/components/cryptauth/cryptauth_gcm_manager_impl.h
new file mode 100644
index 00000000000..28c64db8e36
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_gcm_manager_impl.h
@@ -0,0 +1,81 @@
+// 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_CRYPTAUTH_GCM_MANAGER_IMPL_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_GCM_MANAGER_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "components/cryptauth/cryptauth_gcm_manager.h"
+#include "components/gcm_driver/common/gcm_messages.h"
+#include "components/gcm_driver/gcm_app_handler.h"
+#include "components/gcm_driver/gcm_client.h"
+
+class PrefService;
+
+namespace gcm {
+class GCMDriver;
+};
+
+namespace cryptauth {
+
+// Implementation of CryptAuthGCMManager.
+class CryptAuthGCMManagerImpl : public CryptAuthGCMManager,
+ public gcm::GCMAppHandler {
+ public:
+ // Creates the manager:
+ // |gcm_driver|: Handles the actual GCM communications. The driver is not
+ // owned and must outlive this instance.
+ // |pref_service|: Contains preferences across browser restarts, and should
+ // have been registered through RegisterPrefs(). The service is not owned
+ // and must outlive this instance.
+ CryptAuthGCMManagerImpl(gcm::GCMDriver* gcm_driver,
+ PrefService* pref_service);
+
+ ~CryptAuthGCMManagerImpl() override;
+
+ // CryptAuthGCMManager:
+ void StartListening() override;
+ void RegisterWithGCM() override;
+ std::string GetRegistrationId() override;
+ void AddObserver(Observer* observer) override;
+ void RemoveObserver(Observer* observer) override;
+
+ private:
+ // GCMAppHandler:
+ 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;
+
+ // Called when GCM registration completes.
+ void OnRegistrationCompleted(const std::string& registration_id,
+ gcm::GCMClient::Result result);
+
+ // Handles the communications with GCM. Not owned.
+ gcm::GCMDriver* gcm_driver_;
+
+ // Manages preferences across process restarts. Not owned.
+ PrefService* pref_service_;
+
+ // Whether a GCM registration is currently being processed.
+ bool registration_in_progress_;
+
+ // List of observers.
+ base::ObserverList<Observer> observers_;
+
+ base::WeakPtrFactory<CryptAuthGCMManagerImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthGCMManagerImpl);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_GCM_MANAGER_IMPL_H_
diff --git a/chromium/components/cryptauth/cryptauth_gcm_manager_impl_unittest.cc b/chromium/components/cryptauth/cryptauth_gcm_manager_impl_unittest.cc
new file mode 100644
index 00000000000..473a24c0c79
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_gcm_manager_impl_unittest.cc
@@ -0,0 +1,198 @@
+// 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/cryptauth_gcm_manager_impl.h"
+
+#include "base/macros.h"
+#include "components/cryptauth/pref_names.h"
+#include "components/gcm_driver/fake_gcm_driver.h"
+#include "components/gcm_driver/gcm_client.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::SaveArg;
+
+namespace cryptauth {
+
+namespace {
+
+const char kCryptAuthGCMAppId[] = "com.google.chrome.cryptauth";
+const char kCryptAuthGCMSenderId[] = "381449029288";
+const char kExistingGCMRegistrationId[] = "cirrus";
+const char kNewGCMRegistrationId[] = "stratus";
+const char kCryptAuthMessageCollapseKey[] =
+ "collapse_cryptauth_sync_DEVICES_SYNC";
+
+// Mock GCMDriver implementation for testing.
+class MockGCMDriver : public gcm::FakeGCMDriver {
+ public:
+ MockGCMDriver() {}
+ ~MockGCMDriver() override {}
+
+ MOCK_METHOD2(AddAppHandler,
+ void(const std::string& app_id, gcm::GCMAppHandler* handler));
+
+ MOCK_METHOD2(RegisterImpl,
+ void(const std::string& app_id,
+ const std::vector<std::string>& sender_ids));
+
+ using gcm::GCMDriver::RegisterFinished;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockGCMDriver);
+};
+
+} // namespace
+
+class CryptAuthGCMManagerImplTest
+ : public testing::Test,
+ public CryptAuthGCMManager::Observer {
+ protected:
+ CryptAuthGCMManagerImplTest()
+ : gcm_manager_(&gcm_driver_, &pref_service_) {}
+
+ // testing::Test:
+ void SetUp() override {
+ CryptAuthGCMManager::RegisterPrefs(pref_service_.registry());
+ gcm_manager_.AddObserver(this);
+ EXPECT_CALL(gcm_driver_, AddAppHandler(kCryptAuthGCMAppId, &gcm_manager_));
+ gcm_manager_.StartListening();
+ }
+
+ void TearDown() override { gcm_manager_.RemoveObserver(this); }
+
+ void RegisterWithGCM(gcm::GCMClient::Result registration_result) {
+ std::vector<std::string> sender_ids;
+ EXPECT_CALL(gcm_driver_, RegisterImpl(kCryptAuthGCMAppId, _))
+ .WillOnce(SaveArg<1>(&sender_ids));
+ gcm_manager_.RegisterWithGCM();
+
+ ASSERT_EQ(1u, sender_ids.size());
+ EXPECT_EQ(kCryptAuthGCMSenderId, sender_ids[0]);
+
+ bool success = (registration_result == gcm::GCMClient::SUCCESS);
+ EXPECT_CALL(*this, OnGCMRegistrationResultProxy(success));
+ gcm_driver_.RegisterFinished(kCryptAuthGCMAppId, kNewGCMRegistrationId,
+ registration_result);
+ }
+
+ // CryptAuthGCMManager::Observer:
+ void OnGCMRegistrationResult(bool success) override {
+ OnGCMRegistrationResultProxy(success);
+ }
+
+ void OnReenrollMessage() override { OnReenrollMessageProxy(); }
+
+ void OnResyncMessage() override { OnResyncMessageProxy(); }
+
+ MOCK_METHOD1(OnGCMRegistrationResultProxy, void(bool));
+ MOCK_METHOD0(OnReenrollMessageProxy, void());
+ MOCK_METHOD0(OnResyncMessageProxy, void());
+
+ testing::StrictMock<MockGCMDriver> gcm_driver_;
+
+ TestingPrefServiceSimple pref_service_;
+
+ CryptAuthGCMManagerImpl gcm_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthGCMManagerImplTest);
+};
+
+TEST_F(CryptAuthGCMManagerImplTest, RegisterPrefs) {
+ TestingPrefServiceSimple pref_service;
+ CryptAuthGCMManager::RegisterPrefs(pref_service.registry());
+ EXPECT_TRUE(pref_service.FindPreference(prefs::kCryptAuthGCMRegistrationId));
+}
+
+TEST_F(CryptAuthGCMManagerImplTest, RegistrationSucceeds) {
+ EXPECT_EQ(std::string(), gcm_manager_.GetRegistrationId());
+ RegisterWithGCM(gcm::GCMClient::SUCCESS);
+ EXPECT_EQ(kNewGCMRegistrationId, gcm_manager_.GetRegistrationId());
+}
+
+TEST_F(CryptAuthGCMManagerImplTest,
+ RegistrationSucceedsWithExistingRegistration) {
+ pref_service_.SetString(prefs::kCryptAuthGCMRegistrationId,
+ kExistingGCMRegistrationId);
+ EXPECT_EQ(kExistingGCMRegistrationId, gcm_manager_.GetRegistrationId());
+ RegisterWithGCM(gcm::GCMClient::SUCCESS);
+ EXPECT_EQ(kNewGCMRegistrationId, gcm_manager_.GetRegistrationId());
+ EXPECT_EQ(kNewGCMRegistrationId,
+ pref_service_.GetString(prefs::kCryptAuthGCMRegistrationId));
+}
+
+TEST_F(CryptAuthGCMManagerImplTest, RegisterWithGCMFails) {
+ EXPECT_EQ(std::string(), gcm_manager_.GetRegistrationId());
+ RegisterWithGCM(gcm::GCMClient::SERVER_ERROR);
+ EXPECT_EQ(std::string(), gcm_manager_.GetRegistrationId());
+ EXPECT_EQ(std::string(),
+ pref_service_.GetString(prefs::kCryptAuthGCMRegistrationId));
+}
+
+TEST_F(CryptAuthGCMManagerImplTest,
+ RegisterWithGCMFailsWithExistingRegistration) {
+ pref_service_.SetString(prefs::kCryptAuthGCMRegistrationId,
+ kExistingGCMRegistrationId);
+ EXPECT_EQ(kExistingGCMRegistrationId, gcm_manager_.GetRegistrationId());
+ RegisterWithGCM(gcm::GCMClient::SERVER_ERROR);
+ EXPECT_EQ(kExistingGCMRegistrationId, gcm_manager_.GetRegistrationId());
+ EXPECT_EQ(kExistingGCMRegistrationId,
+ pref_service_.GetString(prefs::kCryptAuthGCMRegistrationId));
+}
+
+TEST_F(CryptAuthGCMManagerImplTest,
+ RegistrationFailsThenSucceeds) {
+ EXPECT_EQ(std::string(), gcm_manager_.GetRegistrationId());
+ RegisterWithGCM(gcm::GCMClient::NETWORK_ERROR);
+ EXPECT_EQ(std::string(), gcm_manager_.GetRegistrationId());
+ RegisterWithGCM(gcm::GCMClient::SUCCESS);
+ EXPECT_EQ(kNewGCMRegistrationId, gcm_manager_.GetRegistrationId());
+}
+
+TEST_F(CryptAuthGCMManagerImplTest, ConcurrentRegistrations) {
+ // If multiple RegisterWithGCM() calls are made concurrently, only one
+ // registration attempt should actually be made.
+ EXPECT_CALL(gcm_driver_, RegisterImpl(kCryptAuthGCMAppId, _));
+ gcm_manager_.RegisterWithGCM();
+ gcm_manager_.RegisterWithGCM();
+ gcm_manager_.RegisterWithGCM();
+
+ EXPECT_CALL(*this, OnGCMRegistrationResultProxy(true));
+ gcm_driver_.RegisterFinished(kCryptAuthGCMAppId, kNewGCMRegistrationId,
+ gcm::GCMClient::SUCCESS);
+ EXPECT_EQ(kNewGCMRegistrationId, gcm_manager_.GetRegistrationId());
+}
+
+TEST_F(CryptAuthGCMManagerImplTest, ReenrollmentMessagesReceived) {
+ EXPECT_CALL(*this, OnReenrollMessageProxy()).Times(2);
+
+ gcm::IncomingMessage message;
+ message.data["registrationTickleType"] = "1"; // FORCE_ENROLLMENT
+ message.collapse_key = kCryptAuthMessageCollapseKey;
+ message.sender_id = kCryptAuthGCMSenderId;
+
+ gcm::GCMAppHandler* gcm_app_handler =
+ static_cast<gcm::GCMAppHandler*>(&gcm_manager_);
+ gcm_app_handler->OnMessage(kCryptAuthGCMAppId, message);
+ message.data["registrationTickleType"] = "2"; // UPDATE_ENROLLMENT
+ gcm_app_handler->OnMessage(kCryptAuthGCMAppId, message);
+}
+
+TEST_F(CryptAuthGCMManagerImplTest, ResyncMessagesReceived) {
+ EXPECT_CALL(*this, OnResyncMessageProxy()).Times(2);
+
+ gcm::IncomingMessage message;
+ message.data["registrationTickleType"] = "3"; // DEVICES_SYNC
+ message.collapse_key = kCryptAuthMessageCollapseKey;
+ message.sender_id = kCryptAuthGCMSenderId;
+
+ gcm::GCMAppHandler* gcm_app_handler =
+ static_cast<gcm::GCMAppHandler*>(&gcm_manager_);
+ gcm_app_handler->OnMessage(kCryptAuthGCMAppId, message);
+ gcm_app_handler->OnMessage(kCryptAuthGCMAppId, message);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_test_util.cc b/chromium/components/cryptauth/cryptauth_test_util.cc
new file mode 100644
index 00000000000..60b31f8ecff
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_test_util.cc
@@ -0,0 +1,17 @@
+// 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/cryptauth_test_util.h"
+
+namespace cryptauth {
+
+// Attributes of the default test remote device.
+const char kTestRemoteDeviceUserId[] = "example@gmail.com";
+const char kTestRemoteDeviceName[] = "remote device";
+const char kTestRemoteDevicePublicKey[] = "public key";
+const char kTestRemoteDeviceBluetoothAddress[] = "AA:BB:CC:DD:EE:FF";
+const char kTestRemoteDevicePSK[] = "remote device psk";
+const char kTestRemoteDeviceSignInChallenge[] = "sign-in challenge";
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_test_util.h b/chromium/components/cryptauth/cryptauth_test_util.h
new file mode 100644
index 00000000000..6712f42db06
--- /dev/null
+++ b/chromium/components/cryptauth/cryptauth_test_util.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_CRYPTAUTH_CRYPTAUTH_TEST_UTIL_H_
+#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_TEST_UTIL_H_
+
+#include "components/cryptauth/remote_device.h"
+
+namespace cryptauth {
+
+// Attributes of the default test remote device.
+extern const char kTestRemoteDeviceUserId[];
+extern const char kTestRemoteDeviceName[];
+extern const char kTestRemoteDevicePublicKey[];
+extern const char kTestRemoteDeviceBluetoothAddress[];
+extern const char kTestRemoteDevicePSK[];
+extern const char kTestRemoteDeviceSignInChallenge[];
+
+// Returns a BLE RemoteDevice used for tests.
+inline RemoteDevice CreateLERemoteDeviceForTest() {
+ return RemoteDevice(kTestRemoteDeviceUserId, kTestRemoteDeviceName,
+ kTestRemoteDevicePublicKey, RemoteDevice::BLUETOOTH_LE,
+ kTestRemoteDeviceBluetoothAddress, kTestRemoteDevicePSK,
+ kTestRemoteDeviceSignInChallenge);
+}
+
+// Returns a classic Bluetooth RemoteDevice used for tests.
+inline RemoteDevice CreateClassicRemoteDeviceForTest() {
+ return RemoteDevice(kTestRemoteDeviceUserId, kTestRemoteDeviceName,
+ kTestRemoteDevicePublicKey,
+ RemoteDevice::BLUETOOTH_CLASSIC,
+ kTestRemoteDeviceBluetoothAddress, kTestRemoteDevicePSK,
+ kTestRemoteDeviceSignInChallenge);
+}
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_TEST_UTIL_H_
diff --git a/chromium/components/cryptauth/eid_generator.cc b/chromium/components/cryptauth/eid_generator.cc
new file mode 100644
index 00000000000..ecbada9c545
--- /dev/null
+++ b/chromium/components/cryptauth/eid_generator.cc
@@ -0,0 +1,450 @@
+// 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/eid_generator.h"
+
+#include <cstring>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/sys_byteorder.h"
+#include "base/time/default_clock.h"
+#include "base/time/time.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/remote_device.h"
+#include "components/proximity_auth/logging/logging.h"
+#include "crypto/sha2.h"
+
+namespace cryptauth {
+
+namespace {
+const int64_t kNoTimestamp = 0;
+const int64_t kMaxPositiveInt64TValue = 0x7FFFFFFF;
+} // namespace
+
+const int64_t EidGenerator::kNumMsInEidPeriod =
+ base::TimeDelta::FromHours(8).InMilliseconds();
+const int64_t EidGenerator::kNumMsInBeginningOfEidPeriod =
+ base::TimeDelta::FromHours(2).InMilliseconds();
+const int32_t EidGenerator::kNumBytesInEidValue = 2;
+const int8_t EidGenerator::kBluetooth4Flag = 0x01;
+
+// static
+EidGenerator* EidGenerator::GetInstance() {
+ return base::Singleton<EidGenerator>::get();
+}
+
+std::string EidGenerator::EidComputationHelperImpl::GenerateEidDataForDevice(
+ const std::string& eid_seed,
+ const int64_t start_of_period_timestamp_ms,
+ const std::string* extra_entropy) {
+ // The data to hash is the eid seed, followed by the extra entropy (if it
+ // exists), followed by the timestamp.
+ std::string to_hash = eid_seed;
+ if (extra_entropy) {
+ to_hash += *extra_entropy;
+ }
+ uint64_t timestamp_data =
+ base::HostToNet64(static_cast<uint64_t>(start_of_period_timestamp_ms));
+ to_hash.append(reinterpret_cast<char*>(&timestamp_data), sizeof(uint64_t));
+
+ std::string result = crypto::SHA256HashString(to_hash);
+ result.resize(EidGenerator::kNumBytesInEidValue);
+ return result;
+}
+
+EidGenerator::EidData::EidData(const DataWithTimestamp current_data,
+ std::unique_ptr<DataWithTimestamp> adjacent_data)
+ : current_data(current_data), adjacent_data(std::move(adjacent_data)) {}
+
+EidGenerator::EidData::~EidData() {}
+
+EidGenerator::EidData::AdjacentDataType
+EidGenerator::EidData::GetAdjacentDataType() const {
+ if (!adjacent_data) {
+ return AdjacentDataType::NONE;
+ }
+
+ if (adjacent_data->start_timestamp_ms < current_data.start_timestamp_ms) {
+ return AdjacentDataType::PAST;
+ }
+
+ return AdjacentDataType::FUTURE;
+}
+
+std::string EidGenerator::EidData::DataInHex() const {
+ std::string str = "[" + current_data.DataInHex();
+
+ if (adjacent_data) {
+ return str + ", " + adjacent_data->DataInHex() + "]";
+ }
+
+ return str + "]";
+}
+
+EidGenerator::DataWithTimestamp::DataWithTimestamp(
+ const std::string& data,
+ const int64_t start_timestamp_ms,
+ const int64_t end_timestamp_ms)
+ : data(data),
+ start_timestamp_ms(start_timestamp_ms),
+ end_timestamp_ms(end_timestamp_ms) {
+ DCHECK(start_timestamp_ms < end_timestamp_ms);
+ DCHECK(data.size());
+}
+
+EidGenerator::DataWithTimestamp::DataWithTimestamp(
+ const DataWithTimestamp& other)
+ : data(other.data),
+ start_timestamp_ms(other.start_timestamp_ms),
+ end_timestamp_ms(other.end_timestamp_ms) {
+ DCHECK(start_timestamp_ms < end_timestamp_ms);
+ DCHECK(data.size());
+}
+
+bool EidGenerator::DataWithTimestamp::ContainsTime(
+ const int64_t timestamp_ms) const {
+ return start_timestamp_ms <= timestamp_ms && timestamp_ms < end_timestamp_ms;
+}
+
+std::string EidGenerator::DataWithTimestamp::DataInHex() const {
+ std::stringstream ss;
+ ss << "0x" << std::hex;
+
+ for (size_t i = 0; i < data.size(); i++) {
+ ss << static_cast<int>(data.data()[i]);
+ }
+
+ return ss.str();
+}
+
+EidGenerator::EidGenerator()
+ : EidGenerator(base::WrapUnique(new EidComputationHelperImpl()),
+ base::WrapUnique(new base::DefaultClock())) {}
+
+EidGenerator::EidGenerator(
+ std::unique_ptr<EidComputationHelper> computation_helper,
+ std::unique_ptr<base::Clock> clock)
+ : clock_(std::move(clock)),
+ eid_computation_helper_(std::move(computation_helper)) {}
+
+EidGenerator::~EidGenerator() {}
+
+std::unique_ptr<EidGenerator::EidData>
+EidGenerator::GenerateBackgroundScanFilter(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+ std::unique_ptr<EidPeriodTimestamps> timestamps =
+ GetEidPeriodTimestamps(scanning_device_beacon_seeds);
+ if (!timestamps) {
+ // If the device does not have seeds for the correct period, no EIDs can be
+ // generated.
+ return nullptr;
+ }
+
+ std::unique_ptr<DataWithTimestamp> current_eid = GenerateEidDataWithTimestamp(
+ scanning_device_beacon_seeds,
+ timestamps->current_period_start_timestamp_ms,
+ timestamps->current_period_end_timestamp_ms);
+ if (!current_eid) {
+ // The current EID could not be generated.
+ return nullptr;
+ }
+
+ std::unique_ptr<DataWithTimestamp> adjacent_eid =
+ GenerateEidDataWithTimestamp(
+ scanning_device_beacon_seeds,
+ timestamps->adjacent_period_start_timestamp_ms,
+ timestamps->adjacent_period_end_timestamp_ms);
+ return base::WrapUnique(new EidData(*current_eid, std::move(adjacent_eid)));
+}
+
+std::unique_ptr<EidGenerator::DataWithTimestamp>
+EidGenerator::GenerateAdvertisement(
+ const std::string& advertising_device_public_key,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+ std::unique_ptr<EidPeriodTimestamps> timestamps =
+ GetEidPeriodTimestamps(scanning_device_beacon_seeds);
+ if (!timestamps) {
+ return nullptr;
+ }
+
+ return GenerateAdvertisement(advertising_device_public_key,
+ scanning_device_beacon_seeds,
+ timestamps->current_period_start_timestamp_ms,
+ timestamps->current_period_end_timestamp_ms);
+}
+
+RemoteDevice const* EidGenerator::IdentifyRemoteDeviceByAdvertisement(
+ const std::string& advertisement_service_data,
+ const std::vector<RemoteDevice>& device_list,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+ // Resize the service data to analyze only the first |2 * kNumBytesInEidValue|
+ // bytes. The bytes following these are flags, so they are not needed to
+ // identify the device which sent a message.
+ std::string service_data_without_flags = advertisement_service_data;
+ service_data_without_flags.resize(2 * kNumBytesInEidValue);
+
+ for (const auto& remote_device : device_list) {
+ std::vector<std::string> possible_advertisements =
+ GeneratePossibleAdvertisements(remote_device.public_key,
+ scanning_device_beacon_seeds);
+ for (const auto& possible_advertisement : possible_advertisements) {
+ if (service_data_without_flags == possible_advertisement) {
+ return const_cast<RemoteDevice*>(&remote_device);
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+std::vector<std::string> EidGenerator::GeneratePossibleAdvertisements(
+ const std::string& advertising_device_public_key,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+ std::vector<std::string> possible_advertisements;
+
+ std::unique_ptr<EidPeriodTimestamps> timestamps =
+ GetEidPeriodTimestamps(scanning_device_beacon_seeds, true);
+ if (!timestamps) {
+ // If the devices do not have seeds for the correct period, no
+ // advertisements can be generated.
+ return possible_advertisements;
+ }
+
+ if (timestamps->current_period_start_timestamp_ms != kNoTimestamp) {
+ std::unique_ptr<DataWithTimestamp> current_advertisement =
+ GenerateAdvertisement(advertising_device_public_key,
+ scanning_device_beacon_seeds,
+ timestamps->current_period_start_timestamp_ms,
+ timestamps->current_period_end_timestamp_ms);
+ if (current_advertisement) {
+ possible_advertisements.push_back(current_advertisement->data);
+ }
+ }
+
+ std::unique_ptr<DataWithTimestamp> adjacent_advertisement =
+ GenerateAdvertisement(advertising_device_public_key,
+ scanning_device_beacon_seeds,
+ timestamps->adjacent_period_start_timestamp_ms,
+ timestamps->adjacent_period_end_timestamp_ms);
+ if (adjacent_advertisement) {
+ possible_advertisements.push_back(adjacent_advertisement->data);
+ }
+
+ return possible_advertisements;
+}
+
+std::unique_ptr<EidGenerator::DataWithTimestamp>
+EidGenerator::GenerateAdvertisement(
+ const std::string& advertising_device_public_key,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t start_of_period_timestamp_ms,
+ const int64_t end_of_period_timestamp_ms) const {
+ std::unique_ptr<DataWithTimestamp> advertising_device_identifying_data =
+ GenerateEidDataWithTimestamp(
+ scanning_device_beacon_seeds, start_of_period_timestamp_ms,
+ end_of_period_timestamp_ms, &advertising_device_public_key);
+ std::unique_ptr<DataWithTimestamp> scanning_device_identifying_data =
+ GenerateEidDataWithTimestamp(scanning_device_beacon_seeds,
+ start_of_period_timestamp_ms,
+ end_of_period_timestamp_ms);
+ if (!advertising_device_identifying_data ||
+ !scanning_device_identifying_data) {
+ return nullptr;
+ }
+
+ std::string full_advertisement = scanning_device_identifying_data->data +
+ advertising_device_identifying_data->data;
+ return base::WrapUnique(new DataWithTimestamp(full_advertisement,
+ start_of_period_timestamp_ms,
+ end_of_period_timestamp_ms));
+}
+
+std::unique_ptr<EidGenerator::DataWithTimestamp>
+EidGenerator::GenerateEidDataWithTimestamp(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t start_of_period_timestamp_ms,
+ const int64_t end_of_period_timestamp_ms) const {
+ return GenerateEidDataWithTimestamp(scanning_device_beacon_seeds,
+ start_of_period_timestamp_ms,
+ end_of_period_timestamp_ms, nullptr);
+}
+
+std::unique_ptr<EidGenerator::DataWithTimestamp>
+EidGenerator::GenerateEidDataWithTimestamp(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t start_of_period_timestamp_ms,
+ const int64_t end_of_period_timestamp_ms,
+ const std::string* extra_entropy) const {
+ std::unique_ptr<std::string> eid_seed = GetEidSeedForPeriod(
+ scanning_device_beacon_seeds, start_of_period_timestamp_ms);
+ if (!eid_seed) {
+ return nullptr;
+ }
+
+ std::string eid_data = eid_computation_helper_->GenerateEidDataForDevice(
+ *eid_seed, start_of_period_timestamp_ms, extra_entropy);
+
+ return base::WrapUnique(new DataWithTimestamp(
+ eid_data, start_of_period_timestamp_ms, end_of_period_timestamp_ms));
+}
+
+std::unique_ptr<std::string> EidGenerator::GetEidSeedForPeriod(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t start_of_period_timestamp_ms) const {
+ for (auto seed : scanning_device_beacon_seeds) {
+ if (seed.start_time_millis() <= start_of_period_timestamp_ms &&
+ start_of_period_timestamp_ms < seed.end_time_millis()) {
+ return base::WrapUnique(new std::string(seed.data()));
+ }
+ }
+
+ return nullptr;
+}
+
+std::unique_ptr<EidGenerator::EidPeriodTimestamps>
+EidGenerator::GetEidPeriodTimestamps(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+ return GetEidPeriodTimestamps(scanning_device_beacon_seeds, false);
+}
+
+std::unique_ptr<EidGenerator::EidPeriodTimestamps>
+EidGenerator::GetEidPeriodTimestamps(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const bool allow_non_current_periods) const {
+ base::Time now = clock_->Now();
+ int64_t current_time_ms = now.ToJavaTime();
+
+ std::unique_ptr<BeaconSeed> current_seed = GetBeaconSeedForCurrentPeriod(
+ scanning_device_beacon_seeds, current_time_ms);
+ if (!current_seed) {
+ if (!allow_non_current_periods) {
+ // No seed existed for the current time.
+ return nullptr;
+ }
+
+ return GetClosestPeriod(scanning_device_beacon_seeds, current_time_ms);
+ }
+
+ // Now that the start of the 14-day EID seed period has been determined, the
+ // 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;
+ if (start_of_eid_period_ms <= current_time_ms &&
+ current_time_ms < end_of_eid_period_ms) {
+ int64_t start_of_adjacent_period_ms;
+ int64_t end_of_adjacent_period_ms;
+ if (IsCurrentTimeAtStartOfEidPeriod(
+ start_of_eid_period_ms, end_of_eid_period_ms, current_time_ms)) {
+ // 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;
+ 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;
+ }
+
+ return base::WrapUnique(new EidPeriodTimestamps{
+ start_of_eid_period_ms, end_of_eid_period_ms,
+ start_of_adjacent_period_ms, end_of_adjacent_period_ms});
+ }
+ }
+
+ PA_LOG(ERROR) << "Could not find valid EID period for seed. "
+ << "seed.start_timestamp_ms: "
+ << current_seed->start_time_millis()
+ << ", seed.end_timestamp_ms: "
+ << current_seed->end_time_millis();
+ NOTREACHED();
+ return nullptr;
+}
+
+std::unique_ptr<BeaconSeed> EidGenerator::GetBeaconSeedForCurrentPeriod(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t current_time_ms) const {
+ 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) {
+ PA_LOG(WARNING) << "Seed has period length which is not an multiple of "
+ << "the rotation length.";
+ continue;
+ }
+
+ if (seed.start_time_millis() <= current_time_ms &&
+ current_time_ms < seed.end_time_millis()) {
+ return base::WrapUnique(new BeaconSeed(seed));
+ }
+ }
+
+ return nullptr;
+}
+
+std::unique_ptr<EidGenerator::EidPeriodTimestamps>
+EidGenerator::GetClosestPeriod(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t current_time_ms) const {
+ int64_t smallest_diff_so_far_ms = kMaxPositiveInt64TValue;
+ int64_t start_of_period_timestamp_ms = kMaxPositiveInt64TValue;
+ int64_t end_of_period_timestamp_ms = 0;
+
+ 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) {
+ PA_LOG(WARNING) << "Seed has period length which is not an multiple of "
+ << "the rotation length.";
+ continue;
+ }
+
+ DCHECK(seed.start_time_millis() > current_time_ms ||
+ current_time_ms >= seed.end_time_millis());
+
+ if (seed.start_time_millis() > current_time_ms) {
+ int64_t diff = seed.start_time_millis() - current_time_ms;
+ if (diff < smallest_diff_so_far_ms) {
+ 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;
+ }
+ } else if (seed.end_time_millis() <= current_time_ms) {
+ int64_t diff = current_time_ms - seed.end_time_millis();
+ if (diff < smallest_diff_so_far_ms) {
+ 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;
+ }
+ }
+ }
+
+ if (smallest_diff_so_far_ms < kMaxPositiveInt64TValue &&
+ smallest_diff_so_far_ms > kNumMsInEidPeriod) {
+ return nullptr;
+ }
+
+ return base::WrapUnique(new EidPeriodTimestamps{
+ kNoTimestamp, // current_period_start_timestamp_ms is unused.
+ kNoTimestamp, // current_period_end_timestamp_ms is unused.
+ start_of_period_timestamp_ms, end_of_period_timestamp_ms});
+}
+
+bool EidGenerator::IsCurrentTimeAtStartOfEidPeriod(
+ const int64_t start_of_period_timestamp_ms,
+ const int64_t end_of_period_timestamp_ms,
+ const int64_t current_timestamp_ms) {
+ DCHECK(start_of_period_timestamp_ms <= current_timestamp_ms);
+ DCHECK(current_timestamp_ms < end_of_period_timestamp_ms);
+
+ return current_timestamp_ms <
+ start_of_period_timestamp_ms + kNumMsInBeginningOfEidPeriod;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/eid_generator.h b/chromium/components/cryptauth/eid_generator.h
new file mode 100644
index 00000000000..3fd706186f4
--- /dev/null
+++ b/chromium/components/cryptauth/eid_generator.h
@@ -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.
+
+#ifndef COMPONENTS_CRYPTAUTH_BLE_EID_GENERATOR_H_
+#define COMPONENTS_CRYPTAUTH_BLE_EID_GENERATOR_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/time/clock.h"
+
+namespace cryptauth {
+
+class BeaconSeed;
+struct RemoteDevice;
+
+// Generates ephemeral ID (EID) values for BLE communication in ProximityAuth.
+//
+// A peripheral-role device advertises a 4-byte advertisement with two parts: a
+// 2-byte EID which is specific to the central-role device with which it intends
+// to communicate, and a 2-byte EID which is specific to the peripheral-role
+// device.
+//
+// This class uses EID seed values synced from the back-end to generate these
+// EIDs.
+//
+// See go/proximity-auth-ble-advertising.
+class EidGenerator {
+ public:
+ // Stores EID-related data and timestamps at which time this data becomes
+ // active or inactive.
+ struct DataWithTimestamp {
+ DataWithTimestamp(const std::string& data,
+ const int64_t start_timestamp_ms,
+ const int64_t end_timestamp_ms);
+ DataWithTimestamp(const DataWithTimestamp& other);
+
+ bool ContainsTime(const int64_t timestamp_ms) const;
+ std::string DataInHex() const;
+
+ const std::string data;
+ const int64_t start_timestamp_ms;
+ const int64_t end_timestamp_ms;
+ };
+
+ // Data for both a current and adjacent EID. The current EID *must* be
+ // supplied, but adjacent data may be null. Each EID consists of a 2-byte EID
+ // value paired with the timestamp at which time this value becomes active or
+ // inactive.
+ struct EidData {
+ enum AdjacentDataType { NONE, PAST, FUTURE };
+
+ EidData(const DataWithTimestamp current_data,
+ std::unique_ptr<DataWithTimestamp> adjacent_data);
+ ~EidData();
+
+ AdjacentDataType GetAdjacentDataType() const;
+ std::string DataInHex() const;
+
+ const DataWithTimestamp current_data;
+ const std::unique_ptr<DataWithTimestamp> adjacent_data;
+ };
+
+ // The flag used to denote that a Bluetooth 4.0 device has sent an
+ // advertisement. This flag indicates to the recipient that the sender cannot
+ // act as both a central- and peripheral-role device simultaneously, so the
+ // recipient should advertise back instead of initializing a connection.
+ static const int8_t kBluetooth4Flag;
+
+ static EidGenerator* GetInstance();
+ virtual ~EidGenerator();
+
+ // Generates EID data for the given EID seeds to be used as a background scan
+ // filter. In the normal case, two DataWithTimestamp values are returned, one
+ // for each EID seed rotation period. If data has not been synced from the
+ // backend recently and EID seeds are unavailable, nullptr is returned.
+ virtual std::unique_ptr<EidData> GenerateBackgroundScanFilter(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const;
+
+ // Generates advertisement data for the given EID seeds. If data has not been
+ // synced from the back-end recently and EID seeds are unavailable, nullptr is
+ // returned.
+ virtual std::unique_ptr<DataWithTimestamp> GenerateAdvertisement(
+ const std::string& advertising_device_public_key,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const;
+
+ // Generates all possible advertisements that could be created by a device
+ // given that device's public key and the beacon seeds of the device which is
+ // intended to scan for the advertisement.
+ virtual std::vector<std::string> GeneratePossibleAdvertisements(
+ const std::string& advertising_device_public_key,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const;
+
+ // Given a list of RemoteDevices, identifies the device which could have
+ // produced the supplied advertisement service data.
+ virtual RemoteDevice const* IdentifyRemoteDeviceByAdvertisement(
+ const std::string& advertisement_service_data,
+ const std::vector<RemoteDevice>& device_list,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const;
+
+ protected:
+ EidGenerator();
+
+ private:
+ friend struct base::DefaultSingletonTraits<EidGenerator>;
+
+ struct EidPeriodTimestamps {
+ int64_t current_period_start_timestamp_ms;
+ int64_t current_period_end_timestamp_ms;
+ int64_t adjacent_period_start_timestamp_ms;
+ int64_t adjacent_period_end_timestamp_ms;
+ };
+
+ class EidComputationHelper {
+ public:
+ virtual std::string GenerateEidDataForDevice(
+ const std::string& eid_seed,
+ const int64_t start_of_period_timestamp_ms,
+ const std::string* extra_entropy) = 0;
+ };
+
+ class EidComputationHelperImpl : public EidComputationHelper {
+ public:
+ std::string GenerateEidDataForDevice(
+ const std::string& eid_seed,
+ const int64_t start_of_period_timestamp_ms,
+ const std::string* extra_entropy) override;
+ };
+
+ static const int64_t kNumMsInEidPeriod;
+ static const int64_t kNumMsInBeginningOfEidPeriod;
+ static const int32_t kNumBytesInEidValue;
+
+ EidGenerator(std::unique_ptr<EidComputationHelper> computation_helper,
+ std::unique_ptr<base::Clock> clock);
+
+ std::unique_ptr<DataWithTimestamp> GenerateAdvertisement(
+ const std::string& advertising_device_public_key,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t start_of_period_timestamp_ms,
+ const int64_t end_of_period_timestamp_ms) const;
+
+ std::unique_ptr<DataWithTimestamp> GenerateEidDataWithTimestamp(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t start_of_period_timestamp_ms,
+ const int64_t end_of_period_timestamp_ms) const;
+
+ std::unique_ptr<DataWithTimestamp> GenerateEidDataWithTimestamp(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t start_of_period_timestamp_ms,
+ const int64_t end_of_period_timestamp_ms,
+ const std::string* extra_entropy) const;
+
+ std::unique_ptr<std::string> GetEidSeedForPeriod(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t start_of_period_timestamp_ms) const;
+
+ std::unique_ptr<EidPeriodTimestamps> GetEidPeriodTimestamps(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const;
+
+ std::unique_ptr<EidPeriodTimestamps> GetEidPeriodTimestamps(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const bool allow_non_current_periods) const;
+
+ std::unique_ptr<BeaconSeed> GetBeaconSeedForCurrentPeriod(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t current_time_ms) const;
+
+ std::unique_ptr<EidPeriodTimestamps> GetClosestPeriod(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+ const int64_t current_time_ms) const;
+
+ static bool IsCurrentTimeAtStartOfEidPeriod(
+ const int64_t start_of_period_timestamp_ms,
+ const int64_t end_of_period_timestamp_ms,
+ const int64_t current_timestamp_ms);
+
+ std::unique_ptr<base::Clock> clock_;
+
+ std::unique_ptr<EidComputationHelper> eid_computation_helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(EidGenerator);
+
+ friend class CryptAuthEidGeneratorTest;
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ TestGenerateEidDataForDevice_UsingRealEidComputationHelper);
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_CurrentAndPastAdjacentPeriods);
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ testGeneratePossibleAdvertisements_CurrentAndFutureAdjacentPeriods);
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_OnlyCurrentPeriod);
+ FRIEND_TEST_ALL_PREFIXES(CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_OnlyFuturePeriod);
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInFuture);
+ FRIEND_TEST_ALL_PREFIXES(CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_OnlyPastPeriod);
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInPast);
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_NoAdvertisements_EmptySeeds);
+ FRIEND_TEST_ALL_PREFIXES(CryptAuthEidGeneratorTest,
+ TestEidComputationHelperImpl_ProducesTwoByteValue);
+ FRIEND_TEST_ALL_PREFIXES(CryptAuthEidGeneratorTest,
+ TestEidComputationHelperImpl_Deterministic);
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ TestEidComputationHelperImpl_ChangingSeedChangesOutput);
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ TestEidComputationHelperImpl_ChangingTimestampChangesOutput);
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ TestEidComputationHelperImpl_ChangingExtraEntropyChangesOutput);
+ FRIEND_TEST_ALL_PREFIXES(
+ CryptAuthEidGeneratorTest,
+ TestEidComputationHelper_ChangingTimestampWithLongExtraEntropy);
+ FRIEND_TEST_ALL_PREFIXES(CryptAuthEidGeneratorTest,
+ TestEidComputationHelper_EnsureTestVectorsPass);
+};
+}
+
+#endif // COMPONENTS_CRYPTAUTH_BLE_EID_GENERATOR_H_
diff --git a/chromium/components/cryptauth/eid_generator_unittest.cc b/chromium/components/cryptauth/eid_generator_unittest.cc
new file mode 100644
index 00000000000..a0e35593b9b
--- /dev/null
+++ b/chromium/components/cryptauth/eid_generator_unittest.cc
@@ -0,0 +1,770 @@
+// 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/eid_generator.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/test/simple_test_clock.h"
+#include "base/time/time.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/remote_device.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::AtLeast;
+using testing::NiceMock;
+using testing::Return;
+using testing::StrictMock;
+using testing::SaveArg;
+
+namespace cryptauth {
+
+namespace {
+const int64_t kNumMsInEidPeriod =
+ base::TimeDelta::FromHours(8).InMilliseconds();
+const int64_t kNumMsInBeginningOfEidPeriod =
+ base::TimeDelta::FromHours(2).InMilliseconds();
+const int32_t kNumBytesInEidValue = 2;
+const int64_t kNumMsInEidSeedPeriod =
+ base::TimeDelta::FromDays(14).InMilliseconds();
+
+// Midnight on 1/1/2020.
+const int64_t kDefaultCurrentPeriodStart = 1577836800000L;
+// 1:43am on 1/1/2020.
+const int64_t kDefaultCurrentTime = 1577843000000L;
+
+// The Base64 encoded values of these raw data strings are, respectively:
+// "Zmlyc3RTZWVk", "c2Vjb25kU2VlZA==", "dGhpcmRTZWVk","Zm91cnRoU2VlZA==".
+const std::string kFirstSeed = "firstSeed";
+const std::string kSecondSeed = "secondSeed";
+const std::string kThirdSeed = "thirdSeed";
+const std::string kFourthSeed = "fourthSeed";
+
+const std::string kDefaultAdvertisingDevicePublicKey = "publicKey";
+
+static BeaconSeed CreateBeaconSeed(const std::string& data,
+ const int64_t start_timestamp_ms,
+ const int64_t end_timestamp_ms) {
+ BeaconSeed seed;
+ seed.set_data(data);
+ seed.set_start_time_millis(start_timestamp_ms);
+ seed.set_end_time_millis(end_timestamp_ms);
+ return seed;
+}
+
+static std::string GenerateFakeEidData(
+ const std::string& eid_seed,
+ const int64_t start_of_period_timestamp_ms,
+ const std::string* extra_entropy) {
+ std::hash<std::string> string_hash;
+ int64_t seed_hash = string_hash(eid_seed);
+ int64_t extra_hash = extra_entropy ? string_hash(*extra_entropy) : 0;
+ int64_t fake_data_xor = seed_hash ^ start_of_period_timestamp_ms ^ extra_hash;
+
+ std::string fake_data(reinterpret_cast<const char*>(&fake_data_xor),
+ sizeof(fake_data_xor));
+ fake_data.resize(kNumBytesInEidValue);
+
+ return fake_data;
+}
+
+static std::string GenerateFakeAdvertisement(
+ const std::string& scanning_device_eid_seed,
+ const int64_t start_of_period_timestamp_ms,
+ const std::string& advertising_device_public_key) {
+ std::string fake_scanning_eid = GenerateFakeEidData(
+ scanning_device_eid_seed, start_of_period_timestamp_ms, nullptr);
+ std::string fake_advertising_id = GenerateFakeEidData(
+ scanning_device_eid_seed, start_of_period_timestamp_ms,
+ &advertising_device_public_key);
+ std::string fake_advertisement;
+ fake_advertisement.append(fake_scanning_eid);
+ fake_advertisement.append(fake_advertising_id);
+ return fake_advertisement;
+}
+
+static RemoteDevice CreateRemoteDevice(
+ const std::string& public_key) {
+ RemoteDevice remote_device;
+ remote_device.public_key = public_key;
+ return remote_device;
+}
+} // namespace
+
+class CryptAuthEidGeneratorTest : public testing::Test {
+ protected:
+ CryptAuthEidGeneratorTest() {
+ scanning_device_beacon_seeds_.push_back(CreateBeaconSeed(
+ kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+ 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));
+ scanning_device_beacon_seeds_.push_back(CreateBeaconSeed(
+ kFourthSeed, kDefaultCurrentPeriodStart + 2 * kNumMsInEidSeedPeriod,
+ kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod));
+ }
+
+ class TestEidComputationHelper : public EidGenerator::EidComputationHelper {
+ public:
+ TestEidComputationHelper() {}
+
+ std::string GenerateEidDataForDevice(
+ const std::string& eid_seed,
+ const int64_t start_of_period_timestamp_ms,
+ const std::string* extra_entropy) override {
+ return GenerateFakeEidData(eid_seed, start_of_period_timestamp_ms,
+ extra_entropy);
+ }
+ };
+
+ void SetUp() override {
+ test_clock_ = new base::SimpleTestClock();
+ test_computation_helper_ = new TestEidComputationHelper();
+ real_computation_helper_.reset(
+ new EidGenerator::EidComputationHelperImpl());
+
+ SetTestTime(kDefaultCurrentTime);
+
+ eid_generator_.reset(
+ new EidGenerator(base::WrapUnique(test_computation_helper_),
+ base::WrapUnique(test_clock_)));
+ }
+
+ // TODO(khorimoto): Is there an easier way to do this?
+ void SetTestTime(int64_t timestamp_ms) {
+ base::Time time = base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(timestamp_ms);
+ test_clock_->SetNow(time);
+ }
+
+ std::unique_ptr<EidGenerator> eid_generator_;
+ base::SimpleTestClock* test_clock_;
+ TestEidComputationHelper* test_computation_helper_;
+ std::unique_ptr<EidGenerator::EidComputationHelper> real_computation_helper_;
+ std::vector<BeaconSeed> scanning_device_beacon_seeds_;
+};
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGenerateEidDataForDevice_StartOfPeriod_AnotherSeedInPreviousPeriod) {
+ SetTestTime(kDefaultCurrentTime);
+
+ std::unique_ptr<EidGenerator::EidData> data =
+ eid_generator_->GenerateBackgroundScanFilter(
+ scanning_device_beacon_seeds_);
+ ASSERT_TRUE(data);
+ EXPECT_EQ(data->GetAdjacentDataType(),
+ EidGenerator::EidData::AdjacentDataType::PAST);
+
+ EXPECT_EQ(kDefaultCurrentPeriodStart, data->current_data.start_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+ 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,
+ data->adjacent_data->start_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart, data->adjacent_data->end_timestamp_ms);
+ EXPECT_EQ(
+ GenerateFakeEidData(
+ kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidPeriod, nullptr),
+ data->adjacent_data->data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGenerateEidDataForDevice_StartOfPeriod_NoSeedBefore) {
+ SetTestTime(kDefaultCurrentTime - kNumMsInEidSeedPeriod);
+
+ std::unique_ptr<EidGenerator::EidData> data =
+ eid_generator_->GenerateBackgroundScanFilter(
+ scanning_device_beacon_seeds_);
+ ASSERT_TRUE(data);
+ EXPECT_EQ(data->GetAdjacentDataType(),
+ EidGenerator::EidData::AdjacentDataType::NONE);
+
+ EXPECT_EQ(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+ data->current_data.start_timestamp_ms);
+ EXPECT_EQ(
+ kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod + kNumMsInEidPeriod,
+ data->current_data.end_timestamp_ms);
+ EXPECT_EQ(GenerateFakeEidData(
+ kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+ nullptr),
+ data->current_data.data);
+
+ EXPECT_FALSE(data->adjacent_data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGenerateEidDataForDevice_PastStartOfPeriod) {
+ SetTestTime(kDefaultCurrentTime +
+ base::TimeDelta::FromHours(3).InMilliseconds());
+
+ std::unique_ptr<EidGenerator::EidData> data =
+ eid_generator_->GenerateBackgroundScanFilter(
+ scanning_device_beacon_seeds_);
+ ASSERT_TRUE(data);
+ EXPECT_EQ(data->GetAdjacentDataType(),
+ EidGenerator::EidData::AdjacentDataType::FUTURE);
+
+ EXPECT_EQ(kDefaultCurrentPeriodStart, data->current_data.start_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+ 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,
+ data->adjacent_data->start_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart + 2 * kNumMsInEidPeriod,
+ data->adjacent_data->end_timestamp_ms);
+ EXPECT_EQ(
+ GenerateFakeEidData(
+ kSecondSeed, kDefaultCurrentPeriodStart + kNumMsInEidPeriod, nullptr),
+ data->adjacent_data->data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestGenerateEidDataForDevice_EndOfPeriod) {
+ SetTestTime(kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod - 1);
+
+ std::unique_ptr<EidGenerator::EidData> data =
+ eid_generator_->GenerateBackgroundScanFilter(
+ scanning_device_beacon_seeds_);
+ ASSERT_TRUE(data);
+ EXPECT_EQ(data->GetAdjacentDataType(),
+ EidGenerator::EidData::AdjacentDataType::FUTURE);
+
+ EXPECT_EQ(
+ kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod - kNumMsInEidPeriod,
+ data->current_data.start_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
+ data->current_data.end_timestamp_ms);
+ EXPECT_EQ(GenerateFakeEidData(kSecondSeed,
+ kDefaultCurrentPeriodStart +
+ kNumMsInEidSeedPeriod - kNumMsInEidPeriod,
+ nullptr),
+ data->current_data.data);
+
+ ASSERT_TRUE(data->adjacent_data);
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
+ data->adjacent_data->start_timestamp_ms);
+ EXPECT_EQ(
+ kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod + kNumMsInEidPeriod,
+ data->adjacent_data->end_timestamp_ms);
+ EXPECT_EQ(GenerateFakeEidData(
+ kThirdSeed, kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
+ nullptr),
+ data->adjacent_data->data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGenerateEidDataForDevice_EndOfPeriod_NoSeedAfter) {
+ SetTestTime(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod - 1);
+
+ std::unique_ptr<EidGenerator::EidData> data =
+ eid_generator_->GenerateBackgroundScanFilter(
+ scanning_device_beacon_seeds_);
+ ASSERT_TRUE(data);
+ EXPECT_EQ(data->GetAdjacentDataType(),
+ EidGenerator::EidData::AdjacentDataType::NONE);
+
+ EXPECT_EQ(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod -
+ kNumMsInEidPeriod,
+ data->current_data.start_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod,
+ data->current_data.end_timestamp_ms);
+ EXPECT_EQ(GenerateFakeEidData(kFourthSeed, kDefaultCurrentPeriodStart +
+ 3 * kNumMsInEidSeedPeriod -
+ kNumMsInEidPeriod,
+ nullptr),
+ data->current_data.data);
+
+ EXPECT_FALSE(data->adjacent_data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGenerateEidDataForDevice_NoCurrentPeriodSeed) {
+ SetTestTime(kDefaultCurrentPeriodStart + 4 * kNumMsInEidSeedPeriod - 1);
+
+ std::unique_ptr<EidGenerator::EidData> data =
+ eid_generator_->GenerateBackgroundScanFilter(
+ scanning_device_beacon_seeds_);
+ EXPECT_FALSE(data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest, testGenerateEidDataForDevice_EmptySeeds) {
+ SetTestTime(kDefaultCurrentTime);
+
+ std::vector<BeaconSeed> empty;
+ std::unique_ptr<EidGenerator::EidData> data =
+ eid_generator_->GenerateBackgroundScanFilter(empty);
+ EXPECT_FALSE(data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGenerateEidDataForDevice_InvalidSeed_PeriodNotMultipleOf8Hours) {
+ SetTestTime(kDefaultCurrentTime);
+
+ // Seed has a period of 1ms, but it should have a period of 8 hours.
+ std::vector<BeaconSeed> invalid_seed_vector = {CreateBeaconSeed(
+ kFirstSeed, kDefaultCurrentPeriodStart, kDefaultCurrentPeriodStart + 1)};
+ std::unique_ptr<EidGenerator::EidData> data =
+ eid_generator_->GenerateBackgroundScanFilter(invalid_seed_vector);
+ EXPECT_FALSE(data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGenerateEidDataForDevice_UsingRealEidComputationHelper) {
+ test_clock_ = new base::SimpleTestClock();
+ SetTestTime(kDefaultCurrentTime);
+
+ // Use real EidComputationHelper implementation instead of test version.
+ eid_generator_.reset(new EidGenerator(std::move(real_computation_helper_),
+ base::WrapUnique(test_clock_)));
+
+ std::unique_ptr<EidGenerator::EidData> data =
+ eid_generator_->GenerateBackgroundScanFilter(
+ scanning_device_beacon_seeds_);
+ ASSERT_TRUE(data);
+ EXPECT_EQ(data->GetAdjacentDataType(),
+ EidGenerator::EidData::AdjacentDataType::PAST);
+
+ EXPECT_EQ(kDefaultCurrentPeriodStart, data->current_data.start_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+ data->current_data.end_timestamp_ms);
+ // Since this uses the real EidComputationHelper, 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,
+ data->adjacent_data->start_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart, data->adjacent_data->end_timestamp_ms);
+ // Since this uses the real EidComputationHelper, just make sure the data
+ // exists and has the proper length.
+ EXPECT_EQ((size_t)kNumBytesInEidValue, data->adjacent_data->data.length());
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestGenerateAdvertisementData) {
+ SetTestTime(kDefaultCurrentTime);
+
+ std::unique_ptr<EidGenerator::DataWithTimestamp> data =
+ eid_generator_->GenerateAdvertisement(kDefaultAdvertisingDevicePublicKey,
+ scanning_device_beacon_seeds_);
+ ASSERT_TRUE(data);
+
+ EXPECT_EQ(kDefaultCurrentPeriodStart, data->start_timestamp_ms);
+ EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+ data->end_timestamp_ms);
+ EXPECT_EQ(GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultAdvertisingDevicePublicKey),
+ data->data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGenerateAdvertisementData_NoSeedForPeriod) {
+ SetTestTime(kDefaultCurrentTime + 4 * kNumMsInEidSeedPeriod);
+
+ std::unique_ptr<EidGenerator::DataWithTimestamp> data =
+ eid_generator_->GenerateAdvertisement(kDefaultAdvertisingDevicePublicKey,
+ scanning_device_beacon_seeds_);
+ EXPECT_FALSE(data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestGenerateAdvertisementData_EmptySeeds) {
+ SetTestTime(kDefaultCurrentTime + 4 * kNumMsInEidSeedPeriod);
+
+ std::vector<BeaconSeed> empty;
+ std::unique_ptr<EidGenerator::DataWithTimestamp> data =
+ eid_generator_->GenerateAdvertisement(kDefaultAdvertisingDevicePublicKey,
+ empty);
+ EXPECT_FALSE(data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_CurrentAndPastAdjacentPeriods) {
+ SetTestTime(kDefaultCurrentPeriodStart);
+
+ std::vector<std::string> possible_advertisements =
+ eid_generator_->GeneratePossibleAdvertisements(
+ kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+
+ EXPECT_EQ((size_t)2, possible_advertisements.size());
+ EXPECT_EQ(GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultAdvertisingDevicePublicKey),
+ possible_advertisements[0]);
+ EXPECT_EQ(GenerateFakeAdvertisement(
+ kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidPeriod,
+ kDefaultAdvertisingDevicePublicKey),
+ possible_advertisements[1]);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ testGeneratePossibleAdvertisements_CurrentAndFutureAdjacentPeriods) {
+ SetTestTime(kDefaultCurrentPeriodStart +
+ base::TimeDelta::FromHours(3).InMilliseconds());
+
+ std::vector<std::string> possible_advertisements =
+ eid_generator_->GeneratePossibleAdvertisements(
+ kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+
+ EXPECT_EQ((size_t)2, possible_advertisements.size());
+ EXPECT_EQ(GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultAdvertisingDevicePublicKey),
+ possible_advertisements[0]);
+ EXPECT_EQ(GenerateFakeAdvertisement(
+ kSecondSeed, kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+ kDefaultAdvertisingDevicePublicKey),
+ possible_advertisements[1]);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_OnlyCurrentPeriod) {
+ SetTestTime(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod);
+
+ std::vector<std::string> possible_advertisements =
+ eid_generator_->GeneratePossibleAdvertisements(
+ kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+
+ EXPECT_EQ((size_t)1, possible_advertisements.size());
+ EXPECT_EQ(GenerateFakeAdvertisement(
+ kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+ kDefaultAdvertisingDevicePublicKey),
+ possible_advertisements[0]);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_OnlyFuturePeriod) {
+ SetTestTime(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod -
+ kNumMsInEidPeriod);
+
+ std::vector<std::string> possible_advertisements =
+ eid_generator_->GeneratePossibleAdvertisements(
+ kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+
+ EXPECT_EQ((size_t)1, possible_advertisements.size());
+ EXPECT_EQ(GenerateFakeAdvertisement(
+ kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+ kDefaultAdvertisingDevicePublicKey),
+ possible_advertisements[0]);
+}
+
+TEST_F(
+ CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInFuture) {
+ SetTestTime(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod -
+ kNumMsInEidPeriod - 1);
+
+ std::vector<std::string> possible_advertisements =
+ eid_generator_->GeneratePossibleAdvertisements(
+ kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+ EXPECT_TRUE(possible_advertisements.empty());
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_OnlyPastPeriod) {
+ SetTestTime(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod +
+ kNumMsInEidPeriod);
+
+ std::vector<std::string> possible_advertisements =
+ eid_generator_->GeneratePossibleAdvertisements(
+ kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+
+ EXPECT_EQ((size_t)1, possible_advertisements.size());
+ EXPECT_EQ(GenerateFakeAdvertisement(
+ kFourthSeed, kDefaultCurrentPeriodStart +
+ 3 * kNumMsInEidSeedPeriod - kNumMsInEidPeriod,
+ kDefaultAdvertisingDevicePublicKey),
+ possible_advertisements[0]);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInPast) {
+ SetTestTime(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod +
+ kNumMsInEidPeriod + 1);
+
+ std::vector<std::string> possible_advertisements =
+ eid_generator_->GeneratePossibleAdvertisements(
+ kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+ EXPECT_TRUE(possible_advertisements.empty());
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestGeneratePossibleAdvertisements_NoAdvertisements_EmptySeeds) {
+ SetTestTime(kDefaultCurrentPeriodStart);
+
+ std::vector<BeaconSeed> empty;
+ std::vector<std::string> possible_advertisements =
+ eid_generator_->GeneratePossibleAdvertisements(
+ kDefaultAdvertisingDevicePublicKey, empty);
+ EXPECT_TRUE(possible_advertisements.empty());
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestIdentifyRemoteDevice_NoDevices) {
+ SetTestTime(kDefaultCurrentPeriodStart);
+
+ std::string service_data =
+ GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultAdvertisingDevicePublicKey);
+
+ std::vector<RemoteDevice> device_list;
+ const RemoteDevice* identified_device =
+ eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+ service_data, device_list, scanning_device_beacon_seeds_);
+ EXPECT_FALSE(identified_device);
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestIdentifyRemoteDevice_OneDevice_Success) {
+ SetTestTime(kDefaultCurrentPeriodStart);
+
+ std::string service_data =
+ GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultAdvertisingDevicePublicKey);
+
+ RemoteDevice correct_device =
+ CreateRemoteDevice(kDefaultAdvertisingDevicePublicKey);
+ std::vector<RemoteDevice> device_list = {correct_device};
+ const RemoteDevice* identified_device =
+ eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+ service_data, device_list, scanning_device_beacon_seeds_);
+ EXPECT_EQ(correct_device.public_key, identified_device->public_key);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestIdentifyRemoteDevice_OneDevice_ServiceDataWithOneByteFlag_Success) {
+ SetTestTime(kDefaultCurrentPeriodStart);
+
+ std::string service_data =
+ GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultAdvertisingDevicePublicKey);
+
+ // Identifying device should still succeed if there is an extra "flag" byte
+ // after the first 4 bytes.
+ service_data.append(1, static_cast<char>(EidGenerator::kBluetooth4Flag));
+
+ RemoteDevice correct_device =
+ CreateRemoteDevice(kDefaultAdvertisingDevicePublicKey);
+ std::vector<RemoteDevice> device_list = {correct_device};
+ const RemoteDevice* identified_device =
+ eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+ service_data, device_list, scanning_device_beacon_seeds_);
+ EXPECT_EQ(correct_device.public_key, identified_device->public_key);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestIdentifyRemoteDevice_OneDevice_ServiceDataWithLongerFlag_Success) {
+ SetTestTime(kDefaultCurrentPeriodStart);
+
+ std::string service_data =
+ GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultAdvertisingDevicePublicKey);
+
+ // Identifying device should still succeed if there are extra "flag" bytes
+ // after the first 4 bytes.
+ service_data.append("extra_flag_bytes");
+
+ RemoteDevice correct_device =
+ CreateRemoteDevice(kDefaultAdvertisingDevicePublicKey);
+ std::vector<RemoteDevice> device_list = {correct_device};
+ const RemoteDevice* identified_device =
+ eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+ service_data, device_list, scanning_device_beacon_seeds_);
+ EXPECT_EQ(correct_device.public_key, identified_device->public_key);
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestIdentifyRemoteDevice_OneDevice_Failure) {
+ SetTestTime(kDefaultCurrentPeriodStart);
+
+ std::string service_data =
+ GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultAdvertisingDevicePublicKey);
+
+ RemoteDevice wrong_device =
+ CreateRemoteDevice("wrongPublicKey");
+ std::vector<RemoteDevice> device_list = {wrong_device};
+ const RemoteDevice* identified_device =
+ eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+ service_data, device_list, scanning_device_beacon_seeds_);
+ EXPECT_FALSE(identified_device);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestIdentifyRemoteDevice_MultipleDevices_Success) {
+ SetTestTime(kDefaultCurrentPeriodStart);
+
+ std::string service_data =
+ GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultAdvertisingDevicePublicKey);
+
+ RemoteDevice correct_device =
+ CreateRemoteDevice(kDefaultAdvertisingDevicePublicKey);
+ RemoteDevice wrong_device =
+ CreateRemoteDevice("wrongPublicKey");
+ std::vector<RemoteDevice> device_list = {correct_device,
+ wrong_device};
+ const RemoteDevice* identified_device =
+ eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+ service_data, device_list, scanning_device_beacon_seeds_);
+ EXPECT_EQ(correct_device.public_key, identified_device->public_key);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestIdentifyRemoteDevice_MultipleDevices_Failure) {
+ SetTestTime(kDefaultCurrentPeriodStart);
+
+ std::string service_data =
+ GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+ kDefaultAdvertisingDevicePublicKey);
+
+ RemoteDevice wrong_device =
+ CreateRemoteDevice("wrongPublicKey");
+ std::vector<RemoteDevice> device_list = {wrong_device,
+ wrong_device};
+ const RemoteDevice* identified_device =
+ eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+ service_data, device_list, scanning_device_beacon_seeds_);
+ EXPECT_FALSE(identified_device);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestEidComputationHelperImpl_ProducesTwoByteValue) {
+ EidGenerator::EidComputationHelperImpl helper;
+ std::string eid = helper.GenerateEidDataForDevice(
+ "eidSeed", kDefaultCurrentPeriodStart, nullptr);
+ EXPECT_EQ((size_t)kNumBytesInEidValue, eid.length());
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestEidComputationHelperImpl_Deterministic) {
+ EidGenerator::EidComputationHelperImpl helper;
+ std::string eid1 = helper.GenerateEidDataForDevice(
+ "eidSeed", kDefaultCurrentPeriodStart, nullptr);
+ std::string eid2 = helper.GenerateEidDataForDevice(
+ "eidSeed", kDefaultCurrentPeriodStart, nullptr);
+ EXPECT_EQ((size_t)kNumBytesInEidValue, eid1.length());
+ EXPECT_EQ((size_t)kNumBytesInEidValue, eid2.length());
+ EXPECT_EQ(eid1, eid2);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestEidComputationHelperImpl_ChangingSeedChangesOutput) {
+ EidGenerator::EidComputationHelperImpl helper;
+ std::string eid1 = helper.GenerateEidDataForDevice(
+ "eidSeed1", kDefaultCurrentPeriodStart, nullptr);
+ std::string eid2 = helper.GenerateEidDataForDevice(
+ "eidSeed2", kDefaultCurrentPeriodStart, nullptr);
+ EXPECT_EQ((size_t)kNumBytesInEidValue, eid1.length());
+ EXPECT_EQ((size_t)kNumBytesInEidValue, eid2.length());
+ EXPECT_NE(eid1, eid2);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestEidComputationHelperImpl_ChangingTimestampChangesOutput) {
+ EidGenerator::EidComputationHelperImpl helper;
+ std::string eid1 = helper.GenerateEidDataForDevice(
+ "eidSeed", kDefaultCurrentPeriodStart, nullptr);
+ std::string eid2 = helper.GenerateEidDataForDevice(
+ "eidSeed", kDefaultCurrentPeriodStart + 1, nullptr);
+ EXPECT_EQ((size_t)kNumBytesInEidValue, eid1.length());
+ EXPECT_EQ((size_t)kNumBytesInEidValue, eid2.length());
+ EXPECT_NE(eid1, eid2);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestEidComputationHelperImpl_ChangingExtraEntropyChangesOutput) {
+ EidGenerator::EidComputationHelperImpl helper;
+ std::string eid1 = helper.GenerateEidDataForDevice(
+ "eidSeed", kDefaultCurrentPeriodStart, nullptr);
+ std::string extra_entropy = "extraEntropy";
+ std::string eid2 = helper.GenerateEidDataForDevice(
+ "eidSeed", kDefaultCurrentPeriodStart, &extra_entropy);
+ EXPECT_EQ((size_t)kNumBytesInEidValue, eid1.length());
+ EXPECT_EQ((size_t)kNumBytesInEidValue, eid2.length());
+ EXPECT_NE(eid1, eid2);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestEidComputationHelper_ChangingTimestampWithLongExtraEntropy) {
+ EidGenerator::EidComputationHelperImpl helper;
+ std::string long_extra_entropy =
+ "reallyReallyReallyReallyReallyReallyReallyLongExtraEntropy";
+ std::string eid1 = helper.GenerateEidDataForDevice(
+ "eidSeed", kDefaultCurrentPeriodStart, &long_extra_entropy);
+ std::string extra_entropy = "extraEntropy";
+ std::string eid2 = helper.GenerateEidDataForDevice(
+ "eidSeed", kDefaultCurrentPeriodStart + 1, &long_extra_entropy);
+ EXPECT_NE(eid1, eid2);
+}
+
+// Tests that "known test vectors" are correct. This ensures that the same test
+// vectors produce the same outputs on other platforms.
+TEST_F(CryptAuthEidGeneratorTest,
+ TestEidComputationHelper_EnsureTestVectorsPass) {
+ const int8_t test_eid_seed1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
+ const int64_t test_timestamp1 = 2468101214L;
+ const int8_t test_entropy1[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33};
+
+ std::string test_eid_seed1_str(reinterpret_cast<const char*>(test_eid_seed1),
+ sizeof(test_eid_seed1));
+ std::string test_entropy1_str(reinterpret_cast<const char*>(test_entropy1),
+ sizeof(test_entropy1));
+
+ const int8_t test_eid_seed2[] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34};
+ const int64_t test_timestamp2 = 51015202530L;
+ const int8_t test_entropy2[] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35};
+
+ std::string test_eid_seed2_str(reinterpret_cast<const char*>(test_eid_seed2),
+ sizeof(test_eid_seed2));
+ std::string test_entropy2_str(reinterpret_cast<const char*>(test_entropy2),
+ sizeof(test_entropy2));
+
+ EidGenerator::EidComputationHelperImpl helper;
+
+ std::string test_eid_without_entropy1 = helper.GenerateEidDataForDevice(
+ test_eid_seed1_str, test_timestamp1, nullptr);
+ EXPECT_EQ("\x7d\x2c", test_eid_without_entropy1);
+
+ std::string test_eid_with_entropy1 = helper.GenerateEidDataForDevice(
+ test_eid_seed1_str, test_timestamp1, &test_entropy1_str);
+ EXPECT_EQ("\xdc\xf3", test_eid_with_entropy1);
+
+ std::string test_eid_without_entropy2 = helper.GenerateEidDataForDevice(
+ test_eid_seed2_str, test_timestamp2, nullptr);
+ EXPECT_EQ("\x02\xdd", test_eid_without_entropy2);
+
+ std::string test_eid_with_entropy2 = helper.GenerateEidDataForDevice(
+ test_eid_seed2_str, test_timestamp2, &test_entropy2_str);
+ EXPECT_EQ("\xee\xcc", test_eid_with_entropy2);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+ TestDataWithTimestamp_ContainsTime) {
+ EidGenerator::DataWithTimestamp data_with_timestamp(
+ "data", /* start */ 1000L, /* end */ 2000L);
+ EXPECT_FALSE(data_with_timestamp.ContainsTime(999L));
+ EXPECT_TRUE(data_with_timestamp.ContainsTime(1000L));
+ EXPECT_TRUE(data_with_timestamp.ContainsTime(1500L));
+ EXPECT_TRUE(data_with_timestamp.ContainsTime(1999L));
+ EXPECT_FALSE(data_with_timestamp.ContainsTime(2000L));
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/fake_connection.cc b/chromium/components/cryptauth/fake_connection.cc
new file mode 100644
index 00000000000..ce11c643bc8
--- /dev/null
+++ b/chromium/components/cryptauth/fake_connection.cc
@@ -0,0 +1,61 @@
+// 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/fake_connection.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/wire_message.h"
+
+namespace cryptauth {
+
+namespace {
+const char kFakeFeatureName[] = "fakeFeature";
+} // namespace
+
+FakeConnection::FakeConnection(const RemoteDevice& remote_device)
+ : Connection(remote_device) {
+ Connect();
+}
+
+FakeConnection::~FakeConnection() {
+ Disconnect();
+}
+
+void FakeConnection::Connect() {
+ SetStatus(CONNECTED);
+}
+
+void FakeConnection::Disconnect() {
+ SetStatus(DISCONNECTED);
+}
+
+void FakeConnection::FinishSendingMessageWithSuccess(bool success) {
+ CHECK(current_message_);
+ // Capture a copy of the message, as OnDidSendMessage() might reentrantly
+ // call SendMessage().
+ std::unique_ptr<WireMessage> sent_message = std::move(current_message_);
+ OnDidSendMessage(*sent_message, success);
+}
+
+void FakeConnection::ReceiveMessageWithPayload(const std::string& payload) {
+ pending_payload_ = payload;
+ OnBytesReceived(std::string());
+ pending_payload_.clear();
+}
+
+void FakeConnection::SendMessageImpl(std::unique_ptr<WireMessage> message) {
+ CHECK(!current_message_);
+ current_message_ = std::move(message);
+}
+
+std::unique_ptr<WireMessage> FakeConnection::DeserializeWireMessage(
+ bool* is_incomplete_message) {
+ *is_incomplete_message = false;
+ return base::MakeUnique<WireMessage>(
+ pending_payload_, std::string(kFakeFeatureName));
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/fake_connection.h b/chromium/components/cryptauth/fake_connection.h
new file mode 100644
index 00000000000..9cb804fff5b
--- /dev/null
+++ b/chromium/components/cryptauth/fake_connection.h
@@ -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.
+
+#ifndef COMPONENTS_CRYPTAUTH_FAKE_CONNECTION_H_
+#define COMPONENTS_CRYPTAUTH_FAKE_CONNECTION_H_
+
+#include "base/macros.h"
+#include "components/cryptauth/connection.h"
+
+namespace cryptauth {
+
+// A fake implementation of Connection to use in tests.
+class FakeConnection : public Connection {
+ public:
+ FakeConnection(const RemoteDevice& remote_device);
+ ~FakeConnection() override;
+
+ // Connection:
+ void Connect() override;
+ void Disconnect() override;
+
+ // Completes the current send operation with success |success|.
+ void FinishSendingMessageWithSuccess(bool success);
+
+ // Simulates receiving a wire message with the given |payload|, bypassing the
+ // container WireMessage format.
+ void ReceiveMessageWithPayload(const std::string& payload);
+
+ // Returns the current message in progress of being sent.
+ WireMessage* current_message() { return current_message_.get(); }
+
+ using Connection::SetStatus;
+
+ private:
+ // Connection:
+ void SendMessageImpl(std::unique_ptr<WireMessage> message) override;
+ std::unique_ptr<WireMessage> DeserializeWireMessage(
+ bool* is_incomplete_message) override;
+
+ // The message currently being sent. Only set between a call to
+ // SendMessageImpl() and FinishSendingMessageWithSuccess().
+ std::unique_ptr<WireMessage> current_message_;
+
+ // The payload that should be returned when DeserializeWireMessage() is
+ // called.
+ std::string pending_payload_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeConnection);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_FAKE_CONNECTION_H_
diff --git a/chromium/components/cryptauth/fake_cryptauth_gcm_manager.cc b/chromium/components/cryptauth/fake_cryptauth_gcm_manager.cc
new file mode 100644
index 00000000000..72acb8796bb
--- /dev/null
+++ b/chromium/components/cryptauth/fake_cryptauth_gcm_manager.cc
@@ -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.
+
+#include "components/cryptauth/fake_cryptauth_gcm_manager.h"
+
+namespace cryptauth {
+
+FakeCryptAuthGCMManager::FakeCryptAuthGCMManager(
+ const std::string& registration_id)
+ : registration_in_progress_(false), registration_id_(registration_id) {}
+
+FakeCryptAuthGCMManager::~FakeCryptAuthGCMManager() {}
+
+void FakeCryptAuthGCMManager::StartListening() {}
+
+void FakeCryptAuthGCMManager::RegisterWithGCM() {
+ registration_in_progress_ = true;
+}
+
+std::string FakeCryptAuthGCMManager::GetRegistrationId() {
+ return registration_id_;
+}
+
+void FakeCryptAuthGCMManager::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void FakeCryptAuthGCMManager::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void FakeCryptAuthGCMManager::CompleteRegistration(
+ const std::string& registration_id) {
+ DCHECK(registration_in_progress_);
+ registration_in_progress_ = false;
+ registration_id_ = registration_id;
+ bool success = !registration_id_.empty();
+ for (auto& observer : observers_)
+ observer.OnGCMRegistrationResult(success);
+}
+
+void FakeCryptAuthGCMManager::PushReenrollMessage() {
+ for (auto& observer : observers_)
+ observer.OnReenrollMessage();
+}
+
+void FakeCryptAuthGCMManager::PushResyncMessage() {
+ for (auto& observer : observers_)
+ observer.OnResyncMessage();
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/fake_cryptauth_gcm_manager.h b/chromium/components/cryptauth/fake_cryptauth_gcm_manager.h
new file mode 100644
index 00000000000..40607afefb6
--- /dev/null
+++ b/chromium/components/cryptauth/fake_cryptauth_gcm_manager.h
@@ -0,0 +1,64 @@
+// 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_FAKE_CRYPTAUTH_GCM_MANAGER_H_
+#define COMPONENTS_CRYPTAUTH_FAKE_CRYPTAUTH_GCM_MANAGER_H_
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/cryptauth/cryptauth_gcm_manager.h"
+
+namespace cryptauth {
+
+// CryptAuthGCMManager implementation for testing purposes.
+class FakeCryptAuthGCMManager : public CryptAuthGCMManager {
+ public:
+ // Creates the instance:
+ // |registration_id|: The GCM registration id from a previous successful
+ // enrollment. Pass in an empty |registration_id| to simulate never having
+ // registered successfully.
+ explicit FakeCryptAuthGCMManager(const std::string& registration_id);
+
+ ~FakeCryptAuthGCMManager() override;
+
+ // CryptAuthGCMManager:
+ void StartListening() override;
+ void RegisterWithGCM() override;
+ std::string GetRegistrationId() override;
+ void AddObserver(Observer* observer) override;
+ void RemoveObserver(Observer* observer) override;
+
+ // Simulates completing a GCM registration with the resulting
+ // |registration_id|. Passing an empty |registration_id| will simulate a
+ // registration failure.
+ // A registration attempt must currently be in progress.
+ void CompleteRegistration(const std::string& registration_id);
+
+ // Simulates receiving a re-enroll push message from GCM.
+ void PushReenrollMessage();
+
+ // Simulates receiving a re-sync push message from GCM.
+ void PushResyncMessage();
+
+ bool registration_in_progress() { return registration_in_progress_; }
+
+ void set_registration_id(const std::string& registration_id) {
+ registration_id_ = registration_id;
+ }
+
+ private:
+ // Whether a registration attempt is currently in progress.
+ bool registration_in_progress_;
+
+ // The registration id obtained from the last successful registration.
+ std::string registration_id_;
+
+ base::ObserverList<Observer> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeCryptAuthGCMManager);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_FAKE_CRYPTAUTH_GCM_MANAGER_H_
diff --git a/chromium/components/cryptauth/fake_secure_message_delegate.cc b/chromium/components/cryptauth/fake_secure_message_delegate.cc
new file mode 100644
index 00000000000..8c7024dd531
--- /dev/null
+++ b/chromium/components/cryptauth/fake_secure_message_delegate.cc
@@ -0,0 +1,200 @@
+// 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/fake_secure_message_delegate.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "base/callback.h"
+#include "base/md5.h"
+#include "base/strings/string_util.h"
+
+namespace cryptauth {
+
+namespace {
+
+const char kKeyPrefix[] = "fake_key_";
+const char kPrivateKeyPrefix[] = "private_";
+
+// Encrypts the |plaintext| with the |key| using the |encryption_scheme| and
+// returns the ciphertext.
+std::string Encrypt(const std::string& plaintext,
+ const std::string& key,
+ const securemessage::EncScheme& encryption_scheme) {
+ if (encryption_scheme == securemessage::NONE)
+ return plaintext;
+
+ // Encrypt with a Vigenere cipher.
+ std::string ciphertext;
+ ciphertext.resize(plaintext.size());
+ for (size_t i = 0; i < plaintext.size(); ++i) {
+ unsigned char plaintext_char = plaintext[i];
+ unsigned char key_char = key[i % key.size()];
+ ciphertext[i] = plaintext_char + key_char;
+ }
+ return ciphertext;
+}
+
+// Decrypts the |ciphertext| with the |key| using the |encryption_scheme| and
+// returns the plaintext.
+std::string Decrypt(const std::string& ciphertext,
+ const std::string& key,
+ const securemessage::EncScheme& encryption_scheme) {
+ if (encryption_scheme == securemessage::NONE)
+ return ciphertext;
+
+ // Decrypt with a Vigenere cipher.
+ std::string plaintext;
+ plaintext.resize(ciphertext.size());
+ for (size_t i = 0; i < ciphertext.size(); ++i) {
+ unsigned char ciphertext_char = ciphertext[i];
+ unsigned char key_char = key[i % key.size()];
+ plaintext[i] = ciphertext_char - key_char;
+ }
+ return plaintext;
+}
+
+// Signs the |payload| and |associated_data| with the |key| using the
+// |signature_scheme| and returns the signature.
+std::string Sign(const std::string& payload,
+ const std::string& associated_data,
+ const std::string& key) {
+ return base::MD5String(payload + "|" + associated_data + "|" + key);
+}
+
+// Verifies that the |signature| for the the |payload| and |associated_data| is
+// valid for the given the |key| and |signature_scheme|.
+bool Verify(const std::string& signature,
+ const std::string& payload,
+ const std::string& associated_data,
+ const std::string& key,
+ const securemessage::SigScheme& signature_scheme) {
+ std::string signing_key;
+
+ if (signature_scheme == securemessage::HMAC_SHA256) {
+ signing_key = key;
+ } else {
+ std::string prefix = kPrivateKeyPrefix;
+ bool is_private_key =
+ base::StartsWith(key, prefix, base::CompareCase::SENSITIVE);
+ signing_key = is_private_key ? key.substr(prefix.size()) : prefix + key;
+ }
+
+ std::string expected_signature = Sign(payload, associated_data, signing_key);
+ return signature == expected_signature;
+}
+
+} // namespace
+
+FakeSecureMessageDelegate::FakeSecureMessageDelegate()
+ : next_public_key_(std::string(kKeyPrefix) + "0") {
+}
+
+FakeSecureMessageDelegate::~FakeSecureMessageDelegate() {
+}
+
+void FakeSecureMessageDelegate::GenerateKeyPair(
+ const GenerateKeyPairCallback& callback) {
+ std::string public_key = next_public_key_;
+
+ // The private key is simply the public key prepended with "private_".
+ std::string private_key(kPrivateKeyPrefix + public_key);
+
+ next_public_key_ = std::string(kKeyPrefix) + base::MD5String(public_key);
+
+ callback.Run(public_key, private_key);
+}
+
+void FakeSecureMessageDelegate::DeriveKey(const std::string& private_key,
+ const std::string& public_key,
+ const DeriveKeyCallback& callback) {
+ // To ensure that the same symmetric key is derived for DeriveKey(private1,
+ // public2) and DeriveKey(private2, public1), we remove the prefix from the
+ // private key so it is equal to its corresponding public key.
+ std::string prefix(kPrivateKeyPrefix);
+ std::string normalized_private_key =
+ base::StartsWith(private_key, prefix, base::CompareCase::SENSITIVE)
+ ? private_key.substr(prefix.size())
+ : private_key;
+
+ std::vector<std::string> keys;
+ keys.push_back(normalized_private_key);
+ keys.push_back(public_key);
+ std::sort(keys.begin(), keys.end());
+ callback.Run(base::MD5String(keys[0] + "|" + keys[1]));
+}
+
+void FakeSecureMessageDelegate::CreateSecureMessage(
+ const std::string& payload,
+ const std::string& key,
+ const CreateOptions& create_options,
+ const CreateSecureMessageCallback& callback) {
+ securemessage::Header header;
+ header.set_signature_scheme(create_options.signature_scheme);
+ header.set_encryption_scheme(create_options.encryption_scheme);
+ if (!create_options.public_metadata.empty())
+ header.set_public_metadata(create_options.public_metadata);
+ if (!create_options.verification_key_id.empty())
+ header.set_verification_key_id(create_options.verification_key_id);
+ if (!create_options.decryption_key_id.empty())
+ header.set_decryption_key_id(create_options.decryption_key_id);
+
+ securemessage::HeaderAndBody header_and_body;
+ header_and_body.mutable_header()->CopyFrom(header);
+ header_and_body.set_body(
+ Encrypt(payload, key, create_options.encryption_scheme));
+ std::string serialized_header_and_body;
+ header_and_body.SerializeToString(&serialized_header_and_body);
+
+ securemessage::SecureMessage secure_message;
+ secure_message.set_header_and_body(serialized_header_and_body);
+ secure_message.set_signature(
+ Sign(payload, create_options.associated_data, key));
+
+ std::string serialized_secure_message;
+ secure_message.SerializeToString(&serialized_secure_message);
+ callback.Run(serialized_secure_message);
+}
+
+void FakeSecureMessageDelegate::UnwrapSecureMessage(
+ const std::string& serialized_message,
+ const std::string& key,
+ const UnwrapOptions& unwrap_options,
+ const UnwrapSecureMessageCallback& callback) {
+ securemessage::SecureMessage secure_message;
+ if (!secure_message.ParseFromString(serialized_message)) {
+ LOG(ERROR) << "Failed to parse SecureMessage.";
+ callback.Run(false, std::string(), securemessage::Header());
+ return;
+ }
+
+ securemessage::HeaderAndBody header_and_body;
+ if (!header_and_body.ParseFromString(secure_message.header_and_body())) {
+ LOG(ERROR) << "Failed to parse secure message HeaderAndBody.";
+ callback.Run(false, std::string(), securemessage::Header());
+ return;
+ }
+
+ const securemessage::Header& header = header_and_body.header();
+ std::string payload =
+ Decrypt(header_and_body.body(), key, unwrap_options.encryption_scheme);
+
+ bool verified = Verify(secure_message.signature(), payload,
+ unwrap_options.associated_data, key,
+ unwrap_options.signature_scheme);
+ if (verified) {
+ callback.Run(true, payload, header);
+ } else {
+ callback.Run(false, std::string(), securemessage::Header());
+ }
+}
+
+std::string FakeSecureMessageDelegate::GetPrivateKeyForPublicKey(
+ const std::string& public_key) {
+ return kPrivateKeyPrefix + public_key;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/fake_secure_message_delegate.h b/chromium/components/cryptauth/fake_secure_message_delegate.h
new file mode 100644
index 00000000000..5bc5bbedf68
--- /dev/null
+++ b/chromium/components/cryptauth/fake_secure_message_delegate.h
@@ -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.
+
+#ifndef COMPONENTS_CRYPTAUTH_FAKE_SECURE_MESSAGE_DELEGATE_H_
+#define COMPONENTS_CRYPTAUTH_FAKE_SECURE_MESSAGE_DELEGATE_H_
+
+#include "base/macros.h"
+#include "components/cryptauth/secure_message_delegate.h"
+
+namespace cryptauth {
+
+// Fake implementation of SecureMessageDelegate used in tests.
+// For clarity in tests, all functions in this delegate will invoke their
+// callback with the result before returning.
+class FakeSecureMessageDelegate : public SecureMessageDelegate {
+ public:
+ FakeSecureMessageDelegate();
+ ~FakeSecureMessageDelegate() override;
+
+ // SecureMessageDelegate:
+ void GenerateKeyPair(const GenerateKeyPairCallback& callback) override;
+ void DeriveKey(const std::string& private_key,
+ const std::string& public_key,
+ const DeriveKeyCallback& callback) override;
+ void CreateSecureMessage(
+ const std::string& payload,
+ const std::string& key,
+ const CreateOptions& create_options,
+ const CreateSecureMessageCallback& callback) override;
+ void UnwrapSecureMessage(
+ const std::string& serialized_message,
+ const std::string& key,
+ const UnwrapOptions& unwrap_options,
+ const UnwrapSecureMessageCallback& callback) override;
+
+ // Returns the corresponding private key for the given |public_key|.
+ std::string GetPrivateKeyForPublicKey(const std::string& public_key);
+
+ // Sets the next public key to be returned by GenerateKeyPair(). The
+ // corresponding private key will be derived from this public key.
+ void set_next_public_key(const std::string& public_key) {
+ next_public_key_ = public_key;
+ }
+
+ private:
+ std::string next_public_key_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeSecureMessageDelegate);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_FAKE_SECURE_MESSAGE_DELEGATE_H_
diff --git a/chromium/components/cryptauth/fake_secure_message_delegate_unittest.cc b/chromium/components/cryptauth/fake_secure_message_delegate_unittest.cc
new file mode 100644
index 00000000000..712a5714118
--- /dev/null
+++ b/chromium/components/cryptauth/fake_secure_message_delegate_unittest.cc
@@ -0,0 +1,206 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/fake_secure_message_delegate.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cryptauth {
+
+namespace {
+
+const char kTestPublicKey[] = "the private key is in another castle";
+const char kPayload[] = "500 tons of uranium";
+const char kSymmetricKey[] = "hunter2";
+const char kPublicMetadata[] = "brought to you by our sponsors";
+const char kAssociatedData[] = "save 20% bytes on your nonce insurance";
+const char kVerificationKeyId[] = "the one with the red stripes";
+const char kDecryptionKeyId[] = "it's in your pocket somewhere";
+
+// Callback for saving the result of GenerateKeys().
+void SaveKeyPair(std::string* private_key_out,
+ std::string* public_key_out,
+ const std::string& private_key,
+ const std::string& public_key) {
+ *private_key_out = private_key;
+ *public_key_out = public_key;
+}
+
+// Callback for saving the result of DeriveKey() and CreateSecureMessage().
+void SaveString(std::string* out, const std::string& value) {
+ *out = value;
+}
+
+// Callback for saving the result of UnwrapSecureMessage().
+void SaveUnwrapResults(std::string* payload_out,
+ securemessage::Header* header_out,
+ bool verified,
+ const std::string& payload,
+ const securemessage::Header& header) {
+ ASSERT_TRUE(verified);
+ *payload_out = payload;
+ *header_out = header;
+}
+
+// Returns the CreateOptions struct to create the test message.
+SecureMessageDelegate::CreateOptions GetCreateOptions(
+ securemessage::EncScheme encryption_scheme,
+ securemessage::SigScheme signature_scheme) {
+ SecureMessageDelegate::CreateOptions create_options;
+ create_options.encryption_scheme = encryption_scheme;
+ create_options.signature_scheme = signature_scheme;
+ create_options.public_metadata = kPublicMetadata;
+ create_options.associated_data = kAssociatedData;
+ create_options.verification_key_id = kVerificationKeyId;
+ create_options.decryption_key_id = kDecryptionKeyId;
+ return create_options;
+}
+
+// Returns the UnwrapOptions struct to unwrap the test message.
+SecureMessageDelegate::UnwrapOptions GetUnwrapOptions(
+ securemessage::EncScheme encryption_scheme,
+ securemessage::SigScheme signature_scheme) {
+ SecureMessageDelegate::UnwrapOptions unwrap_options;
+ unwrap_options.encryption_scheme = encryption_scheme;
+ unwrap_options.signature_scheme = signature_scheme;
+ unwrap_options.associated_data = kAssociatedData;
+ return unwrap_options;
+}
+
+void CheckSerializedSecureMessage(
+ const std::string& serialized_message,
+ const SecureMessageDelegate::CreateOptions& create_options) {
+ securemessage::SecureMessage secure_message;
+ ASSERT_TRUE(secure_message.ParseFromString(serialized_message));
+ securemessage::HeaderAndBody header_and_body;
+ ASSERT_TRUE(
+ header_and_body.ParseFromString(secure_message.header_and_body()));
+
+ const securemessage::Header& header = header_and_body.header();
+ EXPECT_EQ(create_options.signature_scheme, header.signature_scheme());
+ EXPECT_EQ(create_options.encryption_scheme, header.encryption_scheme());
+ EXPECT_EQ(create_options.verification_key_id, header.verification_key_id());
+ EXPECT_EQ(create_options.decryption_key_id, header.decryption_key_id());
+ EXPECT_EQ(create_options.public_metadata, header.public_metadata());
+}
+
+} // namespace
+
+class CryptAuthFakeSecureMessageDelegateTest : public testing::Test {
+ protected:
+ CryptAuthFakeSecureMessageDelegateTest() {}
+
+ FakeSecureMessageDelegate delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthFakeSecureMessageDelegateTest);
+};
+
+TEST_F(CryptAuthFakeSecureMessageDelegateTest, GenerateKeyPair) {
+ std::string public_key1, private_key1;
+ delegate_.GenerateKeyPair(
+ base::Bind(&SaveKeyPair, &public_key1, &private_key1));
+ EXPECT_NE(private_key1, public_key1);
+
+ std::string public_key2, private_key2;
+ delegate_.GenerateKeyPair(
+ base::Bind(&SaveKeyPair, &public_key2, &private_key2));
+ EXPECT_NE(private_key2, public_key2);
+
+ EXPECT_NE(public_key1, public_key2);
+ EXPECT_NE(private_key1, private_key2);
+
+ delegate_.set_next_public_key(kTestPublicKey);
+ std::string public_key3, private_key3;
+ delegate_.GenerateKeyPair(
+ base::Bind(&SaveKeyPair, &public_key3, &private_key3));
+ EXPECT_EQ(kTestPublicKey, public_key3);
+ EXPECT_NE(private_key3, public_key3);
+
+ EXPECT_NE(public_key1, public_key3);
+ EXPECT_NE(private_key1, private_key3);
+}
+
+TEST_F(CryptAuthFakeSecureMessageDelegateTest, DeriveKey) {
+ delegate_.set_next_public_key("key_pair_1");
+ std::string public_key1, private_key1;
+ delegate_.GenerateKeyPair(
+ base::Bind(&SaveKeyPair, &public_key1, &private_key1));
+
+ delegate_.set_next_public_key("key_pair_2");
+ std::string public_key2, private_key2;
+ delegate_.GenerateKeyPair(
+ base::Bind(&SaveKeyPair, &public_key2, &private_key2));
+
+ std::string symmetric_key1, symmetric_key2;
+ delegate_.DeriveKey(private_key1, public_key2,
+ base::Bind(&SaveString, &symmetric_key1));
+ delegate_.DeriveKey(private_key2, public_key1,
+ base::Bind(&SaveString, &symmetric_key2));
+
+ EXPECT_EQ(symmetric_key1, symmetric_key2);
+}
+
+TEST_F(CryptAuthFakeSecureMessageDelegateTest,
+ CreateAndUnwrapWithSymmetricKey) {
+ // Create SecureMessage using symmetric key.
+ SecureMessageDelegate::CreateOptions create_options =
+ GetCreateOptions(securemessage::AES_256_CBC, securemessage::HMAC_SHA256);
+ std::string serialized_message;
+ delegate_.CreateSecureMessage(kPayload, kSymmetricKey, create_options,
+ base::Bind(&SaveString, &serialized_message));
+
+ CheckSerializedSecureMessage(serialized_message, create_options);
+
+ // Unwrap SecureMessage using symmetric key.
+ SecureMessageDelegate::UnwrapOptions unwrap_options =
+ GetUnwrapOptions(securemessage::AES_256_CBC, securemessage::HMAC_SHA256);
+ std::string payload;
+ securemessage::Header header;
+ delegate_.UnwrapSecureMessage(
+ serialized_message, kSymmetricKey, unwrap_options,
+ base::Bind(&SaveUnwrapResults, &payload, &header));
+
+ EXPECT_EQ(kPayload, payload);
+}
+
+TEST_F(CryptAuthFakeSecureMessageDelegateTest,
+ CreateAndUnwrapWithAsymmetricKey) {
+ delegate_.set_next_public_key(kTestPublicKey);
+ std::string public_key, private_key;
+ delegate_.GenerateKeyPair(
+ base::Bind(&SaveKeyPair, &public_key, &private_key));
+
+ // Create SecureMessage using asymmetric key.
+ SecureMessageDelegate::CreateOptions create_options =
+ GetCreateOptions(securemessage::NONE, securemessage::ECDSA_P256_SHA256);
+ std::string serialized_message;
+ delegate_.CreateSecureMessage(kPayload, private_key, create_options,
+ base::Bind(&SaveString, &serialized_message));
+
+ CheckSerializedSecureMessage(serialized_message, create_options);
+
+ // Unwrap SecureMessage using symmetric key.
+ SecureMessageDelegate::UnwrapOptions unwrap_options =
+ GetUnwrapOptions(securemessage::NONE, securemessage::ECDSA_P256_SHA256);
+ std::string payload;
+ securemessage::Header header;
+ delegate_.UnwrapSecureMessage(
+ serialized_message, public_key, unwrap_options,
+ base::Bind(&SaveUnwrapResults, &payload, &header));
+
+ EXPECT_EQ(kPayload, payload);
+}
+
+TEST_F(CryptAuthFakeSecureMessageDelegateTest, GetPrivateKeyForPublicKey) {
+ delegate_.set_next_public_key(kTestPublicKey);
+ std::string public_key, private_key;
+ delegate_.GenerateKeyPair(
+ base::Bind(&SaveKeyPair, &public_key, &private_key));
+ EXPECT_EQ(kTestPublicKey, public_key);
+ EXPECT_EQ(private_key, delegate_.GetPrivateKeyForPublicKey(kTestPublicKey));
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/mock_cryptauth_client.cc b/chromium/components/cryptauth/mock_cryptauth_client.cc
new file mode 100644
index 00000000000..e10d2ef4e72
--- /dev/null
+++ b/chromium/components/cryptauth/mock_cryptauth_client.cc
@@ -0,0 +1,46 @@
+// 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_cryptauth_client.h"
+
+#include <utility>
+
+#include "base/callback.h"
+
+namespace cryptauth {
+
+MockCryptAuthClient::MockCryptAuthClient() {
+}
+
+MockCryptAuthClient::~MockCryptAuthClient() {
+}
+
+MockCryptAuthClientFactory::MockCryptAuthClientFactory(MockType mock_type)
+ : mock_type_(mock_type) {
+}
+
+MockCryptAuthClientFactory::~MockCryptAuthClientFactory() {
+}
+
+std::unique_ptr<CryptAuthClient> MockCryptAuthClientFactory::CreateInstance() {
+ std::unique_ptr<MockCryptAuthClient> client;
+ if (mock_type_ == MockType::MAKE_STRICT_MOCKS)
+ client.reset(new testing::StrictMock<MockCryptAuthClient>());
+ else
+ client.reset(new testing::NiceMock<MockCryptAuthClient>());
+
+ for (auto& observer : observer_list_)
+ observer.OnCryptAuthClientCreated(client.get());
+ return std::move(client);
+}
+
+void MockCryptAuthClientFactory::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void MockCryptAuthClientFactory::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/mock_cryptauth_client.h b/chromium/components/cryptauth/mock_cryptauth_client.h
new file mode 100644
index 00000000000..ddc40e90537
--- /dev/null
+++ b/chromium/components/cryptauth/mock_cryptauth_client.h
@@ -0,0 +1,88 @@
+// 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_MOCK_CRYPTAUTH_CLIENT_H_
+#define COMPONENTS_CRYPTAUTH_MOCK_CRYPTAUTH_CLIENT_H_
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/cryptauth/cryptauth_client.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace cryptauth {
+
+class MockCryptAuthClient : public CryptAuthClient {
+ public:
+ MockCryptAuthClient();
+ ~MockCryptAuthClient() override;
+
+ // CryptAuthClient:
+ MOCK_METHOD3(GetMyDevices,
+ void(const GetMyDevicesRequest& request,
+ const GetMyDevicesCallback& callback,
+ const ErrorCallback& error_callback));
+ MOCK_METHOD3(FindEligibleUnlockDevices,
+ void(const FindEligibleUnlockDevicesRequest& request,
+ const FindEligibleUnlockDevicesCallback& callback,
+ const ErrorCallback& error_callback));
+ MOCK_METHOD3(SendDeviceSyncTickle,
+ void(const SendDeviceSyncTickleRequest& request,
+ const SendDeviceSyncTickleCallback& callback,
+ const ErrorCallback& error_callback));
+ MOCK_METHOD3(ToggleEasyUnlock,
+ void(const ToggleEasyUnlockRequest& request,
+ const ToggleEasyUnlockCallback& callback,
+ const ErrorCallback& error_callback));
+ MOCK_METHOD3(SetupEnrollment,
+ void(const SetupEnrollmentRequest& request,
+ const SetupEnrollmentCallback& callback,
+ const ErrorCallback& error_callback));
+ MOCK_METHOD3(FinishEnrollment,
+ void(const FinishEnrollmentRequest& request,
+ const FinishEnrollmentCallback& callback,
+ const ErrorCallback& error_callback));
+ MOCK_METHOD0(GetAccessTokenUsed, std::string());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockCryptAuthClient);
+};
+
+class MockCryptAuthClientFactory : public CryptAuthClientFactory {
+ public:
+ class Observer {
+ public:
+ // Called with the new instance when it is requested from the factory,
+ // allowing expectations to be set. Ownership of |client| will be taken by
+ // the caller of CreateInstance().
+ virtual void OnCryptAuthClientCreated(MockCryptAuthClient* client) = 0;
+ };
+
+ // Represents the type of mock instances to create.
+ enum class MockType { MAKE_NICE_MOCKS, MAKE_STRICT_MOCKS };
+
+ // If |mock_type| is STRICT, then StrictMocks will be created. Otherwise,
+ // NiceMocks will be created.
+ explicit MockCryptAuthClientFactory(MockType mock_type);
+ ~MockCryptAuthClientFactory() override;
+
+ // CryptAuthClientFactory:
+ std::unique_ptr<CryptAuthClient> CreateInstance() override;
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ private:
+ // Whether to create StrictMocks or NiceMocks.
+ const MockType mock_type_;
+
+ // Observers of the factory.
+ base::ObserverList<Observer> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCryptAuthClientFactory);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_MOCK_CRYPTAUTH_CLIENT_H_
diff --git a/chromium/components/cryptauth/mock_eid_generator.cc b/chromium/components/cryptauth/mock_eid_generator.cc
new file mode 100644
index 00000000000..c9515a74086
--- /dev/null
+++ b/chromium/components/cryptauth/mock_eid_generator.cc
@@ -0,0 +1,74 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/mock_eid_generator.h"
+
+#include "base/memory/ptr_util.h"
+
+namespace cryptauth {
+
+MockEidGenerator::MockEidGenerator() : background_scan_filter_(nullptr),
+ advertisement_(nullptr),
+ possible_advertisements_(nullptr),
+ identified_device_(nullptr),
+ num_identify_calls_(0) {}
+
+MockEidGenerator::~MockEidGenerator() {}
+
+std::unique_ptr<EidGenerator::EidData>
+MockEidGenerator::GenerateBackgroundScanFilter(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+ if (!background_scan_filter_) {
+ return nullptr;
+ }
+
+ std::unique_ptr<EidGenerator::DataWithTimestamp> adjacent_data;
+ if (background_scan_filter_->adjacent_data) {
+ adjacent_data = base::MakeUnique<DataWithTimestamp>(
+ background_scan_filter_->adjacent_data->data,
+ background_scan_filter_->adjacent_data->start_timestamp_ms,
+ background_scan_filter_->adjacent_data->end_timestamp_ms);
+ }
+
+ return base::MakeUnique<EidData>(
+ background_scan_filter_->current_data, std::move(adjacent_data));
+}
+
+std::unique_ptr<EidGenerator::DataWithTimestamp>
+MockEidGenerator::GenerateAdvertisement(
+ const std::string& advertising_device_public_key,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+ if (!advertisement_) {
+ return nullptr;
+ }
+
+ return base::MakeUnique<DataWithTimestamp>(
+ advertisement_->data,
+ advertisement_->start_timestamp_ms,
+ advertisement_->end_timestamp_ms);
+}
+
+std::vector<std::string> MockEidGenerator::GeneratePossibleAdvertisements(
+ const std::string& advertising_device_public_key,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+ if (!possible_advertisements_) {
+ return std::vector<std::string>();
+ }
+
+ return *possible_advertisements_;
+}
+
+RemoteDevice const* MockEidGenerator::IdentifyRemoteDeviceByAdvertisement(
+ const std::string& advertisement_service_data,
+ const std::vector<RemoteDevice>& device_list,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+ // Increment num_identify_calls_. Since this overrides a const method, some
+ // hacking is needed to modify the num_identify_calls_ instance variable.
+ int* num_identify_calls_ptr = const_cast<int*>(&num_identify_calls_);
+ *num_identify_calls_ptr = *num_identify_calls_ptr + 1;
+
+ return identified_device_;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/mock_eid_generator.h b/chromium/components/cryptauth/mock_eid_generator.h
new file mode 100644
index 00000000000..4f624a22fe8
--- /dev/null
+++ b/chromium/components/cryptauth/mock_eid_generator.h
@@ -0,0 +1,78 @@
+// 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_BLE_MOCK_EID_GENERATOR_H_
+#define COMPONENTS_CRYPTAUTH_BLE_MOCK_EID_GENERATOR_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/cryptauth/eid_generator.h"
+
+namespace cryptauth {
+
+class BeaconSeed;
+struct RemoteDevice;
+
+// Mock class for EidGenerator. Note that GoogleMock cannot be used to mock this
+// class because GoogleMock's mock functions cannot return a |std::unique_ptr|.
+class MockEidGenerator : public EidGenerator {
+ public:
+ MockEidGenerator();
+ ~MockEidGenerator() override;
+
+ // Setters for the return values of the overridden functions below.
+ void set_background_scan_filter(
+ std::unique_ptr<EidData> background_scan_filter) {
+ background_scan_filter_ = std::move(background_scan_filter);
+ }
+
+ void set_advertisement(std::unique_ptr<DataWithTimestamp> advertisement) {
+ advertisement_ = std::move(advertisement);
+ }
+
+ void set_possible_advertisements(
+ std::unique_ptr<std::vector<std::string>> possible_advertisements) {
+ possible_advertisements_ = std::move(possible_advertisements);
+ }
+
+ void set_identified_device(RemoteDevice* identified_device) {
+ identified_device_ = identified_device;
+ }
+
+ // EidGenerator:
+ std::unique_ptr<EidData> GenerateBackgroundScanFilter(
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds)
+ const override;
+ std::unique_ptr<DataWithTimestamp> GenerateAdvertisement(
+ const std::string& advertising_device_public_key,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds)
+ const override;
+ std::vector<std::string> GeneratePossibleAdvertisements(
+ const std::string& advertising_device_public_key,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds)
+ const override;
+ RemoteDevice const* IdentifyRemoteDeviceByAdvertisement(
+ const std::string& advertisement_service_data,
+ const std::vector<RemoteDevice>& device_list,
+ const std::vector<BeaconSeed>& scanning_device_beacon_seeds)
+ const override;
+
+ int num_identify_calls() {
+ return num_identify_calls_;
+ }
+
+ private:
+ std::unique_ptr<EidData> background_scan_filter_;
+ std::unique_ptr<DataWithTimestamp> advertisement_;
+ std::unique_ptr<std::vector<std::string>> possible_advertisements_;
+ const RemoteDevice* identified_device_;
+
+ int num_identify_calls_;
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_BLE_MOCK_EID_GENERATOR_H_
diff --git a/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.cc b/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.cc
new file mode 100644
index 00000000000..21fc30818e6
--- /dev/null
+++ b/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.cc
@@ -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.
+
+#include "components/cryptauth/mock_remote_beacon_seed_fetcher.h"
+
+#include "components/cryptauth/cryptauth_device_manager.h"
+
+namespace cryptauth {
+
+MockRemoteBeaconSeedFetcher::MockRemoteBeaconSeedFetcher()
+ : RemoteBeaconSeedFetcher(nullptr) {}
+
+MockRemoteBeaconSeedFetcher::~MockRemoteBeaconSeedFetcher() {}
+
+bool MockRemoteBeaconSeedFetcher::FetchSeedsForDevice(
+ const RemoteDevice& remote_device,
+ std::vector<BeaconSeed>* beacon_seeds_out) const {
+ const auto& seeds_iter =
+ public_key_to_beacon_seeds_map_.find(remote_device.public_key);
+ if (seeds_iter == public_key_to_beacon_seeds_map_.end()) {
+ return false;
+ }
+
+ *beacon_seeds_out = seeds_iter->second;
+ return true;
+}
+
+void MockRemoteBeaconSeedFetcher::SetSeedsForDevice(
+ const RemoteDevice& remote_device,
+ const std::vector<BeaconSeed>* beacon_seeds) {
+ if (!beacon_seeds) {
+ const auto& it =
+ public_key_to_beacon_seeds_map_.find(remote_device.public_key);
+ if (it != public_key_to_beacon_seeds_map_.end()) {
+ public_key_to_beacon_seeds_map_.erase(it);
+ }
+ return;
+ }
+
+ public_key_to_beacon_seeds_map_[remote_device.public_key] = *beacon_seeds;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.h b/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.h
new file mode 100644
index 00000000000..9b8efe045ca
--- /dev/null
+++ b/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.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_CRYPTAUTH_MOCK_BEACON_SEED_FETCHER_H_
+#define COMPONENTS_CRYPTAUTH_MOCK_BEACON_SEED_FETCHER_H_
+
+#include <map>
+#include <vector>
+
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/remote_beacon_seed_fetcher.h"
+
+namespace cryptauth {
+
+class MockRemoteBeaconSeedFetcher : public RemoteBeaconSeedFetcher {
+ public:
+ MockRemoteBeaconSeedFetcher();
+ ~MockRemoteBeaconSeedFetcher() override;
+
+ // RemoteBeaconSeedFetcher:
+ bool FetchSeedsForDevice(
+ const RemoteDevice& remote_device,
+ std::vector<BeaconSeed>* beacon_seeds_out) const override;
+
+ // If |beacon_seeds| is null, FetchSeedsForDevice() will fail for the same
+ // |remote_device|.
+ void SetSeedsForDevice(
+ const RemoteDevice& remote_device,
+ const std::vector<BeaconSeed>* beacon_seeds);
+
+ private:
+ std::map<std::string, std::vector<BeaconSeed>>
+ public_key_to_beacon_seeds_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockRemoteBeaconSeedFetcher);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_MOCK_BEACON_SEED_FETCHER_H_
diff --git a/chromium/components/cryptauth/mock_sync_scheduler.cc b/chromium/components/cryptauth/mock_sync_scheduler.cc
new file mode 100644
index 00000000000..11f5dd82183
--- /dev/null
+++ b/chromium/components/cryptauth/mock_sync_scheduler.cc
@@ -0,0 +1,15 @@
+// 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_sync_scheduler.h"
+
+namespace cryptauth {
+
+MockSyncScheduler::MockSyncScheduler() {
+}
+
+MockSyncScheduler::~MockSyncScheduler() {
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/mock_sync_scheduler.h b/chromium/components/cryptauth/mock_sync_scheduler.h
new file mode 100644
index 00000000000..b3fd9b33cf5
--- /dev/null
+++ b/chromium/components/cryptauth/mock_sync_scheduler.h
@@ -0,0 +1,36 @@
+// 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_MOCK_SYNC_SCHEDULER_H_
+#define COMPONENTS_CRYPTAUTH_MOCK_SYNC_SCHEDULER_H_
+
+#include "base/macros.h"
+#include "components/cryptauth/sync_scheduler.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace cryptauth {
+
+// Mock implementation of SyncScheduler.
+class MockSyncScheduler : public SyncScheduler {
+ public:
+ MockSyncScheduler();
+ ~MockSyncScheduler() override;
+
+ // SyncScheduler:
+ MOCK_METHOD2(Start,
+ void(const base::TimeDelta& elapsed_time_since_last_sync,
+ Strategy strategy));
+ MOCK_METHOD0(ForceSync, void(void));
+ MOCK_CONST_METHOD0(GetTimeToNextSync, base::TimeDelta(void));
+ MOCK_CONST_METHOD0(GetStrategy, Strategy(void));
+ MOCK_CONST_METHOD0(GetSyncState, SyncState(void));
+ MOCK_METHOD1(OnSyncCompleted, void(bool success));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockSyncScheduler);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_MOCK_SYNC_SCHEDULER_H_
diff --git a/chromium/components/cryptauth/pref_names.cc b/chromium/components/cryptauth/pref_names.cc
new file mode 100644
index 00000000000..01545752803
--- /dev/null
+++ b/chromium/components/cryptauth/pref_names.cc
@@ -0,0 +1,57 @@
+// 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/pref_names.h"
+
+namespace cryptauth {
+namespace prefs {
+
+// Whether the system is scheduling device_syncs more aggressively to recover
+// from the previous device_sync failure.
+const char kCryptAuthDeviceSyncIsRecoveringFromFailure[] =
+ "cryptauth.device_sync.is_recovering_from_failure";
+
+// The timestamp of the last successful CryptAuth device_sync in seconds.
+const char kCryptAuthDeviceSyncLastSyncTimeSeconds[] =
+ "cryptauth.device_sync.last_device_sync_time_seconds";
+
+// The reason that the next device_sync is performed. This should be one of the
+// enum values of InvocationReason in
+// components/cryptauth/proto/cryptauth_api.proto.
+const char kCryptAuthDeviceSyncReason[] = "cryptauth.device_sync.reason";
+
+// A list of unlock keys (stored as dictionaries) synced from CryptAuth. Unlock
+// Keys are phones belonging to the user that can unlock other devices, such as
+// desktop PCs.
+const char kCryptAuthDeviceSyncUnlockKeys[] =
+ "cryptauth.device_sync.unlock_keys";
+
+// Whether the system is scheduling enrollments more aggressively to recover
+// from the previous enrollment failure.
+const char kCryptAuthEnrollmentIsRecoveringFromFailure[] =
+ "cryptauth.enrollment.is_recovering_from_failure";
+
+// The timestamp of the last successful CryptAuth enrollment in seconds.
+const char kCryptAuthEnrollmentLastEnrollmentTimeSeconds[] =
+ "cryptauth.enrollment.last_enrollment_time_seconds";
+
+// The reason that the next enrollment is performed. This should be one of the
+// enum values of InvocationReason in
+// components/cryptauth/proto/cryptauth_api.proto.
+extern const char kCryptAuthEnrollmentReason[] = "cryptauth.enrollment.reason";
+
+// The public key of the user and device enrolled with CryptAuth.
+extern const char kCryptAuthEnrollmentUserPublicKey[] =
+ "cryptauth.enrollment.user_public_key";
+
+// The private key of the user and device enrolled with CryptAuth.
+extern const char kCryptAuthEnrollmentUserPrivateKey[] =
+ "cryptauth.enrollment.user_private_key";
+
+// The GCM registration id used for receiving push messages from CryptAuth.
+extern const char kCryptAuthGCMRegistrationId[] =
+ "cryptauth.gcm_registration_id";
+
+} // namespace prefs
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/pref_names.h b/chromium/components/cryptauth/pref_names.h
new file mode 100644
index 00000000000..dfc3803fae0
--- /dev/null
+++ b/chromium/components/cryptauth/pref_names.h
@@ -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.
+
+#ifndef COMPONENTS_CRYPTAUTH_PREF_NAMES_H_
+#define COMPONENTS_CRYPTAUTH_PREF_NAMES_H_
+
+namespace cryptauth {
+namespace prefs {
+
+extern const char kCryptAuthDeviceSyncLastSyncTimeSeconds[];
+extern const char kCryptAuthDeviceSyncIsRecoveringFromFailure[];
+extern const char kCryptAuthDeviceSyncReason[];
+extern const char kCryptAuthDeviceSyncUnlockKeys[];
+extern const char kCryptAuthEnrollmentIsRecoveringFromFailure[];
+extern const char kCryptAuthEnrollmentLastEnrollmentTimeSeconds[];
+extern const char kCryptAuthEnrollmentReason[];
+extern const char kCryptAuthEnrollmentUserPublicKey[];
+extern const char kCryptAuthEnrollmentUserPrivateKey[];
+extern const char kCryptAuthGCMRegistrationId[];
+
+} // namespace prefs
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_PREF_NAMES_H_
diff --git a/chromium/components/proximity_auth/cryptauth/proto/BUILD.gn b/chromium/components/cryptauth/proto/BUILD.gn
index 9affa372285..9affa372285 100644
--- a/chromium/components/proximity_auth/cryptauth/proto/BUILD.gn
+++ b/chromium/components/cryptauth/proto/BUILD.gn
diff --git a/chromium/components/cryptauth/proto/cryptauth_api.proto b/chromium/components/cryptauth/proto/cryptauth_api.proto
new file mode 100644
index 00000000000..6e2be7e2956
--- /dev/null
+++ b/chromium/components/cryptauth/proto/cryptauth_api.proto
@@ -0,0 +1,513 @@
+// 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.
+
+// Definitions for CryptAuth API calls. Do not edit unless transcribing
+// from server definitions.
+syntax = "proto2";
+
+package cryptauth;
+
+option optimize_for = LITE_RUNTIME;
+
+// Basic device information used to classify the source of a request.
+message DeviceClassifier {
+ // The Operating System version number on the device.
+ // (e.g., an android.os.Build.VERSION.SDK_INT)
+ optional int64 device_os_version_code = 14;
+
+ // The software version number running on the device
+ // (e.g., GmsCore version code).
+ optional int64 device_software_version_code = 18;
+
+ // Software package information if applicable
+ // (e.g., com.google.android.apps.authenticator2).
+ optional string device_software_package = 19;
+
+ // Device type/platform.
+ optional DeviceType device_type = 32 [default = UNKNOWN];
+}
+
+enum DeviceType {
+ UNKNOWN = 0;
+ ANDROIDOS = 1;
+ CHROME = 2;
+ IOS = 3;
+ BROWSER = 4;
+}
+
+// A seed used to feed an EID BLE advertisement for some time period. This
+// key is specific to the device being targeted by the advertisement.
+// Next ID: 4
+message BeaconSeed {
+ // The actual key bytes.
+ optional bytes data = 1;
+
+ // The time at which this key becomes active.
+ optional int64 start_time_millis = 2;
+
+ // The time at which this key becomes inactive.
+ optional int64 end_time_millis = 3;
+}
+
+// Device information provided to external clients that need to sync
+// device state.
+message ExternalDeviceInfo {
+ // A cryptographic public key associated with the device.
+ // The format of this key is a serialized SecureMessage.GenericPublicKey
+ // for all GCM_V1 devices.
+ optional bytes public_key = 1;
+
+ // A user friendly (human readable) name for this device.
+ optional string friendly_device_name = 2;
+
+ // If available, the device's bluetooth MAC address
+ optional string bluetooth_address = 3;
+
+ // Whether or not this device can be used as an unlock key
+ optional bool unlock_key = 4 [ default = false ];
+
+ // Whether or not this device can be unlocked
+ optional bool unlockable = 5 [ default = false ];
+
+ // The last time this device updated its info with the server.
+ optional int64 last_update_time_millis = 6;
+
+ // Whether or not this device hardware supports providing a mobile hotspot.
+ optional bool mobile_hotspot_supported = 7 [ default = false ];
+
+ // The type of the device (e.g. Android vs iOS).
+ optional DeviceType device_type = 8 [ default = UNKNOWN ];
+
+ // A list of seeds for EID BLE advertisements targeting this device.
+ repeated BeaconSeed beacon_seeds = 9;
+}
+
+// Determine if the calling device is allowed to promote the SmartLock
+// feature to the user and contact the user's authzen enrolled devices to make
+// them update their enrollments and to check if they're reachable (we don't
+// want to show a promotional popup if the user has no reachable devices).
+message FindEligibleForPromotionRequest {
+ // The public key of the device that is asking us whether it should show
+ // promotional material. Required.
+ optional bytes promoter_public_key = 2;
+
+ // Information about the requesting device and its platform.
+ optional DeviceClassifier device_classifier = 3;
+}
+
+// Contains the authzen transaction id that the caller can use to check if
+// there are reachable devices.
+message FindEligibleForPromotionResponse {
+ // Whether the caller is allowed to show promotional material.
+ optional bool may_show_promo = 1;
+}
+
+// Request for a list of devices that could be used as Unlock Keys, optionally
+// requesting a callback over bluetooth (for proximity detection).
+message FindEligibleUnlockDevicesRequest {
+ // A bluetooth MAC address to be contacted if a device that may be eligible
+ // for unlock is nearby. If set, a message will be pushed to all eligible
+ // unlock devices requesting that they contact the specified MAC address. If
+ // this field is left unset, no callback will be made, and no message will be
+ // pushed to the user's devices.
+ optional string callback_bluetooth_address = 2;
+
+ // During setup, we call find and sendDeviceSyncTickle. If no devices are
+ // found, we call find again, in hopes that the tickle caused a state
+ // change that made some device eligible. This is the count for these
+ // retries (in practice, this will always be 0 or 1).
+ // Should always be set.
+ optional int32 retry_count = 3;
+
+ // If present and positive, the devices must have been updated within this
+ // many milliseconds of the RPC in order to be considered eligible.
+ optional int64 max_last_update_time_delta_millis = 4;
+
+ // If true, we will not examine the push connectivity status of devices
+ // when determining eligibility.
+ optional bool offline_allowed = 5 [default = false];
+
+ // Information about the requesting device and its platform.
+ optional DeviceClassifier device_classifier = 6;
+}
+
+// Response containing a list of devices that could be made Unlock Keys
+message FindEligibleUnlockDevicesResponse {
+ // Devices that could be made Unlock Keys (even if they aren't enabled yet)
+ repeated ExternalDeviceInfo eligible_devices = 1;
+
+ // Devices that cannot be made unlock keys, and reasons for this. This list
+ // will not contain any non-gms core devices, even though these are also not
+ // eligible to be unlock keys.
+ repeated IneligibleDevice ineligible_devices = 2;
+}
+
+// Request to complete a device enrollment.
+message FinishEnrollmentRequest {
+ // The enrollment session identifer from the <code>setup</code> response.
+ optional bytes enrollment_session_id = 2;
+
+ // An encrypted payload containing enrollment information for the device.
+ optional bytes enrollment_message = 3;
+
+ // A Diffie-Hellman public key for the device, to complete the key exchange.
+ optional bytes device_ephemeral_key = 4;
+
+ // An integer encoding the reason this enrollment was invoked (triggered).
+ // See InvocationReason enum for definitions.
+ optional int32 invocation_reason = 11 [default = 0];
+
+ // How many retries of this operation have happened thus far.
+ optional int32 retry_count = 12 [default = 0];
+
+ // Information about the requesting device and its platform.
+ optional DeviceClassifier device_classifier = 13;
+}
+
+// Response indicating whether a device enrollment completed successfully.
+message FinishEnrollmentResponse {
+ // Status should be OK if the request was successful.
+ optional string status = 1;
+
+ // A detailed error message if there was a failure.
+ optional string error_message = 2;
+}
+
+// Device info uploaded during enrollment.
+message GcmDeviceInfo {
+ // This field's name does not match the one in DeviceInfo for legacy reasons.
+ // Consider using long_device_id and device_type instead when enrolling
+ // non-android devices.
+ optional fixed64 android_device_id = 1;
+
+ // Used for device_address of DeviceInfo field 2, but for GCM capable devices.
+ optional bytes gcm_registration_id = 102;
+
+ // Used for device_address of DeviceInfo field 2, but for iOS devices.
+ optional bytes apn_registration_id = 202;
+
+ // Has the user enabled the associated apn_registration_id for notifications.
+ optional bool apn_notification_enabled = 203 [default = false];
+
+ // Used for device_address of DeviceInfo field 2, a Bluetooth Mac address for
+ // the device (e.g., to be used with EasyUnlock).
+ optional string bluetooth_mac_address = 302;
+
+ // SHA-256 hash of the device master key (from the key exchange).
+ // Differs from DeviceInfo field 3, which contains the actual master key.
+ optional bytes device_master_key_hash = 103;
+
+ // A SecureMessage.EcP256PublicKey.
+ required bytes user_public_key = 4;
+
+ // device's model name
+ // (e.g., an android.os.Build.MODEL or UIDevice.model).
+ optional string device_model = 7;
+
+ // device's locale
+ optional string locale = 8;
+
+ // The handle for user_public_key (and implicitly, a master key).
+ optional bytes key_handle = 9;
+
+ // The initial counter value for the device, sent by the device.
+ optional int64 counter = 12 [default = 0];
+
+ // The Operating System version on the device
+ // (e.g., an android.os.Build.DISPLAY or UIDevice.systemVersion).
+ optional string device_os_version = 13;
+
+ // The Operating System version number on the device
+ // (e.g., an android.os.Build.VERSION.SDK_INT).
+ optional int64 device_os_version_code = 14;
+
+ // The Operating System release on the device
+ // (e.g., an android.os.Build.VERSION.RELEASE).
+ optional string device_os_release = 15;
+
+ // The Operating System codename on the device
+ // (e.g., an android.os.Build.VERSION.CODENAME or UIDevice.systemName).
+ optional string device_os_codename = 16;
+
+ // The software version running on the device
+ // (e.g., Authenticator app version string).
+ optional string device_software_version = 17;
+
+ // The software version number running on the device
+ // (e.g., Authenticator app version code).
+ optional int64 device_software_version_code = 18;
+
+ // Software package information if applicable
+ // (e.g., com.google.android.apps.authenticator2).
+ optional string device_software_package = 19;
+
+ // Size of the display in thousandths of an inch (e.g., 7000 mils = 7 in).
+ optional int32 device_display_diagonal_mils = 22;
+
+ // For Authzen capable devices, their Authzen protocol version.
+ optional int32 device_authzen_version = 24;
+
+ // Not all devices have device identifiers that fit in 64 bits.
+ optional bytes long_device_id = 29;
+
+ // The device manufacturer name
+ // (e.g., android.os.Build.MANUFACTURER).
+ optional string device_manufacturer = 31;
+
+ // Used to indicate which type of device this is.
+ optional DeviceType device_type = 32 [default = ANDROIDOS];
+
+ // Fields corresponding to screenlock type/features and hardware features
+ // should be numbered in the 400 range.
+
+ // Is this device using a secure screenlock (e.g., pattern or pin unlock).
+ optional bool using_secure_screenlock = 400 [default = false];
+
+ // Is auto-unlocking the screenlock (e.g., when at "home") supported?
+ optional bool auto_unlock_screenlock_supported = 401 [default = false];
+
+ // Is auto-unlocking the screenlock (e.g., when at "home") enabled?
+ optional bool auto_unlock_screenlock_enabled = 402 [default = false];
+
+ // Does the device have a Bluetooth (classic) radio?
+ optional bool bluetooth_radio_supported = 403 [default = false];
+
+ // Is the Bluetooth (classic) radio on?
+ optional bool bluetooth_radio_enabled = 404 [default = false];
+
+ // The enrollment session id this is sent with.
+ optional bytes enrollment_session_id = 1000;
+
+ // A copy of the user's OAuth token.
+ optional string oauth_token = 1001;
+}
+
+message GcmMetadata {
+ required MessageType type = 1;
+ optional int32 version = 2 [default = 0];
+}
+
+// Request for a listing of a user's own devices.
+message GetMyDevicesRequest {
+ // Return only devices that can act as EasyUnlock keys.
+ optional bool approved_for_unlock_required = 2;
+
+ // Allow the returned list to be somewhat out of date (read will be faster).
+ optional bool allow_stale_read = 3 [default = false];
+
+ // An integer encoding the reason this request was invoked (triggered).
+ // See InvocationReason enum for definitions.
+ optional int32 invocation_reason = 4 [default = 0];
+
+ // How many retries of this operation have happened thus far.
+ optional int32 retry_count = 5 [default = 0];
+
+ // Information about the requesting device and its platform.
+ optional DeviceClassifier device_classifier = 6;
+}
+
+// Response containing a listing of the users devices.
+message GetMyDevicesResponse {
+ // A listing of all sync-able devices.
+ repeated ExternalDeviceInfo devices = 1;
+}
+
+// A device that the server thinks is not eligible to be an unlock key, and the
+// reason for this.
+message IneligibleDevice {
+ // The device that is not eligible to be an unlock key.
+ optional ExternalDeviceInfo device = 1;
+
+ // The reasons why the server thinks it is not an unlock key. NOTE: for now,
+ // this list of reasons will contain exactly one element. It is a repeated
+ // field because, in principle, there can be more than one reason that makes a
+ // device not eligible to be an unlock key, and we want to be able to add
+ // multiple reasons in the future.
+ repeated string reasons = 2;
+}
+
+// A list of "reasons" that can be provided for calling server-side APIs.
+// This is particularly important for calls that can be triggered by different
+// kinds of events.
+// NOTE: Added INVOCATION_* prefix to enum names due to name conflict with
+// preprocessor MACRO on Windows.
+enum InvocationReason {
+ INVOCATION_REASON_UNKNOWN = 0;
+ // First run of the software package invoking this call.
+ INVOCATION_REASON_INITIALIZATION = 1;
+ // Ordinary periodic actions (e.g. monthly master key rotation).
+ INVOCATION_REASON_PERIODIC = 2;
+ // Slow-cycle periodic action (e.g. yearly keypair rotation???).
+ INVOCATION_REASON_SLOW_PERIODIC = 3;
+ // Fast-cycle periodic action (e.g. daily sync for Smart Lock users).
+ INVOCATION_REASON_FAST_PERIODIC = 4;
+ // Expired state (e.g. expired credentials, or cached entries) was detected.
+ INVOCATION_REASON_EXPIRATION = 5;
+ // An unexpected protocol failure occurred (so attempting to repair state).
+ INVOCATION_REASON_FAILURE_RECOVERY = 6;
+ // A new account has been added to the device.
+ INVOCATION_REASON_NEW_ACCOUNT = 7;
+ // An existing account on the device has been changed.
+ INVOCATION_REASON_CHANGED_ACCOUNT = 8;
+ // The user toggled the state of a feature (e.g. Smart Lock enabled via BT).
+ INVOCATION_REASON_FEATURE_TOGGLED = 9;
+ // A "push" from the server caused this action (e.g. a sync tickle).
+ INVOCATION_REASON_SERVER_INITIATED = 10;
+ // A local address change triggered this (e.g. GCM registration id changed).
+ INVOCATION_REASON_ADDRESS_CHANGE = 11;
+ // A software update has triggered this.
+ INVOCATION_REASON_SOFTWARE_UPDATE = 12;
+ // A manual action by the user triggered this (e.g. commands sent via adb).
+ INVOCATION_REASON_MANUAL = 13;
+}
+
+// Note: This is the same enum as securegcm.Type in securegcm.proto in the
+// server definitions. Renamed for clarity here.
+enum MessageType {
+ ENROLLMENT = 0;
+ TICKLE = 1;
+ TX_REQUEST = 2;
+ TX_REPLY = 3;
+ TX_SYNC_REQUEST = 4;
+ TX_SYNC_RESPONSE = 5;
+ TX_PING = 6;
+ DEVICE_INFO_UPDATE = 7;
+ TX_CANCEL_REQUEST = 8;
+ PROXIMITYAUTH_PAIRING = 10;
+ GCMV1_IDENTITY_ASSERTION = 11;
+
+ // Device-to-device communications are protected by an unauthenticated
+ // Diffie-Hellman exchange. The InitiatorHello message is simply the
+ // initiator's public DH key, and is not encoded as a SecureMessage, so
+ // it doesn't have a tag.
+ // The ResponderHello message (which is sent by the responder
+ // to the initiator), on the other hand, carries a payload that is protected
+ // by the derived shared key. It also contains the responder's
+ // public DH key. ResponderHelloAndPayload messages have the
+ // DEVICE_TO_DEVICE_RESPONDER_HELLO tag.
+ DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD = 12;
+
+ // Device-to-device communications are protected by an unauthenticated
+ // Diffie-Hellman exchange. Once the initiator and responder
+ // agree on a shared key (through Diffie-Hellman), they will use messages
+ // tagged with DEVICE_TO_DEVICE_MESSAGE to exchange data.
+ DEVICE_TO_DEVICE_MESSAGE = 13;
+
+ // Notification to let a device know it should contact a nearby device.
+ DEVICE_PROXIMITY_CALLBACK = 14;
+
+ // Device-to-device communications are protected by an unauthenticated
+ // Diffie-Hellman exchange. During device-to-device authentication, the first
+ // message from initiator (the challenge) is signed and put into the payload
+ // of the message sent back to the initiator.
+ UNLOCK_KEY_SIGNED_CHALLENGE = 15;
+}
+
+// GCM tickles related to registration management.
+enum RegistrationTickleType {
+ UNKNOWN_REGISTRATION_TICKLE_TYPE = 0;
+
+ // Force a re-enrollment with the server.
+ FORCE_ENROLLMENT = 1;
+
+ // Update enrollment information with the server. This could either be an
+ // authzen re-enrollment or a SyncTx.
+ UPDATE_ENROLLMENT = 2;
+
+ // Devices that receive this should sync the user's list of devices.
+ DEVICES_SYNC = 3;
+}
+
+// Requests to send a "tickle" requesting to sync all of a user's devices now
+message SendDeviceSyncTickleRequest {
+ // The type of tickle. *_ENROLLMENT tickles will only be sent to
+ // android devices. DEVICES_SYNC will be sent to chromebooks and android
+ // devices. UNKNOWN_TICKLE_TYPE or absent will be treated as
+ // UPDATE_ENROLLMENT.
+ // This field will also determine GCM parameters, such as
+ // TTL and collapse token.
+ optional RegistrationTickleType tickle_type = 3;
+
+ // Information about the requesting device and its platform.
+ optional DeviceClassifier device_classifier = 4;
+}
+
+message SendDeviceSyncTickleResponse {
+ // empty for now.
+}
+
+// Contains information needed to begin a device enrollment.
+message SetupEnrollmentInfo {
+ // Type of protocol this setup information was requested for.
+ optional string type = 1;
+
+ // A session identifier to be used for this enrollment session.
+ optional bytes enrollment_session_id = 2;
+
+ // A Diffie-Hellman public key used to perform a key exchange during
+ // enrollment.
+ optional bytes server_ephemeral_key = 3;
+}
+
+// Requests information needed to begin a device enrollment.
+message SetupEnrollmentRequest {
+ // Deprecated. See <code>application_id</code>.
+ optional string origin = 2;
+
+ // Type(s) of protocol supported by this enrolling device (e.g. "gcmV1").
+ repeated string types = 3;
+
+ // Indicates whether a legacy crypto suite must be used with this device.
+ optional bool use_legacy_crypto = 4;
+
+ // A URL describing which application facets this enrollment can be used (see
+ // http://go/appid).
+ optional string application_id = 5;
+
+ // An integer encoding the reason this enrollment was invoked (triggered).
+ // See InvocationReason enum for definitions.
+ optional int32 invocation_reason = 6 [default = 0];
+
+ // How many retries of this operation have happened thus far.
+ optional int32 retry_count = 7 [default = 0];
+
+ // Information about the requesting device and its platform.
+ optional DeviceClassifier device_classifier = 8;
+}
+
+// Contains information needed to begin a device enrollment.
+message SetupEnrollmentResponse {
+ // Should return OK if the request was well formed.
+ optional string status = 1;
+
+ // Information for each of the requested protocol <code>type</code>s.
+ repeated SetupEnrollmentInfo infos = 2;
+}
+
+// Used to enable or disable EasyUnlock features on a specified device, and also
+// causes other devices to sync the new EasyUnlock state.
+message ToggleEasyUnlockRequest {
+ // If true, Easy Unlock will be enabled for the device with public key equal
+ // to public_key. Otherwise, it will be disabled for that device.
+ optional bool enable = 1;
+
+ // Encoded public key of the device to enable/disable (here you must use the
+ // same exact encoding that was sent during device enrollment).
+ optional bytes public_key = 2;
+
+ // If true, EasyUnlock enabled state will be set to the value of "enable" for
+ // all of a user's devices. This is the same as calling the toggle RPC for
+ // every device. However, this removes the need for calling GetMyDevices, so
+ // it reduces network overhead. If this field is set "public_key" must not be
+ // set. NOTE: the case enable=true is not yet supported, so this option can
+ // only disable EasyUnlock for all devices.
+ optional bool apply_to_all = 3;
+
+ // Information about the requesting device and its platform.
+ optional DeviceClassifier device_classifier = 4;
+}
+
+message ToggleEasyUnlockResponse {
+ // empty for now.
+}
diff --git a/chromium/components/cryptauth/proto/securemessage.proto b/chromium/components/cryptauth/proto/securemessage.proto
new file mode 100644
index 00000000000..c086145a06a
--- /dev/null
+++ b/chromium/components/cryptauth/proto/securemessage.proto
@@ -0,0 +1,116 @@
+// 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.
+
+// Definitions related to the SecureMessage format, used by CryptAuth. Do not
+// edit unless transcribing from server definitions.
+syntax = "proto2";
+
+package securemessage;
+
+option optimize_for = LITE_RUNTIME;
+
+message SecureMessage {
+ // Must contain a HeaderAndBody message.
+ required bytes header_and_body = 1;
+ // Signature of header_and_body.
+ required bytes signature = 2;
+}
+
+// Supported "signature" schemes (both symmetric key and public key based).
+enum SigScheme {
+ HMAC_SHA256 = 1;
+ ECDSA_P256_SHA256 = 2;
+ // Not recommended -- use ECDSA_P256_SHA256 instead
+ RSA2048_SHA256 = 3;
+}
+
+// Supported encryption schemes.
+enum EncScheme {
+ // No encryption.
+ NONE = 1;
+ AES_256_CBC = 2;
+}
+
+message Header {
+ required SigScheme signature_scheme = 1;
+ required EncScheme encryption_scheme = 2;
+ // Identifies the verification key.
+ optional bytes verification_key_id = 3;
+ // Identifies the decryption key.
+ optional bytes decryption_key_id = 4;
+ // Encryption may use an IV.
+ optional bytes iv = 5;
+ // Arbitrary per-protocol public data, to be sent with the plain-text header.
+ optional bytes public_metadata = 6;
+ // The length of some associated data that is not sent in this SecureMessage,
+ // but which will be bound to the signature.
+ optional uint32 associated_data_length = 7 [default = 0];
+}
+
+message HeaderAndBody {
+ // Public data about this message (to be bound in the signature).
+ required Header header = 1;
+ // Payload data.
+ required bytes body = 2;
+}
+
+// A list of supported public key types.
+enum PublicKeyType {
+ EC_P256 = 1;
+ RSA2048 = 2;
+ // 2048-bit MODP group 14, from RFC 3526.
+ DH2048_MODP = 3;
+}
+
+// A convenience proto for encoding NIST P-256 elliptic curve public keys.
+message EcP256PublicKey {
+ // x and y are encoded in big-endian two's complement (slightly wasteful)
+ // Client MUST verify (x,y) is a valid point on NIST P256.
+ required bytes x = 1;
+ required bytes y = 2;
+}
+
+// A convenience proto for encoding RSA public keys with small exponents.
+message SimpleRsaPublicKey {
+ // Encoded in big-endian two's complement.
+ required bytes n = 1;
+ optional int32 e = 2 [default = 65537];
+}
+
+// A convenience proto for encoding Diffie-Hellman public keys,
+// for use only when Elliptic Curve based key exchanges are not possible.
+// (Note that the group parameters must be specified separately).
+message DhPublicKey {
+ // Big-endian two's complement encoded group element.
+ required bytes y = 1;
+}
+
+message GenericPublicKey {
+ required PublicKeyType type = 1;
+ optional EcP256PublicKey ec_p256_public_key = 2;
+ optional SimpleRsaPublicKey rsa2048_public_key = 3;
+ // Use only as a last resort.
+ optional DhPublicKey dh2048_public_key = 4;
+}
+
+// Used by protocols for communicating between a pair of devices.
+message DeviceToDeviceMessage {
+ // The payload of the message.
+ optional bytes message = 1;
+
+ // The sequence number of the message - must be increasing.
+ optional int32 sequence_number = 2;
+}
+
+// Sent as the first message from initiator to responder in an unauthenticated
+// Diffie-Hellman Key Exchange.
+message InitiatorHello {
+ optional GenericPublicKey public_dh_key = 1;
+}
+
+// Sent inside the header of the first message from the responder to the
+// initiator in an unauthenticated Diffie-Hellman Key Exchange.
+message ResponderHello {
+ optional GenericPublicKey public_dh_key = 1;
+}
diff --git a/chromium/components/cryptauth/remote_beacon_seed_fetcher.cc b/chromium/components/cryptauth/remote_beacon_seed_fetcher.cc
new file mode 100644
index 00000000000..c53a630d2b6
--- /dev/null
+++ b/chromium/components/cryptauth/remote_beacon_seed_fetcher.cc
@@ -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.
+
+#include "components/cryptauth/remote_beacon_seed_fetcher.h"
+
+#include "components/cryptauth/cryptauth_device_manager.h"
+
+namespace cryptauth {
+
+RemoteBeaconSeedFetcher::RemoteBeaconSeedFetcher(
+ const CryptAuthDeviceManager* device_manager)
+ : device_manager_(device_manager) {}
+
+RemoteBeaconSeedFetcher::~RemoteBeaconSeedFetcher() {}
+
+bool RemoteBeaconSeedFetcher::FetchSeedsForDevice(
+ const RemoteDevice& remote_device,
+ std::vector<BeaconSeed>* beacon_seeds_out) const {
+ if (remote_device.public_key.empty()) {
+ return false;
+ }
+
+ for(const auto& device_info : device_manager_->GetSyncedDevices()) {
+ if (device_info.public_key() == remote_device.public_key) {
+ if (device_info.beacon_seeds_size() == 0) {
+ return false;
+ }
+
+ beacon_seeds_out->clear();
+ for (int i = 0; i < device_info.beacon_seeds_size(); i++) {
+ beacon_seeds_out->push_back(device_info.beacon_seeds(i));
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/remote_beacon_seed_fetcher.h b/chromium/components/cryptauth/remote_beacon_seed_fetcher.h
new file mode 100644
index 00000000000..6eb3825f4d9
--- /dev/null
+++ b/chromium/components/cryptauth/remote_beacon_seed_fetcher.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_REMOTE_BEACON_SEED_FETCHER_H_
+#define COMPONENTS_CRYPTAUTH_REMOTE_BEACON_SEED_FETCHER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/remote_device.h"
+
+namespace cryptauth {
+
+class CryptAuthDeviceManager;
+
+// Fetches |BeaconSeed|s corresponding to a given |RemoteDevice|. Note that an
+// alternate solution would be to embed a list of |BeaconSeed|s in each
+// |RemoteDevice| object; however, because |BeaconSeed| objects take up almost
+// 1kB of memory apiece, that approach adds unnecessary memory overhead.
+class RemoteBeaconSeedFetcher {
+ public:
+ RemoteBeaconSeedFetcher(const CryptAuthDeviceManager* device_manager);
+ virtual ~RemoteBeaconSeedFetcher();
+
+ virtual bool FetchSeedsForDevice(
+ const RemoteDevice& remote_device,
+ std::vector<BeaconSeed>* beacon_seeds_out) const;
+
+ private:
+ // Not owned by this instance and must outlive it.
+ const CryptAuthDeviceManager* device_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteBeaconSeedFetcher);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_REMOTE_BEACON_SEED_FETCHER_H_
diff --git a/chromium/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc b/chromium/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc
new file mode 100644
index 00000000000..25cda2fcdd6
--- /dev/null
+++ b/chromium/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc
@@ -0,0 +1,173 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/remote_beacon_seed_fetcher.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/cryptauth_client.h"
+#include "components/cryptauth/cryptauth_device_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::StrictMock;
+using testing::Return;
+
+namespace cryptauth {
+
+namespace {
+
+const std::string fake_beacon_seed1_data = "fakeBeaconSeed1Data";
+const int64_t fake_beacon_seed1_start_ms = 1000L;
+const int64_t fake_beacon_seed1_end_ms = 2000L;
+
+const std::string fake_beacon_seed2_data = "fakeBeaconSeed2Data";
+const int64_t fake_beacon_seed2_start_ms = 2000L;
+const int64_t fake_beacon_seed2_end_ms = 3000L;
+
+const std::string fake_beacon_seed3_data = "fakeBeaconSeed3Data";
+const int64_t fake_beacon_seed3_start_ms = 1000L;
+const int64_t fake_beacon_seed3_end_ms = 2000L;
+
+const std::string fake_beacon_seed4_data = "fakeBeaconSeed4Data";
+const int64_t fake_beacon_seed4_start_ms = 2000L;
+const int64_t fake_beacon_seed4_end_ms = 3000L;
+
+const std::string public_key1 = "publicKey1";
+const std::string public_key2 = "publicKey2";
+
+class MockDeviceManager : public CryptAuthDeviceManager {
+ public:
+ MockDeviceManager() {}
+ ~MockDeviceManager() override {}
+
+ MOCK_CONST_METHOD0(GetSyncedDevices, std::vector<ExternalDeviceInfo>());
+};
+
+RemoteDevice CreateRemoteDevice(const std::string& public_key) {
+ RemoteDevice remote_device;
+ remote_device.public_key = public_key;
+ return remote_device;
+}
+
+ExternalDeviceInfo CreateFakeInfo1() {
+ BeaconSeed seed1;
+ seed1.set_data(fake_beacon_seed1_data);
+ seed1.set_start_time_millis(fake_beacon_seed1_start_ms);
+ seed1.set_end_time_millis(fake_beacon_seed1_end_ms);
+
+ BeaconSeed seed2;
+ seed2.set_data(fake_beacon_seed2_data);
+ seed2.set_start_time_millis(fake_beacon_seed2_start_ms);
+ seed2.set_end_time_millis(fake_beacon_seed2_end_ms);
+
+ ExternalDeviceInfo info1;
+ info1.set_public_key(public_key1);
+ info1.add_beacon_seeds()->CopyFrom(seed1);
+ info1.add_beacon_seeds()->CopyFrom(seed2);
+ return info1;
+}
+
+ExternalDeviceInfo CreateFakeInfo2() {
+ BeaconSeed seed3;
+ seed3.set_data(fake_beacon_seed3_data);
+ seed3.set_start_time_millis(fake_beacon_seed3_start_ms);
+ seed3.set_end_time_millis(fake_beacon_seed3_end_ms);
+
+ BeaconSeed seed4;
+ seed4.set_data(fake_beacon_seed4_data);
+ seed4.set_start_time_millis(fake_beacon_seed4_start_ms);
+ seed4.set_end_time_millis(fake_beacon_seed4_end_ms);
+
+ ExternalDeviceInfo info2;
+ info2.set_public_key(public_key2);
+ info2.add_beacon_seeds()->CopyFrom(seed3);
+ info2.add_beacon_seeds()->CopyFrom(seed4);
+ return info2;
+}
+
+} // namespace
+
+class CryptAuthRemoteBeaconSeedFetcherTest : public testing::Test {
+ protected:
+ CryptAuthRemoteBeaconSeedFetcherTest()
+ : fake_info1_(CreateFakeInfo1()), fake_info2_(CreateFakeInfo2()) {}
+
+ void SetUp() override {
+ mock_device_manager_ = base::MakeUnique<MockDeviceManager>();
+ fetcher_ = base::MakeUnique<StrictMock<RemoteBeaconSeedFetcher>>(
+ mock_device_manager_.get());
+ }
+
+ std::unique_ptr<RemoteBeaconSeedFetcher> fetcher_;
+ std::unique_ptr<MockDeviceManager> mock_device_manager_;
+
+ const ExternalDeviceInfo fake_info1_;
+ const ExternalDeviceInfo fake_info2_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthRemoteBeaconSeedFetcherTest);
+};
+
+TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestRemoteDeviceWithNoPublicKey) {
+ RemoteDevice device = CreateRemoteDevice("");
+
+ std::vector<BeaconSeed> seeds;
+ EXPECT_FALSE(fetcher_->FetchSeedsForDevice(device, &seeds));
+}
+
+TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestNoSyncedDevices) {
+ RemoteDevice device = CreateRemoteDevice(public_key1);
+
+ EXPECT_CALL(*mock_device_manager_, GetSyncedDevices())
+ .WillOnce(Return(std::vector<ExternalDeviceInfo>()));
+
+ std::vector<BeaconSeed> seeds;
+ EXPECT_FALSE(fetcher_->FetchSeedsForDevice(device, &seeds));
+}
+
+TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestDeviceHasDifferentPublicKey) {
+ // A public key which is different from the public keys of all of the synced
+ // devices.
+ RemoteDevice device = CreateRemoteDevice("differentPublicKey");
+
+ std::vector<ExternalDeviceInfo> device_infos = {fake_info1_, fake_info2_};
+ EXPECT_CALL(*mock_device_manager_, GetSyncedDevices())
+ .WillOnce(Return(device_infos));
+
+ std::vector<BeaconSeed> seeds;
+ EXPECT_FALSE(fetcher_->FetchSeedsForDevice(device, &seeds));
+}
+
+TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestSuccess) {
+ RemoteDevice device1 = CreateRemoteDevice(public_key1);
+ RemoteDevice device2 = CreateRemoteDevice(public_key2);
+
+ std::vector<ExternalDeviceInfo> device_infos = {fake_info1_, fake_info2_};
+ EXPECT_CALL(*mock_device_manager_, GetSyncedDevices())
+ .Times(2)
+ .WillRepeatedly(Return(device_infos));
+
+ std::vector<BeaconSeed> seeds1;
+ ASSERT_TRUE(fetcher_->FetchSeedsForDevice(device1, &seeds1));
+ ASSERT_EQ(static_cast<size_t>(2), seeds1.size());
+ EXPECT_EQ(fake_beacon_seed1_data, seeds1[0].data());
+ EXPECT_EQ(fake_beacon_seed1_start_ms, seeds1[0].start_time_millis());
+ EXPECT_EQ(fake_beacon_seed1_end_ms, seeds1[0].end_time_millis());
+ EXPECT_EQ(fake_beacon_seed2_data, seeds1[1].data());
+ EXPECT_EQ(fake_beacon_seed2_start_ms, seeds1[1].start_time_millis());
+ EXPECT_EQ(fake_beacon_seed2_end_ms, seeds1[1].end_time_millis());
+
+ std::vector<BeaconSeed> seeds2;
+ ASSERT_TRUE(fetcher_->FetchSeedsForDevice(device2, &seeds2));
+ ASSERT_EQ(static_cast<size_t>(2), seeds2.size());
+ EXPECT_EQ(fake_beacon_seed3_data, seeds2[0].data());
+ EXPECT_EQ(fake_beacon_seed3_start_ms, seeds2[0].start_time_millis());
+ EXPECT_EQ(fake_beacon_seed3_end_ms, seeds2[0].end_time_millis());
+ EXPECT_EQ(fake_beacon_seed4_data, seeds2[1].data());
+ EXPECT_EQ(fake_beacon_seed4_start_ms, seeds2[1].start_time_millis());
+ EXPECT_EQ(fake_beacon_seed4_end_ms, seeds2[1].end_time_millis());
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/remote_device.cc b/chromium/components/cryptauth/remote_device.cc
new file mode 100644
index 00000000000..7f30144312f
--- /dev/null
+++ b/chromium/components/cryptauth/remote_device.cc
@@ -0,0 +1,70 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/remote_device.h"
+
+#include "base/base64.h"
+
+namespace cryptauth {
+
+RemoteDevice::RemoteDevice() : bluetooth_type(BLUETOOTH_CLASSIC) {}
+
+RemoteDevice::RemoteDevice(const std::string& user_id,
+ const std::string& name,
+ const std::string& public_key,
+ BluetoothType bluetooth_type,
+ const std::string& bluetooth_address,
+ const std::string& persistent_symmetric_key,
+ std::string sign_in_challenge)
+ : user_id(user_id),
+ name(name),
+ public_key(public_key),
+ bluetooth_type(bluetooth_type),
+ bluetooth_address(bluetooth_address),
+ persistent_symmetric_key(persistent_symmetric_key),
+ sign_in_challenge(sign_in_challenge) {}
+
+RemoteDevice::RemoteDevice(const RemoteDevice& other) = default;
+
+RemoteDevice::~RemoteDevice() {}
+
+std::string RemoteDevice::GetDeviceId() const {
+ std::string to_return;
+ base::Base64Encode(public_key, &to_return);
+ return to_return;
+}
+
+std::string RemoteDevice::GetTruncatedDeviceIdForLogs() const {
+ return RemoteDevice::TruncateDeviceIdForLogs(GetDeviceId());
+}
+
+bool RemoteDevice::operator==(const RemoteDevice& other) const {
+ return user_id == other.user_id
+ && name == other.name
+ && public_key == other.public_key
+ && bluetooth_type == other.bluetooth_type
+ && bluetooth_address == other.bluetooth_address
+ && persistent_symmetric_key == other.persistent_symmetric_key
+ && sign_in_challenge == other.sign_in_challenge;
+}
+
+bool RemoteDevice::operator<(const RemoteDevice& other) const {
+ // |public_key| is the only field guaranteed to be set and is also unique to
+ // each RemoteDevice. However, since it can contain null bytes, use
+ // GetDeviceId(), which cannot contain null bytes, to compare devices.
+ return GetDeviceId().compare(other.GetDeviceId()) < 0;
+}
+
+// static
+std::string RemoteDevice::TruncateDeviceIdForLogs(const std::string& full_id) {
+ if (full_id.length() <= 10) {
+ return full_id;
+ }
+
+ return full_id.substr(0, 5)
+ + "..."
+ + full_id.substr(full_id.length() - 5, full_id.length());
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/remote_device.h b/chromium/components/cryptauth/remote_device.h
new file mode 100644
index 00000000000..e6e7731ca38
--- /dev/null
+++ b/chromium/components/cryptauth/remote_device.h
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_H_
+#define COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_H_
+
+#include <string>
+#include <vector>
+
+namespace cryptauth {
+
+struct RemoteDevice {
+ public:
+ enum BluetoothType { BLUETOOTH_CLASSIC, BLUETOOTH_LE };
+
+ std::string user_id;
+ std::string name;
+ std::string public_key;
+ BluetoothType bluetooth_type;
+ std::string bluetooth_address;
+ std::string persistent_symmetric_key;
+ std::string sign_in_challenge;
+
+ RemoteDevice();
+ RemoteDevice(const std::string& user_id,
+ const std::string& name,
+ const std::string& public_key,
+ BluetoothType bluetooth_type,
+ const std::string& bluetooth_address,
+ const std::string& persistent_symmetric_key,
+ std::string sign_in_challenge);
+ RemoteDevice(const RemoteDevice& other);
+ ~RemoteDevice();
+
+ // Returns a unique ID for the device.
+ std::string GetDeviceId() const;
+
+ // Returns a shortened device ID for the purpose of concise logging (device
+ // IDs are often so long that logs are difficult to read). Note that this
+ // ID is not guaranteed to be unique, so it should only be used for log.
+ std::string GetTruncatedDeviceIdForLogs() const;
+
+ bool operator==(const RemoteDevice& other) const;
+
+ // Compares devices via their public keys. Note that this function is
+ // necessary in order to use |RemoteDevice| as a key of a std::map.
+ bool operator<(const RemoteDevice& other) const;
+
+ // Static method for truncated device ID for logs.
+ static std::string TruncateDeviceIdForLogs(const std::string& full_id);
+};
+
+typedef std::vector<RemoteDevice> RemoteDeviceList;
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_H_
diff --git a/chromium/components/cryptauth/remote_device_test_util.cc b/chromium/components/cryptauth/remote_device_test_util.cc
new file mode 100644
index 00000000000..73fe0928ec8
--- /dev/null
+++ b/chromium/components/cryptauth/remote_device_test_util.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/cryptauth/remote_device_test_util.h"
+
+#include "base/base64.h"
+
+namespace cryptauth {
+
+std::vector<RemoteDevice> GenerateTestRemoteDevices(size_t num_to_create) {
+ std::vector<RemoteDevice> generated_devices;
+
+ for (size_t i = 0; i < num_to_create; i++) {
+ RemoteDevice device;
+ device.public_key = "publicKey" + std::to_string(i);
+ generated_devices.push_back(device);
+ }
+
+ return generated_devices;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/remote_device_test_util.h b/chromium/components/cryptauth/remote_device_test_util.h
new file mode 100644
index 00000000000..f3af23409e4
--- /dev/null
+++ b/chromium/components/cryptauth/remote_device_test_util.h
@@ -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.
+
+#ifndef COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_TEST_UTIL_H_
+#define COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_TEST_UTIL_H_
+
+#include <vector>
+
+#include "components/cryptauth/remote_device.h"
+
+namespace cryptauth {
+
+std::vector<RemoteDevice> GenerateTestRemoteDevices(size_t num_to_create);
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_TEST_UTIL_H_
diff --git a/chromium/components/cryptauth/secure_message_delegate.cc b/chromium/components/cryptauth/secure_message_delegate.cc
new file mode 100644
index 00000000000..4dd039dca0a
--- /dev/null
+++ b/chromium/components/cryptauth/secure_message_delegate.cc
@@ -0,0 +1,30 @@
+// 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/secure_message_delegate.h"
+
+namespace cryptauth {
+
+SecureMessageDelegate::SecureMessageDelegate() {
+}
+
+SecureMessageDelegate::~SecureMessageDelegate() {
+}
+
+SecureMessageDelegate::CreateOptions::CreateOptions() {
+}
+
+SecureMessageDelegate::CreateOptions::CreateOptions(
+ const CreateOptions& other) = default;
+
+SecureMessageDelegate::CreateOptions::~CreateOptions() {
+}
+
+SecureMessageDelegate::UnwrapOptions::UnwrapOptions() {
+}
+
+SecureMessageDelegate::UnwrapOptions::~UnwrapOptions() {
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/secure_message_delegate.h b/chromium/components/cryptauth/secure_message_delegate.h
new file mode 100644
index 00000000000..abd9ec73f77
--- /dev/null
+++ b/chromium/components/cryptauth/secure_message_delegate.h
@@ -0,0 +1,102 @@
+// 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_PROXIMITY_AUTH_SECURE_MESSAGE_DELEGATE_H_
+#define COMPONENTS_PROXIMITY_AUTH_SECURE_MESSAGE_DELEGATE_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "components/cryptauth/proto/securemessage.pb.h"
+
+namespace cryptauth {
+
+// Interface of delegate responsible for cryptographic operations based on the
+// secure message library. This interface is asynchronous as the current
+// implementation on ChromeOS communicates with a daemon process over IPC.
+class SecureMessageDelegate {
+ public:
+ // Fields specifying how to create a SecureMessage.
+ struct CreateOptions {
+ CreateOptions();
+ CreateOptions(const CreateOptions& other);
+ ~CreateOptions();
+
+ // The scheme used to encrypt the message.
+ securemessage::EncScheme encryption_scheme;
+ // The scheme used to sign the message.
+ securemessage::SigScheme signature_scheme;
+ // Additional data that is used as part of the signature computation but not
+ // included in the message contents.
+ std::string associated_data;
+ // Plain-text data included in the message header.
+ std::string public_metadata;
+ // Identifies the key to use for verifying the message signature.
+ std::string verification_key_id;
+ // Identifies the key to use for decrypting the message.
+ std::string decryption_key_id;
+ };
+
+ // Fields specifying how to unwrap a SecureMessage.
+ struct UnwrapOptions {
+ UnwrapOptions();
+ ~UnwrapOptions();
+
+ // The scheme used to decrypt the message.
+ securemessage::EncScheme encryption_scheme;
+ // The scheme used to verify the message signature.
+ securemessage::SigScheme signature_scheme;
+ // Additional data that is used as part of the signature computation but not
+ // included in the message contents.
+ std::string associated_data;
+ };
+
+ SecureMessageDelegate();
+ virtual ~SecureMessageDelegate();
+
+ // Generates a new asymmetric key pair.
+ typedef base::Callback<void(const std::string& public_key,
+ const std::string& private_key)>
+ GenerateKeyPairCallback;
+ virtual void GenerateKeyPair(const GenerateKeyPairCallback& callback) = 0;
+
+ // Derives a symmetric key from our private key and the remote device's
+ // public key.
+ typedef base::Callback<void(const std::string& derived_key)>
+ DeriveKeyCallback;
+ virtual void DeriveKey(const std::string& private_key,
+ const std::string& public_key,
+ const DeriveKeyCallback& callback) = 0;
+
+ // Creates a new secure message with a |payload| given the |key| and
+ // |create_options| specifying the cryptographic details.
+ // |callback| will be invoked with the serialized SecureMessage upon success
+ // or the empty string upon failure.
+ typedef base::Callback<void(const std::string& secure_message)>
+ CreateSecureMessageCallback;
+ virtual void CreateSecureMessage(
+ const std::string& payload,
+ const std::string& key,
+ const CreateOptions& create_options,
+ const CreateSecureMessageCallback& callback) = 0;
+
+ // Unwraps |secure_message| given the |key| and |unwrap_options| specifying
+ // the cryptographic details.
+ // |callback| will be invoked with true for the |verified| argument if the
+ // message was verified and decrypted successfully. The |payload| and
+ // |header| fields will be non-empty if the message was verified successfully.
+ typedef base::Callback<void(bool verified,
+ const std::string& payload,
+ const securemessage::Header& header)>
+ UnwrapSecureMessageCallback;
+ virtual void UnwrapSecureMessage(
+ const std::string& serialized_message,
+ const std::string& key,
+ const UnwrapOptions& unwrap_options,
+ const UnwrapSecureMessageCallback& callback) = 0;
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_PROXIMITY_AUTH_SECURE_MESSAGE_DELEGATE_H_
diff --git a/chromium/components/cryptauth/switches.cc b/chromium/components/cryptauth/switches.cc
new file mode 100644
index 00000000000..d5aec05d392
--- /dev/null
+++ b/chromium/components/cryptauth/switches.cc
@@ -0,0 +1,15 @@
+// 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/switches.h"
+
+namespace cryptauth {
+namespace switches {
+
+// Overrides the default URL for Google APIs (https://www.googleapis.com) used
+// by CryptAuth.
+const char kCryptAuthHTTPHost[] = "cryptauth-http-host";
+
+} // namespace switches
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/switches.h b/chromium/components/cryptauth/switches.h
new file mode 100644
index 00000000000..51fecb6fb39
--- /dev/null
+++ b/chromium/components/cryptauth/switches.h
@@ -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.
+
+#ifndef COMPONENTS_CRYPTAUTH_SWITCHES_H_
+#define COMPONENTS_CRYPTAUTH_SWITCHES_H_
+
+namespace cryptauth {
+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 kCryptAuthHTTPHost[];
+
+} // namespace switches
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_SWITCHES_H_
diff --git a/chromium/components/cryptauth/sync_scheduler.cc b/chromium/components/cryptauth/sync_scheduler.cc
new file mode 100644
index 00000000000..52892e2b326
--- /dev/null
+++ b/chromium/components/cryptauth/sync_scheduler.cc
@@ -0,0 +1,31 @@
+// 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/sync_scheduler.h"
+
+#include "components/proximity_auth/logging/logging.h"
+
+namespace cryptauth {
+
+SyncScheduler::SyncRequest::SyncRequest(
+ base::WeakPtr<SyncScheduler> sync_scheduler)
+ : sync_scheduler_(sync_scheduler), completed_(false) {
+}
+
+SyncScheduler::SyncRequest::~SyncRequest() {
+ if (!completed_)
+ PA_LOG(ERROR) << "SyncRequest destroyed but Complete() was never called";
+}
+
+void SyncScheduler::SyncRequest::OnDidComplete(bool success) {
+ if (sync_scheduler_) {
+ sync_scheduler_->OnSyncCompleted(success);
+ sync_scheduler_.reset();
+ completed_ = true;
+ } else {
+ PA_LOG(ERROR) << "SyncRequest completed, but SyncScheduler destroyed.";
+ }
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/sync_scheduler.h b/chromium/components/cryptauth/sync_scheduler.h
new file mode 100644
index 00000000000..df0fac8e26a
--- /dev/null
+++ b/chromium/components/cryptauth/sync_scheduler.h
@@ -0,0 +1,98 @@
+// 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_SYNC_SCHEDULER_H_
+#define COMPONENTS_CRYPTAUTH_SYNC_SCHEDULER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+
+namespace cryptauth {
+
+// Interface for scheduling the next CryptAuth sync (e.g. enrollment or device
+// sync). The scheduler has two different strategies affecting when to perform
+// the next operation:
+// PERIODIC_REFRESH: The last sync was made successfully, so we can wait a
+// relatively long time before making another sync.
+// AGGRESSIVE_RECOVERY: The last sync failed, so we try more aggressively to
+// make enrollment attempts with subsequent backoff for repeated
+// failures.
+// A random jitter is applied to each sync period to smooth qps to the server.
+class SyncScheduler {
+ public:
+ // The sync strategies mentioned in the class comments.
+ enum class Strategy { PERIODIC_REFRESH, AGGRESSIVE_RECOVERY };
+
+ // The states that the scheduler can be in.
+ enum class SyncState { NOT_STARTED, WAITING_FOR_REFRESH, SYNC_IN_PROGRESS };
+
+ // An instance is passed to the delegate when the scheduler fires for each
+ // sync attempt. The delegate should call |Complete()| when the sync succeeds
+ // or fails to resume the scheduler.
+ class SyncRequest {
+ public:
+ explicit SyncRequest(base::WeakPtr<SyncScheduler> sync_scheduler);
+ ~SyncRequest();
+
+ void OnDidComplete(bool success);
+
+ protected:
+ // The parent scheduler that dispatched this request.
+ base::WeakPtr<SyncScheduler> sync_scheduler_;
+
+ // True if |OnDidComplete()| has been called.
+ bool completed_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncRequest);
+ };
+
+ // Handles the actual sync operation.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called when the scheduler fires and requests a sync attempt. The delegate
+ // should call sync_request->Complete() when the request finishes.
+ virtual void OnSyncRequested(std::unique_ptr<SyncRequest> sync_request) = 0;
+ };
+
+ virtual ~SyncScheduler() {}
+
+ // Starts the scheduler with an aggressive recovery strategy if
+ // |strategy| is true; otherwise, it will be started with
+ // periodic refresh.
+ //
+ // |elapsed_time_since_last_sync| is the time since the last successful sync,
+ // so we can determine the duration of the first sync period. For example, the
+ // scheduler will immediately issue a sync request if the elapsed time is
+ // greater than the refresh period.
+ virtual void Start(const base::TimeDelta& elapsed_time_since_last_sync,
+ Strategy strategy) = 0;
+
+ // Cancels the current scheduled sync, and forces a sync immediately. Note
+ // that if this sync fails, the scheduler will adopt the AGGRESSIVE_RECOVERY
+ // strategy.
+ virtual void ForceSync() = 0;
+
+ // Returns the time until the next scheduled sync operation. If no sync is
+ // scheduled, a TimeDelta of zero will be returned.
+ virtual base::TimeDelta GetTimeToNextSync() const = 0;
+
+ // Returns the current sync strategy.
+ virtual Strategy GetStrategy() const = 0;
+
+ // Returns the current state of the scheduler.
+ virtual SyncState GetSyncState() const = 0;
+
+ protected:
+ // Called by SyncRequest instances when the sync completes.
+ virtual void OnSyncCompleted(bool success) = 0;
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_SYNC_SCHEDULER_H_
diff --git a/chromium/components/cryptauth/sync_scheduler_impl.cc b/chromium/components/cryptauth/sync_scheduler_impl.cc
new file mode 100644
index 00000000000..d073eddc03b
--- /dev/null
+++ b/chromium/components/cryptauth/sync_scheduler_impl.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/cryptauth/sync_scheduler_impl.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace cryptauth {
+
+namespace {
+
+// Returns a human readable string given a |time_delta|.
+std::string TimeDeltaToString(const base::TimeDelta& time_delta) {
+ if (time_delta.InDays() > 0)
+ return base::StringPrintf("%d days", time_delta.InDays());
+
+ if (time_delta.InHours() > 0)
+ return base::StringPrintf("%d hours", time_delta.InHours());
+
+ if (time_delta.InMinutes() > 0)
+ return base::StringPrintf("%d minutes", time_delta.InMinutes());
+
+ return base::StringPrintf("%d seconds",
+ base::saturated_cast<int>(time_delta.InSeconds()));
+}
+
+} // namespace
+
+SyncSchedulerImpl::SyncSchedulerImpl(Delegate* delegate,
+ base::TimeDelta refresh_period,
+ base::TimeDelta base_recovery_period,
+ double max_jitter_ratio,
+ const std::string& scheduler_name)
+ : delegate_(delegate),
+ refresh_period_(refresh_period),
+ base_recovery_period_(base_recovery_period),
+ max_jitter_ratio_(max_jitter_ratio),
+ scheduler_name_(scheduler_name),
+ strategy_(Strategy::PERIODIC_REFRESH),
+ sync_state_(SyncState::NOT_STARTED),
+ failure_count_(0),
+ weak_ptr_factory_(this) {
+}
+
+SyncSchedulerImpl::~SyncSchedulerImpl() {
+}
+
+void SyncSchedulerImpl::Start(
+ const base::TimeDelta& elapsed_time_since_last_sync,
+ Strategy strategy) {
+ strategy_ = strategy;
+ sync_state_ = SyncState::WAITING_FOR_REFRESH;
+ // We reset the failure backoff when the scheduler is started again, as the
+ // configuration that caused the previous attempts to fail most likely won't
+ // be present after a restart.
+ if (strategy_ == Strategy::AGGRESSIVE_RECOVERY)
+ failure_count_ = 1;
+
+ // To take into account the time waited when the system is powered off, we
+ // subtract the time elapsed with a normal sync period to the initial time
+ // to wait.
+ base::TimeDelta sync_delta =
+ GetJitteredPeriod() - elapsed_time_since_last_sync;
+
+ // The elapsed time may be negative if the system clock is changed. In this
+ // case, we immediately schedule a sync.
+ base::TimeDelta zero_delta = base::TimeDelta::FromSeconds(0);
+ if (elapsed_time_since_last_sync < zero_delta || sync_delta < zero_delta)
+ sync_delta = zero_delta;
+
+ ScheduleNextSync(sync_delta);
+}
+
+void SyncSchedulerImpl::ForceSync() {
+ OnTimerFired();
+}
+
+base::TimeDelta SyncSchedulerImpl::GetTimeToNextSync() const {
+ if (!timer_)
+ return base::TimeDelta::FromSeconds(0);
+ return timer_->GetCurrentDelay();
+}
+
+SyncScheduler::Strategy SyncSchedulerImpl::GetStrategy() const {
+ return strategy_;
+}
+
+SyncScheduler::SyncState SyncSchedulerImpl::GetSyncState() const {
+ return sync_state_;
+}
+
+void SyncSchedulerImpl::OnTimerFired() {
+ timer_.reset();
+ if (strategy_ == Strategy::PERIODIC_REFRESH) {
+ PA_LOG(INFO) << "Timer fired for periodic refresh, making request...";
+ sync_state_ = SyncState::SYNC_IN_PROGRESS;
+ } else if (strategy_ == Strategy::AGGRESSIVE_RECOVERY) {
+ PA_LOG(INFO) << "Timer fired for aggressive recovery, making request...";
+ sync_state_ = SyncState::SYNC_IN_PROGRESS;
+ } else {
+ NOTREACHED();
+ return;
+ }
+
+ delegate_->OnSyncRequested(
+ base::MakeUnique<SyncRequest>(weak_ptr_factory_.GetWeakPtr()));
+}
+
+std::unique_ptr<base::Timer> SyncSchedulerImpl::CreateTimer() {
+ bool retain_user_task = false;
+ bool is_repeating = false;
+ return base::MakeUnique<base::Timer>(retain_user_task, is_repeating);
+}
+
+void SyncSchedulerImpl::ScheduleNextSync(const base::TimeDelta& sync_delta) {
+ if (sync_state_ != SyncState::WAITING_FOR_REFRESH) {
+ PA_LOG(ERROR) << "Unexpected state when scheduling next sync: sync_state="
+ << static_cast<int>(sync_state_);
+ return;
+ }
+
+ bool is_aggressive_recovery = (strategy_ == Strategy::AGGRESSIVE_RECOVERY);
+ PA_LOG(INFO) << "Scheduling next sync for " << scheduler_name_ << ":\n"
+ << " Strategy: " << (is_aggressive_recovery
+ ? "Aggressive Recovery"
+ : "Periodic Refresh") << "\n"
+ << " Time Delta: " << TimeDeltaToString(sync_delta)
+ << (is_aggressive_recovery
+ ? base::StringPrintf(
+ "\n Previous Failures: %d",
+ base::saturated_cast<int>(failure_count_))
+ : "");
+
+ timer_ = CreateTimer();
+ timer_->Start(FROM_HERE, sync_delta,
+ base::Bind(&SyncSchedulerImpl::OnTimerFired,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SyncSchedulerImpl::OnSyncCompleted(bool success) {
+ if (sync_state_ != SyncState::SYNC_IN_PROGRESS) {
+ PA_LOG(ERROR) << "Unexpected state when sync completed: sync_state="
+ << static_cast<int>(sync_state_)
+ << ", strategy_=" << static_cast<int>(strategy_);
+ return;
+ }
+ sync_state_ = SyncState::WAITING_FOR_REFRESH;
+
+ if (success) {
+ strategy_ = Strategy::PERIODIC_REFRESH;
+ failure_count_ = 0;
+ } else {
+ strategy_ = Strategy::AGGRESSIVE_RECOVERY;
+ ++failure_count_;
+ }
+
+ ScheduleNextSync(GetJitteredPeriod());
+}
+
+base::TimeDelta SyncSchedulerImpl::GetJitteredPeriod() {
+ double jitter = 2 * max_jitter_ratio_ * (base::RandDouble() - 0.5);
+ base::TimeDelta period = GetPeriod();
+ base::TimeDelta jittered_time_delta = period + (period * jitter);
+ if (jittered_time_delta.InMilliseconds() < 0)
+ jittered_time_delta = base::TimeDelta::FromMilliseconds(0);
+ return jittered_time_delta;
+}
+
+base::TimeDelta SyncSchedulerImpl::GetPeriod() {
+ if (strategy_ == Strategy::PERIODIC_REFRESH) {
+ return refresh_period_;
+ } else if (strategy_ == Strategy::AGGRESSIVE_RECOVERY && failure_count_ > 0) {
+ // The backoff for each consecutive failure is exponentially doubled until
+ // it is equal to the normal refresh period.
+ // Note: |backoff_factor| may evaulate to INF if |failure_count_| is large,
+ // but multiplication operations for TimeDelta objects are saturated.
+ double backoff_factor = pow(2, failure_count_ - 1);
+ base::TimeDelta backoff_period = base_recovery_period_ * backoff_factor;
+ return backoff_period < refresh_period_ ? backoff_period : refresh_period_;
+ } else {
+ PA_LOG(ERROR) << "Error getting period for strategy: "
+ << static_cast<int>(strategy_);
+ return base::TimeDelta();
+ }
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/sync_scheduler_impl.h b/chromium/components/cryptauth/sync_scheduler_impl.h
new file mode 100644
index 00000000000..3d61bb0015f
--- /dev/null
+++ b/chromium/components/cryptauth/sync_scheduler_impl.h
@@ -0,0 +1,104 @@
+// 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_SYNC_SCHEDULER_IMPL_H_
+#define COMPONENTS_CRYPTAUTH_SYNC_SCHEDULER_IMPL_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/timer/timer.h"
+#include "components/cryptauth/sync_scheduler.h"
+
+namespace cryptauth {
+
+// Implementation of SyncScheduler.
+class SyncSchedulerImpl : public SyncScheduler {
+ public:
+ // Creates the scheduler:
+ // |delegate|: Handles sync requests and must outlive the scheduler.
+ // |refresh_period|: The time to wait for the PERIODIC_REFRESH strategy.
+ // |base_recovery_period|: The initial time to wait for the
+ // AGGRESSIVE_RECOVERY strategy. The time delta is increased for each
+ // subsequent failure.
+ // |max_jitter_ratio|: The maximum ratio that the time to next sync can be
+ // jittered (both positively and negatively).
+ // |scheduler_name|: The name of the scheduler for debugging purposes.
+ SyncSchedulerImpl(Delegate* delegate,
+ base::TimeDelta refresh_period,
+ base::TimeDelta base_recovery_period,
+ double max_jitter_ratio,
+ const std::string& scheduler_name);
+
+ ~SyncSchedulerImpl() override;
+
+ // SyncScheduler:
+ void Start(const base::TimeDelta& elapsed_time_since_last_sync,
+ Strategy strategy) override;
+ void ForceSync() override;
+ base::TimeDelta GetTimeToNextSync() const override;
+ Strategy GetStrategy() const override;
+ SyncState GetSyncState() const override;
+
+ protected:
+ // Creates and returns a base::Timer object. Exposed for testing.
+ virtual std::unique_ptr<base::Timer> CreateTimer();
+
+ private:
+ // SyncScheduler:
+ void OnSyncCompleted(bool success) override;
+
+ // Called when |timer_| is fired.
+ void OnTimerFired();
+
+ // Schedules |timer_| for the next sync request.
+ void ScheduleNextSync(const base::TimeDelta& sync_delta);
+
+ // Adds a random jitter to the value of GetPeriod(). The returned
+ // TimeDelta will be clamped to be non-negative.
+ base::TimeDelta GetJitteredPeriod();
+
+ // Returns the time to wait for the current strategy.
+ base::TimeDelta GetPeriod();
+
+ // The delegate handling sync requests when they are fired.
+ Delegate* const delegate_;
+
+ // The time to wait until the next refresh when the last sync attempt was
+ // successful.
+ const base::TimeDelta refresh_period_;
+
+ // The base recovery period for the AGGRESSIVE_RECOVERY strategy before
+ // backoffs are applied.
+ const base::TimeDelta base_recovery_period_;
+
+ // The maximum percentage (both positively and negatively) that the time to
+ // wait between each sync request is jittered. The jitter is randomly applied
+ // to each period so we can avoid synchronous calls to the server.
+ const double max_jitter_ratio_;
+
+ // The name of the scheduler, used for debugging purposes.
+ const std::string scheduler_name_;
+
+ // The current strategy of the scheduler.
+ Strategy strategy_;
+
+ // The current state of the scheduler.
+ SyncState sync_state_;
+
+ // The number of failed syncs made in a row. Once a sync request succeeds,
+ // this counter is reset.
+ size_t failure_count_;
+
+ // Timer firing for the next sync request.
+ std::unique_ptr<base::Timer> timer_;
+
+ base::WeakPtrFactory<SyncSchedulerImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncSchedulerImpl);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_SYNC_SCHEDULER_IMPL_H_
diff --git a/chromium/components/cryptauth/sync_scheduler_impl_unittest.cc b/chromium/components/cryptauth/sync_scheduler_impl_unittest.cc
new file mode 100644
index 00000000000..77b037c5da3
--- /dev/null
+++ b/chromium/components/cryptauth/sync_scheduler_impl_unittest.cc
@@ -0,0 +1,292 @@
+// 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/sync_scheduler_impl.h"
+
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/timer/mock_timer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cryptauth {
+
+using Strategy = SyncScheduler::Strategy;
+using SyncState = SyncScheduler::SyncState;
+
+namespace {
+
+// Constants configuring the the scheduler.
+const int kElapsedTimeDays = 40;
+const int kRefreshPeriodDays = 30;
+const int kRecoveryPeriodSeconds = 10;
+const double kMaxJitterPercentage = 0.1;
+const char kTestSchedulerName[] = "TestSyncSchedulerImpl";
+
+// Returns true if |jittered_time_delta| is within the range of a jittered
+// |base_time_delta| with a maximum of |max_jitter_ratio|.
+bool IsTimeDeltaWithinJitter(const base::TimeDelta& base_time_delta,
+ const base::TimeDelta& jittered_time_delta,
+ double max_jitter_ratio) {
+ if (base_time_delta.is_zero())
+ return jittered_time_delta.is_zero();
+
+ base::TimeDelta difference =
+ (jittered_time_delta - base_time_delta).magnitude();
+ double percentage_of_base =
+ difference.InMillisecondsF() / base_time_delta.InMillisecondsF();
+ return percentage_of_base < max_jitter_ratio;
+}
+
+// Test harness for the SyncSchedulerImpl to create MockTimers.
+class TestSyncSchedulerImpl : public SyncSchedulerImpl {
+ public:
+ TestSyncSchedulerImpl(Delegate* delegate,
+ base::TimeDelta refresh_period,
+ base::TimeDelta recovery_period,
+ double max_jitter_ratio)
+ : SyncSchedulerImpl(delegate,
+ refresh_period,
+ recovery_period,
+ max_jitter_ratio,
+ kTestSchedulerName) {}
+
+ ~TestSyncSchedulerImpl() override {}
+
+ base::MockTimer* timer() { return mock_timer_; }
+
+ private:
+ std::unique_ptr<base::Timer> CreateTimer() override {
+ bool retain_user_task = false;
+ bool is_repeating = false;
+ mock_timer_ = new base::MockTimer(retain_user_task, is_repeating);
+ return base::WrapUnique(mock_timer_);
+ }
+
+ // A timer instance for testing. Owned by the parent scheduler.
+ base::MockTimer* mock_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestSyncSchedulerImpl);
+};
+
+} // namespace
+
+class CryptAuthSyncSchedulerImplTest : public testing::Test,
+ public SyncSchedulerImpl::Delegate {
+ protected:
+ CryptAuthSyncSchedulerImplTest()
+ : refresh_period_(base::TimeDelta::FromDays(kRefreshPeriodDays)),
+ base_recovery_period_(
+ base::TimeDelta::FromSeconds(kRecoveryPeriodSeconds)),
+ zero_elapsed_time_(base::TimeDelta::FromSeconds(0)),
+ scheduler_(new TestSyncSchedulerImpl(this,
+ refresh_period_,
+ base_recovery_period_,
+ 0)) {}
+
+ ~CryptAuthSyncSchedulerImplTest() override {}
+
+ void OnSyncRequested(
+ std::unique_ptr<SyncScheduler::SyncRequest> sync_request) override {
+ sync_request_ = std::move(sync_request);
+ }
+
+ base::MockTimer* timer() { return scheduler_->timer(); }
+
+ // The time deltas used to configure |scheduler_|.
+ base::TimeDelta refresh_period_;
+ base::TimeDelta base_recovery_period_;
+ base::TimeDelta zero_elapsed_time_;
+
+ // The scheduler instance under test.
+ std::unique_ptr<TestSyncSchedulerImpl> scheduler_;
+
+ std::unique_ptr<SyncScheduler::SyncRequest> sync_request_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthSyncSchedulerImplTest);
+};
+
+TEST_F(CryptAuthSyncSchedulerImplTest, ForceSyncSuccess) {
+ scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH);
+ EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy());
+ EXPECT_EQ(SyncState::WAITING_FOR_REFRESH, scheduler_->GetSyncState());
+
+ scheduler_->ForceSync();
+ EXPECT_EQ(SyncState::SYNC_IN_PROGRESS, scheduler_->GetSyncState());
+ EXPECT_TRUE(sync_request_);
+ sync_request_->OnDidComplete(true);
+ EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy());
+ EXPECT_EQ(SyncState::WAITING_FOR_REFRESH, scheduler_->GetSyncState());
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, ForceSyncFailure) {
+ scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH);
+ EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy());
+
+ scheduler_->ForceSync();
+ EXPECT_TRUE(sync_request_);
+ sync_request_->OnDidComplete(false);
+ EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy());
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, PeriodicRefreshSuccess) {
+ EXPECT_EQ(SyncState::NOT_STARTED, scheduler_->GetSyncState());
+ scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH);
+ EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy());
+
+ EXPECT_EQ(refresh_period_, timer()->GetCurrentDelay());
+ timer()->Fire();
+ EXPECT_EQ(SyncState::SYNC_IN_PROGRESS, scheduler_->GetSyncState());
+ ASSERT_TRUE(sync_request_.get());
+
+ sync_request_->OnDidComplete(true);
+ EXPECT_EQ(SyncState::WAITING_FOR_REFRESH, scheduler_->GetSyncState());
+ EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy());
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, PeriodicRefreshFailure) {
+ scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH);
+ EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy());
+ timer()->Fire();
+ sync_request_->OnDidComplete(false);
+ EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy());
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, AggressiveRecoverySuccess) {
+ scheduler_->Start(zero_elapsed_time_, Strategy::AGGRESSIVE_RECOVERY);
+ EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy());
+
+ EXPECT_EQ(base_recovery_period_, timer()->GetCurrentDelay());
+ timer()->Fire();
+ EXPECT_EQ(SyncState::SYNC_IN_PROGRESS, scheduler_->GetSyncState());
+ ASSERT_TRUE(sync_request_.get());
+
+ sync_request_->OnDidComplete(true);
+ EXPECT_EQ(SyncState::WAITING_FOR_REFRESH, scheduler_->GetSyncState());
+ EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy());
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, AggressiveRecoveryFailure) {
+ scheduler_->Start(zero_elapsed_time_, Strategy::AGGRESSIVE_RECOVERY);
+
+ timer()->Fire();
+ sync_request_->OnDidComplete(false);
+ EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy());
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, AggressiveRecoveryBackOff) {
+ scheduler_->Start(zero_elapsed_time_, Strategy::AGGRESSIVE_RECOVERY);
+ base::TimeDelta last_recovery_period = base::TimeDelta::FromSeconds(0);
+
+ for (int i = 0; i < 20; ++i) {
+ timer()->Fire();
+ EXPECT_EQ(SyncState::SYNC_IN_PROGRESS, scheduler_->GetSyncState());
+ sync_request_->OnDidComplete(false);
+ EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy());
+ EXPECT_EQ(SyncState::WAITING_FOR_REFRESH, scheduler_->GetSyncState());
+
+ base::TimeDelta recovery_period = scheduler_->GetTimeToNextSync();
+ EXPECT_LE(last_recovery_period, recovery_period);
+ last_recovery_period = recovery_period;
+ }
+
+ // Backoffs should rapidly converge to the normal refresh period.
+ EXPECT_EQ(refresh_period_, last_recovery_period);
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, RefreshFailureRecoverySuccess) {
+ scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH);
+ EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy());
+
+ timer()->Fire();
+ sync_request_->OnDidComplete(false);
+ EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy());
+
+ timer()->Fire();
+ sync_request_->OnDidComplete(true);
+ EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy());
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, SyncImmediatelyForPeriodicRefresh) {
+ scheduler_->Start(base::TimeDelta::FromDays(kElapsedTimeDays),
+ Strategy::PERIODIC_REFRESH);
+ EXPECT_TRUE(scheduler_->GetTimeToNextSync().is_zero());
+ EXPECT_TRUE(timer()->GetCurrentDelay().is_zero());
+ timer()->Fire();
+ EXPECT_TRUE(sync_request_);
+
+ EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy());
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest,
+ SyncImmediatelyForAggressiveRecovery) {
+ scheduler_->Start(base::TimeDelta::FromDays(kElapsedTimeDays),
+ Strategy::AGGRESSIVE_RECOVERY);
+ EXPECT_TRUE(scheduler_->GetTimeToNextSync().is_zero());
+ EXPECT_TRUE(timer()->GetCurrentDelay().is_zero());
+ timer()->Fire();
+ EXPECT_TRUE(sync_request_);
+
+ EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy());
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, InitialSyncShorterByElapsedTime) {
+ base::TimeDelta elapsed_time = base::TimeDelta::FromDays(2);
+ scheduler_->Start(elapsed_time, Strategy::PERIODIC_REFRESH);
+ EXPECT_EQ(refresh_period_ - elapsed_time, scheduler_->GetTimeToNextSync());
+ timer()->Fire();
+ EXPECT_TRUE(sync_request_);
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, PeriodicRefreshJitter) {
+ scheduler_.reset(new TestSyncSchedulerImpl(
+ this, refresh_period_, base_recovery_period_, kMaxJitterPercentage));
+
+ scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH);
+
+ base::TimeDelta cumulative_jitter = base::TimeDelta::FromSeconds(0);
+ for (int i = 0; i < 10; ++i) {
+ base::TimeDelta next_sync_delta = scheduler_->GetTimeToNextSync();
+ cumulative_jitter += (next_sync_delta - refresh_period_).magnitude();
+ EXPECT_TRUE(IsTimeDeltaWithinJitter(refresh_period_, next_sync_delta,
+ kMaxJitterPercentage));
+ timer()->Fire();
+ sync_request_->OnDidComplete(true);
+ }
+
+ // The probablility that all periods are randomly equal to |refresh_period_|
+ // is so low that we would expect the heat death of the universe before this
+ // test flakes.
+ EXPECT_FALSE(cumulative_jitter.is_zero());
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, JitteredTimeDeltaIsNonNegative) {
+ base::TimeDelta zero_delta = base::TimeDelta::FromSeconds(0);
+ double max_jitter_ratio = 1;
+ scheduler_.reset(new TestSyncSchedulerImpl(this, zero_delta, zero_delta,
+ max_jitter_ratio));
+ scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH);
+
+ for (int i = 0; i < 10; ++i) {
+ base::TimeDelta next_sync_delta = scheduler_->GetTimeToNextSync();
+ EXPECT_GE(zero_delta, next_sync_delta);
+ EXPECT_TRUE(
+ IsTimeDeltaWithinJitter(zero_delta, next_sync_delta, max_jitter_ratio));
+ timer()->Fire();
+ sync_request_->OnDidComplete(true);
+ }
+}
+
+TEST_F(CryptAuthSyncSchedulerImplTest, StartWithNegativeElapsedTime) {
+ // This could happen in rare cases where the system clock changes.
+ scheduler_->Start(base::TimeDelta::FromDays(-1000),
+ Strategy::PERIODIC_REFRESH);
+
+ base::TimeDelta zero_delta = base::TimeDelta::FromSeconds(0);
+ EXPECT_EQ(zero_delta, scheduler_->GetTimeToNextSync());
+ EXPECT_EQ(zero_delta, timer()->GetCurrentDelay());
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/wire_message.cc b/chromium/components/cryptauth/wire_message.cc
new file mode 100644
index 00000000000..b91b9435589
--- /dev/null
+++ b/chromium/components/cryptauth/wire_message.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/cryptauth/wire_message.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/base64url.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "components/proximity_auth/logging/logging.h"
+
+// The wire messages have a simple format:
+// [ message version ] [ body length ] [ JSON body ]
+// 1 byte 2 bytes body length
+// The JSON body contains two fields: an optional permit_id field and a required
+// data field.
+
+namespace cryptauth {
+namespace {
+
+// The length of the message header, in bytes.
+const size_t kHeaderLength = 3;
+
+// The protocol version of the message format.
+const int kMessageFormatVersionThree = 3;
+
+const char kPayloadKey[] = "payload";
+const char kFeatureKey[] = "feature";
+
+// The default feature value. This is the default for backward compatibility
+// reasons; previously, the protocol did not transmit the feature in the
+// message, but because EasyUnlock was the only feature used, it didn't matter.
+// So, if a message is received without a feature, it is assumed to be
+// EasyUnlock by default.
+const char kDefaultFeature[] = "easy_unlock";
+
+// Parses the |serialized_message|'s header. Returns |true| iff the message has
+// a valid header, is complete, and is well-formed according to the header. Sets
+// |is_incomplete_message| to true iff the message does not have enough data to
+// parse the header, or if the message length encoded in the message header
+// exceeds the size of the |serialized_message|.
+bool ParseHeader(const std::string& serialized_message,
+ bool* is_incomplete_message) {
+ *is_incomplete_message = false;
+ if (serialized_message.size() < kHeaderLength) {
+ *is_incomplete_message = true;
+ return false;
+ }
+
+ static_assert(kHeaderLength > 2, "kHeaderLength too small");
+ size_t version = serialized_message[0];
+ if (version != kMessageFormatVersionThree) {
+ PA_LOG(WARNING) << "Error: Invalid message version. Got " << version
+ << ", expected " << kMessageFormatVersionThree;
+ return false;
+ }
+
+ uint16_t expected_body_length =
+ (static_cast<uint8_t>(serialized_message[1]) << 8) |
+ (static_cast<uint8_t>(serialized_message[2]) << 0);
+ size_t expected_message_length = kHeaderLength + expected_body_length;
+ if (serialized_message.size() < expected_message_length) {
+ *is_incomplete_message = true;
+ return false;
+ }
+ if (serialized_message.size() != expected_message_length) {
+ PA_LOG(WARNING) << "Error: Invalid message length. Got "
+ << serialized_message.size() << ", expected "
+ << expected_message_length;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+WireMessage::~WireMessage() {}
+
+// static
+std::unique_ptr<WireMessage> WireMessage::Deserialize(
+ const std::string& serialized_message,
+ bool* is_incomplete_message) {
+ if (!ParseHeader(serialized_message, is_incomplete_message))
+ return nullptr;
+
+ std::unique_ptr<base::Value> body_value =
+ base::JSONReader::Read(serialized_message.substr(kHeaderLength));
+ if (!body_value || !body_value->IsType(base::Value::Type::DICTIONARY)) {
+ PA_LOG(WARNING) << "Error: Unable to parse message as JSON.";
+ return nullptr;
+ }
+
+ base::DictionaryValue* body;
+ bool success = body_value->GetAsDictionary(&body);
+ DCHECK(success);
+
+ std::string payload_base64;
+ if (!body->GetString(kPayloadKey, &payload_base64) ||
+ payload_base64.empty()) {
+ PA_LOG(WARNING) << "Error: Missing payload.";
+ return nullptr;
+ }
+
+ std::string payload;
+ if (!base::Base64UrlDecode(payload_base64,
+ base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+ &payload)) {
+ PA_LOG(WARNING) << "Error: Invalid base64 encoding for payload.";
+ return nullptr;
+ }
+
+ std::string feature;
+ if (!body->GetString(kFeatureKey, &feature) || feature.empty()) {
+ feature = std::string(kDefaultFeature);
+ }
+
+ return base::WrapUnique(new WireMessage(payload, feature));
+}
+
+std::string WireMessage::Serialize() const {
+ if (payload_.empty()) {
+ PA_LOG(ERROR) << "Failed to serialize empty wire message.";
+ return std::string();
+ }
+
+ // Create JSON body containing permit id and payload.
+ base::DictionaryValue body;
+
+ std::string base64_payload;
+ base::Base64UrlEncode(payload_, base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &base64_payload);
+ body.SetString(kPayloadKey, base64_payload);
+ body.SetString(kFeatureKey, feature_);
+
+ std::string json_body;
+ if (!base::JSONWriter::Write(body, &json_body)) {
+ PA_LOG(ERROR) << "Failed to convert WireMessage body to JSON: " << body;
+ return std::string();
+ }
+
+ // Create header containing version and payload size.
+ size_t body_size = json_body.size();
+ if (body_size > std::numeric_limits<uint16_t>::max()) {
+ PA_LOG(ERROR) << "Can not create WireMessage because body size exceeds "
+ << "16-bit unsigned integer: " << body_size;
+ return std::string();
+ }
+
+ uint8_t header[] = {
+ static_cast<uint8_t>(kMessageFormatVersionThree),
+ static_cast<uint8_t>((body_size >> 8) & 0xFF),
+ static_cast<uint8_t>(body_size & 0xFF),
+ };
+ static_assert(sizeof(header) == kHeaderLength, "Malformed header.");
+
+ std::string header_string(kHeaderLength, 0);
+ std::memcpy(&header_string[0], header, kHeaderLength);
+ return header_string + json_body;
+}
+
+WireMessage::WireMessage(const std::string& payload, const std::string& feature)
+ : payload_(payload), feature_(feature) {}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/wire_message.h b/chromium/components/cryptauth/wire_message.h
new file mode 100644
index 00000000000..2ab1db9a179
--- /dev/null
+++ b/chromium/components/cryptauth/wire_message.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_WIRE_MESSAGE_H_
+#define COMPONENTS_CRYPTAUTH_WIRE_MESSAGE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+
+namespace cryptauth {
+
+class WireMessage {
+ public:
+ // Creates a WireMessage containing |payload| for feature |feature|.
+ explicit WireMessage(const std::string& payload, const std::string& feature);
+ virtual ~WireMessage();
+
+ // Returns the deserialized message from |serialized_message|, or nullptr if
+ // the message is malformed. Sets |is_incomplete_message| to true if the
+ // message does not have enough data to parse the header, or if the message
+ // length encoded in the message header exceeds the size of the
+ // |serialized_message|.
+ static std::unique_ptr<WireMessage> Deserialize(
+ const std::string& serialized_message,
+ bool* is_incomplete_message);
+
+ // Returns a serialized representation of |this| message.
+ virtual std::string Serialize() const;
+
+ const std::string& payload() const { return payload_; }
+ const std::string& feature() const { return feature_; }
+
+ private:
+ // The message payload.
+ const std::string payload_;
+
+ // The feature which sends or intends to receive this message (e.g.,
+ // EasyUnlock).
+ const std::string feature_;
+
+ DISALLOW_COPY_AND_ASSIGN(WireMessage);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_WIRE_MESSAGE_H_
diff --git a/chromium/components/cryptauth/wire_message_unittest.cc b/chromium/components/cryptauth/wire_message_unittest.cc
new file mode 100644
index 00000000000..7c7caca3524
--- /dev/null
+++ b/chromium/components/cryptauth/wire_message_unittest.cc
@@ -0,0 +1,214 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/wire_message.h"
+
+#include <stdint.h>
+
+#include "base/strings/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cryptauth {
+
+class CryptAuthWireMessageTest : public testing::Test {
+ protected:
+ CryptAuthWireMessageTest() {}
+ ~CryptAuthWireMessageTest() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CryptAuthWireMessageTest);
+};
+
+TEST(CryptAuthWireMessageTest, Deserialize_EmptyMessage) {
+ bool is_incomplete;
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(std::string(), &is_incomplete);
+ EXPECT_TRUE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_IncompleteHeader) {
+ bool is_incomplete;
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize("\3", &is_incomplete);
+ EXPECT_TRUE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_UnexpectedMessageFormatVersion) {
+ bool is_incomplete;
+ // Version 2 is below the minimum supported version.
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize("\2\1\1", &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_BodyOfSizeZero) {
+ bool is_incomplete;
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(std::string("\3\0\0", 3), &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_IncompleteBody) {
+ bool is_incomplete;
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(std::string("\3\0\5", 3), &is_incomplete);
+ EXPECT_TRUE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_BodyLongerThanSpecifiedInHeader) {
+ bool is_incomplete;
+ std::unique_ptr<WireMessage> message = WireMessage::Deserialize(
+ std::string("\3\0\5", 3) + "123456", &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_BodyIsNotValidJSON) {
+ bool is_incomplete;
+ std::unique_ptr<WireMessage> message = WireMessage::Deserialize(
+ std::string("\3\0\5", 3) + "12345", &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_BodyIsNotADictionary) {
+ bool is_incomplete;
+ std::string header("\3\0\x15", 3);
+ std::string bytes =
+ header + "[{\"payload\": \"YQ==\"}]";
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_BodyLacksPayload) {
+ bool is_incomplete;
+ std::string header("\3\0\x02", 3);
+ std::string bytes = header + "{}";
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_BodyHasEmptyPayload) {
+ bool is_incomplete;
+ std::string header("\3\0\x29", 3);
+ std::string bytes = header
+ + "{\"payload\": \"\", \"feature\": \"testFeature\"}";
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_PayloadIsNotBase64Encoded) {
+ bool is_incomplete;
+ std::string header("\3\0\x30", 3);
+ std::string bytes =
+ header + "{\"payload\": \"garbage\", \"feature\": \"testFeature\"}";
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_ValidMessage) {
+ bool is_incomplete;
+ std::string header("\3\0\x2d", 3);
+ std::string bytes =
+ header + "{\"payload\": \"YQ==\", \"feature\": \"testFeature\"}";
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ ASSERT_TRUE(message);
+ EXPECT_EQ("a", message->payload());
+ EXPECT_EQ("testFeature", message->feature());
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_ValidMessageWithBase64UrlEncoding) {
+ bool is_incomplete;
+ std::string header("\3\0\x2d", 3);
+ std::string bytes =
+ header + "{\"payload\": \"_-Y=\", \"feature\": \"testFeature\"}";
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ ASSERT_TRUE(message);
+ EXPECT_EQ("\xFF\xE6", message->payload());
+ EXPECT_EQ("testFeature", message->feature());
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_ValidMessageWithExtraUnknownFields) {
+ bool is_incomplete;
+ std::string header("\3\0\x4c", 3);
+ std::string bytes = header +
+ "{"
+ " \"payload\": \"YQ==\","
+ " \"feature\": \"testFeature\","
+ " \"unexpected\": \"surprise!\""
+ "}";
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ ASSERT_TRUE(message);
+ EXPECT_EQ("a", message->payload());
+ EXPECT_EQ("testFeature", message->feature());
+}
+
+TEST(CryptAuthWireMessageTest, Deserialize_SizeEquals0x01FF) {
+ // Create a message with a body of 0x01FF bytes to test the size contained in
+ // the header is parsed correctly.
+ std::string header("\3\x01\xff", 3);
+ char json_template[] = "{"
+ " \"payload\":\"YQ==\","
+ " \"feature\": \"testFeature\","
+ " \"filler\":\"$1\""
+ "}";
+ // Add 3 to the size to take into account the "$1" and NUL terminator ("\0")
+ // characters in |json_template|.
+ uint16_t filler_size = 0x01ff - sizeof(json_template) + 3;
+ std::string filler(filler_size, 'F');
+
+ std::string body = base::ReplaceStringPlaceholders(
+ json_template, std::vector<std::string>(1u, filler), nullptr);
+ std::string serialized_message = header + body;
+
+ bool is_incomplete;
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(serialized_message, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ ASSERT_TRUE(message);
+ EXPECT_EQ("a", message->payload());
+ EXPECT_EQ("testFeature", message->feature());
+}
+
+TEST(CryptAuthWireMessageTest, Serialize_WithoutFeature) {
+ bool is_incomplete;
+ std::string header("\3\0\x13", 3);
+ std::string bytes = header + "{\"payload\": \"YQ==\"}";
+ std::unique_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_TRUE(message);
+ EXPECT_EQ("a", message->payload());
+
+ // If unspecified, the default feature is "easy_unlock" for backwards
+ // compatibility.
+ EXPECT_EQ("easy_unlock", message->feature());
+}
+
+TEST(CryptAuthWireMessageTest, Serialize_FailsWithoutPayload) {
+ WireMessage message1(std::string(), "example id");
+ std::string bytes = message1.Serialize();
+ EXPECT_TRUE(bytes.empty());
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/data_reduction_proxy/OWNERS b/chromium/components/data_reduction_proxy/OWNERS
index 2868ce9f75b..340aa35a1f9 100644
--- a/chromium/components/data_reduction_proxy/OWNERS
+++ b/chromium/components/data_reduction_proxy/OWNERS
@@ -1,7 +1,7 @@
bengr@chromium.org
bolian@chromium.org
-kundaji@chromium.org
megjablon@chromium.org
+rajendrant@chromium.org
ryansturm@chromium.org
sclittle@chromium.org
tbansal@chromium.org
diff --git a/chromium/components/data_reduction_proxy/content/browser/BUILD.gn b/chromium/components/data_reduction_proxy/content/browser/BUILD.gn
index d81d3755796..e6de8f9d18b 100644
--- a/chromium/components/data_reduction_proxy/content/browser/BUILD.gn
+++ b/chromium/components/data_reduction_proxy/content/browser/BUILD.gn
@@ -8,6 +8,8 @@ static_library("browser") {
"content_lofi_decider.h",
"content_lofi_ui_service.cc",
"content_lofi_ui_service.h",
+ "content_resource_type_provider.cc",
+ "content_resource_type_provider.h",
]
deps = [
@@ -25,6 +27,7 @@ source_set("unit_tests") {
sources = [
"content_lofi_decider_unittest.cc",
"content_lofi_ui_service_unittest.cc",
+ "content_resource_type_provider_unittest.cc",
]
deps = [
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 c5e2b4daa09..fdecf634ac2 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
@@ -12,6 +12,8 @@
#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"
+#include "content/public/common/previews_state.h"
+#include "content/public/common/resource_type.h"
#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
#include "net/url_request/url_request.h"
@@ -32,8 +34,10 @@ bool ContentLoFiDecider::IsUsingLoFiMode(const net::URLRequest& request) const {
params::IsLoFiOnViaFlags() || params::IsIncludedInLoFiEnabledFieldTrial();
// Return if the user is using Lo-Fi and not part of the "Control" group.
- if (request_info)
- return request_info->IsUsingLoFi() && lofi_enabled_via_flag_or_field_trial;
+ if (request_info) {
+ return (request_info->GetPreviewsState() & content::SERVER_LOFI_ON) &&
+ lofi_enabled_via_flag_or_field_trial;
+ }
return false;
}
@@ -82,6 +86,11 @@ void ContentLoFiDecider::MaybeSetAcceptTransformHeader(
if (is_previews_disabled)
return;
+ // 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)
+ return;
+
// LoFi is not allowed on the main frame, stylesheet, script, font resource,
// media, service worker, or CSP report.
bool resource_type_supports_empty_image =
@@ -108,7 +117,7 @@ void ContentLoFiDecider::MaybeSetAcceptTransformHeader(
if (accept_transform_value.empty())
return;
- if (!request_info->IsUsingLoFi())
+ if (!(request_info->GetPreviewsState() & content::SERVER_LOFI_ON))
accept_transform_value += base::StringPrintf(";%s", if_heavy_qualifier());
headers->SetHeader(chrome_proxy_accept_transform_header(),
@@ -179,8 +188,10 @@ bool ContentLoFiDecider::ShouldRecordLoFiUMA(
content::ResourceRequestInfo::ForRequest(&request);
// User is not using Lo-Fi.
- if (!request_info || !request_info->IsUsingLoFi())
+ if (!request_info ||
+ !(request_info->GetPreviewsState() & content::SERVER_LOFI_ON)) {
return false;
+ }
return params::IsIncludedInLoFiEnabledFieldTrial() ||
params::IsIncludedInLoFiControlFieldTrial();
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 40121e2ff70..8c806f149d4 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
@@ -19,12 +19,14 @@
#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_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"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "content/public/browser/resource_request_info.h"
+#include "content/public/common/previews_state.h"
#include "net/base/load_flags.h"
#include "net/base/network_delegate_impl.h"
#include "net/http/http_request_headers.h"
@@ -32,6 +34,7 @@
#include "net/proxy/proxy_retry_info.h"
#include "net/socket/socket_test_util.h"
#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -67,13 +70,9 @@ const Client kClient = Client::UNKNOWN;
class ContentLoFiDeciderTest : public testing::Test {
public:
- ContentLoFiDeciderTest() : context_(true) {
- context_.set_client_socket_factory(&mock_socket_factory_);
- context_.Init();
-
+ ContentLoFiDeciderTest() : context_(false) {
test_context_ = DataReductionProxyTestContext::Builder()
.WithClient(kClient)
- .WithMockClientSocketFactory(&mock_socket_factory_)
.WithURLRequestContext(&context_)
.Build();
@@ -88,7 +87,6 @@ class ContentLoFiDeciderTest : public testing::Test {
data_reduction_proxy_network_delegate_->InitIODataAndUMA(
test_context_->io_data(), test_context_->io_data()->bypass_stats());
- context_.set_network_delegate(data_reduction_proxy_network_delegate_.get());
std::unique_ptr<data_reduction_proxy::ContentLoFiDecider>
data_reduction_proxy_lofi_decider(
@@ -99,14 +97,14 @@ class ContentLoFiDeciderTest : public testing::Test {
void AllocateRequestInfoForTesting(net::URLRequest* request,
content::ResourceType resource_type,
- bool is_using_lofi) {
+ content::PreviewsState previews_state) {
content::ResourceRequestInfo::AllocateForTesting(
request, resource_type, NULL, -1, -1, -1,
resource_type == content::RESOURCE_TYPE_MAIN_FRAME,
false, // parent_is_main_frame
false, // allow_download
false, // is_async
- is_using_lofi);
+ previews_state);
}
std::unique_ptr<net::URLRequest> CreateRequest(bool is_main_frame,
@@ -116,7 +114,7 @@ class ContentLoFiDeciderTest : public testing::Test {
AllocateRequestInfoForTesting(
request.get(), (is_main_frame ? content::RESOURCE_TYPE_MAIN_FRAME
: content::RESOURCE_TYPE_SUB_FRAME),
- is_using_lofi);
+ is_using_lofi ? content::SERVER_LOFI_ON : content::PREVIEWS_OFF);
return request;
}
@@ -128,7 +126,20 @@ class ContentLoFiDeciderTest : public testing::Test {
context_.CreateRequest(GURL(scheme_is_https ? "https://www.google.com/"
: "http://www.google.com/"),
net::IDLE, &delegate_);
- AllocateRequestInfoForTesting(request.get(), resource_type, is_using_lofi);
+ AllocateRequestInfoForTesting(
+ request.get(), resource_type,
+ is_using_lofi ? content::SERVER_LOFI_ON : content::PREVIEWS_OFF);
+ return request;
+ }
+
+ std::unique_ptr<net::URLRequest> CreateNoTransformRequest(
+ bool is_main_frame) {
+ std::unique_ptr<net::URLRequest> request = context_.CreateRequest(
+ GURL("http://www.google.com/"), net::IDLE, &delegate_);
+ AllocateRequestInfoForTesting(
+ request.get(), (is_main_frame ? content::RESOURCE_TYPE_MAIN_FRAME
+ : content::RESOURCE_TYPE_SUB_FRAME),
+ content::PREVIEWS_NO_TRANSFORM);
return request;
}
@@ -225,7 +236,6 @@ class ContentLoFiDeciderTest : public testing::Test {
protected:
base::MessageLoopForIO message_loop_;
- net::MockClientSocketFactory mock_socket_factory_;
net::TestURLRequestContext context_;
net::TestDelegate delegate_;
std::unique_ptr<DataReductionProxyTestContext> test_context_;
@@ -281,9 +291,10 @@ TEST_F(ContentLoFiDeciderTest, LoFiFlags) {
// The Lo-Fi flag is "always-on" and Lo-Fi is being used. Lo-Fi header
// should be added.
- AllocateRequestInfoForTesting(request.get(),
- content::RESOURCE_TYPE_SUB_FRAME,
- tests[i].is_using_lofi);
+ AllocateRequestInfoForTesting(
+ request.get(), content::RESOURCE_TYPE_SUB_FRAME,
+ tests[i].is_using_lofi ? content::SERVER_LOFI_ON
+ : content::PREVIEWS_OFF);
headers.Clear();
NotifyBeforeSendHeaders(&headers, request.get(), true);
VerifyLoFiHeader(!tests[i].is_using_lite_page, !tests[i].is_using_lofi,
@@ -687,4 +698,14 @@ TEST_F(ContentLoFiDeciderTest, MaybeIgnoreBlacklist) {
EXPECT_EQ("Foo, exp=ignore_preview_blacklist", header_value);
}
-} // namespace data_reduction_roxy
+TEST_F(ContentLoFiDeciderTest, NoTransformDoesNotAddHeader) {
+ base::FieldTrialList field_trial_list(nullptr);
+ base::FieldTrialList::CreateFieldTrial(params::GetLoFiFieldTrialName(),
+ "Enabled");
+ std::unique_ptr<net::URLRequest> request = CreateNoTransformRequest(false);
+ net::HttpRequestHeaders headers;
+ NotifyBeforeSendHeaders(&headers, request.get(), true);
+ EXPECT_FALSE(headers.HasHeader(chrome_proxy_accept_transform_header()));
+}
+
+} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/content/browser/content_lofi_ui_service_unittest.cc b/chromium/components/data_reduction_proxy/content/browser/content_lofi_ui_service_unittest.cc
index ee9affb9e44..e42be3df3d4 100644
--- a/chromium/components/data_reduction_proxy/content/browser/content_lofi_ui_service_unittest.cc
+++ b/chromium/components/data_reduction_proxy/content/browser/content_lofi_ui_service_unittest.cc
@@ -17,6 +17,7 @@
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/previews_state.h"
#include "content/public/test/test_renderer_host.h"
#include "net/socket/socket_test_util.h"
#include "net/url_request/url_request.h"
@@ -72,11 +73,10 @@ class ContentLoFiUIServiceTest : public content::RenderViewHostTestHarness {
request.get(), content::RESOURCE_TYPE_SUB_FRAME, NULL,
web_contents()->GetMainFrame()->GetProcess()->GetID(), -1,
web_contents()->GetMainFrame()->GetRoutingID(),
- false, // is_main_frame
- false, // parent_is_main_frame
- false, // allow_download
- false, // is_async
- true); // is_using_lofi
+ /*is_main_frame=*/false,
+ /*parent_is_main_frame=*/false,
+ /*allow_download=*/false,
+ /*is_async=*/false, content::SERVER_LOFI_ON);
return request;
}
diff --git a/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc
new file mode 100644
index 00000000000..0d2990d0690
--- /dev/null
+++ b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.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/data_reduction_proxy/content/browser/content_resource_type_provider.h"
+
+#include "content/public/browser/resource_request_info.h"
+#include "content/public/common/resource_type.h"
+#include "net/url_request/url_request.h"
+#include "url/gurl.h"
+
+namespace data_reduction_proxy {
+
+namespace {
+
+// The maximum size of the cache that contains the mapping from GURL to
+// ResourceTypeProvider::ContentType.
+static const size_t kMaxCacheSize = 15;
+
+// Returns the content type of |request|.
+ResourceTypeProvider::ContentType GetContentTypeInternal(
+ const net::URLRequest& request) {
+ const content::ResourceRequestInfo* request_info =
+ content::ResourceRequestInfo::ForRequest(&request);
+
+ if (!request_info)
+ return ContentResourceTypeProvider::CONTENT_TYPE_UNKNOWN;
+
+ content::ResourceType resource_type = request_info->GetResourceType();
+ if (resource_type == content::RESOURCE_TYPE_MEDIA)
+ return ContentResourceTypeProvider::CONTENT_TYPE_MEDIA;
+ return ContentResourceTypeProvider::CONTENT_TYPE_UNKNOWN;
+}
+
+} // namespace
+
+ContentResourceTypeProvider::ContentResourceTypeProvider()
+ : resource_content_types_(kMaxCacheSize) {
+ // |this| is used on a different thread than the one on which it was created.
+ thread_checker_.DetachFromThread();
+}
+
+ContentResourceTypeProvider::~ContentResourceTypeProvider() {}
+
+ResourceTypeProvider::ContentType ContentResourceTypeProvider::GetContentType(
+ const GURL& url) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // Use Peek() instead of Get() to avoid changing the recency timestamp.
+ auto iter = resource_content_types_.Peek(url.spec());
+ if (iter == resource_content_types_.end())
+ return ResourceTypeProvider::CONTENT_TYPE_UNKNOWN;
+ return iter->second;
+}
+
+void ContentResourceTypeProvider::SetContentType(
+ const net::URLRequest& request) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ResourceTypeProvider::ContentType content_type =
+ GetContentTypeInternal(request);
+ if (content_type == CONTENT_TYPE_UNKNOWN) {
+ // No need to insert entries with value |CONTENT_TYPE_UNKNOWN| since
+ // |CONTENT_TYPE_UNKNOWN| is the default value and is returned when the URL
+ // is not found in |resource_content_types_|.
+ return;
+ }
+ resource_content_types_.Put(request.url().spec(), content_type);
+}
+
+} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.h b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.h
new file mode 100644
index 00000000000..d4ed06ef208
--- /dev/null
+++ b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.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_DATA_REDUCTION_PROXY_CONTENT_BROWSER_CONTENT_RESOURCE_TYPE_PROVIDER_H_
+#define COMPONENTS_DATA_REDUCTION_PROXY_CONTENT_BROWSER_CONTENT_RESOURCE_TYPE_PROVIDER_H_
+
+#include "base/containers/mru_cache.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "components/data_reduction_proxy/core/common/resource_type_provider.h"
+
+class GURL;
+
+namespace net {
+class URLRequest;
+}
+
+namespace data_reduction_proxy {
+
+// Implements ResourceTypeProvider by maintaining a map from each request's URL
+// to its last known content type.
+class ContentResourceTypeProvider : public ResourceTypeProvider {
+ public:
+ ContentResourceTypeProvider();
+ ~ContentResourceTypeProvider() override;
+
+ private:
+ // ResourceTypeProvider:
+ void SetContentType(const net::URLRequest& request) override;
+ ResourceTypeProvider::ContentType GetContentType(
+ const GURL& url) const override;
+
+ // Map that evicts entries lazily based on the recency of being added.
+ base::MRUCache<std::string, ResourceTypeProvider::ContentType>
+ resource_content_types_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentResourceTypeProvider);
+};
+
+} // namespace data_reduction_proxy
+
+#endif // COMPONENTS_DATA_REDUCTION_PROXY_CONTENT_BROWSER_CONTENT_RESOURCE_TYPE_PROVIDER_H_
diff --git a/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider_unittest.cc b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider_unittest.cc
new file mode 100644
index 00000000000..503f0c3cb0d
--- /dev/null
+++ b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider_unittest.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/data_reduction_proxy/content/browser/content_resource_type_provider.h"
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/metrics/field_trial.h"
+#include "base/run_loop.h"
+#include "base/test/histogram_tester.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
+#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_headers.h"
+#include "content/public/browser/resource_request_info.h"
+#include "content/public/common/previews_state.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace data_reduction_proxy {
+
+namespace {
+
+#if defined(OS_ANDROID)
+const Client kClient = Client::CHROME_ANDROID;
+#elif defined(OS_IOS)
+const Client kClient = Client::CHROME_IOS;
+#elif defined(OS_MACOSX)
+const Client kClient = Client::CHROME_MAC;
+#elif defined(OS_CHROMEOS)
+const Client kClient = Client::CHROME_CHROMEOS;
+#elif defined(OS_LINUX)
+const Client kClient = Client::CHROME_LINUX;
+#elif defined(OS_WIN)
+const Client kClient = Client::CHROME_WINDOWS;
+#elif defined(OS_FREEBSD)
+const Client kClient = Client::CHROME_FREEBSD;
+#elif defined(OS_OPENBSD)
+const Client kClient = Client::CHROME_OPENBSD;
+#elif defined(OS_SOLARIS)
+const Client kClient = Client::CHROME_SOLARIS;
+#elif defined(OS_QNX)
+const Client kClient = Client::CHROME_QNX;
+#else
+const Client kClient = Client::UNKNOWN;
+#endif
+
+} // namespace
+
+class ContentResourceProviderTest : public testing::Test {
+ public:
+ ContentResourceProviderTest()
+ : context_(true), content_resource_type_provider_(nullptr) {
+ test_context_ = DataReductionProxyTestContext::Builder()
+ .WithClient(kClient)
+ .WithURLRequestContext(&context_)
+ .Build();
+
+ data_reduction_proxy_network_delegate_.reset(
+ new DataReductionProxyNetworkDelegate(
+ std::unique_ptr<net::NetworkDelegate>(
+ new net::NetworkDelegateImpl()),
+ test_context_->config(),
+ test_context_->io_data()->request_options(),
+ test_context_->configurator()));
+
+ data_reduction_proxy_network_delegate_->InitIODataAndUMA(
+ test_context_->io_data(), test_context_->io_data()->bypass_stats());
+
+ context_.set_network_delegate(data_reduction_proxy_network_delegate_.get());
+ context_.set_proxy_delegate(test_context_->io_data()->proxy_delegate());
+ context_.Init();
+
+ std::unique_ptr<data_reduction_proxy::ContentResourceTypeProvider>
+ content_resource_type_provider(
+ new data_reduction_proxy::ContentResourceTypeProvider());
+ content_resource_type_provider_ = content_resource_type_provider.get();
+ test_context_->io_data()->set_resource_type_provider(
+ std::move(content_resource_type_provider));
+ }
+
+ void AllocateRequestInfoForTesting(net::URLRequest* request,
+ content::ResourceType resource_type) {
+ content::ResourceRequestInfo::AllocateForTesting(
+ request, resource_type, NULL, -1, -1, -1,
+ resource_type == content::RESOURCE_TYPE_MAIN_FRAME,
+ false, // parent_is_main_frame
+ false, // allow_download
+ false, // is_async
+ content::PREVIEWS_OFF);
+ }
+
+ std::unique_ptr<net::URLRequest> CreateRequestByType(
+ const GURL& gurl,
+ content::ResourceType resource_type) {
+ std::unique_ptr<net::URLRequest> request =
+ context_.CreateRequest(gurl, net::IDLE, &delegate_);
+ AllocateRequestInfoForTesting(request.get(), resource_type);
+ return request;
+ }
+
+ ResourceTypeProvider* content_resource_type_provider() const {
+ return content_resource_type_provider_;
+ }
+
+ protected:
+ base::MessageLoopForIO message_loop_;
+ net::TestURLRequestContext context_;
+ net::TestDelegate delegate_;
+ std::unique_ptr<DataReductionProxyTestContext> test_context_;
+ std::unique_ptr<DataReductionProxyNetworkDelegate>
+ data_reduction_proxy_network_delegate_;
+ ResourceTypeProvider* content_resource_type_provider_;
+};
+
+// Tests that the resource content type was correctly computed, and was
+// available to the data reduction proxy delegate.
+TEST_F(ContentResourceProviderTest, SetAndGetContentResourceType) {
+ const struct {
+ GURL gurl;
+ content::ResourceType resource_type;
+ ResourceTypeProvider::ContentType expected_content_type;
+ } tests[] = {
+ {GURL("http://www.google.com/main-frame"),
+ content::RESOURCE_TYPE_MAIN_FRAME,
+ ResourceTypeProvider::CONTENT_TYPE_UNKNOWN},
+ {GURL("http://www.google.com/sub-frame"),
+ content::RESOURCE_TYPE_SUB_FRAME,
+ ResourceTypeProvider::CONTENT_TYPE_UNKNOWN},
+ {GURL("http://www.google.com/stylesheet"),
+ content::RESOURCE_TYPE_STYLESHEET,
+ ResourceTypeProvider::CONTENT_TYPE_UNKNOWN},
+ {GURL("http://www.google.com/script"), content::RESOURCE_TYPE_SCRIPT,
+ ResourceTypeProvider::CONTENT_TYPE_UNKNOWN},
+ {GURL("http://www.google.com/image"), content::RESOURCE_TYPE_IMAGE,
+ ResourceTypeProvider::CONTENT_TYPE_UNKNOWN},
+ {GURL("http://www.google.com/font"), content::RESOURCE_TYPE_FONT_RESOURCE,
+ ResourceTypeProvider::CONTENT_TYPE_UNKNOWN},
+ {GURL("http://www.google.com/sub-resource"),
+ content::RESOURCE_TYPE_SUB_RESOURCE,
+ ResourceTypeProvider::CONTENT_TYPE_UNKNOWN},
+ {GURL("http://www.google.com/object"), content::RESOURCE_TYPE_OBJECT,
+ ResourceTypeProvider::CONTENT_TYPE_UNKNOWN},
+ {GURL("http://www.google.com/media"), content::RESOURCE_TYPE_MEDIA,
+ ResourceTypeProvider::CONTENT_TYPE_MEDIA},
+ {GURL("http://www.google.com/worker"), content::RESOURCE_TYPE_WORKER,
+ ResourceTypeProvider::CONTENT_TYPE_UNKNOWN},
+ {GURL("http://www.google.com/shared-worker"),
+ content::RESOURCE_TYPE_SHARED_WORKER,
+ ResourceTypeProvider::CONTENT_TYPE_UNKNOWN}};
+
+ for (const auto test : tests) {
+ base::HistogramTester histogram_tester;
+ std::unique_ptr<net::URLRequest> request =
+ CreateRequestByType(test.gurl, test.resource_type);
+ request->Start();
+ base::RunLoop().RunUntilIdle();
+
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.ResourceContentType", test.expected_content_type,
+ 1);
+
+ EXPECT_EQ(test.expected_content_type,
+ content_resource_type_provider()->GetContentType(request->url()));
+
+ // Querying for the content type of |request->url()| again should still
+ // give the correct result.
+ EXPECT_EQ(test.expected_content_type,
+ content_resource_type_provider()->GetContentType(request->url()));
+ }
+}
+
+} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
index 9119d7c22a1..40e848405f2 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
@@ -85,6 +85,7 @@ bool DataReductionProxyBypassProtocol::MaybeBypassProxyAndPrepareToRetry(
net::URLRequest* request,
DataReductionProxyBypassType* proxy_bypass_type,
DataReductionProxyInfo* data_reduction_proxy_info) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(request);
const net::HttpResponseHeaders* response_headers =
request->response_info().headers.get();
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h
index db75fd5cec8..b185cb307a3 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h
@@ -6,6 +6,7 @@
#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_BYPASS_PROTOCOL_H_
#include "base/macros.h"
+#include "base/threading/thread_checker.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
namespace net {
@@ -53,6 +54,8 @@ class DataReductionProxyBypassProtocol {
// Must outlive |this|.
DataReductionProxyConfig* config_;
+ base::ThreadChecker thread_checker_;
+
DISALLOW_COPY_AND_ASSIGN(DataReductionProxyBypassProtocol);
};
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 a4cc7bdcb3b..3aa6b85d42d 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
@@ -20,12 +20,6 @@
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
-using net::HostPortPair;
-using net::ProxyServer;
-using net::ProxyService;
-using net::NetworkChangeNotifier;
-using net::URLRequest;
-
namespace data_reduction_proxy {
namespace {
@@ -63,7 +57,7 @@ ProxyScheme ConvertNetProxySchemeToProxyScheme(
// proxy (|is_primary| is true) or the data reduction proxy fallback.
void RecordDataReductionProxyBypassOnNetworkError(
bool is_primary,
- const ProxyServer& proxy_server,
+ const net::ProxyServer& proxy_server,
int net_error) {
if (is_primary) {
UMA_HISTOGRAM_SPARSE_SLOWLY(
@@ -134,17 +128,24 @@ DataReductionProxyBypassStats::DataReductionProxyBypassStats(
proxy_net_errors_count_(0),
unavailable_(false) {
DCHECK(config);
- NetworkChangeNotifier::AddNetworkChangeObserver(this);
+ // Constructed on the UI thread, but should be checked on the IO thread.
+ thread_checker_.DetachFromThread();
}
DataReductionProxyBypassStats::~DataReductionProxyBypassStats() {
- NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+ net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+}
+
+void DataReductionProxyBypassStats::InitializeOnIOThread() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
}
void DataReductionProxyBypassStats::OnUrlRequestCompleted(
const net::URLRequest* request,
bool started,
int net_error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DataReductionProxyTypeInfo proxy_info;
// Ignore requests that did not use the data reduction proxy. The check for
// LOAD_BYPASS_PROXY is necessary because the proxy_server() in the |request|
@@ -184,12 +185,14 @@ void DataReductionProxyBypassStats::OnUrlRequestCompleted(
void DataReductionProxyBypassStats::SetBypassType(
DataReductionProxyBypassType type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
last_bypass_type_ = type;
triggering_request_ = true;
}
DataReductionProxyBypassType
DataReductionProxyBypassStats::GetBypassType() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return last_bypass_type_;
}
@@ -197,6 +200,7 @@ void DataReductionProxyBypassStats::RecordBytesHistograms(
const net::URLRequest& request,
bool data_reduction_proxy_enabled,
const net::ProxyConfig& data_reduction_proxy_config) {
+ DCHECK(thread_checker_.CalledOnValidThread());
RecordBypassedBytesHistograms(request, data_reduction_proxy_enabled,
data_reduction_proxy_config);
RecordMissingViaHeaderBytes(request);
@@ -205,6 +209,7 @@ void DataReductionProxyBypassStats::RecordBytesHistograms(
void DataReductionProxyBypassStats::OnProxyFallback(
const net::ProxyServer& bypassed_proxy,
int net_error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DataReductionProxyTypeInfo data_reduction_proxy_info;
if (bypassed_proxy.is_valid() && !bypassed_proxy.is_direct() &&
data_reduction_proxy_config_->IsDataReductionProxy(
@@ -237,11 +242,13 @@ void DataReductionProxyBypassStats::OnProxyFallback(
}
void DataReductionProxyBypassStats::ClearRequestCounts() {
+ DCHECK(thread_checker_.CalledOnValidThread());
successful_requests_through_proxy_count_ = 0;
proxy_net_errors_count_ = 0;
}
void DataReductionProxyBypassStats::NotifyUnavailabilityIfChanged() {
+ DCHECK(thread_checker_.CalledOnValidThread());
bool prev_unavailable = unavailable_;
unavailable_ =
(proxy_net_errors_count_ >= kMinFailedRequestsWhenUnavailable &&
@@ -255,6 +262,7 @@ void DataReductionProxyBypassStats::RecordBypassedBytesHistograms(
const net::URLRequest& request,
bool data_reduction_proxy_enabled,
const net::ProxyConfig& data_reduction_proxy_config) {
+ DCHECK(thread_checker_.CalledOnValidThread());
int64_t content_length = request.received_response_content_length();
// Only record histograms when the data reduction proxy is enabled.
@@ -369,7 +377,8 @@ void DataReductionProxyBypassStats::RecordBypassedBytesHistograms(
}
void DataReductionProxyBypassStats::RecordMissingViaHeaderBytes(
- const URLRequest& request) {
+ const net::URLRequest& request) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// Responses that were served from cache should have been filtered out
// already.
DCHECK(!request.was_cached());
@@ -394,7 +403,8 @@ void DataReductionProxyBypassStats::RecordMissingViaHeaderBytes(
}
void DataReductionProxyBypassStats::OnNetworkChanged(
- NetworkChangeNotifier::ConnectionType type) {
+ net::NetworkChangeNotifier::ConnectionType type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
ClearRequestCounts();
}
@@ -402,6 +412,7 @@ void DataReductionProxyBypassStats::RecordBypassedBytes(
DataReductionProxyBypassType bypass_type,
DataReductionProxyBypassStats::BypassedBytesType bypassed_bytes_type,
int64_t content_length) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// Individual histograms are needed to count the bypassed bytes for each
// bypass type so that we can see the size of requests. This helps us
// remove outliers that would skew the sum of bypassed bytes for each type.
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h
index be08aa55afd..22674e9f1aa 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
+#include "base/threading/thread_checker.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
#include "net/base/host_port_pair.h"
#include "net/base/network_change_notifier.h"
@@ -55,6 +56,9 @@ class DataReductionProxyBypassStats
~DataReductionProxyBypassStats() override;
+ // Performs initialization on the IO thread.
+ void InitializeOnIOThread();
+
// Callback intended to be called from |DataReductionProxyNetworkDelegate|
// when a request completes. This method is used to gather bypass stats.
void OnUrlRequestCompleted(const net::URLRequest* request,
@@ -154,6 +158,8 @@ class DataReductionProxyBypassStats
// Whether or not the data reduction proxy is unavailable.
bool unavailable_;
+ base::ThreadChecker thread_checker_;
+
DISALLOW_COPY_AND_ASSIGN(DataReductionProxyBypassStats);
};
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 afd11dcdc3d..064d0aa45e2 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
@@ -27,6 +27,8 @@
#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"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "components/prefs/testing_pref_service.h"
#include "net/base/host_port_pair.h"
#include "net/base/load_flags.h"
@@ -252,20 +254,26 @@ class DataReductionProxyBypassStatsEndToEndTest : public testing::Test {
}
void SetUp() override {
- // Only use the primary data reduction proxy in order to make it easier to
- // test bypassed bytes due to proxy fallbacks. This way, a test just needs
- // to cause one proxy fallback in order for the data reduction proxy to be
- // fully bypassed.
- drp_test_context_ =
- DataReductionProxyTestContext::Builder()
- .WithParamsFlags(DataReductionProxyParams::kAllowed)
- .WithURLRequestContext(&context_)
- .WithMockClientSocketFactory(&mock_socket_factory_)
- .Build();
+ drp_test_context_ = DataReductionProxyTestContext::Builder()
+ .WithParamsFlags(0)
+ .WithURLRequestContext(&context_)
+ .WithMockClientSocketFactory(&mock_socket_factory_)
+ .Build();
drp_test_context_->AttachToURLRequestContext(&context_storage_);
context_.set_client_socket_factory(&mock_socket_factory_);
proxy_delegate_ = drp_test_context_->io_data()->CreateProxyDelegate();
context_.set_proxy_delegate(proxy_delegate_.get());
+
+ // Only use the primary data reduction proxy in order to make it easier to
+ // test bypassed bytes due to proxy fallbacks. This way, a test just needs
+ // to cause one proxy fallback in order for the data reduction proxy to be
+ // 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),
+ ProxyServer::CORE));
+ config()->test_params()->SetProxiesForHttp(data_reduction_proxy_servers);
}
// Create and execute a fake request using the data reduction proxy stack.
@@ -284,7 +292,12 @@ class DataReductionProxyBypassStatsEndToEndTest : public testing::Test {
finish_code);
if (url.SchemeIsCryptographic() ||
(!config()->test_params()->proxies_for_http().empty() &&
- config()->test_params()->proxies_for_http().front().is_https())) {
+ config()
+ ->test_params()
+ ->proxies_for_http()
+ .front()
+ .proxy_server()
+ .is_https())) {
mock_socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data_provider);
}
@@ -697,7 +710,8 @@ 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();
+ 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());
@@ -966,9 +980,10 @@ TEST_F(DataReductionProxyBypassStatsEndToEndTest, HttpProxyScheme) {
TEST_F(DataReductionProxyBypassStatsEndToEndTest, HttpsProxyScheme) {
net::ProxyServer origin =
net::ProxyServer::FromURI("test.com:443", net::ProxyServer::SCHEME_HTTPS);
- std::vector<net::ProxyServer> proxies_for_http;
- proxies_for_http.push_back(origin);
- config()->test_params()->SetProxiesForHttp(proxies_for_http);
+ 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();
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 40fbb7882ca..5d4b87c9187 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
@@ -201,6 +201,13 @@ void RecordDailyContentLengthHistograms(
percent_savings_via_data_reduction_proxy);
}
+void RecordSavingsClearedNegativeClockMetric(int days_since_last_update) {
+ // Data savings are cleared if the system clock moved back by more than
+ // one day.
+ UMA_HISTOGRAM_BOOLEAN("DataReductionProxy.SavingsCleared.NegativeSystemClock",
+ days_since_last_update < -1);
+}
+
} // namespace
class DataReductionProxyCompressionStats::DailyContentLengthUpdate {
@@ -333,6 +340,8 @@ DataReductionProxyCompressionStats::DataReductionProxyCompressionStats(
pref_service_(prefs),
delay_(delay),
data_usage_map_is_dirty_(false),
+ session_total_received_(0),
+ session_total_original_(0),
current_data_usage_load_status_(NOT_LOADED),
weak_factory_(this) {
DCHECK(service);
@@ -413,11 +422,6 @@ void DataReductionProxyCompressionStats::Init() {
InitListPref(prefs::kDailyHttpReceivedContentLength);
InitListPref(prefs::kDailyOriginalContentLengthViaDataReductionProxy);
InitListPref(prefs::kDailyOriginalContentLengthWithDataReductionProxyEnabled);
-
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kClearDataReductionProxyDataSavings)) {
- ClearDataSavingStatistics();
- }
}
void DataReductionProxyCompressionStats::UpdateDataSavings(
@@ -442,6 +446,8 @@ void DataReductionProxyCompressionStats::UpdateContentLengths(
const scoped_refptr<DataUseGroup>& data_use_group,
const std::string& mime_type) {
DCHECK(thread_checker_.CalledOnValidThread());
+ 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();
@@ -480,7 +486,7 @@ void DataReductionProxyCompressionStats::InitListPref(const char* pref) {
std::unique_ptr<base::ListValue> pref_value =
std::unique_ptr<base::ListValue>(
pref_service_->GetList(pref)->DeepCopy());
- list_pref_map_.add(pref, std::move(pref_value));
+ list_pref_map_[pref] = std::move(pref_value);
}
int64_t DataReductionProxyCompressionStats::GetInt64(const char* pref_path) {
@@ -514,7 +520,10 @@ base::ListValue* DataReductionProxyCompressionStats::GetList(
return ListPrefUpdate(pref_service_, pref_path).Get();
DelayedWritePrefs();
- return list_pref_map_.get(pref_path);
+ auto it = list_pref_map_.find(pref_path);
+ if (it == list_pref_map_.end())
+ return nullptr;
+ return it->second.get();
}
void DataReductionProxyCompressionStats::WritePrefs() {
@@ -527,9 +536,9 @@ void DataReductionProxyCompressionStats::WritePrefs() {
pref_service_->SetInt64(iter->first, iter->second);
}
- for (DataReductionProxyListPrefMap::iterator iter = list_pref_map_.begin();
- iter != list_pref_map_.end(); ++iter) {
- TransferList(*(iter->second),
+ for (auto iter = list_pref_map_.begin(); iter != list_pref_map_.end();
+ ++iter) {
+ TransferList(*(iter->second.get()),
ListPrefUpdate(pref_service_, iter->first).Get());
}
}
@@ -549,6 +558,19 @@ DataReductionProxyCompressionStats::HistoricNetworkStatsInfoToValue() {
return std::move(dict);
}
+std::unique_ptr<base::Value>
+DataReductionProxyCompressionStats::SessionNetworkStatsInfoToValue() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+ // Use strings to avoid overflow. base::Value only supports 32-bit integers.
+ dict->SetString("session_received_content_length",
+ base::Int64ToString(session_total_received_));
+ dict->SetString("session_original_content_length",
+ base::Int64ToString(session_total_original_));
+ return std::move(dict);
+}
+
int64_t DataReductionProxyCompressionStats::GetLastUpdateTime() {
int64_t last_update_internal =
GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate);
@@ -663,8 +685,8 @@ void DataReductionProxyCompressionStats::OnCurrentDataUsageLoaded(
data_usage->connection_usage_size() == 1);
for (const auto& connection_usage : data_usage->connection_usage()) {
for (const auto& site_usage : connection_usage.site_usage()) {
- data_usage_map_.set(site_usage.hostname(),
- base::MakeUnique<PerSiteDataUsage>(site_usage));
+ data_usage_map_[site_usage.hostname()] =
+ base::MakeUnique<PerSiteDataUsage>(site_usage);
}
}
@@ -676,29 +698,28 @@ void DataReductionProxyCompressionStats::OnCurrentDataUsageLoaded(
void DataReductionProxyCompressionStats::ClearDataSavingStatistics() {
DeleteHistoricalDataUsage();
- list_pref_map_.get(
- prefs::kDailyContentLengthHttpsWithDataReductionProxyEnabled)->Clear();
- list_pref_map_
- .get(prefs::kDailyContentLengthLongBypassWithDataReductionProxyEnabled)
- ->Clear();
- list_pref_map_
- .get(prefs::kDailyContentLengthShortBypassWithDataReductionProxyEnabled)
- ->Clear();
- list_pref_map_
- .get(prefs::kDailyContentLengthUnknownWithDataReductionProxyEnabled)
- ->Clear();
- list_pref_map_.get(prefs::kDailyContentLengthViaDataReductionProxy)->Clear();
- list_pref_map_.get(prefs::kDailyContentLengthWithDataReductionProxyEnabled)
- ->Clear();
- list_pref_map_.get(prefs::kDailyHttpOriginalContentLength)->Clear();
- list_pref_map_.get(prefs::kDailyHttpReceivedContentLength)->Clear();
- list_pref_map_.get(prefs::kDailyOriginalContentLengthViaDataReductionProxy)
- ->Clear();
- list_pref_map_
- .get(prefs::kDailyOriginalContentLengthWithDataReductionProxyEnabled)
- ->Clear();
-
- WritePrefs();
+ pref_service_->ClearPref(
+ prefs::kDailyContentLengthHttpsWithDataReductionProxyEnabled);
+ pref_service_->ClearPref(
+ prefs::kDailyContentLengthLongBypassWithDataReductionProxyEnabled);
+ pref_service_->ClearPref(
+ prefs::kDailyContentLengthShortBypassWithDataReductionProxyEnabled);
+ pref_service_->ClearPref(
+ prefs::kDailyContentLengthUnknownWithDataReductionProxyEnabled);
+ pref_service_->ClearPref(prefs::kDailyContentLengthViaDataReductionProxy);
+ pref_service_->ClearPref(
+ prefs::kDailyContentLengthWithDataReductionProxyEnabled);
+ pref_service_->ClearPref(prefs::kDailyHttpOriginalContentLength);
+ pref_service_->ClearPref(prefs::kDailyHttpReceivedContentLength);
+ pref_service_->ClearPref(
+ prefs::kDailyOriginalContentLengthViaDataReductionProxy);
+ pref_service_->ClearPref(
+ prefs::kDailyOriginalContentLengthWithDataReductionProxyEnabled);
+
+ for (auto iter = list_pref_map_.begin(); iter != list_pref_map_.end();
+ ++iter) {
+ iter->second->Clear();
+ }
}
void DataReductionProxyCompressionStats::DelayedWritePrefs() {
@@ -923,6 +944,8 @@ void DataReductionProxyCompressionStats::RecordRequestSizePrefs(
"Net.DailyContentLength_ViaDataReductionProxy_UnknownMime");
}
+ RecordSavingsClearedNegativeClockMetric(days_since_last_update);
+
// The system may go backwards in time by up to a day for legitimate
// reasons, such as with changes to the time zone. In such cases, we
// keep adding to the current day which is why we check for
@@ -930,6 +953,11 @@ void DataReductionProxyCompressionStats::RecordRequestSizePrefs(
// Note: we accept the fact that some reported data is shifted to
// the adjacent day if users travel back and forth across time zones.
if (days_since_last_update && (days_since_last_update != -1)) {
+ if (days_since_last_update < -1) {
+ pref_service_->SetInt64(
+ prefs::kDataReductionProxySavingsClearedNegativeSystemClock,
+ now.ToInternalValue());
+ }
SetInt64(data_reduction_proxy::prefs::
kDailyHttpOriginalContentLengthApplication,
0);
@@ -1112,28 +1140,6 @@ void DataReductionProxyCompressionStats::IncrementDailyUmaPrefs(
}
}
-void DataReductionProxyCompressionStats::RecordUserVisibleDataSavings() {
- int64_t original_content_length;
- int64_t received_content_length;
- int64_t last_update_internal;
- GetContentLengths(kNumDaysInHistorySummary, &original_content_length,
- &received_content_length, &last_update_internal);
-
- if (original_content_length == 0)
- return;
-
- int64_t user_visible_savings_bytes =
- original_content_length - received_content_length;
- int user_visible_savings_percent =
- user_visible_savings_bytes * 100 / original_content_length;
- UMA_HISTOGRAM_PERCENTAGE(
- "Net.DailyUserVisibleSavingsPercent_DataReductionProxyEnabled",
- user_visible_savings_percent);
- UMA_HISTOGRAM_COUNTS(
- "Net.DailyUserVisibleSavingsSize_DataReductionProxyEnabled",
- user_visible_savings_bytes >> 10);
-}
-
void DataReductionProxyCompressionStats::RecordDataUsage(
const std::string& data_usage_host,
int64_t data_used,
@@ -1151,9 +1157,9 @@ void DataReductionProxyCompressionStats::RecordDataUsage(
}
std::string normalized_host = NormalizeHostname(data_usage_host);
- auto j = data_usage_map_.add(normalized_host,
- base::MakeUnique<PerSiteDataUsage>());
- PerSiteDataUsage* per_site_usage = j.first->second;
+ auto j = data_usage_map_.insert(
+ std::make_pair(normalized_host, base::MakeUnique<PerSiteDataUsage>()));
+ PerSiteDataUsage* per_site_usage = j.first->second.get();
per_site_usage->set_hostname(normalized_host);
per_site_usage->set_original_size(per_site_usage->original_size() +
original_size);
@@ -1174,7 +1180,7 @@ void DataReductionProxyCompressionStats::PersistDataUsage() {
data_usage_bucket->add_connection_usage();
for (auto i = data_usage_map_.begin(); i != data_usage_map_.end(); ++i) {
PerSiteDataUsage* per_site_usage = connection_usage->add_site_usage();
- per_site_usage->CopyFrom(*(i->second));
+ per_site_usage->CopyFrom(*(i->second.get()));
}
service_->StoreCurrentDataUsageBucket(std::move(data_usage_bucket));
}
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 236501fc82f..60c00200f0d 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
@@ -11,8 +11,8 @@
#include <map>
#include <memory>
#include <string>
+#include <unordered_map>
-#include "base/containers/scoped_ptr_hash_map.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
@@ -45,7 +45,7 @@ class PerSiteDataUsage;
// prefs must be stored and read on the UI thread.
class DataReductionProxyCompressionStats {
public:
- typedef base::ScopedPtrHashMap<std::string, std::unique_ptr<PerSiteDataUsage>>
+ typedef std::unordered_map<std::string, std::unique_ptr<PerSiteDataUsage>>
SiteUsageMap;
// Collects and store data usage and compression statistics. Basic data usage
@@ -79,10 +79,15 @@ class DataReductionProxyCompressionStats {
const scoped_refptr<DataUseGroup>& data_use_group,
const std::string& mime_type);
- // Creates a |Value| summary of the persistent state of the network session.
+ // Creates a |Value| summary of the persistent state of the network
+ // statistics.
// Must be called on the UI thread.
std::unique_ptr<base::Value> HistoricNetworkStatsInfoToValue();
+ // Creates a |Value| summary of the the session network statistics.
+ // Must be called on the UI thread.
+ std::unique_ptr<base::Value> SessionNetworkStatsInfoToValue() const;
+
// Returns the time in milliseconds since epoch that the last update was made
// to the daily original and received content lengths.
int64_t GetLastUpdateTime();
@@ -135,7 +140,7 @@ class DataReductionProxyCompressionStats {
friend class DataReductionProxyCompressionStatsTest;
typedef std::map<const char*, int64_t> DataReductionProxyPrefMap;
- typedef base::ScopedPtrHashMap<const char*, std::unique_ptr<base::ListValue>>
+ typedef std::unordered_map<const char*, std::unique_ptr<base::ListValue>>
DataReductionProxyListPrefMap;
class DailyContentLengthUpdate;
@@ -201,11 +206,6 @@ class DataReductionProxyCompressionStats {
const char* original_size_via_proxy_pref,
const char* received_size_via_proxy_pref);
- // Record UMA with data savings bytes and percent over the past
- // |DataReductionProxy::kNumDaysInHistorySummary| days. These numbers
- // are displayed to users as their data savings.
- void RecordUserVisibleDataSavings();
-
// Record data usage and original size of request broken down by host. |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()|.
@@ -269,6 +269,12 @@ class DataReductionProxyCompressionStats {
// persisted to storage.
bool data_usage_map_is_dirty_;
+ // Total size of all content that has been received over the network.
+ int64_t session_total_received_;
+
+ // Total original size of all content before it was proxied.
+ int64_t session_total_original_;
+
// Tracks state of loading data usage from storage.
CurrentDataUsageLoadStatus current_data_usage_load_status_;
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 31a62cc287d..cadbcd3cda7 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
@@ -10,7 +10,6 @@
#include <string>
#include <utility>
-#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
@@ -114,7 +113,7 @@ const char kLastUpdateTime[] = "Wed, 18 Sep 2013 03:45:26";
class DataReductionProxyCompressionStatsTest : public testing::Test {
protected:
DataReductionProxyCompressionStatsTest() {
- base::Time::FromString(kLastUpdateTime, &now_);
+ EXPECT_TRUE(base::Time::FromString(kLastUpdateTime, &now_));
}
void SetUp() override {
@@ -543,27 +542,7 @@ TEST_F(DataReductionProxyCompressionStatsTest,
VerifyPrefs(dict);
}
-TEST_F(DataReductionProxyCompressionStatsTest,
- ClearPrefsOnRestartEnabled) {
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- command_line->AppendSwitch(
- data_reduction_proxy::switches::kClearDataReductionProxyDataSavings);
-
- base::ListValue list_value;
- list_value.Insert(
- 0, base::MakeUnique<base::StringValue>(base::Int64ToString(1234)));
- pref_service()->Set(prefs::kDailyHttpOriginalContentLength, list_value);
-
- ResetCompressionStatsWithDelay(
- base::TimeDelta::FromMinutes(kWriteDelayMinutes));
-
- const base::ListValue* value = pref_service()->GetList(
- prefs::kDailyHttpOriginalContentLength);
- EXPECT_EQ(0u, value->GetSize());
-}
-
-TEST_F(DataReductionProxyCompressionStatsTest,
- ClearPrefsOnRestartDisabled) {
+TEST_F(DataReductionProxyCompressionStatsTest, StatsRestoredOnOnRestart) {
base::ListValue list_value;
list_value.Insert(
0, base::MakeUnique<base::StringValue>(base::Int64ToString(1234)));
@@ -904,11 +883,14 @@ TEST_F(DataReductionProxyCompressionStatsTest, PartialDayTimeChange) {
}
TEST_F(DataReductionProxyCompressionStatsTest, ForwardMultipleDays) {
+ base::HistogramTester histogram_tester;
const int64_t kOriginalLength = 200;
const int64_t kReceivedLength = 100;
RecordContentLengthPrefs(
kReceivedLength, kOriginalLength, true, VIA_DATA_REDUCTION_PROXY,
FakeNow());
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", false, 1);
// Forward three days.
SetFakeTimeDeltaInHours(3 * 24);
@@ -916,6 +898,8 @@ TEST_F(DataReductionProxyCompressionStatsTest, ForwardMultipleDays) {
RecordContentLengthPrefs(
kReceivedLength, kOriginalLength, true, VIA_DATA_REDUCTION_PROXY,
FakeNow());
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", false, 2);
int64_t original[] = {kOriginalLength, 0, 0, kOriginalLength};
int64_t received[] = {kReceivedLength, 0, 0, kReceivedLength};
@@ -939,6 +923,8 @@ TEST_F(DataReductionProxyCompressionStatsTest, ForwardMultipleDays) {
original2, 8, received2, 8,
original2, 8, received2, 8,
original2, 8, received2, 8);
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", false, 3);
// Forward |kNumDaysInHistory| more days.
AddFakeTimeDeltaInHours(kNumDaysInHistory * 24);
@@ -951,6 +937,8 @@ TEST_F(DataReductionProxyCompressionStatsTest, ForwardMultipleDays) {
original3, 1, received3, 1,
original3, 1, received3, 1,
original3, 1, received3, 1);
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", false, 4);
// Forward |kNumDaysInHistory| + 1 more days.
AddFakeTimeDeltaInHours((kNumDaysInHistory + 1)* 24);
@@ -961,9 +949,12 @@ TEST_F(DataReductionProxyCompressionStatsTest, ForwardMultipleDays) {
original3, 1, received3, 1,
original3, 1, received3, 1,
original3, 1, received3, 1);
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", false, 5);
}
TEST_F(DataReductionProxyCompressionStatsTest, BackwardAndForwardOneDay) {
+ base::HistogramTester histogram_tester;
const int64_t kOriginalLength = 200;
const int64_t kReceivedLength = 100;
int64_t original[] = {kOriginalLength};
@@ -972,6 +963,8 @@ TEST_F(DataReductionProxyCompressionStatsTest, BackwardAndForwardOneDay) {
RecordContentLengthPrefs(
kReceivedLength, kOriginalLength, true, VIA_DATA_REDUCTION_PROXY,
FakeNow());
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", false, 1);
// Backward one day.
SetFakeTimeDeltaInHours(-24);
@@ -984,6 +977,8 @@ TEST_F(DataReductionProxyCompressionStatsTest, BackwardAndForwardOneDay) {
original, 1, received, 1,
original, 1, received, 1,
original, 1, received, 1);
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", false, 2);
// Then, Forward one day
AddFakeTimeDeltaInHours(24);
@@ -996,9 +991,12 @@ TEST_F(DataReductionProxyCompressionStatsTest, BackwardAndForwardOneDay) {
original2, 2, received2, 2,
original2, 2, received2, 2,
original2, 2, received2, 2);
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", false, 3);
}
TEST_F(DataReductionProxyCompressionStatsTest, BackwardTwoDays) {
+ base::HistogramTester histogram_tester;
const int64_t kOriginalLength = 200;
const int64_t kReceivedLength = 100;
int64_t original[] = {kOriginalLength};
@@ -1007,6 +1005,9 @@ TEST_F(DataReductionProxyCompressionStatsTest, BackwardTwoDays) {
RecordContentLengthPrefs(
kReceivedLength, kOriginalLength, true, VIA_DATA_REDUCTION_PROXY,
FakeNow());
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", false, 1);
+
// Backward two days.
SetFakeTimeDeltaInHours(-2 * 24);
RecordContentLengthPrefs(
@@ -1016,6 +1017,30 @@ TEST_F(DataReductionProxyCompressionStatsTest, BackwardTwoDays) {
original, 1, received, 1,
original, 1, received, 1,
original, 1, received, 1);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", 2);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", true, 1);
+ VerifyPrefInt64(prefs::kDataReductionProxySavingsClearedNegativeSystemClock,
+ FakeNow().ToInternalValue());
+
+ // Backward two days.
+ SetFakeTimeDeltaInHours(-4 * 24);
+ RecordContentLengthPrefs(kReceivedLength, kOriginalLength, true,
+ VIA_DATA_REDUCTION_PROXY, FakeNow());
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", 3);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", true, 2);
+
+ // Forward 10 days.
+ AddFakeTimeDeltaInHours(10 * 24);
+ RecordContentLengthPrefs(kReceivedLength, kOriginalLength, true,
+ VIA_DATA_REDUCTION_PROXY, FakeNow());
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", 4);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.SavingsCleared.NegativeSystemClock", false, 2);
}
TEST_F(DataReductionProxyCompressionStatsTest, NormalizeHostname) {
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 67d424f5e64..4e1d800fead 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
@@ -28,6 +28,7 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/variations/variations_associated_data.h"
#include "net/base/host_port_pair.h"
@@ -42,6 +43,10 @@
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
+#if defined(OS_ANDROID)
+#include "net/android/network_library.h"
+#endif // OS_ANDROID
+
using base::FieldTrialList;
namespace {
@@ -120,9 +125,12 @@ base::HistogramBase* GetEnumeratedHistogram(
}
// Following UMA is plotted to measure how frequently Lo-Fi state changes.
-// Too frequent changes are undesirable.
-void RecordAutoLoFiRequestHeaderStateChange(bool previous_header_low,
- bool current_header_low) {
+// Too frequent changes are undesirable. |connection_type| is the current
+// connection type.
+void RecordAutoLoFiRequestHeaderStateChange(
+ net::NetworkChangeNotifier::ConnectionType connection_type,
+ bool previous_header_low,
+ bool current_header_low) {
// Auto Lo-Fi request header state changes.
// Possible Lo-Fi header directives are empty ("") and low ("q=low").
// This enum must remain synchronized with the enum of the same name in
@@ -136,8 +144,6 @@ void RecordAutoLoFiRequestHeaderStateChange(bool previous_header_low,
};
AutoLoFiRequestHeaderState state;
- net::NetworkChangeNotifier::ConnectionType connection_type =
- net::NetworkChangeNotifier::GetConnectionType();
if (!previous_header_low) {
if (current_header_low)
@@ -201,6 +207,14 @@ void RecordAutoLoFiRequestHeaderStateChange(bool previous_header_low,
}
}
+// Records UMA containing the result of requesting the secure proxy check.
+void RecordSecureProxyCheckFetchResult(
+ data_reduction_proxy::SecureProxyCheckFetchResult result) {
+ UMA_HISTOGRAM_ENUMERATION(
+ kUMAProxyProbeURL, result,
+ data_reduction_proxy::SECURE_PROXY_CHECK_FETCH_RESULT_COUNT);
+}
+
} // namespace
namespace data_reduction_proxy {
@@ -270,13 +284,69 @@ class SecureProxyChecker : public net::URLFetcherDelegate {
DISALLOW_COPY_AND_ASSIGN(SecureProxyChecker);
};
+// URLFetcherDelegate for fetching the warmup URL.
+class WarmupURLFetcher : public net::URLFetcherDelegate {
+ public:
+ explicit WarmupURLFetcher(const scoped_refptr<net::URLRequestContextGetter>&
+ url_request_context_getter)
+ : url_request_context_getter_(url_request_context_getter) {
+ DCHECK(url_request_context_getter_);
+ }
+
+ ~WarmupURLFetcher() override {}
+
+ // Creates and starts a URLFetcher that fetches the warmup URL.
+ void FetchWarmupURL() {
+ UMA_HISTOGRAM_EXACT_LINEAR("DataReductionProxy.WarmupURL.FetchInitiated", 1,
+ 2);
+
+ fetcher_ = net::URLFetcher::Create(params::GetWarmupURL(),
+ net::URLFetcher::GET, this);
+ data_use_measurement::DataUseUserData::AttachToFetcher(
+ fetcher_.get(),
+ data_use_measurement::DataUseUserData::DATA_REDUCTION_PROXY);
+ fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE);
+ fetcher_->SetRequestContext(url_request_context_getter_.get());
+ // |fetcher| should not retry on 5xx errors.
+ fetcher_->SetAutomaticallyRetryOn5xx(false);
+ fetcher_->SetAutomaticallyRetryOnNetworkChanges(0);
+ fetcher_->Start();
+ }
+
+ void SetWarmupURLFetcherCallbackForTesting(
+ base::Callback<void()> warmup_url_fetched_callback) {
+ fetch_completion_callback_ = warmup_url_fetched_callback;
+ }
+
+ private:
+ void OnURLFetchComplete(const net::URLFetcher* source) override {
+ DCHECK_EQ(source, fetcher_.get());
+ UMA_HISTOGRAM_BOOLEAN(
+ "DataReductionProxy.WarmupURL.FetchSuccessful",
+ source->GetStatus().status() == net::URLRequestStatus::SUCCESS);
+
+ if (fetch_completion_callback_)
+ fetch_completion_callback_.Run();
+ }
+
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+
+ // The URLFetcher being used for fetching the warmup URL.
+ std::unique_ptr<net::URLFetcher> fetcher_;
+
+ // Called upon the completion of fetching of the warmup URL. May be null.
+ base::Callback<void()> fetch_completion_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(WarmupURLFetcher);
+};
+
DataReductionProxyConfig::DataReductionProxyConfig(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
net::NetLog* net_log,
std::unique_ptr<DataReductionProxyConfigValues> config_values,
DataReductionProxyConfigurator* configurator,
DataReductionProxyEventCreator* event_creator)
- : secure_proxy_allowed_(params::ShouldUseSecureProxyByDefault()),
+ : secure_proxy_allowed_(true),
unreachable_(false),
enabled_by_user_(false),
config_values_(std::move(config_values)),
@@ -289,9 +359,11 @@ DataReductionProxyConfig::DataReductionProxyConfig(
auto_lofi_hysteresis_(base::TimeDelta::Max()),
network_prohibitively_slow_(false),
connection_type_(net::NetworkChangeNotifier::GetConnectionType()),
+ connection_type_changed_(false),
lofi_off_(false),
network_quality_at_last_query_(NETWORK_QUALITY_AT_LAST_QUERY_UNKNOWN),
previous_state_lofi_on_(false),
+ is_captive_portal_(false),
weak_factory_(this) {
DCHECK(io_task_runner_);
DCHECK(configurator);
@@ -305,19 +377,24 @@ DataReductionProxyConfig::DataReductionProxyConfig(
DataReductionProxyConfig::~DataReductionProxyConfig() {
net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
+ net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
}
-void DataReductionProxyConfig::InitializeOnIOThread(const scoped_refptr<
- net::URLRequestContextGetter>& url_request_context_getter) {
- secure_proxy_checker_.reset(
- new SecureProxyChecker(url_request_context_getter));
+void DataReductionProxyConfig::InitializeOnIOThread(
+ const scoped_refptr<net::URLRequestContextGetter>&
+ basic_url_request_context_getter,
+ const scoped_refptr<net::URLRequestContextGetter>&
+ url_request_context_getter) {
+ DCHECK(thread_checker_.CalledOnValidThread());
- if (!config_values_->allowed())
- return;
+ secure_proxy_checker_.reset(
+ new SecureProxyChecker(basic_url_request_context_getter));
+ warmup_url_fetcher_.reset(new WarmupURLFetcher(url_request_context_getter));
PopulateAutoLoFiParams();
AddDefaultProxyBypassRules();
net::NetworkChangeNotifier::AddIPAddressObserver(this);
+ net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
// Record accuracy at 3 different intervals. The values used here must remain
// in sync with the suffixes specified in
@@ -332,7 +409,18 @@ void DataReductionProxyConfig::InitializeOnIOThread(const scoped_refptr<
void DataReductionProxyConfig::ReloadConfig() {
DCHECK(thread_checker_.CalledOnValidThread());
- UpdateConfigurator(enabled_by_user_, secure_proxy_allowed_);
+ DCHECK(configurator_);
+
+ const std::vector<net::ProxyServer> proxies_for_http =
+ DataReductionProxyServer::ConvertToNetProxyServers(
+ config_values_->proxies_for_http());
+ if (enabled_by_user_ && !config_values_->holdback() &&
+ !proxies_for_http.empty()) {
+ configurator_->Enable(!secure_proxy_allowed_ || is_captive_portal_,
+ proxies_for_http);
+ } else {
+ configurator_->Disable();
+ }
}
bool DataReductionProxyConfig::WasDataReductionProxyUsed(
@@ -351,8 +439,9 @@ bool DataReductionProxyConfig::IsDataReductionProxy(
if (!proxy_server.is_valid() || proxy_server.is_direct())
return false;
- const std::vector<net::ProxyServer>& proxy_list =
- config_values_->proxies_for_http();
+ const std::vector<net::ProxyServer> proxy_list =
+ DataReductionProxyServer::ConvertToNetProxyServers(
+ config_values_->proxies_for_http());
net::HostPortPair host_port_pair = proxy_server.host_port_pair();
const auto proxy_it =
@@ -457,15 +546,6 @@ bool DataReductionProxyConfig::IsNetworkQualityProhibitivelySlow(
if (!network_quality_estimator)
return false;
- // True iff network type changed since the last call to
- // IsNetworkQualityProhibitivelySlow(). This call happens only on main frame
- // requests.
- bool network_type_changed = false;
- if (net::NetworkChangeNotifier::GetConnectionType() != connection_type_) {
- connection_type_ = net::NetworkChangeNotifier::GetConnectionType();
- network_type_changed = true;
- }
-
const net::EffectiveConnectionType effective_connection_type =
network_quality_estimator->GetEffectiveConnectionType();
@@ -502,12 +582,13 @@ bool DataReductionProxyConfig::IsNetworkQualityProhibitivelySlow(
// Return the cached entry if the last update was within the hysteresis
// duration and if the connection type has not changed.
- if (!network_type_changed && !network_quality_last_checked_.is_null() &&
+ if (!connection_type_changed_ && !network_quality_last_checked_.is_null() &&
GetTicksNow() - network_quality_last_checked_ <= auto_lofi_hysteresis_) {
return network_prohibitively_slow_;
}
network_quality_last_checked_ = GetTicksNow();
+ connection_type_changed_ = false;
if (!is_network_quality_available)
return false;
@@ -606,11 +687,6 @@ bool DataReductionProxyConfig::ContainsDataReductionProxy(
return false;
}
-// Returns true if the Data Reduction Proxy configuration may be used.
-bool DataReductionProxyConfig::allowed() const {
- return config_values_->allowed();
-}
-
// 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 {
@@ -620,10 +696,13 @@ bool DataReductionProxyConfig::promo_allowed() const {
void DataReductionProxyConfig::SetProxyConfig(bool enabled, bool at_startup) {
DCHECK(thread_checker_.CalledOnValidThread());
enabled_by_user_ = enabled;
- UpdateConfigurator(enabled_by_user_, secure_proxy_allowed_);
+ ReloadConfig();
- // Check if the proxy has been restricted explicitly by the carrier.
if (enabled) {
+ HandleCaptivePortal();
+ FetchWarmupURL();
+
+ // Check if the proxy has been restricted explicitly by the carrier.
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the IO thread, and |this| outlives
// |secure_proxy_checker_|.
@@ -634,16 +713,32 @@ void DataReductionProxyConfig::SetProxyConfig(bool enabled, bool at_startup) {
}
}
-void DataReductionProxyConfig::UpdateConfigurator(bool enabled,
- bool secure_proxy_allowed) {
- DCHECK(configurator_);
- const std::vector<net::ProxyServer>& proxies_for_http =
- config_values_->proxies_for_http();
- if (enabled && !config_values_->holdback() && !proxies_for_http.empty()) {
- configurator_->Enable(!secure_proxy_allowed, proxies_for_http);
- } else {
- configurator_->Disable();
- }
+void DataReductionProxyConfig::HandleCaptivePortal() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ bool is_captive_portal = GetIsCaptivePortal();
+ UMA_HISTOGRAM_BOOLEAN("DataReductionProxy.CaptivePortalDetected.Platform",
+ is_captive_portal);
+ if (is_captive_portal == is_captive_portal_)
+ return;
+ is_captive_portal_ = is_captive_portal;
+ ReloadConfig();
+}
+
+bool DataReductionProxyConfig::GetIsCaptivePortal() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+#if defined(OS_ANDROID)
+ return net::android::GetIsCaptivePortal();
+#endif // OS_ANDROID
+ return false;
+}
+
+void DataReductionProxyConfig::UpdateConfigForTesting(
+ bool enabled,
+ bool secure_proxy_allowed) {
+ enabled_by_user_ = enabled;
+ secure_proxy_allowed_ = secure_proxy_allowed;
}
void DataReductionProxyConfig::HandleSecureProxyCheckResponse(
@@ -652,9 +747,10 @@ void DataReductionProxyConfig::HandleSecureProxyCheckResponse(
int http_response_code) {
bool success_response =
base::StartsWith(response, "OK", base::CompareCase::SENSITIVE);
- if (event_creator_)
+ if (event_creator_) {
event_creator_->EndSecureProxyCheck(net_log_with_source_, status.error(),
http_response_code, success_response);
+ }
if (!status.is_success()) {
if (status.error() == net::ERR_INTERNET_DISCONNECTED) {
@@ -668,56 +764,46 @@ void DataReductionProxyConfig::HandleSecureProxyCheckResponse(
std::abs(status.error()));
}
- if (success_response) {
- DVLOG(1) << "The data reduction proxy is unrestricted.";
-
- if (enabled_by_user_) {
- if (!secure_proxy_allowed_) {
- secure_proxy_allowed_ = true;
- // The user enabled the proxy, but sometime previously in the session,
- // the network operator had blocked the secure proxy check and
- // restricted the user. The current network doesn't block the secure
- // proxy check, so don't restrict the proxy configurations.
- ReloadConfig();
- RecordSecureProxyCheckFetchResult(SUCCEEDED_PROXY_ENABLED);
- } else {
- RecordSecureProxyCheckFetchResult(SUCCEEDED_PROXY_ALREADY_ENABLED);
- }
- }
- secure_proxy_allowed_ = true;
+ bool secure_proxy_allowed_past = secure_proxy_allowed_;
+ secure_proxy_allowed_ = success_response;
+ if (!enabled_by_user_)
return;
+
+ if (secure_proxy_allowed_ != secure_proxy_allowed_past)
+ ReloadConfig();
+
+ // Record the result.
+ if (secure_proxy_allowed_past && secure_proxy_allowed_) {
+ RecordSecureProxyCheckFetchResult(SUCCEEDED_PROXY_ALREADY_ENABLED);
+ } else if (secure_proxy_allowed_past && !secure_proxy_allowed_) {
+ RecordSecureProxyCheckFetchResult(FAILED_PROXY_DISABLED);
+ } else if (!secure_proxy_allowed_past && secure_proxy_allowed_) {
+ RecordSecureProxyCheckFetchResult(SUCCEEDED_PROXY_ENABLED);
+ } else {
+ DCHECK(!secure_proxy_allowed_past && !secure_proxy_allowed_);
+ RecordSecureProxyCheckFetchResult(FAILED_PROXY_ALREADY_DISABLED);
}
- DVLOG(1) << "The data reduction proxy is restricted to the configured "
- << "fallback proxy.";
- if (enabled_by_user_) {
- if (secure_proxy_allowed_) {
- // Restrict the proxy.
- secure_proxy_allowed_ = false;
- ReloadConfig();
- RecordSecureProxyCheckFetchResult(FAILED_PROXY_DISABLED);
- } else {
- RecordSecureProxyCheckFetchResult(FAILED_PROXY_ALREADY_DISABLED);
- }
- }
- secure_proxy_allowed_ = false;
+}
+
+void DataReductionProxyConfig::OnConnectionTypeChanged(
+ net::NetworkChangeNotifier::ConnectionType type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ connection_type_changed_ = true;
+ connection_type_ = type;
+ FetchWarmupURL();
}
void DataReductionProxyConfig::OnIPAddressChanged() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
if (enabled_by_user_) {
- DCHECK(config_values_->allowed());
RecordNetworkChangeEvent(IP_CHANGED);
// Reset |network_quality_at_last_query_| to prevent recording of network
// quality prediction accuracy if there was a change in the IP address.
network_quality_at_last_query_ = NETWORK_QUALITY_AT_LAST_QUERY_UNKNOWN;
- bool should_use_secure_proxy = params::ShouldUseSecureProxyByDefault();
- if (!should_use_secure_proxy && secure_proxy_allowed_) {
- secure_proxy_allowed_ = false;
- RecordSecureProxyCheckFetchResult(PROXY_DISABLED_BEFORE_CHECK);
- ReloadConfig();
- }
-
+ HandleCaptivePortal();
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the IO thread, and |this| outlives
// |secure_proxy_checker_|.
@@ -754,12 +840,6 @@ void DataReductionProxyConfig::AddDefaultProxyBypassRules() {
configurator_->AddHostPatternToBypass("*-v4.metric.gstatic.com");
}
-void DataReductionProxyConfig::RecordSecureProxyCheckFetchResult(
- SecureProxyCheckFetchResult result) {
- UMA_HISTOGRAM_ENUMERATION(kUMAProxyProbeURL, result,
- SECURE_PROXY_CHECK_FETCH_RESULT_COUNT);
-}
-
void DataReductionProxyConfig::SecureProxyCheck(
const GURL& secure_proxy_check_url,
FetcherResponseCallback fetcher_callback) {
@@ -774,6 +854,26 @@ void DataReductionProxyConfig::SecureProxyCheck(
fetcher_callback);
}
+void DataReductionProxyConfig::FetchWarmupURL() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!enabled_by_user_ || !params::FetchWarmupURLEnabled())
+ return;
+
+ if (connection_type_ == net::NetworkChangeNotifier::CONNECTION_NONE)
+ return;
+
+ warmup_url_fetcher_->FetchWarmupURL();
+}
+
+void DataReductionProxyConfig::SetWarmupURLFetcherCallbackForTesting(
+ base::Callback<void()> warmup_url_fetched_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ warmup_url_fetcher_->SetWarmupURLFetcherCallbackForTesting(
+ warmup_url_fetched_callback);
+}
+
void DataReductionProxyConfig::SetLoFiModeOff() {
DCHECK(thread_checker_.CalledOnValidThread());
lofi_off_ = true;
@@ -879,8 +979,8 @@ bool DataReductionProxyConfig::ShouldEnableLoFiMode(
if (params::IsLoFiSlowConnectionsOnlyViaFlags() ||
params::IsIncludedInLoFiEnabledFieldTrial()) {
- RecordAutoLoFiRequestHeaderStateChange(previous_state_lofi_on_,
- enable_lofi);
+ RecordAutoLoFiRequestHeaderStateChange(
+ connection_type_, previous_state_lofi_on_, enable_lofi);
previous_state_lofi_on_ = enable_lofi;
}
@@ -907,8 +1007,7 @@ bool DataReductionProxyConfig::ShouldEnableLoFiModeInternal(
return true;
if (params::IsLoFiCellularOnlyViaFlags()) {
- return net::NetworkChangeNotifier::IsConnectionCellular(
- net::NetworkChangeNotifier::GetConnectionType());
+ return net::NetworkChangeNotifier::IsConnectionCellular(connection_type_);
}
if (params::IsLoFiSlowConnectionsOnlyViaFlags() ||
@@ -942,7 +1041,8 @@ base::TimeTicks DataReductionProxyConfig::GetTicksNow() const {
net::ProxyConfig DataReductionProxyConfig::ProxyConfigIgnoringHoldback() const {
std::vector<net::ProxyServer> proxies_for_http =
- config_values_->proxies_for_http();
+ DataReductionProxyServer::ConvertToNetProxyServers(
+ config_values_->proxies_for_http());
if (!enabled_by_user_ || proxies_for_http.empty())
return net::ProxyConfig::CreateDirect();
return configurator_->CreateProxyConfig(!secure_proxy_allowed_,
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 196203352ba..14003b24e07 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
@@ -34,10 +34,8 @@ class SingleThreadTaskRunner;
}
namespace net {
-class HostPortPair;
class NetLog;
class ProxyServer;
-class URLFetcher;
class URLRequest;
class URLRequestContextGetter;
class URLRequestStatus;
@@ -52,8 +50,8 @@ typedef base::Callback<void(const std::string&,
class DataReductionProxyConfigValues;
class DataReductionProxyConfigurator;
class DataReductionProxyEventCreator;
-class DataReductionProxyService;
class SecureProxyChecker;
+class WarmupURLFetcher;
struct DataReductionProxyTypeInfo;
// Values of the UMA DataReductionProxy.ProbeURL histogram.
@@ -88,7 +86,8 @@ enum SecureProxyCheckFetchResult {
// This object lives on the IO thread and all of its methods are expected to be
// called from there.
class DataReductionProxyConfig
- : public net::NetworkChangeNotifier::IPAddressObserver {
+ : public net::NetworkChangeNotifier::IPAddressObserver,
+ public net::NetworkChangeNotifier::ConnectionTypeObserver {
public:
// The caller must ensure that all parameters remain alive for the lifetime
// of the |DataReductionProxyConfig| instance, with the exception of
@@ -106,7 +105,13 @@ class DataReductionProxyConfig
~DataReductionProxyConfig() override;
// Performs initialization on the IO thread.
+ // |basic_url_request_context_getter| is the net::URLRequestContextGetter that
+ // disables the use of alternative protocols and proxies.
+ // |url_request_context_getter| is the default net::URLRequestContextGetter
+ // used for making URL requests.
void InitializeOnIOThread(const scoped_refptr<net::URLRequestContextGetter>&
+ basic_url_request_context_getter,
+ const scoped_refptr<net::URLRequestContextGetter>&
url_request_context_getter);
// Sets the proxy configs, enabling or disabling the proxy according to
@@ -175,9 +180,6 @@ class DataReductionProxyConfig
virtual bool ContainsDataReductionProxy(
const net::ProxyConfig::ProxyRules& proxy_rules) const;
- // Returns true if the Data Reduction Proxy configuration may be used.
- bool allowed() 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;
@@ -202,11 +204,6 @@ class DataReductionProxyConfig
net::ProxyConfig ProxyConfigIgnoringHoldback() const;
protected:
- // Virtualized for mocking. Records UMA containing the result of requesting
- // the secure proxy check.
- virtual void RecordSecureProxyCheckFetchResult(
- SecureProxyCheckFetchResult result);
-
// Virtualized for mocking. Returns the list of network interfaces in use.
// |interfaces| can be null.
virtual void GetNetworkList(net::NetworkInterfaceList* interfaces,
@@ -219,17 +216,17 @@ class DataReductionProxyConfig
virtual base::TimeTicks GetTicksNow() const;
+ // Updates the Data Reduction Proxy configurator with the current config.
+ void UpdateConfigForTesting(bool enabled, bool restricted);
+
+ // Updates the callback that is called when the warmup URL has been fetched.
+ void SetWarmupURLFetcherCallbackForTesting(
+ base::Callback<void()> warmup_url_fetched_callback);
+
private:
- friend class DataReductionProxyConfigTest;
friend class MockDataReductionProxyConfig;
friend class TestDataReductionProxyConfig;
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
- TestGetDataReductionProxies);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
- TestOnIPAddressChanged);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
- TestOnIPAddressChanged_SecureProxyDisabledByDefault);
- FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
TestSetProxyConfigsHoldback);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
AreProxiesBypassed);
@@ -242,6 +239,7 @@ class DataReductionProxyConfig
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest, LoFiAccuracy);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
LoFiAccuracyNonZeroDelay);
+ FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest, WarmupURL);
// Values of the estimated network quality at the beginning of the most
// recent query of the Network Quality Estimator.
@@ -253,9 +251,8 @@ class DataReductionProxyConfig
// NetworkChangeNotifier::IPAddressObserver:
void OnIPAddressChanged() override;
-
- // Updates the Data Reduction Proxy configurator with the current config.
- virtual void UpdateConfigurator(bool enabled, bool restricted);
+ void OnConnectionTypeChanged(
+ net::NetworkChangeNotifier::ConnectionType type) override;
// Populates the parameters for the Lo-Fi field trial if the session is part
// of either Lo-Fi enabled or Lo-Fi control field trial group.
@@ -314,8 +311,24 @@ class DataReductionProxyConfig
bool IsEffectiveConnectionTypeSlowerThanThreshold(
net::EffectiveConnectionType effective_connection_type) const;
+ // Checks if the current network has captive portal, and handles the result.
+ // If the captive portal probe was blocked on the current network, disables
+ // the use of secure proxies.
+ void HandleCaptivePortal();
+
+ // Returns true if the current network has captive portal. Virtualized
+ // for testing.
+ virtual bool GetIsCaptivePortal() const;
+
+ // Fetches the warmup URL.
+ void FetchWarmupURL();
+
+ // URL fetcher used for performing the secure proxy check.
std::unique_ptr<SecureProxyChecker> secure_proxy_checker_;
+ // URL fetcher used for fetching the warmup URL.
+ std::unique_ptr<WarmupURLFetcher> warmup_url_fetcher_;
+
// Indicates if the secure Data Reduction Proxy can be used or not.
bool secure_proxy_allowed_;
@@ -362,10 +375,14 @@ class DataReductionProxyConfig
// than |auto_lofi_hysteresis_|.
bool network_prohibitively_slow_;
- // Set to the connection type reported by NetworkChangeNotifier when the
- // network quality was last checked.
+ // The current connection type.
net::NetworkChangeNotifier::ConnectionType connection_type_;
+ // True if the connection type changed since the last call to
+ // IsNetworkQualityProhibitivelySlow(). This call happens only on main frame
+ // requests.
+ bool connection_type_changed_;
+
// If true, Lo-Fi is turned off for the rest of the session. This is set to
// true if Lo-Fi is disabled via flags or if the user implicitly opts out.
bool lofi_off_;
@@ -388,6 +405,10 @@ class DataReductionProxyConfig
// quality prediction is recorded.
std::vector<base::TimeDelta> lofi_accuracy_recording_intervals_;
+ // Set to true if the captive portal probe for the current network has been
+ // blocked.
+ bool is_captive_portal_;
+
base::WeakPtrFactory<DataReductionProxyConfig> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(DataReductionProxyConfig);
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
index 715f3f1d1c4..2b58c6b14f5 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -24,6 +24,7 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h"
#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
@@ -80,14 +81,16 @@ const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
};
// Extracts the list of Data Reduction Proxy servers to use for HTTP requests.
-std::vector<net::ProxyServer> GetProxiesForHTTP(
+std::vector<DataReductionProxyServer> GetProxiesForHTTP(
const data_reduction_proxy::ProxyConfig& proxy_config) {
- std::vector<net::ProxyServer> proxies;
+ std::vector<DataReductionProxyServer> proxies;
for (const auto& server : proxy_config.http_proxy_servers()) {
if (server.scheme() != ProxyServer_ProxyScheme_UNSPECIFIED) {
- proxies.push_back(net::ProxyServer(
- protobuf_parser::SchemeFromProxyScheme(server.scheme()),
- net::HostPortPair(server.host(), server.port())));
+ proxies.push_back(DataReductionProxyServer(
+ net::ProxyServer(
+ protobuf_parser::SchemeFromProxyScheme(server.scheme()),
+ net::HostPortPair(server.host(), server.port())),
+ server.type()));
}
}
@@ -151,7 +154,8 @@ DataReductionProxyConfigServiceClient::DataReductionProxyConfigServiceClient(
foreground_fetch_pending_(false),
#endif
previous_request_failed_authentication_(false),
- failed_attempts_before_success_(0) {
+ failed_attempts_before_success_(0),
+ fetch_in_progress_(false) {
DCHECK(request_options);
DCHECK(config_values);
DCHECK(config);
@@ -287,6 +291,15 @@ bool DataReductionProxyConfigServiceClient::ShouldRetryDueToAuthFailure(
RecordAuthExpiredHistogram(true);
previous_request_failed_authentication_ = true;
InvalidateConfig();
+ DCHECK(!config_->IsDataReductionProxy(proxy_server, nullptr));
+
+ if (fetch_in_progress_) {
+ // If a client config fetch is already in progress, then do not start
+ // another fetch since starting a new fetch will cause extra data
+ // usage, and also cancel the ongoing fetch.
+ return true;
+ }
+
RetrieveConfig();
if (!load_timing_info.send_start.is_null() &&
@@ -341,6 +354,7 @@ void DataReductionProxyConfigServiceClient::OnURLFetchComplete(
const net::URLFetcher* source) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(source == fetcher_.get());
+ fetch_in_progress_ = false;
net::URLRequestStatus status = source->GetStatus();
std::string response;
source->GetResponseAsString(&response);
@@ -374,6 +388,7 @@ void DataReductionProxyConfigServiceClient::RetrieveRemoteConfig() {
}
fetcher_ = std::move(fetcher);
+ fetch_in_progress_ = true;
fetcher_->Start();
}
@@ -403,8 +418,9 @@ DataReductionProxyConfigServiceClient::GetURLFetcherForConfig(
fetcher->SetRequestContext(url_request_context_getter_);
// |fetcher| should not retry on 5xx errors since the server may already be
// overloaded. Spurious 5xx errors are still retried on exponential backoff.
- static const int kMaxRetries = 5;
- fetcher->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries);
+ // |fetcher| should not retry on network changes since a new fetch will be
+ // initiated.
+ fetcher->SetAutomaticallyRetryOnNetworkChanges(0);
return fetcher;
}
@@ -429,7 +445,7 @@ void DataReductionProxyConfigServiceClient::HandleResponse(
// These are proxies listed in the config. The proxies that client eventually
// ends up using depend on the field trials.
- std::vector<net::ProxyServer> proxies;
+ std::vector<DataReductionProxyServer> proxies;
base::TimeDelta refresh_duration;
if (succeeded) {
proxies = GetProxiesForHTTP(config.proxy_config());
@@ -457,10 +473,11 @@ void DataReductionProxyConfigServiceClient::HandleResponse(
succeeded, refresh_duration, GetBackoffEntry()->GetTimeUntilRelease());
SetConfigRefreshTimer(next_config_refresh_time);
- event_creator_->EndConfigRequest(net_log_with_source_, status.error(),
- response_code,
- GetBackoffEntry()->failure_count(), proxies,
- refresh_duration, next_config_refresh_time);
+ event_creator_->EndConfigRequest(
+ net_log_with_source_, status.error(), response_code,
+ GetBackoffEntry()->failure_count(),
+ DataReductionProxyServer::ConvertToNetProxyServers(proxies),
+ refresh_duration, next_config_refresh_time);
}
bool DataReductionProxyConfigServiceClient::ParseAndApplyProxyConfig(
@@ -478,7 +495,7 @@ bool DataReductionProxyConfigServiceClient::ParseAndApplyProxyConfig(
if (!config.has_proxy_config())
return false;
- std::vector<net::ProxyServer> proxies =
+ std::vector<DataReductionProxyServer> proxies =
GetProxiesForHTTP(config.proxy_config());
if (proxies.empty())
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
index 4a3d8e7c600..c14d723f453 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
@@ -46,7 +46,6 @@ class DataReductionProxyIOData;
class DataReductionProxyMutableConfigValues;
class DataReductionProxyParams;
class DataReductionProxyRequestOptions;
-class DataReductionProxyService;
typedef base::Callback<void(const std::string&)> ConfigStorer;
@@ -277,6 +276,9 @@ class DataReductionProxyConfigServiceClient
// Time when the IP address last changed.
base::TimeTicks last_ip_address_change_;
+ // True if a client config fetch is in progress.
+ bool fetch_in_progress_;
+
// Enforce usage on the IO thread.
base::ThreadChecker thread_checker_;
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
index b30a9b900ad..92467e26608 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
@@ -22,10 +22,12 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.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"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "net/base/network_change_notifier.h"
@@ -81,9 +83,11 @@ ClientConfig CreateConfig(const std::string& session_key,
ProxyServer_ProxyScheme primary_scheme,
const std::string& primary_host,
int primary_port,
+ const ProxyServer_ProxyType& primary_proxy_type,
ProxyServer_ProxyScheme secondary_scheme,
const std::string& secondary_host,
int secondary_port,
+ const ProxyServer_ProxyType& secondary_proxy_type,
float reporting_fraction) {
ClientConfig config;
@@ -102,11 +106,14 @@ ClientConfig CreateConfig(const std::string& session_key,
primary_proxy->set_scheme(primary_scheme);
primary_proxy->set_host(primary_host);
primary_proxy->set_port(primary_port);
+ primary_proxy->set_type(primary_proxy_type);
+
ProxyServer* secondary_proxy =
config.mutable_proxy_config()->add_http_proxy_servers();
secondary_proxy->set_scheme(secondary_scheme);
secondary_proxy->set_host(secondary_host);
secondary_proxy->set_port(secondary_port);
+ secondary_proxy->set_type(secondary_proxy_type);
return config;
}
@@ -162,33 +169,38 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test {
ClientConfig config =
CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
ProxyServer_ProxyScheme_HTTPS, "origin.net", 443,
- ProxyServer_ProxyScheme_HTTP, "fallback.net", 80, 0.5f);
+ ProxyServer::CORE, ProxyServer_ProxyScheme_HTTP,
+ "fallback.net", 80, ProxyServer::UNSPECIFIED_TYPE, 0.5f);
config.SerializeToString(&config_);
encoded_config_ = EncodeConfig(config);
ClientConfig previous_config = CreateConfig(
kOldSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
- ProxyServer_ProxyScheme_HTTPS, "old.origin.net", 443,
- ProxyServer_ProxyScheme_HTTP, "old.fallback.net", 80, 0.0f);
+ ProxyServer_ProxyScheme_HTTPS, "old.origin.net", 443, ProxyServer::CORE,
+ ProxyServer_ProxyScheme_HTTP, "old.fallback.net", 80,
+ ProxyServer::UNSPECIFIED_TYPE, 0.0f);
previous_config.SerializeToString(&previous_config_);
ClientConfig persisted =
CreateConfig(kPersistedSessionKey, kConfigRefreshDurationSeconds, 0,
ProxyServer_ProxyScheme_HTTPS, "persisted.net", 443,
- ProxyServer_ProxyScheme_HTTP, "persisted.net", 80, 0.0f);
+ ProxyServer::CORE, ProxyServer_ProxyScheme_HTTP,
+ "persisted.net", 80, ProxyServer::UNSPECIFIED_TYPE, 0.0f);
loaded_config_ = EncodeConfig(persisted);
ClientConfig zero_reporting_fraction_config =
CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
ProxyServer_ProxyScheme_HTTPS, "origin.net", 443,
- ProxyServer_ProxyScheme_HTTP, "origin.net", 0, 0.0f);
+ ProxyServer::CORE, ProxyServer_ProxyScheme_HTTP,
+ "origin.net", 0, ProxyServer::UNSPECIFIED_TYPE, 0.0f);
zero_reporting_fraction_encoded_config_ =
EncodeConfig(zero_reporting_fraction_config);
ClientConfig one_reporting_fraction_config =
CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
- ProxyServer_ProxyScheme_HTTPS, "", 443,
- ProxyServer_ProxyScheme_HTTP, "", 0, 1.0f);
+ ProxyServer_ProxyScheme_HTTPS, "", 443, ProxyServer::CORE,
+ ProxyServer_ProxyScheme_HTTP, "", 0,
+ ProxyServer::UNSPECIFIED_TYPE, 1.0f);
one_reporting_fraction_encoded_config_ =
EncodeConfig(one_reporting_fraction_config);
@@ -198,14 +210,16 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test {
ClientConfig empty_reporting_fraction_config =
CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
ProxyServer_ProxyScheme_HTTPS, "origin.net", 443,
- ProxyServer_ProxyScheme_HTTP, "origin.net", 0, -1.0f);
+ ProxyServer::CORE, ProxyServer_ProxyScheme_HTTP,
+ "origin.net", 0, ProxyServer::UNSPECIFIED_TYPE, -1.0f);
empty_reporting_fraction_encoded_config_ =
EncodeConfig(empty_reporting_fraction_config);
ClientConfig half_reporting_fraction_config =
CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
ProxyServer_ProxyScheme_HTTPS, "origin.net", 443,
- ProxyServer_ProxyScheme_HTTP, "origin.net", 0, 0.5f);
+ ProxyServer::CORE, ProxyServer_ProxyScheme_HTTP,
+ "origin.net", 0, ProxyServer::UNSPECIFIED_TYPE, 0.5f);
half_reporting_fraction_encoded_config_ =
EncodeConfig(half_reporting_fraction_config);
@@ -224,7 +238,8 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test {
}
void SetDataReductionProxyEnabled(bool enabled, bool secure_proxy_allowed) {
- test_context_->config()->SetStateForTest(enabled, secure_proxy_allowed);
+ test_context_->config()->UpdateConfigForTesting(enabled,
+ secure_proxy_allowed);
}
void ResetBackoffEntryReleaseTime() {
@@ -232,44 +247,112 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test {
}
void VerifyRemoteSuccess(bool expect_secure_proxies) {
- std::vector<net::ProxyServer> expected_http_proxies;
+ std::vector<DataReductionProxyServer> expected_http_proxies;
if (expect_secure_proxies) {
- expected_http_proxies.push_back(net::ProxyServer::FromURI(
- kSuccessOrigin, net::ProxyServer::SCHEME_HTTP));
+ expected_http_proxies.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI(kSuccessOrigin,
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::CORE));
}
- expected_http_proxies.push_back(net::ProxyServer::FromURI(
- kSuccessFallback, net::ProxyServer::SCHEME_HTTP));
+ expected_http_proxies.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI(kSuccessFallback,
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
+
EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds),
config_client()->GetDelay());
- EXPECT_EQ(expected_http_proxies, GetConfiguredProxiesForHttp());
+ EXPECT_EQ(DataReductionProxyServer::ConvertToNetProxyServers(
+ expected_http_proxies),
+ GetConfiguredProxiesForHttp());
EXPECT_EQ(kSuccessSessionKey, request_options()->GetSecureSession());
// The config should be persisted on the pref.
EXPECT_EQ(encoded_config(), persisted_config());
EXPECT_EQ(0.5f, pingback_reporting_fraction());
+
+ // Verify that the data reduction proxy servers are correctly set.
+ // The first proxy must have type CORE. The second proxy must have type
+ // UNSPECIFIED_TYPE since these are the types specified in the encoded
+ // configs.
+ ASSERT_EQ(
+ 2U, test_context_->mutable_config_values()->proxies_for_http().size());
+ EXPECT_EQ(ProxyServer::CORE, test_context_->mutable_config_values()
+ ->proxies_for_http()
+ .at(0)
+ .GetProxyTypeForTesting());
+ EXPECT_EQ(ProxyServer::UNSPECIFIED_TYPE,
+ test_context_->mutable_config_values()
+ ->proxies_for_http()
+ .at(1)
+ .GetProxyTypeForTesting());
}
void VerifyRemoteSuccessWithOldConfig() {
- std::vector<net::ProxyServer> expected_http_proxies;
- expected_http_proxies.push_back(net::ProxyServer::FromURI(
- kOldSuccessOrigin, net::ProxyServer::SCHEME_HTTP));
- expected_http_proxies.push_back(net::ProxyServer::FromURI(
- kOldSuccessFallback, net::ProxyServer::SCHEME_HTTP));
+ std::vector<DataReductionProxyServer> expected_http_proxies;
+ expected_http_proxies.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI(kOldSuccessOrigin,
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::CORE));
+ expected_http_proxies.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI(kOldSuccessFallback,
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
+
EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds),
config_client()->GetDelay());
- EXPECT_EQ(expected_http_proxies, GetConfiguredProxiesForHttp());
+ EXPECT_EQ(DataReductionProxyServer::ConvertToNetProxyServers(
+ expected_http_proxies),
+ GetConfiguredProxiesForHttp());
EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession());
+
+ // Verify that the data reduction proxy servers are correctly set.
+ // The first proxy must have type CORE. The second proxy must have type
+ // UNSPECIFIED_TYPE since these are the types specified in the encoded
+ // configs.
+ ASSERT_EQ(
+ 2U, test_context_->mutable_config_values()->proxies_for_http().size());
+ EXPECT_EQ(ProxyServer::CORE, test_context_->mutable_config_values()
+ ->proxies_for_http()
+ .at(0)
+ .GetProxyTypeForTesting());
+ EXPECT_EQ(ProxyServer::UNSPECIFIED_TYPE,
+ test_context_->mutable_config_values()
+ ->proxies_for_http()
+ .at(1)
+ .GetProxyTypeForTesting());
}
void VerifySuccessWithLoadedConfig(bool expect_secure_proxies) {
- std::vector<net::ProxyServer> expected_http_proxies;
+ std::vector<DataReductionProxyServer> expected_http_proxies;
if (expect_secure_proxies) {
- expected_http_proxies.push_back(net::ProxyServer::FromURI(
- kPersistedOrigin, net::ProxyServer::SCHEME_HTTP));
+ expected_http_proxies.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI(kPersistedOrigin,
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::CORE));
}
- expected_http_proxies.push_back(net::ProxyServer::FromURI(
- kPersistedFallback, net::ProxyServer::SCHEME_HTTP));
- EXPECT_EQ(expected_http_proxies, GetConfiguredProxiesForHttp());
+ expected_http_proxies.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI(kPersistedFallback,
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
+ EXPECT_EQ(DataReductionProxyServer::ConvertToNetProxyServers(
+ expected_http_proxies),
+ GetConfiguredProxiesForHttp());
EXPECT_EQ(kPersistedSessionKey, request_options()->GetSecureSession());
+
+ // Verify that the data reduction proxy servers are correctly set.
+ // The first proxy must have type CORE. The second proxy must have type
+ // UNSPECIFIED_TYPE since these are the types specified in the encoded
+ // configs.
+ ASSERT_EQ(
+ 2U, test_context_->mutable_config_values()->proxies_for_http().size());
+ EXPECT_EQ(ProxyServer::CORE, test_context_->mutable_config_values()
+ ->proxies_for_http()
+ .at(0)
+ .GetProxyTypeForTesting());
+ EXPECT_EQ(ProxyServer::UNSPECIFIED_TYPE,
+ test_context_->mutable_config_values()
+ ->proxies_for_http()
+ .at(1)
+ .GetProxyTypeForTesting());
}
TestDataReductionProxyConfigServiceClient* config_client() {
@@ -661,8 +744,6 @@ TEST_F(DataReductionProxyConfigServiceClientTest, OnIPAddressChangeDisabled) {
// headers matches the currrent session key.
TEST_F(DataReductionProxyConfigServiceClientTest, AuthFailure) {
Init(true);
- net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
- net::NetworkChangeNotifier::CONNECTION_WIFI);
net::HttpRequestHeaders request_headers;
request_headers.SetHeader(
"chrome-proxy", "something=something_else, s=" +
@@ -744,6 +825,170 @@ TEST_F(DataReductionProxyConfigServiceClientTest, AuthFailure) {
1 /* AUTH_EXPIRED_SESSION_KEY_MATCH */, 2);
}
+// Verifies that a new config is not fetched due to auth failure while a
+// previous client config fetch triggered due to auth failure is already in
+// progress.
+TEST_F(DataReductionProxyConfigServiceClientTest, MultipleAuthFailures) {
+ Init(true);
+ net::HttpRequestHeaders request_headers;
+ request_headers.SetHeader(
+ "chrome-proxy", "something=something_else, s=" +
+ std::string(kOldSuccessSessionKey) + ", key=value");
+
+ base::HistogramTester histogram_tester;
+ AddMockPreviousSuccess();
+ AddMockSuccess();
+
+ SetDataReductionProxyEnabled(true, true);
+ EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.ConfigService.AuthExpired", 0);
+ config_client()->RetrieveConfig();
+ RunUntilIdle();
+ // First remote config should be fetched.
+ VerifyRemoteSuccessWithOldConfig();
+ EXPECT_TRUE(configurator()->GetProxyConfig().is_valid());
+ EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession());
+ EXPECT_EQ(0, config_client()->GetBackoffErrorCount());
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.ConfigService.AuthExpired", false, 1);
+
+ // Trigger an auth failure.
+ scoped_refptr<net::HttpResponseHeaders> parsed(new net::HttpResponseHeaders(
+ "HTTP/1.1 407 Proxy Authentication Required\n"));
+ net::ProxyServer origin = net::ProxyServer::FromURI(
+ kOldSuccessOrigin, net::ProxyServer::SCHEME_HTTP);
+ // Calling ShouldRetryDueToAuthFailure should trigger fetching of remote
+ // config.
+ net::LoadTimingInfo load_timing_info;
+ load_timing_info.request_start =
+ base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1);
+ load_timing_info.send_start = load_timing_info.request_start;
+ EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure(
+ request_headers, parsed.get(), origin, load_timing_info));
+ EXPECT_EQ(1, config_client()->GetBackoffErrorCount());
+ EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+
+ // Persisted config on pref should be cleared.
+ EXPECT_TRUE(persisted_config().empty());
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ConfigService.AuthExpired", false, 1);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ConfigService.AuthExpired", true, 1);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.ConfigService.AuthFailure.LatencyPenalty", 1);
+
+ // Trigger a second auth failure.
+ EXPECT_EQ(std::string(), request_options()->GetSecureSession());
+ request_headers.SetHeader(
+ "chrome-proxy", "something=something_else, s=" +
+ std::string(kSuccessSessionKey) + ", key=value");
+ // Calling ShouldRetryDueToAuthFailure should trigger fetching of remote
+ // config.
+ EXPECT_FALSE(config_client()->ShouldRetryDueToAuthFailure(
+ request_headers, parsed.get(), origin, load_timing_info));
+ EXPECT_EQ(1, config_client()->GetBackoffErrorCount());
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ConfigService.AuthExpired", false, 1);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ConfigService.AuthExpired", true, 1);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.ConfigService.AuthFailure.LatencyPenalty", 1);
+
+ RunUntilIdle();
+ VerifyRemoteSuccess(true);
+
+ // Config should be fetched successfully.
+ EXPECT_FALSE(persisted_config().empty());
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.ConfigService.AuthExpiredSessionKey",
+ 1 /* AUTH_EXPIRED_SESSION_KEY_MATCH */, 1);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ConfigService.AuthExpired", false, 2);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ConfigService.AuthExpired", true, 1);
+}
+
+// Verifies that a new config is not fetched due to auth failure while a
+// previous client config fetch triggered due to IP address changeis already in
+// progress.
+TEST_F(DataReductionProxyConfigServiceClientTest,
+ IPAddressChangeWithAuthFailure) {
+ Init(true);
+ net::HttpRequestHeaders request_headers;
+ request_headers.SetHeader(
+ "chrome-proxy", "something=something_else, s=" +
+ std::string(kOldSuccessSessionKey) + ", key=value");
+
+ base::HistogramTester histogram_tester;
+ AddMockPreviousSuccess();
+ AddMockSuccess();
+
+ SetDataReductionProxyEnabled(true, true);
+ EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.ConfigService.AuthExpired", 0);
+ config_client()->RetrieveConfig();
+
+ RunUntilIdle();
+ // First remote config should be fetched.
+ VerifyRemoteSuccessWithOldConfig();
+ EXPECT_TRUE(configurator()->GetProxyConfig().is_valid());
+ EXPECT_EQ(kOldSuccessSessionKey, request_options()->GetSecureSession());
+ EXPECT_EQ(0, config_client()->GetBackoffErrorCount());
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.ConfigService.AuthExpired", false, 1);
+
+ // Trigger IP address change again.
+ AddMockPreviousSuccess();
+ AddMockPreviousSuccess();
+
+ SetDataReductionProxyEnabled(true, true);
+ EXPECT_TRUE(configurator()->GetProxyConfig().is_valid());
+ config_client()->RetrieveConfig();
+
+ // Trigger an auth failure.
+ scoped_refptr<net::HttpResponseHeaders> parsed(new net::HttpResponseHeaders(
+ "HTTP/1.1 407 Proxy Authentication Required\n"));
+ net::ProxyServer origin = net::ProxyServer::FromURI(
+ kOldSuccessOrigin, net::ProxyServer::SCHEME_HTTP);
+ // Calling ShouldRetryDueToAuthFailure should trigger fetching of remote
+ // config.
+ net::LoadTimingInfo load_timing_info;
+ load_timing_info.request_start =
+ base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1);
+ load_timing_info.send_start = load_timing_info.request_start;
+ EXPECT_TRUE(config_client()->ShouldRetryDueToAuthFailure(
+ request_headers, parsed.get(), origin, load_timing_info));
+ EXPECT_EQ(1, config_client()->GetBackoffErrorCount());
+ EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+
+ // Persisted config on pref should be cleared.
+ EXPECT_TRUE(persisted_config().empty());
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ConfigService.AuthExpired", false, 1);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ConfigService.AuthExpired", true, 1);
+
+ EXPECT_FALSE(configurator()->GetProxyConfig().is_valid());
+ // Persisted config on pref should be cleared.
+ EXPECT_TRUE(persisted_config().empty());
+
+ // Config should be fetched now.
+ RunUntilIdle();
+ VerifyRemoteSuccess(true);
+
+ EXPECT_TRUE(configurator()->GetProxyConfig().is_valid());
+ // Persisted config on pref should be cleared.
+ EXPECT_FALSE(persisted_config().empty());
+ EXPECT_EQ(kSuccessSessionKey, request_options()->GetSecureSession());
+ EXPECT_EQ(0, config_client()->GetBackoffErrorCount());
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ConfigService.AuthExpired", false, 2);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ConfigService.AuthExpired", true, 1);
+}
+
// Verifies the correctness of AuthFailure when the session key in the request
// headers do not match the currrent session key.
TEST_F(DataReductionProxyConfigServiceClientTest,
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 ac04ec6a6c1..470bc614fa0 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
@@ -54,7 +54,8 @@ TestDataReductionProxyConfig::TestDataReductionProxyConfig(
tick_clock_(nullptr),
network_quality_prohibitively_slow_set_(false),
network_quality_prohibitively_slow_(false),
- lofi_accuracy_recording_intervals_set_(false) {
+ lofi_accuracy_recording_intervals_set_(false),
+ is_captive_portal_(false) {
network_interfaces_.reset(new net::NetworkInterfaceList());
}
@@ -89,13 +90,6 @@ DataReductionProxyConfigValues* TestDataReductionProxyConfig::config_values() {
return config_values_.get();
}
-void TestDataReductionProxyConfig::SetStateForTest(
- bool enabled_by_user,
- bool secure_proxy_allowed) {
- enabled_by_user_ = enabled_by_user;
- secure_proxy_allowed_ = secure_proxy_allowed;
-}
-
void TestDataReductionProxyConfig::ResetLoFiStatusForTest() {
lofi_off_ = false;
}
@@ -157,6 +151,14 @@ void TestDataReductionProxyConfig::ResetWasDataReductionProxyUsed() {
proxy_index_.reset();
}
+void TestDataReductionProxyConfig::SetIsCaptivePortal(bool is_captive_portal) {
+ is_captive_portal_ = is_captive_portal;
+}
+
+bool TestDataReductionProxyConfig::GetIsCaptivePortal() const {
+ return is_captive_portal_;
+}
+
MockDataReductionProxyConfig::MockDataReductionProxyConfig(
std::unique_ptr<DataReductionProxyConfigValues> config_values,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
@@ -172,12 +174,6 @@ MockDataReductionProxyConfig::MockDataReductionProxyConfig(
MockDataReductionProxyConfig::~MockDataReductionProxyConfig() {
}
-void MockDataReductionProxyConfig::UpdateConfigurator(
- bool enabled,
- bool secure_proxy_allowed) {
- DataReductionProxyConfig::UpdateConfigurator(enabled, secure_proxy_allowed);
-}
-
void MockDataReductionProxyConfig::ResetLoFiStatusForTest() {
lofi_off_ = false;
}
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 4d00ecb0c96..4198e68ad23 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
@@ -74,10 +74,6 @@ class TestDataReductionProxyConfig : public DataReductionProxyConfig {
// TODO(jeremyim): Rationalize with test_params().
DataReductionProxyConfigValues* config_values();
- // Allows tests to set the internal state.
- void SetStateForTest(bool enabled_by_user,
- bool secure_proxy_enabled);
-
// Resets the Lo-Fi status to default state.
void ResetLoFiStatusForTest();
@@ -118,7 +114,15 @@ class TestDataReductionProxyConfig : public DataReductionProxyConfig {
// Resets the behavior of WasDataReductionProxyUsed() calls.
void ResetWasDataReductionProxyUsed();
+ // Sets if the captive portal probe has been blocked for the current network.
+ void SetIsCaptivePortal(bool is_captive_portal);
+
+ using DataReductionProxyConfig::UpdateConfigForTesting;
+ using DataReductionProxyConfig::SetWarmupURLFetcherCallbackForTesting;
+
private:
+ bool GetIsCaptivePortal() const override;
+
base::TickClock* tick_clock_;
base::Optional<bool> was_data_reduction_proxy_used_;
@@ -133,6 +137,10 @@ class TestDataReductionProxyConfig : public DataReductionProxyConfig {
bool lofi_accuracy_recording_intervals_set_;
std::vector<base::TimeDelta> lofi_accuracy_recording_intervals_;
+ // Set to true if the captive portal probe for the current network has been
+ // blocked.
+ bool is_captive_portal_;
+
DISALLOW_COPY_AND_ASSIGN(TestDataReductionProxyConfig);
};
@@ -149,8 +157,6 @@ class MockDataReductionProxyConfig : public TestDataReductionProxyConfig {
DataReductionProxyEventCreator* event_creator);
~MockDataReductionProxyConfig();
- MOCK_METHOD1(RecordSecureProxyCheckFetchResult,
- void(SecureProxyCheckFetchResult result));
MOCK_METHOD2(SetProxyPrefs, void(bool enabled, bool at_startup));
MOCK_CONST_METHOD2(IsDataReductionProxy,
bool(const net::ProxyServer& proxy_server,
@@ -174,7 +180,7 @@ class MockDataReductionProxyConfig : public TestDataReductionProxyConfig {
IsNetworkQualityProhibitivelySlow,
bool(const net::NetworkQualityEstimator* network_quality_estimator));
- void UpdateConfigurator(bool enabled, bool restricted) override;
+ using DataReductionProxyConfig::UpdateConfigForTesting;
// Resets the Lo-Fi status to default state.
void ResetLoFiStatusForTest();
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 7c52872f462..585bf4ae405 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
@@ -14,10 +14,13 @@
#include <vector>
#include "base/command_line.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/metrics/field_trial.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"
@@ -32,19 +35,23 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.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"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "components/variations/variations_associated_data.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
+#include "net/base/network_change_notifier.h"
#include "net/http/http_status_code.h"
#include "net/log/test_net_log.h"
#include "net/nqe/effective_connection_type.h"
#include "net/nqe/external_estimate_provider.h"
-#include "net/nqe/network_quality_estimator.h"
#include "net/nqe/network_quality_estimator_test_util.h"
#include "net/proxy/proxy_server.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request.h"
#include "net/url_request/url_request_test_util.h"
using testing::_;
@@ -79,29 +86,24 @@ class DataReductionProxyConfigTest : public testing::Test {
~DataReductionProxyConfigTest() override {}
void SetUp() override {
+ net::NetworkChangeNotifier::SetTestNotificationsOnly(true);
+ base::RunLoop().RunUntilIdle();
+ network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
+
test_context_ = DataReductionProxyTestContext::Builder()
.WithMockConfig()
.WithMockDataReductionProxyService()
.Build();
- ResetSettings(true, true, true, false);
+ ResetSettings(true, false);
expected_params_.reset(new TestDataReductionProxyParams(
- DataReductionProxyParams::kAllowed |
- DataReductionProxyParams::kFallbackAllowed |
- DataReductionProxyParams::kPromoAllowed,
+ DataReductionProxyParams::kPromoAllowed,
TestDataReductionProxyParams::HAS_EVERYTHING));
}
- void ResetSettings(bool allowed,
- bool fallback_allowed,
- bool promo_allowed,
- bool holdback) {
+ void ResetSettings(bool promo_allowed, bool holdback) {
int flags = 0;
- if (allowed)
- flags |= DataReductionProxyParams::kAllowed;
- if (fallback_allowed)
- flags |= DataReductionProxyParams::kFallbackAllowed;
if (promo_allowed)
flags |= DataReductionProxyParams::kPromoAllowed;
if (holdback)
@@ -113,10 +115,6 @@ class DataReductionProxyConfigTest : public testing::Test {
return message_loop_.task_runner();
}
- void ExpectSecureProxyCheckResult(SecureProxyCheckFetchResult result) {
- EXPECT_CALL(*config(), RecordSecureProxyCheckFetchResult(result)).Times(1);
- }
-
class TestResponder {
public:
void ExecuteCallback(FetcherResponseCallback callback) {
@@ -130,13 +128,13 @@ class DataReductionProxyConfigTest : public testing::Test {
void CheckSecureProxyCheckOnIPChange(
const std::string& response,
+ bool is_captive_portal,
int response_code,
net::URLRequestStatus status,
SecureProxyCheckFetchResult expected_fetch_result,
const std::vector<net::ProxyServer>& expected_proxies_for_http) {
base::HistogramTester histogram_tester;
- ExpectSecureProxyCheckResult(expected_fetch_result);
TestResponder responder;
responder.response = response;
responder.status = status;
@@ -145,7 +143,8 @@ class DataReductionProxyConfigTest : public testing::Test {
.Times(1)
.WillRepeatedly(testing::WithArgs<1>(
testing::Invoke(&responder, &TestResponder::ExecuteCallback)));
- config()->OnIPAddressChanged();
+ config()->SetIsCaptivePortal(is_captive_portal);
+ net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
test_context_->RunUntilIdle();
EXPECT_EQ(expected_proxies_for_http, GetConfiguredProxiesForHttp());
@@ -157,6 +156,24 @@ class DataReductionProxyConfigTest : public testing::Test {
histogram_tester.ExpectTotalCount("DataReductionProxy.ProbeURLNetError",
0);
}
+ histogram_tester.ExpectUniqueSample("DataReductionProxy.ProbeURL",
+ expected_fetch_result, 1);
+
+ // Recorded on every IP change.
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.CaptivePortalDetected.Platform", is_captive_portal,
+ 1);
+ }
+
+ void WarmupURLFetchedCallBack() const {
+ warmup_url_fetched_run_loop_->Quit();
+ }
+
+ void WarmUpURLFetchedRunLoop() {
+ warmup_url_fetched_run_loop_.reset(new base::RunLoop());
+ // |warmup_url_fetched_run_loop_| will run until WarmupURLFetchedCallBack()
+ // is called.
+ warmup_url_fetched_run_loop_->Run();
}
void RunUntilIdle() {
@@ -193,47 +210,25 @@ class DataReductionProxyConfigTest : public testing::Test {
}
private:
+ std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
+
base::MessageLoopForIO message_loop_;
+ std::unique_ptr<base::RunLoop> warmup_url_fetched_run_loop_;
std::unique_ptr<DataReductionProxyTestContext> test_context_;
std::unique_ptr<TestDataReductionProxyParams> expected_params_;
};
-TEST_F(DataReductionProxyConfigTest, TestUpdateConfigurator) {
+TEST_F(DataReductionProxyConfigTest, TestReloadConfigHoldback) {
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, true, false);
-
- // Expect both secure and insecure proxies to be available when the DRP is
- // enabled and secure proxies are allowed.
- config()->UpdateConfigurator(true, true);
- EXPECT_EQ(std::vector<net::ProxyServer>({kHttpsProxy, kHttpProxy}),
- GetConfiguredProxiesForHttp());
+ ResetSettings(true, true);
- // Expect only insecure proxies to be available when the DRP is enabled and
- // secure proxies are not allowed.
- config()->UpdateConfigurator(true, false);
- EXPECT_EQ(std::vector<net::ProxyServer>(1, kHttpProxy),
- GetConfiguredProxiesForHttp());
-
- // Expect no proxies to be available when the DRP is disabled.
- config()->UpdateConfigurator(false, false);
- EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp());
-}
-
-TEST_F(DataReductionProxyConfigTest, TestUpdateConfiguratorHoldback) {
- 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, true, true);
-
- config()->UpdateConfigurator(true, false);
+ config()->UpdateConfigForTesting(true, false);
+ config()->ReloadConfig();
EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp());
}
@@ -245,22 +240,39 @@ TEST_F(DataReductionProxyConfigTest, TestOnIPAddressChanged) {
"insecure_origin.net:80", net::ProxyServer::SCHEME_HTTP);
SetProxiesForHttpOnCommandLine({kHttpsProxy, kHttpProxy});
- ResetSettings(true, true, true, false);
+ ResetSettings(true, false);
// The proxy is enabled initially.
- config()->enabled_by_user_ = true;
- config()->secure_proxy_allowed_ = true;
- config()->UpdateConfigurator(true, true);
+ config()->UpdateConfigForTesting(true, true);
+ config()->ReloadConfig();
// IP address change triggers a secure proxy check that succeeds. Proxy
// remains unrestricted.
- CheckSecureProxyCheckOnIPChange("OK", net::HTTP_OK, kSuccess,
+ CheckSecureProxyCheckOnIPChange("OK", false, net::HTTP_OK, kSuccess,
+ SUCCEEDED_PROXY_ALREADY_ENABLED,
+ {kHttpsProxy, kHttpProxy});
+
+ // IP address change triggers a secure proxy check that succeeds but captive
+ // portal fails. Proxy is restricted.
+ CheckSecureProxyCheckOnIPChange("OK", true, net::HTTP_OK, kSuccess,
SUCCEEDED_PROXY_ALREADY_ENABLED,
+ std::vector<net::ProxyServer>(1, kHttpProxy));
+
+ // IP address change triggers a secure proxy check that fails. Proxy is
+ // restricted.
+ CheckSecureProxyCheckOnIPChange("Bad", false, net::HTTP_OK, kSuccess,
+ FAILED_PROXY_DISABLED,
+ std::vector<net::ProxyServer>(1, kHttpProxy));
+
+ // IP address change triggers a secure proxy check that succeeds. Proxies
+ // are unrestricted.
+ CheckSecureProxyCheckOnIPChange("OK", false, net::HTTP_OK, kSuccess,
+ SUCCEEDED_PROXY_ENABLED,
{kHttpsProxy, kHttpProxy});
// IP address change triggers a secure proxy check that fails. Proxy is
// restricted.
- CheckSecureProxyCheckOnIPChange("Bad", net::HTTP_OK, kSuccess,
+ CheckSecureProxyCheckOnIPChange("Bad", true, net::HTTP_OK, kSuccess,
FAILED_PROXY_DISABLED,
std::vector<net::ProxyServer>(1, kHttpProxy));
@@ -268,20 +280,20 @@ TEST_F(DataReductionProxyConfigTest, TestOnIPAddressChanged) {
// network changing again. This should be ignored, so the proxy should remain
// unrestricted.
CheckSecureProxyCheckOnIPChange(
- std::string(), net::URLFetcher::RESPONSE_CODE_INVALID,
+ std::string(), false, net::URLFetcher::RESPONSE_CODE_INVALID,
net::URLRequestStatus(net::URLRequestStatus::FAILED,
net::ERR_INTERNET_DISCONNECTED),
INTERNET_DISCONNECTED, std::vector<net::ProxyServer>(1, kHttpProxy));
// IP address change triggers a secure proxy check that fails. Proxy remains
// restricted.
- CheckSecureProxyCheckOnIPChange("Bad", net::HTTP_OK, kSuccess,
+ CheckSecureProxyCheckOnIPChange("Bad", false, net::HTTP_OK, kSuccess,
FAILED_PROXY_ALREADY_DISABLED,
std::vector<net::ProxyServer>(1, kHttpProxy));
// IP address change triggers a secure proxy check that succeeds. Proxy is
// unrestricted.
- CheckSecureProxyCheckOnIPChange("OK", net::HTTP_OK, kSuccess,
+ CheckSecureProxyCheckOnIPChange("OK", false, net::HTTP_OK, kSuccess,
SUCCEEDED_PROXY_ENABLED,
{kHttpsProxy, kHttpProxy});
@@ -289,7 +301,7 @@ TEST_F(DataReductionProxyConfigTest, TestOnIPAddressChanged) {
// network changing again. This should be ignored, so the proxy should remain
// unrestricted.
CheckSecureProxyCheckOnIPChange(
- std::string(), net::URLFetcher::RESPONSE_CODE_INVALID,
+ std::string(), false, net::URLFetcher::RESPONSE_CODE_INVALID,
net::URLRequestStatus(net::URLRequestStatus::FAILED,
net::ERR_INTERNET_DISCONNECTED),
INTERNET_DISCONNECTED, {kHttpsProxy, kHttpProxy});
@@ -297,51 +309,130 @@ TEST_F(DataReductionProxyConfigTest, TestOnIPAddressChanged) {
// IP address change triggers a secure proxy check that fails because of a
// redirect response, e.g. by a captive portal. Proxy is restricted.
CheckSecureProxyCheckOnIPChange(
- "Bad", net::HTTP_FOUND,
+ "Bad", false, net::HTTP_FOUND,
net::URLRequestStatus(net::URLRequestStatus::CANCELED, net::ERR_ABORTED),
FAILED_PROXY_DISABLED, std::vector<net::ProxyServer>(1, kHttpProxy));
}
-TEST_F(DataReductionProxyConfigTest,
- TestOnIPAddressChanged_SecureProxyDisabledByDefault) {
+// Verifies that the warm up URL is fetched correctly.
+TEST_F(DataReductionProxyConfigTest, WarmupURL) {
const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
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});
- base::CommandLine::ForCurrentProcess()->AppendSwitch(
- data_reduction_proxy::switches::kDataReductionProxyStartSecureDisabled);
+ // Set up the embedded test server from where the warm up URL will be fetched.
+ net::EmbeddedTestServer embedded_test_server;
+ embedded_test_server.AddDefaultHandlers(
+ base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
+ EXPECT_TRUE(embedded_test_server.Start());
- ResetSettings(true, true, true, false);
+ GURL warmup_url = embedded_test_server.GetURL("/simple.html");
- // The proxy is enabled initially.
- config()->enabled_by_user_ = true;
- config()->secure_proxy_allowed_ = false;
- config()->UpdateConfigurator(true, false);
+ const struct {
+ bool data_reduction_proxy_enabled;
+ bool enabled_via_field_trial;
+ } tests[] = {
+ {
+ false, false,
+ },
+ {
+ false, true,
+ },
+ {
+ true, false,
+ },
+ {
+ true, true,
+ },
+ };
+ for (const auto& test : tests) {
+ base::HistogramTester histogram_tester;
+ SetProxiesForHttpOnCommandLine({kHttpsProxy, kHttpProxy});
- // IP address change triggers a secure proxy check that succeeds. Proxy
- // becomes unrestricted.
- CheckSecureProxyCheckOnIPChange("OK", net::HTTP_OK, kSuccess,
- SUCCEEDED_PROXY_ENABLED,
- {kHttpsProxy, kHttpProxy});
- // IP address change triggers a secure proxy check that fails. Proxy is
- // restricted before the check starts, and remains disabled.
- ExpectSecureProxyCheckResult(PROXY_DISABLED_BEFORE_CHECK);
- CheckSecureProxyCheckOnIPChange("Bad", net::HTTP_OK, kSuccess,
- FAILED_PROXY_ALREADY_DISABLED,
- std::vector<net::ProxyServer>(1, kHttpProxy));
- // IP address change triggers a secure proxy check that fails. Proxy remains
- // restricted.
- CheckSecureProxyCheckOnIPChange("Bad", net::HTTP_OK, kSuccess,
- FAILED_PROXY_ALREADY_DISABLED,
- std::vector<net::ProxyServer>(1, kHttpProxy));
- // IP address change triggers a secure proxy check that succeeds. Proxy is
- // unrestricted.
- CheckSecureProxyCheckOnIPChange("OK", net::HTTP_OK, kSuccess,
- SUCCEEDED_PROXY_ENABLED,
- {kHttpsProxy, kHttpProxy});
+ ResetSettings(true, false);
+
+ variations::testing::ClearAllVariationParams();
+ std::map<std::string, std::string> variation_params;
+ variation_params["enable_warmup"] =
+ test.enabled_via_field_trial ? "true" : "false";
+ variation_params["warmup_url"] = warmup_url.spec();
+
+ ASSERT_TRUE(variations::AssociateVariationParams(
+ params::GetQuicFieldTrialName(), "Enabled", variation_params));
+
+ base::FieldTrialList field_trial_list(nullptr);
+ base::FieldTrialList::CreateFieldTrial(params::GetQuicFieldTrialName(),
+ "Enabled");
+
+ base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
+ TestDataReductionProxyConfig config(
+ 0, TestDataReductionProxyParams::HAS_EVERYTHING, task_runner(), nullptr,
+ configurator(), event_creator());
+
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_ =
+ new net::TestURLRequestContextGetter(task_runner());
+ config.InitializeOnIOThread(request_context_getter_.get(),
+ request_context_getter_.get());
+ config.SetWarmupURLFetcherCallbackForTesting(
+ base::Bind(&DataReductionProxyConfigTest::WarmupURLFetchedCallBack,
+ base::Unretained(this)));
+
+ // Set the connection type to WiFi so that warm up URL is fetched even if
+ // the test device does not have connectivity.
+ config.connection_type_ = net::NetworkChangeNotifier::CONNECTION_WIFI;
+ config.SetProxyConfig(test.data_reduction_proxy_enabled, true);
+ bool warmup_url_enabled =
+ test.data_reduction_proxy_enabled && test.enabled_via_field_trial;
+
+ if (warmup_url_enabled) {
+ // Block until warm up URL is fetched successfully.
+ WarmUpURLFetchedRunLoop();
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.WarmupURL.FetchInitiated", 1, 1);
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.WarmupURL.FetchSuccessful", 1, 1);
+ }
+
+ // Set the connection type to 4G so that warm up URL is fetched even if
+ // the test device does not have connectivity.
+ net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
+ net::NetworkChangeNotifier::CONNECTION_4G);
+ RunUntilIdle();
+
+ if (warmup_url_enabled) {
+ // Block until warm up URL is fetched successfully.
+ WarmUpURLFetchedRunLoop();
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.WarmupURL.FetchInitiated", 1, 2);
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.WarmupURL.FetchSuccessful", 1, 2);
+ } else {
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.WarmupURL.FetchInitiated", 0);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.WarmupURL.FetchSuccessful", 0);
+ }
+
+ // Warm up URL should not be fetched since the device does not have
+ // connectivity.
+ net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
+ net::NetworkChangeNotifier::CONNECTION_NONE);
+ RunUntilIdle();
+
+ if (warmup_url_enabled) {
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.WarmupURL.FetchInitiated", 1, 2);
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.WarmupURL.FetchSuccessful", 1, 2);
+ } else {
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.WarmupURL.FetchInitiated", 0);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.WarmupURL.FetchSuccessful", 0);
+ }
+ }
}
TEST_F(DataReductionProxyConfigTest, AreProxiesBypassed) {
@@ -500,10 +591,6 @@ TEST_F(DataReductionProxyConfigTest, AreProxiesBypassed) {
rules.ParseFromString(proxy_rules);
int flags = 0;
- if (tests[i].allowed)
- flags |= DataReductionProxyParams::kAllowed;
- if (tests[i].fallback_allowed)
- flags |= DataReductionProxyParams::kFallbackAllowed;
unsigned int has_definitions = TestDataReductionProxyParams::HAS_EVERYTHING;
std::unique_ptr<TestDataReductionProxyParams> params(
new TestDataReductionProxyParams(flags, has_definitions));
@@ -546,8 +633,6 @@ TEST_F(DataReductionProxyConfigTest, AreProxiesBypassedRetryDelay) {
rules.ParseFromString(proxy_rules);
int flags = 0;
- flags |= DataReductionProxyParams::kAllowed;
- flags |= DataReductionProxyParams::kFallbackAllowed;
unsigned int has_definitions = TestDataReductionProxyParams::HAS_EVERYTHING;
std::unique_ptr<TestDataReductionProxyParams> params(
new TestDataReductionProxyParams(flags, has_definitions));
@@ -592,7 +677,6 @@ TEST_F(DataReductionProxyConfigTest, AreProxiesBypassedRetryDelay) {
TEST_F(DataReductionProxyConfigTest, IsDataReductionProxyWithParams) {
const struct {
net::ProxyServer proxy_server;
- bool fallback_allowed;
bool expected_result;
net::ProxyServer expected_first;
net::ProxyServer expected_second;
@@ -600,35 +684,23 @@ TEST_F(DataReductionProxyConfigTest, IsDataReductionProxyWithParams) {
} tests[] = {
{net::ProxyServer::FromURI(TestDataReductionProxyParams::DefaultOrigin(),
net::ProxyServer::SCHEME_HTTP),
- true, true,
+ true,
net::ProxyServer::FromURI(TestDataReductionProxyParams::DefaultOrigin(),
net::ProxyServer::SCHEME_HTTP),
net::ProxyServer::FromURI(
TestDataReductionProxyParams::DefaultFallbackOrigin(),
net::ProxyServer::SCHEME_HTTP),
false},
- {net::ProxyServer::FromURI(TestDataReductionProxyParams::DefaultOrigin(),
- net::ProxyServer::SCHEME_HTTP),
- false, true,
- net::ProxyServer::FromURI(TestDataReductionProxyParams::DefaultOrigin(),
- net::ProxyServer::SCHEME_HTTP),
- net::ProxyServer(), false},
{net::ProxyServer::FromURI(
TestDataReductionProxyParams::DefaultFallbackOrigin(),
net::ProxyServer::SCHEME_HTTP),
- true, true, net::ProxyServer::FromURI(
- TestDataReductionProxyParams::DefaultFallbackOrigin(),
- net::ProxyServer::SCHEME_HTTP),
+ true, net::ProxyServer::FromURI(
+ TestDataReductionProxyParams::DefaultFallbackOrigin(),
+ net::ProxyServer::SCHEME_HTTP),
net::ProxyServer(), true},
- {net::ProxyServer::FromURI(
- TestDataReductionProxyParams::DefaultFallbackOrigin(),
- net::ProxyServer::SCHEME_HTTP),
- false, false, net::ProxyServer(), net::ProxyServer(), false},
};
for (size_t i = 0; i < arraysize(tests); ++i) {
- int flags = DataReductionProxyParams::kAllowed;
- if (tests[i].fallback_allowed)
- flags |= DataReductionProxyParams::kFallbackAllowed;
+ int flags = 0;
unsigned int has_definitions = TestDataReductionProxyParams::HAS_EVERYTHING;
std::unique_ptr<TestDataReductionProxyParams> params(
new TestDataReductionProxyParams(flags, has_definitions));
@@ -665,64 +737,82 @@ TEST_F(DataReductionProxyConfigTest, IsDataReductionProxyWithParams) {
}
TEST_F(DataReductionProxyConfigTest, IsDataReductionProxyWithMutableConfig) {
- std::vector<net::ProxyServer> proxies_for_http;
- proxies_for_http.push_back(net::ProxyServer::FromURI(
- "https://origin.net:443", net::ProxyServer::SCHEME_HTTP));
- proxies_for_http.push_back(net::ProxyServer::FromURI(
- "http://origin.net:80", net::ProxyServer::SCHEME_HTTP));
- proxies_for_http.push_back(net::ProxyServer::FromURI(
- "quic://anotherorigin.net:443", net::ProxyServer::SCHEME_HTTP));
+ std::vector<DataReductionProxyServer> proxies_for_http;
+ proxies_for_http.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("https://origin.net:443",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::CORE));
+ proxies_for_http.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("http://origin.net:80",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::CORE));
+
+ proxies_for_http.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("quic://anotherorigin.net:443",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::CORE));
+
const struct {
- net::ProxyServer proxy_server;
+ DataReductionProxyServer proxy_server;
bool expected_result;
- std::vector<net::ProxyServer> expected_proxies;
+ std::vector<DataReductionProxyServer> expected_proxies;
size_t expected_proxy_index;
} tests[] = {
{
proxies_for_http[0], true,
- std::vector<net::ProxyServer>(proxies_for_http.begin(),
- proxies_for_http.end()),
+ std::vector<DataReductionProxyServer>(proxies_for_http.begin(),
+ proxies_for_http.end()),
0,
},
{
proxies_for_http[1], true,
- std::vector<net::ProxyServer>(proxies_for_http.begin() + 1,
- proxies_for_http.end()),
+ std::vector<DataReductionProxyServer>(proxies_for_http.begin() + 1,
+ proxies_for_http.end()),
1,
},
{
proxies_for_http[2], true,
- std::vector<net::ProxyServer>(proxies_for_http.begin() + 2,
- proxies_for_http.end()),
+ std::vector<DataReductionProxyServer>(proxies_for_http.begin() + 2,
+ proxies_for_http.end()),
2,
},
{
- net::ProxyServer(), false, std::vector<net::ProxyServer>(), 0,
+ DataReductionProxyServer(net::ProxyServer(),
+ ProxyServer::UNSPECIFIED_TYPE),
+ false, std::vector<DataReductionProxyServer>(), 0,
},
{
- net::ProxyServer(
- net::ProxyServer::SCHEME_HTTPS,
- net::HostPortPair::FromString("otherorigin.net:443")),
- false, std::vector<net::ProxyServer>(), 0,
+ DataReductionProxyServer(
+ net::ProxyServer(
+ net::ProxyServer::SCHEME_HTTPS,
+ net::HostPortPair::FromString("otherorigin.net:443")),
+ ProxyServer::UNSPECIFIED_TYPE),
+ false, std::vector<DataReductionProxyServer>(), 0,
},
{
// Verifies that when determining if a proxy is a valid data reduction
// proxy, only the host port pairs are compared.
- net::ProxyServer::FromURI("origin.net:443",
- net::ProxyServer::SCHEME_QUIC),
- true, std::vector<net::ProxyServer>(proxies_for_http.begin(),
- proxies_for_http.end()),
+ DataReductionProxyServer(
+ net::ProxyServer::FromURI("origin.net:443",
+ net::ProxyServer::SCHEME_QUIC),
+ ProxyServer::UNSPECIFIED_TYPE),
+ true, std::vector<DataReductionProxyServer>(proxies_for_http.begin(),
+ proxies_for_http.end()),
0,
},
{
- net::ProxyServer::FromURI("origin2.net:443",
- net::ProxyServer::SCHEME_HTTPS),
- false, std::vector<net::ProxyServer>(), 0,
+ DataReductionProxyServer(
+ net::ProxyServer::FromURI("origin2.net:443",
+ net::ProxyServer::SCHEME_HTTPS),
+ ProxyServer::UNSPECIFIED_TYPE),
+ false, std::vector<DataReductionProxyServer>(), 0,
},
{
- net::ProxyServer::FromURI("origin2.net:443",
- net::ProxyServer::SCHEME_QUIC),
- false, std::vector<net::ProxyServer>(), 0,
+ DataReductionProxyServer(
+ net::ProxyServer::FromURI("origin2.net:443",
+ net::ProxyServer::SCHEME_QUIC),
+ ProxyServer::UNSPECIFIED_TYPE),
+ false, std::vector<DataReductionProxyServer>(), 0,
},
};
@@ -734,10 +824,12 @@ TEST_F(DataReductionProxyConfigTest, IsDataReductionProxyWithMutableConfig) {
event_creator()));
for (const auto& test : tests) {
DataReductionProxyTypeInfo proxy_type_info;
- EXPECT_EQ(test.expected_result, config->IsDataReductionProxy(
- test.proxy_server, &proxy_type_info));
- EXPECT_THAT(proxy_type_info.proxy_servers,
- testing::ContainerEq(test.expected_proxies));
+ EXPECT_EQ(test.expected_result,
+ config->IsDataReductionProxy(test.proxy_server.proxy_server(),
+ &proxy_type_info));
+ EXPECT_EQ(proxy_type_info.proxy_servers,
+ DataReductionProxyServer::ConvertToNetProxyServers(
+ test.expected_proxies));
EXPECT_EQ(test.expected_proxy_index, proxy_type_info.proxy_index);
}
}
@@ -893,6 +985,11 @@ TEST_F(DataReductionProxyConfigTest, AutoLoFiParams) {
base::FieldTrialList::CreateFieldTrial(params::GetLoFiFlagFieldTrialName(),
"Enabled");
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter =
+ new net::TestURLRequestContextGetter(task_runner());
+ config.InitializeOnIOThread(request_context_getter.get(),
+ request_context_getter.get());
+
const struct {
bool lofi_flag_group;
@@ -926,9 +1023,7 @@ TEST_F(DataReductionProxyConfigTest, AutoLoFiParams) {
EXPECT_EQ(base::TimeDelta::FromSeconds(expected_hysteresis_sec),
config.auto_lofi_hysteresis_);
- std::map<std::string, std::string> network_quality_estimator_params;
- net::TestNetworkQualityEstimator test_network_quality_estimator(
- network_quality_estimator_params);
+ net::TestNetworkQualityEstimator test_network_quality_estimator;
// Network is slow.
test_network_quality_estimator.set_effective_connection_type(
@@ -959,7 +1054,12 @@ TEST_F(DataReductionProxyConfigTest, AutoLoFiParams) {
&test_network_quality_estimator));
// Change in connection type changes the network quality despite hysteresis.
- config.connection_type_ = net::NetworkChangeNotifier::CONNECTION_WIFI;
+ EXPECT_FALSE(config.connection_type_changed_);
+ net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
+ net::NetworkChangeNotifier::CONNECTION_WIFI);
+ RunUntilIdle();
+
+ EXPECT_TRUE(config.connection_type_changed_);
EXPECT_TRUE(config.IsNetworkQualityProhibitivelySlow(
&test_network_quality_estimator));
}
@@ -996,6 +1096,10 @@ TEST_F(DataReductionProxyConfigTest, AutoLoFiParamsSlowConnectionsFlag) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kDataReductionProxyLoFi,
switches::kDataReductionProxyLoFiValueSlowConnectionsOnly);
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter =
+ new net::TestURLRequestContextGetter(task_runner());
+ config.InitializeOnIOThread(request_context_getter.get(),
+ request_context_getter.get());
config.PopulateAutoLoFiParams();
@@ -1007,9 +1111,7 @@ TEST_F(DataReductionProxyConfigTest, AutoLoFiParamsSlowConnectionsFlag) {
EXPECT_EQ(base::TimeDelta::FromSeconds(hysteresis_sec),
config.auto_lofi_hysteresis_);
- std::map<std::string, std::string> network_quality_estimator_params;
- net::TestNetworkQualityEstimator test_network_quality_estimator(
- network_quality_estimator_params);
+ net::TestNetworkQualityEstimator test_network_quality_estimator;
// Network is slow.
test_network_quality_estimator.set_effective_connection_type(
@@ -1039,7 +1141,12 @@ TEST_F(DataReductionProxyConfigTest, AutoLoFiParamsSlowConnectionsFlag) {
&test_network_quality_estimator));
// Change in connection type changes the network quality despite hysteresis.
- config.connection_type_ = net::NetworkChangeNotifier::CONNECTION_WIFI;
+ EXPECT_FALSE(config.connection_type_changed_);
+ net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
+ net::NetworkChangeNotifier::CONNECTION_WIFI);
+ RunUntilIdle();
+
+ EXPECT_TRUE(config.connection_type_changed_);
EXPECT_TRUE(config.IsNetworkQualityProhibitivelySlow(
&test_network_quality_estimator));
}
@@ -1053,9 +1160,7 @@ TEST_F(DataReductionProxyConfigTest, LoFiAccuracy) {
lofi_accuracy_recording_intervals.push_back(base::TimeDelta::FromSeconds(0));
TestDataReductionProxyConfig config(
- DataReductionProxyParams::kAllowed |
- DataReductionProxyParams::kFallbackAllowed,
- TestDataReductionProxyParams::HAS_EVERYTHING, task_runner(), nullptr,
+ 0, TestDataReductionProxyParams::HAS_EVERYTHING, task_runner(), nullptr,
configurator(), event_creator());
config.SetLofiAccuracyRecordingIntervals(lofi_accuracy_recording_intervals);
config.SetTickClock(tick_clock.get());
@@ -1113,9 +1218,7 @@ TEST_F(DataReductionProxyConfigTest, LoFiAccuracy) {
<< test.description;
config.PopulateAutoLoFiParams();
- std::map<std::string, std::string> network_quality_estimator_params;
- net::TestNetworkQualityEstimator test_network_quality_estimator(
- network_quality_estimator_params);
+ net::TestNetworkQualityEstimator test_network_quality_estimator;
base::HistogramTester histogram_tester;
test_network_quality_estimator.set_effective_connection_type(
@@ -1145,9 +1248,7 @@ TEST_F(DataReductionProxyConfigTest, LoFiAccuracyNonZeroDelay) {
lofi_accuracy_recording_intervals.push_back(base::TimeDelta::FromSeconds(1));
TestDataReductionProxyConfig config(
- DataReductionProxyParams::kAllowed |
- DataReductionProxyParams::kFallbackAllowed,
- TestDataReductionProxyParams::HAS_EVERYTHING, task_runner(), nullptr,
+ 0, TestDataReductionProxyParams::HAS_EVERYTHING, task_runner(), nullptr,
configurator(), event_creator());
config.SetLofiAccuracyRecordingIntervals(lofi_accuracy_recording_intervals);
config.SetTickClock(tick_clock.get());
@@ -1165,9 +1266,7 @@ TEST_F(DataReductionProxyConfigTest, LoFiAccuracyNonZeroDelay) {
"Enabled");
config.PopulateAutoLoFiParams();
- std::map<std::string, std::string> network_quality_estimator_params;
- net::TestNetworkQualityEstimator test_network_quality_estimator(
- network_quality_estimator_params);
+ net::TestNetworkQualityEstimator test_network_quality_estimator;
base::HistogramTester histogram_tester;
// Network was predicted to be slow and actually was slow.
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc
index 11e7a1b2147..e20ae9a744c 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc
@@ -43,6 +43,7 @@ void DataReductionProxyConfigurator::Enable(
net::ProxyConfig DataReductionProxyConfigurator::CreateProxyConfig(
bool secure_transport_restricted,
const std::vector<net::ProxyServer>& proxies_for_http) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
net::ProxyConfig config;
config.proxy_rules().type =
net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h
index 2d008c85dc0..55643234ff6 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h
@@ -15,13 +15,9 @@
namespace net {
class NetLog;
-class ProxyInfo;
class ProxyServer;
-class ProxyService;
}
-class PrefService;
-
namespace data_reduction_proxy {
class DataReductionProxyEventCreator;
@@ -39,6 +35,8 @@ class DataReductionProxyConfigurator {
// Enables data reduction using the proxy servers in |proxies_for_http|.
// |secure_transport_restricted| indicates that proxies going over secure
// transports can not be used.
+ // TODO: crbug.com/675764: Pass a vector of DataReductionProxyServer
+ // instead of net::ProxyServer.
virtual void Enable(bool secure_transport_restricted,
const std::vector<net::ProxyServer>& proxies_for_http);
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
index 6988b4fbf7f..7324f6895ba 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
@@ -8,9 +8,12 @@
#include "base/command_line.h"
#include "base/metrics/histogram_macros.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
@@ -20,6 +23,7 @@
#include "net/base/url_util.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
+#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_service.h"
namespace data_reduction_proxy {
@@ -39,15 +43,30 @@ DataReductionProxyDelegate::DataReductionProxyDelegate(
event_creator_(event_creator),
bypass_stats_(bypass_stats),
alternative_proxies_broken_(false),
+ tick_clock_(new base::DefaultTickClock()),
+ first_data_saver_request_recorded_(false),
+ io_data_(nullptr),
net_log_(net_log) {
- DCHECK(config);
- DCHECK(configurator);
- DCHECK(event_creator);
- DCHECK(bypass_stats);
- DCHECK(net_log);
+ DCHECK(config_);
+ DCHECK(configurator_);
+ DCHECK(event_creator_);
+ DCHECK(bypass_stats_);
+ DCHECK(net_log_);
+ // Constructed on the UI thread, but should be checked on the IO thread.
+ thread_checker_.DetachFromThread();
}
DataReductionProxyDelegate::~DataReductionProxyDelegate() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
+}
+
+void DataReductionProxyDelegate::InitializeOnIOThread(
+ DataReductionProxyIOData* io_data) {
+ DCHECK(io_data);
+ DCHECK(thread_checker_.CalledOnValidThread());
+ net::NetworkChangeNotifier::AddIPAddressObserver(this);
+ io_data_ = io_data;
}
void DataReductionProxyDelegate::OnResolveProxy(
@@ -56,18 +75,31 @@ void DataReductionProxyDelegate::OnResolveProxy(
const net::ProxyService& proxy_service,
net::ProxyInfo* result) {
DCHECK(result);
+ DCHECK(thread_checker_.CalledOnValidThread());
+
OnResolveProxyHandler(url, method, configurator_->GetProxyConfig(),
- proxy_service.proxy_retry_info(), config_, result);
+ proxy_service.proxy_retry_info(), config_, io_data_,
+ result);
+
+ if (!first_data_saver_request_recorded_ && !result->is_empty() &&
+ config_->IsDataReductionProxy(result->proxy_server(), nullptr)) {
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "DataReductionProxy.TimeToFirstDataSaverRequest",
+ tick_clock_->NowTicks() - last_network_change_time_);
+ first_data_saver_request_recorded_ = true;
+ }
}
void DataReductionProxyDelegate::OnTunnelConnectCompleted(
const net::HostPortPair& endpoint,
const net::HostPortPair& proxy_server,
int net_error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
}
void DataReductionProxyDelegate::OnFallback(const net::ProxyServer& bad_proxy,
int net_error) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (bad_proxy.is_valid() &&
config_->IsDataReductionProxy(bad_proxy, nullptr)) {
event_creator_->AddProxyFallbackEvent(net_log_, bad_proxy.ToURI(),
@@ -81,10 +113,12 @@ void DataReductionProxyDelegate::OnFallback(const net::ProxyServer& bad_proxy,
void DataReductionProxyDelegate::OnBeforeTunnelRequest(
const net::HostPortPair& proxy_server,
net::HttpRequestHeaders* extra_headers) {
+ DCHECK(thread_checker_.CalledOnValidThread());
}
bool DataReductionProxyDelegate::IsTrustedSpdyProxy(
const net::ProxyServer& proxy_server) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (!proxy_server.is_https() ||
!params::IsIncludedInTrustedSpdyProxyFieldTrial() ||
!proxy_server.is_valid()) {
@@ -97,12 +131,22 @@ void DataReductionProxyDelegate::OnTunnelHeadersReceived(
const net::HostPortPair& origin,
const net::HostPortPair& proxy_server,
const net::HttpResponseHeaders& response_headers) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void DataReductionProxyDelegate::SetTickClockForTesting(
+ std::unique_ptr<base::TickClock> tick_clock) {
+ tick_clock_ = std::move(tick_clock);
+ // Update |last_network_change_time_| to the provided tick clock's current
+ // time for testing.
+ last_network_change_time_ = tick_clock_->NowTicks();
}
void DataReductionProxyDelegate::GetAlternativeProxy(
const GURL& url,
const net::ProxyServer& resolved_proxy_server,
net::ProxyServer* alternative_proxy_server) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!alternative_proxy_server->is_valid());
if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS() ||
@@ -140,6 +184,7 @@ void DataReductionProxyDelegate::GetAlternativeProxy(
void DataReductionProxyDelegate::OnAlternativeProxyBroken(
const net::ProxyServer& alternative_proxy_server) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// TODO(tbansal): Reset this on connection change events.
// Currently, DataReductionProxyDelegate does not maintain a list of broken
// proxies. If one alternative proxy is broken, use of all alternative proxies
@@ -152,6 +197,7 @@ void DataReductionProxyDelegate::OnAlternativeProxyBroken(
net::ProxyServer DataReductionProxyDelegate::GetDefaultAlternativeProxy()
const {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (!params::IsZeroRttQuicEnabled())
return net::ProxyServer();
@@ -175,6 +221,7 @@ net::ProxyServer DataReductionProxyDelegate::GetDefaultAlternativeProxy()
bool DataReductionProxyDelegate::SupportsQUIC(
const net::ProxyServer& proxy_server) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
// Enable QUIC for whitelisted proxies.
// TODO(tbansal): Use client config service to control this whitelist.
return base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -186,27 +233,38 @@ bool DataReductionProxyDelegate::SupportsQUIC(
void DataReductionProxyDelegate::RecordQuicProxyStatus(
QuicProxyStatus status) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.Quic.ProxyStatus", status,
QUIC_PROXY_STATUS_BOUNDARY);
}
void DataReductionProxyDelegate::RecordGetDefaultAlternativeProxy(
DefaultAlternativeProxyStatus status) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.Quic.DefaultAlternativeProxy",
status, DEFAULT_ALTERNATIVE_PROXY_STATUS_BOUNDARY);
}
+void DataReductionProxyDelegate::OnIPAddressChanged() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ first_data_saver_request_recorded_ = false;
+ last_network_change_time_ = tick_clock_->NowTicks();
+}
+
void OnResolveProxyHandler(const GURL& url,
const std::string& method,
const net::ProxyConfig& data_reduction_proxy_config,
const net::ProxyRetryInfoMap& proxy_retry_info,
const DataReductionProxyConfig* config,
+ DataReductionProxyIOData* io_data,
net::ProxyInfo* result) {
DCHECK(config);
DCHECK(result->is_empty() || result->is_direct() ||
!config->IsDataReductionProxy(result->proxy_server(), NULL));
+
if (!util::EligibleForDataReductionProxy(*result, url, method))
return;
+
net::ProxyInfo data_reduction_proxy_info;
bool data_saver_proxy_used = util::ApplyProxyConfigToProxyInfo(
data_reduction_proxy_config, proxy_retry_info, url,
@@ -214,6 +272,17 @@ void OnResolveProxyHandler(const GURL& url,
if (data_saver_proxy_used)
result->OverrideProxyList(data_reduction_proxy_info.proxy_list());
+ if (io_data && io_data->resource_type_provider()) {
+ ResourceTypeProvider::ContentType content_type =
+ io_data->resource_type_provider()->GetContentType(url);
+ DCHECK_GT(ResourceTypeProvider::CONTENT_TYPE_MAX, content_type);
+ UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.ResourceContentType",
+ content_type,
+ ResourceTypeProvider::CONTENT_TYPE_MAX);
+ // TODO(tbansal): crbug.com/671810: Use the content type to determine the
+ // proxy that should be used for fetching |url|.
+ }
+
// The |data_reduction_proxy_config| must be valid otherwise the proxy
// cannot be used.
DCHECK(data_reduction_proxy_config.is_valid() || !data_saver_proxy_used);
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h
index e65a9a5caac..fadac3e18e4 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h
@@ -8,11 +8,17 @@
#include <string>
#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "net/base/network_change_notifier.h"
#include "net/base/proxy_delegate.h"
#include "net/proxy/proxy_retry_info.h"
#include "net/proxy/proxy_server.h"
#include "url/gurl.h"
+namespace base {
+class TickClock;
+}
+
namespace net {
class HostPortPair;
class HttpRequestHeaders;
@@ -21,7 +27,6 @@ class NetLog;
class ProxyConfig;
class ProxyInfo;
class ProxyService;
-class URLRequest;
}
namespace data_reduction_proxy {
@@ -30,21 +35,25 @@ class DataReductionProxyBypassStats;
class DataReductionProxyConfig;
class DataReductionProxyConfigurator;
class DataReductionProxyEventCreator;
-class DataReductionProxyRequestOptions;
+class DataReductionProxyIOData;
-class DataReductionProxyDelegate : public net::ProxyDelegate {
+class DataReductionProxyDelegate
+ : public net::ProxyDelegate,
+ public net::NetworkChangeNotifier::IPAddressObserver {
public:
// ProxyDelegate instance is owned by io_thread. |auth_handler| and |config|
// outlives this class instance.
- explicit DataReductionProxyDelegate(
- DataReductionProxyConfig* config,
- const DataReductionProxyConfigurator* configurator,
- DataReductionProxyEventCreator* event_creator,
- DataReductionProxyBypassStats* bypass_stats,
- net::NetLog* net_log);
+ DataReductionProxyDelegate(DataReductionProxyConfig* config,
+ const DataReductionProxyConfigurator* configurator,
+ DataReductionProxyEventCreator* event_creator,
+ DataReductionProxyBypassStats* bypass_stats,
+ net::NetLog* net_log);
~DataReductionProxyDelegate() override;
+ // Performs initialization on the IO thread.
+ void InitializeOnIOThread(DataReductionProxyIOData* io_data);
+
// net::ProxyDelegate implementation:
void OnResolveProxy(const GURL& url,
const std::string& method,
@@ -62,6 +71,8 @@ class DataReductionProxyDelegate : public net::ProxyDelegate {
const net::HostPortPair& proxy_server,
const net::HttpResponseHeaders& response_headers) override;
+ void SetTickClockForTesting(std::unique_ptr<base::TickClock> tick_clock);
+
protected:
// Protected so that these methods are accessible for testing.
// net::ProxyDelegate implementation:
@@ -104,6 +115,9 @@ class DataReductionProxyDelegate : public net::ProxyDelegate {
void RecordGetDefaultAlternativeProxy(
DefaultAlternativeProxyStatus status) const;
+ // NetworkChangeNotifier::IPAddressObserver:
+ void OnIPAddressChanged() override;
+
const DataReductionProxyConfig* config_;
const DataReductionProxyConfigurator* configurator_;
DataReductionProxyEventCreator* event_creator_;
@@ -112,8 +126,24 @@ class DataReductionProxyDelegate : public net::ProxyDelegate {
// True if the use of alternate proxies is disabled.
bool alternative_proxies_broken_;
+ // Tick clock used for obtaining the current time.
+ std::unique_ptr<base::TickClock> tick_clock_;
+
+ // True if the metrics related to the first request whose resolved proxy was a
+ // data saver proxy has been recorded. |first_data_saver_request_recorded_| is
+ // reset to false on IP address change events.
+ bool first_data_saver_request_recorded_;
+
+ // Set to the time when last IP address change event was received, or the time
+ // of initialization of |this|, whichever is later.
+ base::TimeTicks last_network_change_time_;
+
+ DataReductionProxyIOData* io_data_;
+
net::NetLog* net_log_;
+ base::ThreadChecker thread_checker_;
+
DISALLOW_COPY_AND_ASSIGN(DataReductionProxyDelegate);
};
@@ -127,6 +157,7 @@ void OnResolveProxyHandler(const GURL& url,
const net::ProxyConfig& data_reduction_proxy_config,
const net::ProxyRetryInfoMap& proxy_retry_info,
const DataReductionProxyConfig* config,
+ DataReductionProxyIOData* io_data,
net::ProxyInfo* result);
} // namespace data_reduction_proxy
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 808943d6e80..fb48f0e39bd 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
@@ -22,21 +22,28 @@
#include "base/strings/string_number_conversions.h"
#include "base/test/histogram_tester.h"
#include "base/test/mock_entropy_provider.h"
+#include "base/test/simple_test_tick_clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.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_metrics.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
#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_reduction_proxy_test_utils.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"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "components/variations/variations_associated_data.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_errors.h"
+#include "net/base/network_change_notifier.h"
#include "net/base/proxy_delegate.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
@@ -226,16 +233,18 @@ TEST(DataReductionProxyDelegate, IsTrustedSpdyProxy) {
test.second_proxy_scheme == net::ProxyServer::SCHEME_HTTPS))
<< (&test - test_cases);
- std::vector<net::ProxyServer> proxies_for_http;
+ std::vector<DataReductionProxyServer> proxies_for_http;
net::ProxyServer first_proxy;
net::ProxyServer second_proxy;
if (test.first_proxy_scheme != net::ProxyServer::SCHEME_INVALID) {
first_proxy = GetProxyWithScheme(test.first_proxy_scheme);
- proxies_for_http.push_back(first_proxy);
+ proxies_for_http.push_back(
+ DataReductionProxyServer(first_proxy, ProxyServer::CORE));
}
if (test.second_proxy_scheme != net::ProxyServer::SCHEME_INVALID) {
second_proxy = GetProxyWithScheme(test.second_proxy_scheme);
- proxies_for_http.push_back(second_proxy);
+ proxies_for_http.push_back(DataReductionProxyServer(
+ second_proxy, ProxyServer::UNSPECIFIED_TYPE));
}
std::unique_ptr<DataReductionProxyMutableConfigValues> config_values =
@@ -329,16 +338,18 @@ TEST(DataReductionProxyDelegate, AlternativeProxy) {
!test.gurl.SchemeIsCryptographic() &&
test.second_proxy_scheme == net::ProxyServer::SCHEME_HTTPS;
- std::vector<net::ProxyServer> proxies_for_http;
+ std::vector<DataReductionProxyServer> proxies_for_http;
net::ProxyServer first_proxy;
net::ProxyServer second_proxy;
if (test.first_proxy_scheme != net::ProxyServer::SCHEME_INVALID) {
first_proxy = GetProxyWithScheme(test.first_proxy_scheme);
- proxies_for_http.push_back(first_proxy);
+ proxies_for_http.push_back(
+ DataReductionProxyServer(first_proxy, ProxyServer::CORE));
}
if (test.second_proxy_scheme != net::ProxyServer::SCHEME_INVALID) {
second_proxy = GetProxyWithScheme(test.second_proxy_scheme);
- proxies_for_http.push_back(second_proxy);
+ proxies_for_http.push_back(DataReductionProxyServer(
+ second_proxy, ProxyServer::UNSPECIFIED_TYPE));
}
std::unique_ptr<DataReductionProxyMutableConfigValues> config_values =
@@ -483,7 +494,7 @@ TEST(DataReductionProxyDelegate, DefaultAlternativeProxyStatus) {
{true, false, true},
{true, true, true}};
for (const auto test : tests) {
- std::vector<net::ProxyServer> proxies_for_http;
+ std::vector<DataReductionProxyServer> proxies_for_http;
net::ProxyServer first_proxy;
net::ProxyServer second_proxy =
GetProxyWithScheme(net::ProxyServer::SCHEME_HTTP);
@@ -496,8 +507,10 @@ TEST(DataReductionProxyDelegate, DefaultAlternativeProxyStatus) {
first_proxy = GetProxyWithScheme(net::ProxyServer::SCHEME_HTTPS);
}
- proxies_for_http.push_back(first_proxy);
- proxies_for_http.push_back(second_proxy);
+ proxies_for_http.push_back(
+ DataReductionProxyServer(first_proxy, ProxyServer::CORE));
+ proxies_for_http.push_back(
+ DataReductionProxyServer(second_proxy, ProxyServer::UNSPECIFIED_TYPE));
std::unique_ptr<DataReductionProxyMutableConfigValues> config_values =
DataReductionProxyMutableConfigValues::CreateFromParams(
@@ -613,10 +626,16 @@ class DataReductionProxyDelegateTest : public testing::Test {
lofi_ui_service_ = lofi_ui_service.get();
test_context_->io_data()->set_lofi_ui_service(std::move(lofi_ui_service));
+ // Create a mock network change notifier to make it possible to call its
+ // static methods.
+ network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
+ base::RunLoop().RunUntilIdle();
+
proxy_delegate_ = test_context_->io_data()->CreateProxyDelegate();
context_.set_proxy_delegate(proxy_delegate_.get());
context_.Init();
+ proxy_delegate_->InitializeOnIOThread(test_context_->io_data());
test_context_->EnableDataReductionProxyWithSecureProxyCheckSuccess();
}
@@ -650,10 +669,12 @@ class DataReductionProxyDelegateTest : public testing::Test {
}
int64_t total_received_bytes() const {
+ test_context_->RunUntilIdle();
return GetSessionNetworkStatsInfoInt64("session_received_content_length");
}
int64_t total_original_received_bytes() const {
+ test_context_->RunUntilIdle();
return GetSessionNetworkStatsInfoInt64("session_original_content_length");
}
@@ -671,15 +692,17 @@ class DataReductionProxyDelegateTest : public testing::Test {
return test_context_->config();
}
+ DataReductionProxyDelegate* proxy_delegate() const {
+ return proxy_delegate_.get();
+ }
+
private:
int64_t GetSessionNetworkStatsInfoInt64(const char* key) const {
- const DataReductionProxyNetworkDelegate* drp_network_delegate =
- reinterpret_cast<const DataReductionProxyNetworkDelegate*>(
- context_.network_delegate());
-
std::unique_ptr<base::DictionaryValue> session_network_stats_info =
- base::DictionaryValue::From(
- drp_network_delegate->SessionNetworkStatsInfoToValue());
+ base::DictionaryValue::From(test_context_->settings()
+ ->data_reduction_proxy_service()
+ ->compression_stats()
+ ->SessionNetworkStatsInfoToValue());
EXPECT_TRUE(session_network_stats_info);
std::string string_value;
@@ -695,7 +718,9 @@ class DataReductionProxyDelegateTest : public testing::Test {
net::URLRequestContextStorage context_storage_;
TestLoFiUIService* lofi_ui_service_;
- std::unique_ptr<net::ProxyDelegate> proxy_delegate_;
+
+ std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
+ std::unique_ptr<DataReductionProxyDelegate> proxy_delegate_;
std::unique_ptr<DataReductionProxyTestContext> test_context_;
};
@@ -747,7 +772,7 @@ TEST_F(DataReductionProxyDelegateTest, OnResolveProxyHandler) {
// Another proxy is used. It should be used afterwards.
result.Use(other_proxy_info);
OnResolveProxyHandler(url, "GET", data_reduction_proxy_config,
- empty_proxy_retry_info, config(), &result);
+ empty_proxy_retry_info, config(), nullptr, &result);
EXPECT_EQ(other_proxy_info.proxy_server(), result.proxy_server());
// A direct connection is used. The data reduction proxy should be used
@@ -756,7 +781,7 @@ TEST_F(DataReductionProxyDelegateTest, OnResolveProxyHandler) {
result.Use(direct_proxy_info);
net::ProxyConfig::ID prev_id = result.config_id();
OnResolveProxyHandler(url, "GET", data_reduction_proxy_config,
- empty_proxy_retry_info, config(), &result);
+ empty_proxy_retry_info, config(), nullptr, &result);
EXPECT_EQ(data_reduction_proxy_info.proxy_server(), result.proxy_server());
// Only the proxy list should be updated, not the proxy info.
EXPECT_EQ(result.config_id(), prev_id);
@@ -765,9 +790,9 @@ TEST_F(DataReductionProxyDelegateTest, OnResolveProxyHandler) {
// list. A direct connection should be used afterwards.
result.Use(direct_proxy_info);
prev_id = result.config_id();
- OnResolveProxyHandler(GURL("ws://echo.websocket.org/"), "GET",
- data_reduction_proxy_config,
- data_reduction_proxy_retry_info, config(), &result);
+ OnResolveProxyHandler(
+ GURL("ws://echo.websocket.org/"), "GET", data_reduction_proxy_config,
+ data_reduction_proxy_retry_info, config(), nullptr, &result);
EXPECT_TRUE(result.proxy_server().is_direct());
EXPECT_EQ(result.config_id(), prev_id);
@@ -775,30 +800,31 @@ TEST_F(DataReductionProxyDelegateTest, OnResolveProxyHandler) {
result.UseDirect();
OnResolveProxyHandler(GURL("wss://echo.websocket.org/"), "GET",
data_reduction_proxy_config, empty_proxy_retry_info,
- config(), &result);
+ config(), nullptr, &result);
EXPECT_TRUE(result.is_direct());
result.UseDirect();
OnResolveProxyHandler(GURL("wss://echo.websocket.org/"), "GET",
data_reduction_proxy_config, empty_proxy_retry_info,
- config(), &result);
+ config(), nullptr, &result);
EXPECT_TRUE(result.is_direct());
// POST methods go direct.
result.UseDirect();
OnResolveProxyHandler(url, "POST", data_reduction_proxy_config,
- empty_proxy_retry_info, config(), &result);
+ empty_proxy_retry_info, config(), nullptr, &result);
EXPECT_TRUE(result.is_direct());
// Without DataCompressionProxyCriticalBypass Finch trial set, the
// BYPASS_DATA_REDUCTION_PROXY load flag should be ignored.
result.UseDirect();
OnResolveProxyHandler(url, "GET", data_reduction_proxy_config,
- empty_proxy_retry_info, config(), &result);
+ empty_proxy_retry_info, config(), nullptr, &result);
EXPECT_FALSE(result.is_direct());
OnResolveProxyHandler(url, "GET", data_reduction_proxy_config,
- empty_proxy_retry_info, config(), &other_proxy_info);
+ empty_proxy_retry_info, config(), nullptr,
+ &other_proxy_info);
EXPECT_FALSE(other_proxy_info.is_direct());
}
@@ -869,8 +895,8 @@ TEST_F(DataReductionProxyDelegateTest, HTTPRequests) {
data_reduction_proxy_config.set_id(1);
}
EXPECT_NE(test.use_direct_proxy, data_reduction_proxy_config.is_valid());
- config()->SetStateForTest(test.enabled_by_user /* enabled */,
- false /* at_startup */);
+ config()->UpdateConfigForTesting(test.enabled_by_user /* enabled */,
+ false /* at_startup */);
net::ProxyRetryInfoMap empty_proxy_retry_info;
@@ -881,7 +907,7 @@ TEST_F(DataReductionProxyDelegateTest, HTTPRequests) {
net::ProxyInfo result;
result.Use(direct_proxy_info);
OnResolveProxyHandler(url, "GET", data_reduction_proxy_config,
- empty_proxy_retry_info, config(), &result);
+ empty_proxy_retry_info, config(), nullptr, &result);
histogram_tester.ExpectTotalCount(
"DataReductionProxy.ConfigService.HTTPRequests",
test.expect_histogram ? 1 : 0);
@@ -927,6 +953,57 @@ TEST_F(DataReductionProxyDelegateTest, OnCompletedSizeFor200) {
"DataReductionProxy.ConfigService.HTTPRequests", 1, 1);
}
+TEST_F(DataReductionProxyDelegateTest, TimeToFirstHttpDataSaverRequest) {
+ std::unique_ptr<base::SimpleTestTickClock> tick_clock(
+ new base::SimpleTestTickClock());
+ base::SimpleTestTickClock* tick_clock_ptr = tick_clock.get();
+ proxy_delegate()->SetTickClockForTesting(std::move(tick_clock));
+
+ const char kResponseHeaders[] =
+ "HTTP/1.1 200 OK\r\n"
+ "Via: 1.1 Chrome-Compression-Proxy-Suffix\r\n"
+ "Content-Length: 10\r\n\r\n";
+
+ {
+ base::HistogramTester histogram_tester;
+ base::TimeDelta advance_time(base::TimeDelta::FromSeconds(1));
+ tick_clock_ptr->Advance(advance_time);
+
+ FetchURLRequest(GURL("http://example.com/path/"), nullptr, kResponseHeaders,
+ 10);
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.TimeToFirstDataSaverRequest",
+ advance_time.InMilliseconds(), 1);
+
+ // Second request should not result in recording of UMA.
+ FetchURLRequest(GURL("http://example.com/path/"), nullptr, kResponseHeaders,
+ 10);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.TimeToFirstDataSaverRequest", 1);
+ }
+
+ {
+ base::HistogramTester histogram_tester;
+ // Third request should result in recording of UMA due to change in IP.
+ base::TimeDelta advance_time(base::TimeDelta::FromSeconds(2));
+ net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ base::RunLoop().RunUntilIdle();
+
+ tick_clock_ptr->Advance(advance_time);
+ FetchURLRequest(GURL("http://example.com/path/"), nullptr, kResponseHeaders,
+ 10);
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.TimeToFirstDataSaverRequest",
+ advance_time.InMilliseconds(), 1);
+
+ // Fourth request should not result in recording of UMA.
+ FetchURLRequest(GURL("http://example.com/path/"), nullptr, kResponseHeaders,
+ 10);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.TimeToFirstDataSaverRequest", 1);
+ }
+}
+
TEST_F(DataReductionProxyDelegateTest, OnCompletedSizeFor304) {
int64_t baseline_received_bytes = total_received_bytes();
int64_t baseline_original_received_bytes = total_original_received_bytes();
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc
index 90be43e8530..f4d38f3d7b4 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc
@@ -38,6 +38,8 @@ DataReductionProxyInterceptor::~DataReductionProxyInterceptor() {
net::URLRequestJob* DataReductionProxyInterceptor::MaybeInterceptRequest(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
return nullptr;
}
@@ -45,12 +47,16 @@ net::URLRequestJob* DataReductionProxyInterceptor::MaybeInterceptRedirect(
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const GURL& location) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
return MaybeInterceptResponseOrRedirect(request, network_delegate);
}
net::URLRequestJob* DataReductionProxyInterceptor::MaybeInterceptResponse(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
return MaybeInterceptResponseOrRedirect(request, network_delegate);
}
@@ -58,6 +64,8 @@ net::URLRequestJob*
DataReductionProxyInterceptor::MaybeInterceptResponseOrRedirect(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
DCHECK(request);
if (request->response_info().was_cached)
return nullptr;
@@ -107,6 +115,8 @@ void DataReductionProxyInterceptor::MaybeAddBypassEvent(
const DataReductionProxyInfo& data_reduction_proxy_info,
DataReductionProxyBypassType bypass_type,
bool should_retry) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
if (data_reduction_proxy_info.bypass_action != BYPASS_ACTION_TYPE_NONE) {
event_creator_->AddBypassActionEvent(
request->net_log(), data_reduction_proxy_info.bypass_action,
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h
index 365d4a2c9e1..2c4af95ba0f 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/macros.h"
+#include "base/threading/thread_checker.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
#include "net/url_request/url_request_interceptor.h"
@@ -85,6 +86,8 @@ class DataReductionProxyInterceptor : public net::URLRequestInterceptor {
// these cases.
std::unique_ptr<DataReductionProxyBypassProtocol> bypass_protocol_;
+ base::ThreadChecker thread_checker_;
+
DISALLOW_COPY_AND_ASSIGN(DataReductionProxyInterceptor);
};
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 40d50fd359e..70390e396ac 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
@@ -21,6 +21,8 @@
#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"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "components/prefs/pref_service.h"
#include "net/base/net_errors.h"
#include "net/base/request_priority.h"
@@ -111,11 +113,15 @@ class DataReductionProxyInterceptorTest : public testing::Test {
DataReductionProxyInterceptorTest() {
test_context_ =
DataReductionProxyTestContext::Builder()
- .WithParamsFlags(DataReductionProxyParams::kAllowed)
+ .WithParamsFlags(0)
.WithParamsDefinitions(TestDataReductionProxyParams::HAS_EVERYTHING)
.Build();
default_context_.reset(new TestURLRequestContextWithDataReductionProxy(
- test_context_->config()->test_params()->proxies_for_http().front(),
+ test_context_->config()
+ ->test_params()
+ ->proxies_for_http()
+ .front()
+ .proxy_server(),
&default_network_delegate_));
default_context_->set_network_delegate(&default_network_delegate_);
default_context_->set_net_log(test_context_->net_log());
@@ -204,17 +210,17 @@ class DataReductionProxyInterceptorWithServerTest : public testing::Test {
ASSERT_TRUE(proxy_.Start());
ASSERT_TRUE(direct_.Start());
- test_context_ =
- DataReductionProxyTestContext::Builder()
- .WithParamsFlags(DataReductionProxyParams::kAllowed)
- .WithURLRequestContext(&context_)
- .Build();
+ test_context_ = DataReductionProxyTestContext::Builder()
+ .WithParamsFlags(0)
+ .WithURLRequestContext(&context_)
+ .Build();
std::string spec;
base::TrimString(proxy_.GetURL("/").spec(), "/", &spec);
net::ProxyServer origin =
net::ProxyServer::FromURI(spec, net::ProxyServer::SCHEME_HTTP);
- std::vector<net::ProxyServer> proxies_for_http;
- proxies_for_http.push_back(origin);
+ std::vector<DataReductionProxyServer> proxies_for_http;
+ proxies_for_http.push_back(
+ DataReductionProxyServer(origin, ProxyServer::UNSPECIFIED_TYPE));
test_context_->config()->test_params()->SetProxiesForHttp(proxies_for_http);
std::string proxy_name = origin.ToURI();
proxy_service_ = net::ProxyService::CreateFixedFromPacResult(
@@ -323,7 +329,7 @@ class DataReductionProxyInterceptorEndToEndTest : public testing::Test {
}
net::ProxyServer origin() const {
- return config()->test_params()->proxies_for_http().front();
+ return config()->test_params()->proxies_for_http().front().proxy_server();
}
private:
@@ -476,7 +482,13 @@ TEST_F(DataReductionProxyInterceptorEndToEndTest, RedirectWithBypassAndRetry) {
EXPECT_EQ(std::vector<GURL>(1, GURL("http://foo.com")), request->url_chain());
}
-TEST_F(DataReductionProxyInterceptorEndToEndTest, RedirectChainToHttps) {
+// https://crbug.com/668197: Flaky on android_n5x_swarming_rel bot.
+#if defined(OS_ANDROID)
+#define MAYBE_RedirectChainToHttps DISABLED_RedirectChainToHttps
+#else
+#define MAYBE_RedirectChainToHttps RedirectChainToHttps
+#endif
+TEST_F(DataReductionProxyInterceptorEndToEndTest, MAYBE_RedirectChainToHttps) {
// First, a redirect is successfully received through the Data Reduction
// Proxy. HSTS is forced for play.google.com and prebaked into Chrome, so
// http://play.google.com will automatically be redirected to
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 5ce71b4a3bc..43a0352a1e1 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
@@ -157,16 +157,18 @@ DataReductionProxyIOData::DataReductionProxyIOData(
proxy_delegate_.reset(new DataReductionProxyDelegate(
config_.get(), configurator_.get(), event_creator_.get(),
bypass_stats_.get(), net_log_));
- }
-
- DataReductionProxyIOData::DataReductionProxyIOData()
- : client_(Client::UNKNOWN),
- net_log_(nullptr),
- url_request_context_getter_(nullptr),
- weak_factory_(this) {
}
+DataReductionProxyIOData::DataReductionProxyIOData()
+ : client_(Client::UNKNOWN),
+ net_log_(nullptr),
+ url_request_context_getter_(nullptr),
+ weak_factory_(this) {}
+
DataReductionProxyIOData::~DataReductionProxyIOData() {
+ // Guaranteed to be destroyed on IO thread if the IO thread is still
+ // available at the time of destruction. If the IO thread is unavailable,
+ // then the destruction will happen on the UI thread.
}
void DataReductionProxyIOData::ShutdownOnUIThread() {
@@ -193,7 +195,10 @@ void DataReductionProxyIOData::SetDataReductionProxyService(
void DataReductionProxyIOData::InitializeOnIOThread() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
- config_->InitializeOnIOThread(basic_url_request_context_getter_.get());
+ config_->InitializeOnIOThread(basic_url_request_context_getter_.get(),
+ url_request_context_getter_);
+ bypass_stats_->InitializeOnIOThread();
+ proxy_delegate_->InitializeOnIOThread(this);
if (config_client_.get())
config_client_->InitializeOnIOThread(url_request_context_getter_);
if (ui_task_runner_->BelongsToCurrentThread()) {
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 d9b71e11ed5..339e8764554 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
@@ -24,6 +24,7 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h"
#include "components/data_reduction_proxy/core/common/lofi_decider.h"
#include "components/data_reduction_proxy/core/common/lofi_ui_service.h"
+#include "components/data_reduction_proxy/core/common/resource_type_provider.h"
namespace base {
class Value;
@@ -169,18 +170,27 @@ class DataReductionProxyIOData : public DataReductionProxyEventStorageDelegate {
LoFiDecider* lofi_decider() const { return lofi_decider_.get(); }
- void set_lofi_decider(std::unique_ptr<LoFiDecider> lofi_decider) const {
+ void set_lofi_decider(std::unique_ptr<LoFiDecider> lofi_decider) {
lofi_decider_ = std::move(lofi_decider);
}
LoFiUIService* lofi_ui_service() const { return lofi_ui_service_.get(); }
// Takes ownership of |lofi_ui_service|.
- void set_lofi_ui_service(
- std::unique_ptr<LoFiUIService> lofi_ui_service) const {
+ void set_lofi_ui_service(std::unique_ptr<LoFiUIService> lofi_ui_service) {
lofi_ui_service_ = std::move(lofi_ui_service);
}
+ ResourceTypeProvider* resource_type_provider() const {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ return resource_type_provider_.get();
+ }
+
+ void set_resource_type_provider(
+ std::unique_ptr<ResourceTypeProvider> resource_type_provider) {
+ 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);
@@ -226,10 +236,13 @@ class DataReductionProxyIOData : public DataReductionProxyEventStorageDelegate {
std::unique_ptr<DataReductionProxyConfig> config_;
// Handles getting if a request is in Lo-Fi mode.
- mutable std::unique_ptr<LoFiDecider> lofi_decider_;
+ std::unique_ptr<LoFiDecider> lofi_decider_;
// Handles showing Lo-Fi UI when a Lo-Fi response is received.
- mutable std::unique_ptr<LoFiUIService> lofi_ui_service_;
+ std::unique_ptr<LoFiUIService> lofi_ui_service_;
+
+ // Handles getting the content type of a request.
+ std::unique_ptr<ResourceTypeProvider> resource_type_provider_;
// Creates Data Reduction Proxy-related events for logging.
std::unique_ptr<DataReductionProxyEventCreator> event_creator_;
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 fc6c3ce5b35..7b09403fd4c 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
@@ -19,6 +19,7 @@
#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"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "net/http/http_network_session.h"
@@ -100,10 +101,10 @@ class DataReductionProxyIODataTest : public testing::Test {
TEST_F(DataReductionProxyIODataTest, TestConstruction) {
std::unique_ptr<DataReductionProxyIOData> io_data(
- new DataReductionProxyIOData(
- Client::UNKNOWN, DataReductionProxyParams::kAllowed, net_log(),
- task_runner(), task_runner(), false /* enabled */,
- std::string() /* user_agent */, std::string() /* channel */));
+ new DataReductionProxyIOData(Client::UNKNOWN, 0, net_log(), task_runner(),
+ task_runner(), false /* enabled */,
+ std::string() /* user_agent */,
+ std::string() /* channel */));
// Check that the SimpleURLRequestContextGetter uses vanilla HTTP.
net::URLRequestContext* request_context =
@@ -149,9 +150,7 @@ TEST_F(DataReductionProxyIODataTest, TestResetBadProxyListOnDisableDataSaver) {
net::TestURLRequestContext context(false);
std::unique_ptr<DataReductionProxyTestContext> drp_test_context =
DataReductionProxyTestContext::Builder()
- .WithParamsFlags(DataReductionProxyParams::kAllowed |
- DataReductionProxyParams::kFallbackAllowed |
- DataReductionProxyParams::kPromoAllowed)
+ .WithParamsFlags(DataReductionProxyParams::kPromoAllowed)
.WithURLRequestContext(&context)
.SkipSettingsInitialization()
.Build();
@@ -191,17 +190,18 @@ TEST_F(DataReductionProxyIODataTest, HoldbackConfiguresProxies) {
net::TestURLRequestContext context(false);
std::unique_ptr<DataReductionProxyTestContext> drp_test_context =
DataReductionProxyTestContext::Builder()
- .WithParamsFlags(DataReductionProxyParams::kAllowed |
- DataReductionProxyParams::kFallbackAllowed |
- DataReductionProxyParams::kPromoAllowed |
+ .WithParamsFlags(DataReductionProxyParams::kPromoAllowed |
DataReductionProxyParams::kHoldback)
.WithURLRequestContext(&context)
.SkipSettingsInitialization()
.Build();
EXPECT_TRUE(drp_test_context->test_params()->proxies_for_http().size() > 0);
- EXPECT_FALSE(
- drp_test_context->test_params()->proxies_for_http().front().is_direct());
+ EXPECT_FALSE(drp_test_context->test_params()
+ ->proxies_for_http()
+ .front()
+ .proxy_server()
+ .is_direct());
}
} // namespace data_reduction_proxy
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 57fdbe3b5f7..c1ff06552b3 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
@@ -12,8 +12,6 @@ class ProxyConfig;
class URLRequest;
}
-class PrefService;
-
namespace data_reduction_proxy {
class DataReductionProxyConfig;
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 8dd6bc18039..da8d668cad6 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
@@ -13,6 +13,7 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.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 "net/base/load_flags.h"
#include "net/log/net_log_source_type.h"
#include "net/log/net_log_with_source.h"
@@ -32,12 +33,13 @@ TEST(ChromeNetworkDailyDataSavingMetricsTest,
base::MessageLoopForIO message_loop;
std::unique_ptr<DataReductionProxyTestContext> test_context =
DataReductionProxyTestContext::Builder()
- .WithParamsFlags(DataReductionProxyParams::kAllowed)
+ .WithParamsFlags(0)
.WithParamsDefinitions(TestDataReductionProxyParams::HAS_ORIGIN)
.Build();
TestDataReductionProxyConfig* config = test_context->config();
- net::ProxyServer origin = config->test_params()->proxies_for_http().front();
+ net::ProxyServer origin =
+ config->test_params()->proxies_for_http().front().proxy_server();
net::ProxyConfig data_reduction_proxy_config;
data_reduction_proxy_config.proxy_rules().ParseFromString(
"http=" + origin.host_port_pair().ToString() + ",direct://");
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 b5f5cd2e2e5..905160c4899 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
@@ -7,6 +7,7 @@
#include <vector>
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
namespace data_reduction_proxy {
@@ -17,8 +18,6 @@ DataReductionProxyMutableConfigValues::CreateFromParams(
new DataReductionProxyMutableConfigValues());
config_values->promo_allowed_ = params->promo_allowed();
config_values->holdback_ = params->holdback();
- config_values->allowed_ = params->allowed();
- config_values->fallback_allowed_ = params->fallback_allowed();
config_values->secure_proxy_check_url_ = params->secure_proxy_check_url();
return config_values;
}
@@ -26,8 +25,6 @@ DataReductionProxyMutableConfigValues::CreateFromParams(
DataReductionProxyMutableConfigValues::DataReductionProxyMutableConfigValues()
: promo_allowed_(false),
holdback_(false),
- allowed_(false),
- fallback_allowed_(false),
use_override_proxies_for_http_(false) {
use_override_proxies_for_http_ =
params::GetOverrideProxiesForHttpFromCommandLine(
@@ -49,15 +46,7 @@ bool DataReductionProxyMutableConfigValues::holdback() const {
return holdback_;
}
-bool DataReductionProxyMutableConfigValues::allowed() const {
- return allowed_;
-}
-
-bool DataReductionProxyMutableConfigValues::fallback_allowed() const {
- return fallback_allowed_;
-}
-
-const std::vector<net::ProxyServer>&
+const std::vector<DataReductionProxyServer>
DataReductionProxyMutableConfigValues::proxies_for_http() const {
DCHECK(thread_checker_.CalledOnValidThread());
if (use_override_proxies_for_http_ && !proxies_for_http_.empty()) {
@@ -79,7 +68,7 @@ const GURL& DataReductionProxyMutableConfigValues::secure_proxy_check_url()
}
void DataReductionProxyMutableConfigValues::UpdateValues(
- const std::vector<net::ProxyServer>& proxies_for_http) {
+ const std::vector<DataReductionProxyServer>& proxies_for_http) {
DCHECK(thread_checker_.CalledOnValidThread());
proxies_for_http_ = proxies_for_http;
}
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 b51f915305b..18374527716 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
@@ -17,6 +17,7 @@
namespace data_reduction_proxy {
class DataReductionProxyParams;
+class DataReductionProxyServer;
// A |DataReductionProxyConfigValues| which is permitted to change its
// underlying values via the UpdateValues method.
@@ -33,7 +34,7 @@ class DataReductionProxyMutableConfigValues
// Updates |proxies_for_http_| with the provided values.
// Virtual for testing.
virtual void UpdateValues(
- const std::vector<net::ProxyServer>& proxies_for_http);
+ const std::vector<DataReductionProxyServer>& proxies_for_http);
// Invalidates |this| by clearing the stored Data Reduction Proxy servers.
void Invalidate();
@@ -41,9 +42,7 @@ class DataReductionProxyMutableConfigValues
// Overrides of |DataReductionProxyConfigValues|
bool promo_allowed() const override;
bool holdback() const override;
- bool allowed() const override;
- bool fallback_allowed() const override;
- const std::vector<net::ProxyServer>& proxies_for_http() const override;
+ const std::vector<DataReductionProxyServer> proxies_for_http() const override;
const GURL& secure_proxy_check_url() const override;
protected:
@@ -52,15 +51,13 @@ class DataReductionProxyMutableConfigValues
private:
bool promo_allowed_;
bool holdback_;
- bool allowed_;
- bool fallback_allowed_;
- std::vector<net::ProxyServer> proxies_for_http_;
+ 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.
bool use_override_proxies_for_http_;
- std::vector<net::ProxyServer> override_proxies_for_http_;
+ std::vector<DataReductionProxyServer> override_proxies_for_http_;
// Enforce usage on the IO thread.
base::ThreadChecker thread_checker_;
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 34df69877a6..8753724173d 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
@@ -8,7 +8,9 @@
#include "base/command_line.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "net/proxy/proxy_server.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -22,8 +24,7 @@ class DataReductionProxyMutableConfigValuesTest : public testing::Test {
~DataReductionProxyMutableConfigValuesTest() override {}
void Init() {
- params_.reset(new DataReductionProxyParams(
- DataReductionProxyParams::kAllowAllProxyConfigurations));
+ params_.reset(new DataReductionProxyParams(0));
mutable_config_values_ =
DataReductionProxyMutableConfigValues::CreateFromParams(params_.get());
}
@@ -39,21 +40,27 @@ class DataReductionProxyMutableConfigValuesTest : public testing::Test {
TEST_F(DataReductionProxyMutableConfigValuesTest, UpdateValuesAndInvalidate) {
Init();
- EXPECT_EQ(std::vector<net::ProxyServer>(),
+ EXPECT_EQ(std::vector<DataReductionProxyServer>(),
mutable_config_values()->proxies_for_http());
- std::vector<net::ProxyServer> proxies_for_http;
- proxies_for_http.push_back(net::ProxyServer::FromURI(
+ std::vector<DataReductionProxyServer> proxies_for_http;
+
+ net::ProxyServer first_proxy_server(net::ProxyServer::FromURI(
"http://first.net", net::ProxyServer::SCHEME_HTTP));
- proxies_for_http.push_back(net::ProxyServer::FromURI(
- "http://second.net", net::ProxyServer::SCHEME_HTTP));
+ proxies_for_http.push_back(
+ DataReductionProxyServer(first_proxy_server, ProxyServer::CORE));
+
+ net::ProxyServer second_proxy_server = net::ProxyServer::FromURI(
+ "http://second.net", net::ProxyServer::SCHEME_HTTP);
+ proxies_for_http.push_back(DataReductionProxyServer(
+ second_proxy_server, ProxyServer::UNSPECIFIED_TYPE));
mutable_config_values()->UpdateValues(proxies_for_http);
EXPECT_EQ(proxies_for_http, mutable_config_values()->proxies_for_http());
+ // Invalidation must clear out the list of proxies and their properties.
mutable_config_values()->Invalidate();
- EXPECT_EQ(std::vector<net::ProxyServer>(),
- mutable_config_values()->proxies_for_http());
+ EXPECT_TRUE(mutable_config_values()->proxies_for_http().empty());
}
// Tests if HTTP proxies are overridden when |kDataReductionProxyHttpProxies|
@@ -64,29 +71,39 @@ TEST_F(DataReductionProxyMutableConfigValuesTest, OverrideProxiesForHttp) {
"http://override-first.net;http://override-second.net");
Init();
- EXPECT_EQ(std::vector<net::ProxyServer>(),
+ EXPECT_EQ(std::vector<DataReductionProxyServer>(),
mutable_config_values()->proxies_for_http());
- std::vector<net::ProxyServer> proxies_for_http;
- proxies_for_http.push_back(net::ProxyServer::FromURI(
+ std::vector<DataReductionProxyServer> proxies_for_http;
+
+ net::ProxyServer first_proxy_server(net::ProxyServer::FromURI(
"http://first.net", net::ProxyServer::SCHEME_HTTP));
- proxies_for_http.push_back(net::ProxyServer::FromURI(
- "http://second.net", net::ProxyServer::SCHEME_HTTP));
+ proxies_for_http.push_back(
+ DataReductionProxyServer(first_proxy_server, ProxyServer::CORE));
+
+ net::ProxyServer second_proxy_server = net::ProxyServer::FromURI(
+ "http://second.net", net::ProxyServer::SCHEME_HTTP);
+ proxies_for_http.push_back(DataReductionProxyServer(
+ second_proxy_server, ProxyServer::UNSPECIFIED_TYPE));
mutable_config_values()->UpdateValues(proxies_for_http);
- std::vector<net::ProxyServer> expected_override_proxies_for_http;
- expected_override_proxies_for_http.push_back(net::ProxyServer::FromURI(
- "http://override-first.net", net::ProxyServer::SCHEME_HTTP));
- expected_override_proxies_for_http.push_back(net::ProxyServer::FromURI(
- "http://override-second.net", net::ProxyServer::SCHEME_HTTP));
+ std::vector<DataReductionProxyServer> expected_override_proxies_for_http;
+ expected_override_proxies_for_http.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("http://override-first.net",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
+ expected_override_proxies_for_http.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("http://override-second.net",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
EXPECT_EQ(expected_override_proxies_for_http,
mutable_config_values()->proxies_for_http());
+ // Invalidation must clear out the list of proxies and their properties.
mutable_config_values()->Invalidate();
- EXPECT_EQ(std::vector<net::ProxyServer>(),
- mutable_config_values()->proxies_for_http());
+ EXPECT_TRUE(mutable_config_values()->proxies_for_http().empty());
}
// Tests if HTTP proxies are overridden when |kDataReductionProxy| or
@@ -112,37 +129,48 @@ TEST_F(DataReductionProxyMutableConfigValuesTest, OverrideDataReductionProxy) {
}
Init();
- EXPECT_EQ(std::vector<net::ProxyServer>(),
+ EXPECT_EQ(std::vector<DataReductionProxyServer>(),
mutable_config_values()->proxies_for_http());
- std::vector<net::ProxyServer> proxies_for_http;
+ std::vector<DataReductionProxyServer> proxies_for_http;
+
if (test.set_primary) {
- proxies_for_http.push_back(net::ProxyServer::FromURI(
+ net::ProxyServer first_proxy_server = (net::ProxyServer::FromURI(
"http://first.net", net::ProxyServer::SCHEME_HTTP));
+ proxies_for_http.push_back(
+ DataReductionProxyServer(first_proxy_server, ProxyServer::CORE));
}
if (test.set_fallback) {
- proxies_for_http.push_back(net::ProxyServer::FromURI(
- "http://second.net", net::ProxyServer::SCHEME_HTTP));
+ net::ProxyServer second_proxy_server = net::ProxyServer::FromURI(
+ "http://second.net", net::ProxyServer::SCHEME_HTTP);
+
+ proxies_for_http.push_back(DataReductionProxyServer(
+ second_proxy_server, ProxyServer::UNSPECIFIED_TYPE));
}
mutable_config_values()->UpdateValues(proxies_for_http);
- std::vector<net::ProxyServer> expected_override_proxies_for_http;
+ // Overriding proxies must have type UNSPECIFIED_TYPE.
+ std::vector<DataReductionProxyServer> expected_override_proxies_for_http;
if (test.set_primary) {
- expected_override_proxies_for_http.push_back(net::ProxyServer::FromURI(
- "http://override-first.net", net::ProxyServer::SCHEME_HTTP));
+ expected_override_proxies_for_http.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("http://override-first.net",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
}
if (test.set_fallback) {
- expected_override_proxies_for_http.push_back(net::ProxyServer::FromURI(
- "http://override-second.net", net::ProxyServer::SCHEME_HTTP));
+ expected_override_proxies_for_http.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("http://override-second.net",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
}
EXPECT_EQ(expected_override_proxies_for_http,
mutable_config_values()->proxies_for_http());
+ // Invalidation must clear out the list of proxies and their properties.
mutable_config_values()->Invalidate();
- EXPECT_EQ(std::vector<net::ProxyServer>(),
- mutable_config_values()->proxies_for_http());
+ EXPECT_TRUE(mutable_config_values()->proxies_for_http().empty());
}
}
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 af42d048a15..7c1fda14859 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
@@ -11,7 +11,6 @@
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
-#include "base/values.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
@@ -35,6 +34,8 @@
#include "net/url_request/url_request_status.h"
#include "url/gurl.h"
+namespace data_reduction_proxy {
+
namespace {
// |lofi_low_header_added| is set to true iff Lo-Fi "q=low" request header can
@@ -98,85 +99,28 @@ void RecordContentLengthHistograms(bool lofi_low_header_added,
received_content_length);
}
-// Scales |byte_count| by the ratio of |numerator|:|denomenator|.
-int64_t ScaleByteCountByRatio(int64_t byte_count,
- int64_t numerator,
- int64_t denomenator) {
- DCHECK_LE(0, byte_count);
- DCHECK_LE(0, numerator);
- DCHECK_LT(0, denomenator);
-
- // As an optimization, use integer arithmetic if it won't overflow.
- if (byte_count <= std::numeric_limits<int32_t>::max() &&
- numerator <= std::numeric_limits<int32_t>::max()) {
- return byte_count * numerator / denomenator;
- }
-
- double scaled_byte_count = static_cast<double>(byte_count) *
- static_cast<double>(numerator) /
- static_cast<double>(denomenator);
- if (scaled_byte_count >
- static_cast<double>(std::numeric_limits<int64_t>::max())) {
- // If this ever triggers, then byte counts can no longer be safely stored in
- // 64-bit ints.
- NOTREACHED();
- return byte_count;
- }
- return static_cast<int64_t>(scaled_byte_count);
-}
-
-// Calculates the effective original content length of the |request|, accounting
-// for partial responses if necessary.
-int64_t CalculateEffectiveOCL(const net::URLRequest& request, int net_error) {
- int64_t original_content_length_from_header =
- request.response_headers()->GetInt64HeaderValue(
- "x-original-content-length");
-
- if (original_content_length_from_header < 0)
- return request.received_response_content_length();
- if (net_error == net::OK)
- return original_content_length_from_header;
-
- int64_t content_length_from_header =
- request.response_headers()->GetContentLength();
-
- if (content_length_from_header < 0)
- return request.received_response_content_length();
- if (content_length_from_header == 0)
- return original_content_length_from_header;
-
- return ScaleByteCountByRatio(request.received_response_content_length(),
- original_content_length_from_header,
- content_length_from_header);
-}
-
// Given a |request| that went through the Data Reduction Proxy, this function
// estimates how many bytes would have been received if the response had been
// received directly from the origin using HTTP/1.1 with a content length of
// |adjusted_original_content_length|.
-int64_t EstimateOriginalReceivedBytes(const net::URLRequest& request,
- int net_error) {
+int64_t EstimateOriginalReceivedBytes(const net::URLRequest& request) {
if (request.was_cached() || !request.response_headers())
return request.GetTotalReceivedBytes();
// TODO(sclittle): Remove headers added by Data Reduction Proxy when computing
// original size. http://crbug/535701.
return request.response_headers()->raw_headers().size() +
- CalculateEffectiveOCL(request, net_error);
+ util::CalculateEffectiveOCL(request);
}
} // namespace
-namespace data_reduction_proxy {
-
DataReductionProxyNetworkDelegate::DataReductionProxyNetworkDelegate(
std::unique_ptr<net::NetworkDelegate> network_delegate,
DataReductionProxyConfig* config,
DataReductionProxyRequestOptions* request_options,
const DataReductionProxyConfigurator* configurator)
: LayeredNetworkDelegate(std::move(network_delegate)),
- total_received_bytes_(0),
- total_original_received_bytes_(0),
data_reduction_proxy_config_(config),
data_reduction_proxy_bypass_stats_(nullptr),
data_reduction_proxy_request_options_(request_options),
@@ -193,26 +137,17 @@ DataReductionProxyNetworkDelegate::~DataReductionProxyNetworkDelegate() {
void DataReductionProxyNetworkDelegate::InitIODataAndUMA(
DataReductionProxyIOData* io_data,
DataReductionProxyBypassStats* bypass_stats) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(bypass_stats);
data_reduction_proxy_io_data_ = io_data;
data_reduction_proxy_bypass_stats_ = bypass_stats;
}
-std::unique_ptr<base::Value>
-DataReductionProxyNetworkDelegate::SessionNetworkStatsInfoToValue() const {
- auto dict = base::MakeUnique<base::DictionaryValue>();
- // Use strings to avoid overflow. base::Value only supports 32-bit integers.
- dict->SetString("session_received_content_length",
- base::Int64ToString(total_received_bytes_));
- dict->SetString("session_original_content_length",
- base::Int64ToString(total_original_received_bytes_));
- return std::move(dict);
-}
-
void DataReductionProxyNetworkDelegate::OnBeforeURLRequestInternal(
net::URLRequest* request,
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
@@ -234,14 +169,31 @@ void DataReductionProxyNetworkDelegate::OnBeforeStartTransactionInternal(
net::URLRequest* request,
const net::CompletionCallback& callback,
net::HttpRequestHeaders* headers) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
if (!data_reduction_proxy_io_data_)
return;
- if (!data_reduction_proxy_io_data_->lofi_decider())
- return;
if (!data_reduction_proxy_io_data_->IsEnabled())
return;
- data_reduction_proxy_io_data_->lofi_decider()->MaybeSetAcceptTransformHeader(
- *request, data_reduction_proxy_config_->lofi_off(), headers);
+
+ if (request->url().SchemeIsCryptographic() ||
+ !request->url().SchemeIsHTTPOrHTTPS()) {
+ return;
+ }
+
+ if (data_reduction_proxy_io_data_->resource_type_provider()) {
+ // Sets content type of |request| in the resource type provider, so it can
+ // be later used for determining the proxy that should be used for fetching
+ // |request|.
+ data_reduction_proxy_io_data_->resource_type_provider()->SetContentType(
+ *request);
+ }
+
+ if (data_reduction_proxy_io_data_->lofi_decider()) {
+ data_reduction_proxy_io_data_->lofi_decider()
+ ->MaybeSetAcceptTransformHeader(
+ *request, data_reduction_proxy_config_->lofi_off(), headers);
+ }
}
void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal(
@@ -249,6 +201,7 @@ void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal(
const net::ProxyInfo& proxy_info,
const net::ProxyRetryInfoMap& proxy_retry_info,
net::HttpRequestHeaders* headers) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(data_reduction_proxy_config_);
DCHECK(request);
@@ -318,9 +271,7 @@ void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal(
data->set_lofi_requested(
lofi_decider ? lofi_decider->ShouldRecordLoFiUMA(*request) : false);
}
-
- if (!data_reduction_proxy_request_options_)
- return;
+ MaybeAddBrotliToAcceptEncodingHeader(proxy_info, headers, *request);
data_reduction_proxy_request_options_->AddRequestHeader(headers);
if (lofi_decider)
@@ -330,6 +281,7 @@ void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal(
void DataReductionProxyNetworkDelegate::OnCompletedInternal(
net::URLRequest* request,
bool started) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(request);
// TODO(maksims): remove this once OnCompletedInternal() has net_error in
// arguments.
@@ -372,18 +324,15 @@ void DataReductionProxyNetworkDelegate::OnCompletedInternal(
"x-original-content-length")
: -1;
- CalculateAndRecordDataUsage(*request, request_type, original_content_length,
- net_error);
+ CalculateAndRecordDataUsage(*request, request_type);
RecordContentLength(*request, request_type, original_content_length);
}
void DataReductionProxyNetworkDelegate::CalculateAndRecordDataUsage(
const net::URLRequest& request,
- DataReductionProxyRequestType request_type,
- int64_t original_content_length,
- int net_error) {
- DCHECK_LE(-1, original_content_length);
+ DataReductionProxyRequestType request_type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
int64_t data_used = request.GetTotalReceivedBytes();
// Estimate how many bytes would have been used if the DataReductionProxy was
@@ -391,7 +340,7 @@ void DataReductionProxyNetworkDelegate::CalculateAndRecordDataUsage(
int64_t original_size = data_used;
if (request_type == VIA_DATA_REDUCTION_PROXY)
- original_size = EstimateOriginalReceivedBytes(request, net_error);
+ original_size = EstimateOriginalReceivedBytes(request);
std::string mime_type;
if (request.response_headers())
@@ -411,6 +360,7 @@ void DataReductionProxyNetworkDelegate::AccumulateDataUsage(
DataReductionProxyRequestType request_type,
const scoped_refptr<DataUseGroup>& data_use_group,
const std::string& mime_type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_GE(data_used, 0);
DCHECK_GE(original_size, 0);
if (data_reduction_proxy_io_data_) {
@@ -418,14 +368,13 @@ void DataReductionProxyNetworkDelegate::AccumulateDataUsage(
data_used, original_size, data_reduction_proxy_io_data_->IsEnabled(),
request_type, data_use_group, mime_type);
}
- total_received_bytes_ += data_used;
- total_original_received_bytes_ += original_size;
}
void DataReductionProxyNetworkDelegate::RecordContentLength(
const net::URLRequest& request,
DataReductionProxyRequestType request_type,
int64_t original_content_length) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (!request.response_headers() || request.was_cached() ||
request.received_response_content_length() == 0) {
return;
@@ -457,6 +406,7 @@ void DataReductionProxyNetworkDelegate::RecordContentLength(
void DataReductionProxyNetworkDelegate::RecordLitePageTransformationType(
LitePageTransformationType type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.LoFi.TransformationType", type,
LITE_PAGE_TRANSFORMATION_TYPES_INDEX_BOUNDARY);
}
@@ -465,6 +415,7 @@ bool DataReductionProxyNetworkDelegate::WasEligibleWithoutHoldback(
const net::URLRequest& request,
const net::ProxyInfo& proxy_info,
const net::ProxyRetryInfoMap& proxy_retry_info) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(proxy_info.is_empty() || proxy_info.is_direct() ||
!data_reduction_proxy_config_->IsDataReductionProxy(
proxy_info.proxy_server(), nullptr));
@@ -482,7 +433,61 @@ bool DataReductionProxyNetworkDelegate::WasEligibleWithoutHoldback(
void DataReductionProxyNetworkDelegate::SetDataUseGroupProvider(
std::unique_ptr<DataUseGroupProvider> data_use_group_provider) {
- data_use_group_provider_.reset(data_use_group_provider.release());
+ 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,
+ const net::URLRequest& request) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // This method should be called only when the resolved proxy was a data
+ // saver proxy.
+ DCHECK(data_reduction_proxy_config_->IsDataReductionProxy(
+ proxy_info.proxy_server(), nullptr));
+ DCHECK(request.url().is_valid());
+ DCHECK(!request.url().SchemeIsCryptographic());
+ DCHECK(request.url().SchemeIsHTTPOrHTTPS());
+
+ static const char kBrotli[] = "br";
+
+ if (!request.context()->enable_brotli()) {
+ // Verify that Brotli is enabled globally.
+ return;
+ }
+
+ if (!params::IsBrotliAcceptEncodingEnabled()) {
+ // Verify that Brotli is enabled for data reduction proxy.
+ return;
+ }
+
+ if (!proxy_info.proxy_server().is_https() &&
+ !proxy_info.proxy_server().is_quic()) {
+ // Brotli encoding can be used only when the proxy server is a secure proxy
+ // server.
+ return;
+ }
+
+ if (!request_headers->HasHeader(net::HttpRequestHeaders::kAcceptEncoding))
+ return;
+
+ std::string header_value;
+ request_headers->GetHeader(net::HttpRequestHeaders::kAcceptEncoding,
+ &header_value);
+
+ // Brotli should not be already present in the header since the URL is non-
+ // cryptographic. This is an approximate check, and would trigger even if the
+ // accept-encoding header contains an encoding that has prefix |kBrotli|.
+ DCHECK_EQ(std::string::npos, header_value.find(kBrotli));
+
+ request_headers->RemoveHeader(net::HttpRequestHeaders::kAcceptEncoding);
+ if (!header_value.empty())
+ header_value += ", ";
+ header_value += kBrotli;
+ request_headers->SetHeader(net::HttpRequestHeaders::kAcceptEncoding,
+ header_value);
}
} // namespace data_reduction_proxy
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 0372bd7d2aa..0d5799c6b71 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
@@ -13,6 +13,7 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
#include "net/base/completion_callback.h"
#include "net/base/layered_network_delegate.h"
@@ -20,18 +21,11 @@
class GURL;
-namespace base {
-class Value;
-}
-
namespace net {
-class HttpResponseHeaders;
class HttpRequestHeaders;
class NetworkDelegate;
class ProxyConfig;
class ProxyInfo;
-class ProxyServer;
-class ProxyService;
class URLRequest;
}
@@ -39,7 +33,6 @@ namespace data_reduction_proxy {
class DataReductionProxyBypassStats;
class DataReductionProxyConfig;
class DataReductionProxyConfigurator;
-class DataReductionProxyExperimentsStats;
class DataReductionProxyIOData;
class DataReductionProxyRequestOptions;
class DataUseGroupProvider;
@@ -83,13 +76,12 @@ class DataReductionProxyNetworkDelegate : public net::LayeredNetworkDelegate {
DataReductionProxyIOData* io_data,
DataReductionProxyBypassStats* bypass_stats);
- // Creates a base::Value summary of the state of the network session.
- std::unique_ptr<base::Value> SessionNetworkStatsInfoToValue() const;
-
void SetDataUseGroupProvider(
std::unique_ptr<DataUseGroupProvider> data_use_group_provider);
private:
+ friend class DataReductionProxyTestContext;
+
// Resets if Lo-Fi has been used for the last main frame load to false.
void OnBeforeURLRequestInternal(net::URLRequest* request,
const net::CompletionCallback& callback,
@@ -120,12 +112,9 @@ class DataReductionProxyNetworkDelegate : public net::LayeredNetworkDelegate {
// Calculates actual data usage that went over the network at the HTTP layer
// (e.g. not including network layer overhead) and estimates original data
- // usage for |request|. Passing in -1 for |original_content_length| indicates
- // that the original content length of the response could not be determined.
+ // usage for |request|.
void CalculateAndRecordDataUsage(const net::URLRequest& request,
- DataReductionProxyRequestType request_type,
- int64_t original_content_length,
- int net_error);
+ DataReductionProxyRequestType request_type);
// Posts to the UI thread to UpdateContentLengthPrefs in the data reduction
// proxy metrics and updates |received_content_length_| and
@@ -156,11 +145,12 @@ class DataReductionProxyNetworkDelegate : public net::LayeredNetworkDelegate {
const net::ProxyInfo& proxy_info,
const net::ProxyRetryInfoMap& proxy_retry_info) const;
- // Total size of all content that has been received over the network.
- int64_t total_received_bytes_;
-
- // Total original size of all content before it was transferred.
- int64_t total_original_received_bytes_;
+ // May add Brotli to Accept Encoding request header if |proxy_info| contains
+ // a proxy server that is expected to support Brotli encoding.
+ void MaybeAddBrotliToAcceptEncodingHeader(
+ const net::ProxyInfo& proxy_info,
+ net::HttpRequestHeaders* request_headers,
+ const net::URLRequest& request) const;
// All raw Data Reduction Proxy pointers must outlive |this|.
DataReductionProxyConfig* data_reduction_proxy_config_;
@@ -175,6 +165,8 @@ class DataReductionProxyNetworkDelegate : public net::LayeredNetworkDelegate {
std::unique_ptr<DataUseGroupProvider> data_use_group_provider_;
+ base::ThreadChecker thread_checker_;
+
DISALLOW_COPY_AND_ASSIGN(DataReductionProxyNetworkDelegate);
};
} // namespace data_reduction_proxy
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 32b576790ea..338c4f3080c 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
@@ -34,8 +34,10 @@
#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"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/data_reduction_proxy/core/common/lofi_decider.h"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "net/base/host_port_pair.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
@@ -43,14 +45,18 @@
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "net/nqe/effective_connection_type.h"
-#include "net/nqe/network_quality_estimator.h"
#include "net/nqe/network_quality_estimator_test_util.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_retry_info.h"
#include "net/proxy/proxy_server.h"
+#include "net/proxy/proxy_service.h"
#include "net/socket/socket_test_util.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/gtest_util.h"
+#include "net/test/test_data_directory.h"
#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job_factory_impl.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -172,13 +178,27 @@ class TestLoFiUIService : public LoFiUIService {
class DataReductionProxyNetworkDelegateTest : public testing::Test {
public:
DataReductionProxyNetworkDelegateTest()
- : context_(true),
- context_storage_(&context_),
- test_context_(DataReductionProxyTestContext::Builder()
- .WithClient(kClient)
- .WithMockClientSocketFactory(&mock_socket_factory_)
- .WithURLRequestContext(&context_)
- .Build()) {
+ : context_(true), context_storage_(&context_) {}
+
+ void Init(bool use_secure_proxy, bool enable_brotli_globally) {
+ net::ProxyServer proxy_server =
+ use_secure_proxy
+ ? net::ProxyServer::FromURI("https://origin.net:443",
+ net::ProxyServer::SCHEME_HTTPS)
+ : net::ProxyServer::FromURI("http://origin.net:80",
+ net::ProxyServer::SCHEME_HTTP);
+
+ proxy_service_ =
+ net::ProxyService::CreateFixedFromPacResult(proxy_server.ToPacString());
+ context_.set_proxy_service(proxy_service_.get());
+ test_context_ = (DataReductionProxyTestContext::Builder()
+ .WithClient(kClient)
+ .WithMockClientSocketFactory(&mock_socket_factory_)
+ .WithURLRequestContext(&context_)
+ .WithProxiesForHttp({DataReductionProxyServer(
+ proxy_server, ProxyServer::UNSPECIFIED_TYPE)})
+ .Build());
+
context_.set_client_socket_factory(&mock_socket_factory_);
test_context_->AttachToURLRequestContext(&context_storage_);
@@ -190,6 +210,7 @@ class DataReductionProxyNetworkDelegateTest : public testing::Test {
lofi_ui_service_ = lofi_ui_service.get();
test_context_->io_data()->set_lofi_ui_service(std::move(lofi_ui_service));
+ context_.set_enable_brotli(enable_brotli_globally);
context_.Init();
test_context_->EnableDataReductionProxyWithSecureProxyCheckSuccess();
@@ -239,6 +260,7 @@ class DataReductionProxyNetworkDelegateTest : public testing::Test {
int64_t response_content_length) {
const std::string response_body(
base::checked_cast<size_t>(response_content_length), ' ');
+
net::MockRead reads[] = {net::MockRead(response_headers.c_str()),
net::MockRead(response_body.c_str()),
net::MockRead(net::SYNCHRONOUS, net::OK)};
@@ -256,6 +278,112 @@ class DataReductionProxyNetworkDelegateTest : public testing::Test {
return request;
}
+ // Fetches a single URL request, verifies the correctness of Accept-Encoding
+ // header, and verifies that the response is cached only if |expect_cached|
+ // is set to true. Each line in |response_headers| should end with "\r\n" and
+ // not '\0', and the last line should have a second "\r\n". An empty
+ // |response_headers| is allowed. It works by making this look like an
+ // HTTP/0.9 response, since HTTP/0.9 responses don't have headers.
+ void FetchURLRequestAndVerifyBrotli(net::HttpRequestHeaders* request_headers,
+ const std::string& response_headers,
+ bool expect_cached,
+ bool expect_brotli) {
+ GURL url("http://www.example.com/a.html");
+ net::SSLSocketDataProvider ssl_socket_data_provider(net::ASYNC, net::OK);
+
+ int response_body_size = 140;
+ const std::string response_body(
+ base::checked_cast<size_t>(response_body_size), ' ');
+
+ ssl_socket_data_provider.next_proto = net::kProtoHTTP11;
+ ssl_socket_data_provider.cert = net::ImportCertFromFile(
+ net::GetTestCertsDirectory(), "unittest.selfsigned.der");
+ mock_socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data_provider);
+
+ net::MockRead reads[] = {net::MockRead(response_headers.c_str()),
+ net::MockRead(response_body.c_str()),
+ net::MockRead(net::SYNCHRONOUS, net::OK)};
+
+ if (io_data()->test_request_options()->GetHeaderValueForTesting().empty()) {
+ // Force regeneration of Chrome-Proxy header.
+ io_data()->test_request_options()->SetSecureSession("123");
+ }
+ EXPECT_FALSE(
+ io_data()->test_request_options()->GetHeaderValueForTesting().empty());
+
+ std::string prefix_headers(
+ "GET http://www.example.com/a.html HTTP/1.1\r\n"
+ "Host: www.example.com\r\n"
+ "Proxy-Connection: keep-alive\r\n"
+ "User-Agent:\r\n");
+
+ std::string accept_language_header("Accept-Language: en-us,fr\r\n");
+
+ // Brotli is included in accept-encoding header only if the request went
+ // to the network (i.e., it was not a cached response), and if data
+ // reduction ptroxy network delegate added Brotli to the header.
+ std::string accept_encoding_header =
+ expect_brotli && !expect_cached
+ ? "Accept-Encoding: gzip, deflate, br\r\n"
+ : "Accept-Encoding: gzip, deflate\r\n";
+
+ std::string suffix_headers =
+ std::string("Chrome-Proxy: ") +
+ io_data()->test_request_options()->GetHeaderValueForTesting() +
+ std::string("\r\n\r\n");
+
+ std::string mock_write = prefix_headers + accept_language_header +
+ accept_encoding_header + suffix_headers;
+
+ if (expect_cached || !expect_brotli) {
+ // Order of headers is different if the headers were modified by data
+ // reduction proxy network delegate.
+ mock_write = prefix_headers + accept_encoding_header +
+ accept_language_header + suffix_headers;
+ }
+
+ net::MockWrite writes[] = {net::MockWrite(mock_write.c_str())};
+ net::StaticSocketDataProvider socket(reads, arraysize(reads), writes,
+ arraysize(writes));
+ mock_socket_factory_.AddSocketDataProvider(&socket);
+
+ net::TestDelegate delegate;
+ std::unique_ptr<net::URLRequest> request =
+ context_.CreateRequest(url, net::IDLE, &delegate);
+ if (request_headers)
+ request->SetExtraRequestHeaders(*request_headers);
+
+ request->Start();
+ base::RunLoop().RunUntilIdle();
+
+ if (!expect_cached) {
+ EXPECT_EQ(response_body_size,
+ request->received_response_content_length());
+ EXPECT_NE(0, request->GetTotalSentBytes());
+ EXPECT_NE(0, request->GetTotalReceivedBytes());
+ EXPECT_FALSE(request->was_cached());
+ VerifyBrotliPresent(request.get(), expect_brotli);
+ } else {
+ EXPECT_TRUE(request->was_cached());
+ }
+ }
+
+ void VerifyBrotliPresent(net::URLRequest* request, bool expect_brotli) {
+ net::HttpRequestHeaders request_headers_sent;
+ EXPECT_TRUE(request->GetFullRequestHeaders(&request_headers_sent));
+ std::string accept_encoding_value;
+ EXPECT_TRUE(request_headers_sent.GetHeader("Accept-Encoding",
+ &accept_encoding_value));
+ EXPECT_NE(std::string::npos, accept_encoding_value.find("gzip"));
+ if (expect_brotli) {
+ // Brotli should be the last entry in the Accept-Encoding header.
+ EXPECT_EQ(accept_encoding_value.length() - 2,
+ accept_encoding_value.find("br"));
+ } else {
+ EXPECT_EQ(std::string::npos, accept_encoding_value.find("br"));
+ }
+ }
+
void DelegateStageDone(int result) {}
void NotifyNetworkDelegate(net::URLRequest* request,
@@ -276,14 +404,6 @@ class DataReductionProxyNetworkDelegateTest : public testing::Test {
request, data_reduction_proxy_info, proxy_retry_info, headers);
}
- int64_t total_received_bytes() const {
- return GetSessionNetworkStatsInfoInt64("session_received_content_length");
- }
-
- int64_t total_original_received_bytes() const {
- return GetSessionNetworkStatsInfoInt64("session_original_content_length");
- }
-
net::MockClientSocketFactory* mock_socket_factory() {
return &mock_socket_factory_;
}
@@ -309,25 +429,9 @@ class DataReductionProxyNetworkDelegateTest : public testing::Test {
TestLoFiDecider* lofi_decider() const { return lofi_decider_; }
private:
- int64_t GetSessionNetworkStatsInfoInt64(const char* key) const {
- const DataReductionProxyNetworkDelegate* drp_network_delegate =
- reinterpret_cast<const DataReductionProxyNetworkDelegate*>(
- context_.network_delegate());
-
- std::unique_ptr<base::DictionaryValue> session_network_stats_info =
- base::DictionaryValue::From(
- drp_network_delegate->SessionNetworkStatsInfoToValue());
- EXPECT_TRUE(session_network_stats_info);
-
- std::string string_value;
- EXPECT_TRUE(session_network_stats_info->GetString(key, &string_value));
- int64_t value = 0;
- EXPECT_TRUE(base::StringToInt64(string_value, &value));
- return value;
- }
-
base::MessageLoopForIO message_loop_;
net::MockClientSocketFactory mock_socket_factory_;
+ std::unique_ptr<net::ProxyService> proxy_service_;
net::TestURLRequestContext context_;
net::URLRequestContextStorage context_storage_;
@@ -337,6 +441,7 @@ class DataReductionProxyNetworkDelegateTest : public testing::Test {
};
TEST_F(DataReductionProxyNetworkDelegateTest, AuthenticationTest) {
+ Init(false, false);
std::unique_ptr<net::URLRequest> fake_request(FetchURLRequest(
GURL("http://www.google.com/"), nullptr, std::string(), 0));
@@ -359,6 +464,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest, AuthenticationTest) {
}
TEST_F(DataReductionProxyNetworkDelegateTest, LoFiTransitions) {
+ Init(false, false);
// Enable Lo-Fi.
const struct {
bool lofi_switch_enabled;
@@ -522,6 +628,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest, LoFiTransitions) {
}
TEST_F(DataReductionProxyNetworkDelegateTest, RequestDataConfigurations) {
+ Init(false, false);
const struct {
bool lofi_on;
bool used_data_reduction_proxy;
@@ -559,9 +666,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest, RequestDataConfigurations) {
net::HttpRequestHeaders headers;
net::ProxyRetryInfoMap proxy_retry_info;
- std::map<std::string, std::string> network_quality_estimator_params;
- net::TestNetworkQualityEstimator test_network_quality_estimator(
- network_quality_estimator_params);
+ net::TestNetworkQualityEstimator test_network_quality_estimator;
test_network_quality_estimator.set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_OFFLINE);
context()->set_network_quality_estimator(&test_network_quality_estimator);
@@ -593,6 +698,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest, RequestDataConfigurations) {
TEST_F(DataReductionProxyNetworkDelegateTest,
RequestDataHoldbackConfigurations) {
+ Init(false, false);
const struct {
bool data_reduction_proxy_enabled;
bool used_direct;
@@ -619,7 +725,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
data_reduction_proxy_info.UseDirect();
else
data_reduction_proxy_info.UseNamedProxy("some.other.proxy");
- config()->SetStateForTest(test.data_reduction_proxy_enabled, true);
+ config()->UpdateConfigForTesting(test.data_reduction_proxy_enabled, true);
std::unique_ptr<net::URLRequest> request = context()->CreateRequest(
GURL("http://www.google.com/"), net::RequestPriority::IDLE, nullptr);
request->set_method("GET");
@@ -639,6 +745,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
}
TEST_F(DataReductionProxyNetworkDelegateTest, RedirectRequestDataCleared) {
+ Init(false, false);
net::ProxyInfo data_reduction_proxy_info;
std::string data_reduction_proxy;
base::TrimString(params()->DefaultOrigin(), "/", &data_reduction_proxy);
@@ -648,9 +755,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest, RedirectRequestDataCleared) {
net::HttpRequestHeaders headers;
net::ProxyRetryInfoMap proxy_retry_info;
- std::map<std::string, std::string> network_quality_estimator_params;
- net::TestNetworkQualityEstimator test_network_quality_estimator(
- network_quality_estimator_params);
+ net::TestNetworkQualityEstimator test_network_quality_estimator;
test_network_quality_estimator.set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_OFFLINE);
context()->set_network_quality_estimator(&test_network_quality_estimator);
@@ -684,6 +789,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest, RedirectRequestDataCleared) {
}
TEST_F(DataReductionProxyNetworkDelegateTest, NetHistograms) {
+ Init(false, false);
const std::string kReceivedValidOCLHistogramName =
"Net.HttpContentLengthWithValidOCL";
const std::string kOriginalValidOCLHistogramName =
@@ -833,6 +939,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest, NetHistograms) {
}
TEST_F(DataReductionProxyNetworkDelegateTest, OnCompletedInternalLoFi) {
+ Init(false, false);
// Enable Lo-Fi.
const struct {
bool lofi_response;
@@ -861,6 +968,7 @@ TEST_F(DataReductionProxyNetworkDelegateTest, OnCompletedInternalLoFi) {
TEST_F(DataReductionProxyNetworkDelegateTest,
TestLoFiTransformationTypeHistogram) {
+ Init(false, false);
const char kLoFiTransformationTypeHistogram[] =
"DataReductionProxy.LoFi.TransformationType";
base::HistogramTester histogram_tester;
@@ -889,6 +997,93 @@ TEST_F(DataReductionProxyNetworkDelegateTest,
LITE_PAGE, 1);
}
+// Test that Brotli is not added to the accept-encoding header when it is
+// disabled globally.
+TEST_F(DataReductionProxyNetworkDelegateTest,
+ BrotliAdvertisement_BrotliDisabled) {
+ Init(true /* use_secure_proxy */, false /* enable_brotli_globally */);
+
+ std::string response_headers =
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 140\r\n"
+ "Via: 1.1 Chrome-Compression-Proxy\r\n"
+ "x-original-content-length: 200\r\n"
+ "Cache-Control: max-age=1200\r\n"
+ "Vary: accept-encoding\r\n";
+ response_headers += "\r\n";
+
+ // Use secure sockets when fetching the request since Brotli is only enabled
+ // for secure connections.
+ FetchURLRequestAndVerifyBrotli(nullptr, response_headers, false, false);
+}
+
+// Test that Brotli is not added to the accept-encoding header when the request
+// is fetched from an insecure proxy.
+TEST_F(DataReductionProxyNetworkDelegateTest,
+ BrotliAdvertisementInsecureProxy) {
+ Init(false /* use_secure_proxy */, true /* enable_brotli_globally */);
+ std::string response_headers =
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 140\r\n"
+ "Via: 1.1 Chrome-Compression-Proxy\r\n"
+ "x-original-content-length: 200\r\n"
+ "Cache-Control: max-age=1200\r\n"
+ "Vary: accept-encoding\r\n";
+ response_headers += "\r\n";
+
+ // Use secure sockets when fetching the request since Brotli is only enabled
+ // for secure connections.
+ std::unique_ptr<net::URLRequest> request(FetchURLRequest(
+ GURL("http://www.example.com/a.html"), nullptr, response_headers, 140));
+ EXPECT_EQ(140, request->received_response_content_length());
+ EXPECT_NE(0, request->GetTotalSentBytes());
+ EXPECT_NE(0, request->GetTotalReceivedBytes());
+ EXPECT_FALSE(request->was_cached());
+ // Brotli should be added to Accept Encoding header only if secure proxy is in
+ VerifyBrotliPresent(request.get(), false);
+}
+
+// Test that Brotli is not added to the accept-encoding header when it is
+// disabled via data reduction proxy field trial.
+TEST_F(DataReductionProxyNetworkDelegateTest,
+ BrotliAdvertisementDisabledViaFieldTrial) {
+ Init(true /* use_secure_proxy */, true /* enable_brotli_globally */);
+
+ base::FieldTrialList field_trial_list(nullptr);
+ ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
+ "DataReductionProxyBrotliAcceptEncoding", "Disabled"));
+
+ std::string response_headers =
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 140\r\n"
+ "Via: 1.1 Chrome-Compression-Proxy\r\n"
+ "x-original-content-length: 200\r\n"
+ "Cache-Control: max-age=1200\r\n"
+ "Vary: accept-encoding\r\n";
+ response_headers += "\r\n";
+
+ FetchURLRequestAndVerifyBrotli(nullptr, response_headers, false, false);
+ FetchURLRequestAndVerifyBrotli(nullptr, response_headers, true, false);
+}
+
+// Test that Brotli is correctly added to the accept-encoding header when it is
+// enabled globally.
+TEST_F(DataReductionProxyNetworkDelegateTest, BrotliAdvertisement) {
+ Init(true /* use_secure_proxy */, true /* enable_brotli_globally */);
+
+ std::string response_headers =
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 140\r\n"
+ "Via: 1.1 Chrome-Compression-Proxy\r\n"
+ "x-original-content-length: 200\r\n"
+ "Cache-Control: max-age=1200\r\n"
+ "Vary: accept-encoding\r\n";
+ response_headers += "\r\n";
+
+ FetchURLRequestAndVerifyBrotli(nullptr, response_headers, false, true);
+ FetchURLRequestAndVerifyBrotli(nullptr, response_headers, true, true);
+}
+
} // namespace
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc
index c2b2b447040..b3d912c464d 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc
@@ -21,6 +21,8 @@ void RegisterSyncableProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
false);
registry->RegisterInt64Pref(prefs::kDataReductionProxyLastEnabledTime, 0L);
+ registry->RegisterInt64Pref(
+ prefs::kDataReductionProxySavingsClearedNegativeSystemClock, 0);
registry->RegisterBooleanPref(prefs::kDataUsageReportingEnabled, false);
@@ -108,7 +110,8 @@ void RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(prefs::kDataReductionProxy, std::string());
registry->RegisterInt64Pref(prefs::kDataReductionProxyLastEnabledTime, 0L);
registry->RegisterInt64Pref(
- prefs::kHttpReceivedContentLength, 0);
+ prefs::kDataReductionProxySavingsClearedNegativeSystemClock, 0);
+ registry->RegisterInt64Pref(prefs::kHttpReceivedContentLength, 0);
registry->RegisterInt64Pref(
prefs::kHttpOriginalContentLength, 0);
registry->RegisterListPref(
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h
index 891e302cb97..b444851064c 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h
@@ -12,7 +12,6 @@ class PrefRegistrySyncable;
}
class PrefRegistrySimple;
-class PrefService;
namespace data_reduction_proxy {
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
index d4f6ae26af0..3b66f062cfa 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
@@ -77,17 +77,23 @@ DataReductionProxyRequestOptions::DataReductionProxyRequestOptions(
data_reduction_proxy_config_(config) {
DCHECK(data_reduction_proxy_config_);
util::GetChromiumBuildAndPatch(version, &build_, &patch_);
- // Constructed on the UI thread, but should be checked on the IO thread.
- thread_checker_.DetachFromThread();
}
DataReductionProxyRequestOptions::~DataReductionProxyRequestOptions() {
}
void DataReductionProxyRequestOptions::Init() {
+ DCHECK(thread_checker_.CalledOnValidThread());
key_ = GetDefaultKey(),
UpdateCredentials();
UpdateExperiments();
+ // Called on the UI thread, but should be checked on the IO thread.
+ thread_checker_.DetachFromThread();
+}
+
+std::string DataReductionProxyRequestOptions::GetHeaderValueForTesting() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return header_value_;
}
void DataReductionProxyRequestOptions::UpdateExperiments() {
@@ -133,16 +139,19 @@ base::string16 DataReductionProxyRequestOptions::AuthHashForSalt(
}
base::Time DataReductionProxyRequestOptions::Now() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
return base::Time::Now();
}
void DataReductionProxyRequestOptions::RandBytes(void* output,
size_t length) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
crypto::RandBytes(output, length);
}
void DataReductionProxyRequestOptions::AddRequestHeader(
net::HttpRequestHeaders* request_headers) {
+ DCHECK(thread_checker_.CalledOnValidThread());
base::Time now = Now();
// Authorization credentials must be regenerated if they are expired.
if (!use_assigned_credentials_ && (now > credentials_expiration_time_))
@@ -207,10 +216,12 @@ void DataReductionProxyRequestOptions::SetSecureSession(
}
void DataReductionProxyRequestOptions::Invalidate() {
+ DCHECK(thread_checker_.CalledOnValidThread());
SetSecureSession(std::string());
}
std::string DataReductionProxyRequestOptions::GetDefaultKey() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
std::string key =
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
index 4cf3c7c4c44..b18dee44472 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
@@ -20,11 +20,7 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h"
namespace net {
-class HostPortPair;
class HttpRequestHeaders;
-class HttpResponseHeaders;
-class ProxyServer;
-class URLRequest;
}
namespace data_reduction_proxy {
@@ -56,7 +52,7 @@ class DataReductionProxyRequestOptions {
// Sets |key_| to the default key and initializes the credentials, version,
// client, and lo-fi header values. Generates the |header_value_| string,
- // which is concatenated to the Chrome-proxy header.
+ // which is concatenated to the Chrome-proxy header. Called on the UI thread.
void Init();
// Adds a 'Chrome-Proxy' header to |request_headers| with the data reduction
@@ -102,6 +98,10 @@ class DataReductionProxyRequestOptions {
const std::string& version,
DataReductionProxyConfig* config);
+ // Returns the chrome proxy header. Protected so that it is available for
+ // testing.
+ std::string GetHeaderValueForTesting() const;
+
private:
FRIEND_TEST_ALL_PREFIXES(DataReductionProxyRequestOptionsTest,
AuthHashForSalt);
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 32cb51db7b0..40cc55ec0df 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
@@ -134,8 +134,7 @@ class DataReductionProxyRequestOptionsTest : public testing::Test {
DataReductionProxyRequestOptionsTest() {
test_context_ =
DataReductionProxyTestContext::Builder()
- .WithParamsFlags(
- DataReductionProxyParams::kAllowAllProxyConfigurations)
+ .WithParamsFlags(0)
.WithParamsDefinitions(TestDataReductionProxyParams::HAS_EVERYTHING)
.Build();
}
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 f2fab9f59f0..b1d8fc167ae 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
@@ -21,7 +21,6 @@
#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"
-class GURL;
class PrefService;
namespace base {
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 ee018ffec3b..fecd6ea13ef 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
@@ -52,7 +52,6 @@ const char kDataReductionPassThroughHeader[] =
DataReductionProxySettings::DataReductionProxySettings()
: unreachable_(false),
deferred_initialization_(false),
- allowed_(false),
promo_allowed_(false),
lo_fi_mode_active_(false),
lo_fi_load_image_requested_(false),
@@ -69,8 +68,7 @@ DataReductionProxySettings::DataReductionProxySettings()
}
DataReductionProxySettings::~DataReductionProxySettings() {
- if (allowed_)
- spdy_proxy_auth_enabled_.Destroy();
+ spdy_proxy_auth_enabled_.Destroy();
}
void DataReductionProxySettings::InitPrefMembers() {
@@ -83,7 +81,6 @@ void DataReductionProxySettings::InitPrefMembers() {
void DataReductionProxySettings::UpdateConfigValues() {
DCHECK(config_);
- allowed_ = config_->allowed();
promo_allowed_ = config_->promo_allowed();
}
@@ -144,10 +141,6 @@ bool DataReductionProxySettings::IsDataReductionProxyManaged() {
void DataReductionProxySettings::SetDataReductionProxyEnabled(bool enabled) {
DCHECK(thread_checker_.CalledOnValidThread());
- // Prevent configuring the proxy when it is not allowed to be used.
- if (!allowed_)
- return;
-
if (spdy_proxy_auth_enabled_.GetValue() != enabled) {
spdy_proxy_auth_enabled_.SetValue(enabled);
OnProxyEnabledPrefChange();
@@ -240,8 +233,6 @@ void DataReductionProxySettings::OnProxyEnabledPrefChange() {
if (!register_synthetic_field_trial_.is_null()) {
RegisterDataReductionProxyFieldTrial();
}
- if (!allowed_)
- return;
MaybeActivateDataReductionProxy(false);
}
@@ -277,6 +268,22 @@ void DataReductionProxySettings::MaybeActivateDataReductionProxy(
(clock_->Now() - base::Time::FromInternalValue(last_enabled_time))
.InDays());
}
+
+ int64_t last_savings_cleared_time = prefs->GetInt64(
+ prefs::kDataReductionProxySavingsClearedNegativeSystemClock);
+ if (last_savings_cleared_time != 0) {
+ int32_t days_since_savings_cleared =
+ (clock_->Now() -
+ base::Time::FromInternalValue(last_savings_cleared_time))
+ .InDays();
+
+ // Sample in the UMA histograms must be at least 1.
+ if (days_since_savings_cleared == 0)
+ days_since_savings_cleared = 1;
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "DataReductionProxy.DaysSinceSavingsCleared.NegativeSystemClock",
+ days_since_savings_cleared, 1, 365, 50);
+ }
}
if (spdy_proxy_auth_enabled_.GetValue() &&
@@ -314,20 +321,14 @@ DataReductionProxyEventStore* DataReductionProxySettings::GetEventStore()
}
// Metrics methods
-void DataReductionProxySettings::RecordDataReductionInit() {
+void DataReductionProxySettings::RecordDataReductionInit() const {
DCHECK(thread_checker_.CalledOnValidThread());
- ProxyStartupState state = PROXY_NOT_AVAILABLE;
- if (allowed_) {
- if (IsDataReductionProxyEnabled())
- state = PROXY_ENABLED;
- else
- state = PROXY_DISABLED;
- }
-
- RecordStartupState(state);
+ RecordStartupState(IsDataReductionProxyEnabled() ? PROXY_ENABLED
+ : PROXY_DISABLED);
}
-void DataReductionProxySettings::RecordStartupState(ProxyStartupState state) {
+void DataReductionProxySettings::RecordStartupState(
+ ProxyStartupState state) const {
UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram,
state,
PROXY_STARTUP_STATE_COUNT);
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 513a7977b66..a258151356b 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
@@ -187,11 +187,6 @@ class DataReductionProxySettings : public DataReductionProxyServiceObserver,
// InitDataReductionProxySettings has not been called.
DataReductionProxyEventStore* GetEventStore() const;
- // Returns true if the data reduction proxy configuration may be used.
- bool Allowed() const {
- return allowed_;
- }
-
// 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 {
@@ -224,12 +219,12 @@ class DataReductionProxySettings : public DataReductionProxyServiceObserver,
// Metrics method. Subclasses should override if they wish to provide
// alternatives.
- virtual void RecordDataReductionInit();
+ virtual void RecordDataReductionInit() const;
// Virtualized for mocking. Records UMA specifying whether the proxy was
// enabled or disabled at startup.
virtual void RecordStartupState(
- data_reduction_proxy::ProxyStartupState state);
+ data_reduction_proxy::ProxyStartupState state) const;
private:
friend class DataReductionProxySettingsTestBase;
@@ -270,6 +265,8 @@ class DataReductionProxySettings : public DataReductionProxyServiceObserver,
TestDaysSinceEnabledWithTestClock);
FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
TestDaysSinceEnabledExistingUser);
+ FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
+ TestDaysSinceSavingsCleared);
// Override of DataReductionProxyService::Observer.
void OnServiceInitialized() override;
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 9b5fe4bdf37..25e64a07917 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, true, true, false);
+ ResetSettings(nullptr, true, false);
ListPrefUpdate original_update(test_context_->pref_service(),
prefs::kDailyHttpOriginalContentLength);
@@ -74,15 +74,9 @@ void DataReductionProxySettingsTestBase::SetUp() {
template <class C>
void DataReductionProxySettingsTestBase::ResetSettings(
std::unique_ptr<base::Clock> clock,
- bool allowed,
- bool fallback_allowed,
bool promo_allowed,
bool holdback) {
int flags = 0;
- if (allowed)
- flags |= DataReductionProxyParams::kAllowed;
- if (fallback_allowed)
- flags |= DataReductionProxyParams::kFallbackAllowed;
if (promo_allowed)
flags |= DataReductionProxyParams::kPromoAllowed;
if (holdback)
@@ -109,8 +103,6 @@ void DataReductionProxySettingsTestBase::ResetSettings(
// Explicitly generate required instantiations.
template void DataReductionProxySettingsTestBase::ResetSettings<
DataReductionProxySettings>(std::unique_ptr<base::Clock> clock,
- bool allowed,
- bool fallback_allowed,
bool promo_allowed,
bool holdback);
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 2f156375381..987de6337db 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
@@ -21,12 +21,10 @@
#include "testing/gtest/include/gtest/gtest.h"
class PrefService;
-class TestingPrefServiceSimple;
namespace data_reduction_proxy {
class DataReductionProxyTestContext;
-class MockDataReductionProxyConfig;
template <class C>
class MockDataReductionProxySettings : public C {
@@ -35,8 +33,7 @@ class MockDataReductionProxySettings : public C {
}
MOCK_METHOD0(GetOriginalProfilePrefs, PrefService*());
MOCK_METHOD0(GetLocalStatePrefs, PrefService*());
- MOCK_METHOD1(RecordStartupState,
- void(ProxyStartupState state));
+ MOCK_CONST_METHOD1(RecordStartupState, void(ProxyStartupState state));
};
class DataReductionProxySettingsTestBase : public testing::Test {
@@ -44,9 +41,7 @@ class DataReductionProxySettingsTestBase : public testing::Test {
static void AddTestProxyToCommandLine();
DataReductionProxySettingsTestBase();
- DataReductionProxySettingsTestBase(bool allowed,
- bool fallback_allowed,
- bool promo_allowed);
+ DataReductionProxySettingsTestBase(bool promo_allowed);
~DataReductionProxySettingsTestBase() override;
void AddProxyToCommandLine();
@@ -55,13 +50,9 @@ class DataReductionProxySettingsTestBase : public testing::Test {
template <class C>
void ResetSettings(std::unique_ptr<base::Clock> clock,
- bool allowed,
- bool fallback_allowed,
bool promo_allowed,
bool holdback);
virtual void ResetSettings(std::unique_ptr<base::Clock> clock,
- bool allowed,
- bool fallback_allowed,
bool promo_allowed,
bool holdback) = 0;
@@ -99,12 +90,10 @@ class ConcreteDataReductionProxySettingsTest
public:
typedef MockDataReductionProxySettings<C> MockSettings;
void ResetSettings(std::unique_ptr<base::Clock> clock,
- bool allowed,
- bool fallback_allowed,
bool promo_allowed,
bool holdback) override {
return DataReductionProxySettingsTestBase::ResetSettings<C>(
- std::move(clock), allowed, fallback_allowed, promo_allowed, holdback);
+ std::move(clock), promo_allowed, holdback);
}
};
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 b029fd2c065..d2ef4375ad6 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
@@ -46,8 +46,8 @@ class DataReductionProxySettingsTest
bool expected_restricted,
bool expected_fallback_restricted) {
test_context_->SetDataReductionProxyEnabled(initially_enabled);
- test_context_->config()->SetStateForTest(initially_enabled,
- request_succeeded);
+ test_context_->config()->UpdateConfigForTesting(initially_enabled,
+ request_succeeded);
ExpectSetProxyPrefs(expected_enabled, false);
settings_->MaybeActivateDataReductionProxy(false);
test_context_->RunUntilIdle();
@@ -63,7 +63,7 @@ class DataReductionProxySettingsTest
TEST_F(DataReductionProxySettingsTest, TestIsProxyEnabledOrManaged) {
InitPrefMembers();
// The proxy is disabled initially.
- test_context_->config()->SetStateForTest(false, true);
+ test_context_->config()->UpdateConfigForTesting(false, true);
EXPECT_FALSE(settings_->IsDataReductionProxyEnabled());
EXPECT_FALSE(settings_->UpdateDataSavings(std::string(), 0, 0));
@@ -85,7 +85,7 @@ TEST_F(DataReductionProxySettingsTest, TestIsProxyEnabledOrManaged) {
TEST_F(DataReductionProxySettingsTest, TestCanUseDataReductionProxy) {
InitPrefMembers();
// The proxy is disabled initially.
- test_context_->config()->SetStateForTest(false, true);
+ test_context_->config()->UpdateConfigForTesting(false, true);
GURL http_gurl("http://url.com/");
EXPECT_FALSE(settings_->CanUseDataReductionProxy(http_gurl));
@@ -248,7 +248,7 @@ TEST(DataReductionProxySettingsStandaloneTest, TestOnProxyEnabledPrefChange) {
.Build();
// The proxy is enabled initially.
- drp_test_context->config()->SetStateForTest(true, true);
+ drp_test_context->config()->UpdateConfigForTesting(true, true);
drp_test_context->InitSettings();
MockDataReductionProxyService* mock_service =
@@ -651,7 +651,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), true, true, false, false);
+ ResetSettings(std::move(clock), false, false);
base::Time last_enabled_time = clock_ptr->Now();
@@ -731,6 +731,32 @@ TEST_F(DataReductionProxySettingsTest, TestDaysSinceEnabledExistingUser) {
prefs::kDataReductionProxyLastEnabledTime));
}
+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);
+
+ InitPrefMembers();
+ base::HistogramTester histogram_tester;
+ test_context_->pref_service()->SetInt64(
+ prefs::kDataReductionProxySavingsClearedNegativeSystemClock,
+ clock_ptr->Now().ToInternalValue());
+
+ settings_->data_reduction_proxy_service_->SetIOData(
+ test_context_->io_data()->GetWeakPtr());
+ test_context_->RunUntilIdle();
+
+ clock_ptr->Advance(base::TimeDelta::FromDays(100));
+
+ // Simulate Chromium startup with data reduction proxy already enabled.
+ settings_->spdy_proxy_auth_enabled_.SetValue(true);
+ settings_->MaybeActivateDataReductionProxy(true /* at_startup */);
+ test_context_->RunUntilIdle();
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.DaysSinceSavingsCleared.NegativeSystemClock", 100, 1);
+}
+
TEST_F(DataReductionProxySettingsTest, TestGetDailyContentLengths) {
ContentLengthList result =
settings_->GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength);
@@ -745,27 +771,4 @@ TEST_F(DataReductionProxySettingsTest, TestGetDailyContentLengths) {
}
}
-TEST_F(DataReductionProxySettingsTest, CheckInitMetricsWhenNotAllowed) {
- // No call to |AddProxyToCommandLine()| was made, so the proxy feature
- // should be unavailable.
- // Clear the command line. Setting flags can force the proxy to be allowed.
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
-
- ResetSettings(nullptr, false, false, false, false);
- MockSettings* settings = static_cast<MockSettings*>(settings_.get());
- EXPECT_FALSE(settings->allowed_);
- EXPECT_CALL(*settings, RecordStartupState(PROXY_NOT_AVAILABLE));
-
- settings_->InitDataReductionProxySettings(
- test_context_->GetDataReductionProxyEnabledPrefName(),
- test_context_->pref_service(), test_context_->io_data(),
- test_context_->CreateDataReductionProxyService(settings_.get()));
- settings_->SetCallbackToRegisterSyntheticFieldTrial(
- base::Bind(&DataReductionProxySettingsTestBase::
- SyntheticFieldTrialRegistrationCallback,
- base::Unretained(this)));
-
- test_context_->RunUntilIdle();
-}
-
} // 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 ae6e0fd8ed5..3b5cb25aea1 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
@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
@@ -27,6 +28,7 @@
#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"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "net/proxy/proxy_config.h"
@@ -218,13 +220,14 @@ TestDataReductionProxyIOData::TestDataReductionProxyIOData(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
std::unique_ptr<DataReductionProxyConfig> config,
std::unique_ptr<DataReductionProxyEventCreator> event_creator,
- std::unique_ptr<DataReductionProxyRequestOptions> request_options,
+ std::unique_ptr<TestDataReductionProxyRequestOptions> request_options,
std::unique_ptr<DataReductionProxyConfigurator> configurator,
net::NetLog* net_log,
bool enabled)
: DataReductionProxyIOData(),
service_set_(false),
- pingback_reporting_fraction_(0.0f) {
+ pingback_reporting_fraction_(0.0f),
+ test_request_options_(request_options.get()) {
io_task_runner_ = task_runner;
ui_task_runner_ = task_runner;
config_ = std::move(config);
@@ -284,9 +287,7 @@ DataStore::Status TestDataStore::Delete(base::StringPiece key) {
}
DataReductionProxyTestContext::Builder::Builder()
- : params_flags_(DataReductionProxyParams::kAllowed |
- DataReductionProxyParams::kFallbackAllowed |
- DataReductionProxyParams::kPromoAllowed),
+ : params_flags_(DataReductionProxyParams::kPromoAllowed),
params_definitions_(TestDataReductionProxyParams::HAS_EVERYTHING),
client_(Client::UNKNOWN),
request_context_(nullptr),
@@ -298,6 +299,8 @@ DataReductionProxyTestContext::Builder::Builder()
use_test_config_client_(false),
skip_settings_initialization_(false) {}
+DataReductionProxyTestContext::Builder::~Builder() {}
+
DataReductionProxyTestContext::Builder&
DataReductionProxyTestContext::Builder::WithParamsFlags(int params_flags) {
params_flags_ = params_flags;
@@ -368,6 +371,14 @@ DataReductionProxyTestContext::Builder::SkipSettingsInitialization() {
return *this;
}
+DataReductionProxyTestContext::Builder&
+DataReductionProxyTestContext::Builder::WithProxiesForHttp(
+ const std::vector<DataReductionProxyServer>& proxy_servers) {
+ DCHECK(!proxy_servers.empty());
+ proxy_servers_ = proxy_servers;
+ return *this;
+}
+
std::unique_ptr<DataReductionProxyTestContext>
DataReductionProxyTestContext::Builder::Build() {
// Check for invalid builder combinations.
@@ -412,6 +423,9 @@ DataReductionProxyTestContext::Builder::Build() {
test_context_flags |= USE_CONFIG_CLIENT;
std::unique_ptr<DataReductionProxyMutableConfigValues> mutable_config =
DataReductionProxyMutableConfigValues::CreateFromParams(params.get());
+ if (!proxy_servers_.empty()) {
+ mutable_config->UpdateValues(proxy_servers_);
+ }
raw_mutable_config = mutable_config.get();
config.reset(new TestDataReductionProxyConfig(
std::move(mutable_config), task_runner, net_log.get(),
@@ -422,19 +436,23 @@ DataReductionProxyTestContext::Builder::Build() {
std::move(params), task_runner, net_log.get(), configurator.get(),
event_creator.get()));
} else {
+ if (!proxy_servers_.empty()) {
+ params->SetProxiesForHttp(proxy_servers_);
+ }
config.reset(new TestDataReductionProxyConfig(
std::move(params), task_runner, net_log.get(), configurator.get(),
event_creator.get()));
}
- std::unique_ptr<DataReductionProxyRequestOptions> request_options;
+ std::unique_ptr<TestDataReductionProxyRequestOptions> request_options;
+
if (use_mock_request_options_) {
test_context_flags |= USE_MOCK_REQUEST_OPTIONS;
request_options.reset(
new MockDataReductionProxyRequestOptions(client_, config.get()));
} else {
- request_options.reset(
- new DataReductionProxyRequestOptions(client_, config.get()));
+ request_options.reset(new TestDataReductionProxyRequestOptions(
+ client_, "1.2.3.4", config.get()));
}
std::unique_ptr<DataReductionProxySettings> settings(
@@ -476,6 +494,10 @@ DataReductionProxyTestContext::Builder::Build() {
}
io_data->set_config_client(std::move(config_client));
+ io_data->set_proxy_delegate(base::WrapUnique(new DataReductionProxyDelegate(
+ io_data->config(), io_data->configurator(), io_data->event_creator(),
+ io_data->bypass_stats(), net_log.get())));
+
std::unique_ptr<DataReductionProxyTestContext> test_context(
new DataReductionProxyTestContext(
task_runner, std::move(pref_service), std::move(net_log),
@@ -593,13 +615,15 @@ DataReductionProxyTestContext::CreateDataReductionProxyServiceInternal(
}
void DataReductionProxyTestContext::AttachToURLRequestContext(
- net::URLRequestContextStorage* request_context_storage) const {
+ net::URLRequestContextStorage* request_context_storage) const {
DCHECK(request_context_storage);
// |request_context_storage| takes ownership of the network delegate.
- request_context_storage->set_network_delegate(
+ std::unique_ptr<DataReductionProxyNetworkDelegate> network_delegate =
io_data()->CreateNetworkDelegate(
- base::MakeUnique<net::TestNetworkDelegate>(), true));
+ base::MakeUnique<net::TestNetworkDelegate>(), true);
+
+ request_context_storage->set_network_delegate(std::move(network_delegate));
request_context_storage->set_job_factory(
base::MakeUnique<net::URLRequestInterceptingJobFactory>(
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 48633eb407c..e42557f3a0e 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
@@ -21,6 +21,7 @@
#include "base/time/time.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.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_reduction_proxy_service.h"
@@ -35,10 +36,6 @@
class GURL;
class TestingPrefServiceSimple;
-namespace base {
-class MessageLoopForUI;
-}
-
namespace net {
class MockClientSocketFactory;
class NetLog;
@@ -53,8 +50,8 @@ class DataReductionProxyConfigurator;
class DataReductionProxyEventCreator;
class DataReductionProxyMutableConfigValues;
class DataReductionProxyRequestOptions;
+class DataReductionProxyServer;
class DataReductionProxySettings;
-class DataReductionProxyCompressionStats;
class MockDataReductionProxyConfig;
class TestDataReductionProxyConfig;
class TestDataReductionProxyEventStorageDelegate;
@@ -76,6 +73,8 @@ class TestDataReductionProxyRequestOptions
// Time after the unix epoch that Now() reports.
void set_offset(const base::TimeDelta& now_offset);
+ using DataReductionProxyRequestOptions::GetHeaderValueForTesting;
+
private:
base::TimeDelta now_offset_;
};
@@ -191,7 +190,7 @@ class TestDataReductionProxyIOData : public DataReductionProxyIOData {
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
std::unique_ptr<DataReductionProxyConfig> config,
std::unique_ptr<DataReductionProxyEventCreator> event_creator,
- std::unique_ptr<DataReductionProxyRequestOptions> request_options,
+ std::unique_ptr<TestDataReductionProxyRequestOptions> request_options,
std::unique_ptr<DataReductionProxyConfigurator> configurator,
net::NetLog* net_log,
bool enabled);
@@ -212,6 +211,15 @@ class TestDataReductionProxyIOData : public DataReductionProxyIOData {
return config_client_.get();
}
+ TestDataReductionProxyRequestOptions* test_request_options() const {
+ return test_request_options_;
+ }
+
+ void set_proxy_delegate(
+ std::unique_ptr<DataReductionProxyDelegate> proxy_delegate) {
+ proxy_delegate_ = std::move(proxy_delegate);
+ }
+
void SetSimpleURLRequestContextGetter(
const scoped_refptr<net::URLRequestContextGetter> context_getter) {
basic_url_request_context_getter_ = context_getter;
@@ -234,6 +242,8 @@ class TestDataReductionProxyIOData : public DataReductionProxyIOData {
// Reporting fraction last set via SetPingbackReportingFraction.
float pingback_reporting_fraction_;
+
+ TestDataReductionProxyRequestOptions* test_request_options_;
};
// Test version of |DataStore|. Uses an in memory hash map to store data.
@@ -268,6 +278,8 @@ class DataReductionProxyTestContext {
public:
Builder();
+ ~Builder();
+
// |DataReductionProxyParams| flags to use.
Builder& WithParamsFlags(int params_flags);
@@ -312,6 +324,10 @@ class DataReductionProxyTestContext {
// Construct, but do not initialize the |DataReductionProxySettings| object.
Builder& SkipSettingsInitialization();
+ // Specifies the data reduction proxy servers.
+ Builder& WithProxiesForHttp(
+ const std::vector<DataReductionProxyServer>& proxy_servers);
+
// Creates a |DataReductionProxyTestContext|. Owned by the caller.
std::unique_ptr<DataReductionProxyTestContext> Build();
@@ -328,6 +344,7 @@ class DataReductionProxyTestContext {
bool use_config_client_;
bool use_test_config_client_;
bool skip_settings_initialization_;
+ std::vector<DataReductionProxyServer> proxy_servers_;
};
virtual ~DataReductionProxyTestContext();
diff --git a/chromium/components/data_reduction_proxy/core/browser/db_data_owner.h b/chromium/components/data_reduction_proxy/core/browser/db_data_owner.h
index 2160ce54ea5..a0b045f05a3 100644
--- a/chromium/components/data_reduction_proxy/core/browser/db_data_owner.h
+++ b/chromium/components/data_reduction_proxy/core/browser/db_data_owner.h
@@ -12,10 +12,6 @@
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
-namespace base {
-class FilePath;
-} // namespace base
-
namespace data_reduction_proxy {
class DataStore;
class DataUsageBucket;
diff --git a/chromium/components/data_reduction_proxy/core/common/BUILD.gn b/chromium/components/data_reduction_proxy/core/common/BUILD.gn
index d829ef253f5..ba5918c21ed 100644
--- a/chromium/components/data_reduction_proxy/core/common/BUILD.gn
+++ b/chromium/components/data_reduction_proxy/core/common/BUILD.gn
@@ -25,6 +25,8 @@ template("common_tmpl") {
"data_reduction_proxy_params.h",
"data_reduction_proxy_pref_names.cc",
"data_reduction_proxy_pref_names.h",
+ "data_reduction_proxy_server.cc",
+ "data_reduction_proxy_server.h",
"data_reduction_proxy_switches.cc",
"data_reduction_proxy_switches.h",
"data_reduction_proxy_util.cc",
@@ -32,6 +34,7 @@ template("common_tmpl") {
"data_savings_recorder.h",
"lofi_decider.h",
"lofi_ui_service.h",
+ "resource_type_provider.h",
]
public_deps = [
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 82ba6d900ad..15c45792288 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
@@ -9,13 +9,9 @@
class GURL;
-namespace net {
-class ProxyServer;
-}
-
namespace data_reduction_proxy {
-struct DataReductionProxyTypeInfo;
+class DataReductionProxyServer;
class DataReductionProxyConfigValues {
public:
@@ -30,14 +26,9 @@ class DataReductionProxyConfigValues {
// proxy if enabled.
virtual bool holdback() const = 0;
- // Returns true if the data reduction proxy configuration may be used.
- virtual bool allowed() const = 0;
-
- // Returns true if the fallback proxy may be used.
- virtual bool fallback_allowed() const = 0;
-
// Returns the HTTP proxy servers to be used.
- virtual const std::vector<net::ProxyServer>& proxies_for_http() const = 0;
+ 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.
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h
index 9ac949af126..f85a7d0cee6 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h
@@ -21,7 +21,6 @@ class GURL;
namespace base {
class TimeDelta;
-class Value;
}
namespace net {
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 df8c57c6b6f..8f7db517adb 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
@@ -5,19 +5,16 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
#include <stddef.h>
-#include <stdint.h>
+
#include <utility>
-#include <vector>
#include "base/json/json_writer.h"
-#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "base/values.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h"
namespace {
@@ -60,7 +57,7 @@ std::string JoinListValueStrings(base::ListValue* list_value) {
if (!value->GetAsString(&value_string))
return std::string();
- values.push_back(value_string);
+ values.push_back(std::move(value_string));
}
return base::JoinString(values, ";");
@@ -93,10 +90,10 @@ void DataReductionProxyEventStore::AddConstants(
}
DataReductionProxyEventStore::DataReductionProxyEventStore()
- : enabled_(false),
+ : oldest_event_index_(0),
+ enabled_(false),
secure_proxy_check_state_(CHECK_UNKNOWN),
- expiration_ticks_(0) {
-}
+ expiration_ticks_(0) {}
DataReductionProxyEventStore::~DataReductionProxyEventStore() {
}
@@ -137,17 +134,29 @@ DataReductionProxyEventStore::GetSummaryValue() const {
}
auto events_list = base::MakeUnique<base::ListValue>();
- for (const auto& event : stored_events_)
- events_list->Append(event->CreateDeepCopy());
+
+ DCHECK(oldest_event_index_ == 0 ||
+ stored_events_.size() == kMaxEventsToStore);
+ for (size_t i = oldest_event_index_; i < stored_events_.size(); ++i)
+ events_list->Append(stored_events_[i]->CreateDeepCopy());
+ for (size_t i = 0; i < oldest_event_index_; ++i)
+ events_list->Append(stored_events_[i]->CreateDeepCopy());
+
data_reduction_proxy_values->Set("events", std::move(events_list));
return data_reduction_proxy_values;
}
void DataReductionProxyEventStore::AddEvent(
std::unique_ptr<base::Value> event) {
- if (stored_events_.size() == kMaxEventsToStore)
- stored_events_.pop_front();
- stored_events_.push_back(std::move(event));
+ if (stored_events_.size() < kMaxEventsToStore) {
+ stored_events_.push_back(std::move(event));
+ return;
+ }
+ DCHECK_EQ(kMaxEventsToStore, stored_events_.size());
+
+ stored_events_[oldest_event_index_++] = std::move(event);
+ if (oldest_event_index_ >= stored_events_.size())
+ oldest_event_index_ = 0;
}
void DataReductionProxyEventStore::AddEnabledEvent(
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h
index 1523fac1a83..28584e0f14d 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h
@@ -7,9 +7,9 @@
#include <stdint.h>
-#include <deque>
#include <memory>
#include <string>
+#include <vector>
#include "base/macros.h"
#include "base/threading/thread_checker.h"
@@ -18,7 +18,6 @@
namespace base {
class DictionaryValue;
-class TimeDelta;
class Value;
}
@@ -31,15 +30,15 @@ class DataReductionProxyEventStore
// constants dictionary.
static void AddConstants(base::DictionaryValue* constants_dict);
- // Constructs a DataReductionProxyEventStore object
+ // Constructs a DataReductionProxyEventStore object.
DataReductionProxyEventStore();
virtual ~DataReductionProxyEventStore();
// Creates a Value summary of Data Reduction Proxy related information:
- // - Whether the proxy is enabled
- // - The proxy configuration
- // - The state of the last secure proxy check response
+ // - Whether the proxy is enabled,
+ // - The proxy configuration,
+ // - The state of the last secure proxy check response,
// - A stream of the last Data Reduction Proxy related events.
std::unique_ptr<base::DictionaryValue> GetSummaryValue() const;
@@ -47,19 +46,18 @@ class DataReductionProxyEventStore
void AddEvent(std::unique_ptr<base::Value> event) override;
// Override of DataReductionProxyEventStorageDelegate.
- // Put |entry| on the deque of stored events and set |current_configuration_|.
+ // Adds |entry| to the event store and sets |current_configuration_|.
void AddEnabledEvent(std::unique_ptr<base::Value> entry,
bool enabled) override;
// Override of DataReductionProxyEventStorageDelegate.
- // Put |entry| on a deque of events to store and set
- // |secure_proxy_check_state_|
+ // Adds |entry| to the event store and sets |secure_proxy_check_state_|.
void AddEventAndSecureProxyCheckState(std::unique_ptr<base::Value> entry,
SecureProxyCheckState state) override;
// Override of DataReductionProxyEventStorageDelegate.
- // Put |entry| on a deque of events to store and set |last_bypass_event_| and
- // |expiration_ticks_|
+ // Adds |entry| to the event store and sets |last_bypass_event_| and
+ // |expiration_ticks_|.
void AddAndSetLastBypassEvent(std::unique_ptr<base::Value> entry,
int64_t expiration_ticks) override;
@@ -72,9 +70,12 @@ class DataReductionProxyEventStore
private:
friend class DataReductionProxyEventStoreTest;
- // A deque of data reduction proxy related events. It is used as a circular
+ // A vector of data reduction proxy related events. It is used as a circular
// buffer to prevent unbounded memory utilization.
- std::deque<std::unique_ptr<base::Value>> stored_events_;
+ std::vector<std::unique_ptr<base::Value>> stored_events_;
+ // The index of the oldest event in |stored_events_|.
+ size_t oldest_event_index_;
+
// Whether the data reduction proxy is enabled or not.
bool enabled_;
// The current data reduction proxy configuration.
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 bbda9ae4730..e86cb5af22a 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
@@ -12,8 +12,6 @@
#include "base/time/time.h"
#include "net/proxy/proxy_service.h"
-class GURL;
-
namespace net {
class HttpResponseHeaders;
@@ -22,8 +20,6 @@ class HttpResponseHeaders;
namespace data_reduction_proxy {
-class DataReductionProxyEventCreator;
-
// 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
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 7392f2364e7..8b9433ca3c2 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,6 +13,7 @@
#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_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"
@@ -33,7 +34,7 @@ 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://www.gstatic.com/generate_204";
+const char kDefaultWarmupUrl[] = "http://check.googlezip.net/generate_204";
const char kAndroidOneIdentifier[] = "sprout";
@@ -138,6 +139,29 @@ bool IsIncludedInTamperDetectionExperiment() {
"TamperDetection_Enabled", base::CompareCase::SENSITIVE);
}
+bool FetchWarmupURLEnabled() {
+ // Fetching of the warmup URL can be enabled only for Enabled* and Control*
+ // groups.
+ if (!base::StartsWith(FieldTrialList::FindFullName(kQuicFieldTrial), kEnabled,
+ base::CompareCase::SENSITIVE) &&
+ !base::StartsWith(FieldTrialList::FindFullName(kQuicFieldTrial), kControl,
+ base::CompareCase::SENSITIVE)) {
+ return false;
+ }
+
+ std::map<std::string, std::string> params;
+ variations::GetVariationParams(GetQuicFieldTrialName(), &params);
+ return GetStringValueForVariationParamWithDefaultValue(
+ params, "enable_warmup", "false") == "true";
+}
+
+GURL GetWarmupURL() {
+ std::map<std::string, std::string> params;
+ variations::GetVariationParams(GetQuicFieldTrialName(), &params);
+ return GURL(GetStringValueForVariationParamWithDefaultValue(
+ params, "warmup_url", kDefaultWarmupUrl));
+}
+
bool IsLoFiOnViaFlags() {
return IsLoFiAlwaysOnViaFlags() || IsLoFiCellularOnlyViaFlags() ||
IsLoFiSlowConnectionsOnlyViaFlags();
@@ -207,6 +231,16 @@ bool IsZeroRttQuicEnabled() {
params, "enable_zero_rtt", "false") == "true";
}
+bool IsBrotliAcceptEncodingEnabled() {
+ // Brotli encoding is enabled by default since the data reduction proxy server
+ // controls when to serve Brotli encoded content. It can be disabled in
+ // Chromium only if Chromium belongs to a field trial group whose name starts
+ // with "Disabled".
+ return !base::StartsWith(base::FieldTrialList::FindFullName(
+ "DataReductionProxyBrotliAcceptEncoding"),
+ kDisabled, base::CompareCase::SENSITIVE);
+}
+
bool IsConfigClientEnabled() {
// Config client is enabled by default. It can be disabled only if Chromium
// belongs to a field trial group whose name starts with "Disabled".
@@ -260,19 +294,6 @@ bool ShouldForceEnableDataReductionProxy() {
data_reduction_proxy::switches::kEnableDataReductionProxy);
}
-bool ShouldUseSecureProxyByDefault() {
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- data_reduction_proxy::switches::
- kDataReductionProxyStartSecureDisabled))
- return false;
-
- if (FieldTrialList::FindFullName("DataReductionProxySecureProxyAfterCheck") ==
- kEnabled)
- return false;
-
- return true;
-}
-
int GetFieldTrialParameterAsInteger(const std::string& group,
const std::string& param_name,
int default_value,
@@ -290,7 +311,7 @@ int GetFieldTrialParameterAsInteger(const std::string& group,
}
bool GetOverrideProxiesForHttpFromCommandLine(
- std::vector<net::ProxyServer>* override_proxies_for_http) {
+ std::vector<DataReductionProxyServer>* override_proxies_for_http) {
DCHECK(override_proxies_for_http);
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDataReductionProxyHttpProxies)) {
@@ -310,8 +331,11 @@ bool GetOverrideProxiesForHttpFromCommandLine(
std::vector<std::string> proxy_override_values = base::SplitString(
proxy_overrides, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (const std::string& proxy_override : proxy_override_values) {
- override_proxies_for_http->push_back(net::ProxyServer::FromURI(
- proxy_override, net::ProxyServer::SCHEME_HTTP));
+ // Overriding proxies have type UNSPECIFIED_TYPE.
+ override_proxies_for_http->push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI(proxy_override,
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
}
return true;
@@ -328,13 +352,17 @@ bool GetOverrideProxiesForHttpFromCommandLine(
return false;
override_proxies_for_http->clear();
+ // Overriding proxies have type UNSPECIFIED_TYPE.
if (!origin.empty()) {
- override_proxies_for_http->push_back(
- net::ProxyServer::FromURI(origin, net::ProxyServer::SCHEME_HTTP));
+ override_proxies_for_http->push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI(origin, net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
}
if (!fallback_origin.empty()) {
- override_proxies_for_http->push_back(net::ProxyServer::FromURI(
- fallback_origin, net::ProxyServer::SCHEME_HTTP));
+ override_proxies_for_http->push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI(fallback_origin,
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
}
return true;
@@ -360,58 +388,47 @@ DataReductionProxyParams::~DataReductionProxyParams() {}
DataReductionProxyParams::DataReductionProxyParams(int flags,
bool should_call_init)
- : allowed_((flags & kAllowed) == kAllowed),
- fallback_allowed_((flags & kFallbackAllowed) == kFallbackAllowed),
- promo_allowed_((flags & kPromoAllowed) == kPromoAllowed),
+ : promo_allowed_((flags & kPromoAllowed) == kPromoAllowed),
holdback_((flags & kHoldback) == kHoldback),
- configured_on_command_line_(false),
use_override_proxies_for_http_(false) {
if (should_call_init) {
- bool result = Init(allowed_, fallback_allowed_);
+ bool result = Init();
DCHECK(result);
}
}
-bool DataReductionProxyParams::Init(bool allowed, bool fallback_allowed) {
+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 (allowed) {
- if (!origin_.is_valid()) {
- DVLOG(1) << "Invalid data reduction proxy origin: " << origin_.ToURI();
- return false;
- }
- }
-
- if (allowed && fallback_allowed) {
- if (!fallback_origin_.is_valid()) {
- DVLOG(1) << "Invalid data reduction proxy fallback origin: "
- << fallback_origin_.ToURI();
- return false;
- }
- }
-
- if (allowed && !secure_proxy_check_url_.is_valid()) {
- DVLOG(1) << "Invalid secure proxy check url: <null>";
+ if (!origin_.is_valid()) {
+ DVLOG(1) << "Invalid data reduction proxy origin: " << origin_.ToURI();
return false;
}
- if (fallback_allowed_ && !allowed_) {
- DVLOG(1) << "The data reduction proxy fallback cannot be allowed if "
- << "the data reduction proxy is not allowed";
+ if (!fallback_origin_.is_valid()) {
+ DVLOG(1) << "Invalid data reduction proxy fallback origin: "
+ << fallback_origin_.ToURI();
return false;
}
- if (promo_allowed_ && !allowed_) {
- DVLOG(1) << "The data reduction proxy promo cannot be allowed if the "
- << "data reduction proxy is not allowed";
+
+ 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());
+
use_override_proxies_for_http_ =
params::GetOverrideProxiesForHttpFromCommandLine(
- &override_proxies_for_http_);
+ &override_data_reduction_proxy_servers_);
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
@@ -419,14 +436,6 @@ void DataReductionProxyParams::InitWithoutChecks() {
origin = command_line.GetSwitchValueASCII(switches::kDataReductionProxy);
std::string fallback_origin =
command_line.GetSwitchValueASCII(switches::kDataReductionProxyFallback);
-
- configured_on_command_line_ = !(origin.empty() && fallback_origin.empty());
-
- // Configuring the proxy on the command line overrides the values of
- // |allowed_|.
- if (configured_on_command_line_)
- allowed_ = true;
-
std::string secure_proxy_check_url = command_line.GetSwitchValueASCII(
switches::kDataReductionProxySecureProxyCheckURL);
std::string warmup_url = command_line.GetSwitchValueASCII(
@@ -440,25 +449,28 @@ void DataReductionProxyParams::InitWithoutChecks() {
fallback_origin = GetDefaultFallbackOrigin();
if (secure_proxy_check_url.empty())
secure_proxy_check_url = GetDefaultSecureProxyCheckURL();
- if (warmup_url.empty())
- warmup_url = GetDefaultWarmupURL();
origin_ = net::ProxyServer::FromURI(origin, net::ProxyServer::SCHEME_HTTP);
fallback_origin_ =
net::ProxyServer::FromURI(fallback_origin, net::ProxyServer::SCHEME_HTTP);
- if (origin_.is_valid())
- proxies_for_http_.push_back(origin_);
- if (fallback_allowed_ && fallback_origin_.is_valid())
- proxies_for_http_.push_back(fallback_origin_);
+ if (origin_.is_valid()) {
+ // |origin_| is the core proxy server.
+ proxies_for_http_.push_back(
+ DataReductionProxyServer(origin_, ProxyServer::CORE));
+ }
+ if (fallback_origin_.is_valid()) {
+ // |fallback| is also a core proxy server.
+ proxies_for_http_.push_back(
+ DataReductionProxyServer(fallback_origin_, ProxyServer::CORE));
+ }
secure_proxy_check_url_ = GURL(secure_proxy_check_url);
- warmup_url_ = GURL(warmup_url);
}
-const std::vector<net::ProxyServer>&
+const std::vector<DataReductionProxyServer>
DataReductionProxyParams::proxies_for_http() const {
if (use_override_proxies_for_http_)
- return override_proxies_for_http_;
+ return override_data_reduction_proxy_servers_;
return proxies_for_http_;
}
@@ -468,16 +480,6 @@ const GURL& DataReductionProxyParams::secure_proxy_check_url() const {
return secure_proxy_check_url_;
}
-// Returns true if the data reduction proxy configuration may be used.
-bool DataReductionProxyParams::allowed() const {
- return allowed_;
-}
-
-// Returns true if the fallback proxy may be used.
-bool DataReductionProxyParams::fallback_allowed() const {
- return fallback_allowed_;
-}
-
// 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.
@@ -508,8 +510,5 @@ std::string DataReductionProxyParams::GetDefaultSecureProxyCheckURL() const {
return kDefaultSecureProxyCheckUrl;
}
-std::string DataReductionProxyParams::GetDefaultWarmupURL() const {
- return kDefaultWarmupUrl;
-}
} // 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 c48ec854ec9..f7bdf3b8ae3 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
@@ -14,17 +14,14 @@
#include "net/proxy/proxy_server.h"
#include "url/gurl.h"
-namespace base {
-class TimeDelta;
-}
-
namespace net {
-class HostPortPair;
class ProxyServer;
}
namespace data_reduction_proxy {
+class DataReductionProxyServer;
+
// The data_reduction_proxy::params namespace is a collection of methods to
// determine the operating parameters of the Data Reduction Proxy as specified
// by field trials and command line switches.
@@ -121,6 +118,9 @@ const char* GetQuicFieldTrialName();
// Returns true if zero RTT for QUIC is enabled.
bool IsZeroRttQuicEnabled();
+// Returns true if Brotli should be added to the accept-encoding header.
+bool IsBrotliAcceptEncodingEnabled();
+
// Returns true if the Data Reduction Proxy config client should be used.
bool IsConfigClientEnabled();
@@ -136,10 +136,6 @@ GURL GetConfigServiceURL();
// command line.
bool ShouldForceEnableDataReductionProxy();
-// Returns true if the secure Data Reduction Proxy should be used until the
-// secure proxy check fails.
-bool ShouldUseSecureProxyByDefault();
-
// Retrieves the int stored in |param_name| from the field trial group
// |group|. If the value is not present, cannot be parsed, or is less than
// |min_value|, returns |default_value|.
@@ -152,11 +148,17 @@ int GetFieldTrialParameterAsInteger(const std::string& group,
// has been overridden on the command line, and if so, returns the override
// proxy list in |override_proxies_for_http|.
bool GetOverrideProxiesForHttpFromCommandLine(
- std::vector<net::ProxyServer>* override_proxies_for_http);
+ std::vector<DataReductionProxyServer>* override_proxies_for_http);
// Returns the name of the server side experiment field trial.
const char* GetServerExperimentsFieldTrialName();
+// Returns true if fetching of the warmup URL is enabled.
+bool FetchWarmupURLEnabled();
+
+// Returns the warmup URL.
+GURL GetWarmupURL();
+
} // namespace params
// Contains information about a given proxy server. |proxies_for_http| contains
@@ -177,37 +179,28 @@ struct DataReductionProxyTypeInfo {
// Reduction Proxy.
class DataReductionProxyParams : public DataReductionProxyConfigValues {
public:
- // Flags used during construction that specify if the data reduction proxy
- // is allowed to be used, if the fallback proxy is allowed to be used, if the
- // promotion is allowed to be shown, and if this instance is part of a
- // holdback experiment.
- static const unsigned int kAllowed = (1 << 0);
- static const unsigned int kFallbackAllowed = (1 << 1);
- static const unsigned int kAllowAllProxyConfigurations =
- kAllowed | kFallbackAllowed;
+ // 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 |kAllowed|, then the standard
- // data reduction proxy configuration is allowed to be used. If
- // |kfallbackAllowed| a fallback proxy can be used if the primary proxy is
- // bypassed or disabled. Finally if |kPromoAllowed|, the client may show a
- // promotion for the data reduction proxy.
+ // 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);
+ // Updates |proxies_for_http_|.
+ void SetProxiesForHttpForTesting(
+ const std::vector<DataReductionProxyServer>& proxies_for_http);
+
~DataReductionProxyParams() override;
- const std::vector<net::ProxyServer>& proxies_for_http() const override;
+ const std::vector<DataReductionProxyServer> proxies_for_http() const override;
const GURL& secure_proxy_check_url() const override;
- bool allowed() const override;
-
- bool fallback_allowed() const override;
-
bool promo_allowed() const override;
bool holdback() const override;
@@ -220,7 +213,7 @@ class DataReductionProxyParams : public DataReductionProxyConfigValues {
// 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(bool allowed, bool fallback_allowed);
+ bool Init();
// Initialize the values of the proxies, and secure proxy check URL from
// command line flags and preprocessor constants.
@@ -231,26 +224,20 @@ class DataReductionProxyParams : public DataReductionProxyConfigValues {
virtual std::string GetDefaultOrigin() const;
virtual std::string GetDefaultFallbackOrigin() const;
virtual std::string GetDefaultSecureProxyCheckURL() const;
- virtual std::string GetDefaultWarmupURL() const;
-
- std::vector<net::ProxyServer> proxies_for_http_;
private:
+ std::vector<DataReductionProxyServer> proxies_for_http_;
+
net::ProxyServer origin_;
net::ProxyServer fallback_origin_;
GURL secure_proxy_check_url_;
- GURL warmup_url_;
- bool allowed_;
- bool fallback_allowed_;
bool promo_allowed_;
bool holdback_;
- bool configured_on_command_line_;
-
bool use_override_proxies_for_http_;
- std::vector<net::ProxyServer> override_proxies_for_http_;
+ std::vector<DataReductionProxyServer> override_data_reduction_proxy_servers_;
DISALLOW_COPY_AND_ASSIGN(DataReductionProxyParams);
};
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 d8e7a08de29..3a68f3ab5d1 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
@@ -20,8 +20,7 @@ TestDataReductionProxyParams::TestDataReductionProxyParams(
int flags, unsigned int has_definitions)
: DataReductionProxyParams(flags, false),
has_definitions_(has_definitions) {
- init_result_ = Init(flags & DataReductionProxyParams::kAllowed,
- flags & DataReductionProxyParams::kFallbackAllowed);
+ init_result_ = Init();
}
bool TestDataReductionProxyParams::init_result() const {
@@ -29,8 +28,8 @@ bool TestDataReductionProxyParams::init_result() const {
}
void TestDataReductionProxyParams::SetProxiesForHttp(
- const std::vector<net::ProxyServer>& proxies) {
- proxies_for_http_ = proxies;
+ const std::vector<DataReductionProxyServer>& proxies) {
+ SetProxiesForHttpForTesting(proxies);
}
// Test values to replace the values specified in preprocessor defines.
std::string TestDataReductionProxyParams::DefaultOrigin() {
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 bcaea2e189e..f779e291a6d 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
@@ -9,18 +9,10 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
-namespace base {
-class TimeDelta;
-}
-
-namespace net {
-class ProxyConfig;
-class ProxyServer;
-class URLRequest;
-}
-
namespace data_reduction_proxy {
+class DataReductionProxyServer;
+
class TestDataReductionProxyParams : public DataReductionProxyParams {
public:
// Used to emulate having constants defined by the preprocessor.
@@ -36,7 +28,7 @@ class TestDataReductionProxyParams : public DataReductionProxyParams {
unsigned int has_definitions);
bool init_result() const;
- void SetProxiesForHttp(const std::vector<net::ProxyServer>& proxies);
+ void SetProxiesForHttp(const std::vector<DataReductionProxyServer>& proxies);
// Test values to replace the values specified in preprocessor defines.
static std::string DefaultOrigin();
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 412e09555eb..0d10608ef48 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
@@ -14,7 +14,9 @@
#include "base/macros.h"
#include "base/metrics/field_trial.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"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "components/variations/variations_associated_data.h"
#include "net/proxy/proxy_server.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -25,31 +27,28 @@ class DataReductionProxyParamsTest : public testing::Test {
public:
void CheckParams(const TestDataReductionProxyParams& params,
bool expected_init_result,
- bool expected_allowed,
- bool expected_fallback_allowed,
bool expected_promo_allowed) {
EXPECT_EQ(expected_init_result, params.init_result());
- EXPECT_EQ(expected_allowed, params.allowed());
- EXPECT_EQ(expected_fallback_allowed, params.fallback_allowed());
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) {
- std::vector<net::ProxyServer> proxies_for_http;
+ std::vector<net::ProxyServer> expected_proxies;
if (!expected_origin.empty()) {
- proxies_for_http.push_back(net::ProxyServer::FromURI(
+ expected_proxies.push_back(net::ProxyServer::FromURI(
expected_origin, net::ProxyServer::SCHEME_HTTP));
}
if (!expected_fallback_origin.empty()) {
- proxies_for_http.push_back(net::ProxyServer::FromURI(
+ expected_proxies.push_back(net::ProxyServer::FromURI(
expected_fallback_origin, net::ProxyServer::SCHEME_HTTP));
}
- EXPECT_THAT(proxies_for_http,
- testing::ContainerEq(params.proxies_for_http()));
+ EXPECT_EQ(expected_proxies,
+ DataReductionProxyServer::ConvertToNetProxyServers(
+ params.proxies_for_http()));
EXPECT_EQ(GURL(expected_secure_proxy_check_url),
params.secure_proxy_check_url());
}
@@ -57,14 +56,25 @@ class DataReductionProxyParamsTest : public testing::Test {
TEST_F(DataReductionProxyParamsTest, EverythingDefined) {
TestDataReductionProxyParams params(
- DataReductionProxyParams::kAllowed |
- DataReductionProxyParams::kFallbackAllowed |
DataReductionProxyParams::kPromoAllowed,
TestDataReductionProxyParams::HAS_EVERYTHING);
- CheckParams(params, true, true, true, true);
- CheckValues(params, TestDataReductionProxyParams::DefaultOrigin(),
- TestDataReductionProxyParams::DefaultFallbackOrigin(),
- TestDataReductionProxyParams::DefaultSecureProxyCheckURL());
+ CheckParams(params, true, true);
+ 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::SCHEME_HTTP),
+ ProxyServer::CORE));
+ expected_proxies.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI(
+ TestDataReductionProxyParams::DefaultFallbackOrigin(),
+ 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) {
@@ -78,11 +88,9 @@ TEST_F(DataReductionProxyParamsTest, Flags) {
switches::kDataReductionProxySecureProxyCheckURL,
TestDataReductionProxyParams::FlagSecureProxyCheckURL());
TestDataReductionProxyParams params(
- DataReductionProxyParams::kAllowed |
- DataReductionProxyParams::kFallbackAllowed |
DataReductionProxyParams::kPromoAllowed,
TestDataReductionProxyParams::HAS_EVERYTHING);
- CheckParams(params, true, true, true, true);
+ CheckParams(params, true, true);
CheckValues(params, TestDataReductionProxyParams::FlagOrigin(),
TestDataReductionProxyParams::FlagFallbackOrigin(),
TestDataReductionProxyParams::FlagSecureProxyCheckURL());
@@ -91,52 +99,37 @@ TEST_F(DataReductionProxyParamsTest, Flags) {
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(DataReductionProxyParams::kAllowed);
- std::vector<net::ProxyServer> proxies_for_http;
- proxies_for_http.push_back(net::ProxyServer::FromURI(
- kCarrierTestOrigin, net::ProxyServer::SCHEME_HTTP));
- EXPECT_THAT(params.proxies_for_http(),
- testing::ContainerEq(proxies_for_http));
+ 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 allowed;
- bool fallback_allowed;
bool promo_allowed;
unsigned int missing_definitions;
bool expected_result;
} tests[] = {
- {true, true, true, TestDataReductionProxyParams::HAS_NOTHING, true},
- {true, false, true, TestDataReductionProxyParams::HAS_NOTHING, true},
- {false, true, true, TestDataReductionProxyParams::HAS_NOTHING, false},
- {true, true, true, TestDataReductionProxyParams::HAS_ORIGIN, false},
- {true, false, true, TestDataReductionProxyParams::HAS_ORIGIN, false},
- {false, true, true, TestDataReductionProxyParams::HAS_ORIGIN, false},
- {true, true, true, TestDataReductionProxyParams::HAS_NOTHING, true},
- {true, false, true, TestDataReductionProxyParams::HAS_FALLBACK_ORIGIN,
- true},
- {false, true, true, TestDataReductionProxyParams::HAS_FALLBACK_ORIGIN,
- false},
- {true, true, true, TestDataReductionProxyParams::HAS_FALLBACK_ORIGIN,
- false},
- {true, true, true,
- TestDataReductionProxyParams::HAS_SECURE_PROXY_CHECK_URL, false},
- {true, false, true,
- TestDataReductionProxyParams::HAS_SECURE_PROXY_CHECK_URL, false},
- {false, true, true,
- TestDataReductionProxyParams::HAS_SECURE_PROXY_CHECK_URL, false},
+ {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].allowed)
- flags |= DataReductionProxyParams::kAllowed;
- if (tests[i].fallback_allowed)
- flags |= DataReductionProxyParams::kFallbackAllowed;
if (tests[i].promo_allowed)
flags |= DataReductionProxyParams::kPromoAllowed;
TestDataReductionProxyParams params(
@@ -193,6 +186,46 @@ TEST_F(DataReductionProxyParamsTest, IsClientConfigEnabled) {
}
}
+TEST_F(DataReductionProxyParamsTest, IsBrotliAcceptEncodingEnabled) {
+ const struct {
+ std::string test_case;
+ std::string trial_group_value;
+ bool expected;
+ } tests[] = {
+ {
+ "Nothing set", "", true,
+ },
+ {
+ "Enabled in experiment", "Enabled", true,
+ },
+ {
+ "Alternate enabled in experiment", "Enabled_Other", true,
+ },
+ {
+ "Control in experiment", "Control", true,
+ },
+ {
+ "Disabled in experiment", "Disabled", false,
+ },
+ {
+ "Disabled in experiment", "Disabled_Other", false,
+ },
+ {
+ "disabled in experiment lower case", "disabled", true,
+ },
+ };
+
+ for (const auto& test : tests) {
+ base::FieldTrialList field_trial_list(nullptr);
+ if (!test.trial_group_value.empty()) {
+ ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
+ "DataReductionProxyBrotliAcceptEncoding", test.trial_group_value));
+ }
+ EXPECT_EQ(test.expected, params::IsBrotliAcceptEncodingEnabled())
+ << test.test_case;
+ }
+}
+
TEST_F(DataReductionProxyParamsTest, AreServerExperimentsEnabled) {
const struct {
std::string test_case;
@@ -277,57 +310,6 @@ TEST_F(DataReductionProxyParamsTest, IsTamperDetectionEnabled) {
}
}
-TEST_F(DataReductionProxyParamsTest, SecureProxyCheckDefault) {
- struct {
- bool command_line_set;
- bool experiment_enabled;
- bool in_trial_group;
- bool expected_use_by_default;
- } test_cases[]{
- {
- false, false, false, true,
- },
- {
- true, false, false, false,
- },
- {
- true, true, false, false,
- },
- {
- true, true, true, false,
- },
- {
- false, true, true, false,
- },
- {
- false, true, false, true,
- },
- };
-
- int test_index = 0;
- for (const auto& test_case : test_cases) {
- // Reset all flags.
- base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
-
- base::FieldTrialList trial_list(nullptr);
- if (test_case.command_line_set) {
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kDataReductionProxyStartSecureDisabled, "");
- }
-
- if (test_case.experiment_enabled) {
- ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
- "DataReductionProxySecureProxyAfterCheck",
- test_case.in_trial_group ? "Enabled" : "Disabled"));
- }
-
- EXPECT_EQ(test_case.expected_use_by_default,
- params::ShouldUseSecureProxyByDefault())
- << test_index;
- test_index++;
- }
-}
-
// Tests if Lo-Fi field trial is set correctly.
TEST_F(DataReductionProxyParamsTest, LoFiEnabledFieldTrial) {
const struct {
@@ -428,20 +410,31 @@ TEST_F(DataReductionProxyParamsTest, QuicFieldTrial) {
bool expected_enabled;
std::string zero_rtt_param;
bool expected_zero_rtt;
+ bool enable_warmup_url;
+ bool expect_warmup_url_enabled;
+ std::string warmup_url;
} tests[] = {
- {"Enabled", true, "true", true},
- {"Enabled_Control", true, "true", true},
- {"Enabled_Control", true, "false", false},
- {"Enabled_Control", true, std::string(), false},
- {"Control", false, "true", false},
- {"Disabled", false, "false", false},
- {"enabled", false, "false", false},
+ {"Enabled", true, "true", true, true, true, std::string()},
+ {"Enabled", true, "true", true, false, false, std::string()},
+ {"Enabled_Control", true, "true", true, true, true, std::string()},
+ {"Enabled_Control", true, "false", false, true, true, std::string()},
+ {"Enabled_Control", true, std::string(), false, true, true,
+ std::string()},
+ {"Control", false, "true", false, true, true, std::string()},
+ {"Disabled", false, "false", false, true, false, std::string()},
+ {"enabled", false, "false", false, true, false, std::string()},
+ {"Enabled", true, "true", true, true, true, "example.com/test.html"},
};
for (const auto& test : tests) {
variations::testing::ClearAllVariationParams();
std::map<std::string, std::string> variation_params;
variation_params["enable_zero_rtt"] = test.zero_rtt_param;
+ if (test.enable_warmup_url)
+ variation_params["enable_warmup"] = "true";
+
+ if (!test.warmup_url.empty())
+ variation_params["warmup_url"] = test.warmup_url;
ASSERT_TRUE(variations::AssociateVariationParams(
params::GetQuicFieldTrialName(), test.trial_group_name,
variation_params));
@@ -452,6 +445,13 @@ TEST_F(DataReductionProxyParamsTest, QuicFieldTrial) {
EXPECT_EQ(test.expected_enabled, params::IsIncludedInQuicFieldTrial());
EXPECT_EQ(test.expected_zero_rtt, params::IsZeroRttQuicEnabled());
+ if (!test.warmup_url.empty()) {
+ EXPECT_EQ(GURL(test.warmup_url), params::GetWarmupURL());
+ } else {
+ EXPECT_EQ(GURL("http://check.googlezip.net/generate_204"),
+ params::GetWarmupURL());
+ }
+ EXPECT_EQ(test.expect_warmup_url_enabled, params::FetchWarmupURLEnabled());
}
}
@@ -528,14 +528,18 @@ TEST(DataReductionProxyParamsStandaloneTest, OverrideProxiesForHttp) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kDataReductionProxyHttpProxies,
"http://override-first.net;http://override-second.net");
- DataReductionProxyParams params(
- DataReductionProxyParams::kAllowAllProxyConfigurations);
-
- std::vector<net::ProxyServer> expected_override_proxies_for_http;
- expected_override_proxies_for_http.push_back(net::ProxyServer::FromURI(
- "http://override-first.net", net::ProxyServer::SCHEME_HTTP));
- expected_override_proxies_for_http.push_back(net::ProxyServer::FromURI(
- "http://override-second.net", net::ProxyServer::SCHEME_HTTP));
+ DataReductionProxyParams params(0);
+
+ // Overriding proxies must have type UNSPECIFIED_TYPE.
+ std::vector<DataReductionProxyServer> expected_override_proxies_for_http;
+ expected_override_proxies_for_http.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("http://override-first.net",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
+ expected_override_proxies_for_http.push_back(DataReductionProxyServer(
+ net::ProxyServer::FromURI("http://override-second.net",
+ net::ProxyServer::SCHEME_HTTP),
+ ProxyServer::UNSPECIFIED_TYPE));
EXPECT_EQ(expected_override_proxies_for_http, params.proxies_for_http());
}
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.cc
index cd72646424f..0ac6d04b235 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.cc
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.cc
@@ -191,6 +191,11 @@ const char kDataReductionProxyWasEnabledBefore[] =
const char kDataReductionProxyLastEnabledTime[] =
"data_reduction.last_enabled_time";
+// An integer pref that contains the time when the data reduction proxy savings
+// were last cleared because the system clock was moved back by more than 1 day.
+const char kDataReductionProxySavingsClearedNegativeSystemClock[] =
+ "data_reduction.savings_cleared_negative_system_clock";
+
// An int64_t pref that contains the total size of all HTTP content received
// from the network.
const char kHttpReceivedContentLength[] = "http_received_content_length";
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h
index 65c93b2b70e..e67a0828af4 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h
@@ -48,6 +48,7 @@ extern const char kDataReductionProxyConfig[];
extern const char kDataUsageReportingEnabled[];
extern const char kDataReductionProxyWasEnabledBefore[];
extern const char kDataReductionProxyLastEnabledTime[];
+extern const char kDataReductionProxySavingsClearedNegativeSystemClock[];
extern const char kHttpOriginalContentLength[];
extern const char kHttpReceivedContentLength[];
extern const char kLoFiImplicitOptOutEpoch[];
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_server.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_server.cc
new file mode 100644
index 00000000000..bddd81d41ac
--- /dev/null
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_server.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
+
+namespace data_reduction_proxy {
+
+DataReductionProxyServer::DataReductionProxyServer(
+ const net::ProxyServer& proxy_server,
+ ProxyServer_ProxyType proxy_type)
+ : proxy_server_(proxy_server), proxy_type_(proxy_type) {}
+
+bool DataReductionProxyServer::operator==(
+ const DataReductionProxyServer& other) const {
+ return proxy_server_ == other.proxy_server_ &&
+ proxy_type_ == other.proxy_type_;
+}
+
+// static
+std::vector<net::ProxyServer>
+DataReductionProxyServer::ConvertToNetProxyServers(
+ const std::vector<DataReductionProxyServer>& data_reduction_proxy_servers) {
+ std::vector<net::ProxyServer> net_proxy_servers;
+ for (auto data_reduction_proxy_server : data_reduction_proxy_servers)
+ net_proxy_servers.push_back(data_reduction_proxy_server.proxy_server());
+ return net_proxy_servers;
+}
+
+ProxyServer_ProxyType DataReductionProxyServer::GetProxyTypeForTesting() const {
+ return proxy_type_;
+}
+
+} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_server.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_server.h
new file mode 100644
index 00000000000..4f6626fa6e8
--- /dev/null
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_server.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_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_SERVER_H_
+#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_SERVER_H_
+
+#include <vector>
+
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
+#include "net/proxy/proxy_server.h"
+
+namespace data_reduction_proxy {
+
+// A class that stores information about a single data reduction proxy server.
+class DataReductionProxyServer {
+ public:
+ DataReductionProxyServer(const net::ProxyServer& proxy_server,
+ ProxyServer_ProxyType proxy_type);
+
+ DataReductionProxyServer(const DataReductionProxyServer& other) = default;
+
+ DataReductionProxyServer& operator=(const DataReductionProxyServer& other) =
+ default;
+
+ bool operator==(const DataReductionProxyServer& other) const;
+
+ net::ProxyServer proxy_server() const { return proxy_server_; }
+
+ static std::vector<net::ProxyServer> ConvertToNetProxyServers(
+ const std::vector<DataReductionProxyServer>&
+ data_reduction_proxy_servers);
+
+ // Returns |proxy_type_| for verification by tests.
+ ProxyServer_ProxyType GetProxyTypeForTesting() const;
+
+ private:
+ net::ProxyServer proxy_server_;
+ ProxyServer_ProxyType proxy_type_;
+};
+
+} // namespace data_reduction_proxy
+
+#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_SERVER_H_
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 6f7aa8a2bda..8e2caccccc3 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
@@ -7,10 +7,6 @@
namespace data_reduction_proxy {
namespace switches {
-// Clear data savings on Chrome startup.
-const char kClearDataReductionProxyDataSavings[] =
- "clear-data-reduction-proxy-data-savings";
-
// The origin of the data reduction proxy.
const char kDataReductionProxy[] = "spdy-proxy-auth-origin";
@@ -59,11 +55,6 @@ const char kDataReductionProxySecureProxyCheckURL[] =
const char kDataReductionProxyServerExperimentsDisabled[] =
"data-reduction-proxy-server-experiments-disabled";
-// Starts the secure Data Reduction Proxy in the disabled state until the secure
-// proxy check succeeds.
-const char kDataReductionProxyStartSecureDisabled[] =
- "data-reduction-proxy-secure-proxy-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";
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 7e33494a0ee..c3ab54b10b1 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
@@ -11,7 +11,6 @@ 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 kClearDataReductionProxyDataSavings[];
extern const char kDataReductionProxy[];
extern const char kDataReductionProxyConfigURL[];
extern const char kDataReductionProxyExperiment[];
@@ -26,7 +25,6 @@ extern const char kDataReductionProxyLoFiValueSlowConnectionsOnly[];
extern const char kDataReductionPingbackURL[];
extern const char kDataReductionProxySecureProxyCheckURL[];
extern const char kDataReductionProxyServerExperimentsDisabled[];
-extern const char kDataReductionProxyStartSecureDisabled[];
extern const char kDataReductionProxyWarmupURL[];
extern const char kEnableDataReductionProxy[];
extern const char kEnableDataReductionProxyBypassWarning[];
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 118c8096a15..9af46dd61df 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
@@ -4,13 +4,18 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h"
+#include <stdint.h>
+
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "base/version.h"
#include "components/data_reduction_proxy/core/common/version.h"
+#include "net/base/net_errors.h"
#include "net/base/url_util.h"
+#include "net/http/http_response_headers.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_info.h"
+#include "net/url_request/url_request.h"
#if defined(USE_GOOGLE_API_KEYS)
#include "google_apis/google_api_keys.h"
@@ -24,6 +29,34 @@ namespace {
// Used in all Data Reduction Proxy URLs to specify API Key.
const char kApiKeyName[] = "key";
#endif
+
+// Scales |byte_count| by the ratio of |numerator|:|denomenator|.
+int64_t ScaleByteCountByRatio(int64_t byte_count,
+ int64_t numerator,
+ int64_t denomenator) {
+ DCHECK_LE(0, byte_count);
+ DCHECK_LE(0, numerator);
+ DCHECK_LT(0, denomenator);
+
+ // As an optimization, use integer arithmetic if it won't overflow.
+ if (byte_count <= std::numeric_limits<int32_t>::max() &&
+ numerator <= std::numeric_limits<int32_t>::max()) {
+ return byte_count * numerator / denomenator;
+ }
+
+ double scaled_byte_count = static_cast<double>(byte_count) *
+ static_cast<double>(numerator) /
+ static_cast<double>(denomenator);
+ if (scaled_byte_count >
+ static_cast<double>(std::numeric_limits<int64_t>::max())) {
+ // If this ever triggers, then byte counts can no longer be safely stored in
+ // 64-bit ints.
+ NOTREACHED();
+ return byte_count;
+ }
+ return static_cast<int64_t>(scaled_byte_count);
+}
+
} // namespace
namespace util {
@@ -133,6 +166,31 @@ bool ApplyProxyConfigToProxyInfo(const net::ProxyConfig& proxy_config,
return !data_reduction_proxy_info->proxy_server().is_direct();
}
+int64_t CalculateEffectiveOCL(const net::URLRequest& request) {
+ if (request.was_cached() || !request.response_headers())
+ return request.received_response_content_length();
+ int64_t original_content_length_from_header =
+ request.response_headers()->GetInt64HeaderValue(
+ "x-original-content-length");
+
+ if (original_content_length_from_header < 0)
+ return request.received_response_content_length();
+ if (request.status().error() == net::OK)
+ return original_content_length_from_header;
+
+ int64_t content_length_from_header =
+ request.response_headers()->GetContentLength();
+
+ if (content_length_from_header < 0)
+ return request.received_response_content_length();
+ if (content_length_from_header == 0)
+ return original_content_length_from_header;
+
+ return ScaleByteCountByRatio(request.received_response_content_length(),
+ original_content_length_from_header,
+ content_length_from_header);
+}
+
} // namespace util
namespace protobuf_parser {
@@ -166,8 +224,6 @@ net::ProxyServer::Scheme SchemeFromProxyScheme(
return net::ProxyServer::SCHEME_HTTP;
case ProxyServer_ProxyScheme_HTTPS:
return net::ProxyServer::SCHEME_HTTPS;
- case ProxyServer_ProxyScheme_QUIC:
- return net::ProxyServer::SCHEME_QUIC;
default:
return net::ProxyServer::SCHEME_INVALID;
}
@@ -179,8 +235,6 @@ ProxyServer_ProxyScheme ProxySchemeFromScheme(net::ProxyServer::Scheme scheme) {
return ProxyServer_ProxyScheme_HTTP;
case net::ProxyServer::SCHEME_HTTPS:
return ProxyServer_ProxyScheme_HTTPS;
- case net::ProxyServer::SCHEME_QUIC:
- return ProxyServer_ProxyScheme_QUIC;
default:
return ProxyServer_ProxyScheme_UNSPECIFIED;
}
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 340c74afd13..be0a16dabff 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
@@ -23,6 +23,7 @@ class TimeDelta;
namespace net {
class ProxyConfig;
class ProxyInfo;
+class URLRequest;
}
namespace data_reduction_proxy {
@@ -87,6 +88,10 @@ bool ApplyProxyConfigToProxyInfo(const net::ProxyConfig& proxy_config,
const GURL& url,
net::ProxyInfo* data_reduction_proxy_info);
+// Calculates the effective original content length of the |request|, accounting
+// for partial responses if necessary.
+int64_t CalculateEffectiveOCL(const net::URLRequest& request);
+
} // namespace util
namespace protobuf_parser {
diff --git a/chromium/components/data_reduction_proxy/core/common/resource_type_provider.h b/chromium/components/data_reduction_proxy/core/common/resource_type_provider.h
new file mode 100644
index 00000000000..ca80c4ec8b4
--- /dev/null
+++ b/chromium/components/data_reduction_proxy/core/common/resource_type_provider.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_DATA_REDUCTION_PROXY_CORE_COMMON_RESOURCE_TYPE_PROVIDER_H_
+#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_RESOURCE_TYPE_PROVIDER_H_
+
+#include "base/macros.h"
+
+class GURL;
+
+namespace net {
+class URLRequest;
+}
+
+namespace data_reduction_proxy {
+
+// Class responsible for deciding the content type of a request.
+class ResourceTypeProvider {
+ public:
+ // This enum should remain synchronized with the
+ // DataReductionProxyResourceContentType enum in histograms.xml.
+ enum ContentType {
+ CONTENT_TYPE_UNKNOWN = 0,
+ CONTENT_TYPE_MEDIA = 1,
+ CONTENT_TYPE_MAX = 2,
+ };
+ virtual ~ResourceTypeProvider() {}
+
+ virtual void SetContentType(const net::URLRequest& request) = 0;
+
+ virtual ContentType GetContentType(const GURL& url) const = 0;
+
+ protected:
+ ResourceTypeProvider() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceTypeProvider);
+};
+
+} // namespace data_reduction_proxy
+
+#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_RESOURCE_TYPE_PROVIDER_H_
diff --git a/chromium/components/data_reduction_proxy/proto/client_config.proto b/chromium/components/data_reduction_proxy/proto/client_config.proto
index 44fd32b45d8..273875f1aec 100644
--- a/chromium/components/data_reduction_proxy/proto/client_config.proto
+++ b/chromium/components/data_reduction_proxy/proto/client_config.proto
@@ -16,7 +16,7 @@ message ClientConfig {
// The time at which the client should request a new configuration. The
// session_key is guaranteed to be valid through this time and may be valid
// for some time thereafter.
- optional Timestamp refresh_time = 2;
+ optional Timestamp DEPRECATED_refresh_time = 2 [deprecated = true];
// The proxy configuration the client should use to connect to the Data Saver
// service.
optional ProxyConfig proxy_config = 3;
@@ -94,7 +94,15 @@ message ProxyServer {
// HTTPS
HTTPS = 2;
// HTTPS over QUIC
- QUIC = 3;
+ DEPRECATED_QUIC = 3 [deprecated = true];
+ }
+
+ // The deployment type of the proxy server.
+ enum ProxyType {
+ // The proxy type is unspecified.
+ UNSPECIFIED_TYPE = 0;
+ // Core Google datacenter.
+ CORE = 1;
}
// The scheme for the proxy server.
@@ -103,6 +111,8 @@ message ProxyServer {
optional string host = 2;
// The port number for the proxy server.
optional int32 port = 3;
+ // The type for the proxy server.
+ optional ProxyType type = 4;
}
// Request object to create a client configuration object.
diff --git a/chromium/components/data_usage/OWNERS b/chromium/components/data_usage/OWNERS
index ad4b2b21ecf..78d68ca2be7 100644
--- a/chromium/components/data_usage/OWNERS
+++ b/chromium/components/data_usage/OWNERS
@@ -1,3 +1,4 @@
bengr@chromium.org
+rajendrant@chromium.org
sclittle@chromium.org
tbansal@chromium.org \ No newline at end of file
diff --git a/chromium/components/data_use_measurement/content/BUILD.gn b/chromium/components/data_use_measurement/content/BUILD.gn
index 999bc022b67..b9c18ae112e 100644
--- a/chromium/components/data_use_measurement/content/BUILD.gn
+++ b/chromium/components/data_use_measurement/content/BUILD.gn
@@ -10,6 +10,7 @@ static_library("content") {
deps = [
"//base",
"//content/public/browser",
+ "//content/public/common",
"//net:net",
]
}
diff --git a/chromium/components/data_use_measurement/content/DEPS b/chromium/components/data_use_measurement/content/DEPS
index 8c57389c3be..8dbf71c6590 100644
--- a/chromium/components/data_use_measurement/content/DEPS
+++ b/chromium/components/data_use_measurement/content/DEPS
@@ -1,4 +1,5 @@
include_rules = [
"+content/public/browser",
+ "+content/public/common",
"+net",
]
diff --git a/chromium/components/data_use_measurement/content/content_url_request_classifier.cc b/chromium/components/data_use_measurement/content/content_url_request_classifier.cc
index 23bbd8baa96..2c723b69684 100644
--- a/chromium/components/data_use_measurement/content/content_url_request_classifier.cc
+++ b/chromium/components/data_use_measurement/content/content_url_request_classifier.cc
@@ -4,7 +4,12 @@
#include "components/data_use_measurement/content/content_url_request_classifier.h"
+#include <string>
+
+#include "base/strings/string_util.h"
#include "content/public/browser/resource_request_info.h"
+#include "content/public/common/resource_type.h"
+#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
namespace data_use_measurement {
@@ -26,4 +31,40 @@ bool ContentURLRequestClassifier::IsUserRequest(
return data_use_measurement::IsUserRequest(request);
}
+DataUseUserData::DataUseContentType ContentURLRequestClassifier::GetContentType(
+ const net::URLRequest& request,
+ const net::HttpResponseHeaders& response_headers) const {
+ const content::ResourceRequestInfo* request_info =
+ content::ResourceRequestInfo::ForRequest(&request);
+ std::string mime_type;
+ if (response_headers.GetMimeType(&mime_type)) {
+ if (mime_type == "text/html" && request_info &&
+ request_info->GetResourceType() ==
+ content::ResourceType::RESOURCE_TYPE_MAIN_FRAME) {
+ return DataUseUserData::MAIN_FRAME_HTML;
+ } else if (mime_type == "text/html") {
+ return DataUseUserData::NON_MAIN_FRAME_HTML;
+ } else if (mime_type == "text/css") {
+ return DataUseUserData::CSS;
+ } else if (base::StartsWith(mime_type, "image/",
+ base::CompareCase::SENSITIVE)) {
+ return DataUseUserData::IMAGE;
+ } else if (base::EndsWith(mime_type, "javascript",
+ base::CompareCase::SENSITIVE) ||
+ base::EndsWith(mime_type, "ecmascript",
+ base::CompareCase::SENSITIVE)) {
+ return DataUseUserData::JAVASCRIPT;
+ } else if (mime_type.find("font") != std::string::npos) {
+ return DataUseUserData::FONT;
+ } else if (base::StartsWith(mime_type, "audio/",
+ base::CompareCase::SENSITIVE)) {
+ return DataUseUserData::AUDIO;
+ } else if (base::StartsWith(mime_type, "video/",
+ base::CompareCase::SENSITIVE)) {
+ return DataUseUserData::VIDEO;
+ }
+ }
+ return DataUseUserData::OTHER;
+}
+
} // namespace data_use_measurement
diff --git a/chromium/components/data_use_measurement/content/content_url_request_classifier.h b/chromium/components/data_use_measurement/content/content_url_request_classifier.h
index 2e102bb782f..8b978fed773 100644
--- a/chromium/components/data_use_measurement/content/content_url_request_classifier.h
+++ b/chromium/components/data_use_measurement/content/content_url_request_classifier.h
@@ -19,6 +19,10 @@ bool IsUserRequest(const net::URLRequest& request);
class ContentURLRequestClassifier : public URLRequestClassifier {
public:
bool IsUserRequest(const net::URLRequest& request) const override;
+
+ DataUseUserData::DataUseContentType GetContentType(
+ const net::URLRequest& request,
+ const net::HttpResponseHeaders& response_headers) const override;
};
} // namespace data_use_measurement
diff --git a/chromium/components/data_use_measurement/core/BUILD.gn b/chromium/components/data_use_measurement/core/BUILD.gn
index c74f8d423a6..c76d1e5b494 100644
--- a/chromium/components/data_use_measurement/core/BUILD.gn
+++ b/chromium/components/data_use_measurement/core/BUILD.gn
@@ -32,6 +32,10 @@ static_library("ascriber") {
"//components/metrics",
"//net",
]
+
+ if (!is_ios) {
+ deps += [ "//components/domain_reliability" ]
+ }
}
source_set("unit_tests") {
diff --git a/chromium/components/data_use_measurement/core/DEPS b/chromium/components/data_use_measurement/core/DEPS
index de1112987c6..ed27444ed64 100644
--- a/chromium/components/data_use_measurement/core/DEPS
+++ b/chromium/components/data_use_measurement/core/DEPS
@@ -1,4 +1,5 @@
include_rules = [
"+net",
"+components/metrics",
+ "+components/domain_reliability",
]
diff --git a/chromium/components/data_use_measurement/core/data_use.cc b/chromium/components/data_use_measurement/core/data_use.cc
index 6a8ec7fe61b..c84da07f1fa 100644
--- a/chromium/components/data_use_measurement/core/data_use.cc
+++ b/chromium/components/data_use_measurement/core/data_use.cc
@@ -8,6 +8,10 @@ namespace data_use_measurement {
DataUse::DataUse() : total_bytes_sent_(0), total_bytes_received_(0) {}
+DataUse::DataUse(const DataUse& other) :
+ total_bytes_sent_(other.total_bytes_sent_),
+ total_bytes_received_(other.total_bytes_received_) {}
+
DataUse::~DataUse() {}
void DataUse::MergeFrom(const DataUse& other) {
diff --git a/chromium/components/data_use_measurement/core/data_use.h b/chromium/components/data_use_measurement/core/data_use.h
index 55cb7e57ed1..18e706dc6d4 100644
--- a/chromium/components/data_use_measurement/core/data_use.h
+++ b/chromium/components/data_use_measurement/core/data_use.h
@@ -19,6 +19,7 @@ namespace data_use_measurement {
class DataUse {
public:
DataUse();
+ DataUse(const DataUse& other);
~DataUse();
// Merge data use from another instance.
@@ -46,8 +47,6 @@ class DataUse {
int64_t total_bytes_sent_;
int64_t total_bytes_received_;
-
- DISALLOW_COPY_AND_ASSIGN(DataUse);
};
} // namespace data_use_measurement
diff --git a/chromium/components/data_use_measurement/core/data_use_ascriber.cc b/chromium/components/data_use_measurement/core/data_use_ascriber.cc
index c4dd1e13015..c7c9d49052e 100644
--- a/chromium/components/data_use_measurement/core/data_use_ascriber.cc
+++ b/chromium/components/data_use_measurement/core/data_use_ascriber.cc
@@ -10,6 +10,7 @@
#include "components/data_use_measurement/core/data_use_network_delegate.h"
#include "components/data_use_measurement/core/data_use_recorder.h"
#include "components/data_use_measurement/core/url_request_classifier.h"
+#include "net/http/http_response_headers.h"
namespace data_use_measurement {
@@ -22,31 +23,30 @@ std::unique_ptr<net::NetworkDelegate> DataUseAscriber::CreateNetworkDelegate(
}
void DataUseAscriber::OnBeforeUrlRequest(net::URLRequest* request) {
- DataUseRecorder* recorder = GetDataUseRecorder(request);
+ DataUseRecorder* recorder = GetOrCreateDataUseRecorder(request);
if (recorder)
recorder->OnBeforeUrlRequest(request);
}
-void DataUseAscriber::OnBeforeRedirect(net::URLRequest* request,
- const GURL& new_location) {}
-
void DataUseAscriber::OnNetworkBytesSent(net::URLRequest* request,
int64_t bytes_sent) {
- DataUseRecorder* recorder = GetDataUseRecorder(request);
+ DataUseRecorder* recorder = GetDataUseRecorder(*request);
if (recorder)
recorder->OnNetworkBytesSent(request, bytes_sent);
}
void DataUseAscriber::OnNetworkBytesReceived(net::URLRequest* request,
int64_t bytes_received) {
- DataUseRecorder* recorder = GetDataUseRecorder(request);
+ DataUseRecorder* recorder = GetDataUseRecorder(*request);
if (recorder)
recorder->OnNetworkBytesReceived(request, bytes_received);
}
+void DataUseAscriber::OnUrlRequestCompleted(net::URLRequest* request,
+ bool started) {}
+
void DataUseAscriber::OnUrlRequestDestroyed(net::URLRequest* request) {
- DataUseRecorder* recorder = GetDataUseRecorder(request);
- // TODO(kundaji): Enforce DCHECK(recorder).
+ DataUseRecorder* recorder = GetOrCreateDataUseRecorder(request);
if (recorder)
recorder->OnUrlRequestDestroyed(request);
}
diff --git a/chromium/components/data_use_measurement/core/data_use_ascriber.h b/chromium/components/data_use_measurement/core/data_use_ascriber.h
index 58a669cb620..78f2b3813aa 100644
--- a/chromium/components/data_use_measurement/core/data_use_ascriber.h
+++ b/chromium/components/data_use_measurement/core/data_use_ascriber.h
@@ -40,23 +40,26 @@ class DataUseAscriber {
// Returns the DataUseRecorder to which data usage for the given URL should
// be ascribed. If no existing DataUseRecorder exists, a new one will be
// created.
- virtual DataUseRecorder* GetDataUseRecorder(net::URLRequest* request) = 0;
+ virtual DataUseRecorder* GetOrCreateDataUseRecorder(
+ net::URLRequest* request) = 0;
- // Methods called by DataUseNetworkDelegate to propagate data use information:
- virtual void OnBeforeUrlRequest(net::URLRequest* request);
+ // Returns the existing DataUseRecorder to which data usage for the given URL
+ // should be ascribed.
+ virtual DataUseRecorder* GetDataUseRecorder(
+ const net::URLRequest& request) = 0;
- virtual void OnBeforeRedirect(net::URLRequest* request,
- const GURL& new_location);
+ // Returns a URLRequestClassifier that can classify requests for metrics
+ // recording.
+ virtual std::unique_ptr<URLRequestClassifier> CreateURLRequestClassifier()
+ const = 0;
+ // Methods called by DataUseNetworkDelegate to propagate data use information:
+ virtual void OnBeforeUrlRequest(net::URLRequest* request);
virtual void OnNetworkBytesSent(net::URLRequest* request, int64_t bytes_sent);
-
virtual void OnNetworkBytesReceived(net::URLRequest* request,
int64_t bytes_received);
-
+ virtual void OnUrlRequestCompleted(net::URLRequest* request, bool started);
virtual void OnUrlRequestDestroyed(net::URLRequest* request);
-
- virtual std::unique_ptr<URLRequestClassifier> CreateURLRequestClassifier()
- const = 0;
};
} // namespace data_use_measurement
diff --git a/chromium/components/data_use_measurement/core/data_use_measurement.cc b/chromium/components/data_use_measurement/core/data_use_measurement.cc
index 5ded37e7fe9..4408bc3625a 100644
--- a/chromium/components/data_use_measurement/core/data_use_measurement.cc
+++ b/chromium/components/data_use_measurement/core/data_use_measurement.cc
@@ -8,8 +8,11 @@
#include "base/metrics/sparse_histogram.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
+#include "components/data_use_measurement/core/data_use_ascriber.h"
+#include "components/data_use_measurement/core/data_use_recorder.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/data_use_measurement/core/url_request_classifier.h"
+#include "components/domain_reliability/uploader.h"
#include "net/base/network_change_notifier.h"
#include "net/base/upload_data_stream.h"
#include "net/http/http_response_headers.h"
@@ -65,9 +68,11 @@ void IncrementLatencyHistogramByCount(const std::string& name,
DataUseMeasurement::DataUseMeasurement(
std::unique_ptr<URLRequestClassifier> url_request_classifier,
- const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder)
+ const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder,
+ DataUseAscriber* ascriber)
: url_request_classifier_(std::move(url_request_classifier)),
- metrics_data_use_forwarder_(metrics_data_use_forwarder)
+ metrics_data_use_forwarder_(metrics_data_use_forwarder),
+ ascriber_(ascriber)
#if defined(OS_ANDROID)
,
app_state_(base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES),
@@ -80,6 +85,7 @@ DataUseMeasurement::DataUseMeasurement(
no_reads_since_background_(false)
#endif
{
+ DCHECK(ascriber_);
DCHECK(url_request_classifier_);
}
@@ -89,9 +95,21 @@ void DataUseMeasurement::OnBeforeURLRequest(net::URLRequest* request) {
DataUseUserData* data_use_user_data = reinterpret_cast<DataUseUserData*>(
request->GetUserData(DataUseUserData::kUserDataKey));
if (!data_use_user_data) {
- data_use_user_data = new DataUseUserData(
- DataUseUserData::ServiceName::NOT_TAGGED, CurrentAppState());
+ DataUseUserData::ServiceName service_name =
+ DataUseUserData::ServiceName::NOT_TAGGED;
+ if (!url_request_classifier_->IsUserRequest(*request) &&
+ domain_reliability::DomainReliabilityUploader::
+ OriginatedFromDomainReliability(*request)) {
+ // Detect if the request originated from DomainReliability.
+ // DataUseUserData::AttachToFetcher() cannot be called from domain
+ // reliability, since it sets userdata on URLFetcher for its purposes.
+ service_name = DataUseUserData::ServiceName::DOMAIN_RELIABILITY;
+ }
+
+ data_use_user_data = new DataUseUserData(service_name, CurrentAppState());
request->SetUserData(DataUseUserData::kUserDataKey, data_use_user_data);
+ } else {
+ data_use_user_data->set_app_state(CurrentAppState());
}
}
@@ -103,6 +121,17 @@ void DataUseMeasurement::OnBeforeRedirect(const net::URLRequest& request,
ReportServicesMessageSizeUMA(request);
}
+void DataUseMeasurement::OnHeadersReceived(
+ net::URLRequest* request,
+ const net::HttpResponseHeaders* response_headers) {
+ DataUseUserData* data_use_user_data = reinterpret_cast<DataUseUserData*>(
+ request->GetUserData(DataUseUserData::kUserDataKey));
+ if (data_use_user_data) {
+ data_use_user_data->set_content_type(
+ url_request_classifier_->GetContentType(*request, *response_headers));
+ }
+}
+
void DataUseMeasurement::OnNetworkBytesReceived(const net::URLRequest& request,
int64_t bytes_received) {
UMA_HISTOGRAM_COUNTS("DataUse.BytesReceived.Delegate", bytes_received);
@@ -179,6 +208,23 @@ void DataUseMeasurement::ReportDataUseUMA(const net::URLRequest& request,
}
}
#endif
+
+ bool is_tab_visible = false;
+
+ if (is_user_traffic) {
+ const DataUseRecorder* recorder = ascriber_->GetDataUseRecorder(request);
+ if (recorder) {
+ is_tab_visible = recorder->is_visible();
+ RecordTabStateHistogram(dir, new_app_state, recorder->is_visible(),
+ bytes);
+ }
+ }
+ if (attached_service_data && dir == DOWNSTREAM &&
+ new_app_state != DataUseUserData::UNKNOWN) {
+ RecordContentTypeHistogram(attached_service_data->content_type(),
+ is_user_traffic, new_app_state, is_tab_visible,
+ bytes);
+ }
}
void DataUseMeasurement::UpdateDataUsePrefs(
@@ -319,4 +365,60 @@ void DataUseMeasurement::ReportDataUsageServices(
}
}
+void DataUseMeasurement::RecordTabStateHistogram(
+ TrafficDirection dir,
+ DataUseUserData::AppState app_state,
+ bool is_tab_visible,
+ int64_t bytes) {
+ if (app_state == DataUseUserData::UNKNOWN)
+ return;
+
+ std::string histogram_name = "DataUse.AppTabState.";
+ histogram_name.append(dir == UPSTREAM ? "Upstream." : "Downstream.");
+ if (app_state == DataUseUserData::BACKGROUND) {
+ histogram_name.append("AppBackground");
+ } else if (is_tab_visible) {
+ histogram_name.append("AppForeground.TabForeground");
+ } else {
+ histogram_name.append("AppForeground.TabBackground");
+ }
+ RecordUMAHistogramCount(histogram_name, bytes);
+}
+
+void DataUseMeasurement::RecordContentTypeHistogram(
+ DataUseUserData::DataUseContentType content_type,
+ bool is_user_traffic,
+ DataUseUserData::AppState app_state,
+ bool is_tab_visible,
+ int64_t bytes) {
+ if (content_type == DataUseUserData::AUDIO) {
+ content_type = app_state != DataUseUserData::FOREGROUND
+ ? DataUseUserData::AUDIO_APPBACKGROUND
+ : (!is_tab_visible ? DataUseUserData::AUDIO_TABBACKGROUND
+ : DataUseUserData::AUDIO);
+ } else if (content_type == DataUseUserData::VIDEO) {
+ content_type = app_state != DataUseUserData::FOREGROUND
+ ? DataUseUserData::VIDEO_APPBACKGROUND
+ : (!is_tab_visible ? DataUseUserData::VIDEO_TABBACKGROUND
+ : DataUseUserData::VIDEO);
+ }
+ // Use the more primitive STATIC_HISTOGRAM_POINTER_BLOCK macro because the
+ // simple UMA_HISTOGRAM_ENUMERATION macros don't expose 'AddCount'.
+ if (is_user_traffic) {
+ STATIC_HISTOGRAM_POINTER_BLOCK(
+ "DataUse.ContentType.UserTraffic", AddCount(content_type, bytes),
+ base::LinearHistogram::FactoryGet(
+ "DataUse.ContentType.UserTraffic", 1, DataUseUserData::TYPE_MAX,
+ DataUseUserData::TYPE_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag));
+ } else {
+ STATIC_HISTOGRAM_POINTER_BLOCK(
+ "DataUse.ContentType.Services", AddCount(content_type, bytes),
+ base::LinearHistogram::FactoryGet(
+ "DataUse.ContentType.Services", 1, DataUseUserData::TYPE_MAX,
+ DataUseUserData::TYPE_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag));
+ }
+}
+
} // namespace data_use_measurement
diff --git a/chromium/components/data_use_measurement/core/data_use_measurement.h b/chromium/components/data_use_measurement/core/data_use_measurement.h
index 1422ea5bd22..a0e4298c94e 100644
--- a/chromium/components/data_use_measurement/core/data_use_measurement.h
+++ b/chromium/components/data_use_measurement/core/data_use_measurement.h
@@ -24,11 +24,13 @@
class GURL;
namespace net {
+class HttpResponseHeaders;
class URLRequest;
}
namespace data_use_measurement {
+class DataUseAscriber;
class URLRequestClassifier;
// Records the data use of user traffic and various services in UMA histograms.
@@ -41,7 +43,8 @@ class DataUseMeasurement {
public:
DataUseMeasurement(
std::unique_ptr<URLRequestClassifier> url_request_classifier,
- const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder);
+ const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder,
+ DataUseAscriber* ascriber);
~DataUseMeasurement();
// Called before a request is sent.
@@ -51,6 +54,10 @@ class DataUseMeasurement {
void OnBeforeRedirect(const net::URLRequest& request,
const GURL& new_location);
+ // Called when response headers are received for |request|.
+ void OnHeadersReceived(net::URLRequest* request,
+ const net::HttpResponseHeaders* response_headers);
+
// Called when data is received or sent on the network, respectively.
void OnNetworkBytesReceived(const net::URLRequest& request,
int64_t bytes_received);
@@ -115,9 +122,9 @@ class DataUseMeasurement {
// Reports the message size of the service requests.
void ReportServicesMessageSizeUMA(const net::URLRequest& request);
- // A helper function used to record data use of services. It gets the size of
- // exchanged message, its direction (which is upstream or downstream) and
- // reports to two histogram groups. DataUse.MessageSize.ServiceName and
+ // Records data use histograms of services. It gets the size of exchanged
+ // message, its direction (which is upstream or downstream) and reports to two
+ // histogram groups. DataUse.MessageSize.ServiceName and
// DataUse.Services.{Dimensions}. In the second one, services are buckets.
// |app_state| indicates the app state which can be foreground, background, or
// unknown.
@@ -128,6 +135,22 @@ class DataUseMeasurement {
bool is_connection_cellular,
int64_t message_size) const;
+ // Records data use histograms split on TrafficDirection, AppState and
+ // TabState.
+ void RecordTabStateHistogram(TrafficDirection dir,
+ DataUseUserData::AppState app_state,
+ bool is_tab_visible,
+ int64_t bytes);
+
+ // Records data use histograms of user traffic and services traffic split on
+ // content type, AppState and TabState.
+ void RecordContentTypeHistogram(
+ DataUseUserData::DataUseContentType content_type,
+ bool is_user_traffic,
+ DataUseUserData::AppState app_state,
+ bool is_tab_visible,
+ int64_t bytes);
+
// Classifier for identifying if an URL request is user initiated.
std::unique_ptr<URLRequestClassifier> url_request_classifier_;
@@ -137,6 +160,9 @@ class DataUseMeasurement {
// class to support registering arbitrary observers. crbug.com/601185
metrics::UpdateUsagePrefCallbackType metrics_data_use_forwarder_;
+ // DataUseAscriber used to get the attributes of data use.
+ DataUseAscriber* ascriber_;
+
#if defined(OS_ANDROID)
// Application listener store the last known state of the application in this
// field.
diff --git a/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc b/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc
index 707577b6515..39429f95ad4 100644
--- a/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc
+++ b/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc
@@ -8,10 +8,13 @@
#include <string>
#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/histogram_tester.h"
#include "build/build_config.h"
+#include "components/data_use_measurement/core/data_use_ascriber.h"
+#include "components/data_use_measurement/core/data_use_recorder.h"
#include "components/data_use_measurement/core/url_request_classifier.h"
#include "net/base/network_change_notifier.h"
#include "net/base/request_priority.h"
@@ -27,38 +30,93 @@
namespace data_use_measurement {
-class UserRequestUserDataForTesting : public base::SupportsUserData::Data,
- public URLRequestClassifier {
+class TestURLRequestClassifier : public base::SupportsUserData::Data,
+ public URLRequestClassifier {
public:
static const void* const kUserDataKey;
+ TestURLRequestClassifier() : content_type_(DataUseUserData::OTHER) {}
+
bool IsUserRequest(const net::URLRequest& request) const override {
return request.GetUserData(kUserDataKey) != nullptr;
}
static void MarkAsUserRequest(net::URLRequest* request) {
- request->SetUserData(kUserDataKey, new UserRequestUserDataForTesting());
+ request->SetUserData(kUserDataKey, new TestURLRequestClassifier());
+ }
+
+ DataUseUserData::DataUseContentType GetContentType(
+ const net::URLRequest& request,
+ const net::HttpResponseHeaders& response_headers) const override {
+ return content_type_;
+ }
+
+ void set_content_type(DataUseUserData::DataUseContentType content_type) {
+ content_type_ = content_type;
}
+
+ private:
+ DataUseUserData::DataUseContentType content_type_;
};
+class TestDataUseAscriber : public DataUseAscriber {
+ public:
+ TestDataUseAscriber() {}
+
+ DataUseRecorder* GetOrCreateDataUseRecorder(
+ net::URLRequest* request) override {
+ return &recorder_;
+ }
+
+ DataUseRecorder* GetDataUseRecorder(const net::URLRequest& request) override {
+ return &recorder_;
+ }
+
+ std::unique_ptr<URLRequestClassifier> CreateURLRequestClassifier()
+ const override {
+ return nullptr;
+ }
+
+ void SetTabVisibility(bool visible) { recorder_.set_is_visible(visible); }
+
+ private:
+ DataUseRecorder recorder_;
+};
+
+// The more usual initialization of kUserDataKey would be along the lines of
+// const void* const TestURLRequestClassifier::kUserDataKey =
+// &TestURLRequestClassifier::kUserDataKey;
+// but lld's identical constant folding then folds that with
+// DataUseUserData::kUserDataKey which is initialized like that as well, and
+// then TestURLRequestClassifier::IsUserRequest() starts classifying service
+// requests as user requests. To work around this, make
+// TestURLRequestClassifier::kUserDataKey point to an arbitrary integer
+// instead.
+// TODO(thakis): If we changed lld to only ICF over code and not over data,
+// we could undo this again.
+const int kICFBuster = 12345634;
+
// static
-const void* const UserRequestUserDataForTesting::kUserDataKey =
- &UserRequestUserDataForTesting::kUserDataKey;
+const void* const TestURLRequestClassifier::kUserDataKey = &kICFBuster;
class DataUseMeasurementTest : public testing::Test {
public:
DataUseMeasurementTest()
- : data_use_measurement_(
- base::MakeUnique<UserRequestUserDataForTesting>(),
+ : url_request_classifier_(new TestURLRequestClassifier()),
+ data_use_measurement_(
+ std::unique_ptr<URLRequestClassifier>(url_request_classifier_),
base::Bind(&DataUseMeasurementTest::FakeDataUseforwarder,
- base::Unretained(this))) {
+ base::Unretained(this)),
+ &ascriber_) {
// During the test it is expected to not have cellular connection.
DCHECK(!net::NetworkChangeNotifier::IsConnectionCellular(
net::NetworkChangeNotifier::GetConnectionType()));
}
// Creates a test request.
- std::unique_ptr<net::URLRequest> CreateTestRequest(bool is_user_request) {
+ enum RequestKind { kServiceRequest, kUserRequest };
+ std::unique_ptr<net::URLRequest> CreateTestRequest(
+ RequestKind is_user_request) {
net::TestDelegate test_delegate;
InitializeContext();
net::MockRead reads[] = {net::MockRead("HTTP/1.1 200 OK\r\n"
@@ -70,8 +128,8 @@ class DataUseMeasurementTest : public testing::Test {
std::unique_ptr<net::URLRequest> request(context_->CreateRequest(
GURL("http://foo.com"), net::DEFAULT_PRIORITY, &test_delegate));
- if (is_user_request) {
- UserRequestUserDataForTesting::MarkAsUserRequest(request.get());
+ if (is_user_request == kUserRequest) {
+ TestURLRequestClassifier::MarkAsUserRequest(request.get());
} else {
request->SetUserData(
data_use_measurement::DataUseUserData::kUserDataKey,
@@ -87,7 +145,7 @@ class DataUseMeasurementTest : public testing::Test {
// Sends a request and reports data use attaching either user data or service
// data based on |is_user_request|.
- void SendRequest(bool is_user_request) {
+ void SendRequest(RequestKind is_user_request) {
std::unique_ptr<net::URLRequest> request =
CreateTestRequest(is_user_request);
data_use_measurement_.OnBeforeURLRequest(request.get());
@@ -100,7 +158,7 @@ class DataUseMeasurementTest : public testing::Test {
// reflected in proper histograms.
void TestForAUserRequest(const std::string& target_dimension) {
base::HistogramTester histogram_tester;
- SendRequest(true);
+ SendRequest(kUserRequest);
histogram_tester.ExpectTotalCount("DataUse.TrafficSize.User.Downstream." +
target_dimension + kConnectionType,
1);
@@ -121,7 +179,7 @@ class DataUseMeasurementTest : public testing::Test {
// reflected in proper histograms.
void TestForAServiceRequest(const std::string& target_dimension) {
base::HistogramTester histogram_tester;
- SendRequest(false);
+ SendRequest(kServiceRequest);
histogram_tester.ExpectTotalCount("DataUse.TrafficSize.System.Downstream." +
target_dimension + kConnectionType,
1);
@@ -151,7 +209,11 @@ class DataUseMeasurementTest : public testing::Test {
}
base::MessageLoopForIO loop_;
+
+ TestDataUseAscriber ascriber_;
+ TestURLRequestClassifier* url_request_classifier_;
DataUseMeasurement data_use_measurement_;
+
std::unique_ptr<net::MockClientSocketFactory> socket_factory_;
std::unique_ptr<net::TestURLRequestContext> context_;
const std::string kConnectionType = "NotCellular";
@@ -188,14 +250,14 @@ TEST_F(DataUseMeasurementTest, ApplicationStateTest) {
TEST_F(DataUseMeasurementTest, DataUseForwarderIsCalled) {
EXPECT_FALSE(IsDataUseForwarderCalled());
- SendRequest(true);
+ SendRequest(kUserRequest);
EXPECT_TRUE(IsDataUseForwarderCalled());
}
#if defined(OS_ANDROID)
TEST_F(DataUseMeasurementTest, AppStateUnknown) {
base::HistogramTester histogram_tester;
- std::unique_ptr<net::URLRequest> request = CreateTestRequest(true);
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
data_use_measurement_.OnBeforeURLRequest(request.get());
{
@@ -231,7 +293,7 @@ TEST_F(DataUseMeasurementTest, AppStateUnknown) {
TEST_F(DataUseMeasurementTest, TimeOfBackgroundDownstreamBytes) {
{
- std::unique_ptr<net::URLRequest> request = CreateTestRequest(true);
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
data_use_measurement_.OnBeforeURLRequest(request.get());
base::HistogramTester histogram_tester;
data_use_measurement()->OnApplicationStateChange(
@@ -249,7 +311,7 @@ TEST_F(DataUseMeasurementTest, TimeOfBackgroundDownstreamBytes) {
{
// Create new request when app is in foreground..
base::HistogramTester histogram_tester;
- std::unique_ptr<net::URLRequest> request = CreateTestRequest(true);
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
data_use_measurement_.OnBeforeURLRequest(request.get());
data_use_measurement_.OnNetworkBytesSent(*request, 100);
data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
@@ -262,7 +324,7 @@ TEST_F(DataUseMeasurementTest, TimeOfBackgroundDownstreamBytes) {
}
{
- std::unique_ptr<net::URLRequest> request = CreateTestRequest(true);
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
data_use_measurement_.OnBeforeURLRequest(request.get());
base::HistogramTester histogram_tester;
data_use_measurement()->OnApplicationStateChange(
@@ -280,7 +342,7 @@ TEST_F(DataUseMeasurementTest, TimeOfBackgroundDownstreamBytes) {
{
// Create new request when app is in background.
base::HistogramTester histogram_tester;
- std::unique_ptr<net::URLRequest> request = CreateTestRequest(true);
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
data_use_measurement_.OnBeforeURLRequest(request.get());
data_use_measurement_.OnNetworkBytesSent(*request, 100);
data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
@@ -295,7 +357,8 @@ TEST_F(DataUseMeasurementTest, TimeOfBackgroundDownstreamBytes) {
{
// Create new request when app is in background.
base::HistogramTester histogram_tester;
- std::unique_ptr<net::URLRequest> request = CreateTestRequest(false);
+ std::unique_ptr<net::URLRequest> request =
+ CreateTestRequest(kServiceRequest);
data_use_measurement_.OnBeforeURLRequest(request.get());
data_use_measurement_.OnNetworkBytesSent(*request, 100);
data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
@@ -312,7 +375,7 @@ TEST_F(DataUseMeasurementTest, TimeOfBackgroundDownstreamBytes) {
}
{
- std::unique_ptr<net::URLRequest> request = CreateTestRequest(true);
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
data_use_measurement_.OnBeforeURLRequest(request.get());
base::HistogramTester histogram_tester;
data_use_measurement()->OnApplicationStateChange(
@@ -327,6 +390,116 @@ TEST_F(DataUseMeasurementTest, TimeOfBackgroundDownstreamBytes) {
"DataUse.BackgroundToFirstDownstream.User", 0);
}
}
+
+TEST_F(DataUseMeasurementTest, AppTabState) {
+ base::HistogramTester histogram_tester;
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
+
+ // App in foreground, Tab in background.
+ data_use_measurement_.OnBeforeURLRequest(request.get());
+ data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
+ data_use_measurement_.OnNetworkBytesSent(*request, 100);
+
+ histogram_tester.ExpectTotalCount(
+ "DataUse.AppTabState.Upstream.AppForeground.TabBackground", 1);
+ histogram_tester.ExpectTotalCount(
+ "DataUse.AppTabState.Downstream.AppForeground.TabBackground", 1);
+
+ // App and Tab in foreground.
+ ascriber_.SetTabVisibility(true);
+ data_use_measurement_.OnBeforeURLRequest(request.get());
+ data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
+ data_use_measurement_.OnNetworkBytesSent(*request, 100);
+
+ histogram_tester.ExpectTotalCount(
+ "DataUse.AppTabState.Upstream.AppForeground.TabForeground", 1);
+ histogram_tester.ExpectTotalCount(
+ "DataUse.AppTabState.Downstream.AppForeground.TabForeground", 1);
+
+ // App and Tab in background.
+ data_use_measurement()->OnApplicationStateChangeForTesting(
+ base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES);
+ ascriber_.SetTabVisibility(false);
+ data_use_measurement_.OnBeforeURLRequest(request.get());
+ // First network access changes the app state to UNKNOWN and the next nextwork
+ // access changes to BACKGROUND.
+ data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
+ data_use_measurement_.OnNetworkBytesSent(*request, 100);
+
+ histogram_tester.ExpectTotalCount(
+ "DataUse.AppTabState.Upstream.AppBackground", 1);
+ histogram_tester.ExpectTotalCount(
+ "DataUse.AppTabState.Downstream.AppBackground", 1);
+}
+
+TEST_F(DataUseMeasurementTest, ContentType) {
+ {
+ base::HistogramTester histogram_tester;
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
+ data_use_measurement_.OnBeforeURLRequest(request.get());
+ data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
+ histogram_tester.ExpectUniqueSample("DataUse.ContentType.UserTraffic",
+ DataUseUserData::OTHER, 1000);
+ }
+
+ {
+ base::HistogramTester histogram_tester;
+ std::unique_ptr<net::URLRequest> request =
+ CreateTestRequest(kServiceRequest);
+ data_use_measurement_.OnBeforeURLRequest(request.get());
+ data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
+ histogram_tester.ExpectUniqueSample("DataUse.ContentType.Services",
+ DataUseUserData::OTHER, 1000);
+ }
+
+ // Video request in foreground.
+ {
+ base::HistogramTester histogram_tester;
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
+
+ ascriber_.SetTabVisibility(true);
+ url_request_classifier_->set_content_type(DataUseUserData::VIDEO);
+ data_use_measurement_.OnBeforeURLRequest(request.get());
+ data_use_measurement_.OnHeadersReceived(request.get(), nullptr);
+ data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
+
+ histogram_tester.ExpectUniqueSample("DataUse.ContentType.UserTraffic",
+ DataUseUserData::VIDEO, 1000);
+ }
+
+ // Audio request from background tab.
+ {
+ base::HistogramTester histogram_tester;
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
+ ascriber_.SetTabVisibility(false);
+ url_request_classifier_->set_content_type(DataUseUserData::AUDIO);
+ data_use_measurement_.OnBeforeURLRequest(request.get());
+ data_use_measurement_.OnHeadersReceived(request.get(), nullptr);
+ data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
+
+ histogram_tester.ExpectUniqueSample("DataUse.ContentType.UserTraffic",
+ DataUseUserData::AUDIO_TABBACKGROUND,
+ 1000);
+ }
+
+ // Video request from background app.
+ {
+ base::HistogramTester histogram_tester;
+ std::unique_ptr<net::URLRequest> request = CreateTestRequest(kUserRequest);
+ url_request_classifier_->set_content_type(DataUseUserData::VIDEO);
+ data_use_measurement()->OnApplicationStateChangeForTesting(
+ base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES);
+ ascriber_.SetTabVisibility(false);
+ data_use_measurement_.OnBeforeURLRequest(request.get());
+ data_use_measurement_.OnHeadersReceived(request.get(), nullptr);
+ data_use_measurement_.OnNetworkBytesReceived(*request, 1000);
+
+ histogram_tester.ExpectUniqueSample("DataUse.ContentType.UserTraffic",
+ DataUseUserData::VIDEO_APPBACKGROUND,
+ 1000);
+ }
+}
+
#endif
} // namespace data_use_measurement
diff --git a/chromium/components/data_use_measurement/core/data_use_network_delegate.cc b/chromium/components/data_use_measurement/core/data_use_network_delegate.cc
index 5e80c1f0e4c..0229787ffa2 100644
--- a/chromium/components/data_use_measurement/core/data_use_network_delegate.cc
+++ b/chromium/components/data_use_measurement/core/data_use_network_delegate.cc
@@ -20,7 +20,8 @@ DataUseNetworkDelegate::DataUseNetworkDelegate(
: net::LayeredNetworkDelegate(std::move(nested_network_delegate)),
ascriber_(ascriber),
data_use_measurement_(std::move(url_request_classifier),
- metrics_data_use_forwarder) {
+ metrics_data_use_forwarder,
+ ascriber_) {
DCHECK(ascriber);
}
@@ -37,10 +38,18 @@ void DataUseNetworkDelegate::OnBeforeURLRequestInternal(
void DataUseNetworkDelegate::OnBeforeRedirectInternal(
net::URLRequest* request,
const GURL& new_location) {
- ascriber_->OnBeforeRedirect(request, new_location);
data_use_measurement_.OnBeforeRedirect(*request, new_location);
}
+void DataUseNetworkDelegate::OnHeadersReceivedInternal(
+ net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ const net::HttpResponseHeaders* original_response_headers,
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) {
+ data_use_measurement_.OnHeadersReceived(request, original_response_headers);
+}
+
void DataUseNetworkDelegate::OnNetworkBytesReceivedInternal(
net::URLRequest* request,
int64_t bytes_received) {
@@ -55,14 +64,14 @@ void DataUseNetworkDelegate::OnNetworkBytesSentInternal(
data_use_measurement_.OnNetworkBytesSent(*request, bytes_sent);
}
-void DataUseNetworkDelegate::OnURLRequestDestroyedInternal(
- net::URLRequest* request) {
- ascriber_->OnUrlRequestDestroyed(request);
-}
-
void DataUseNetworkDelegate::OnCompletedInternal(net::URLRequest* request,
bool started) {
data_use_measurement_.OnCompleted(*request, started);
}
+void DataUseNetworkDelegate::OnURLRequestDestroyedInternal(
+ net::URLRequest* request) {
+ ascriber_->OnUrlRequestDestroyed(request);
+}
+
} // namespace data_use_measurement
diff --git a/chromium/components/data_use_measurement/core/data_use_network_delegate.h b/chromium/components/data_use_measurement/core/data_use_network_delegate.h
index 1aea4e6a5da..014c683d71b 100644
--- a/chromium/components/data_use_measurement/core/data_use_network_delegate.h
+++ b/chromium/components/data_use_measurement/core/data_use_network_delegate.h
@@ -45,6 +45,13 @@ class DataUseNetworkDelegate : public net::LayeredNetworkDelegate {
void OnBeforeRedirectInternal(net::URLRequest* request,
const GURL& new_location) override;
+ void OnHeadersReceivedInternal(
+ net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ const net::HttpResponseHeaders* original_response_headers,
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) override;
+
void OnNetworkBytesReceivedInternal(net::URLRequest* request,
int64_t bytes_received) override;
diff --git a/chromium/components/data_use_measurement/core/data_use_network_delegate_unittest.cc b/chromium/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
index b671935b00c..53d585b1230 100644
--- a/chromium/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
+++ b/chromium/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
@@ -4,6 +4,7 @@
#include "components/data_use_measurement/core/data_use_network_delegate.h"
+#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/histogram_tester.h"
@@ -20,8 +21,8 @@ namespace data_use_measurement {
namespace {
-class UserRequestUserDataForTesting : public base::SupportsUserData::Data,
- public URLRequestClassifier {
+class TestURLRequestClassifier : public base::SupportsUserData::Data,
+ public URLRequestClassifier {
public:
static const void* const kUserDataKey;
@@ -30,7 +31,13 @@ class UserRequestUserDataForTesting : public base::SupportsUserData::Data,
}
static void MarkAsUserRequest(net::URLRequest* request) {
- request->SetUserData(kUserDataKey, new UserRequestUserDataForTesting());
+ request->SetUserData(kUserDataKey, new TestURLRequestClassifier());
+ }
+
+ DataUseUserData::DataUseContentType GetContentType(
+ const net::URLRequest& request,
+ const net::HttpResponseHeaders& response_headers) const override {
+ return DataUseUserData::OTHER;
}
};
@@ -38,7 +45,11 @@ class TestDataUseAscriber : public DataUseAscriber {
public:
TestDataUseAscriber() {}
- DataUseRecorder* GetDataUseRecorder(net::URLRequest* request) override {
+ DataUseRecorder* GetOrCreateDataUseRecorder(
+ net::URLRequest* request) override {
+ return nullptr;
+ }
+ DataUseRecorder* GetDataUseRecorder(const net::URLRequest& request) override {
return nullptr;
}
@@ -49,8 +60,8 @@ class TestDataUseAscriber : public DataUseAscriber {
};
// static
-const void* const UserRequestUserDataForTesting::kUserDataKey =
- &UserRequestUserDataForTesting::kUserDataKey;
+const void* const TestURLRequestClassifier::kUserDataKey =
+ &TestURLRequestClassifier::kUserDataKey;
// This function requests a URL, and makes it return a known response. If
// |from_user| is true, it attaches a ResourceRequestInfo to the URLRequest,
@@ -87,7 +98,7 @@ std::unique_ptr<net::URLRequest> RequestURL(
GURL("http://example.com"), net::DEFAULT_PRIORITY, &test_delegate));
if (from_user) {
- UserRequestUserDataForTesting::MarkAsUserRequest(request.get());
+ TestURLRequestClassifier::MarkAsUserRequest(request.get());
} else {
request->SetUserData(
data_use_measurement::DataUseUserData::kUserDataKey,
@@ -104,11 +115,10 @@ class DataUseNetworkDelegateTest : public testing::Test {
public:
DataUseNetworkDelegateTest()
: context_(true),
- data_use_network_delegate_(
- base::MakeUnique<net::TestNetworkDelegate>(),
- &test_data_use_ascriber_,
- base::MakeUnique<UserRequestUserDataForTesting>(),
- metrics::UpdateUsagePrefCallbackType()) {
+ data_use_network_delegate_(base::MakeUnique<net::TestNetworkDelegate>(),
+ &test_data_use_ascriber_,
+ base::MakeUnique<TestURLRequestClassifier>(),
+ metrics::UpdateUsagePrefCallbackType()) {
context_.set_client_socket_factory(&mock_socket_factory_);
context_.set_network_delegate(&data_use_network_delegate_);
context_.Init();
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 ce45d1b7101..4ba402c9502 100644
--- a/chromium/components/data_use_measurement/core/data_use_recorder.cc
+++ b/chromium/components/data_use_measurement/core/data_use_recorder.cc
@@ -8,7 +8,8 @@
namespace data_use_measurement {
-DataUseRecorder::DataUseRecorder() {}
+DataUseRecorder::DataUseRecorder()
+ : main_url_request_(nullptr), is_visible_(false) {}
DataUseRecorder::~DataUseRecorder() {}
@@ -16,7 +17,7 @@ bool DataUseRecorder::IsDataUseComplete() {
return pending_url_requests_.empty() && pending_data_sources_.empty();
}
-void DataUseRecorder::OnBeforeUrlRequest(net::URLRequest* request) {
+void DataUseRecorder::AddPendingURLRequest(net::URLRequest* request) {
pending_url_requests_.insert(request);
}
@@ -24,6 +25,12 @@ void DataUseRecorder::OnUrlRequestDestroyed(net::URLRequest* request) {
pending_url_requests_.erase(request);
}
+void DataUseRecorder::RemoveAllPendingURLRequests() {
+ pending_url_requests_.clear();
+}
+
+void DataUseRecorder::OnBeforeUrlRequest(net::URLRequest* request) {}
+
void DataUseRecorder::OnNetworkBytesReceived(net::URLRequest* request,
int64_t bytes_received) {
data_use_.total_bytes_received_ += bytes_received;
@@ -46,8 +53,12 @@ void DataUseRecorder::RemovePendingDataSource(void* source) {
pending_data_sources_.erase(source);
}
-bool DataUseRecorder::HasPendingURLRequest(const net::URLRequest* request) {
+bool DataUseRecorder::HasPendingURLRequest(net::URLRequest* request) {
return pending_url_requests_.find(request) != pending_url_requests_.end();
}
+void DataUseRecorder::MergeFrom(DataUseRecorder* other) {
+ data_use_.MergeFrom(other->data_use());
+}
+
} // 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 f9888cbbbfb..8a26da67f02 100644
--- a/chromium/components/data_use_measurement/core/data_use_recorder.h
+++ b/chromium/components/data_use_measurement/core/data_use_recorder.h
@@ -26,15 +26,45 @@ namespace data_use_measurement {
class DataUseRecorder {
public:
DataUseRecorder();
- ~DataUseRecorder();
+ 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) {
+ main_url_request_ = request;
+ }
+
+ bool is_visible() const { return is_visible_; }
+
+ void set_is_visible(bool is_visible) { is_visible_ = is_visible; }
// Returns whether data use is complete and no additional data can be used
// by the entity tracked by this recorder. For example,
bool IsDataUseComplete();
+ // Adds |request| to the list of pending URLRequests that ascribe data use to
+ // this recorder.
+ void AddPendingURLRequest(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
@@ -45,16 +75,6 @@ class DataUseRecorder {
bool HasPendingDataSource(void* source);
void RemovePendingDataSource(void* source);
- // Returns whether there are any pending URLRequests whose data use is tracked
- // by this DataUseRecorder.
- bool HasPendingURLRequest(const net::URLRequest* request);
-
- // Method to merge another DataUseRecorder to this instance.
- void MergeWith(DataUseRecorder* other);
-
- private:
- friend class DataUseAscriber;
-
// Network Delegate methods:
void OnBeforeUrlRequest(net::URLRequest* request);
void OnUrlRequestDestroyed(net::URLRequest* request);
@@ -62,15 +82,22 @@ class DataUseRecorder {
void OnNetworkBytesReceived(net::URLRequest* request, int64_t bytes_received);
// Pending URLRequests whose data is being tracked by this DataUseRecorder.
- base::hash_set<const net::URLRequest*> pending_url_requests_;
+ base::hash_set<net::URLRequest*> pending_url_requests_;
// Data sources other than URLRequests, whose data is being tracked by this
// DataUseRecorder.
base::hash_set<const void*> pending_data_sources_;
+ // The main frame URLRequest for page loads. Null if this is not tracking a
+ // page load.
+ const net::URLRequest* main_url_request_;
+
// The network data use measured by this DataUseRecorder.
DataUse data_use_;
+ // Whether the entity that owns this data use is currently visible.
+ bool is_visible_;
+
DISALLOW_COPY_AND_ASSIGN(DataUseRecorder);
};
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 6c9681661d0..ae86b2e3c59 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
@@ -29,7 +29,9 @@ DataUseUserData::AppState GetCurrentAppState() {
} // namespace
DataUseUserData::DataUseUserData(ServiceName service_name, AppState app_state)
- : service_name_(service_name), app_state_(app_state) {}
+ : service_name_(service_name),
+ app_state_(app_state),
+ content_type_(DataUseContentType::OTHER) {}
DataUseUserData::~DataUseUserData() {}
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 cfae6b9b75c..99ba656b67b 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
@@ -52,6 +52,27 @@ class DataUseUserData : public base::SupportsUserData::Data {
UPDATE_CLIENT,
};
+ // Data use broken by content type. This enum must remain synchronized
+ // with the enum of the same name in metrics/histograms/histograms.xml.
+ // These values are written to logs. New enum values can be added, but
+ // existing enums must never be renumbered or deleted and reused.
+ enum DataUseContentType {
+ OTHER = 0,
+ MAIN_FRAME_HTML = 1,
+ NON_MAIN_FRAME_HTML = 2,
+ CSS = 3,
+ IMAGE = 4,
+ JAVASCRIPT = 5,
+ FONT = 6,
+ AUDIO_APPBACKGROUND = 7,
+ AUDIO_TABBACKGROUND = 8,
+ AUDIO = 9,
+ VIDEO_APPBACKGROUND = 10,
+ VIDEO_TABBACKGROUND = 11,
+ VIDEO = 12,
+ TYPE_MAX = 13,
+ };
+
// The state of the application. Only available on Android and on other
// platforms it is always FOREGROUND.
enum AppState { UNKNOWN, BACKGROUND, FOREGROUND };
@@ -78,6 +99,12 @@ class DataUseUserData : public base::SupportsUserData::Data {
void set_app_state(AppState app_state) { app_state_ = app_state; }
+ DataUseContentType content_type() { return content_type_; }
+
+ void set_content_type(DataUseContentType content_type) {
+ content_type_ = content_type;
+ }
+
// The key for retrieving back this type of user data.
static const void* const kUserDataKey;
@@ -87,6 +114,8 @@ class DataUseUserData : public base::SupportsUserData::Data {
// App state when network access was performed for the request previously.
AppState app_state_;
+ DataUseContentType content_type_;
+
DISALLOW_COPY_AND_ASSIGN(DataUseUserData);
};
diff --git a/chromium/components/data_use_measurement/core/url_request_classifier.h b/chromium/components/data_use_measurement/core/url_request_classifier.h
index 4714a10872e..00dc4108054 100644
--- a/chromium/components/data_use_measurement/core/url_request_classifier.h
+++ b/chromium/components/data_use_measurement/core/url_request_classifier.h
@@ -5,7 +5,10 @@
#ifndef COMPONENTS_DATA_USE_MEASUREMENT_CORE_URL_REQUEST_CLASSIFIER_H_
#define COMPONENTS_DATA_USE_MEASUREMENT_CORE_URL_REQUEST_CLASSIFIER_H_
+#include "components/data_use_measurement/core/data_use_user_data.h"
+
namespace net {
+class HttpResponseHeaders;
class URLRequest;
}
@@ -18,6 +21,13 @@ class URLRequestClassifier {
// Returns true if the URLRequest |request| is initiated by user traffic.
virtual bool IsUserRequest(const net::URLRequest& request) const = 0;
+
+ // Returns the content type of the URL request |request| with response headers
+ // |response_headers|. |is_app_foreground| and |is_tab_visible| indicate the
+ // current app and tab visibility state.
+ virtual DataUseUserData::DataUseContentType GetContentType(
+ const net::URLRequest& request,
+ const net::HttpResponseHeaders& response_headers) const = 0;
};
} // namespace data_use_measurement
diff --git a/chromium/components/device_event_log/OWNERS b/chromium/components/device_event_log/OWNERS
index d313f841d32..baa95dfb004 100644
--- a/chromium/components/device_event_log/OWNERS
+++ b/chromium/components/device_event_log/OWNERS
@@ -1,2 +1,4 @@
stevenjb@chromium.org
reillyg@chromium.org
+
+# COMPONENT: Internals>Logging \ No newline at end of file
diff --git a/chromium/components/discardable_memory/DEPS b/chromium/components/discardable_memory/DEPS
new file mode 100644
index 00000000000..093b1d9fde5
--- /dev/null
+++ b/chromium/components/discardable_memory/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/public/cpp",
+]
diff --git a/chromium/components/discardable_memory/client/BUILD.gn b/chromium/components/discardable_memory/client/BUILD.gn
index 583a8ee11b8..8cf73572b80 100644
--- a/chromium/components/discardable_memory/client/BUILD.gn
+++ b/chromium/components/discardable_memory/client/BUILD.gn
@@ -17,5 +17,6 @@ component("client") {
deps = [
"//base",
"//components/discardable_memory/common",
+ "//components/discardable_memory/public/interfaces",
]
}
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 22239e13869..cd768d0f2d6 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
@@ -21,9 +21,11 @@
#include "base/process/process_metrics.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_event.h"
+#include "mojo/public/cpp/system/platform_handle.h"
namespace discardable_memory {
namespace {
@@ -82,29 +84,52 @@ class DiscardableMemoryImpl : public base::DiscardableMemory {
DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryImpl);
};
-void SendDeletedDiscardableSharedMemoryMessage(
- ClientDiscardableSharedMemoryManager::Delegate* delegate,
- DiscardableSharedMemoryId id) {
- delegate->DeletedDiscardableSharedMemory(id);
+void InitManagerMojoOnIO(mojom::DiscardableSharedMemoryManagerPtr* manager_mojo,
+ mojom::DiscardableSharedMemoryManagerPtrInfo info) {
+ manager_mojo->Bind(std::move(info));
+}
+
+void DeletedDiscardableSharedMemoryOnIO(
+ mojom::DiscardableSharedMemoryManagerPtr* manager_mojo,
+ int32_t id) {
+ (*manager_mojo)->DeletedDiscardableSharedMemory(id);
}
} // namespace
ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager(
- Delegate* delegate)
- : heap_(base::GetPageSize()), delegate_(delegate) {
+ mojom::DiscardableSharedMemoryManagerPtr manager,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+ : io_task_runner_(std::move(io_task_runner)),
+ manager_mojo_(new mojom::DiscardableSharedMemoryManagerPtr),
+ heap_(new DiscardableSharedMemoryHeap(base::GetPageSize())) {
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "ClientDiscardableSharedMemoryManager",
base::ThreadTaskRunnerHandle::Get());
+ mojom::DiscardableSharedMemoryManagerPtrInfo info = manager.PassInterface();
+ io_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&InitManagerMojoOnIO, manager_mojo_.get(),
+ base::Passed(&info)));
}
ClientDiscardableSharedMemoryManager::~ClientDiscardableSharedMemoryManager() {
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this);
// TODO(reveman): Determine if this DCHECK can be enabled. crbug.com/430533
- // DCHECK_EQ(heap_.GetSize(), heap_.GetSizeOfFreeLists());
- if (heap_.GetSize())
+ // DCHECK_EQ(heap_->GetSize(), heap_->GetSizeOfFreeLists());
+ if (heap_->GetSize())
MemoryUsageChanged(0, 0);
+
+ // Releasing the |heap_| before posting a task for deleting |manager_mojo_|.
+ // It is because releasing |heap_| will invoke DeletedDiscardableSharedMemory
+ // which needs |manager_mojo_|.
+ heap_.reset();
+
+ // Delete the |manager_mojo_| on IO thread, so any pending tasks on IO thread
+ // will be executed before the |manager_mojo_| is deleted.
+ bool posted = io_task_runner_->DeleteSoon(FROM_HERE, manager_mojo_.release());
+ if (!posted)
+ manager_mojo_.reset();
}
std::unique_ptr<base::DiscardableMemory>
@@ -138,11 +163,11 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
if (pages < allocation_pages)
slack = allocation_pages - pages;
- size_t heap_size_prior_to_releasing_purged_memory = heap_.GetSize();
+ size_t heap_size_prior_to_releasing_purged_memory = heap_->GetSize();
for (;;) {
// Search free lists for suitable span.
std::unique_ptr<DiscardableSharedMemoryHeap::Span> free_span =
- heap_.SearchFreeLists(pages, slack);
+ heap_->SearchFreeLists(pages, slack);
if (!free_span.get())
break;
@@ -155,7 +180,7 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
base::DiscardableSharedMemory::FAILED) {
DCHECK(!free_span->shared_memory()->IsMemoryResident());
// We have to release purged memory before |free_span| can be destroyed.
- heap_.ReleasePurgedMemory();
+ heap_->ReleasePurgedMemory();
DCHECK(!free_span->shared_memory());
continue;
}
@@ -164,50 +189,53 @@ ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
// Memory usage is guaranteed to have changed after having removed
// at least one span from the free lists.
- MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
+ MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists());
return base::MakeUnique<DiscardableMemoryImpl>(this, std::move(free_span));
}
// Release purged memory to free up the address space before we attempt to
// allocate more memory.
- heap_.ReleasePurgedMemory();
+ heap_->ReleasePurgedMemory();
// Make sure crash keys are up to date in case allocation fails.
- if (heap_.GetSize() != heap_size_prior_to_releasing_purged_memory)
- MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
+ if (heap_->GetSize() != heap_size_prior_to_releasing_purged_memory)
+ MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists());
size_t pages_to_allocate =
std::max(kAllocationSize / base::GetPageSize(), pages);
size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize();
- DiscardableSharedMemoryId new_id =
- g_next_discardable_shared_memory_id.GetNext();
+ int32_t new_id = g_next_discardable_shared_memory_id.GetNext();
// Ask parent process to allocate a new discardable shared memory segment.
- std::unique_ptr<base::DiscardableSharedMemory> shared_memory(
- AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes, new_id));
+ std::unique_ptr<base::DiscardableSharedMemory> shared_memory =
+ AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes, new_id);
// Create span for allocated memory.
- std::unique_ptr<DiscardableSharedMemoryHeap::Span> new_span(
- heap_.Grow(std::move(shared_memory), allocation_size_in_bytes, new_id,
- base::Bind(&SendDeletedDiscardableSharedMemoryMessage,
- delegate_, new_id)));
+ // Spans are managed by |heap_| (the member of
+ // the ClientDiscardableSharedMemoryManager), so it is safe to use
+ // base::Unretained(this) here.
+ std::unique_ptr<DiscardableSharedMemoryHeap::Span> new_span(heap_->Grow(
+ std::move(shared_memory), allocation_size_in_bytes, new_id,
+ base::Bind(
+ &ClientDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory,
+ base::Unretained(this), new_id)));
new_span->set_is_locked(true);
// Unlock and insert any left over memory into free lists.
if (pages < pages_to_allocate) {
std::unique_ptr<DiscardableSharedMemoryHeap::Span> leftover =
- heap_.Split(new_span.get(), pages);
+ heap_->Split(new_span.get(), pages);
leftover->shared_memory()->Unlock(
leftover->start() * base::GetPageSize() -
reinterpret_cast<size_t>(leftover->shared_memory()->memory()),
leftover->length() * base::GetPageSize());
leftover->set_is_locked(false);
- heap_.MergeIntoFreeLists(std::move(leftover));
+ heap_->MergeIntoFreeLists(std::move(leftover));
}
- MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
+ MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists());
return base::MakeUnique<DiscardableMemoryImpl>(this, std::move(new_span));
}
@@ -222,8 +250,8 @@ bool ClientDiscardableSharedMemoryManager::OnMemoryDump(
pmd->CreateAllocatorDump(
base::StringPrintf("discardable/child_0x%" PRIXPTR,
reinterpret_cast<uintptr_t>(this)));
- const size_t total_size = heap_.GetSize();
- const size_t freelist_size = heap_.GetSizeOfFreeLists();
+ const size_t total_size = heap_->GetSize();
+ const size_t freelist_size = heap_->GetSizeOfFreeLists();
total_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes,
total_size - freelist_size);
@@ -233,29 +261,29 @@ bool ClientDiscardableSharedMemoryManager::OnMemoryDump(
return true;
}
- return heap_.OnMemoryDump(pmd);
+ return heap_->OnMemoryDump(pmd);
}
ClientDiscardableSharedMemoryManager::Statistics
ClientDiscardableSharedMemoryManager::GetStatistics() const {
base::AutoLock lock(lock_);
Statistics stats;
- stats.total_size = heap_.GetSize();
- stats.freelist_size = heap_.GetSizeOfFreeLists();
+ stats.total_size = heap_->GetSize();
+ stats.freelist_size = heap_->GetSizeOfFreeLists();
return stats;
}
void ClientDiscardableSharedMemoryManager::ReleaseFreeMemory() {
base::AutoLock lock(lock_);
- size_t heap_size_prior_to_releasing_memory = heap_.GetSize();
+ size_t heap_size_prior_to_releasing_memory = heap_->GetSize();
// Release both purged and free memory.
- heap_.ReleasePurgedMemory();
- heap_.ReleaseFreeMemory();
+ heap_->ReleasePurgedMemory();
+ heap_->ReleaseFreeMemory();
- if (heap_.GetSize() != heap_size_prior_to_releasing_memory)
- MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
+ if (heap_->GetSize() != heap_size_prior_to_releasing_memory)
+ MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists());
}
bool ClientDiscardableSharedMemoryManager::LockSpan(
@@ -306,10 +334,10 @@ void ClientDiscardableSharedMemoryManager::ReleaseSpan(
if (!span->shared_memory())
return;
- heap_.MergeIntoFreeLists(std::move(span));
+ heap_->MergeIntoFreeLists(std::move(span));
// Bytes of free memory changed.
- MemoryUsageChanged(heap_.GetSize(), heap_.GetSizeOfFreeLists());
+ MemoryUsageChanged(heap_->GetSize(), heap_->GetSizeOfFreeLists());
}
base::trace_event::MemoryAllocatorDump*
@@ -318,27 +346,67 @@ ClientDiscardableSharedMemoryManager::CreateMemoryAllocatorDump(
const char* name,
base::trace_event::ProcessMemoryDump* pmd) const {
base::AutoLock lock(lock_);
- return heap_.CreateMemoryAllocatorDump(span, name, pmd);
+ return heap_->CreateMemoryAllocatorDump(span, name, pmd);
}
std::unique_ptr<base::DiscardableSharedMemory>
ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
size_t size,
- DiscardableSharedMemoryId id) {
+ int32_t id) {
TRACE_EVENT2("renderer",
"ClientDiscardableSharedMemoryManager::"
"AllocateLockedDiscardableSharedMemory",
"size", size, "id", id);
-
- base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle();
- delegate_->AllocateLockedDiscardableSharedMemory(size, id, &handle);
- std::unique_ptr<base::DiscardableSharedMemory> memory(
- new base::DiscardableSharedMemory(handle));
+ base::SharedMemoryHandle handle;
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ base::ScopedClosureRunner event_signal_runner(
+ base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)));
+ io_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ClientDiscardableSharedMemoryManager::AllocateOnIO,
+ base::Unretained(this), size, id, &handle,
+ base::Passed(&event_signal_runner)));
+ // Waiting until IPC has finished on the IO thread.
+ event.Wait();
+ auto memory = base::MakeUnique<base::DiscardableSharedMemory>(handle);
if (!memory->Map(size))
base::TerminateBecauseOutOfMemory(size);
return memory;
}
+void ClientDiscardableSharedMemoryManager::AllocateOnIO(
+ size_t size,
+ int32_t id,
+ base::SharedMemoryHandle* handle,
+ base::ScopedClosureRunner closure_runner) {
+ (*manager_mojo_)
+ ->AllocateLockedDiscardableSharedMemory(
+ static_cast<uint32_t>(size), id,
+ base::Bind(
+ &ClientDiscardableSharedMemoryManager::AllocateCompletedOnIO,
+ base::Unretained(this), handle, base::Passed(&closure_runner)));
+}
+
+void ClientDiscardableSharedMemoryManager::AllocateCompletedOnIO(
+ base::SharedMemoryHandle* handle,
+ base::ScopedClosureRunner closure_runner,
+ mojo::ScopedSharedBufferHandle mojo_handle) {
+ size_t memory_size = 0;
+ bool read_only = false;
+ if (!mojo_handle.is_valid())
+ return;
+ auto result = mojo::UnwrapSharedMemoryHandle(std::move(mojo_handle), handle,
+ &memory_size, &read_only);
+ DCHECK_EQ(result, MOJO_RESULT_OK);
+}
+
+void ClientDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory(
+ int32_t id) {
+ io_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&DeletedDiscardableSharedMemoryOnIO, manager_mojo_.get(), id));
+}
+
void ClientDiscardableSharedMemoryManager::MemoryUsageChanged(
size_t new_bytes_total,
size_t new_bytes_free) const {
diff --git a/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.h b/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.h
index 76215bc099f..c6b375bdaa2 100644
--- a/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.h
+++ b/chromium/components/discardable_memory/client/client_discardable_shared_memory_manager.h
@@ -7,6 +7,7 @@
#include <stddef.h>
+#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/memory/discardable_memory_allocator.h"
#include "base/memory/ref_counted.h"
@@ -15,7 +16,11 @@
#include "base/trace_event/memory_dump_provider.h"
#include "components/discardable_memory/common/discardable_memory_export.h"
#include "components/discardable_memory/common/discardable_shared_memory_heap.h"
-#include "components/discardable_memory/common/discardable_shared_memory_id.h"
+#include "components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
namespace discardable_memory {
@@ -25,20 +30,9 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager
: public base::DiscardableMemoryAllocator,
public base::trace_event::MemoryDumpProvider {
public:
- class Delegate {
- public:
- virtual void AllocateLockedDiscardableSharedMemory(
- size_t size,
- DiscardableSharedMemoryId id,
- base::SharedMemoryHandle* handle) = 0;
- virtual void DeletedDiscardableSharedMemory(
- DiscardableSharedMemoryId id) = 0;
-
- protected:
- virtual ~Delegate() {}
- };
-
- explicit ClientDiscardableSharedMemoryManager(Delegate* delegate);
+ ClientDiscardableSharedMemoryManager(
+ mojom::DiscardableSharedMemoryManagerPtr manager,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
~ClientDiscardableSharedMemoryManager() override;
// Overridden from base::DiscardableMemoryAllocator:
@@ -70,14 +64,26 @@ class DISCARDABLE_MEMORY_EXPORT ClientDiscardableSharedMemoryManager
private:
std::unique_ptr<base::DiscardableSharedMemory>
- AllocateLockedDiscardableSharedMemory(size_t size,
- DiscardableSharedMemoryId id);
+ AllocateLockedDiscardableSharedMemory(size_t size, int32_t id);
+ void AllocateOnIO(size_t size,
+ int32_t id,
+ base::SharedMemoryHandle* handle,
+ base::ScopedClosureRunner closure_runner);
+ void AllocateCompletedOnIO(base::SharedMemoryHandle* handle,
+ base::ScopedClosureRunner closure_runner,
+ mojo::ScopedSharedBufferHandle mojo_handle);
+
+ void DeletedDiscardableSharedMemory(int32_t id);
void MemoryUsageChanged(size_t new_bytes_allocated,
size_t new_bytes_free) const;
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+ // TODO(penghuang): Switch to ThreadSafeInterfacePtr when it starts supporting
+ // sync method call.
+ std::unique_ptr<mojom::DiscardableSharedMemoryManagerPtr> manager_mojo_;
+
mutable base::Lock lock_;
- DiscardableSharedMemoryHeap heap_;
- Delegate* const delegate_;
+ std::unique_ptr<DiscardableSharedMemoryHeap> heap_;
DISALLOW_COPY_AND_ASSIGN(ClientDiscardableSharedMemoryManager);
};
diff --git a/chromium/components/discardable_memory/common/BUILD.gn b/chromium/components/discardable_memory/common/BUILD.gn
index 9450c87a8c4..9d9ffae80a8 100644
--- a/chromium/components/discardable_memory/common/BUILD.gn
+++ b/chromium/components/discardable_memory/common/BUILD.gn
@@ -13,7 +13,6 @@ component("common") {
"discardable_memory_export.h",
"discardable_shared_memory_heap.cc",
"discardable_shared_memory_heap.h",
- "discardable_shared_memory_id.h",
]
deps = [
diff --git a/chromium/components/discardable_memory/common/discardable_shared_memory_id.h b/chromium/components/discardable_memory/common/discardable_shared_memory_id.h
deleted file mode 100644
index 53f98a57a34..00000000000
--- a/chromium/components/discardable_memory/common/discardable_shared_memory_id.h
+++ /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.
-
-#ifndef COMPONENTS_DISCARDABLE_MEMORY_COMMON_DISCARDABLE_SHARED_MEMORY_ID_H_
-#define COMPONENTS_DISCARDABLE_MEMORY_COMMON_DISCARDABLE_SHARED_MEMORY_ID_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-namespace discardable_memory {
-
-typedef int32_t DiscardableSharedMemoryId;
-
-} // namespace discardable_memory
-
-#endif // COMPONENTS_DISCARDABLE_MEMORY_COMMON_DISCARDABLE_SHARED_MEMORY_ID_H_
diff --git a/chromium/components/discardable_memory/public/interfaces/BUILD.gn b/chromium/components/discardable_memory/public/interfaces/BUILD.gn
new file mode 100644
index 00000000000..0ce0a31e416
--- /dev/null
+++ b/chromium/components/discardable_memory/public/interfaces/BUILD.gn
@@ -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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+import("//testing/test.gni")
+
+mojom("interfaces") {
+ sources = [
+ "discardable_shared_memory_manager.mojom",
+ ]
+
+ import_dirs = [
+ get_path_info("../../../..", "abspath"),
+ "//mojo/services",
+ ]
+}
diff --git a/chromium/components/discardable_memory/public/interfaces/OWNERS b/chromium/components/discardable_memory/public/interfaces/OWNERS
new file mode 100644
index 00000000000..08850f42120
--- /dev/null
+++ b/chromium/components/discardable_memory/public/interfaces/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom b/chromium/components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom
new file mode 100644
index 00000000000..9d291d82d22
--- /dev/null
+++ b/chromium/components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom
@@ -0,0 +1,16 @@
+// 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 discardable_memory.mojom;
+
+// This interface is used for allocating discardable shared memory from browser
+// process. For mus+ash, this service will live in mus process.
+interface DiscardableSharedMemoryManager {
+ // Allocate a locked discardable shared memory segment.
+ AllocateLockedDiscardableSharedMemory(
+ uint32 size,
+ int32 id) => (handle<shared_buffer>? memory);
+ // Notify manager that a memory segment has been deleted.
+ DeletedDiscardableSharedMemory(int32 id);
+};
diff --git a/chromium/components/discardable_memory/service/BUILD.gn b/chromium/components/discardable_memory/service/BUILD.gn
index 4061d7208df..6ac608b78df 100644
--- a/chromium/components/discardable_memory/service/BUILD.gn
+++ b/chromium/components/discardable_memory/service/BUILD.gn
@@ -17,6 +17,7 @@ component("service") {
deps = [
"//base",
"//components/discardable_memory/common",
+ "//components/discardable_memory/public/interfaces",
]
}
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 1036dc84d69..a974b2d9e27 100644
--- a/chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc
+++ b/chromium/components/discardable_memory/service/discardable_shared_memory_manager.cc
@@ -12,7 +12,6 @@
#include "base/callback.h"
#include "base/command_line.h"
#include "base/debug/crash_logging.h"
-#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/memory/discardable_memory.h"
#include "base/memory/memory_coordinator_client_registry.h"
@@ -29,6 +28,8 @@
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/discardable_memory/common/discardable_shared_memory_heap.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/system/platform_handle.h"
#if defined(OS_LINUX)
#include "base/files/file_path.h"
@@ -57,6 +58,48 @@ uint64_t ClientProcessUniqueIdToTracingProcessId(int client_id) {
1;
}
+// mojom::DiscardableSharedMemoryManager implementation. It contains the
+// |client_id_| which is not visible to client. We associate allocations with a
+// given mojo instance, so if the instance is closed, we can release the
+// allocations associated with that instance.
+class MojoDiscardableSharedMemoryManagerImpl
+ : public mojom::DiscardableSharedMemoryManager {
+ public:
+ MojoDiscardableSharedMemoryManagerImpl(
+ int32_t client_id,
+ ::discardable_memory::DiscardableSharedMemoryManager* manager)
+ : client_id_(client_id), manager_(manager) {}
+
+ ~MojoDiscardableSharedMemoryManagerImpl() override {
+ // Remove this client from the |manager_|, so all allocated discardable
+ // memory belong to this client will be released.
+ manager_->ClientRemoved(client_id_);
+ }
+
+ // mojom::DiscardableSharedMemoryManager overrides:
+ void AllocateLockedDiscardableSharedMemory(
+ uint32_t size,
+ int32_t id,
+ const AllocateLockedDiscardableSharedMemoryCallback& callback) override {
+ base::SharedMemoryHandle handle;
+ manager_->AllocateLockedDiscardableSharedMemoryForClient(client_id_, size,
+ id, &handle);
+ mojo::ScopedSharedBufferHandle memory =
+ mojo::WrapSharedMemoryHandle(handle, size, false /* read_only */);
+ return callback.Run(std::move(memory));
+ }
+
+ void DeletedDiscardableSharedMemory(int32_t id) override {
+ manager_->ClientDeletedDiscardableSharedMemory(id, client_id_);
+ }
+
+ private:
+ const int32_t client_id_;
+ ::discardable_memory::DiscardableSharedMemoryManager* const manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoDiscardableSharedMemoryManagerImpl);
+};
+
class DiscardableMemoryImpl : public base::DiscardableMemory {
public:
DiscardableMemoryImpl(
@@ -163,9 +206,6 @@ int64_t GetDefaultMemoryLimit() {
base::SysInfo::AmountOfPhysicalMemory() / 4);
}
-base::LazyInstance<DiscardableSharedMemoryManager>
- g_discardable_shared_memory_manager = LAZY_INSTANCE_INITIALIZER;
-
const int kEnforceMemoryPolicyDelayMs = 1000;
// Global atomic to generate unique discardable shared memory IDs.
@@ -180,7 +220,8 @@ DiscardableSharedMemoryManager::MemorySegment::MemorySegment(
DiscardableSharedMemoryManager::MemorySegment::~MemorySegment() {}
DiscardableSharedMemoryManager::DiscardableSharedMemoryManager()
- : default_memory_limit_(GetDefaultMemoryLimit()),
+ : next_client_id_(1),
+ default_memory_limit_(GetDefaultMemoryLimit()),
memory_limit_(default_memory_limit_),
bytes_allocated_(0),
memory_pressure_listener_(new base::MemoryPressureListener(
@@ -205,23 +246,25 @@ DiscardableSharedMemoryManager::~DiscardableSharedMemoryManager() {
this);
}
-DiscardableSharedMemoryManager* DiscardableSharedMemoryManager::current() {
- return g_discardable_shared_memory_manager.Pointer();
+void DiscardableSharedMemoryManager::Bind(
+ mojom::DiscardableSharedMemoryManagerRequest request) {
+ mojo::MakeStrongBinding(
+ base::MakeUnique<MojoDiscardableSharedMemoryManagerImpl>(
+ next_client_id_++, this),
+ std::move(request));
}
std::unique_ptr<base::DiscardableMemory>
DiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(size_t size) {
DCHECK_NE(size, 0u);
- DiscardableSharedMemoryId new_id =
- g_next_discardable_shared_memory_id.GetNext();
- base::ProcessHandle current_process_handle = base::GetCurrentProcessHandle();
+ int32_t new_id = g_next_discardable_shared_memory_id.GetNext();
// Note: Use DiscardableSharedMemoryHeap for in-process allocation
// of discardable memory if the cost of each allocation is too high.
base::SharedMemoryHandle handle;
- AllocateLockedDiscardableSharedMemory(
- current_process_handle, kInvalidUniqueClientID, size, new_id, &handle);
+ AllocateLockedDiscardableSharedMemory(kInvalidUniqueClientID, size, new_id,
+ &handle);
std::unique_ptr<base::DiscardableSharedMemory> memory(
new base::DiscardableSharedMemory(handle));
if (!memory->Map(size))
@@ -308,17 +351,16 @@ bool DiscardableSharedMemoryManager::OnMemoryDump(
void DiscardableSharedMemoryManager::
AllocateLockedDiscardableSharedMemoryForClient(
- base::ProcessHandle process_handle,
int client_id,
size_t size,
- DiscardableSharedMemoryId id,
+ int32_t id,
base::SharedMemoryHandle* shared_memory_handle) {
- AllocateLockedDiscardableSharedMemory(process_handle, client_id, size, id,
+ AllocateLockedDiscardableSharedMemory(client_id, size, id,
shared_memory_handle);
}
void DiscardableSharedMemoryManager::ClientDeletedDiscardableSharedMemory(
- DiscardableSharedMemoryId id,
+ int32_t id,
int client_id) {
DeletedDiscardableSharedMemory(id, client_id);
}
@@ -380,10 +422,9 @@ void DiscardableSharedMemoryManager::OnMemoryStateChange(
}
void DiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
- base::ProcessHandle process_handle,
int client_id,
size_t size,
- DiscardableSharedMemoryId id,
+ int32_t id,
base::SharedMemoryHandle* shared_memory_handle) {
base::AutoLock lock(lock_);
@@ -416,15 +457,6 @@ void DiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
return;
}
- if (!memory->ShareToProcess(process_handle, shared_memory_handle)) {
- LOG(ERROR) << "Cannot share discardable memory segment";
- *shared_memory_handle = base::SharedMemory::NULLHandle();
- return;
- }
-
- // Close file descriptor to avoid running out.
- memory->Close();
-
base::CheckedNumeric<size_t> checked_bytes_allocated = bytes_allocated_;
checked_bytes_allocated += memory->mapped_size();
if (!checked_bytes_allocated.IsValid()) {
@@ -435,6 +467,10 @@ void DiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
bytes_allocated_ = checked_bytes_allocated.ValueOrDie();
BytesAllocatedChanged(bytes_allocated_);
+ *shared_memory_handle = base::SharedMemory::DuplicateHandle(memory->handle());
+ // Close file descriptor to avoid running out.
+ memory->Close();
+
scoped_refptr<MemorySegment> segment(new MemorySegment(std::move(memory)));
client_segments[id] = segment.get();
segments_.push_back(segment.get());
@@ -445,7 +481,7 @@ void DiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
}
void DiscardableSharedMemoryManager::DeletedDiscardableSharedMemory(
- DiscardableSharedMemoryId id,
+ int32_t id,
int client_id) {
base::AutoLock lock(lock_);
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 7ccadf18a44..9491af4b663 100644
--- a/chromium/components/discardable_memory/service/discardable_shared_memory_manager.h
+++ b/chromium/components/discardable_memory/service/discardable_shared_memory_manager.h
@@ -27,7 +27,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_provider.h"
#include "components/discardable_memory/common/discardable_memory_export.h"
-#include "components/discardable_memory/common/discardable_shared_memory_id.h"
+#include "components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom.h"
namespace discardable_memory {
@@ -44,8 +44,8 @@ class DISCARDABLE_MEMORY_EXPORT DiscardableSharedMemoryManager
DiscardableSharedMemoryManager();
~DiscardableSharedMemoryManager() override;
- // Returns a singleton instance.
- static DiscardableSharedMemoryManager* current();
+ // Bind the manager to a mojo interface request.
+ void Bind(mojom::DiscardableSharedMemoryManagerRequest request);
// Overridden from base::DiscardableMemoryAllocator:
std::unique_ptr<base::DiscardableMemory> AllocateLockedDiscardableMemory(
@@ -55,20 +55,17 @@ class DISCARDABLE_MEMORY_EXPORT DiscardableSharedMemoryManager
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
- // TODO(penghuang): Get ride of the |process_handle| when we switch to mojo.
// This allocates a discardable memory segment for |process_handle|.
// A valid shared memory handle is returned on success.
void AllocateLockedDiscardableSharedMemoryForClient(
- base::ProcessHandle process_handle,
int client_id,
size_t size,
- DiscardableSharedMemoryId id,
+ int32_t id,
base::SharedMemoryHandle* shared_memory_handle);
// Call this to notify the manager that client process associated with
// |client_id| has deleted discardable memory segment with |id|.
- void ClientDeletedDiscardableSharedMemory(DiscardableSharedMemoryId id,
- int client_id);
+ void ClientDeletedDiscardableSharedMemory(int32_t id, int client_id);
// Call this to notify the manager that client associated with |client_id|
// has been removed. The manager will use this to release memory segments
@@ -111,15 +108,12 @@ class DISCARDABLE_MEMORY_EXPORT DiscardableSharedMemoryManager
// base::MemoryCoordinatorClient implementation:
void OnMemoryStateChange(base::MemoryState state) override;
- // TODO(penghuang): Get ride of the |process_handle| when we switch to mojo.
void AllocateLockedDiscardableSharedMemory(
- base::ProcessHandle process_handle,
int client_id,
size_t size,
- DiscardableSharedMemoryId id,
+ int32_t id,
base::SharedMemoryHandle* shared_memory_handle);
- void DeletedDiscardableSharedMemory(DiscardableSharedMemoryId id,
- int client_id);
+ void DeletedDiscardableSharedMemory(int32_t id, int client_id);
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
void ReduceMemoryUsageUntilWithinMemoryLimit();
@@ -131,15 +125,16 @@ class DISCARDABLE_MEMORY_EXPORT DiscardableSharedMemoryManager
virtual base::Time Now() const;
virtual void ScheduleEnforceMemoryPolicy();
+ int32_t next_client_id_;
+
base::Lock lock_;
- typedef base::hash_map<DiscardableSharedMemoryId,
- scoped_refptr<MemorySegment>>
- MemorySegmentMap;
- typedef base::hash_map<int, MemorySegmentMap> ClientMap;
+ using MemorySegmentMap =
+ base::hash_map<int32_t, scoped_refptr<MemorySegment>>;
+ using ClientMap = base::hash_map<int, MemorySegmentMap>;
ClientMap clients_;
// Note: The elements in |segments_| are arranged in such a way that they form
// a heap. The LRU memory segment always first.
- typedef std::vector<scoped_refptr<MemorySegment>> MemorySegmentVector;
+ using MemorySegmentVector = std::vector<scoped_refptr<MemorySegment>>;
MemorySegmentVector segments_;
size_t default_memory_limit_;
size_t memory_limit_;
diff --git a/chromium/components/discardable_memory/service/discardable_shared_memory_manager_unittest.cc b/chromium/components/discardable_memory/service/discardable_shared_memory_manager_unittest.cc
index f190736a28f..3c2992401a6 100644
--- a/chromium/components/discardable_memory/service/discardable_shared_memory_manager_unittest.cc
+++ b/chromium/components/discardable_memory/service/discardable_shared_memory_manager_unittest.cc
@@ -77,8 +77,7 @@ TEST_F(DiscardableSharedMemoryManagerTest, AllocateForClient) {
base::SharedMemoryHandle shared_handle;
manager_->AllocateLockedDiscardableSharedMemoryForClient(
- base::GetCurrentProcessHandle(), kInvalidUniqueID, kDataSize, 0,
- &shared_handle);
+ kInvalidUniqueID, kDataSize, 0, &shared_handle);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle));
TestDiscardableSharedMemory memory(shared_handle);
@@ -99,8 +98,7 @@ TEST_F(DiscardableSharedMemoryManagerTest, Purge) {
base::SharedMemoryHandle shared_handle1;
manager_->AllocateLockedDiscardableSharedMemoryForClient(
- base::GetCurrentProcessHandle(), kInvalidUniqueID, kDataSize, 1,
- &shared_handle1);
+ kInvalidUniqueID, kDataSize, 1, &shared_handle1);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle1));
TestDiscardableSharedMemory memory1(shared_handle1);
@@ -109,8 +107,7 @@ TEST_F(DiscardableSharedMemoryManagerTest, Purge) {
base::SharedMemoryHandle shared_handle2;
manager_->AllocateLockedDiscardableSharedMemoryForClient(
- base::GetCurrentProcessHandle(), kInvalidUniqueID, kDataSize, 2,
- &shared_handle2);
+ kInvalidUniqueID, kDataSize, 2, &shared_handle2);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle2));
TestDiscardableSharedMemory memory2(shared_handle2);
@@ -165,8 +162,7 @@ TEST_F(DiscardableSharedMemoryManagerTest, EnforceMemoryPolicy) {
base::SharedMemoryHandle shared_handle;
manager_->AllocateLockedDiscardableSharedMemoryForClient(
- base::GetCurrentProcessHandle(), kInvalidUniqueID, kDataSize, 0,
- &shared_handle);
+ kInvalidUniqueID, kDataSize, 0, &shared_handle);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle));
TestDiscardableSharedMemory memory(shared_handle);
@@ -204,8 +200,7 @@ TEST_F(DiscardableSharedMemoryManagerTest,
base::SharedMemoryHandle shared_handle1;
manager_->AllocateLockedDiscardableSharedMemoryForClient(
- base::GetCurrentProcessHandle(), kInvalidUniqueID, kDataSize, 1,
- &shared_handle1);
+ kInvalidUniqueID, kDataSize, 1, &shared_handle1);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle1));
TestDiscardableSharedMemory memory1(shared_handle1);
@@ -214,8 +209,7 @@ TEST_F(DiscardableSharedMemoryManagerTest,
base::SharedMemoryHandle shared_handle2;
manager_->AllocateLockedDiscardableSharedMemoryForClient(
- base::GetCurrentProcessHandle(), kInvalidUniqueID, kDataSize, 2,
- &shared_handle2);
+ kInvalidUniqueID, kDataSize, 2, &shared_handle2);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle2));
TestDiscardableSharedMemory memory2(shared_handle2);
@@ -269,8 +263,7 @@ TEST_F(DiscardableSharedMemoryManagerScheduleEnforceMemoryPolicyTest,
base::SharedMemoryHandle shared_handle;
manager_->AllocateLockedDiscardableSharedMemoryForClient(
- base::GetCurrentProcessHandle(), kInvalidUniqueID, kDataSize, 0,
- &shared_handle);
+ kInvalidUniqueID, kDataSize, 0, &shared_handle);
ASSERT_TRUE(base::SharedMemory::IsHandleValid(shared_handle));
// Set the memory limit to a value that will require EnforceMemoryPolicy()
diff --git a/chromium/components/display_compositor/DEPS b/chromium/components/display_compositor/DEPS
index 352646a7fb1..dd5fd92df3c 100644
--- a/chromium/components/display_compositor/DEPS
+++ b/chromium/components/display_compositor/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+cc/base",
"+cc/output",
"+cc/test",
"+gpu/GLES2",
diff --git a/chromium/components/display_compositor/buffer_queue.cc b/chromium/components/display_compositor/buffer_queue.cc
index fafdb406b4f..afb40e1a491 100644
--- a/chromium/components/display_compositor/buffer_queue.cc
+++ b/chromium/components/display_compositor/buffer_queue.cc
@@ -60,6 +60,10 @@ void BufferQueue::BindFramebuffer() {
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);
+ }
}
}
@@ -115,8 +119,10 @@ void BufferQueue::SwapBuffers(const gfx::Rect& damage) {
void BufferQueue::Reshape(const gfx::Size& size,
float scale_factor,
- const gfx::ColorSpace& color_space) {
- if (size == size_ && color_space == color_space_)
+ 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
@@ -126,11 +132,13 @@ void BufferQueue::Reshape(const gfx::Size& size,
#endif
size_ = size;
color_space_ = color_space;
+ use_stencil_ = use_stencil;
- // TODO: add stencil buffer when needed.
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();
}
@@ -200,6 +208,8 @@ void BufferQueue::FreeSurfaceResources(AllocatedSurface* surface) {
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_--;
}
@@ -215,10 +225,19 @@ std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::GetNextSurface() {
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_->AllocateGpuMemoryBuffer(
+ gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
size_, format_, gfx::BufferUsage::SCANOUT, surface_handle_));
if (!buffer.get()) {
gl_->DeleteTextures(1, &texture);
@@ -240,7 +259,7 @@ std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::GetNextSurface() {
gl_->BindTexture(texture_target_, texture);
gl_->BindTexImage2DCHROMIUM(texture_target_, id);
return base::MakeUnique<AllocatedSurface>(this, std::move(buffer), texture,
- id, gfx::Rect(size_));
+ id, stencil, gfx::Rect(size_));
}
BufferQueue::AllocatedSurface::AllocatedSurface(
@@ -248,11 +267,13 @@ BufferQueue::AllocatedSurface::AllocatedSurface(
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() {
diff --git a/chromium/components/display_compositor/buffer_queue.h b/chromium/components/display_compositor/buffer_queue.h
index 3ae123d4759..6d5a2d33b00 100644
--- a/chromium/components/display_compositor/buffer_queue.h
+++ b/chromium/components/display_compositor/buffer_queue.h
@@ -58,7 +58,8 @@ class DISPLAY_COMPOSITOR_EXPORT BufferQueue {
void PageFlipComplete();
void Reshape(const gfx::Size& size,
float scale_factor,
- const gfx::ColorSpace& color_space);
+ const gfx::ColorSpace& color_space,
+ bool use_stencil);
void RecreateBuffers();
@@ -77,12 +78,14 @@ class DISPLAY_COMPOSITOR_EXPORT BufferQueue {
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.
};
@@ -108,6 +111,7 @@ class DISPLAY_COMPOSITOR_EXPORT BufferQueue {
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_;
diff --git a/chromium/components/display_compositor/buffer_queue_unittest.cc b/chromium/components/display_compositor/buffer_queue_unittest.cc
index 17ef79b0a8c..c99270f7104 100644
--- a/chromium/components/display_compositor/buffer_queue_unittest.cc
+++ b/chromium/components/display_compositor/buffer_queue_unittest.cc
@@ -66,13 +66,13 @@ class StubGpuMemoryBufferManager : public cc::TestGpuMemoryBufferManager {
void set_allocate_succeeds(bool value) { allocate_succeeds_ = value; }
- std::unique_ptr<gfx::GpuMemoryBuffer> AllocateGpuMemoryBuffer(
+ 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::AllocateGpuMemoryBuffer(
+ return TestGpuMemoryBufferManager::CreateGpuMemoryBuffer(
size, format, usage, surface_handle);
}
if (allocate_succeeds_)
@@ -102,7 +102,7 @@ class MockBufferQueue : public BufferQueue {
: BufferQueue(gl,
target,
internalformat,
- ui::DisplaySnapshot::PrimaryFormat(),
+ display::DisplaySnapshot::PrimaryFormat(),
nullptr,
gpu_memory_buffer_manager,
kFakeSurfaceHandle) {}
@@ -263,9 +263,9 @@ 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, ui::DisplaySnapshot::PrimaryFormat(),
- nullptr, gpu_memory_buffer_manager, kFakeSurfaceHandle));
+ 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;
}
@@ -284,7 +284,7 @@ TEST(BufferQueueStandaloneTest, FboInitialization) {
ON_CALL(*context, framebufferTexture2D(_, _, _, _, _))
.WillByDefault(Return());
- output_surface->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace());
+ output_surface->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace(), false);
}
TEST(BufferQueueStandaloneTest, FboBinding) {
@@ -332,12 +332,12 @@ TEST(BufferQueueStandaloneTest, CheckBoundFramebuffer) {
gl_helper.reset(new GLHelper(context_provider->ContextGL(),
context_provider->ContextSupport()));
- output_surface.reset(
- new BufferQueue(context_provider->ContextGL(), GL_TEXTURE_2D, GL_RGB,
- ui::DisplaySnapshot::PrimaryFormat(), gl_helper.get(),
- gpu_memory_buffer_manager.get(), kFakeSurfaceHandle));
+ 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());
+ 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);
@@ -352,7 +352,7 @@ TEST(BufferQueueStandaloneTest, CheckBoundFramebuffer) {
}
TEST_F(BufferQueueTest, PartialSwapReuse) {
- output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace());
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
ASSERT_TRUE(doublebuffering_);
EXPECT_CALL(*mock_output_surface_,
CopyBufferDamage(_, _, small_damage, screen_rect))
@@ -372,7 +372,7 @@ TEST_F(BufferQueueTest, PartialSwapReuse) {
}
TEST_F(BufferQueueTest, PartialSwapFullFrame) {
- output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace());
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
ASSERT_TRUE(doublebuffering_);
EXPECT_CALL(*mock_output_surface_,
CopyBufferDamage(_, _, small_damage, screen_rect))
@@ -385,7 +385,7 @@ TEST_F(BufferQueueTest, PartialSwapFullFrame) {
}
TEST_F(BufferQueueTest, PartialSwapOverlapping) {
- output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace());
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
ASSERT_TRUE(doublebuffering_);
EXPECT_CALL(*mock_output_surface_,
CopyBufferDamage(_, _, small_damage, screen_rect))
@@ -495,7 +495,7 @@ TEST_F(BufferQueueTest, ReshapeWithInFlightSurfaces) {
SwapBuffers();
}
- output_surface_->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace());
+ 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) {
@@ -516,7 +516,7 @@ TEST_F(BufferQueueTest, SwapAfterReshape) {
}
DCHECK_EQ(kSwapCount, gpu_memory_buffer_manager_->set_color_space_count());
- output_surface_->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace());
+ 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) {
@@ -628,7 +628,7 @@ TEST_F(BufferQueueMockedContextTest, RecreateBuffers) {
}
TEST_F(BufferQueueTest, AllocateFails) {
- output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace());
+ output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false);
// Succeed in the two swaps.
output_surface_->BindFramebuffer();
diff --git a/chromium/components/display_compositor/compositor_overlay_candidate_validator_android.cc b/chromium/components/display_compositor/compositor_overlay_candidate_validator_android.cc
index 805dae3b917..a42d0355a14 100644
--- a/chromium/components/display_compositor/compositor_overlay_candidate_validator_android.cc
+++ b/chromium/components/display_compositor/compositor_overlay_candidate_validator_android.cc
@@ -34,6 +34,17 @@ void CompositorOverlayCandidateValidatorAndroid::CheckOverlaySupport(
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;
diff --git a/chromium/components/display_compositor/gl_helper_benchmark.cc b/chromium/components/display_compositor/gl_helper_benchmark.cc
index 38e009fc61c..674e4fd18bd 100644
--- a/chromium/components/display_compositor/gl_helper_benchmark.cc
+++ b/chromium/components/display_compositor/gl_helper_benchmark.cc
@@ -67,16 +67,16 @@ class GLHelperBenchmark : public testing::Test {
attributes.bind_generates_resource = false;
attributes.gpu_preference = gl::PreferDiscreteGpu;
- context_.reset(gpu::GLInProcessContext::Create(
- nullptr, /* service */
- nullptr, /* surface */
- true, /* offscreen */
- gfx::kNullAcceleratedWidget, /* window */
- nullptr, /* share_context */
- attributes, gpu::SharedMemoryLimits(),
- nullptr, /* gpu_memory_buffer_manager */
- nullptr, /* image_factory */
- base::ThreadTaskRunnerHandle::Get()));
+ 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();
diff --git a/chromium/components/display_compositor/gl_helper_unittest.cc b/chromium/components/display_compositor/gl_helper_unittest.cc
index b70e9211d8f..73a837dfd0e 100644
--- a/chromium/components/display_compositor/gl_helper_unittest.cc
+++ b/chromium/components/display_compositor/gl_helper_unittest.cc
@@ -67,16 +67,16 @@ class GLHelperTest : public testing::Test {
attributes.sample_buffers = 1;
attributes.bind_generates_resource = false;
- context_.reset(gpu::GLInProcessContext::Create(
- nullptr, /* service */
- nullptr, /* surface */
- true, /* offscreen */
- gfx::kNullAcceleratedWidget, /* window */
- nullptr, /* share_context */
- attributes, gpu::SharedMemoryLimits(),
- nullptr, /* gpu_memory_buffer_manager */
- nullptr, /* image_factory */
- base::ThreadTaskRunnerHandle::Get()));
+ 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();
diff --git a/chromium/components/display_compositor/yuv_readback_unittest.cc b/chromium/components/display_compositor/yuv_readback_unittest.cc
index 29cc498f3a8..fe595e3364a 100644
--- a/chromium/components/display_compositor/yuv_readback_unittest.cc
+++ b/chromium/components/display_compositor/yuv_readback_unittest.cc
@@ -13,6 +13,7 @@
#include "components/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"
@@ -40,16 +41,16 @@ class YUVReadbackTest : public testing::Test {
attributes.sample_buffers = 1;
attributes.bind_generates_resource = false;
- context_.reset(gpu::GLInProcessContext::Create(
- nullptr, /* service */
- nullptr, /* surface */
- true, /* offscreen */
- gfx::kNullAcceleratedWidget, /* window */
- nullptr, /* share_context */
- attributes, gpu::SharedMemoryLimits(),
- nullptr, /* gpu_memory_buffer_manager */
- nullptr, /* image_factory */
- base::ThreadTaskRunnerHandle::Get()));
+ 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();
diff --git a/chromium/components/dom_distiller/DEPS b/chromium/components/dom_distiller/DEPS
index b02404dd9ac..fad02ef2197 100644
--- a/chromium/components/dom_distiller/DEPS
+++ b/chromium/components/dom_distiller/DEPS
@@ -5,6 +5,7 @@ include_rules = [
"+components/prefs",
"+components/sync/model",
"+components/sync/protocol",
+ "+components/sync_preferences",
"+components/variations",
"+google", # For third_party/protobuf.
"+grit", # For generated headers.
diff --git a/chromium/components/dom_distiller/content/browser/distillability_driver.cc b/chromium/components/dom_distiller/content/browser/distillability_driver.cc
index aea16bc0988..ad9785433ba 100644
--- a/chromium/components/dom_distiller/content/browser/distillability_driver.cc
+++ b/chromium/components/dom_distiller/content/browser/distillability_driver.cc
@@ -4,6 +4,7 @@
#include "components/dom_distiller/content/browser/distillability_driver.h"
+#include "base/memory/ptr_util.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
@@ -89,7 +90,7 @@ void DistillabilityDriver::RenderFrameHostChanged(
void DistillabilityDriver::DidStartProvisionalLoadForFrame(
content::RenderFrameHost* render_frame_host, const GURL& validated_url,
- bool is_error_page, bool is_iframe_srcdoc) {
+ bool is_error_page) {
SetupMojoService(render_frame_host);
}
diff --git a/chromium/components/dom_distiller/content/browser/distillability_driver.h b/chromium/components/dom_distiller/content/browser/distillability_driver.h
index 6bb66870ce2..f547e6a247d 100644
--- a/chromium/components/dom_distiller/content/browser/distillability_driver.h
+++ b/chromium/components/dom_distiller/content/browser/distillability_driver.h
@@ -29,8 +29,7 @@ class DistillabilityDriver
void DidStartProvisionalLoadForFrame(
content::RenderFrameHost* render_frame_host,
const GURL& validated_url,
- bool is_error_page,
- bool is_iframe_srcdoc) override;
+ bool is_error_page) override;
void RenderFrameHostChanged(
content::RenderFrameHost* old_host,
content::RenderFrameHost* new_host) override;
diff --git a/chromium/components/dom_distiller/content/browser/distillable_page_utils_android.cc b/chromium/components/dom_distiller/content/browser/distillable_page_utils_android.cc
index f0890f68c60..68430f6d964 100644
--- a/chromium/components/dom_distiller/content/browser/distillable_page_utils_android.cc
+++ b/chromium/components/dom_distiller/content/browser/distillable_page_utils_android.cc
@@ -15,25 +15,23 @@
#include "jni/DistillablePageUtils_jni.h"
using base::android::JavaParamRef;
+using base::android::JavaRef;
using base::android::ScopedJavaGlobalRef;
namespace dom_distiller {
namespace android {
namespace {
-void OnIsPageDistillableResult(
- std::unique_ptr<ScopedJavaGlobalRef<jobject>> callback_holder,
- bool isDistillable) {
+void OnIsPageDistillableResult(const JavaRef<jobject>& callback,
+ bool isDistillable) {
Java_DistillablePageUtils_callOnIsPageDistillableResult(
- base::android::AttachCurrentThread(), callback_holder->obj(),
- isDistillable);
+ base::android::AttachCurrentThread(), callback, isDistillable);
}
-void OnIsPageDistillableUpdate(
- ScopedJavaGlobalRef<jobject>* callback_holder,
- bool isDistillable, bool isLast) {
+void OnIsPageDistillableUpdate(const JavaRef<jobject>& callback,
+ bool isDistillable,
+ bool isLast) {
Java_DistillablePageUtils_callOnIsPageDistillableUpdate(
- base::android::AttachCurrentThread(), callback_holder->obj(),
- isDistillable, isLast);
+ base::android::AttachCurrentThread(), callback, isDistillable, isLast);
}
} // namespace
@@ -44,19 +42,17 @@ static void IsPageDistillable(JNIEnv* env,
const JavaParamRef<jobject>& callback) {
content::WebContents* web_contents(
content::WebContents::FromJavaWebContents(webContents));
- std::unique_ptr<ScopedJavaGlobalRef<jobject>> callback_holder(
- new ScopedJavaGlobalRef<jobject>());
- callback_holder->Reset(env, callback);
if (!web_contents) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(OnIsPageDistillableResult,
- base::Passed(&callback_holder), false));
+ FROM_HERE,
+ base::Bind(OnIsPageDistillableResult,
+ ScopedJavaGlobalRef<jobject>(env, callback), false));
return;
}
- IsDistillablePage(
- web_contents, is_mobile_optimized,
- base::Bind(OnIsPageDistillableResult, base::Passed(&callback_holder)));
+ IsDistillablePage(web_contents, is_mobile_optimized,
+ base::Bind(OnIsPageDistillableResult,
+ ScopedJavaGlobalRef<jobject>(env, callback)));
}
static void SetDelegate(JNIEnv* env,
@@ -69,13 +65,8 @@ static void SetDelegate(JNIEnv* env,
return;
}
- // TODO(wychen): check memory management
- ScopedJavaGlobalRef<jobject>* callback_holder(
- new ScopedJavaGlobalRef<jobject>());
- callback_holder->Reset(env, callback);
-
- DistillabilityDelegate delegate =
- base::Bind(OnIsPageDistillableUpdate, base::Owned(callback_holder));
+ DistillabilityDelegate delegate = base::Bind(
+ OnIsPageDistillableUpdate, ScopedJavaGlobalRef<jobject>(env, callback));
setDelegate(web_contents, 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 087c94f5667..bcd09f2561a 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
@@ -6,10 +6,10 @@
#include <utility>
+#include "base/memory/ptr_util.h"
#include "components/dom_distiller/content/browser/distiller_ui_handle.h"
#include "components/dom_distiller/core/feedback_reporter.h"
#include "content/public/browser/user_metrics.h"
-#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
namespace dom_distiller {
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 306887e0d35..e8ac057ee0b 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
@@ -8,7 +8,6 @@
#include "base/macros.h"
#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/string.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
namespace dom_distiller {
diff --git a/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.h b/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.h
index 1749e8c19ad..2ed7a3f19df 100644
--- a/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.h
+++ b/chromium/components/dom_distiller/content/browser/dom_distiller_viewer_source.h
@@ -17,8 +17,6 @@ namespace dom_distiller {
class DomDistillerServiceInterface;
class DomDistillerViewerSourceTest;
-class ViewerHandle;
-class ViewRequestDelegate;
// Serves HTML and resources for viewing distilled articles.
class DomDistillerViewerSource : public content::URLDataSource {
diff --git a/chromium/components/dom_distiller/content/renderer/distillability_agent.cc b/chromium/components/dom_distiller/content/renderer/distillability_agent.cc
index 41cbf92f883..e032b677b0d 100644
--- a/chromium/components/dom_distiller/content/renderer/distillability_agent.cc
+++ b/chromium/components/dom_distiller/content/renderer/distillability_agent.cc
@@ -203,6 +203,7 @@ void DistillabilityAgent::DidMeaningfulLayout(
render_frame()->GetRemoteInterfaces()->GetInterface(
&distillability_service);
DCHECK(distillability_service);
+ if (!distillability_service.is_bound()) return;
distillability_service->NotifyIsDistillable(
IsDistillablePage(doc, is_last), is_last);
}
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 9fb77a0bdb0..73c6133836a 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
@@ -7,6 +7,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/memory/ptr_util.h"
#include "components/dom_distiller/content/common/distiller_page_notifier_service.mojom.h"
#include "components/dom_distiller/content/renderer/distiller_page_notifier_service_impl.h"
#include "content/public/renderer/render_frame.h"
@@ -41,7 +42,6 @@ void DistillerJsRenderFrameObserver::DidFinishLoad() {
void DistillerJsRenderFrameObserver::DidCreateScriptContext(
v8::Local<v8::Context> context,
- int extension_group,
int world_id) {
if (world_id != distiller_isolated_world_id_ || !is_distiller_page_) {
return;
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 fb75189adac..93476b5d309 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
@@ -30,7 +30,6 @@ class DistillerJsRenderFrameObserver : public content::RenderFrameObserver {
void DidStartProvisionalLoad() override;
void DidFinishLoad() override;
void DidCreateScriptContext(v8::Local<v8::Context> context,
- int extension_group,
int world_id) override;
// Add the mojo interface to a RenderFrame's
diff --git a/chromium/components/dom_distiller/core/BUILD.gn b/chromium/components/dom_distiller/core/BUILD.gn
index 3ed00be2a3c..49f014fb5a1 100644
--- a/chromium/components/dom_distiller/core/BUILD.gn
+++ b/chromium/components/dom_distiller/core/BUILD.gn
@@ -150,10 +150,10 @@ source_set("unit_tests") {
":unit_tests_bundle_data",
"//base",
"//components/leveldb_proto:test_support",
- "//components/pref_registry:test_support",
"//components/resources",
"//components/strings",
"//components/sync",
+ "//components/sync_preferences:test_support",
"//net:test_support",
"//testing/gmock",
"//testing/gtest",
diff --git a/chromium/components/dom_distiller/core/distilled_page_prefs_unittests.cc b/chromium/components/dom_distiller/core/distilled_page_prefs_unittests.cc
index b034255c2e4..b791e400be3 100644
--- a/chromium/components/dom_distiller/core/distilled_page_prefs_unittests.cc
+++ b/chromium/components/dom_distiller/core/distilled_page_prefs_unittests.cc
@@ -6,7 +6,7 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace dom_distiller {
@@ -49,12 +49,12 @@ class TestingObserver : public DistilledPagePrefs::Observer {
class DistilledPagePrefsTest : public testing::Test {
protected:
void SetUp() override {
- pref_service_.reset(new user_prefs::TestingPrefServiceSyncable());
+ pref_service_.reset(new sync_preferences::TestingPrefServiceSyncable());
DistilledPagePrefs::RegisterProfilePrefs(pref_service_->registry());
distilled_page_prefs_.reset(new DistilledPagePrefs(pref_service_.get()));
}
- std::unique_ptr<user_prefs::TestingPrefServiceSyncable> pref_service_;
+ std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_;
std::unique_ptr<DistilledPagePrefs> distilled_page_prefs_;
private:
diff --git a/chromium/components/dom_distiller/core/distiller_page.cc b/chromium/components/dom_distiller/core/distiller_page.cc
index fb115d8e8f7..7e66e453dc2 100644
--- a/chromium/components/dom_distiller/core/distiller_page.cc
+++ b/chromium/components/dom_distiller/core/distiller_page.cc
@@ -94,7 +94,7 @@ void DistillerPage::OnDistillationDone(const GURL& page_url,
std::unique_ptr<dom_distiller::proto::DomDistillerResult> distiller_result(
new dom_distiller::proto::DomDistillerResult());
bool found_content;
- if (value->IsType(base::Value::TYPE_NULL)) {
+ if (value->IsType(base::Value::Type::NONE)) {
found_content = false;
} else {
found_content =
diff --git a/chromium/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc b/chromium/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc
index 5f661d80e7b..ae6a2f2c527 100644
--- a/chromium/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc
+++ b/chromium/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc
@@ -6,7 +6,7 @@
#include "components/dom_distiller/core/article_distillation_update.h"
#include "components/dom_distiller/core/test_request_view_handle.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "grit/components_resources.h"
#include "grit/components_strings.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -21,12 +21,12 @@ namespace dom_distiller {
class DomDistillerRequestViewTest : public testing::Test {
protected:
void SetUp() override {
- pref_service_.reset(new user_prefs::TestingPrefServiceSyncable());
+ pref_service_.reset(new sync_preferences::TestingPrefServiceSyncable());
DistilledPagePrefs::RegisterProfilePrefs(pref_service_->registry());
distilled_page_prefs_.reset(new DistilledPagePrefs(pref_service_.get()));
}
- std::unique_ptr<user_prefs::TestingPrefServiceSyncable> pref_service_;
+ std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_;
std::unique_ptr<DistilledPagePrefs> distilled_page_prefs_;
};
diff --git a/chromium/components/dom_distiller/core/dom_distiller_service.cc b/chromium/components/dom_distiller/core/dom_distiller_service.cc
index 78534bfa386..bb31c6a0abd 100644
--- a/chromium/components/dom_distiller/core/dom_distiller_service.cc
+++ b/chromium/components/dom_distiller/core/dom_distiller_service.cc
@@ -55,6 +55,9 @@ DomDistillerService::~DomDistillerService() {
}
syncer::SyncableService* DomDistillerService::GetSyncableService() const {
+ if (!store_) {
+ return nullptr;
+ }
return store_->GetSyncableService();
}
@@ -75,7 +78,7 @@ const std::string DomDistillerService::AddToList(
std::unique_ptr<DistillerPage> distiller_page,
const ArticleAvailableCallback& article_cb) {
ArticleEntry entry;
- const bool is_already_added = store_->GetEntryByUrl(url, &entry);
+ const bool is_already_added = store_ && store_->GetEntryByUrl(url, &entry);
TaskTracker* task_tracker = nullptr;
if (is_already_added) {
@@ -112,18 +115,21 @@ const std::string DomDistillerService::AddToList(
}
bool DomDistillerService::HasEntry(const std::string& entry_id) {
- return store_->GetEntryById(entry_id, NULL);
+ return store_ && store_->GetEntryById(entry_id, NULL);
}
std::string DomDistillerService::GetUrlForEntry(const std::string& entry_id) {
ArticleEntry entry;
- if (store_->GetEntryById(entry_id, &entry)) {
+ if (store_ && store_->GetEntryById(entry_id, &entry)) {
return entry.pages().Get(0).url();
}
return "";
}
std::vector<ArticleEntry> DomDistillerService::GetEntries() const {
+ if (!store_) {
+ return std::vector<ArticleEntry>();
+ }
return store_->GetEntries();
}
@@ -136,7 +142,7 @@ std::unique_ptr<ArticleEntry> DomDistillerService::RemoveEntry(
task_tracker->CancelSaveCallbacks();
}
- if (!store_->GetEntryById(entry_id, entry.get())) {
+ if (!store_ || !store_->GetEntryById(entry_id, entry.get())) {
return std::unique_ptr<ArticleEntry>();
}
@@ -151,7 +157,7 @@ std::unique_ptr<ViewerHandle> DomDistillerService::ViewEntry(
std::unique_ptr<DistillerPage> distiller_page,
const std::string& entry_id) {
ArticleEntry entry;
- if (!store_->GetEntryById(entry_id, &entry)) {
+ if (!store_ || !store_->GetEntryById(entry_id, &entry)) {
return std::unique_ptr<ViewerHandle>();
}
@@ -194,7 +200,7 @@ bool DomDistillerService::GetOrCreateTaskTrackerForUrl(
const GURL& url,
TaskTracker** task_tracker) {
ArticleEntry entry;
- if (store_->GetEntryByUrl(url, &entry)) {
+ if (store_ && store_->GetEntryByUrl(url, &entry)) {
return GetOrCreateTaskTrackerForEntry(entry, task_tracker);
}
@@ -261,7 +267,7 @@ void DomDistillerService::AddDistilledPageToList(
const DistilledArticleProto* article_proto,
bool distillation_succeeded) {
DCHECK(IsEntryValid(entry));
- if (distillation_succeeded) {
+ if (store_ && distillation_succeeded) {
DCHECK(article_proto);
DCHECK_GT(article_proto->pages_size(), 0);
store_->AddEntry(entry);
@@ -271,12 +277,16 @@ void DomDistillerService::AddDistilledPageToList(
void DomDistillerService::AddObserver(DomDistillerObserver* observer) {
DCHECK(observer);
- store_->AddObserver(observer);
+ if (store_) {
+ store_->AddObserver(observer);
+ }
}
void DomDistillerService::RemoveObserver(DomDistillerObserver* observer) {
DCHECK(observer);
- store_->RemoveObserver(observer);
+ if (store_) {
+ store_->RemoveObserver(observer);
+ }
}
DistilledPagePrefs* DomDistillerService::GetDistilledPagePrefs() {
diff --git a/chromium/components/dom_distiller/core/dom_distiller_store.cc b/chromium/components/dom_distiller/core/dom_distiller_store.cc
index bb4e85925c9..53797a0c9cf 100644
--- a/chromium/components/dom_distiller/core/dom_distiller_store.cc
+++ b/chromium/components/dom_distiller/core/dom_distiller_store.cc
@@ -287,8 +287,8 @@ SyncMergeResult DomDistillerStore::MergeDataAndStartSyncing(
DCHECK_EQ(syncer::ARTICLES, type);
DCHECK(!sync_processor_);
DCHECK(!error_factory_);
- sync_processor_.reset(sync_processor.release());
- error_factory_.reset(error_handler.release());
+ sync_processor_ = std::move(sync_processor);
+ error_factory_ = std::move(error_handler);
SyncChangeList database_changes;
SyncChangeList sync_changes;
diff --git a/chromium/components/dom_distiller/ios/BUILD.gn b/chromium/components/dom_distiller/ios/BUILD.gn
index fe606465c15..18c9b24d261 100644
--- a/chromium/components/dom_distiller/ios/BUILD.gn
+++ b/chromium/components/dom_distiller/ios/BUILD.gn
@@ -14,7 +14,7 @@ source_set("ios") {
"//base",
"//components/dom_distiller/core",
"//components/dom_distiller/core/proto",
- "//ios/public/provider/web",
+ "//components/favicon/ios",
"//ios/web",
"//url",
]
diff --git a/chromium/components/dom_distiller/ios/DEPS b/chromium/components/dom_distiller/ios/DEPS
index 450c6e92a3a..ea0729b527c 100644
--- a/chromium/components/dom_distiller/ios/DEPS
+++ b/chromium/components/dom_distiller/ios/DEPS
@@ -1,4 +1,4 @@
include_rules = [
- "+ios/public/provider/web",
+ "+components/favicon/ios",
"+ios/web/public",
]
diff --git a/chromium/components/dom_distiller/ios/distiller_page_factory_ios.h b/chromium/components/dom_distiller/ios/distiller_page_factory_ios.h
index 6af72b1dde7..ecd228576a0 100644
--- a/chromium/components/dom_distiller/ios/distiller_page_factory_ios.h
+++ b/chromium/components/dom_distiller/ios/distiller_page_factory_ios.h
@@ -15,14 +15,13 @@ class BrowserState;
namespace dom_distiller {
-class DistillerPageIOS;
-
// DistillerPageFactoryIOS is an iOS-specific implementation of the
// DistillerPageFactory interface allowing the creation of DistillerPage
// instances.
class DistillerPageFactoryIOS : public DistillerPageFactory {
public:
- DistillerPageFactoryIOS(web::BrowserState* browser_state);
+ explicit DistillerPageFactoryIOS(web::BrowserState* browser_state);
+ ~DistillerPageFactoryIOS() override;
// Implementation of DistillerPageFactory:
std::unique_ptr<DistillerPage> CreateDistillerPage(
@@ -32,6 +31,7 @@ class DistillerPageFactoryIOS : public DistillerPageFactory {
private:
web::BrowserState* browser_state_;
+ DISALLOW_COPY_AND_ASSIGN(DistillerPageFactoryIOS);
};
} // namespace dom_distiller
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 ad355f9cd18..6aef14f56d8 100644
--- a/chromium/components/dom_distiller/ios/distiller_page_factory_ios.mm
+++ b/chromium/components/dom_distiller/ios/distiller_page_factory_ios.mm
@@ -12,18 +12,19 @@ namespace dom_distiller {
DistillerPageFactoryIOS::DistillerPageFactoryIOS(
web::BrowserState* browser_state)
- : browser_state_(browser_state) {
-}
+ : browser_state_(browser_state) {}
+
+DistillerPageFactoryIOS::~DistillerPageFactoryIOS() {}
std::unique_ptr<DistillerPage> DistillerPageFactoryIOS::CreateDistillerPage(
const gfx::Size& view_size) const {
- return base::WrapUnique<DistillerPage>(new DistillerPageIOS(browser_state_));
+ return base::MakeUnique<DistillerPageIOS>(browser_state_);
}
std::unique_ptr<DistillerPage>
DistillerPageFactoryIOS::CreateDistillerPageWithHandle(
std::unique_ptr<SourcePageHandle> handle) const {
- return base::WrapUnique<DistillerPage>(new DistillerPageIOS(browser_state_));
+ return base::MakeUnique<DistillerPageIOS>(browser_state_);
}
} // namespace dom_distiller
diff --git a/chromium/components/dom_distiller/ios/distiller_page_ios.h b/chromium/components/dom_distiller/ios/distiller_page_ios.h
index 1fdf87e9a91..43ff2dafa51 100644
--- a/chromium/components/dom_distiller/ios/distiller_page_ios.h
+++ b/chromium/components/dom_distiller/ios/distiller_page_ios.h
@@ -6,6 +6,7 @@
#define COMPONENTS_DOM_DISTILLER_IOS_DISTILLER_PAGE_IOS_H_
#include <memory>
+#include <objc/objc.h>
#include <string>
#include "base/memory/weak_ptr.h"
@@ -13,10 +14,6 @@
#include "ios/web/public/web_state/web_state_observer.h"
#include "url/gurl.h"
-namespace ios {
-class WebControllerProvider;
-}
-
namespace web {
class BrowserState;
class WebState;
@@ -37,22 +34,33 @@ class DistillerPageIOS : public DistillerPage {
bool StringifyOutput() override;
void DistillPageImpl(const GURL& url, const std::string& script) override;
- private:
- friend class DistillerWebStateObserver;
+ // Sets the WebState that will be used for the distillation. Do not call
+ // between |DistillPageImpl| and |OnDistillationDone|.
+ virtual void AttachWebState(std::unique_ptr<web::WebState> web_state);
+
+ // Release the WebState used for distillation. Do not call between
+ // |DistillPageImpl| and |OnDistillationDone|.
+ virtual std::unique_ptr<web::WebState> DetachWebState();
+
+ // Return the current WebState.
+ virtual web::WebState* CurrentWebState();
// Called by |web_state_observer_| once the page has finished loading.
- void OnLoadURLDone(web::PageLoadCompletionStatus load_completion_status);
+ virtual void OnLoadURLDone(
+ web::PageLoadCompletionStatus load_completion_status);
+ private:
+ friend class DistillerWebStateObserver;
// Called once the |script_| has been evaluated on the page.
void HandleJavaScriptResult(id result);
// Converts result of WKWebView script evaluation to base::Value
std::unique_ptr<base::Value> ValueResultFromScriptResult(id wk_result);
- web::BrowserState* browser_state_;
GURL url_;
std::string script_;
- std::unique_ptr<ios::WebControllerProvider> provider_;
+ web::BrowserState* browser_state_;
+ std::unique_ptr<web::WebState> web_state_;
std::unique_ptr<DistillerWebStateObserver> web_state_observer_;
base::WeakPtrFactory<DistillerPageIOS> weak_ptr_factory_;
};
diff --git a/chromium/components/dom_distiller/ios/distiller_page_ios.mm b/chromium/components/dom_distiller/ios/distiller_page_ios.mm
index 213e82fd671..f3eed985dba 100644
--- a/chromium/components/dom_distiller/ios/distiller_page_ios.mm
+++ b/chromium/components/dom_distiller/ios/distiller_page_ios.mm
@@ -8,14 +8,20 @@
#include <utility>
+#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/memory/ptr_util.h"
#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
-#include "ios/public/provider/web/web_controller_provider.h"
#include "ios/web/public/browser_state.h"
+#import "ios/web/public/navigation_manager.h"
+#import "ios/web/public/web_state/js/crw_js_injection_manager.h"
+#import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
+#import "ios/web/public/web_state/web_state.h"
+#include "ios/web/public/web_state/web_state_observer.h"
namespace {
@@ -45,24 +51,24 @@ std::unique_ptr<base::Value> ValueResultFromScriptResult(id wk_result,
CFTypeID result_type = CFGetTypeID(wk_result);
if (result_type == CFStringGetTypeID()) {
result.reset(new base::StringValue(base::SysNSStringToUTF16(wk_result)));
- DCHECK(result->IsType(base::Value::TYPE_STRING));
+ DCHECK(result->IsType(base::Value::Type::STRING));
} else if (result_type == CFNumberGetTypeID()) {
// Different implementation is here.
if ([wk_result intValue] != [wk_result doubleValue]) {
result.reset(new base::FundamentalValue([wk_result doubleValue]));
- DCHECK(result->IsType(base::Value::TYPE_DOUBLE));
+ DCHECK(result->IsType(base::Value::Type::DOUBLE));
} else {
result.reset(new base::FundamentalValue([wk_result intValue]));
- DCHECK(result->IsType(base::Value::TYPE_INTEGER));
+ DCHECK(result->IsType(base::Value::Type::INTEGER));
}
// End of different implementation.
} else if (result_type == CFBooleanGetTypeID()) {
result.reset(
new base::FundamentalValue(static_cast<bool>([wk_result boolValue])));
- DCHECK(result->IsType(base::Value::TYPE_BOOLEAN));
+ DCHECK(result->IsType(base::Value::Type::BOOLEAN));
} else if (result_type == CFNullGetTypeID()) {
result = base::Value::CreateNullValue();
- DCHECK(result->IsType(base::Value::TYPE_NULL));
+ DCHECK(result->IsType(base::Value::Type::NONE));
} else if (result_type == CFDictionaryGetTypeID()) {
std::unique_ptr<base::DictionaryValue> dictionary =
base::MakeUnique<base::DictionaryValue>();
@@ -104,37 +110,85 @@ class DistillerWebStateObserver : public web::WebStateObserver {
// WebStateObserver implementation:
void PageLoaded(
web::PageLoadCompletionStatus load_completion_status) override;
+ void WebStateDestroyed() override;
+ void DidStartLoading() override;
+ void DidStopLoading() override;
private:
DistillerPageIOS* distiller_page_; // weak, owns this object.
+ bool loading_;
};
DistillerWebStateObserver::DistillerWebStateObserver(
web::WebState* web_state,
DistillerPageIOS* distiller_page)
- : web::WebStateObserver(web_state), distiller_page_(distiller_page) {
+ : web::WebStateObserver(web_state),
+ distiller_page_(distiller_page),
+ loading_(false) {
DCHECK(web_state);
DCHECK(distiller_page_);
}
void DistillerWebStateObserver::PageLoaded(
web::PageLoadCompletionStatus load_completion_status) {
+ if (!loading_) {
+ return;
+ }
+ loading_ = false;
distiller_page_->OnLoadURLDone(load_completion_status);
}
-#pragma mark -
+void DistillerWebStateObserver::WebStateDestroyed() {
+ distiller_page_->DetachWebState();
+}
-DistillerPageIOS::DistillerPageIOS(web::BrowserState* browser_state)
- : browser_state_(browser_state), weak_ptr_factory_(this) {
+void DistillerWebStateObserver::DidStartLoading() {
+ loading_ = true;
}
-DistillerPageIOS::~DistillerPageIOS() {
+void DistillerWebStateObserver::DidStopLoading() {
+ if (web_state()->IsShowingWebInterstitial()) {
+ // If there is an interstitial, stop the distillation.
+ // The interstitial is not displayed to the user who cannot choose to
+ // continue.
+ PageLoaded(web::PageLoadCompletionStatus::FAILURE);
+ }
}
+#pragma mark -
+
+DistillerPageIOS::DistillerPageIOS(web::BrowserState* browser_state)
+ : browser_state_(browser_state), weak_ptr_factory_(this) {}
+
bool DistillerPageIOS::StringifyOutput() {
return false;
}
+DistillerPageIOS::~DistillerPageIOS() {}
+
+void DistillerPageIOS::AttachWebState(
+ std::unique_ptr<web::WebState> web_state) {
+ if (web_state_) {
+ DetachWebState();
+ }
+ web_state_ = std::move(web_state);
+ if (web_state_) {
+ web_state_observer_ =
+ base::MakeUnique<DistillerWebStateObserver>(web_state_.get(), this);
+ }
+}
+
+std::unique_ptr<web::WebState> DistillerPageIOS::DetachWebState() {
+ std::unique_ptr<web::WebState> old_web_state = std::move(web_state_);
+ web_state_observer_.reset();
+ web_state_.reset();
+ return old_web_state;
+}
+
+web::WebState* DistillerPageIOS::CurrentWebState() {
+ return web_state_.get();
+}
+
void DistillerPageIOS::DistillPageImpl(const GURL& url,
const std::string& script) {
if (!url.is_valid() || !script.length())
@@ -142,41 +196,40 @@ void DistillerPageIOS::DistillPageImpl(const GURL& url,
url_ = url;
script_ = script;
- // Lazily create provider.
- if (!provider_) {
- if (ios::GetWebControllerProviderFactory()) {
- provider_ =
- ios::GetWebControllerProviderFactory()->CreateWebControllerProvider(
- browser_state_);
- web_state_observer_.reset(
- new DistillerWebStateObserver(provider_->GetWebState(), this));
- }
+ if (!web_state_) {
+ const web::WebState::CreateParams web_state_create_params(browser_state_);
+ std::unique_ptr<web::WebState> web_state_unique =
+ web::WebState::Create(web_state_create_params);
+ AttachWebState(std::move(web_state_unique));
}
-
- // Load page using provider.
- if (provider_)
- provider_->LoadURL(url_);
- else
- OnLoadURLDone(web::PageLoadCompletionStatus::FAILURE);
+ // Load page using WebState.
+ web::NavigationManager::WebLoadParams params(url_);
+ web_state_->SetWebUsageEnabled(true);
+ web_state_->GetNavigationManager()->LoadURLWithParams(params);
+ // GetView is needed because the view is not created (but needed) when
+ // loading the page.
+ web_state_->GetView();
}
void DistillerPageIOS::OnLoadURLDone(
web::PageLoadCompletionStatus load_completion_status) {
// Don't attempt to distill if the page load failed or if there is no
- // provider.
+ // WebState.
if (load_completion_status == web::PageLoadCompletionStatus::FAILURE ||
- !provider_) {
+ !web_state_) {
HandleJavaScriptResult(nil);
return;
}
-
// Inject the script.
base::WeakPtr<DistillerPageIOS> weak_this = weak_ptr_factory_.GetWeakPtr();
- provider_->InjectScript(script_, ^(id result, NSError* error) {
- DistillerPageIOS* distiller_page = weak_this.get();
- if (distiller_page)
- distiller_page->HandleJavaScriptResult(result);
- });
+ [[web_state_->GetJSInjectionReceiver()
+ instanceOfClass:[CRWJSInjectionManager class]]
+ executeJavaScript:base::SysUTF8ToNSString(script_)
+ completionHandler:^(id result, NSError* error) {
+ DistillerPageIOS* distiller_page = weak_this.get();
+ if (distiller_page)
+ distiller_page->HandleJavaScriptResult(result);
+ }];
}
void DistillerPageIOS::HandleJavaScriptResult(id result) {
diff --git a/chromium/components/dom_distiller/standalone/content_extractor_browsertest.cc b/chromium/components/dom_distiller/standalone/content_extractor_browsertest.cc
index cbae88bcab0..459c3d341b4 100644
--- a/chromium/components/dom_distiller/standalone/content_extractor_browsertest.cc
+++ b/chromium/components/dom_distiller/standalone/content_extractor_browsertest.cc
@@ -28,7 +28,7 @@
#include "components/dom_distiller/core/task_tracker.h"
#include "components/leveldb_proto/proto_database.h"
#include "components/leveldb_proto/proto_database_impl.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
@@ -171,8 +171,8 @@ std::unique_ptr<DomDistillerService> CreateDomDistillerService(
options, file_to_url_map));
// Setting up PrefService for DistilledPagePrefs.
- user_prefs::TestingPrefServiceSyncable* pref_service =
- new user_prefs::TestingPrefServiceSyncable();
+ sync_preferences::TestingPrefServiceSyncable* pref_service =
+ new sync_preferences::TestingPrefServiceSyncable();
DistilledPagePrefs::RegisterProfilePrefs(pref_service->registry());
return std::unique_ptr<DomDistillerService>(new DomDistillerService(
diff --git a/chromium/components/dom_distiller/webui/dom_distiller_ui.cc b/chromium/components/dom_distiller/webui/dom_distiller_ui.cc
index 8bee051aba9..3e9f323e910 100644
--- a/chromium/components/dom_distiller/webui/dom_distiller_ui.cc
+++ b/chromium/components/dom_distiller/webui/dom_distiller_ui.cc
@@ -4,6 +4,7 @@
#include "components/dom_distiller/webui/dom_distiller_ui.h"
+#include "base/memory/ptr_util.h"
#include "components/dom_distiller/core/dom_distiller_constants.h"
#include "components/dom_distiller/core/dom_distiller_service.h"
#include "components/dom_distiller/webui/dom_distiller_handler.h"
@@ -51,7 +52,8 @@ DomDistillerUi::DomDistillerUi(content::WebUI* web_ui,
source->SetJsonPath("strings.js");
// Add message handler.
- web_ui->AddMessageHandler(new DomDistillerHandler(service, scheme));
+ web_ui->AddMessageHandler(
+ base::MakeUnique<DomDistillerHandler>(service, scheme));
}
DomDistillerUi::~DomDistillerUi() {}
diff --git a/chromium/components/domain_reliability/BUILD.gn b/chromium/components/domain_reliability/BUILD.gn
index 52620af9e65..160724faf08 100644
--- a/chromium/components/domain_reliability/BUILD.gn
+++ b/chromium/components/domain_reliability/BUILD.gn
@@ -18,10 +18,13 @@ action("bake_in_configs") {
"baked_in_configs/c_youtube_com.json",
"baked_in_configs/clients2_google_com.json",
"baked_in_configs/docs_google_com.json",
+ "baked_in_configs/gcp_gvt2_com.json",
+ "baked_in_configs/gcp_gvt6_com.json",
"baked_in_configs/google-analytics_com.json",
"baked_in_configs/googlevideo_com.json",
"baked_in_configs/gvt1_com.json",
"baked_in_configs/gvt2_com.json",
+ "baked_in_configs/gvt6_com.json",
"baked_in_configs/ssl_gstatic_com.json",
"baked_in_configs/www_google_com.json",
]
@@ -81,7 +84,6 @@ component("domain_reliability") {
deps = [
":bake_in_configs",
"//base",
- "//components/data_use_measurement/core",
"//components/keyed_service/core",
"//content/public/common",
"//net",
diff --git a/chromium/components/domain_reliability/DEPS b/chromium/components/domain_reliability/DEPS
index b5ddd354e6a..e898ad00e61 100644
--- a/chromium/components/domain_reliability/DEPS
+++ b/chromium/components/domain_reliability/DEPS
@@ -4,7 +4,6 @@
include_rules = [
"+net",
- "+components/data_use_measurement/core",
"+components/keyed_service/core",
"+content/public/common",
]
diff --git a/chromium/components/domain_reliability/bake_in_configs.py b/chromium/components/domain_reliability/bake_in_configs.py
index a6fa0ed6caf..d3d5f6e6420 100755
--- a/chromium/components/domain_reliability/bake_in_configs.py
+++ b/chromium/components/domain_reliability/bake_in_configs.py
@@ -311,6 +311,7 @@ DOMAIN_WHITELIST = (
'gstatic.com',
'gvt1.com',
'gvt2.com',
+ 'gvt6.com',
'withgoogle.com',
'youtu.be',
'youtube-3rd-party.com',
diff --git a/chromium/components/domain_reliability/baked_in_configs/c_2mdn_net.json b/chromium/components/domain_reliability/baked_in_configs/c_2mdn_net.json
index 30d052eec30..6041650dc37 100644
--- a/chromium/components/domain_reliability/baked_in_configs/c_2mdn_net.json
+++ b/chromium/components/domain_reliability/baked_in_configs/c_2mdn_net.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": false,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/c_android_clients_google_com.json b/chromium/components/domain_reliability/baked_in_configs/c_android_clients_google_com.json
index 5a2c8925e9f..d168624e830 100644
--- a/chromium/components/domain_reliability/baked_in_configs/c_android_clients_google_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/c_android_clients_google_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": true,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/c_bigcache_googleapis_com.json b/chromium/components/domain_reliability/baked_in_configs/c_bigcache_googleapis_com.json
index 265d64c6a55..2bd1cff79a8 100644
--- a/chromium/components/domain_reliability/baked_in_configs/c_bigcache_googleapis_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/c_bigcache_googleapis_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": false,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/c_doc-0-0-sj_sj_googleusercontent_com.json b/chromium/components/domain_reliability/baked_in_configs/c_doc-0-0-sj_sj_googleusercontent_com.json
index 4598487559b..b8f8672cdf7 100644
--- a/chromium/components/domain_reliability/baked_in_configs/c_doc-0-0-sj_sj_googleusercontent_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/c_doc-0-0-sj_sj_googleusercontent_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": false,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/c_docs_google_com.json b/chromium/components/domain_reliability/baked_in_configs/c_docs_google_com.json
index b94b23a5c8c..2e581c9dc88 100644
--- a/chromium/components/domain_reliability/baked_in_configs/c_docs_google_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/c_docs_google_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": true,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/c_drive_google_com.json b/chromium/components/domain_reliability/baked_in_configs/c_drive_google_com.json
index b8b9ed93667..d8144096828 100644
--- a/chromium/components/domain_reliability/baked_in_configs/c_drive_google_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/c_drive_google_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": true,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/c_googlesyndication_com.json b/chromium/components/domain_reliability/baked_in_configs/c_googlesyndication_com.json
index 6ef17739192..f15dbbfa5a7 100644
--- a/chromium/components/domain_reliability/baked_in_configs/c_googlesyndication_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/c_googlesyndication_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": false,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/c_pack_google_com.json b/chromium/components/domain_reliability/baked_in_configs/c_pack_google_com.json
index e054887ad30..3f480b7628e 100644
--- a/chromium/components/domain_reliability/baked_in_configs/c_pack_google_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/c_pack_google_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": true,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/c_play_google_com.json b/chromium/components/domain_reliability/baked_in_configs/c_play_google_com.json
index 1cd6a34f195..dc85facd14e 100644
--- a/chromium/components/domain_reliability/baked_in_configs/c_play_google_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/c_play_google_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": true,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/c_youtube_com.json b/chromium/components/domain_reliability/baked_in_configs/c_youtube_com.json
index 8f0f35e583e..ea08766635b 100644
--- a/chromium/components/domain_reliability/baked_in_configs/c_youtube_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/c_youtube_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": false,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/clients2_google_com.json b/chromium/components/domain_reliability/baked_in_configs/clients2_google_com.json
index a3ec5dbd48d..dafb2375656 100644
--- a/chromium/components/domain_reliability/baked_in_configs/clients2_google_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/clients2_google_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": true,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/docs_google_com.json b/chromium/components/domain_reliability/baked_in_configs/docs_google_com.json
index 01ec7e79c4d..b8a64a94253 100644
--- a/chromium/components/domain_reliability/baked_in_configs/docs_google_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/docs_google_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": true,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/gcp_gvt2_com.json b/chromium/components/domain_reliability/baked_in_configs/gcp_gvt2_com.json
new file mode 100644
index 00000000000..5de0f658eee
--- /dev/null
+++ b/chromium/components/domain_reliability/baked_in_configs/gcp_gvt2_com.json
@@ -0,0 +1,21 @@
+{
+ "origin": "https://gcp.gvt2.com/",
+ "has_same_origin_collector": false,
+ "success_sample_rate": 0.05,
+ "collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
+ "https://beacons.gvt2.com/domainreliability/upload",
+ "https://beacons2.gvt2.com/domainreliability/upload",
+ "https://beacons3.gvt2.com/domainreliability/upload",
+ "https://beacons4.gvt2.com/domainreliability/upload",
+ "https://beacons5.gvt2.com/domainreliability/upload",
+ "https://beacons5.gvt3.com/domainreliability/upload",
+ "https://clients2.google.com/domainreliability/upload"
+ ],
+ "failure_sample_rate": 1.0,
+ "include_subdomains": true,
+ "path_prefixes": [
+ "/domainreliability/*",
+ ""
+ ]
+}
diff --git a/chromium/components/domain_reliability/baked_in_configs/gcp_gvt6_com.json b/chromium/components/domain_reliability/baked_in_configs/gcp_gvt6_com.json
new file mode 100644
index 00000000000..0159920971e
--- /dev/null
+++ b/chromium/components/domain_reliability/baked_in_configs/gcp_gvt6_com.json
@@ -0,0 +1,21 @@
+{
+ "origin": "https://gcp.gvt6.com/",
+ "has_same_origin_collector": false,
+ "success_sample_rate": 0.05,
+ "collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
+ "https://beacons.gvt2.com/domainreliability/upload",
+ "https://beacons2.gvt2.com/domainreliability/upload",
+ "https://beacons3.gvt2.com/domainreliability/upload",
+ "https://beacons4.gvt2.com/domainreliability/upload",
+ "https://beacons5.gvt2.com/domainreliability/upload",
+ "https://beacons5.gvt3.com/domainreliability/upload",
+ "https://clients2.google.com/domainreliability/upload"
+ ],
+ "failure_sample_rate": 1.0,
+ "include_subdomains": true,
+ "path_prefixes": [
+ "/domainreliability/*",
+ ""
+ ]
+}
diff --git a/chromium/components/domain_reliability/baked_in_configs/google-analytics_com.json b/chromium/components/domain_reliability/baked_in_configs/google-analytics_com.json
index 6bd103b7c74..dbbc2f1b8d9 100644
--- a/chromium/components/domain_reliability/baked_in_configs/google-analytics_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/google-analytics_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": false,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/googlevideo_com.json b/chromium/components/domain_reliability/baked_in_configs/googlevideo_com.json
index 4f4f27427a1..9b87f68c3ce 100644
--- a/chromium/components/domain_reliability/baked_in_configs/googlevideo_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/googlevideo_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": false,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/gvt1_com.json b/chromium/components/domain_reliability/baked_in_configs/gvt1_com.json
index 509639b59c0..50d622a5f6e 100644
--- a/chromium/components/domain_reliability/baked_in_configs/gvt1_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/gvt1_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": false,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/gvt2_com.json b/chromium/components/domain_reliability/baked_in_configs/gvt2_com.json
index cc696b9a583..40cf63dc477 100644
--- a/chromium/components/domain_reliability/baked_in_configs/gvt2_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/gvt2_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": false,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/gvt6_com.json b/chromium/components/domain_reliability/baked_in_configs/gvt6_com.json
new file mode 100644
index 00000000000..1ae04d58b8f
--- /dev/null
+++ b/chromium/components/domain_reliability/baked_in_configs/gvt6_com.json
@@ -0,0 +1,21 @@
+{
+ "origin": "https://gvt6.com/",
+ "has_same_origin_collector": false,
+ "success_sample_rate": 0.05,
+ "collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
+ "https://beacons.gvt2.com/domainreliability/upload",
+ "https://beacons2.gvt2.com/domainreliability/upload",
+ "https://beacons3.gvt2.com/domainreliability/upload",
+ "https://beacons4.gvt2.com/domainreliability/upload",
+ "https://beacons5.gvt2.com/domainreliability/upload",
+ "https://beacons5.gvt3.com/domainreliability/upload",
+ "https://clients2.google.com/domainreliability/upload"
+ ],
+ "failure_sample_rate": 1.0,
+ "include_subdomains": true,
+ "path_prefixes": [
+ "/domainreliability/*",
+ ""
+ ]
+}
diff --git a/chromium/components/domain_reliability/baked_in_configs/ssl_gstatic_com.json b/chromium/components/domain_reliability/baked_in_configs/ssl_gstatic_com.json
index dc9897769ab..645b769d52c 100644
--- a/chromium/components/domain_reliability/baked_in_configs/ssl_gstatic_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/ssl_gstatic_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": false,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/baked_in_configs/www_google_com.json b/chromium/components/domain_reliability/baked_in_configs/www_google_com.json
index d1c9fdd7950..7724a8b20ec 100644
--- a/chromium/components/domain_reliability/baked_in_configs/www_google_com.json
+++ b/chromium/components/domain_reliability/baked_in_configs/www_google_com.json
@@ -3,6 +3,7 @@
"has_same_origin_collector": true,
"success_sample_rate": 0.05,
"collectors": [
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
diff --git a/chromium/components/domain_reliability/beacon.cc b/chromium/components/domain_reliability/beacon.cc
index 8c51e8fb739..8a8a549f8c2 100644
--- a/chromium/components/domain_reliability/beacon.cc
+++ b/chromium/components/domain_reliability/beacon.cc
@@ -24,7 +24,7 @@ std::unique_ptr<Value> DomainReliabilityBeacon::ToValue(
base::TimeTicks upload_time,
base::TimeTicks last_network_change_time,
const GURL& collector_url,
- const ScopedVector<std::string>& path_prefixes) const {
+ const std::vector<std::unique_ptr<std::string>>& path_prefixes) const {
std::unique_ptr<DictionaryValue> beacon_value(new DictionaryValue());
DCHECK(url.is_valid());
GURL sanitized_url = SanitizeURLForReport(url, collector_url, path_prefixes);
diff --git a/chromium/components/domain_reliability/beacon.h b/chromium/components/domain_reliability/beacon.h
index 2162b58f85c..8d352b9190a 100644
--- a/chromium/components/domain_reliability/beacon.h
+++ b/chromium/components/domain_reliability/beacon.h
@@ -8,7 +8,6 @@
#include <memory>
#include <string>
-#include "base/memory/scoped_vector.h"
#include "base/time/time.h"
#include "components/domain_reliability/domain_reliability_export.h"
#include "net/base/net_error_details.h"
@@ -40,7 +39,7 @@ struct DOMAIN_RELIABILITY_EXPORT DomainReliabilityBeacon {
base::TimeTicks upload_time,
base::TimeTicks last_network_change_time,
const GURL& collector_url,
- const ScopedVector<std::string>& path_prefixes) const;
+ const std::vector<std::unique_ptr<std::string>>& path_prefixes) const;
// The URL that the beacon is reporting on, if included.
GURL url;
diff --git a/chromium/components/domain_reliability/config.cc b/chromium/components/domain_reliability/config.cc
index 765e0fa4837..21c93cc1420 100644
--- a/chromium/components/domain_reliability/config.cc
+++ b/chromium/components/domain_reliability/config.cc
@@ -13,7 +13,6 @@
#include <utility>
#include "base/json/json_reader.h"
-#include "base/json/json_value_converter.h"
#include "base/profiler/scoped_tracker.h"
#include "base/strings/pattern.h"
#include "base/strings/string_util.h"
@@ -70,7 +69,7 @@ bool DomainReliabilityConfig::IsValid() const {
return false;
}
- for (const auto* url : collectors) {
+ for (const auto& url : collectors) {
if (!url->is_valid())
return false;
}
diff --git a/chromium/components/domain_reliability/config.h b/chromium/components/domain_reliability/config.h
index 533d891242e..9f4ebb063a8 100644
--- a/chromium/components/domain_reliability/config.h
+++ b/chromium/components/domain_reliability/config.h
@@ -42,11 +42,11 @@ struct DOMAIN_RELIABILITY_EXPORT DomainReliabilityConfig {
GURL origin;
bool include_subdomains;
- ScopedVector<GURL> collectors;
+ std::vector<std::unique_ptr<GURL>> collectors;
double success_sample_rate;
double failure_sample_rate;
- ScopedVector<std::string> path_prefixes;
+ std::vector<std::unique_ptr<std::string>> path_prefixes;
private:
DISALLOW_COPY_AND_ASSIGN(DomainReliabilityConfig);
diff --git a/chromium/components/domain_reliability/config_unittest.cc b/chromium/components/domain_reliability/config_unittest.cc
index d8c7f6d2044..be701b0998a 100644
--- a/chromium/components/domain_reliability/config_unittest.cc
+++ b/chromium/components/domain_reliability/config_unittest.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <string>
+#include "base/memory/ptr_util.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -17,7 +18,8 @@ std::unique_ptr<DomainReliabilityConfig> MakeBaseConfig() {
DomainReliabilityConfig* config = new DomainReliabilityConfig();
config->origin = GURL("https://example/");
config->include_subdomains = false;
- config->collectors.push_back(new GURL("https://example/upload"));
+ config->collectors.push_back(
+ base::MakeUnique<GURL>("https://example/upload"));
config->failure_sample_rate = 1.0;
config->success_sample_rate = 0.0;
EXPECT_TRUE(config->IsValid());
@@ -26,8 +28,8 @@ std::unique_ptr<DomainReliabilityConfig> MakeBaseConfig() {
std::unique_ptr<DomainReliabilityConfig> MakeSampleConfig() {
std::unique_ptr<DomainReliabilityConfig> config(MakeBaseConfig());
- config->path_prefixes.push_back(new std::string("/css/"));
- config->path_prefixes.push_back(new std::string("/js/"));
+ config->path_prefixes.push_back(base::MakeUnique<std::string>("/css/"));
+ config->path_prefixes.push_back(base::MakeUnique<std::string>("/js/"));
EXPECT_TRUE(config->IsValid());
return config;
}
@@ -49,8 +51,7 @@ TEST_F(DomainReliabilityConfigTest, IsValid) {
EXPECT_FALSE(config->IsValid());
config = MakeSampleConfig();
- delete config->collectors[0];
- config->collectors[0] = new GURL();
+ config->collectors[0] = base::MakeUnique<GURL>();
EXPECT_FALSE(config->IsValid());
config = MakeSampleConfig();
diff --git a/chromium/components/domain_reliability/dispatcher.cc b/chromium/components/domain_reliability/dispatcher.cc
index 3abef20e777..c4efdd00dcc 100644
--- a/chromium/components/domain_reliability/dispatcher.cc
+++ b/chromium/components/domain_reliability/dispatcher.cc
@@ -83,6 +83,17 @@ void DomainReliabilityDispatcher::RunEligibleTasks() {
}
}
+void DomainReliabilityDispatcher::RunAllTasksForTesting() {
+ std::set<Task*> tasks;
+ for (auto& task : tasks_)
+ tasks.insert(task.get());
+
+ for (auto* task : tasks) {
+ DCHECK(task);
+ RunAndDeleteTask(task);
+ }
+}
+
void DomainReliabilityDispatcher::MakeTaskWaiting(Task* task) {
DCHECK(task);
DCHECK(!task->eligible);
diff --git a/chromium/components/domain_reliability/dispatcher.h b/chromium/components/domain_reliability/dispatcher.h
index e12b1674d12..b65d404f57b 100644
--- a/chromium/components/domain_reliability/dispatcher.h
+++ b/chromium/components/domain_reliability/dispatcher.h
@@ -13,10 +13,6 @@
#include "base/time/time.h"
#include "components/domain_reliability/domain_reliability_export.h"
-namespace tracked_objects {
-class Location;
-} // namespace tracked_objects
-
namespace domain_reliability {
class MockableTime;
@@ -39,9 +35,15 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityDispatcher {
base::TimeDelta min_delay,
base::TimeDelta max_delay);
- // Runs all tasks whose minimum delay has already passed.
+ // Runs all existing tasks whose minimum delay has already passed. Does not
+ // run tasks added by those existing tasks, even if their minimum delay has
+ // already passed.
void RunEligibleTasks();
+ // Runs all waiting or eligible tasks, regardless of whether their minimum
+ // delay has passed.
+ void RunAllTasksForTesting();
+
private:
struct Task;
diff --git a/chromium/components/domain_reliability/google_configs.cc b/chromium/components/domain_reliability/google_configs.cc
index 00f55d22141..a18710649da 100644
--- a/chromium/components/domain_reliability/google_configs.cc
+++ b/chromium/components/domain_reliability/google_configs.cc
@@ -521,6 +521,7 @@ const GoogleConfigParams kGoogleConfigs[] = {
};
const char* kGoogleStandardCollectors[] = {
+ "https://beacons.gcp.gvt2.com/domainreliability/upload",
"https://beacons.gvt2.com/domainreliability/upload",
"https://beacons2.gvt2.com/domainreliability/upload",
"https://beacons3.gvt2.com/domainreliability/upload",
@@ -551,10 +552,11 @@ static std::unique_ptr<DomainReliabilityConfig> CreateGoogleConfig(
GURL::Replacements replacements;
replacements.SetPathStr(kGoogleOriginSpecificCollectorPathString);
config->collectors.push_back(
- new GURL(config->origin.ReplaceComponents(replacements)));
+ base::MakeUnique<GURL>(config->origin.ReplaceComponents(replacements)));
}
for (size_t i = 0; i < arraysize(kGoogleStandardCollectors); i++)
- config->collectors.push_back(new GURL(kGoogleStandardCollectors[i]));
+ config->collectors.push_back(
+ base::MakeUnique<GURL>(kGoogleStandardCollectors[i]));
config->success_sample_rate = 0.05;
config->failure_sample_rate = 1.00;
config->path_prefixes.clear();
diff --git a/chromium/components/domain_reliability/header.cc b/chromium/components/domain_reliability/header.cc
index 1e9cb6af19f..05a637961e7 100644
--- a/chromium/components/domain_reliability/header.cc
+++ b/chromium/components/domain_reliability/header.cc
@@ -7,7 +7,6 @@
#include <stdint.h>
#include <string>
-
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_tokenizer.h"
@@ -158,7 +157,7 @@ bool Unquote(const std::string& in, std::string* out) {
}
bool ParseReportUri(const std::vector<base::StringPiece> in,
- ScopedVector<GURL>* out) {
+ std::vector<std::unique_ptr<GURL>>* out) {
if (in.size() < 1u)
return false;
@@ -170,7 +169,7 @@ bool ParseReportUri(const std::vector<base::StringPiece> in,
GURL url(unquoted);
if (!url.is_valid() || !content::IsOriginSecure(url))
return false;
- out->push_back(new GURL(url));
+ out->push_back(base::MakeUnique<GURL>(url));
}
return true;
@@ -201,7 +200,7 @@ DomainReliabilityHeader::~DomainReliabilityHeader() {}
// static
std::unique_ptr<DomainReliabilityHeader> DomainReliabilityHeader::Parse(
base::StringPiece value) {
- ScopedVector<GURL> report_uri;
+ std::vector<std::unique_ptr<GURL>> report_uri;
base::TimeDelta max_age;
bool include_subdomains = false;
@@ -284,7 +283,7 @@ std::string DomainReliabilityHeader::ToString() const {
DCHECK_EQ(0, max_age_s);
} else {
string += "report-uri=";
- for (const auto* uri : config_->collectors)
+ for (const auto& uri : config_->collectors)
string += uri->spec() + " ";
// Remove trailing space.
string.erase(string.length() - 1, 1);
diff --git a/chromium/components/domain_reliability/header_unittest.cc b/chromium/components/domain_reliability/header_unittest.cc
index 0ba21c30de2..cbb0b1fe42e 100644
--- a/chromium/components/domain_reliability/header_unittest.cc
+++ b/chromium/components/domain_reliability/header_unittest.cc
@@ -28,8 +28,9 @@ class DomainReliabilityHeaderTest : public testing::Test {
std::unique_ptr<DomainReliabilityHeader> parsed_;
};
-bool CheckReportUris(const char* pipe_separated_expected_report_uris,
- const ScopedVector<GURL>& actual_report_uris) {
+bool CheckReportUris(
+ const char* pipe_separated_expected_report_uris,
+ const std::vector<std::unique_ptr<GURL>>& actual_report_uris) {
if (!pipe_separated_expected_report_uris)
return actual_report_uris.empty();
diff --git a/chromium/components/domain_reliability/monitor.cc b/chromium/components/domain_reliability/monitor.cc
index 2da1d509dd1..d5a437fba09 100644
--- a/chromium/components/domain_reliability/monitor.cc
+++ b/chromium/components/domain_reliability/monitor.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
@@ -90,7 +91,6 @@ DomainReliabilityMonitor::DomainReliabilityMonitor(
discard_uploads_set_(false),
weak_factory_(this) {
DCHECK(OnPrefThread());
- net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
}
DomainReliabilityMonitor::DomainReliabilityMonitor(
@@ -110,22 +110,25 @@ DomainReliabilityMonitor::DomainReliabilityMonitor(
discard_uploads_set_(false),
weak_factory_(this) {
DCHECK(OnPrefThread());
- net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
}
DomainReliabilityMonitor::~DomainReliabilityMonitor() {
- if (moved_to_network_thread_)
+ if (moved_to_network_thread_) {
+ net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
DCHECK(OnNetworkThread());
- else
+ } else {
DCHECK(OnPrefThread());
-
- net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+ }
}
void DomainReliabilityMonitor::MoveToNetworkThread() {
DCHECK(OnPrefThread());
DCHECK(!moved_to_network_thread_);
+ network_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&net::NetworkChangeNotifier::AddNetworkChangeObserver,
+ base::Unretained(this)));
moved_to_network_thread_ = true;
}
@@ -155,6 +158,10 @@ void DomainReliabilityMonitor::InitURLRequestContext(
url_request_context_getter);
}
+void DomainReliabilityMonitor::Shutdown() {
+ uploader_->Shutdown();
+}
+
void DomainReliabilityMonitor::AddBakedInConfigs() {
DCHECK(OnNetworkThread());
DCHECK(moved_to_network_thread_);
@@ -249,6 +256,10 @@ DomainReliabilityContext* DomainReliabilityMonitor::AddContextForTesting(
return context_manager_.AddContextForConfig(std::move(config));
}
+void DomainReliabilityMonitor::ForceUploadsForTesting() {
+ dispatcher_.RunAllTasksForTesting();
+}
+
std::unique_ptr<DomainReliabilityContext>
DomainReliabilityMonitor::CreateContextForConfig(
std::unique_ptr<const DomainReliabilityConfig> config) {
@@ -287,6 +298,10 @@ DomainReliabilityMonitor::RequestInfo::~RequestInfo() {}
// static
bool DomainReliabilityMonitor::RequestInfo::ShouldReportRequest(
const DomainReliabilityMonitor::RequestInfo& request) {
+ // Always report upload requests, even though they have DO_NOT_SEND_COOKIES.
+ if (request.upload_depth > 0)
+ return true;
+
// Don't report requests that weren't supposed to send cookies.
if (request.load_flags & net::LOAD_DO_NOT_SEND_COOKIES)
return false;
diff --git a/chromium/components/domain_reliability/monitor.h b/chromium/components/domain_reliability/monitor.h
index 8fa3501b1f3..29ba9935e18 100644
--- a/chromium/components/domain_reliability/monitor.h
+++ b/chromium/components/domain_reliability/monitor.h
@@ -34,7 +34,6 @@
#include "net/url_request/url_request_status.h"
namespace base {
-class ThreadChecker;
class Value;
} // namespace base
@@ -89,6 +88,10 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityMonitor
const scoped_refptr<net::URLRequestContextGetter>&
url_request_context_getter);
+ // Shuts down the monitor prior to destruction. Currently, ensures that there
+ // are no pending uploads, to avoid hairy lifetime issues at destruction.
+ void Shutdown();
+
// Populates the monitor with contexts that were configured at compile time.
void AddBakedInConfigs();
@@ -131,6 +134,10 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityMonitor
return context_manager_.contexts_size_for_testing();
}
+ // Forces all pending uploads to run now, even if their minimum delay has not
+ // yet passed.
+ void ForceUploadsForTesting();
+
// DomainReliabilityContext::Factory implementation:
std::unique_ptr<DomainReliabilityContext> CreateContextForConfig(
std::unique_ptr<const DomainReliabilityConfig> config) override;
diff --git a/chromium/components/domain_reliability/monitor_unittest.cc b/chromium/components/domain_reliability/monitor_unittest.cc
index 6b75144b4c4..2e46fda2aec 100644
--- a/chromium/components/domain_reliability/monitor_unittest.cc
+++ b/chromium/components/domain_reliability/monitor_unittest.cc
@@ -68,6 +68,10 @@ class DomainReliabilityMonitorTest : public testing::Test {
monitor_.SetDiscardUploads(false);
}
+ ~DomainReliabilityMonitorTest() override {
+ monitor_.Shutdown();
+ }
+
static RequestInfo MakeRequestInfo() {
RequestInfo request;
request.status = net::URLRequestStatus();
@@ -255,6 +259,22 @@ TEST_F(DomainReliabilityMonitorTest, AtLeastOneBakedInConfig) {
DCHECK(kBakedInJsonConfigs[0] != nullptr);
}
+// Make sure the monitor does log uploads, even though they have
+// LOAD_DO_NOT_SEND_COOKIES.
+TEST_F(DomainReliabilityMonitorTest, Upload) {
+ DomainReliabilityContext* context = CreateAndAddContext();
+
+ RequestInfo request = MakeRequestInfo();
+ request.url = GURL("http://example/");
+ request.load_flags =
+ net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES;
+ request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET);
+ request.upload_depth = 1;
+ OnRequestLegComplete(request);
+
+ EXPECT_EQ(1u, CountQueuedBeacons(context));
+}
+
// Will fail when baked-in configs expire, as a reminder to update them.
// (Contact juliatuttle@chromium.org if this starts failing.)
TEST_F(DomainReliabilityMonitorTest, AddBakedInConfigs) {
diff --git a/chromium/components/domain_reliability/quic_error_mapping.cc b/chromium/components/domain_reliability/quic_error_mapping.cc
index d23fc4b26b4..7a370264553 100644
--- a/chromium/components/domain_reliability/quic_error_mapping.cc
+++ b/chromium/components/domain_reliability/quic_error_mapping.cc
@@ -255,7 +255,7 @@ const struct QuicErrorMapping {
};
// Must be updated any time a net::QuicErrorCode is deprecated in
-// net/quic/core/quic_protocol.h.
+// net/quic/core/quic_packets.h.
const int kDeprecatedQuicErrorCount = 4;
const int kActiveQuicErrorCount =
net::QUIC_LAST_ERROR - kDeprecatedQuicErrorCount;
diff --git a/chromium/components/domain_reliability/quic_error_mapping.h b/chromium/components/domain_reliability/quic_error_mapping.h
index 4d1146bbd4b..f9d373b27d6 100644
--- a/chromium/components/domain_reliability/quic_error_mapping.h
+++ b/chromium/components/domain_reliability/quic_error_mapping.h
@@ -7,7 +7,7 @@
#include <string>
-#include "net/quic/core/quic_protocol.h"
+#include "net/quic/core/quic_packets.h"
// N.B. This file and the .cc are separate from util.h/.cc so that they can be
// independently updated by folks working on QUIC when new errors are added.
diff --git a/chromium/components/domain_reliability/scheduler.h b/chromium/components/domain_reliability/scheduler.h
index 6b96546c621..97f598fa1a4 100644
--- a/chromium/components/domain_reliability/scheduler.h
+++ b/chromium/components/domain_reliability/scheduler.h
@@ -23,7 +23,6 @@ class Value;
namespace domain_reliability {
-struct DomainReliabilityConfig;
class MockableTime;
// Determines when an upload should be scheduled. A domain's config will
diff --git a/chromium/components/domain_reliability/service.cc b/chromium/components/domain_reliability/service.cc
index cdc91d7dc27..92eec4238d2 100644
--- a/chromium/components/domain_reliability/service.cc
+++ b/chromium/components/domain_reliability/service.cc
@@ -18,6 +18,15 @@ namespace domain_reliability {
namespace {
+void AddContextForTestingOnNetworkTaskRunner(
+ base::WeakPtr<DomainReliabilityMonitor> monitor,
+ std::unique_ptr<const DomainReliabilityConfig> config) {
+ if (!monitor)
+ return;
+
+ monitor->AddContextForTesting(std::move(config));
+}
+
std::unique_ptr<base::Value> GetWebUIDataOnNetworkTaskRunner(
base::WeakPtr<DomainReliabilityMonitor> monitor) {
if (!monitor) {
@@ -83,6 +92,36 @@ class DomainReliabilityServiceImpl : public DomainReliabilityService {
callback);
}
+ void SetDiscardUploadsForTesting(bool discard_uploads) override {
+ DCHECK(network_task_runner_.get());
+
+ network_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&DomainReliabilityMonitor::SetDiscardUploads,
+ monitor_,
+ discard_uploads));
+ }
+
+ void AddContextForTesting(
+ std::unique_ptr<const DomainReliabilityConfig> config) override {
+ DCHECK(network_task_runner_.get());
+
+ network_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AddContextForTestingOnNetworkTaskRunner,
+ monitor_,
+ base::Passed(&config)));
+ }
+
+ void ForceUploadsForTesting() override {
+ DCHECK(network_task_runner_.get());
+
+ network_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&DomainReliabilityMonitor::ForceUploadsForTesting,
+ monitor_));
+ }
+
private:
std::string upload_reporter_string_;
base::WeakPtr<DomainReliabilityMonitor> monitor_;
diff --git a/chromium/components/domain_reliability/service.h b/chromium/components/domain_reliability/service.h
index c584aa39e26..a96c9fac803 100644
--- a/chromium/components/domain_reliability/service.h
+++ b/chromium/components/domain_reliability/service.h
@@ -13,11 +13,11 @@
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "components/domain_reliability/clear_mode.h"
+#include "components/domain_reliability/config.h"
#include "components/domain_reliability/domain_reliability_export.h"
#include "components/keyed_service/core/keyed_service.h"
class GURL;
-class PrefService;
namespace base {
class Value;
@@ -64,6 +64,14 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityService
const base::Callback<void(std::unique_ptr<base::Value>)>& callback)
const = 0;
+ virtual void SetDiscardUploadsForTesting(
+ bool discard_uploads) = 0;
+
+ virtual void AddContextForTesting(
+ std::unique_ptr<const DomainReliabilityConfig> config) = 0;
+
+ virtual void ForceUploadsForTesting() = 0;
+
protected:
DomainReliabilityService();
diff --git a/chromium/components/domain_reliability/test_util.cc b/chromium/components/domain_reliability/test_util.cc
index bb060fa069c..5bfbb2e195a 100644
--- a/chromium/components/domain_reliability/test_util.cc
+++ b/chromium/components/domain_reliability/test_util.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/memory/ptr_util.h"
#include "components/domain_reliability/scheduler.h"
#include "net/url_request/url_request_status.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -169,7 +170,8 @@ std::unique_ptr<DomainReliabilityConfig> MakeTestConfigWithOrigin(
const GURL& origin) {
DomainReliabilityConfig* config = new DomainReliabilityConfig();
config->origin = origin;
- config->collectors.push_back(new GURL("https://exampleuploader/upload"));
+ config->collectors.push_back(
+ base::MakeUnique<GURL>("https://exampleuploader/upload"));
config->failure_sample_rate = 1.0;
config->success_sample_rate = 0.0;
diff --git a/chromium/components/domain_reliability/test_util.h b/chromium/components/domain_reliability/test_util.h
index 48dcb801be0..252f13554ab 100644
--- a/chromium/components/domain_reliability/test_util.h
+++ b/chromium/components/domain_reliability/test_util.h
@@ -15,10 +15,6 @@
#include "net/base/host_port_pair.h"
#include "url/gurl.h"
-namespace net {
-class URLRequestStatus;
-} // namespace net
-
namespace domain_reliability {
// A simple test callback that remembers whether it's been called.
diff --git a/chromium/components/domain_reliability/uploader.cc b/chromium/components/domain_reliability/uploader.cc
index 3ded0849d33..19dd56d4902 100644
--- a/chromium/components/domain_reliability/uploader.cc
+++ b/chromium/components/domain_reliability/uploader.cc
@@ -8,9 +8,9 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/supports_user_data.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/domain_reliability/util.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
@@ -59,9 +59,12 @@ class DomainReliabilityUploaderImpl
net::URLRequestContextGetter>& url_request_context_getter)
: time_(time),
url_request_context_getter_(url_request_context_getter),
- discard_uploads_(true) {}
+ discard_uploads_(true),
+ shutdown_(false) {}
- ~DomainReliabilityUploaderImpl() override {}
+ ~DomainReliabilityUploaderImpl() override {
+ DCHECK(shutdown_);
+ }
// DomainReliabilityUploader implementation:
void UploadReport(
@@ -72,7 +75,7 @@ class DomainReliabilityUploaderImpl
VLOG(1) << "Uploading report to " << upload_url;
VLOG(2) << "Report JSON: " << report_json;
- if (discard_uploads_) {
+ if (discard_uploads_ || shutdown_) {
VLOG(1) << "Discarding report instead of uploading.";
UploadResult result;
result.status = UploadResult::SUCCESS;
@@ -83,8 +86,6 @@ class DomainReliabilityUploaderImpl
std::unique_ptr<net::URLFetcher> owned_fetcher =
net::URLFetcher::Create(0, upload_url, net::URLFetcher::POST, this);
net::URLFetcher* fetcher = owned_fetcher.get();
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher, data_use_measurement::DataUseUserData::DOMAIN_RELIABILITY);
fetcher->SetRequestContext(url_request_context_getter_.get());
fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
net::LOAD_DO_NOT_SAVE_COOKIES);
@@ -95,7 +96,7 @@ class DomainReliabilityUploaderImpl
UploadUserData::CreateCreateDataCallback(max_upload_depth + 1));
fetcher->Start();
- upload_callbacks_[fetcher] = {std::move(owned_fetcher), callback};
+ uploads_[fetcher] = {std::move(owned_fetcher), callback};
base::TimeTicks now = base::TimeTicks::Now();
if (!last_upload_start_time_.is_null()) {
@@ -110,12 +111,18 @@ class DomainReliabilityUploaderImpl
VLOG(1) << "Setting discard_uploads to " << discard_uploads;
}
+ void Shutdown() override {
+ DCHECK(!shutdown_);
+ shutdown_ = true;
+ uploads_.clear();
+ }
+
// net::URLFetcherDelegate implementation:
void OnURLFetchComplete(const net::URLFetcher* fetcher) override {
DCHECK(fetcher);
- auto callback_it = upload_callbacks_.find(fetcher);
- DCHECK(callback_it != upload_callbacks_.end());
+ auto callback_it = uploads_.find(fetcher);
+ DCHECK(callback_it != uploads_.end());
int net_error = GetNetErrorFromURLRequestStatus(fetcher->GetStatus());
int http_response_code = fetcher->GetResponseCode();
@@ -148,7 +155,7 @@ class DomainReliabilityUploaderImpl
&result);
callback_it->second.second.Run(result);
- upload_callbacks_.erase(callback_it);
+ uploads_.erase(callback_it);
}
private:
@@ -158,9 +165,10 @@ class DomainReliabilityUploaderImpl
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
std::map<const net::URLFetcher*,
std::pair<std::unique_ptr<net::URLFetcher>, UploadCallback>>
- upload_callbacks_;
+ uploads_;
bool discard_uploads_;
base::TimeTicks last_upload_start_time_;
+ bool shutdown_;
};
} // namespace
@@ -178,6 +186,12 @@ std::unique_ptr<DomainReliabilityUploader> DomainReliabilityUploader::Create(
}
// static
+bool DomainReliabilityUploader::OriginatedFromDomainReliability(
+ const net::URLRequest& request) {
+ return request.GetUserData(UploadUserData::kUserDataKey) != nullptr;
+}
+
+// static
int DomainReliabilityUploader::GetURLRequestUploadDepth(
const net::URLRequest& request) {
UploadUserData* data = static_cast<UploadUserData*>(
@@ -187,4 +201,6 @@ int DomainReliabilityUploader::GetURLRequestUploadDepth(
return data->depth();
}
+void DomainReliabilityUploader::Shutdown() {}
+
} // namespace domain_reliability
diff --git a/chromium/components/domain_reliability/uploader.h b/chromium/components/domain_reliability/uploader.h
index 700a4f8c294..c72148f3dfe 100644
--- a/chromium/components/domain_reliability/uploader.h
+++ b/chromium/components/domain_reliability/uploader.h
@@ -15,7 +15,6 @@
#include "url/gurl.h"
namespace net {
-class URLFetcher;
class URLRequest;
class URLRequestContextGetter;
} // namespace net
@@ -56,6 +55,9 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityUploader {
const scoped_refptr<net::URLRequestContextGetter>&
url_request_context_getter);
+ // Returns true if the request originated from domain reliability uploader.
+ static bool OriginatedFromDomainReliability(const net::URLRequest& request);
+
// Uploads |report_json| to |upload_url| and calls |callback| when the upload
// has either completed or failed.
virtual void UploadReport(const std::string& report_json,
@@ -63,6 +65,11 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityUploader {
const GURL& upload_url,
const UploadCallback& callback) = 0;
+ // Shuts down the uploader prior to destruction. Currently, terminates pending
+ // uploads and prevents the uploader from starting new ones to avoid hairy
+ // lifetime issues at destruction.
+ virtual void Shutdown();
+
virtual void set_discard_uploads(bool discard_uploads) = 0;
static int GetURLRequestUploadDepth(const net::URLRequest& request);
diff --git a/chromium/components/domain_reliability/uploader_unittest.cc b/chromium/components/domain_reliability/uploader_unittest.cc
index 4aeb6ddc024..0b99fc0655d 100644
--- a/chromium/components/domain_reliability/uploader_unittest.cc
+++ b/chromium/components/domain_reliability/uploader_unittest.cc
@@ -91,7 +91,7 @@ class UploadMockURLRequestJob : public net::URLRequestJob {
if (result_.net_error == net::OK)
NotifyHeadersComplete();
- else
+ else if (result_.net_error != net::ERR_IO_PENDING)
NotifyStartError(net::URLRequestStatus::FromError(result_.net_error));
}
@@ -111,7 +111,7 @@ class UploadMockURLRequestJob : public net::URLRequestJob {
class UploadInterceptor : public net::URLRequestInterceptor {
public:
- UploadInterceptor() : last_upload_depth_(-1) {}
+ UploadInterceptor() : request_count_(0), last_upload_depth_(-1) {}
~UploadInterceptor() override {
EXPECT_TRUE(results_.empty());
@@ -127,6 +127,8 @@ class UploadInterceptor : public net::URLRequestInterceptor {
last_upload_depth_ =
DomainReliabilityUploader::GetURLRequestUploadDepth(*request);
+ ++request_count_;
+
return new UploadMockURLRequestJob(request, delegate, result);
}
@@ -155,10 +157,12 @@ class UploadInterceptor : public net::URLRequestInterceptor {
results_.push_back(result);
}
+ int request_count() const { return request_count_; }
int last_upload_depth() const { return last_upload_depth_; }
private:
mutable std::list<MockUploadResult> results_;
+ mutable int request_count_;
mutable int last_upload_depth_;
};
@@ -204,6 +208,10 @@ class DomainReliabilityUploaderTest : public testing::Test {
DomainReliabilityUploader* uploader() const { return uploader_.get(); }
UploadInterceptor* interceptor() const { return interceptor_; }
+ scoped_refptr<net::TestURLRequestContextGetter>
+ url_request_context_getter() {
+ return url_request_context_getter_;
+ }
private:
base::MessageLoopForIO message_loop_;
@@ -214,6 +222,7 @@ class DomainReliabilityUploaderTest : public testing::Test {
};
TEST_F(DomainReliabilityUploaderTest, Null) {
+ uploader()->Shutdown();
}
TEST_F(DomainReliabilityUploaderTest, SuccessfulUpload) {
@@ -224,6 +233,8 @@ TEST_F(DomainReliabilityUploaderTest, SuccessfulUpload) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, c.called_count());
EXPECT_TRUE(c.last_result().is_success());
+
+ uploader()->Shutdown();
}
TEST_F(DomainReliabilityUploaderTest, NetworkErrorUpload) {
@@ -234,6 +245,8 @@ TEST_F(DomainReliabilityUploaderTest, NetworkErrorUpload) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, c.called_count());
EXPECT_TRUE(c.last_result().is_failure());
+
+ uploader()->Shutdown();
}
TEST_F(DomainReliabilityUploaderTest, ServerErrorUpload) {
@@ -244,6 +257,8 @@ TEST_F(DomainReliabilityUploaderTest, ServerErrorUpload) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, c.called_count());
EXPECT_TRUE(c.last_result().is_failure());
+
+ uploader()->Shutdown();
}
TEST_F(DomainReliabilityUploaderTest, RetryAfterUpload) {
@@ -256,6 +271,8 @@ TEST_F(DomainReliabilityUploaderTest, RetryAfterUpload) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, c.called_count());
EXPECT_TRUE(c.last_result().is_retry_after());
+
+ uploader()->Shutdown();
}
TEST_F(DomainReliabilityUploaderTest, UploadDepth1) {
@@ -267,6 +284,8 @@ TEST_F(DomainReliabilityUploaderTest, UploadDepth1) {
EXPECT_EQ(1u, c.called_count());
EXPECT_EQ(1, interceptor()->last_upload_depth());
+
+ uploader()->Shutdown();
}
TEST_F(DomainReliabilityUploaderTest, UploadDepth2) {
@@ -278,6 +297,34 @@ TEST_F(DomainReliabilityUploaderTest, UploadDepth2) {
EXPECT_EQ(1u, c.called_count());
EXPECT_EQ(2, interceptor()->last_upload_depth());
+
+ uploader()->Shutdown();
+}
+
+TEST_F(DomainReliabilityUploaderTest, UploadCanceledAtShutdown) {
+ interceptor()->ExpectRequestAndReturnError(net::ERR_IO_PENDING);
+
+ TestUploadCallback c;
+ uploader()->UploadReport("{}", 1, GURL(kUploadURL), c.callback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, interceptor()->request_count());
+ EXPECT_EQ(0u, c.called_count());
+
+ uploader()->Shutdown();
+
+ EXPECT_EQ(0u, c.called_count());
+
+ url_request_context_getter()->GetURLRequestContext()->AssertNoURLRequests();
+}
+
+TEST_F(DomainReliabilityUploaderTest, NoUploadAfterShutdown) {
+ uploader()->Shutdown();
+
+ TestUploadCallback c;
+ uploader()->UploadReport("{}", 1, GURL(kUploadURL), c.callback());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1u, c.called_count());
+ EXPECT_EQ(0, interceptor()->request_count());
}
} // namespace
diff --git a/chromium/components/domain_reliability/util.cc b/chromium/components/domain_reliability/util.cc
index b95a0ba864e..20bf7568376 100644
--- a/chromium/components/domain_reliability/util.cc
+++ b/chromium/components/domain_reliability/util.cc
@@ -124,6 +124,7 @@ std::string GetDomainReliabilityProtocol(
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_34:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_35:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_36:
+ case net::HttpResponseInfo::CONNECTION_INFO_QUIC_37:
return "QUIC";
case net::HttpResponseInfo::NUM_OF_CONNECTION_INFOS:
NOTREACHED();
@@ -169,22 +170,23 @@ void GetUploadResultFromResponseDetails(
return;
}
-// N.B. This uses a ScopedVector because that's what JSONValueConverter uses
-// for repeated fields of any type, and Config uses JSONValueConverter to parse
-// JSON configs.
-GURL SanitizeURLForReport(const GURL& beacon_url,
- const GURL& collector_url,
- const ScopedVector<std::string>& path_prefixes) {
+// N.B. This uses a std::vector<std::unique_ptr<>> because that's what
+// JSONValueConverter uses for repeated fields of any type, and Config uses
+// JSONValueConverter to parse JSON configs.
+GURL SanitizeURLForReport(
+ const GURL& beacon_url,
+ const GURL& collector_url,
+ const std::vector<std::unique_ptr<std::string>>& path_prefixes) {
if (CanReportFullBeaconURLToCollector(beacon_url, collector_url))
return beacon_url.GetAsReferrer();
std::string path = beacon_url.path();
const std::string empty_path;
const std::string* longest_path_prefix = &empty_path;
- for (const std::string* path_prefix : path_prefixes) {
+ for (const auto& path_prefix : path_prefixes) {
if (path.substr(0, path_prefix->length()) == *path_prefix &&
path_prefix->length() > longest_path_prefix->length()) {
- longest_path_prefix = path_prefix;
+ longest_path_prefix = path_prefix.get();
}
}
diff --git a/chromium/components/domain_reliability/util.h b/chromium/components/domain_reliability/util.h
index f8bb2040534..3ef2e7543f6 100644
--- a/chromium/components/domain_reliability/util.h
+++ b/chromium/components/domain_reliability/util.h
@@ -11,7 +11,6 @@
#include "base/callback_forward.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "base/time/clock.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
@@ -51,9 +50,10 @@ void GetUploadResultFromResponseDetails(
base::TimeDelta retry_after,
DomainReliabilityUploader::UploadResult* result);
-GURL SanitizeURLForReport(const GURL& beacon_url,
- const GURL& collector_url,
- const ScopedVector<std::string>& path_prefixes);
+GURL SanitizeURLForReport(
+ const GURL& beacon_url,
+ const GURL& collector_url,
+ const std::vector<std::unique_ptr<std::string>>& path_prefixes);
// Mockable wrapper around TimeTicks::Now and Timer. Mock version is in
// test_util.h.
diff --git a/chromium/components/error_page/common/BUILD.gn b/chromium/components/error_page/common/BUILD.gn
index 98cd30dbe1c..16eaa845971 100644
--- a/chromium/components/error_page/common/BUILD.gn
+++ b/chromium/components/error_page/common/BUILD.gn
@@ -17,7 +17,7 @@ static_library("common") {
deps = [
"//base",
"//base:i18n",
- "//components/offline_pages:switches",
+ "//components/offline_pages/core:switches",
"//components/strings",
"//components/url_formatter",
"//net",
diff --git a/chromium/components/error_page/common/localized_error.cc b/chromium/components/error_page/common/localized_error.cc
index 825a9d80461..5192a110541 100644
--- a/chromium/components/error_page/common/localized_error.cc
+++ b/chromium/components/error_page/common/localized_error.cc
@@ -30,13 +30,14 @@
#include "net/base/net_errors.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
+#include "url/origin.h"
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif
#if defined(OS_ANDROID)
-#include "components/offline_pages/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_feature.h"
#endif
namespace error_page {
@@ -65,6 +66,7 @@ enum NAV_SUGGESTIONS {
SUGGEST_COMPLETE_SETUP = 1 << 11,
// Reload page suggestion for pages created by a post.
SUGGEST_REPOST_RELOAD = 1 << 12,
+ SUGGEST_NAVIGATE_TO_ORIGIN = 1 << 13,
};
enum SHOW_BUTTONS {
@@ -82,6 +84,7 @@ struct LocalizedErrorMap {
int buttons; // Which buttons if any to show.
};
+// clang-format off
const LocalizedErrorMap net_error_options[] = {
{net::ERR_TIMED_OUT,
IDS_ERRORPAGES_HEADING_NOT_AVAILABLE,
@@ -269,6 +272,12 @@ const LocalizedErrorMap net_error_options[] = {
SUGGEST_DISABLE_EXTENSION,
SHOW_BUTTON_RELOAD,
},
+ {net::ERR_BLOCKED_BY_XSS_AUDITOR,
+ IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING,
+ IDS_ERRORPAGES_SUMMARY_BLOCKED_BY_XSS_AUDITOR,
+ SUGGEST_NAVIGATE_TO_ORIGIN,
+ SHOW_NO_BUTTONS,
+ },
{net::ERR_NETWORK_CHANGED,
IDS_ERRORPAGES_HEADING_CONNECTION_INTERRUPTED,
IDS_ERRORPAGES_SUMMARY_NETWORK_CHANGED,
@@ -305,7 +314,14 @@ const LocalizedErrorMap net_error_options[] = {
SUGGEST_NONE,
SHOW_NO_BUTTONS,
},
+ {net::ERR_BLOCKED_BY_RESPONSE,
+ IDS_ERRORPAGES_HEADING_BLOCKED,
+ IDS_ERRORPAGES_SUMMARY_CONNECTION_REFUSED,
+ SUGGEST_NONE,
+ SHOW_NO_BUTTONS
+ },
};
+// clang-format on
// Special error page to be used in the case of navigating back to a page
// generated by a POST. LocalizedError::HasStrings expects this net error code
@@ -614,6 +630,23 @@ void GetSuggestionsSummaryList(int error_code,
}
DCHECK(!IsSuggested(suggestions, SUGGEST_REPOST_RELOAD));
+ if (IsOnlySuggestion(suggestions, SUGGEST_NAVIGATE_TO_ORIGIN)) {
+ DCHECK(suggestions_summary_list->empty());
+ DCHECK(!(suggestions & ~SUGGEST_NAVIGATE_TO_ORIGIN));
+ url::Origin failed_origin(failed_url);
+ if (failed_origin.unique())
+ return;
+
+ auto suggestion = base::MakeUnique<base::DictionaryValue>();
+ suggestion->SetString("summary",
+ l10n_util::GetStringUTF16(
+ IDS_ERRORPAGES_SUGGESTION_NAVIGATE_TO_ORIGIN));
+ suggestion->SetString("originURL", failed_origin.Serialize());
+ suggestions_summary_list->Append(std::move(suggestion));
+ return;
+ }
+ DCHECK(!IsSuggested(suggestions, SUGGEST_NAVIGATE_TO_ORIGIN));
+
if (IsOnlySuggestion(suggestions, SUGGEST_LEARNMORE)) {
DCHECK(suggestions_summary_list->empty());
AddLinkedSuggestionToList(error_code, locale, suggestions_summary_list,
@@ -1004,8 +1037,9 @@ void LocalizedError::GetStrings(
}
#if defined(OS_ANDROID)
- if (!reload_visible && !show_saved_copy_visible && !is_incognito &&
- failed_url.is_valid() && failed_url.SchemeIsHTTPOrHTTPS() &&
+ if (!is_post && !reload_visible && !show_saved_copy_visible &&
+ !is_incognito && failed_url.is_valid() &&
+ failed_url.SchemeIsHTTPOrHTTPS() &&
offline_pages::IsOfflinePagesAsyncDownloadEnabled()) {
std::unique_ptr<base::DictionaryValue> download_button =
base::MakeUnique<base::DictionaryValue>();
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 3b90182b743..f040f97f3d8 100644
--- a/chromium/components/error_page/renderer/net_error_helper_core.cc
+++ b/chromium/components/error_page/renderer/net_error_helper_core.cc
@@ -6,6 +6,7 @@
#include <stddef.h>
+#include <memory>
#include <set>
#include <string>
#include <utility>
@@ -20,7 +21,6 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
#include "base/strings/string16.h"
@@ -103,7 +103,7 @@ struct NavigationCorrection {
struct NavigationCorrectionResponse {
std::string event_id;
std::string fingerprint;
- ScopedVector<NavigationCorrection> corrections;
+ std::vector<std::unique_ptr<NavigationCorrection>> corrections;
static void RegisterJSONConverter(
base::JSONValueConverter<NavigationCorrectionResponse>* converter) {
@@ -302,9 +302,8 @@ std::unique_ptr<ErrorPageParams> CreateErrorPageParams(
std::unique_ptr<ErrorPageParams> params(new ErrorPageParams());
params->override_suggestions.reset(new base::ListValue());
std::unique_ptr<base::ListValue> parsed_corrections(new base::ListValue());
- for (ScopedVector<NavigationCorrection>::const_iterator it =
- response.corrections.begin();
- it != response.corrections.end(); ++it) {
+ for (auto it = response.corrections.begin(); it != response.corrections.end();
+ ++it) {
// Doesn't seem like a good idea to show these.
if ((*it)->is_porn || (*it)->is_soft_porn)
continue;
@@ -497,6 +496,8 @@ bool NetErrorHelperCore::IsReloadableError(
// handshake_failure alert.
// https://crbug.com/431387
info.error.reason != net::ERR_SSL_PROTOCOL_ERROR &&
+ // Do not trigger for XSS Auditor violations.
+ info.error.reason != net::ERR_BLOCKED_BY_XSS_AUDITOR &&
!info.was_failed_post &&
// Don't auto-reload non-http/https schemas.
// https://crbug.com/471713
@@ -656,6 +657,9 @@ void NetErrorHelperCore::OnFinishLoad(FrameType frame_type) {
RecordEvent(NETWORK_ERROR_PAGE_CACHED_COPY_BUTTON_SHOWN);
}
+ delegate_->SetIsShowingDownloadButton(
+ committed_error_page_info_->download_button_in_page);
+
delegate_->EnablePageHelperFunctions();
if (committed_error_page_info_->needs_load_navigation_corrections) {
diff --git a/chromium/components/error_page/renderer/net_error_helper_core.h b/chromium/components/error_page/renderer/net_error_helper_core.h
index 1551553e9ff..57d51941b01 100644
--- a/chromium/components/error_page/renderer/net_error_helper_core.h
+++ b/chromium/components/error_page/renderer/net_error_helper_core.h
@@ -15,10 +15,6 @@
#include "components/error_page/common/net_error_info.h"
#include "url/gurl.h"
-namespace base {
-class ListValue;
-}
-
namespace blink {
struct WebURLError;
}
@@ -114,6 +110,9 @@ class NetErrorHelperCore {
// Schedule to download the page at a later time.
virtual void DownloadPageLater() = 0;
+ // Inform that download button is being shown in the error page.
+ virtual void SetIsShowingDownloadButton(bool show) = 0;
+
protected:
virtual ~Delegate() {}
};
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 79d84d68691..db07d6862e2 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
@@ -435,6 +435,8 @@ class NetErrorHelperCoreTest : public testing::Test,
download_count_++;
}
+ void SetIsShowingDownloadButton(bool show) override {}
+
void SendTrackingRequest(const GURL& tracking_url,
const std::string& tracking_request_body) override {
last_tracking_url_ = tracking_url;
diff --git a/chromium/components/error_page_strings.grdp b/chromium/components/error_page_strings.grdp
index 75a747abd06..89f9593f81d 100644
--- a/chromium/components/error_page_strings.grdp
+++ b/chromium/components/error_page_strings.grdp
@@ -106,6 +106,9 @@
<message name="IDS_ERRORPAGES_SUGGESTION_UNSUPPORTED_CIPHER_BODY" desc="The detailed explanation body text displayed for SSL cipher and version errors.">
The client and server don't support a common SSL protocol version or cipher suite.
</message>
+ <message name="IDS_ERRORPAGES_SUGGESTION_NAVIGATE_TO_ORIGIN" desc="When a webpage fails to load, sometimes we suggest to the user that they might navigate to the page's origin.">
+ Try <ph name="BEGIN_LINK">&lt;a jsvalues="href:originURL;.jstdata:$this" onmousedown="linkClicked(this.jstdata)"&gt;</ph>visiting the site's homepage<ph name="END_LINK">&lt;/a&gt;</ph>.
+ </message>
<message name="IDS_ERRORPAGES_HEADING_NOT_AVAILABLE" desc="Heading in the error page when we can't connect to a site.">
This site can’t be reached
</message>
@@ -233,7 +236,7 @@
It may have been moved or deleted.
</message>
<message name="IDS_ERRORPAGES_HEADING_PAGE_NOT_WORKING" desc="Heading in the error page for HTTP 5xx errors, which are considered to be server side errors. Also used for bad responses.">
- The <ph name="HOST_NAME">&lt;span jscontent="hostName"&gt;&lt;/span&gt;<ex>www.whatever.com</ex></ph> page isn’t working
+ This page isn’t working
</message>
<message name="IDS_ERRORPAGES_SUMMARY_WEBSITE_CANNOT_HANDLE_REQUEST" desc="Summary in the error page when the server returns a 501 or 505.">
@@ -268,6 +271,9 @@
<message name="IDS_ERRORPAGES_SUMMARY_BLOCKED_BY_EXTENSION" desc="Summary in the error page when an extension blocks a request.">
Requests to the server have been blocked by an extension.
</message>
+ <message name="IDS_ERRORPAGES_SUMMARY_BLOCKED_BY_XSS_AUDITOR" desc="Summary in the error page when the XSS Auditor blocks a response.">
+ Chrome detected unusual code on this page and blocked it to protect your personal information (for example, passwords, phone numbers, and credit cards).
+ </message>
<message name="IDS_ERRORPAGES_SUMMARY_BLOCKED_BY_ADMINISTRATOR" desc="Summary in the error page when an administrator policy blocks a request.">
The person who set up this computer has chosen to block this site.
</message>
@@ -297,10 +303,10 @@
<ph name="BEGIN_LINK">&lt;a href="#buttons" onclick="toggleHelpBox()"&gt;</ph>Checking the proxy address<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
</message>
</if>
- <message name="IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMIN_SUMMARY" desc="The message displayed in a list of suggestions following a network error suggesting to the user to contact his system administrator. The suggestions list is prefixed with 'Try:'.">
+ <message name="IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMIN_SUMMARY" desc="The message displayed in a list of suggestions following a network error suggesting to the user to contact their system administrator. The suggestions list is prefixed with 'Try:'.">
Contacting the system admin
</message>
- <message name="IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMIN_SUMMARY_STANDALONE" desc="Stand alone suggestion following a network error suggesting to the user to contact his system administrator.">
+ <message name="IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMIN_SUMMARY_STANDALONE" desc="Stand alone suggestion following a network error suggesting to the user to contact their system administrator.">
Try contacting the system admin.
</message>
<message name="IDS_ERRORPAGES_SUGGESTION_LEARNMORE_SUMMARY" desc="The message displayed in a list of suggestions when a webpage fails to load. A link provided to the help center to learn more about the failure. The suggestions list is prefixed with 'Try:'.">
diff --git a/chromium/components/exo/BUILD.gn b/chromium/components/exo/BUILD.gn
index baa1eade05a..227501d3ba5 100644
--- a/chromium/components/exo/BUILD.gn
+++ b/chromium/components/exo/BUILD.gn
@@ -9,6 +9,10 @@ source_set("exo") {
sources = [
"buffer.cc",
"buffer.h",
+ "compositor_frame_sink.cc",
+ "compositor_frame_sink.h",
+ "compositor_frame_sink_holder.cc",
+ "compositor_frame_sink_holder.h",
"display.cc",
"display.h",
"gamepad.cc",
@@ -16,6 +20,7 @@ source_set("exo") {
"keyboard.cc",
"keyboard.h",
"keyboard_delegate.h",
+ "keyboard_device_configuration_delegate.h",
"notification_surface.cc",
"notification_surface.h",
"notification_surface_manager.h",
@@ -49,20 +54,22 @@ source_set("exo") {
"//ash/public/cpp",
"//base",
"//cc",
- "//cc/surfaces:surfaces",
+ "//cc/ipc:interfaces",
+ "//cc/surfaces",
"//device/gamepad",
"//gpu",
"//gpu/command_buffer/client:gles2_interface",
- "//services/ui/public/cpp",
"//skia",
"//ui/aura",
"//ui/compositor",
+ "//ui/display/manager",
+ "//ui/events/devices:devices",
"//ui/gfx",
"//ui/gfx/geometry",
"//ui/gl",
"//ui/views",
"//ui/views/mus",
- "//ui/wm:wm",
+ "//ui/wm",
]
if (is_chromeos) {
@@ -135,7 +142,7 @@ source_set("unit_tests") {
"//ui/keyboard",
"//ui/message_center",
"//ui/views",
- "//ui/wm:wm",
+ "//ui/wm",
]
if (use_ozone) {
@@ -155,6 +162,7 @@ test("exo_unittests") {
"//base",
"//base/test:test_support",
"//device/gamepad:test_helpers",
+ "//mojo/edk/embedder:headers",
]
data_deps = [
@@ -163,10 +171,6 @@ test("exo_unittests") {
"//ash/resources:ash_test_resources_200_percent",
]
- if (use_x11) {
- deps += [ "//tools/xdisplaycheck" ]
- }
-
if (is_linux) {
deps += [ "//components/exo/wayland:unit_tests" ]
}
diff --git a/chromium/components/exo/DEPS b/chromium/components/exo/DEPS
index 6eb071a69a7..6971866e26f 100644
--- a/chromium/components/exo/DEPS
+++ b/chromium/components/exo/DEPS
@@ -4,8 +4,14 @@ include_rules = [
"+chromeos/audio/chromeos_sounds.h",
"+device/gamepad",
"+gpu",
- "+services/ui/public/cpp",
+ "+mojo/public/cpp",
"+third_party/khronos",
"+third_party/skia",
"+ui",
]
+
+specific_include_rules = {
+ "run_all_unittests\.cc": [
+ "+mojo/edk/embedder/embedder.h",
+ ],
+}
diff --git a/chromium/components/exo/buffer.cc b/chromium/components/exo/buffer.cc
index 9b1778d989d..ba6b375bade 100644
--- a/chromium/components/exo/buffer.cc
+++ b/chromium/components/exo/buffer.cc
@@ -24,6 +24,7 @@
#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 "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "ui/aura/env.h"
@@ -285,7 +286,7 @@ gpu::SyncToken Buffer::Texture::CopyTexImage(Texture* destination,
gles2->BindTexture(texture_target_, texture_id_);
DCHECK_NE(image_id_, 0u);
gles2->BindTexImage2DCHROMIUM(texture_target_, image_id_);
- gles2->CopyTextureCHROMIUM(texture_id_, destination->texture_id_,
+ gles2->CopyTextureCHROMIUM(texture_id_, 0, destination->texture_id_, 0,
internalformat_, GL_UNSIGNED_BYTE, false, false,
false);
DCHECK_NE(query_id_, 0u);
@@ -398,27 +399,16 @@ Buffer::Buffer(std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
Buffer::~Buffer() {}
-std::unique_ptr<cc::SingleReleaseCallback> Buffer::ProduceTextureMailbox(
- cc::TextureMailbox* texture_mailbox,
+bool Buffer::ProduceTransferableResource(
+ CompositorFrameSinkHolder* compositor_frame_sink_holder,
+ cc::ResourceId resource_id,
bool secure_output_only,
- bool client_usage) {
+ bool client_usage,
+ cc::TransferableResource* resource) {
DCHECK(attach_count_);
- DLOG_IF(WARNING, use_count_ && client_usage)
+ DLOG_IF(WARNING, !release_contents_callback_.IsCancelled() && client_usage)
<< "Producing a texture mailbox for a buffer that has not been released";
- // Some clients think that they can reuse a buffer before it's released by
- // performing a fast blit into the buffer. This behavior is bad as it prevents
- // the client from knowing when the buffer is actually released (e.g. the
- // release notification for the previous use of buffer can arrive after the
- // buffer has been reused). We stop running the release callback when this
- // type of behavior is detected as having the buffer always be busy will
- // result in fewer drawing artifacts.
- if (use_count_ && client_usage)
- release_callback_.Reset();
-
- // Increment the use count for this buffer.
- ++use_count_;
-
// If textures are lost, destroy them to ensure that we create new ones below.
if (contents_texture_ && contents_texture_->IsLost())
contents_texture_.reset();
@@ -432,10 +422,20 @@ std::unique_ptr<cc::SingleReleaseCallback> Buffer::ProduceTextureMailbox(
context_factory->SharedMainThreadContextProvider();
if (!context_provider) {
DLOG(WARNING) << "Failed to acquire a context provider";
- Release(); // Decrements the use count
- return nullptr;
+ resource->id = 0;
+ resource->size = gfx::Size();
+ return false;
}
+ // The reference to the CompositorFrameSinkHolder keeps it alive until a
+ // release callback is received.
+ compositor_frame_sink_holder_ = compositor_frame_sink_holder;
+
+ resource->id = resource_id;
+ resource->format = cc::RGBA_8888;
+ resource->filter = GL_LINEAR;
+ resource->size = gpu_memory_buffer_->GetSize();
+
// Create a new image texture for |gpu_memory_buffer_| with |texture_target_|
// if one doesn't already exist. The contents of this buffer are copied to
// |texture| using a call to CopyTexImage.
@@ -444,24 +444,30 @@ std::unique_ptr<cc::SingleReleaseCallback> Buffer::ProduceTextureMailbox(
context_factory, context_provider.get(), gpu_memory_buffer_.get(),
texture_target_, query_type_);
}
+ Texture* contents_texture = contents_texture_.get();
- if (use_zero_copy_) {
- // Zero-copy means using the contents texture directly.
- Texture* texture = contents_texture_.get();
+ // Cancel pending contents release callback.
+ release_contents_callback_.Reset(
+ base::Bind(&Buffer::ReleaseContents, base::Unretained(this)));
- // This binds the latest contents of this buffer to |texture|.
- gpu::SyncToken sync_token = texture->BindTexImage();
+ // Zero-copy means using the contents texture directly.
+ if (use_zero_copy_) {
+ // This binds the latest contents of this buffer to |contents_texture|.
+ gpu::SyncToken sync_token = contents_texture->BindTexImage();
+ resource->mailbox_holder = gpu::MailboxHolder(contents_texture->mailbox(),
+ sync_token, texture_target_);
+ resource->is_overlay_candidate = is_overlay_candidate_;
- *texture_mailbox =
- cc::TextureMailbox(texture->mailbox(), sync_token, texture_target_,
- gpu_memory_buffer_->GetSize(), is_overlay_candidate_,
- secure_output_only);
// The contents texture will be released when no longer used by the
// compositor.
- return cc::SingleReleaseCallback::Create(
- base::Bind(&Buffer::Texture::ReleaseTexImage, base::Unretained(texture),
+ compositor_frame_sink_holder_->SetResourceReleaseCallback(
+ resource_id,
+ base::Bind(&Buffer::Texture::ReleaseTexImage,
+ base::Unretained(contents_texture),
base::Bind(&Buffer::ReleaseContentsTexture, AsWeakPtr(),
- base::Passed(&contents_texture_))));
+ base::Passed(&contents_texture_),
+ release_contents_callback_.callback())));
+ return true;
}
// Create a mailbox texture that we copy the buffer contents to.
@@ -469,38 +475,43 @@ std::unique_ptr<cc::SingleReleaseCallback> Buffer::ProduceTextureMailbox(
texture_ =
base::MakeUnique<Texture>(context_factory, context_provider.get());
}
-
- // Copy the contents of |contents_texture| to |texture| and produce a
- // texture mailbox from the result in |texture|.
- Texture* contents_texture = contents_texture_.get();
Texture* texture = texture_.get();
- // The contents texture will be released when copy has completed.
+ // Copy the contents of |contents_texture| to |texture| and produce a
+ // texture mailbox from the result in |texture|. The contents texture will
+ // be released when copy has completed.
gpu::SyncToken sync_token = contents_texture->CopyTexImage(
texture, base::Bind(&Buffer::ReleaseContentsTexture, AsWeakPtr(),
- base::Passed(&contents_texture_)));
- *texture_mailbox =
- cc::TextureMailbox(texture->mailbox(), sync_token, GL_TEXTURE_2D,
- gpu_memory_buffer_->GetSize(),
- false /* is_overlay_candidate */, secure_output_only);
+ base::Passed(&contents_texture_),
+ release_contents_callback_.callback()));
+ resource->mailbox_holder =
+ gpu::MailboxHolder(texture->mailbox(), sync_token, GL_TEXTURE_2D);
+ resource->is_overlay_candidate = false;
+
// The mailbox texture will be released when no longer used by the
// compositor.
- return cc::SingleReleaseCallback::Create(
+ compositor_frame_sink_holder_->SetResourceReleaseCallback(
+ resource_id,
base::Bind(&Buffer::Texture::Release, base::Unretained(texture),
base::Bind(&Buffer::ReleaseTexture, AsWeakPtr(),
base::Passed(&texture_))));
+ return true;
}
void Buffer::OnAttach() {
- DLOG_IF(WARNING, attach_count_ > 0u)
+ DLOG_IF(WARNING, attach_count_)
<< "Reattaching a buffer that is already attached to another surface.";
- attach_count_++;
+ ++attach_count_;
}
void Buffer::OnDetach() {
DCHECK_GT(attach_count_, 0u);
--attach_count_;
- CheckReleaseCallback();
+
+ // Release buffer if no longer attached to a surface and content has been
+ // released.
+ if (!attach_count_ && release_contents_callback_.IsCancelled())
+ Release();
}
gfx::Size Buffer::GetSize() const {
@@ -522,15 +533,6 @@ std::unique_ptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const {
// Buffer, private:
void Buffer::Release() {
- DCHECK_GT(use_count_, 0u);
- --use_count_;
- CheckReleaseCallback();
-}
-
-void Buffer::CheckReleaseCallback() {
- if (attach_count_ || use_count_)
- return;
-
// Run release callback to notify the client that buffer has been released.
if (!release_callback_.is_null())
release_callback_.Run();
@@ -540,11 +542,21 @@ void Buffer::ReleaseTexture(std::unique_ptr<Texture> texture) {
texture_ = std::move(texture);
}
-void Buffer::ReleaseContentsTexture(std::unique_ptr<Texture> texture) {
- TRACE_EVENT0("exo", "Buffer::ReleaseContentsTexture");
-
+void Buffer::ReleaseContentsTexture(std::unique_ptr<Texture> texture,
+ const base::Closure& callback) {
contents_texture_ = std::move(texture);
- Release();
+ callback.Run();
+}
+
+void Buffer::ReleaseContents() {
+ TRACE_EVENT0("exo", "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_)
+ Release();
}
} // namespace exo
diff --git a/chromium/components/exo/buffer.h b/chromium/components/exo/buffer.h
index 42df36ebd80..518e4bc407d 100644
--- a/chromium/components/exo/buffer.h
+++ b/chromium/components/exo/buffer.h
@@ -8,8 +8,10 @@
#include <memory>
#include "base/callback.h"
+#include "base/cancelable_callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "cc/resources/transferable_resource.h"
#include "ui/gfx/geometry/size.h"
namespace base {
@@ -18,21 +20,14 @@ class TracedValue;
}
}
-namespace cc {
-class SingleReleaseCallback;
-class TextureMailbox;
-}
-
namespace gfx {
class GpuMemoryBuffer;
}
-namespace gpu {
-struct SyncToken;
-}
-
namespace exo {
+class CompositorFrameSinkHolder;
+
// 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
// and not defined as part of this class.
@@ -57,10 +52,12 @@ class Buffer : public base::SupportsWeakPtr<Buffer> {
// buffer. Returns a release callback on success. The release callback should
// be called before a new texture mailbox can be acquired unless
// |non_client_usage| is true.
- std::unique_ptr<cc::SingleReleaseCallback> ProduceTextureMailbox(
- cc::TextureMailbox* mailbox,
+ bool ProduceTransferableResource(
+ CompositorFrameSinkHolder* compositor_frame_sink_holder,
+ cc::ResourceId resource_id,
bool secure_output_only,
- bool client_usage);
+ bool client_usage,
+ cc::TransferableResource* resource);
// This should be called when the buffer is attached to a Surface.
void OnAttach();
@@ -77,13 +74,10 @@ class Buffer : public base::SupportsWeakPtr<Buffer> {
private:
class Texture;
- // Decrements the use count of buffer and notifies the client that buffer
- // as been released if it reached 0.
+ // This should be called when buffer is released and will notify the
+ // client that buffer has been released.
void Release();
- // Runs the release callback if the buffer isn't attached or in use.
- void CheckReleaseCallback();
-
// This is used by ProduceTextureMailbox() to produce a release callback
// that releases a texture so it can be destroyed or reused.
void ReleaseTexture(std::unique_ptr<Texture> texture);
@@ -91,7 +85,12 @@ class Buffer : public base::SupportsWeakPtr<Buffer> {
// This is used by ProduceTextureMailbox() to produce a release callback
// that releases the buffer contents referenced by a texture before the
// texture is destroyed or reused.
- void ReleaseContentsTexture(std::unique_ptr<Texture> texture);
+ void ReleaseContentsTexture(std::unique_ptr<Texture> texture,
+ const base::Closure& callback);
+
+ // Notifies the client that buffer has been released if no longer attached
+ // to a surface.
+ void ReleaseContents();
// The GPU memory buffer that contains the contents of this buffer.
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
@@ -108,25 +107,29 @@ class Buffer : public base::SupportsWeakPtr<Buffer> {
// True if this buffer is an overlay candidate.
const bool is_overlay_candidate_;
- // This is incremented when a texture mailbox is produced and decremented
- // when a texture mailbox is released. It is used to determine when we should
- // notify the client that buffer has been released.
- unsigned use_count_ = 0;
-
// This keeps track of how many Surfaces the buffer is attached to.
unsigned attach_count_ = 0;
- // The last used texture. ProduceTextureMailbox() will use this
+ // The last used texture. ProduceTransferableResource() will use this
// instead of creating a new texture when possible.
std::unique_ptr<Texture> texture_;
- // The last used contents texture. ProduceTextureMailbox() will use this
+ // The last used contents texture. ProduceTransferableResource() will use this
// instead of creating a new texture when possible.
std::unique_ptr<Texture> contents_texture_;
// The client release callback.
base::Closure release_callback_;
+ // CompositorFrameSinkHolder instance that needs to be kept alive to receive
+ // a release callback when the last produced transferable resource is no
+ // longer in use.
+ scoped_refptr<CompositorFrameSinkHolder> compositor_frame_sink_holder_;
+
+ // Cancelable release contents callback. This is set when a release callback
+ // is pending.
+ base::CancelableClosure release_contents_callback_;
+
DISALLOW_COPY_AND_ASSIGN(Buffer);
};
diff --git a/chromium/components/exo/buffer_unittest.cc b/chromium/components/exo/buffer_unittest.cc
index 987c2905653..4f34c960011 100644
--- a/chromium/components/exo/buffer_unittest.cc
+++ b/chromium/components/exo/buffer_unittest.cc
@@ -31,6 +31,13 @@ TEST_F(BufferTest, ReleaseCallback) {
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);
+ const cc::FrameSinkId arbitrary_frame_sink_id(1, 1);
+ scoped_refptr<CompositorFrameSinkHolder> compositor_frame_sink_holder =
+ new CompositorFrameSinkHolder(surface.get(), arbitrary_frame_sink_id,
+ aura::Env::GetInstance()
+ ->context_factory_private()
+ ->GetSurfaceManager());
// Set the release callback.
int release_call_count = 0;
@@ -38,15 +45,21 @@ TEST_F(BufferTest, ReleaseCallback) {
base::Bind(&Release, base::Unretained(&release_call_count)));
buffer->OnAttach();
- // Produce a texture mailbox for the contents of the buffer.
- cc::TextureMailbox texture_mailbox;
- std::unique_ptr<cc::SingleReleaseCallback> buffer_release_callback =
- buffer->ProduceTextureMailbox(&texture_mailbox, false, true);
- ASSERT_TRUE(buffer_release_callback);
+ cc::TransferableResource resource;
+ // Produce a transferable resource for the contents of the buffer.
+ bool rv = buffer->ProduceTransferableResource(
+ compositor_frame_sink_holder.get(), 0, false, true, &resource);
+ ASSERT_TRUE(rv);
// Release buffer.
- buffer_release_callback->Run(gpu::SyncToken(), false);
-
+ cc::ReturnedResource returned_resource;
+ 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);
+
+ RunAllPendingInMessageLoop();
ASSERT_EQ(release_call_count, 0);
buffer->OnDetach();
@@ -59,13 +72,21 @@ 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);
+ std::unique_ptr<Surface> surface(new Surface);
+ scoped_refptr<CompositorFrameSinkHolder> compositor_frame_sink_holder =
+ new CompositorFrameSinkHolder(surface.get(), arbitrary_frame_sink_id,
+ aura::Env::GetInstance()
+ ->context_factory_private()
+ ->GetSurfaceManager());
+ cc::ResourceId resource_id = 0;
buffer->OnAttach();
- // Acquire a texture mailbox for the contents of the buffer.
- cc::TextureMailbox texture_mailbox;
- std::unique_ptr<cc::SingleReleaseCallback> buffer_release_callback =
- buffer->ProduceTextureMailbox(&texture_mailbox, false, true);
- ASSERT_TRUE(buffer_release_callback);
+ // Acquire a texture transferable resource for the contents of the buffer.
+ cc::TransferableResource resource;
+ bool rv = buffer->ProduceTransferableResource(
+ compositor_frame_sink_holder.get(), resource_id, false, true, &resource);
+ ASSERT_TRUE(rv);
scoped_refptr<cc::ContextProvider> context_provider =
aura::Env::GetInstance()
@@ -79,15 +100,31 @@ TEST_F(BufferTest, IsLost) {
// Release buffer.
bool is_lost = true;
- buffer_release_callback->Run(gpu::SyncToken(), is_lost);
-
- // Producing a new texture mailbox for the contents of the buffer.
- std::unique_ptr<cc::SingleReleaseCallback> buffer_release_callback2 =
- buffer->ProduceTextureMailbox(&texture_mailbox, false, false);
- ASSERT_TRUE(buffer_release_callback2);
+ cc::ReturnedResource returned_resource;
+ 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);
+ RunAllPendingInMessageLoop();
+
+ // Producing a new texture transferable resource for the contents of the
+ // buffer.
+ ++resource_id;
+ cc::TransferableResource new_resource;
+ rv = buffer->ProduceTransferableResource(compositor_frame_sink_holder.get(),
+ resource_id, false, false,
+ &new_resource);
+ ASSERT_TRUE(rv);
buffer->OnDetach();
- buffer_release_callback2->Run(gpu::SyncToken(), false);
+ cc::ReturnedResource returned_resource2;
+ 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);
+ RunAllPendingInMessageLoop();
}
} // namespace
diff --git a/chromium/components/exo/compositor_frame_sink.cc b/chromium/components/exo/compositor_frame_sink.cc
new file mode 100644
index 00000000000..c49758a4066
--- /dev/null
+++ b/chromium/components/exo/compositor_frame_sink.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/exo/compositor_frame_sink.h"
+
+#include "base/memory/ptr_util.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_manager.h"
+#include "components/exo/compositor_frame_sink_holder.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace exo {
+
+////////////////////////////////////////////////////////////////////////////////
+// ExoComopositorFrameSink, public:
+
+CompositorFrameSink::CompositorFrameSink(const cc::FrameSinkId& frame_sink_id,
+ cc::SurfaceManager* surface_manager,
+ CompositorFrameSinkHolder* client)
+ : support_(this, surface_manager, frame_sink_id, nullptr, nullptr),
+ client_(client) {}
+
+CompositorFrameSink::~CompositorFrameSink() {}
+
+////////////////////////////////////////////////////////////////////////////////
+// cc::mojom::MojoCompositorFrameSink overrides:
+
+void CompositorFrameSink::SetNeedsBeginFrame(bool needs_begin_frame) {
+ support_.SetNeedsBeginFrame(needs_begin_frame);
+}
+
+void CompositorFrameSink::SubmitCompositorFrame(
+ const cc::LocalFrameId& local_frame_id,
+ cc::CompositorFrame frame) {
+ support_.SubmitCompositorFrame(local_frame_id, std::move(frame));
+}
+
+void CompositorFrameSink::EvictFrame() {
+ support_.EvictFrame();
+}
+
+void CompositorFrameSink::Require(const cc::LocalFrameId& local_frame_id,
+ const cc::SurfaceSequence& sequence) {
+ support_.Require(local_frame_id, sequence);
+}
+
+void CompositorFrameSink::Satisfy(const cc::SurfaceSequence& sequence) {
+ support_.Satisfy(sequence);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// cc::CompositorFrameSinkSupportClient overrides:
+
+void CompositorFrameSink::DidReceiveCompositorFrameAck() {
+ client_->DidReceiveCompositorFrameAck();
+}
+
+void CompositorFrameSink::OnBeginFrame(const cc::BeginFrameArgs& args) {
+ client_->OnBeginFrame(args);
+}
+
+void CompositorFrameSink::ReclaimResources(
+ const cc::ReturnedResourceArray& resources) {
+ client_->ReclaimResources(resources);
+}
+
+void CompositorFrameSink::WillDrawSurface() {
+ client_->WillDrawSurface();
+}
+
+} // namespace exo
diff --git a/chromium/components/exo/compositor_frame_sink.h b/chromium/components/exo/compositor_frame_sink.h
new file mode 100644
index 00000000000..549899ffa0e
--- /dev/null
+++ b/chromium/components/exo/compositor_frame_sink.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.
+
+#ifndef COMPONENTS_EXO_EXO_COMPOSITOR_FRAME_SINK_H_
+#define COMPONENTS_EXO_EXO_COMPOSITOR_FRAME_SINK_H_
+
+#include "cc/ipc/compositor_frame.mojom.h"
+#include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
+#include "cc/resources/transferable_resource.h"
+#include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/compositor_frame_sink_support_client.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace exo {
+
+class CompositorFrameSinkHolder;
+
+class CompositorFrameSink : public cc::CompositorFrameSinkSupportClient,
+ public cc::mojom::MojoCompositorFrameSink {
+ public:
+ CompositorFrameSink(const cc::FrameSinkId& frame_sink_id,
+ cc::SurfaceManager* surface_manager,
+ CompositorFrameSinkHolder* client);
+
+ ~CompositorFrameSink() override;
+
+ // Overridden from cc::mojom::MojoCompositorFrameSink:
+ void SetNeedsBeginFrame(bool needs_begin_frame) override;
+ void SubmitCompositorFrame(const cc::LocalFrameId& local_frame_id,
+ cc::CompositorFrame frame) override;
+ void EvictFrame() override;
+ void Require(const cc::LocalFrameId& local_frame_id,
+ const cc::SurfaceSequence& sequence) override;
+ void Satisfy(const cc::SurfaceSequence& sequence) override;
+
+ // Overridden from cc::CompositorFrameSinkSupportClient:
+ void DidReceiveCompositorFrameAck() override;
+ void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+ void ReclaimResources(const cc::ReturnedResourceArray& resources) override;
+ void WillDrawSurface() override;
+
+ private:
+ cc::CompositorFrameSinkSupport support_;
+ CompositorFrameSinkHolder* const client_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositorFrameSink);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_EXO_COMPOSITOR_FRAME_SINK_H_
diff --git a/chromium/components/exo/compositor_frame_sink_holder.cc b/chromium/components/exo/compositor_frame_sink_holder.cc
new file mode 100644
index 00000000000..34bcf180bcb
--- /dev/null
+++ b/chromium/components/exo/compositor_frame_sink_holder.cc
@@ -0,0 +1,131 @@
+// 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/resources/returned_resource.h"
+#include "components/exo/surface.h"
+
+namespace exo {
+
+////////////////////////////////////////////////////////////////////////////////
+// CompositorFrameSinkHolder, public:
+
+CompositorFrameSinkHolder::CompositorFrameSinkHolder(
+ Surface* surface,
+ const cc::FrameSinkId& frame_sink_id,
+ cc::SurfaceManager* surface_manager)
+ : surface_(surface),
+ frame_sink_(
+ new CompositorFrameSink(frame_sink_id, surface_manager, this)),
+ begin_frame_source_(base::MakeUnique<cc::ExternalBeginFrameSource>(this)),
+ weak_factory_(this) {
+ surface_->AddSurfaceObserver(this);
+}
+
+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;
+}
+
+void CompositorFrameSinkHolder::SetNeedsBeginFrame(bool needs_begin_frame) {
+ needs_begin_frame_ = needs_begin_frame;
+ OnNeedsBeginFrames(needs_begin_frame);
+}
+
+void CompositorFrameSinkHolder::Satisfy(const cc::SurfaceSequence& sequence) {
+ frame_sink_->Satisfy(sequence);
+}
+
+void CompositorFrameSinkHolder::Require(const cc::SurfaceId& id,
+ const cc::SurfaceSequence& sequence) {
+ frame_sink_->Require(id.local_frame_id(), sequence);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// cc::mojom::MojoCompositorFrameSinkClient overrides:
+
+void CompositorFrameSinkHolder::DidReceiveCompositorFrameAck() {
+ // TODO(staraz): Implement this
+}
+
+void CompositorFrameSinkHolder::OnBeginFrame(const cc::BeginFrameArgs& args) {
+ if (surface_)
+ surface_->BeginFrame(args.frame_time);
+
+ begin_frame_source_->OnBeginFrame(args);
+}
+
+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::WillDrawSurface() {
+ if (surface_)
+ surface_->WillDraw();
+
+ UpdateNeedsBeginFrame();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// cc::BeginFrameObserver overrides:
+
+const cc::BeginFrameArgs& CompositorFrameSinkHolder::LastUsedBeginFrameArgs()
+ const {
+ return last_begin_frame_args_;
+}
+
+void CompositorFrameSinkHolder::OnBeginFrameSourcePausedChanged(bool paused) {}
+
+////////////////////////////////////////////////////////////////////////////////
+// cc::ExternalBeginFrameSouceClient overrides:
+
+void CompositorFrameSinkHolder::OnNeedsBeginFrames(bool needs_begin_frames) {
+ frame_sink_->SetNeedsBeginFrame(needs_begin_frames);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SurfaceObserver overrides:
+
+void CompositorFrameSinkHolder::OnSurfaceDestroying(Surface* surface) {
+ surface_->RemoveSurfaceObserver(this);
+ surface_ = nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ExoComopositorFrameSink, private:
+
+CompositorFrameSinkHolder::~CompositorFrameSinkHolder() {
+ if (surface_)
+ surface_->RemoveSurfaceObserver(this);
+}
+
+void CompositorFrameSinkHolder::UpdateNeedsBeginFrame() {
+ if (!begin_frame_source_)
+ return;
+
+ bool needs_begin_frame = surface_ && surface_->NeedsBeginFrame();
+ if (needs_begin_frame == needs_begin_frame_)
+ return;
+
+ needs_begin_frame_ = needs_begin_frame;
+ OnNeedsBeginFrames(needs_begin_frame_);
+}
+
+} // namespace exo
diff --git a/chromium/components/exo/compositor_frame_sink_holder.h b/chromium/components/exo/compositor_frame_sink_holder.h
new file mode 100644
index 00000000000..7efa6ded119
--- /dev/null
+++ b/chromium/components/exo/compositor_frame_sink_holder.h
@@ -0,0 +1,94 @@
+// 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 <list>
+#include <map>
+#include <memory>
+
+#include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
+#include "cc/resources/release_callback.h"
+#include "cc/resources/transferable_resource.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "components/exo/compositor_frame_sink.h"
+#include "components/exo/surface_observer.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+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 base::RefCounted<CompositorFrameSinkHolder>,
+ public cc::ExternalBeginFrameSourceClient,
+ public cc::mojom::MojoCompositorFrameSinkClient,
+ public cc::BeginFrameObserver,
+ public SurfaceObserver {
+ public:
+ CompositorFrameSinkHolder(Surface* surface,
+ const cc::FrameSinkId& frame_sink_id,
+ cc::SurfaceManager* surface_manager);
+
+ bool HasReleaseCallbackForResource(cc::ResourceId id);
+ void SetResourceReleaseCallback(cc::ResourceId id,
+ const cc::ReleaseCallback& callback);
+
+ CompositorFrameSink* GetCompositorFrameSink() { return frame_sink_.get(); }
+
+ base::WeakPtr<CompositorFrameSinkHolder> GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ void SetNeedsBeginFrame(bool needs_begin_frame);
+
+ void Satisfy(const cc::SurfaceSequence& sequence);
+ void Require(const cc::SurfaceId& id, const cc::SurfaceSequence& sequence);
+
+ // Overridden from cc::mojom::MojoCompositorFrameSinkClient:
+ void DidReceiveCompositorFrameAck() override;
+ void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+ void ReclaimResources(const cc::ReturnedResourceArray& resources) override;
+ void WillDrawSurface() override;
+
+ // Overridden from cc::BeginFrameObserver:
+ const cc::BeginFrameArgs& LastUsedBeginFrameArgs() const override;
+ void OnBeginFrameSourcePausedChanged(bool paused) override;
+
+ // Overridden from cc::ExternalBeginFrameSouceClient:
+ void OnNeedsBeginFrames(bool needs_begin_frames) override;
+
+ // Overridden from SurfaceObserver:
+ void OnSurfaceDestroying(Surface* surface) override;
+
+ private:
+ friend class base::RefCounted<CompositorFrameSinkHolder>;
+
+ ~CompositorFrameSinkHolder() override;
+
+ void UpdateNeedsBeginFrame();
+
+ // A collection of callbacks used to release resources.
+ using ResourceReleaseCallbackMap = std::map<int, cc::ReleaseCallback>;
+ ResourceReleaseCallbackMap release_callbacks_;
+
+ Surface* surface_;
+ std::unique_ptr<CompositorFrameSink> frame_sink_;
+
+ std::unique_ptr<cc::ExternalBeginFrameSource> begin_frame_source_;
+ bool needs_begin_frame_ = false;
+ cc::BeginFrameArgs last_begin_frame_args_;
+
+ 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/display.cc b/chromium/components/exo/display.cc
index af2a2ad9f67..505a7a7cd34 100644
--- a/chromium/components/exo/display.cc
+++ b/chromium/components/exo/display.cc
@@ -24,10 +24,9 @@
#if defined(USE_OZONE)
#include <GLES2/gl2extchromium.h>
#include "components/exo/buffer.h"
-#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
-#include "ui/aura/env.h"
#endif
namespace exo {
@@ -77,10 +76,9 @@ std::unique_ptr<Buffer> Display::CreateLinuxDMABufBuffer(
handle.native_pixmap_handle.planes.push_back(plane);
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
- aura::Env::GetInstance()
- ->context_factory()
- ->GetGpuMemoryBufferManager()
- ->CreateGpuMemoryBufferFromHandle(handle, size, format);
+ gpu::GpuMemoryBufferImplOzoneNativePixmap::CreateFromHandle(
+ handle, size, format, gfx::BufferUsage::GPU_READ,
+ gpu::GpuMemoryBufferImpl::DestructionCallback());
if (!gpu_memory_buffer) {
LOG(ERROR) << "Failed to create GpuMemoryBuffer from handle";
return nullptr;
@@ -113,9 +111,9 @@ std::unique_ptr<ShellSurface> Display::CreateShellSurface(Surface* surface) {
return nullptr;
}
- return base::MakeUnique<ShellSurface>(surface, nullptr, gfx::Rect(),
- true /* activatable */,
- ash::kShellWindowId_DefaultContainer);
+ return base::MakeUnique<ShellSurface>(
+ surface, nullptr, gfx::Rect(), true /* activatable */,
+ false /* can_minimize */, ash::kShellWindowId_DefaultContainer);
}
std::unique_ptr<ShellSurface> Display::CreatePopupShellSurface(
@@ -144,9 +142,9 @@ std::unique_ptr<ShellSurface> Display::CreatePopupShellSurface(
&origin);
gfx::Rect initial_bounds(origin, gfx::Size(1, 1));
- return base::MakeUnique<ShellSurface>(surface, parent, initial_bounds,
- false /* activatable */,
- ash::kShellWindowId_DefaultContainer);
+ return base::MakeUnique<ShellSurface>(
+ surface, parent, initial_bounds, false /* activatable */,
+ false /* can_minimize */, ash::kShellWindowId_DefaultContainer);
}
std::unique_ptr<ShellSurface> Display::CreateRemoteShellSurface(
@@ -160,8 +158,12 @@ std::unique_ptr<ShellSurface> Display::CreateRemoteShellSurface(
return nullptr;
}
+ // Remote shell surfaces in system modal container cannot be minimized.
+ bool can_minimize = container != ash::kShellWindowId_SystemModalContainer;
+
return base::MakeUnique<ShellSurface>(surface, nullptr, gfx::Rect(1, 1),
- true /* activatable */, container);
+ true /* activatable */, can_minimize,
+ container);
}
std::unique_ptr<SubSurface> Display::CreateSubSurface(Surface* surface,
diff --git a/chromium/components/exo/gamepad.cc b/chromium/components/exo/gamepad.cc
index 876d05d809a..8d5e23110df 100644
--- a/chromium/components/exo/gamepad.cc
+++ b/chromium/components/exo/gamepad.cc
@@ -123,7 +123,6 @@ class Gamepad::ThreadSafeGamepadChangeFetcher
fetcher_->GetGamepadData(
false /* No hardware changed notification from the system */);
- new_state.length = 0;
device::PadState& pad_state = pad_states_.get()[0];
// After querying the gamepad clear the state if it did not have it's active
@@ -137,22 +136,15 @@ class Gamepad::ThreadSafeGamepadChangeFetcher
MapAndSanitizeGamepadData(&pad_state, &new_state.items[0],
false /* Don't sanitize gamepad data */);
- // If the gamepad was active then increment the length of the WebGamepads
- // struct to indicate it's valid, then set the pad state to inactive. If the
- // gamepad is still actively reporting the next call to GetGamepadData will
- // set the active state to active again.
- if (pad_state.active_state) {
- new_state.length++;
+ // If the gamepad is still actively reporting the next call to
+ // GetGamepadData will set the active state to active again.
+ if (pad_state.active_state)
pad_state.active_state = device::GAMEPAD_INACTIVE;
- }
- if (std::max(new_state.length, state_.length) > 0) {
- if (new_state.items[0].connected != state_.items[0].connected ||
- new_state.items[0].timestamp > state_.items[0].timestamp) {
- origin_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(process_gamepad_changes_, new_state.items[0]));
- }
+ if (new_state.items[0].connected != state_.items[0].connected ||
+ new_state.items[0].timestamp > state_.items[0].timestamp) {
+ origin_task_runner_->PostTask(
+ FROM_HERE, base::Bind(process_gamepad_changes_, new_state.items[0]));
}
state_ = new_state;
diff --git a/chromium/components/exo/gamepad_unittest.cc b/chromium/components/exo/gamepad_unittest.cc
index 34a7e165bd8..560c7e43c51 100644
--- a/chromium/components/exo/gamepad_unittest.cc
+++ b/chromium/components/exo/gamepad_unittest.cc
@@ -105,7 +105,6 @@ TEST_F(GamepadTest, OnStateChange) {
// Gamepad connected.
EXPECT_CALL(delegate, OnStateChange(true)).Times(1);
blink::WebGamepads gamepad_connected;
- gamepad_connected.length = 1;
gamepad_connected.items[0].connected = true;
gamepad_connected.items[0].timestamp = 1;
SetDataAndPostToDelegate(gamepad_connected);
@@ -134,7 +133,6 @@ TEST_F(GamepadTest, OnAxis) {
InitializeGamepad(&delegate);
blink::WebGamepads axis_moved;
- axis_moved.length = 1;
axis_moved.items[0].connected = true;
axis_moved.items[0].timestamp = 1;
axis_moved.items[0].axesLength = 1;
@@ -164,7 +162,6 @@ TEST_F(GamepadTest, OnButton) {
InitializeGamepad(&delegate);
blink::WebGamepads axis_moved;
- axis_moved.length = 1;
axis_moved.items[0].connected = true;
axis_moved.items[0].timestamp = 1;
axis_moved.items[0].buttonsLength = 1;
diff --git a/chromium/components/exo/keyboard.cc b/chromium/components/exo/keyboard.cc
index 2ac24547ef0..c651a5672ba 100644
--- a/chromium/components/exo/keyboard.cc
+++ b/chromium/components/exo/keyboard.cc
@@ -5,16 +5,20 @@
#include "components/exo/keyboard.h"
#include "components/exo/keyboard_delegate.h"
+#include "components/exo/keyboard_device_configuration_delegate.h"
#include "components/exo/shell_surface.h"
#include "components/exo/surface.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/window.h"
#include "ui/base/ime/input_method.h"
#include "ui/events/base_event_utils.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/input_device_manager.h"
#include "ui/events/event.h"
#include "ui/views/widget/widget.h"
namespace exo {
+namespace {
bool ConsumedByIme(Surface* focus, const ui::KeyEvent* event) {
// Check if IME consumed the event, to avoid it to be doubly processed.
@@ -70,6 +74,21 @@ bool ConsumedByIme(Surface* focus, const ui::KeyEvent* event) {
return false;
}
+bool IsPhysicalKeyboardEnabled() {
+ // The internal keyboard is enabled if maximize mode is not enabled.
+ if (!WMHelper::GetInstance()->IsMaximizeModeWindowManagerEnabled())
+ return true;
+
+ for (auto& keyboard :
+ ui::InputDeviceManager::GetInstance()->GetKeyboardDevices()) {
+ if (keyboard.type != ui::InputDeviceType::INPUT_DEVICE_INTERNAL)
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
////////////////////////////////////////////////////////////////////////////////
// Keyboard, public:
@@ -77,16 +96,32 @@ Keyboard::Keyboard(KeyboardDelegate* delegate) : delegate_(delegate) {
auto* helper = WMHelper::GetInstance();
helper->AddPostTargetHandler(this);
helper->AddFocusObserver(this);
+ helper->AddMaximizeModeObserver(this);
+ helper->AddInputDeviceEventObserver(this);
OnWindowFocused(helper->GetFocusedWindow(), nullptr);
}
Keyboard::~Keyboard() {
delegate_->OnKeyboardDestroying(this);
+ if (device_configuration_delegate_)
+ device_configuration_delegate_->OnKeyboardDestroying(this);
if (focus_)
focus_->RemoveSurfaceObserver(this);
auto* helper = WMHelper::GetInstance();
helper->RemoveFocusObserver(this);
helper->RemovePostTargetHandler(this);
+ helper->RemoveMaximizeModeObserver(this);
+ helper->RemoveInputDeviceEventObserver(this);
+}
+
+bool Keyboard::HasDeviceConfigurationDelegate() const {
+ return !!device_configuration_delegate_;
+}
+
+void Keyboard::SetDeviceConfigurationDelegate(
+ KeyboardDeviceConfigurationDelegate* delegate) {
+ device_configuration_delegate_ = delegate;
+ OnKeyboardDeviceConfigurationChanged();
}
////////////////////////////////////////////////////////////////////////////////
@@ -170,6 +205,27 @@ void Keyboard::OnSurfaceDestroying(Surface* surface) {
}
////////////////////////////////////////////////////////////////////////////////
+// ui::InputDeviceEventObserver overrides:
+
+void Keyboard::OnKeyboardDeviceConfigurationChanged() {
+ if (device_configuration_delegate_) {
+ device_configuration_delegate_->OnKeyboardTypeChanged(
+ IsPhysicalKeyboardEnabled());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WMHelper::MaximizeModeObserver overrides:
+
+void Keyboard::OnMaximizeModeStarted() {
+ OnKeyboardDeviceConfigurationChanged();
+}
+
+void Keyboard::OnMaximizeModeEnded() {
+ OnKeyboardDeviceConfigurationChanged();
+}
+
+////////////////////////////////////////////////////////////////////////////////
// Keyboard, private:
Surface* Keyboard::GetEffectiveFocus(aura::Window* window) const {
diff --git a/chromium/components/exo/keyboard.h b/chromium/components/exo/keyboard.h
index dd54a82cec4..b56834fca6c 100644
--- a/chromium/components/exo/keyboard.h
+++ b/chromium/components/exo/keyboard.h
@@ -10,28 +10,33 @@
#include "base/macros.h"
#include "components/exo/surface_observer.h"
#include "components/exo/wm_helper.h"
-#include "ui/aura/client/focus_change_observer.h"
#include "ui/events/event_handler.h"
namespace ui {
enum class DomCode;
-class Event;
class KeyEvent;
}
namespace exo {
class KeyboardDelegate;
+class KeyboardDeviceConfigurationDelegate;
class Surface;
// This class implements a client keyboard that represents one or more keyboard
// devices.
class Keyboard : public ui::EventHandler,
public WMHelper::FocusObserver,
+ public WMHelper::InputDeviceEventObserver,
+ public WMHelper::MaximizeModeObserver,
public SurfaceObserver {
public:
explicit Keyboard(KeyboardDelegate* delegate);
~Keyboard() override;
+ bool HasDeviceConfigurationDelegate() const;
+ void SetDeviceConfigurationDelegate(
+ KeyboardDeviceConfigurationDelegate* delegate);
+
// Overridden from ui::EventHandler:
void OnKeyEvent(ui::KeyEvent* event) override;
@@ -42,13 +47,25 @@ class Keyboard : public ui::EventHandler,
// Overridden from SurfaceObserver:
void OnSurfaceDestroying(Surface* surface) override;
+ // Overridden from ui::InputDeviceEventObserver:
+ void OnKeyboardDeviceConfigurationChanged() override;
+
+ // Overridden from WMHelper::MaximizeModeObserver:
+ void OnMaximizeModeStarted() override;
+ void OnMaximizeModeEnded() override;
+
private:
// Returns the effective focus for |window|.
Surface* GetEffectiveFocus(aura::Window* window) const;
- // The delegate instance that all events are dispatched to.
+ // The delegate instance that all events except for events about device
+ // configuration are dispatched to.
KeyboardDelegate* const delegate_;
+ // The delegate instance that events about device configuration are dispatched
+ // to.
+ KeyboardDeviceConfigurationDelegate* device_configuration_delegate_ = nullptr;
+
// The current focus surface for the keyboard.
Surface* focus_ = nullptr;
diff --git a/chromium/components/exo/keyboard_delegate.h b/chromium/components/exo/keyboard_delegate.h
index 1969067e6d0..619f56a83fc 100644
--- a/chromium/components/exo/keyboard_delegate.h
+++ b/chromium/components/exo/keyboard_delegate.h
@@ -36,7 +36,7 @@ class KeyboardDelegate {
// Called when keyboard focus leaves a valid target surface.
virtual void OnKeyboardLeave(Surface* surface) = 0;
- // Called when pkeyboard key state changed. |pressed| is true when |key|
+ // 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,
diff --git a/chromium/components/exo/keyboard_device_configuration_delegate.h b/chromium/components/exo/keyboard_device_configuration_delegate.h
new file mode 100644
index 00000000000..263be3d6549
--- /dev/null
+++ b/chromium/components/exo/keyboard_device_configuration_delegate.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_EXO_KEYBOARD_DEVICE_CONFIGURATION_H_
+#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;
+
+ protected:
+ virtual ~KeyboardDeviceConfigurationDelegate() {}
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_KEYBOARD_DEVICE_CONFIGURATION_H_
diff --git a/chromium/components/exo/pointer.cc b/chromium/components/exo/pointer.cc
index b10d0186d79..bd267e04bc6 100644
--- a/chromium/components/exo/pointer.cc
+++ b/chromium/components/exo/pointer.cc
@@ -307,14 +307,32 @@ void Pointer::UpdateCursorScale() {
if (!focus_)
return;
+ display::Screen* screen = display::Screen::GetScreen();
+ WMHelper* helper = WMHelper::GetInstance();
+
// Update cursor scale if the effective UI scale has changed.
display::Display display =
- display::Screen::GetScreen()->GetDisplayNearestWindow(
- widget_->GetNativeWindow());
- float ui_scale = WMHelper::GetInstance()
- ->GetDisplayInfo(display.id())
- .GetEffectiveUIScale();
- if (WMHelper::GetInstance()->GetCursorSet() == ui::CURSOR_SET_LARGE)
+ screen->GetDisplayNearestWindow(widget_->GetNativeWindow());
+ float ui_scale = helper->GetDisplayInfo(display.id()).GetEffectiveUIScale();
+
+ if (display::Display::HasInternalDisplay()) {
+ float primary_device_scale_factor =
+ screen->GetPrimaryDisplay().device_scale_factor();
+
+ // The size of the cursor surface is the quotient of its physical size and
+ // the DSF of the primary display. The physical size is proportional to the
+ // DSF of the internal display. For external displays (and the internal
+ // display when secondary to a display with a different DSF), scale the
+ // cursor so its physical size matches with the single display case.
+ if (!display.IsInternal() ||
+ display.device_scale_factor() != primary_device_scale_factor) {
+ ui_scale *= primary_device_scale_factor /
+ helper->GetDisplayInfo(display::Display::InternalDisplayId())
+ .device_scale_factor();
+ }
+ }
+
+ if (helper->GetCursorSet() == ui::CURSOR_SET_LARGE)
ui_scale *= kLargeCursorScale;
if (ui_scale != cursor_scale_) {
diff --git a/chromium/components/exo/pointer.h b/chromium/components/exo/pointer.h
index 2c1a89b1e1d..df158e1df3e 100644
--- a/chromium/components/exo/pointer.h
+++ b/chromium/components/exo/pointer.h
@@ -18,7 +18,6 @@
namespace ui {
class Event;
class MouseEvent;
-class MouseWheelEvent;
}
namespace views {
diff --git a/chromium/components/exo/pointer_delegate.h b/chromium/components/exo/pointer_delegate.h
index 182513d2b85..7e4e4fa2350 100644
--- a/chromium/components/exo/pointer_delegate.h
+++ b/chromium/components/exo/pointer_delegate.h
@@ -9,7 +9,6 @@
#include "ui/events/event_constants.h"
namespace gfx {
-class Point;
class PointF;
class Vector2dF;
}
diff --git a/chromium/components/exo/pointer_unittest.cc b/chromium/components/exo/pointer_unittest.cc
index 018d47b9efe..eb5c071f588 100644
--- a/chromium/components/exo/pointer_unittest.cc
+++ b/chromium/components/exo/pointer_unittest.cc
@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ash/aura/wm_window_aura.h"
#include "ash/common/wm/window_positioning_utils.h"
#include "ash/common/wm_shell.h"
+#include "ash/common/wm_window.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "components/exo/buffer.h"
@@ -176,7 +176,7 @@ TEST_F(PointerTest, OnPointerMotion) {
std::unique_ptr<Surface> child_surface(new Surface);
std::unique_ptr<ShellSurface> child_shell_surface(new ShellSurface(
child_surface.get(), shell_surface.get(), gfx::Rect(9, 9, 1, 1), true,
- ash::kShellWindowId_DefaultContainer));
+ false, ash::kShellWindowId_DefaultContainer));
gfx::Size child_buffer_size(15, 15);
std::unique_ptr<Buffer> child_buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(child_buffer_size)));
@@ -309,12 +309,12 @@ TEST_F(PointerTest, IgnorePointerEventDuringModal) {
std::unique_ptr<Surface> surface2(new Surface);
std::unique_ptr<ShellSurface> shell_surface2(
new ShellSurface(surface2.get(), nullptr, gfx::Rect(0, 0, 5, 5), true,
- ash::kShellWindowId_SystemModalContainer));
+ false, ash::kShellWindowId_SystemModalContainer));
std::unique_ptr<Buffer> buffer2(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(5, 5))));
surface2->Attach(buffer2.get());
surface2->Commit();
- ash::wm::CenterWindow(ash::WmWindowAura::Get(surface2->window()));
+ ash::wm::CenterWindow(ash::WmWindow::Get(surface2->window()));
gfx::Point location2 = surface2->window()->GetBoundsInScreen().origin();
// Make the window modal.
diff --git a/chromium/components/exo/shared_memory.cc b/chromium/components/exo/shared_memory.cc
index d0b2176c36b..4b6f6806c75 100644
--- a/chromium/components/exo/shared_memory.cc
+++ b/chromium/components/exo/shared_memory.cc
@@ -13,9 +13,8 @@
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "components/exo/buffer.h"
-#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h"
#include "third_party/khronos/GLES2/gl2.h"
-#include "ui/aura/env.h"
#include "ui/compositor/compositor.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/geometry/size.h"
@@ -70,10 +69,9 @@ std::unique_ptr<Buffer> SharedMemory::CreateBuffer(const gfx::Size& size,
handle.stride = stride;
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
- aura::Env::GetInstance()
- ->context_factory()
- ->GetGpuMemoryBufferManager()
- ->CreateGpuMemoryBufferFromHandle(handle, size, format);
+ gpu::GpuMemoryBufferImplSharedMemory::CreateFromHandle(
+ handle, size, format, gfx::BufferUsage::GPU_READ,
+ gpu::GpuMemoryBufferImpl::DestructionCallback());
if (!gpu_memory_buffer) {
LOG(ERROR) << "Failed to create GpuMemoryBuffer from handle";
return nullptr;
diff --git a/chromium/components/exo/shell_surface.cc b/chromium/components/exo/shell_surface.cc
index ab5fdb6bd26..0cd949c193c 100644
--- a/chromium/components/exo/shell_surface.cc
+++ b/chromium/components/exo/shell_surface.cc
@@ -6,12 +6,13 @@
#include <algorithm>
-#include "ash/aura/wm_window_aura.h"
+#include "ash/common/frame/custom_frame_view_ash.h"
#include "ash/common/shelf/wm_shelf.h"
#include "ash/common/wm/window_resizer.h"
#include "ash/common/wm/window_state.h"
#include "ash/common/wm/window_state_delegate.h"
#include "ash/common/wm_shell.h"
+#include "ash/common/wm_window.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/wm/window_state_aura.h"
#include "ash/wm/window_util.h"
@@ -37,6 +38,7 @@
#include "ui/wm/core/shadow.h"
#include "ui/wm/core/shadow_controller.h"
#include "ui/wm/core/shadow_types.h"
+#include "ui/wm/core/window_animations.h"
#include "ui/wm/core/window_util.h"
#if defined(OS_CHROMEOS)
@@ -100,6 +102,15 @@ class CustomWindowTargeter : public aura::WindowTargeter {
gfx::Point local_point = event.location();
+ if (window->parent()) {
+ aura::Window::ConvertPointToTarget(window->parent(), window,
+ &local_point);
+ }
+
+ int component = widget_->non_client_view()->NonClientHitTest(local_point);
+ if (component != HTNOWHERE && component != HTCLIENT)
+ return true;
+
// If there is an underlay, test against it's bounds instead since it will
// be equal or larger than the surface's bounds.
aura::Window* shadow_underlay =
@@ -107,16 +118,10 @@ class CustomWindowTargeter : public aura::WindowTargeter {
widget_->widget_delegate()->GetContentsView())
->shadow_underlay();
if (shadow_underlay) {
- if (window->parent())
- aura::Window::ConvertPointToTarget(window->parent(), shadow_underlay,
- &local_point);
+ aura::Window::ConvertPointToTarget(window, shadow_underlay, &local_point);
return gfx::Rect(shadow_underlay->layer()->size()).Contains(local_point);
}
- 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)));
}
@@ -133,9 +138,8 @@ class CustomWindowTargeter : public aura::WindowTargeter {
->shadow_underlay();
if (surface && event->IsLocatedEvent() && shadow_underlay) {
gfx::Point local_point = event->AsLocatedEvent()->location();
- aura::Window::ConvertPointToTarget(window, surface->window(),
- &local_point);
- if (!surface->HitTestRect(gfx::Rect(local_point, gfx::Size(1, 1))))
+ int component = widget_->non_client_view()->NonClientHitTest(local_point);
+ if (component == HTNOWHERE)
return shadow_underlay;
}
return aura::WindowTargeter::FindTargetForEvent(root, event);
@@ -333,12 +337,14 @@ ShellSurface::ShellSurface(Surface* surface,
ShellSurface* parent,
const gfx::Rect& initial_bounds,
bool activatable,
+ bool can_minimize,
int container)
: widget_(nullptr),
surface_(surface),
parent_(parent ? parent->GetWidget()->GetNativeWindow() : nullptr),
initial_bounds_(initial_bounds),
activatable_(activatable),
+ can_minimize_(can_minimize),
container_(container) {
WMHelper::GetInstance()->AddActivationObserver(this);
surface_->SetSurfaceDelegate(this);
@@ -354,6 +360,7 @@ ShellSurface::ShellSurface(Surface* surface)
nullptr,
gfx::Rect(),
true,
+ true,
ash::kShellWindowId_DefaultContainer) {}
ShellSurface::~ShellSurface() {
@@ -363,6 +370,9 @@ ShellSurface::~ShellSurface() {
if (widget_) {
ash::wm::GetWindowState(widget_->GetNativeWindow())->RemoveObserver(this);
widget_->GetNativeWindow()->RemoveObserver(this);
+ // Remove transient children so they are not automatically destroyed.
+ for (auto child : wm::GetTransientChildren(widget_->GetNativeWindow()))
+ wm::RemoveTransientChild(widget_->GetNativeWindow(), child);
if (widget_->IsVisible())
widget_->Hide();
widget_->CloseNow();
@@ -420,6 +430,10 @@ void ShellSurface::SetParent(ShellSurface* parent) {
if (widget_)
wm::AddTransientChild(parent_, widget_->GetNativeWindow());
}
+
+ // If |parent_| is set effects the ability to maximize the window.
+ if (widget_)
+ widget_->OnSizeConstraintsChanged();
}
void ShellSurface::Activate() {
@@ -578,18 +592,41 @@ void ShellSurface::SetGeometry(const gfx::Rect& geometry) {
pending_geometry_ = geometry;
}
-void ShellSurface::SetRectangularShadow(const gfx::Rect& content_bounds) {
- TRACE_EVENT1("exo", "ShellSurface::SetRectangularShadow", "content_bounds",
- content_bounds.ToString());
+void ShellSurface::SetRectangularShadowEnabled(bool enabled) {
+ TRACE_EVENT1("exo", "ShellSurface::SetRectangularShadowEnabled", "enabled",
+ enabled);
+ shadow_underlay_in_surface_ = false;
+ shadow_enabled_ = enabled;
+}
+void ShellSurface::SetRectangularShadow_DEPRECATED(
+ const gfx::Rect& content_bounds) {
+ TRACE_EVENT1("exo", "ShellSurface::SetRectangularShadow_DEPRECATED",
+ "content_bounds", content_bounds.ToString());
+ shadow_underlay_in_surface_ = false;
shadow_content_bounds_ = content_bounds;
+ shadow_enabled_ = !content_bounds.IsEmpty();
+}
+
+void ShellSurface::SetRectangularSurfaceShadow(
+ const gfx::Rect& content_bounds) {
+ TRACE_EVENT1("exo", "ShellSurface::SetRectangularSurfaceShadow",
+ "content_bounds", content_bounds.ToString());
+ shadow_underlay_in_surface_ = true;
+ shadow_content_bounds_ = content_bounds;
+ shadow_enabled_ = !content_bounds.IsEmpty();
}
void ShellSurface::SetRectangularShadowBackgroundOpacity(float opacity) {
TRACE_EVENT1("exo", "ShellSurface::SetRectangularShadowBackgroundOpacity",
"opacity", opacity);
+ shadow_background_opacity_ = opacity;
+}
- rectangular_shadow_background_opacity_ = opacity;
+void ShellSurface::SetFrame(bool enabled) {
+ TRACE_EVENT1("exo", "ShellSurface::SetFrame", "enabled", enabled);
+
+ frame_enabled_ = enabled;
}
void ShellSurface::SetScale(double scale) {
@@ -609,6 +646,25 @@ void ShellSurface::SetTopInset(int height) {
pending_top_inset_height_ = height;
}
+void ShellSurface::SetOrigin(const gfx::Point& origin) {
+ TRACE_EVENT1("exo", "ShellSurface::SetOrigin", "origin", origin.ToString());
+
+ initial_bounds_ = gfx::Rect(origin, gfx::Size(1, 1));
+}
+
+void ShellSurface::SetActivatable(bool activatable) {
+ TRACE_EVENT1("exo", "ShellSurface::SetActivatable", "activatable",
+ activatable);
+
+ activatable_ = activatable;
+}
+
+void ShellSurface::SetContainer(int container) {
+ TRACE_EVENT1("exo", "ShellSurface::SetContainer", "container", container);
+
+ container_ = container;
+}
+
// static
void ShellSurface::SetMainSurface(aura::Window* window, Surface* surface) {
window->SetProperty(kMainSurfaceKey, surface);
@@ -663,15 +719,12 @@ void ShellSurface::OnSurfaceCommit() {
top_inset_height_ = pending_top_inset_height_;
}
- gfx::Point surface_origin = GetSurfaceOrigin();
-
// System modal container is used by clients to implement overlay
// windows using a single ShellSurface instance. If hit-test
// 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() + surface_origin.OffsetFromOrigin();
+ gfx::Rect hit_test_bounds = surface_->GetHitTestBounds();
// Prevent window from being activated when hit test bounds are empty.
bool activatable = activatable_ && !hit_test_bounds.IsEmpty();
@@ -685,9 +738,13 @@ void ShellSurface::OnSurfaceCommit() {
}
}
+ gfx::Rect client_view_bounds =
+ widget_->non_client_view()->frame_view()->GetBoundsForClientView();
+
// Update surface bounds.
surface_->window()->SetBounds(
- gfx::Rect(surface_origin, surface_->window()->layer()->size()));
+ gfx::Rect(GetSurfaceOrigin() + client_view_bounds.OffsetFromOrigin(),
+ surface_->window()->layer()->size()));
// Update surface scale.
if (pending_scale_ != scale_) {
@@ -726,8 +783,12 @@ void ShellSurface::OnSurfaceDestroying(Surface* surface) {
// Hide widget before surface is destroyed. This allows hide animations to
// run using the current surface contents.
- if (widget_)
+ if (widget_) {
+ // Remove transient children so they are not automatically hidden.
+ for (auto child : wm::GetTransientChildren(widget_->GetNativeWindow()))
+ wm::RemoveTransientChild(widget_->GetNativeWindow(), child);
widget_->Hide();
+ }
// Note: In its use in the Wayland server implementation, the surface
// destroyed callback may destroy the ShellSurface instance. This call needs
@@ -745,12 +806,15 @@ bool ShellSurface::CanResize() const {
bool ShellSurface::CanMaximize() const {
// Shell surfaces in system modal container cannot be maximized.
- return container_ != ash::kShellWindowId_SystemModalContainer;
+ if (container_ == ash::kShellWindowId_SystemModalContainer)
+ return false;
+
+ // Non-transient shell surfaces can be maximized.
+ return !parent_;
}
bool ShellSurface::CanMinimize() const {
- // Shell surfaces in system modal container cannot be minimized.
- return container_ != ash::kShellWindowId_SystemModalContainer;
+ return can_minimize_;
}
base::string16 ShellSurface::GetWindowTitle() const {
@@ -780,6 +844,15 @@ views::View* ShellSurface::GetContentsView() {
views::NonClientFrameView* ShellSurface::CreateNonClientFrameView(
views::Widget* widget) {
+ aura::Window* window = widget_->GetNativeWindow();
+ ash::wm::WindowState* window_state = ash::wm::GetWindowState(window);
+ // Set delegate for handling of fullscreening.
+ window_state->SetDelegate(std::unique_ptr<ash::wm::WindowStateDelegate>(
+ new CustomWindowStateDelegate(widget_)));
+
+ if (frame_enabled_)
+ return new ash::CustomFrameViewAsh(widget);
+
return new CustomFrameView(widget);
}
@@ -804,6 +877,10 @@ gfx::Size ShellSurface::GetPreferredSize() const {
return surface_ ? surface_->window()->layer()->size() : gfx::Size();
}
+gfx::Size ShellSurface::GetMinimumSize() const {
+ return gfx::Size(1, 1);
+}
+
////////////////////////////////////////////////////////////////////////////////
// ash::wm::WindowStateObserver overrides:
@@ -864,8 +941,13 @@ void ShellSurface::OnWindowBoundsChanged(aura::Window* window,
pending_origin_config_offset_ += origin_offset;
origin_ -= origin_offset;
+ gfx::Rect client_view_bounds =
+ widget_->non_client_view()->frame_view()->GetBoundsForClientView();
+
+ // Update surface bounds.
surface_->window()->SetBounds(
- gfx::Rect(GetSurfaceOrigin(), surface_->window()->layer()->size()));
+ gfx::Rect(GetSurfaceOrigin() + client_view_bounds.OffsetFromOrigin(),
+ surface_->window()->layer()->size()));
// The shadow size may be updated to match the widget. Change it back
// to the shadow content size.
@@ -880,9 +962,9 @@ void ShellSurface::OnWindowBoundsChanged(aura::Window* window,
void ShellSurface::OnWindowDestroying(aura::Window* window) {
if (window == parent_) {
parent_ = nullptr;
- // Disable shell surface in case parent is destroyed before shell surface
- // widget has been created.
- SetEnabled(false);
+ // |parent_| being set to null effects the ability to maximize the window.
+ if (widget_)
+ widget_->OnSizeConstraintsChanged();
}
window->RemoveObserver(this);
}
@@ -1054,13 +1136,11 @@ void ShellSurface::CreateShellSurfaceWidget(ui::WindowShowState show_state) {
// AutoHide shelf in fullscreen state.
window_state->set_hide_shelf_when_fullscreen(false);
- // Allow Ash to manage the position of a top-level shell surfaces if show
- // state is one that allows auto positioning and |initial_bounds_| has
- // not been set.
- window_state->set_window_position_managed(
- ash::wm::ToWindowShowState(ash::wm::WINDOW_STATE_TYPE_AUTO_POSITIONED) ==
- show_state &&
- initial_bounds_.IsEmpty());
+ // Fade visibility animations for non-activatable windows.
+ if (!activatable_) {
+ wm::SetWindowVisibilityAnimationType(
+ window, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
+ }
// Register close window accelerators.
views::FocusManager* focus_manager = widget_->GetFocusManager();
@@ -1070,10 +1150,6 @@ void ShellSurface::CreateShellSurfaceWidget(ui::WindowShowState show_state) {
ui::AcceleratorManager::kNormalPriority, this);
}
- // Set delegate for handling of fullscreening.
- window_state->SetDelegate(std::unique_ptr<ash::wm::WindowStateDelegate>(
- new CustomWindowStateDelegate(widget_)));
-
// Receive accessibility changes to update shadow underlay.
WMHelper::GetInstance()->AddAccessibilityObserver(this);
@@ -1093,21 +1169,29 @@ void ShellSurface::Configure() {
gfx::Vector2d origin_offset = pending_origin_config_offset_;
pending_origin_config_offset_ = gfx::Vector2d();
+ ash::wm::WindowState* window_state =
+ ash::wm::GetWindowState(widget_->GetNativeWindow());
+
// If surface is being resized, save the resize direction.
- int resize_component =
- resizer_ ? resizer_->details().window_component : HTCAPTION;
+ int resize_component = window_state->is_dragged()
+ ? window_state->drag_details()->window_component
+ : HTCAPTION;
+
+ uint32_t serial = 0;
+ if (!configure_callback_.is_null()) {
+ const views::NonClientView* non_client_view = widget_->non_client_view();
+ serial = configure_callback_.Run(
+ non_client_view->frame_view()->GetBoundsForClientView().size(),
+ ash::wm::GetWindowState(widget_->GetNativeWindow())->GetStateType(),
+ IsResizing(), widget_->IsActive());
+ }
- if (configure_callback_.is_null()) {
+ if (!serial) {
pending_origin_offset_ += origin_offset;
pending_resize_component_ = resize_component;
return;
}
- uint32_t serial = configure_callback_.Run(
- widget_->GetWindowBoundsInScreen().size(),
- ash::wm::GetWindowState(widget_->GetNativeWindow())->GetStateType(),
- IsResizing(), widget_->IsActive());
-
// Apply origin offset and resize component at the first Commit() after this
// configure request has been acknowledged.
pending_configs_.push_back({serial, origin_offset, resize_component});
@@ -1172,8 +1256,8 @@ void ShellSurface::AttemptToStartDrag(int component) {
}
resizer_ = ash::CreateWindowResizer(
- ash::WmWindowAura::Get(widget_->GetNativeWindow()), drag_location,
- component, aura::client::WINDOW_MOVE_SOURCE_MOUSE);
+ ash::WmWindow::Get(widget_->GetNativeWindow()), drag_location, component,
+ aura::client::WINDOW_MOVE_SOURCE_MOUSE);
if (!resizer_)
return;
@@ -1215,10 +1299,12 @@ void ShellSurface::EndDrag(bool revert) {
}
bool ShellSurface::IsResizing() const {
- if (!resizer_)
+ ash::wm::WindowState* window_state =
+ ash::wm::GetWindowState(widget_->GetNativeWindow());
+ if (!window_state->is_dragged())
return false;
- return resizer_->details().bounds_change &
+ return window_state->drag_details()->bounds_change &
ash::WindowResizer::kBoundsChange_Resizes;
}
@@ -1229,17 +1315,17 @@ gfx::Rect ShellSurface::GetVisibleBounds() const {
}
gfx::Point ShellSurface::GetSurfaceOrigin() const {
- gfx::Rect window_bounds = widget_->GetNativeWindow()->bounds();
-
// If initial bounds were specified then surface origin is always relative
// to those bounds.
if (!initial_bounds_.IsEmpty()) {
- gfx::Point origin = window_bounds.origin();
+ gfx::Point origin = widget_->GetNativeWindow()->bounds().origin();
wm::ConvertPointToScreen(widget_->GetNativeWindow()->parent(), &origin);
return initial_bounds_.origin() - origin.OffsetFromOrigin();
}
gfx::Rect visible_bounds = GetVisibleBounds();
+ gfx::Rect client_bounds =
+ widget_->non_client_view()->frame_view()->GetBoundsForClientView();
switch (resize_component_) {
case HTCAPTION:
return origin_ - visible_bounds.OffsetFromOrigin();
@@ -1249,16 +1335,16 @@ gfx::Point ShellSurface::GetSurfaceOrigin() const {
return gfx::Point() - visible_bounds.OffsetFromOrigin();
case HTTOP:
case HTTOPRIGHT:
- return gfx::Point(0, window_bounds.height() - visible_bounds.height()) -
+ return gfx::Point(0, client_bounds.height() - visible_bounds.height()) -
visible_bounds.OffsetFromOrigin();
break;
case HTLEFT:
case HTBOTTOMLEFT:
- return gfx::Point(window_bounds.width() - visible_bounds.width(), 0) -
+ return gfx::Point(client_bounds.width() - visible_bounds.width(), 0) -
visible_bounds.OffsetFromOrigin();
case HTTOPLEFT:
- return gfx::Point(window_bounds.width() - visible_bounds.width(),
- window_bounds.height() - visible_bounds.height()) -
+ return gfx::Point(client_bounds.width() - visible_bounds.width(),
+ client_bounds.height() - visible_bounds.height()) -
visible_bounds.OffsetFromOrigin();
default:
NOTREACHED();
@@ -1288,7 +1374,9 @@ void ShellSurface::UpdateWidgetBounds() {
return;
gfx::Rect visible_bounds = GetVisibleBounds();
- gfx::Rect new_widget_bounds = visible_bounds;
+ gfx::Rect new_widget_bounds =
+ widget_->non_client_view()->GetWindowBoundsForClientBounds(
+ visible_bounds);
// Avoid changing widget origin unless initial bounds were specified and
// widget origin is always relative to it.
@@ -1317,28 +1405,44 @@ void ShellSurface::UpdateWidgetBounds() {
widget_->SetBounds(new_widget_bounds);
ignore_window_bounds_changes_ = false;
+ gfx::Rect client_view_bounds =
+ widget_->non_client_view()->frame_view()->GetBoundsForClientView();
+
// A change to the widget size requires surface bounds to be re-adjusted.
surface_->window()->SetBounds(
- gfx::Rect(GetSurfaceOrigin(), surface_->window()->layer()->size()));
+ gfx::Rect(GetSurfaceOrigin() + client_view_bounds.OffsetFromOrigin(),
+ surface_->window()->layer()->size()));
}
void ShellSurface::UpdateShadow() {
- if (!widget_)
+ if (!widget_ || !surface_)
return;
aura::Window* window = widget_->GetNativeWindow();
- if (shadow_content_bounds_.IsEmpty()) {
- wm::SetShadowType(window, wm::SHADOW_TYPE_NONE);
+ if (!shadow_enabled_) {
+ wm::SetShadowElevation(window, wm::ShadowElevation::NONE);
if (shadow_underlay_)
shadow_underlay_->Hide();
} else {
- wm::SetShadowType(window, wm::SHADOW_TYPE_RECTANGULAR);
+ wm::SetShadowElevation(window, wm::ShadowElevation::MEDIUM);
+ gfx::Rect shadow_content_bounds =
+ gfx::ScaleToEnclosedRect(shadow_content_bounds_, 1.f / scale_);
+ gfx::Rect shadow_underlay_bounds = shadow_content_bounds_;
+ if (shadow_underlay_bounds.IsEmpty())
+ shadow_underlay_bounds = gfx::Rect(surface_->window()->bounds().size());
+
+ if (!shadow_underlay_in_surface_) {
+ shadow_content_bounds = shadow_content_bounds_;
+ if (shadow_content_bounds.IsEmpty()) {
+ shadow_content_bounds = window->bounds();
+ }
+ }
// TODO(oshima): Adjust the coordinates from client screen to
// chromeos screen when multi displays are supported.
gfx::Point origin = window->bounds().origin();
- gfx::Point shadow_origin = shadow_content_bounds_.origin();
+ gfx::Point shadow_origin = shadow_content_bounds.origin();
shadow_origin -= origin.OffsetFromOrigin();
- gfx::Rect shadow_bounds(shadow_origin, shadow_content_bounds_.size());
+ gfx::Rect shadow_bounds(shadow_origin, shadow_content_bounds.size());
// Always create and show the underlay, even in maximized/fullscreen.
if (!shadow_underlay_) {
@@ -1354,15 +1458,20 @@ void ShellSurface::UpdateShadow() {
shadow_underlay_->Init(ui::LAYER_SOLID_COLOR);
shadow_underlay_->layer()->SetColor(SK_ColorBLACK);
DCHECK(shadow_underlay_->layer()->fills_bounds_opaquely());
- window->AddChild(shadow_underlay_);
- window->StackChildAtBottom(shadow_underlay_);
+ if (shadow_underlay_in_surface_) {
+ surface_->window()->AddChild(shadow_underlay_);
+ surface_->window()->StackChildAtBottom(shadow_underlay_);
+ } else {
+ window->AddChild(shadow_underlay_);
+ window->StackChildAtBottom(shadow_underlay_);
+ }
}
bool underlay_capture_events =
WMHelper::GetInstance()->IsSpokenFeedbackEnabled() &&
widget_->IsActive();
- float shadow_underlay_opacity = rectangular_shadow_background_opacity_;
+ float shadow_underlay_opacity = shadow_background_opacity_;
// Put the black background layer behind the window if
// 1) the window is in immersive fullscreen or is active with
// spoken feedback enabled.
@@ -1373,14 +1482,28 @@ void ShellSurface::UpdateShadow() {
if ((widget_->IsFullscreen() || underlay_capture_events) &&
ash::wm::GetWindowState(window)->allow_set_bounds_in_maximized() &&
window->layer()->GetTargetTransform().IsIdentity()) {
- gfx::Point origin;
- origin -= window->bounds().origin().OffsetFromOrigin();
- shadow_bounds.set_origin(origin);
- shadow_bounds.set_size(window->parent()->bounds().size());
+ if (shadow_underlay_in_surface_) {
+ shadow_underlay_bounds = gfx::Rect(surface_->window()->bounds().size());
+ } else {
+ gfx::Point origin;
+ origin -= window->bounds().origin().OffsetFromOrigin();
+ shadow_bounds.set_origin(origin);
+ shadow_bounds.set_size(window->parent()->bounds().size());
+ }
shadow_underlay_opacity = 1.0f;
}
- shadow_underlay_->SetBounds(shadow_bounds);
+ if (!shadow_underlay_in_surface_)
+ shadow_underlay_bounds = shadow_bounds;
+
+ // Constrain the underlay bounds to the client area in case shell surface
+ // frame is enabled.
+ if (frame_enabled_) {
+ shadow_underlay_bounds.Intersect(
+ widget_->non_client_view()->frame_view()->GetBoundsForClientView());
+ }
+
+ shadow_underlay_->SetBounds(shadow_underlay_bounds);
// TODO(oshima): Setting to the same value should be no-op.
// crbug.com/642223.
@@ -1403,10 +1526,17 @@ void ShellSurface::UpdateShadow() {
shadow_overlay_->Init(ui::LAYER_NOT_DRAWN);
shadow_overlay_->layer()->Add(shadow->layer());
window->AddChild(shadow_overlay_);
+ if (shadow_underlay_in_surface_) {
+ window->StackChildBelow(shadow_overlay_, surface_->window());
+ }
shadow_overlay_->Show();
}
shadow_overlay_->SetBounds(shadow_bounds);
shadow->SetContentBounds(gfx::Rect(shadow_bounds.size()));
+ // Surfaces that can't be activated are usually menus and tooltips. Use a
+ // small style shadow for them.
+ if (!activatable_)
+ shadow->SetElevation(wm::ShadowElevation::SMALL);
}
}
diff --git a/chromium/components/exo/shell_surface.h b/chromium/components/exo/shell_surface.h
index d60aab4b767..64eeb544606 100644
--- a/chromium/components/exo/shell_surface.h
+++ b/chromium/components/exo/shell_surface.h
@@ -51,6 +51,7 @@ class ShellSurface : public SurfaceDelegate,
ShellSurface* parent,
const gfx::Rect& initial_bounds,
bool activatable,
+ bool can_minimize,
int container);
explicit ShellSurface(Surface* surface);
~ShellSurface() override;
@@ -146,13 +147,25 @@ class ShellSurface : public SurfaceDelegate,
// for the surface from the user's perspective.
void SetGeometry(const gfx::Rect& geometry);
- // Set the content bounds for the shadow. Empty bounds will delete
- // the shadow.
- void SetRectangularShadow(const gfx::Rect& content_bounds);
+ // Enable/disable rectangular shadow that uses the widget bounds as a content
+ // bounds.
+ void SetRectangularShadowEnabled(bool enabled);
+
+ // [Deprecated] Set the content bounds for the shadow. Shell surface geometry
+ // will be
+ // used if bounds are empty.
+ void SetRectangularShadow_DEPRECATED(const gfx::Rect& content_bounds);
+
+ // Set the content bounds for the shadow in the surface's coordinates.
+ // Setting empty bounds will disable the shadow.
+ void SetRectangularSurfaceShadow(const gfx::Rect& content_bounds);
// Set the pacity of the background for the window that has a shadow.
void SetRectangularShadowBackgroundOpacity(float opacity);
+ // Enable/disable window frame.
+ void SetFrame(bool enabled);
+
// Set scale factor for surface. The scale factor will be applied to surface
// and all descendants.
void SetScale(double scale);
@@ -160,6 +173,15 @@ class ShellSurface : public SurfaceDelegate,
// Set top inset for surface.
void SetTopInset(int height);
+ // Set origin in screen coordinate space.
+ void SetOrigin(const gfx::Point& origin);
+
+ // Set activatable state for surface.
+ void SetActivatable(bool activatable);
+
+ // Set container for surface.
+ void SetContainer(int container);
+
// Sets the main surface for the window.
static void SetMainSurface(aura::Window* window, Surface* surface);
@@ -193,6 +215,7 @@ class ShellSurface : public SurfaceDelegate,
// Overridden from views::View:
gfx::Size GetPreferredSize() const override;
+ gfx::Size GetMinimumSize() const override;
// Overridden from ash::wm::WindowStateObserver:
void OnPreWindowStateTypeChange(ash::wm::WindowState* window_state,
@@ -221,8 +244,11 @@ class ShellSurface : public SurfaceDelegate,
// Overridden from ui::AcceleratorTarget:
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
+ aura::Window* shadow_overlay() { return shadow_overlay_; }
aura::Window* shadow_underlay() { return shadow_underlay_; }
+ Surface* surface_for_testing() { return surface_; }
+
private:
class ScopedConfigure;
class ScopedAnimationsDisabled;
@@ -268,10 +294,13 @@ class ShellSurface : public SurfaceDelegate,
views::Widget* widget_ = nullptr;
Surface* surface_;
aura::Window* parent_;
- const gfx::Rect initial_bounds_;
- const bool activatable_;
+ gfx::Rect initial_bounds_;
+ bool activatable_ = true;
+ const bool can_minimize_;
// Container Window Id (see ash/public/cpp/shell_window_ids.h)
- const int container_;
+ int container_;
+ bool frame_enabled_ = false;
+ bool shadow_enabled_ = false;
bool pending_show_widget_ = false;
base::string16 title_;
std::string application_id_;
@@ -294,12 +323,13 @@ class ShellSurface : public SurfaceDelegate,
aura::Window* shadow_underlay_ = nullptr;
std::unique_ptr<ui::EventHandler> shadow_underlay_event_handler_;
gfx::Rect shadow_content_bounds_;
+ float shadow_background_opacity_ = 1.0;
std::deque<Config> pending_configs_;
std::unique_ptr<ash::WindowResizer> resizer_;
std::unique_ptr<ScopedAnimationsDisabled> scoped_animations_disabled_;
int top_inset_height_ = 0;
int pending_top_inset_height_ = 0;
- float rectangular_shadow_background_opacity_ = 1.0;
+ bool shadow_underlay_in_surface_ = true;
DISALLOW_COPY_AND_ASSIGN(ShellSurface);
};
diff --git a/chromium/components/exo/shell_surface_unittest.cc b/chromium/components/exo/shell_surface_unittest.cc
index 8675fa25ead..01501f45d3d 100644
--- a/chromium/components/exo/shell_surface_unittest.cc
+++ b/chromium/components/exo/shell_surface_unittest.cc
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ash/aura/wm_window_aura.h"
#include "ash/common/accessibility_delegate.h"
#include "ash/common/wm/window_state.h"
#include "ash/common/wm/wm_event.h"
#include "ash/common/wm_shell.h"
+#include "ash/common/wm_window.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/wm/window_state_aura.h"
#include "base/message_loop/message_loop.h"
@@ -133,6 +133,11 @@ TEST_F(ShellSurfaceTest, Minimize) {
// Confirm that attaching and commiting doesn't reset the state.
surface->Attach(buffer.get());
surface->Commit();
+ EXPECT_TRUE(shell_surface->GetWidget()->IsMinimized());
+
+ shell_surface->Restore();
+ EXPECT_FALSE(shell_surface->GetWidget()->IsMinimized());
+
shell_surface->Minimize();
EXPECT_TRUE(shell_surface->GetWidget()->IsMinimized());
}
@@ -406,7 +411,7 @@ TEST_F(ShellSurfaceTest, ConfigureCallback) {
TEST_F(ShellSurfaceTest, ModalWindow) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(
- new ShellSurface(surface.get(), nullptr, gfx::Rect(), true,
+ new ShellSurface(surface.get(), nullptr, gfx::Rect(), true, false,
ash::kShellWindowId_SystemModalContainer));
gfx::Size desktop_size(640, 480);
std::unique_ptr<Buffer> desktop_buffer(
@@ -432,7 +437,7 @@ TEST_F(ShellSurfaceTest, ModalWindow) {
EXPECT_FALSE(ash::WmShell::Get()->IsSystemModalWindowOpen());
// Making the surface opaque shouldn't make it modal either.
- child->SetBlendMode(SkXfermode::kSrc_Mode);
+ child->SetBlendMode(SkBlendMode::kSrc);
child->Commit();
surface->Commit();
EXPECT_FALSE(ash::WmShell::Get()->IsSystemModalWindowOpen());
@@ -491,11 +496,15 @@ TEST_F(ShellSurfaceTest, PopupWindow) {
popup->GetWidget()->GetWindowBoundsInScreen());
}
-TEST_F(ShellSurfaceTest, Shadow) {
+TEST_F(ShellSurfaceTest, SurfaceShadow) {
+ gfx::Size buffer_size(128, 128);
+ 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, gfx::Rect(), true,
+ new ShellSurface(surface.get(), nullptr, gfx::Rect(), true, false,
ash::kShellWindowId_DefaultContainer));
+ surface->Attach(buffer.get());
surface->Commit();
aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
@@ -509,7 +518,6 @@ TEST_F(ShellSurfaceTest, Shadow) {
// 2) Just creating a sub surface won't create a shadow.
std::unique_ptr<Surface> child = display->CreateSurface();
- gfx::Size buffer_size(128, 128);
std::unique_ptr<Buffer> child_buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
child->Attach(child_buffer.get());
@@ -520,7 +528,7 @@ TEST_F(ShellSurfaceTest, Shadow) {
EXPECT_FALSE(shadow->layer()->visible());
// 3) Create a shadow.
- shell_surface->SetRectangularShadow(gfx::Rect(10, 10, 100, 100));
+ shell_surface->SetRectangularSurfaceShadow(gfx::Rect(10, 10, 100, 100));
surface->Commit();
EXPECT_TRUE(shadow->layer()->visible());
@@ -540,26 +548,108 @@ TEST_F(ShellSurfaceTest, Shadow) {
window->SetBounds(gfx::Rect(10, 10, 100, 100));
EXPECT_EQ(before, shadow->layer()->bounds());
- // 5) Set empty content bounds should disable shadow.
- shell_surface->SetRectangularShadow(gfx::Rect());
+ // 5) This should disable shadow.
+ shell_surface->SetRectangularSurfaceShadow(gfx::Rect());
+ surface->Commit();
+
+ EXPECT_EQ(wm::ShadowElevation::NONE, wm::GetShadowElevation(window));
+ EXPECT_FALSE(shadow->layer()->visible());
+
+ // 6) This should enable non surface shadow again.
+ shell_surface->SetRectangularSurfaceShadow(gfx::Rect(10, 10, 100, 100));
+ surface->Commit();
+
+ EXPECT_EQ(wm::ShadowElevation::MEDIUM, wm::GetShadowElevation(window));
+ 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(window, shell_surface->shadow_overlay()->parent());
+
+ EXPECT_EQ(*surface->window()->children().begin(),
+ shell_surface->shadow_underlay());
+}
+
+TEST_F(ShellSurfaceTest, NonSurfaceShadow) {
+ gfx::Size buffer_size(128, 128);
+ 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, gfx::Rect(), true, false,
+ ash::kShellWindowId_DefaultContainer));
+ surface->Attach(buffer.get());
+ surface->Commit();
+
+ aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
+
+ // 1) Initial state, no shadow.
+ wm::Shadow* shadow = wm::ShadowController::GetShadowForWindow(window);
+ ASSERT_TRUE(shadow);
+ EXPECT_FALSE(shadow->layer()->visible());
+
+ std::unique_ptr<Display> display(new Display);
+
+ // 2) Just creating a sub surface won't create a shadow.
+ std::unique_ptr<Surface> child = display->CreateSurface();
+ std::unique_ptr<Buffer> child_buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+ child->Attach(child_buffer.get());
+ std::unique_ptr<SubSurface> sub_surface(
+ display->CreateSubSurface(child.get(), surface.get()));
+ surface->Commit();
+
+ EXPECT_FALSE(shadow->layer()->visible());
+
+ // 3) Enable a shadow.
+ shell_surface->SetRectangularShadowEnabled(true);
+ surface->Commit();
+ EXPECT_TRUE(shadow->layer()->visible());
+
+ gfx::Rect before = shadow->layer()->bounds();
+
+ // 4) Shadow bounds is independent of the sub surface.
+ gfx::Size new_buffer_size(256, 256);
+ std::unique_ptr<Buffer> new_child_buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(new_buffer_size)));
+ child->Attach(new_child_buffer.get());
+ child->Commit();
+ surface->Commit();
+
+ EXPECT_EQ(before, shadow->layer()->bounds());
+
+ // 4) Updating the widget's window bounds should change the non surface shadow
+ // bounds.
+ const gfx::Rect new_bounds(50, 50, 100, 100);
+ window->SetBounds(new_bounds);
+ EXPECT_NE(before, shadow->layer()->bounds());
+ EXPECT_NE(new_bounds, shadow->layer()->bounds());
+
+ // 5) This should disable shadow.
+ shell_surface->SetRectangularShadowEnabled(false);
surface->Commit();
- EXPECT_EQ(wm::SHADOW_TYPE_NONE, wm::GetShadowType(window));
+ EXPECT_EQ(wm::ShadowElevation::NONE, wm::GetShadowElevation(window));
EXPECT_FALSE(shadow->layer()->visible());
- // 6) Setting non empty content bounds should enable shadow.
- shell_surface->SetRectangularShadow(gfx::Rect(10, 10, 100, 100));
+ // 6) This should enable non surface shadow.
+ shell_surface->SetRectangularShadowEnabled(true);
surface->Commit();
- EXPECT_EQ(wm::SHADOW_TYPE_RECTANGULAR, wm::GetShadowType(window));
+ EXPECT_EQ(wm::ShadowElevation::MEDIUM, wm::GetShadowElevation(window));
EXPECT_TRUE(shadow->layer()->visible());
+
+ // For no surface shadow, both of underlay and overlay should be stacked
+ // below the surface window.
+ EXPECT_EQ(window, shell_surface->shadow_underlay()->parent());
+ EXPECT_EQ(window, shell_surface->shadow_overlay()->parent());
}
TEST_F(ShellSurfaceTest, ShadowWithStateChange) {
std::unique_ptr<Surface> surface(new Surface);
// Set the bounds to disable auto managed mode.
std::unique_ptr<ShellSurface> shell_surface(
- new ShellSurface(surface.get(), nullptr, gfx::Rect(640, 480), true,
+ new ShellSurface(surface.get(), nullptr, gfx::Rect(640, 480), true, false,
ash::kShellWindowId_DefaultContainer));
// Postion the widget at 10,10 so that we get non zero offset.
@@ -579,9 +669,9 @@ TEST_F(ShellSurfaceTest, ShadowWithStateChange) {
aura::Window* window = widget->GetNativeWindow();
wm::Shadow* shadow = wm::ShadowController::GetShadowForWindow(window);
- shell_surface->SetRectangularShadow(shadow_bounds);
+ shell_surface->SetRectangularSurfaceShadow(shadow_bounds);
surface->Commit();
- EXPECT_EQ(wm::SHADOW_TYPE_RECTANGULAR, wm::GetShadowType(window));
+ EXPECT_EQ(wm::ShadowElevation::MEDIUM, wm::GetShadowElevation(window));
// Shadow overlay bounds.
EXPECT_TRUE(shadow->layer()->visible());
@@ -596,7 +686,7 @@ TEST_F(ShellSurfaceTest, ShadowWithStateChange) {
ASSERT_TRUE(widget->IsMaximized());
EXPECT_FALSE(shadow->layer()->visible());
- shell_surface->SetRectangularShadow(work_area);
+ shell_surface->SetRectangularSurfaceShadow(work_area);
surface->Commit();
EXPECT_FALSE(shadow->layer()->visible());
@@ -608,7 +698,7 @@ TEST_F(ShellSurfaceTest, ShadowWithStateChange) {
EXPECT_EQ(shadow_in_maximized, shadow->layer()->parent()->bounds());
// The bounds is updated.
- shell_surface->SetRectangularShadow(shadow_bounds);
+ shell_surface->SetRectangularSurfaceShadow(shadow_bounds);
surface->Commit();
EXPECT_EQ(expected_shadow_bounds, shadow->layer()->parent()->bounds());
}
@@ -617,7 +707,7 @@ TEST_F(ShellSurfaceTest, ShadowWithTransform) {
std::unique_ptr<Surface> surface(new Surface);
// Set the bounds to disable auto managed mode.
std::unique_ptr<ShellSurface> shell_surface(
- new ShellSurface(surface.get(), nullptr, gfx::Rect(640, 400), true,
+ new ShellSurface(surface.get(), nullptr, gfx::Rect(640, 400), true, false,
ash::kShellWindowId_DefaultContainer));
// Postion the widget at 10,10 so that we get non zero offset.
@@ -636,7 +726,7 @@ TEST_F(ShellSurfaceTest, ShadowWithTransform) {
gfx::Transform transform;
transform.Translate(50, 50);
window->SetTransform(transform);
- shell_surface->SetRectangularShadow(shadow_bounds);
+ shell_surface->SetRectangularSurfaceShadow(shadow_bounds);
surface->Commit();
EXPECT_TRUE(shadow->layer()->visible());
EXPECT_EQ(gfx::Rect(-10, -10, 100, 100), shadow->layer()->parent()->bounds());
@@ -645,7 +735,7 @@ TEST_F(ShellSurfaceTest, ShadowWithTransform) {
TEST_F(ShellSurfaceTest, ShadowStartMaximized) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(
- new ShellSurface(surface.get(), nullptr, gfx::Rect(640, 480), true,
+ new ShellSurface(surface.get(), nullptr, gfx::Rect(640, 480), true, false,
ash::kShellWindowId_DefaultContainer));
shell_surface->Maximize();
views::Widget* widget = shell_surface->GetWidget();
@@ -655,7 +745,7 @@ TEST_F(ShellSurfaceTest, ShadowStartMaximized) {
EXPECT_FALSE(wm::ShadowController::GetShadowForWindow(window));
// Sending a shadow bounds in maximized state won't create a shaodw.
- shell_surface->SetRectangularShadow(gfx::Rect(10, 10, 100, 100));
+ shell_surface->SetRectangularSurfaceShadow(gfx::Rect(10, 10, 100, 100));
surface->Commit();
EXPECT_FALSE(wm::ShadowController::GetShadowForWindow(window));
@@ -691,7 +781,7 @@ TEST_F(ShellSurfaceTest, ToggleFullscreen) {
ash::wm::WMEvent event(ash::wm::WM_EVENT_TOGGLE_FULLSCREEN);
ash::WmWindow* window =
- ash::WmWindowAura::Get(shell_surface->GetWidget()->GetNativeWindow());
+ ash::WmWindow::Get(shell_surface->GetWidget()->GetNativeWindow());
// Enter fullscreen mode.
window->GetWindowState()->OnWMEvent(&event);
@@ -708,34 +798,39 @@ TEST_F(ShellSurfaceTest, ToggleFullscreen) {
}
TEST_F(ShellSurfaceTest, ImmersiveFullscreenBackground) {
- gfx::Size buffer_size(256, 256);
+ const gfx::Size display_size =
+ display::Screen::GetScreen()->GetPrimaryDisplay().size();
+ const gfx::Size buffer_size(display_size);
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, gfx::Rect(640, 480), true,
+ new ShellSurface(surface.get(), nullptr, gfx::Rect(100, 100), true, false,
ash::kShellWindowId_DefaultContainer));
surface->Attach(buffer.get());
gfx::Rect shadow_bounds(10, 10, 100, 100);
- shell_surface->SetRectangularShadow(shadow_bounds);
+ shell_surface->SetGeometry(shadow_bounds);
+ shell_surface->SetRectangularSurfaceShadow(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());
ash::wm::WMEvent event(ash::wm::WM_EVENT_TOGGLE_FULLSCREEN);
ash::WmWindow* window =
- ash::WmWindowAura::Get(shell_surface->GetWidget()->GetNativeWindow());
+ ash::WmWindow::Get(shell_surface->GetWidget()->GetNativeWindow());
// Enter immersive fullscreen mode. Shadow underlay is fullscreen.
window->GetWindowState()->OnWMEvent(&event);
- EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().bounds(),
- shell_surface->shadow_underlay()->bounds());
EXPECT_TRUE(shell_surface->shadow_underlay()->IsVisible());
EXPECT_EQ(1.f, shell_surface->shadow_underlay()->layer()->opacity());
- EXPECT_NE(shell_surface->GetWidget()->GetWindowBoundsInScreen(),
- shell_surface->shadow_underlay()->bounds());
+ EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().size(),
+ shell_surface->shadow_underlay()->bounds().size());
// Leave fullscreen mode. Shadow underlay is restored.
window->GetWindowState()->OnWMEvent(&event);
@@ -744,29 +839,35 @@ TEST_F(ShellSurfaceTest, ImmersiveFullscreenBackground) {
}
TEST_F(ShellSurfaceTest, SpokenFeedbackFullscreenBackground) {
- gfx::Size buffer_size(256, 256);
+ const gfx::Size display_size =
+ display::Screen::GetScreen()->GetPrimaryDisplay().size();
+ const gfx::Size buffer_size(display_size);
Buffer buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
Surface surface;
- ShellSurface shell_surface(&surface, nullptr, gfx::Rect(640, 480), true,
- ash::kShellWindowId_DefaultContainer);
-
+ ShellSurface shell_surface(&surface, nullptr, gfx::Rect(100, 100), true,
+ false, ash::kShellWindowId_DefaultContainer);
surface.Attach(&buffer);
gfx::Rect shadow_bounds(10, 10, 100, 100);
- shell_surface.SetRectangularShadow(shadow_bounds);
+ shell_surface.SetGeometry(shadow_bounds);
+ shell_surface.SetRectangularSurfaceShadow(shadow_bounds);
surface.Commit();
+ EXPECT_EQ(shadow_bounds,
+ shell_surface.GetWidget()->GetWindowBoundsInScreen());
ASSERT_EQ(shadow_bounds, shell_surface.shadow_underlay()->bounds());
aura::Window* shell_window = shell_surface.GetWidget()->GetNativeWindow();
aura::WindowTargeter* targeter = static_cast<aura::WindowTargeter*>(
static_cast<ui::EventTarget*>(shell_window)->GetEventTargeter());
- gfx::Point pt(300, 300);
- ui::MouseEvent ev_out(ui::ET_MOUSE_PRESSED, pt, pt, ui::EventTimeForNow(),
- ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
- gfx::Point pt2(250, 250);
- ui::MouseEvent ev_in(ui::ET_MOUSE_PRESSED, pt2, pt2, ui::EventTimeForNow(),
- ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+ gfx::Point pt_out(300, 300);
+ ui::MouseEvent ev_out(ui::ET_MOUSE_PRESSED, pt_out, pt_out,
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_LEFT_MOUSE_BUTTON);
+ gfx::Point pt_in(70, 70);
+ ui::MouseEvent ev_in(ui::ET_MOUSE_PRESSED, pt_in, pt_in,
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_LEFT_MOUSE_BUTTON);
EXPECT_FALSE(targeter->SubtreeShouldBeExploredForEvent(shell_window, ev_out));
@@ -794,9 +895,9 @@ TEST_F(ShellSurfaceTest, SpokenFeedbackFullscreenBackground) {
Buffer buffer2(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
Surface surface2;
ShellSurface shell_surface2(&surface2, nullptr, gfx::Rect(640, 480), true,
- ash::kShellWindowId_DefaultContainer);
+ false, ash::kShellWindowId_DefaultContainer);
surface2.Attach(&buffer2);
- shell_surface2.SetRectangularShadow(shadow_bounds);
+ shell_surface2.SetRectangularSurfaceShadow(shadow_bounds);
surface2.Commit();
// spoken-feedback was already on, so underlay should fill screen
diff --git a/chromium/components/exo/surface.cc b/chromium/components/exo/surface.cc
index eda22743a67..046b881dbe0 100644
--- a/chromium/components/exo/surface.cc
+++ b/chromium/components/exo/surface.cc
@@ -17,10 +17,9 @@
#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_factory.h"
#include "cc/surfaces/surface_id_allocator.h"
-#include "cc/surfaces/surface_manager.h"
#include "components/exo/buffer.h"
#include "components/exo/surface_delegate.h"
#include "components/exo/surface_observer.h"
@@ -141,73 +140,29 @@ class CustomWindowTargeter : public aura::WindowTargeter {
DISALLOW_COPY_AND_ASSIGN(CustomWindowTargeter);
};
-void SatisfyCallback(cc::SurfaceManager* manager,
- const cc::SurfaceSequence& sequence) {
- std::vector<uint32_t> sequences;
- sequences.push_back(sequence.sequence);
- manager->DidSatisfySequences(sequence.frame_sink_id, &sequences);
-}
-
-void RequireCallback(cc::SurfaceManager* manager,
- const cc::SurfaceId& id,
- const cc::SurfaceSequence& sequence) {
- cc::Surface* surface = manager->GetSurfaceForId(id);
- if (!surface) {
- LOG(ERROR) << "Attempting to require callback on nonexistent surface";
- return;
- }
- surface->AddDestructionDependency(sequence);
-}
-
} // namespace
////////////////////////////////////////////////////////////////////////////////
-// SurfaceFactoryOwner, public:
-
-SurfaceFactoryOwner::SurfaceFactoryOwner() {}
-
-////////////////////////////////////////////////////////////////////////////////
-// cc::SurfaceFactoryClient overrides:
-
-void SurfaceFactoryOwner::ReturnResources(
- const cc::ReturnedResourceArray& resources) {
- scoped_refptr<SurfaceFactoryOwner> holder(this);
- for (auto& resource : resources) {
- auto it = release_callbacks_.find(resource.id);
- DCHECK(it != release_callbacks_.end());
- it->second.second->Run(resource.sync_token, resource.lost);
- release_callbacks_.erase(it);
- }
-}
-
-void SurfaceFactoryOwner::WillDrawSurface(const cc::LocalFrameId& id,
- const gfx::Rect& damage_rect) {
- if (surface_)
- surface_->WillDraw();
-}
-
-void SurfaceFactoryOwner::SetBeginFrameSource(
- cc::BeginFrameSource* begin_frame_source) {
- if (surface_)
- surface_->SetBeginFrameSource(begin_frame_source);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// SurfaceFactoryOwner, private:
-
-SurfaceFactoryOwner::~SurfaceFactoryOwner() {
- if (surface_factory_->manager())
- surface_factory_->manager()->InvalidateFrameSinkId(frame_sink_id_);
-}
-
-////////////////////////////////////////////////////////////////////////////////
// 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))),
- surface_manager_(
- aura::Env::GetInstance()->context_factory()->GetSurfaceManager()),
- factory_owner_(new SurfaceFactoryOwner) {
+ frame_sink_id_(aura::Env::GetInstance()
+ ->context_factory_private()
+ ->AllocateFrameSinkId()) {
+ compositor_frame_sink_holder_ = new CompositorFrameSinkHolder(
+ this, frame_sink_id_,
+ aura::Env::GetInstance()->context_factory_private()->GetSurfaceManager());
+ // TODO(samans): exo::Surface should not be using
+ // DirectSurfaceReferenceFactory (crbug.com/688573).
+ surface_reference_factory_ = aura::Env::GetInstance()
+ ->context_factory_private()
+ ->GetSurfaceManager()
+ ->reference_factory();
window_->SetType(ui::wm::WINDOW_TYPE_CONTROL);
window_->SetName("ExoSurface");
window_->SetProperty(kSurfaceKey, this);
@@ -215,15 +170,6 @@ Surface::Surface()
window_->SetEventTargeter(base::WrapUnique(new CustomWindowTargeter));
window_->set_owned_by_parent(false);
window_->AddObserver(this);
- factory_owner_->surface_ = this;
- factory_owner_->frame_sink_id_ =
- aura::Env::GetInstance()->context_factory()->AllocateFrameSinkId();
- factory_owner_->id_allocator_.reset(new cc::SurfaceIdAllocator());
- surface_manager_->RegisterFrameSinkId(factory_owner_->frame_sink_id_);
- surface_manager_->RegisterSurfaceFactoryClient(factory_owner_->frame_sink_id_,
- factory_owner_.get());
- factory_owner_->surface_factory_.reset(new cc::SurfaceFactory(
- factory_owner_->frame_sink_id_, surface_manager_, factory_owner_.get()));
aura::Env::GetInstance()->context_factory()->AddObserver(this);
}
@@ -233,26 +179,30 @@ Surface::~Surface() {
observer.OnSurfaceDestroying(this);
window_->RemoveObserver(this);
+ if (window_->layer()->GetCompositor())
+ window_->layer()->GetCompositor()->vsync_manager()->RemoveObserver(this);
window_->layer()->SetShowSolidColorContent();
- factory_owner_->surface_ = nullptr;
-
- // Call pending frame callbacks with a null frame time to indicate that they
- // have been cancelled.
frame_callbacks_.splice(frame_callbacks_.end(), pending_frame_callbacks_);
active_frame_callbacks_.splice(active_frame_callbacks_.end(),
frame_callbacks_);
+ // Call all frame callbacks with a null frame time to indicate that they
+ // have been cancelled.
for (const auto& frame_callback : active_frame_callbacks_)
frame_callback.Run(base::TimeTicks());
- if (begin_frame_source_ && needs_begin_frame_)
- begin_frame_source_->RemoveObserver(this);
+ presentation_callbacks_.splice(presentation_callbacks_.end(),
+ pending_presentation_callbacks_);
+ swapping_presentation_callbacks_.splice(
+ swapping_presentation_callbacks_.end(), presentation_callbacks_);
+ swapped_presentation_callbacks_.splice(swapped_presentation_callbacks_.end(),
+ swapping_presentation_callbacks_);
+ // Call all presentation callbacks with a null presentation time to indicate
+ // that they have been cancelled.
+ for (const auto& presentation_callback : swapped_presentation_callbacks_)
+ presentation_callback.Run(base::TimeTicks(), base::TimeDelta());
- if (local_frame_id_.is_valid())
- factory_owner_->surface_factory_->Destroy(local_frame_id_);
-
- surface_manager_->UnregisterSurfaceFactoryClient(
- factory_owner_->frame_sink_id_);
+ compositor_frame_sink_holder_->GetCompositorFrameSink()->EvictFrame();
}
// static
@@ -261,7 +211,7 @@ Surface* Surface::AsSurface(const aura::Window* window) {
}
cc::SurfaceId Surface::GetSurfaceId() const {
- return cc::SurfaceId(factory_owner_->frame_sink_id_, local_frame_id_);
+ return cc::SurfaceId(frame_sink_id_, local_frame_id_);
}
void Surface::Attach(Buffer* buffer) {
@@ -284,6 +234,13 @@ void Surface::RequestFrameCallback(const FrameCallback& callback) {
pending_frame_callbacks_.push_back(callback);
}
+void Surface::RequestPresentationCallback(
+ const PresentationCallback& callback) {
+ TRACE_EVENT0("exo", "Surface::RequestPresentationCallback");
+
+ pending_presentation_callbacks_.push_back(callback);
+}
+
void Surface::SetOpaqueRegion(const SkRegion& region) {
TRACE_EVENT1("exo", "Surface::SetOpaqueRegion", "region",
gfx::SkIRectToRect(region.getBounds()).ToString());
@@ -318,7 +275,7 @@ void Surface::AddSubSurface(Surface* sub_surface) {
}
void Surface::RemoveSubSurface(Surface* sub_surface) {
- TRACE_EVENT1("exo", "Surface::AddSubSurface", "sub_surface",
+ TRACE_EVENT1("exo", "Surface::RemoveSubSurface", "sub_surface",
sub_surface->AsTracedValue());
window_->RemoveChild(sub_surface->window());
@@ -420,8 +377,9 @@ void Surface::SetOnlyVisibleOnSecureOutput(bool only_visible_on_secure_output) {
pending_state_.only_visible_on_secure_output = only_visible_on_secure_output;
}
-void Surface::SetBlendMode(SkXfermode::Mode blend_mode) {
- TRACE_EVENT1("exo", "Surface::SetBlendMode", "blend_mode", blend_mode);
+void Surface::SetBlendMode(SkBlendMode blend_mode) {
+ TRACE_EVENT1("exo", "Surface::SetBlendMode", "blend_mode",
+ static_cast<int>(blend_mode));
pending_state_.blend_mode = blend_mode;
}
@@ -477,17 +435,10 @@ void Surface::CommitSurfaceHierarchy() {
cc::LocalFrameId old_local_frame_id = local_frame_id_;
if (needs_commit_to_new_surface_ || !local_frame_id_.is_valid()) {
needs_commit_to_new_surface_ = false;
- local_frame_id_ = factory_owner_->id_allocator_->GenerateId();
- factory_owner_->surface_factory_->Create(local_frame_id_);
+ local_frame_id_ = id_allocator_.GenerateId();
}
- UpdateSurface(true);
-
- if (old_local_frame_id.is_valid() && old_local_frame_id != local_frame_id_) {
- factory_owner_->surface_factory_->SetPreviousFrameSurface(
- local_frame_id_, old_local_frame_id);
- factory_owner_->surface_factory_->Destroy(old_local_frame_id);
- }
+ UpdateSurface(false);
if (old_local_frame_id != local_frame_id_) {
float contents_surface_to_layer_scale = 1.0;
@@ -496,13 +447,13 @@ void Surface::CommitSurfaceHierarchy() {
// mirror layer to update its surface using the latest bounds.
window_->layer()->SetBounds(
gfx::Rect(window_->layer()->bounds().origin(), content_size_));
+ cc::SurfaceId surface_id(frame_sink_id_, local_frame_id_);
window_->layer()->SetShowSurface(
- cc::SurfaceId(factory_owner_->frame_sink_id_, local_frame_id_),
- base::Bind(&SatisfyCallback, base::Unretained(surface_manager_)),
- base::Bind(&RequireCallback, base::Unretained(surface_manager_)),
- content_size_, contents_surface_to_layer_scale, content_size_);
+ cc::SurfaceInfo(surface_id, contents_surface_to_layer_scale,
+ content_size_),
+ surface_reference_factory_);
window_->layer()->SetFillsBoundsOpaquely(
- state_.blend_mode == SkXfermode::kSrc_Mode ||
+ state_.blend_mode == SkBlendMode::kSrc ||
state_.opaque_region.contains(
gfx::RectToSkIRect(gfx::Rect(content_size_))));
}
@@ -511,11 +462,16 @@ void Surface::CommitSurfaceHierarchy() {
pending_damage_.setEmpty();
DCHECK(!current_resource_.id ||
- factory_owner_->release_callbacks_.count(current_resource_.id));
+ compositor_frame_sink_holder_->HasReleaseCallbackForResource(
+ current_resource_.id));
// Move pending frame callbacks to the end of frame_callbacks_.
frame_callbacks_.splice(frame_callbacks_.end(), pending_frame_callbacks_);
+ // Move pending presentation callbacks to the end of presentation_callbacks_.
+ presentation_callbacks_.splice(presentation_callbacks_.end(),
+ pending_presentation_callbacks_);
+
// Synchronize window hierarchy. This will position and update the stacking
// order of all sub-surfaces after committing all pending state of sub-surface
// descendants.
@@ -618,16 +574,19 @@ std::unique_ptr<base::trace_event::TracedValue> Surface::AsTracedValue() const {
void Surface::WillDraw() {
active_frame_callbacks_.splice(active_frame_callbacks_.end(),
frame_callbacks_);
- UpdateNeedsBeginFrame();
+ swapping_presentation_callbacks_.splice(
+ swapping_presentation_callbacks_.end(), presentation_callbacks_);
+}
+
+bool Surface::NeedsBeginFrame() const {
+ return !active_frame_callbacks_.empty();
}
-void Surface::SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) {
- if (begin_frame_source_ && needs_begin_frame_) {
- begin_frame_source_->RemoveObserver(this);
- needs_begin_frame_ = false;
+void Surface::BeginFrame(base::TimeTicks frame_time) {
+ while (!active_frame_callbacks_.empty()) {
+ active_frame_callbacks_.front().Run(frame_time);
+ active_frame_callbacks_.pop_front();
}
- begin_frame_source_ = begin_frame_source;
- UpdateNeedsBeginFrame();
}
void Surface::CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces() {
@@ -635,37 +594,56 @@ void Surface::CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces() {
SetSurfaceHierarchyNeedsCommitToNewSurfaces();
}
+////////////////////////////////////////////////////////////////////////////////
+// ui::ContextFactoryObserver overrides:
+
void Surface::OnLostResources() {
if (!local_frame_id_.is_valid())
return;
UpdateResource(false);
- UpdateSurface(false);
+ UpdateSurface(true);
}
+////////////////////////////////////////////////////////////////////////////////
+// aura::WindowObserver overrides:
+
void Surface::OnWindowAddedToRootWindow(aura::Window* window) {
- window->layer()->GetCompositor()->AddFrameSink(
- factory_owner_->frame_sink_id_);
+ window->layer()->GetCompositor()->AddFrameSink(frame_sink_id_);
+ window->layer()->GetCompositor()->vsync_manager()->AddObserver(this);
}
void Surface::OnWindowRemovingFromRootWindow(aura::Window* window,
aura::Window* new_root) {
- window->layer()->GetCompositor()->RemoveFrameSink(
- factory_owner_->frame_sink_id_);
+ window->layer()->GetCompositor()->RemoveFrameSink(frame_sink_id_);
+ window->layer()->GetCompositor()->vsync_manager()->RemoveObserver(this);
}
-void Surface::OnBeginFrame(const cc::BeginFrameArgs& args) {
- while (!active_frame_callbacks_.empty()) {
- active_frame_callbacks_.front().Run(args.frame_time);
- active_frame_callbacks_.pop_front();
+////////////////////////////////////////////////////////////////////////////////
+// ui::CompositorVSyncManager::Observer overrides:
+
+void Surface::OnUpdateVSyncParameters(base::TimeTicks timebase,
+ base::TimeDelta interval) {
+ // Use current time if platform doesn't provide an accurate timebase.
+ if (timebase.is_null())
+ timebase = base::TimeTicks::Now();
+
+ while (!swapped_presentation_callbacks_.empty()) {
+ swapped_presentation_callbacks_.front().Run(timebase, interval);
+ swapped_presentation_callbacks_.pop_front();
}
- last_begin_frame_args_ = args;
-}
-const cc::BeginFrameArgs& Surface::LastUsedBeginFrameArgs() const {
- return last_begin_frame_args_;
+ // VSync parameters updates are generated at the start of a new swap. Move
+ // the swapping presentation callbacks to swapped callbacks so they fire
+ // at the next VSync parameters update as that will contain the presentation
+ // time for the previous frame.
+ swapped_presentation_callbacks_.splice(swapped_presentation_callbacks_.end(),
+ swapping_presentation_callbacks_);
}
+////////////////////////////////////////////////////////////////////////////////
+// Buffer, private:
+
Surface::State::State() : input_region(SkIRect::MakeLargest()) {}
Surface::State::~State() = default;
@@ -729,32 +707,11 @@ void Surface::SetSurfaceHierarchyNeedsCommitToNewSurfaces() {
}
void Surface::UpdateResource(bool client_usage) {
- std::unique_ptr<cc::SingleReleaseCallback> texture_mailbox_release_callback;
-
- cc::TextureMailbox texture_mailbox;
- if (current_buffer_.buffer()) {
- texture_mailbox_release_callback =
- current_buffer_.buffer()->ProduceTextureMailbox(
- &texture_mailbox, state_.only_visible_on_secure_output,
- client_usage);
- }
-
- if (texture_mailbox_release_callback) {
- cc::TransferableResource resource;
- resource.id = next_resource_id_++;
- resource.format = cc::RGBA_8888;
- resource.filter =
- texture_mailbox.nearest_neighbor() ? GL_NEAREST : GL_LINEAR;
- resource.size = texture_mailbox.size_in_pixels();
- resource.mailbox_holder = gpu::MailboxHolder(texture_mailbox.mailbox(),
- texture_mailbox.sync_token(),
- texture_mailbox.target());
- resource.is_overlay_candidate = texture_mailbox.is_overlay_candidate();
-
- factory_owner_->release_callbacks_[resource.id] = std::make_pair(
- factory_owner_, std::move(texture_mailbox_release_callback));
- current_resource_ = resource;
- } else {
+ if (!current_buffer_.buffer() ||
+ !current_buffer_.buffer()->ProduceTransferableResource(
+ compositor_frame_sink_holder_.get(), next_resource_id_++,
+ state_.only_visible_on_secure_output, client_usage,
+ &current_resource_)) {
current_resource_.id = 0;
current_resource_.size = gfx::Size();
}
@@ -800,9 +757,10 @@ void Surface::UpdateSurface(bool full_damage) {
? gfx::Rect(contents_surface_size)
: gfx::SkIRectToRect(pending_damage_.getBounds());
+ const int kRenderPassId = 1;
std::unique_ptr<cc::RenderPass> render_pass = cc::RenderPass::Create();
- render_pass->SetAll(cc::RenderPassId(1, 1), gfx::Rect(contents_surface_size),
- damage_rect, gfx::Transform(), true);
+ render_pass->SetNew(kRenderPassId, gfx::Rect(contents_surface_size),
+ damage_rect, gfx::Transform());
gfx::Rect quad_rect = gfx::Rect(contents_surface_size);
cc::SharedQuadState* quad_state =
@@ -811,8 +769,7 @@ void Surface::UpdateSurface(bool full_damage) {
quad_state->visible_quad_layer_rect = quad_rect;
quad_state->opacity = state_.alpha;
- std::unique_ptr<cc::DelegatedFrameData> delegated_frame(
- new cc::DelegatedFrameData);
+ cc::CompositorFrame frame;
if (current_resource_.id) {
// Texture quad is only needed if buffer is not fully transparent.
if (state_.alpha) {
@@ -820,7 +777,7 @@ void Surface::UpdateSurface(bool full_damage) {
render_pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
float vertex_opacity[4] = {1.0, 1.0, 1.0, 1.0};
gfx::Rect opaque_rect;
- if (state_.blend_mode == SkXfermode::kSrc_Mode ||
+ if (state_.blend_mode == SkBlendMode::kSrc ||
state_.opaque_region.contains(gfx::RectToSkIRect(quad_rect))) {
opaque_rect = quad_rect;
} else if (state_.opaque_region.isRect()) {
@@ -833,7 +790,7 @@ void Surface::UpdateSurface(bool full_damage) {
false, false, state_.only_visible_on_secure_output);
if (current_resource_.is_overlay_candidate)
texture_quad->set_resource_size_in_pixels(current_resource_.size);
- delegated_frame->resource_list.push_back(current_resource_);
+ frame.resource_list.push_back(current_resource_);
}
} else {
cc::SolidColorDrawQuad* solid_quad =
@@ -841,27 +798,9 @@ void Surface::UpdateSurface(bool full_damage) {
solid_quad->SetNew(quad_state, quad_rect, quad_rect, SK_ColorBLACK, false);
}
- delegated_frame->render_pass_list.push_back(std::move(render_pass));
- cc::CompositorFrame frame;
- frame.delegated_frame_data = std::move(delegated_frame);
-
- factory_owner_->surface_factory_->SubmitCompositorFrame(
- local_frame_id_, std::move(frame), cc::SurfaceFactory::DrawCallback());
-}
-
-void Surface::UpdateNeedsBeginFrame() {
- if (!begin_frame_source_)
- return;
-
- bool needs_begin_frame = !active_frame_callbacks_.empty();
- if (needs_begin_frame == needs_begin_frame_)
- return;
-
- needs_begin_frame_ = needs_begin_frame;
- if (needs_begin_frame)
- begin_frame_source_->AddObserver(this);
- else
- begin_frame_source_->RemoveObserver(this);
+ frame.render_pass_list.push_back(std::move(render_pass));
+ compositor_frame_sink_holder_->GetCompositorFrameSink()
+ ->SubmitCompositorFrame(local_frame_id_, std::move(frame));
}
int64_t Surface::SetPropertyInternal(const void* key,
diff --git a/chromium/components/exo/surface.h b/chromium/components/exo/surface.h
index 6a03b04a714..0aeaa0edaa9 100644
--- a/chromium/components/exo/surface.h
+++ b/chromium/components/exo/surface.h
@@ -17,11 +17,14 @@
#include "base/observer_list.h"
#include "cc/resources/transferable_resource.h"
#include "cc/scheduler/begin_frame_source.h"
-#include "cc/surfaces/surface_factory_client.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "components/exo/compositor_frame_sink.h"
+#include "components/exo/compositor_frame_sink_holder.h"
+#include "third_party/skia/include/core/SkBlendMode.h"
#include "third_party/skia/include/core/SkRegion.h"
-#include "third_party/skia/include/core/SkXfermode.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
+#include "ui/compositor/compositor_vsync_manager.h"
#include "ui/gfx/geometry/rect.h"
namespace base {
@@ -31,7 +34,6 @@ class TracedValue;
}
namespace cc {
-class SurfaceFactory;
class SurfaceIdAllocator;
}
@@ -57,42 +59,11 @@ class PropertyHelper;
// change in the future when better hardware cursor support is added.
using CursorProvider = Pointer;
-// This class owns the SurfaceFactory 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 SurfaceFactoryOwner : public base::RefCounted<SurfaceFactoryOwner>,
- public cc::SurfaceFactoryClient {
- public:
- SurfaceFactoryOwner();
-
- // Overridden from cc::SurfaceFactoryClient:
- void ReturnResources(const cc::ReturnedResourceArray& resources) override;
- void WillDrawSurface(const cc::LocalFrameId& id,
- const gfx::Rect& damage_rect) override;
- void SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) override;
-
- private:
- friend class base::RefCounted<SurfaceFactoryOwner>;
- friend class Surface;
-
- ~SurfaceFactoryOwner() override;
-
- std::map<int,
- std::pair<scoped_refptr<SurfaceFactoryOwner>,
- std::unique_ptr<cc::SingleReleaseCallback>>>
- release_callbacks_;
- cc::FrameSinkId frame_sink_id_;
- std::unique_ptr<cc::SurfaceIdAllocator> id_allocator_;
- std::unique_ptr<cc::SurfaceFactory> surface_factory_;
- Surface* surface_ = nullptr;
-};
-
// This class represents a rectangular area that is displayed on the screen.
// It has a location, size and pixel contents.
class Surface : public ui::ContextFactoryObserver,
public aura::WindowObserver,
- public cc::BeginFrameObserver {
+ public ui::CompositorVSyncManager::Observer {
public:
using PropertyDeallocator = void (*)(int64_t value);
@@ -104,9 +75,12 @@ class Surface : public ui::ContextFactoryObserver,
aura::Window* window() { return window_.get(); }
- const cc::LocalFrameId& local_frame_id() const { return local_frame_id_; }
cc::SurfaceId GetSurfaceId() const;
+ CompositorFrameSinkHolder* compositor_frame_sink_holder() {
+ return compositor_frame_sink_holder_.get();
+ }
+
// Set a buffer as the content of this surface. A buffer can only be attached
// to one surface at a time.
void Attach(Buffer* buffer);
@@ -116,11 +90,18 @@ class Surface : public ui::ContextFactoryObserver,
// repainted.
void Damage(const gfx::Rect& rect);
- // Request notification when the next frame is displayed. Useful for
- // throttling redrawing operations, and driving animations.
+ // Request notification when it's a good time to produce a new frame. Useful
+ // for throttling redrawing operations, and driving animations.
using FrameCallback = base::Callback<void(base::TimeTicks frame_time)>;
void RequestFrameCallback(const FrameCallback& callback);
+ // Request notification when the next frame is displayed. Useful for
+ // throttling redrawing operations, and driving animations.
+ using PresentationCallback =
+ base::Callback<void(base::TimeTicks presentation_time,
+ base::TimeDelta refresh)>;
+ void RequestPresentationCallback(const PresentationCallback& callback);
+
// This sets the region of the surface that contains opaque content.
void SetOpaqueRegion(const SkRegion& region);
@@ -153,7 +134,7 @@ class Surface : public ui::ContextFactoryObserver,
void SetOnlyVisibleOnSecureOutput(bool only_visible_on_secure_output);
// This sets the blend mode that will be used when drawing the surface.
- void SetBlendMode(SkXfermode::Mode blend_mode);
+ void SetBlendMode(SkBlendMode blend_mode);
// This sets the alpha value that will be applied to the whole surface.
void SetAlpha(float alpha);
@@ -208,11 +189,15 @@ class Surface : public ui::ContextFactoryObserver,
// Returns a trace value representing the state of the surface.
std::unique_ptr<base::trace_event::TracedValue> AsTracedValue() const;
- // Called when surface is being scheduled for a draw.
+ // Call this to indicate that surface is being scheduled for a draw.
void WillDraw();
- // Called when the begin frame source has changed.
- void SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source);
+ // Returns true when there's an active frame callback that requires a
+ // BeginFrame() call.
+ bool NeedsBeginFrame() const;
+
+ // Call this to indicate that it's a good time to start producing a new frame.
+ void BeginFrame(base::TimeTicks frame_time);
// Check whether this Surface and its children need to create new cc::Surface
// IDs for their contents next time they get new buffer contents.
@@ -229,10 +214,9 @@ class Surface : public ui::ContextFactoryObserver,
void OnWindowRemovingFromRootWindow(aura::Window* window,
aura::Window* new_root) override;
- // Overridden from cc::BeginFrameObserver:
- void OnBeginFrame(const cc::BeginFrameArgs& args) override;
- const cc::BeginFrameArgs& LastUsedBeginFrameArgs() const override;
- void OnBeginFrameSourcePausedChanged(bool paused) override {}
+ // Overridden from ui::CompositorVSyncManager::Observer:
+ void OnUpdateVSyncParameters(base::TimeTicks timebase,
+ base::TimeDelta interval) override;
// Sets the |value| of the given surface |property|. Setting to the default
// value (e.g., NULL) removes the property. The caller is responsible for the
@@ -268,7 +252,7 @@ class Surface : public ui::ContextFactoryObserver,
gfx::Size viewport;
gfx::RectF crop;
bool only_visible_on_secure_output = false;
- SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode;
+ SkBlendMode blend_mode = SkBlendMode::kSrcOver;
float alpha = 1.0f;
};
class BufferAttachment {
@@ -314,9 +298,6 @@ class Surface : public ui::ContextFactoryObserver,
// current_resource_.
void UpdateSurface(bool full_damage);
- // Adds/Removes begin frame observer based on state.
- void UpdateNeedsBeginFrame();
-
int64_t SetPropertyInternal(const void* key,
const char* name,
PropertyDeallocator deallocator,
@@ -350,12 +331,12 @@ class Surface : public ui::ContextFactoryObserver,
// The buffer that will become the content of surface when Commit() is called.
BufferAttachment pending_buffer_;
- cc::SurfaceManager* surface_manager_;
+ const cc::FrameSinkId frame_sink_id_;
+ cc::LocalFrameId local_frame_id_;
- scoped_refptr<SurfaceFactoryOwner> factory_owner_;
+ scoped_refptr<CompositorFrameSinkHolder> compositor_frame_sink_holder_;
- // The Surface Id currently attached to the window.
- cc::LocalFrameId local_frame_id_;
+ cc::SurfaceIdAllocator id_allocator_;
// The next resource id the buffer will be attached to.
int next_resource_id_ = 1;
@@ -372,6 +353,18 @@ class Surface : public ui::ContextFactoryObserver,
std::list<FrameCallback> frame_callbacks_;
std::list<FrameCallback> active_frame_callbacks_;
+ // These lists contains the callbacks to notify the client when surface
+ // contents have been presented. These callbacks move to
+ // |presentation_callbacks_| when Commit() is called. Later they are moved to
+ // |swapping_presentation_callbacks_| when the effect of the Commit() is
+ // scheduled to be drawn and then moved to |swapped_presentation_callbacks_|
+ // after receiving VSync parameters update for the previous frame. They fire
+ // at the next VSync parameters update after that.
+ std::list<PresentationCallback> pending_presentation_callbacks_;
+ std::list<PresentationCallback> presentation_callbacks_;
+ std::list<PresentationCallback> swapping_presentation_callbacks_;
+ std::list<PresentationCallback> swapped_presentation_callbacks_;
+
// This is the state that has yet to be committed.
State pending_state_;
@@ -407,11 +400,6 @@ class Surface : public ui::ContextFactoryObserver,
// maintains.
SurfaceDelegate* delegate_ = nullptr;
- // The begin frame source being observed.
- cc::BeginFrameSource* begin_frame_source_ = nullptr;
- cc::BeginFrameArgs last_begin_frame_args_;
- bool needs_begin_frame_ = false;
-
struct Value {
const char* name;
int64_t value;
@@ -423,6 +411,12 @@ class Surface : public ui::ContextFactoryObserver,
// Surface observer list. Surface does not own the observers.
base::ObserverList<SurfaceObserver, true> observers_;
+ // A reference factory that uses the compositor frame sink holder provided
+ // to this class to construct surface references. This object is passed to
+ // ui::Layer::SetShowSurface because the layer needs to know how to add
+ // references to surfaces.
+ scoped_refptr<cc::SurfaceReferenceFactory> surface_reference_factory_;
+
DISALLOW_COPY_AND_ASSIGN(Surface);
};
diff --git a/chromium/components/exo/surface_unittest.cc b/chromium/components/exo/surface_unittest.cc
index 01bbff2a7cf..f48ea711054 100644
--- a/chromium/components/exo/surface_unittest.cc
+++ b/chromium/components/exo/surface_unittest.cc
@@ -4,7 +4,6 @@
#include "base/bind.h"
#include "cc/output/compositor_frame.h"
-#include "cc/output/delegated_frame_data.h"
#include "cc/quads/texture_draw_quad.h"
#include "cc/surfaces/surface.h"
#include "cc/surfaces/surface_manager.h"
@@ -51,6 +50,11 @@ 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
+ // call to finish so that the release callback finishes running before
+ // the assertion below.
+ RunAllPendingInMessageLoop();
ASSERT_EQ(1, release_buffer_call_count);
}
@@ -188,13 +192,13 @@ TEST_F(SurfaceTest, SetCrop) {
EXPECT_EQ(crop_size.ToString(), surface->content_size().ToString());
}
-const cc::DelegatedFrameData* GetFrameFromSurface(Surface* surface) {
+const cc::CompositorFrame& GetFrameFromSurface(Surface* surface) {
cc::SurfaceId surface_id = surface->GetSurfaceId();
cc::SurfaceManager* surface_manager =
- aura::Env::GetInstance()->context_factory()->GetSurfaceManager();
+ aura::Env::GetInstance()->context_factory_private()->GetSurfaceManager();
const cc::CompositorFrame& frame =
surface_manager->GetSurfaceForId(surface_id)->GetEligibleFrame();
- return frame.delegated_frame_data.get();
+ return frame;
}
TEST_F(SurfaceTest, SetBlendMode) {
@@ -204,13 +208,14 @@ TEST_F(SurfaceTest, SetBlendMode) {
std::unique_ptr<Surface> surface(new Surface);
surface->Attach(buffer.get());
- surface->SetBlendMode(SkXfermode::kSrc_Mode);
+ surface->SetBlendMode(SkBlendMode::kSrc);
surface->Commit();
+ RunAllPendingInMessageLoop();
- const cc::DelegatedFrameData* frame_data = GetFrameFromSurface(surface.get());
- ASSERT_EQ(1u, frame_data->render_pass_list.size());
- ASSERT_EQ(1u, frame_data->render_pass_list.back()->quad_list.size());
- EXPECT_FALSE(frame_data->render_pass_list.back()
+ const cc::CompositorFrame& frame = GetFrameFromSurface(surface.get());
+ ASSERT_EQ(1u, frame.render_pass_list.size());
+ ASSERT_EQ(1u, frame.render_pass_list.back()->quad_list.size());
+ EXPECT_FALSE(frame.render_pass_list.back()
->quad_list.back()
->ShouldDrawWithBlending());
}
@@ -223,12 +228,12 @@ TEST_F(SurfaceTest, OverlayCandidate) {
surface->Attach(buffer.get());
surface->Commit();
+ RunAllPendingInMessageLoop();
- const cc::DelegatedFrameData* frame_data = GetFrameFromSurface(surface.get());
- ASSERT_EQ(1u, frame_data->render_pass_list.size());
- ASSERT_EQ(1u, frame_data->render_pass_list.back()->quad_list.size());
- cc::DrawQuad* draw_quad =
- frame_data->render_pass_list.back()->quad_list.back();
+ const cc::CompositorFrame& frame = GetFrameFromSurface(surface.get());
+ ASSERT_EQ(1u, frame.render_pass_list.size());
+ ASSERT_EQ(1u, frame.render_pass_list.back()->quad_list.size());
+ cc::DrawQuad* draw_quad = frame.render_pass_list.back()->quad_list.back();
ASSERT_EQ(cc::DrawQuad::TEXTURE_CONTENT, draw_quad->material);
const cc::TextureDrawQuad* texture_quad =
diff --git a/chromium/components/exo/touch_unittest.cc b/chromium/components/exo/touch_unittest.cc
index 8ae056c8f18..18917fb7544 100644
--- a/chromium/components/exo/touch_unittest.cc
+++ b/chromium/components/exo/touch_unittest.cc
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ash/aura/wm_window_aura.h"
#include "ash/common/wm/window_positioner.h"
#include "ash/common/wm/window_positioning_utils.h"
#include "ash/common/wm_shell.h"
+#include "ash/common/wm_window.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "components/exo/buffer.h"
diff --git a/chromium/components/exo/wayland/BUILD.gn b/chromium/components/exo/wayland/BUILD.gn
index 0b4085d5aa7..231b7bc59c4 100644
--- a/chromium/components/exo/wayland/BUILD.gn
+++ b/chromium/components/exo/wayland/BUILD.gn
@@ -4,6 +4,7 @@
import("//build/config/linux/pkg_config.gni")
import("//build/config/ui.gni")
+import("//ui/base/ui_features.gni")
import("//ui/ozone/ozone.gni")
if (use_xkbcommon) {
@@ -34,21 +35,25 @@ source_set("wayland") {
"//base",
"//components/exo",
"//device/gamepad",
- "//ipc:ipc",
"//skia",
"//third_party/wayland:wayland_server",
"//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:presentation_time_protocol",
"//third_party/wayland-protocols:remote_shell_protocol",
"//third_party/wayland-protocols:secure_output_protocol",
"//third_party/wayland-protocols:stylus_protocol",
"//third_party/wayland-protocols:viewporter_protocol",
"//third_party/wayland-protocols:vsync_feedback_protocol",
"//third_party/wayland-protocols:xdg_shell_protocol",
- "//ui/aura:aura",
+ "//ui/aura",
+ "//ui/base",
+ "//ui/display/manager",
"//ui/events:dom_keycode_converter",
"//ui/events:events_base",
"//ui/views",
+ "//ui/wm:wm",
]
if (use_ozone) {
@@ -61,7 +66,6 @@ source_set("wayland") {
if (use_xkbcommon) {
configs += [ ":xkbcommon" ]
- defines += [ "USE_XKBCOMMON" ]
deps += [ "//ui/events/keycodes:xkb" ]
}
}
@@ -92,6 +96,7 @@ executable("wayland_motion_events") {
"//skia",
"//third_party/wayland:wayland_client",
"//third_party/wayland-protocols:linux_dmabuf_protocol",
+ "//third_party/wayland-protocols:presentation_time_protocol",
"//ui/gfx/geometry",
"//ui/gl",
"//ui/gl/init",
diff --git a/chromium/components/exo/wayland/DEPS b/chromium/components/exo/wayland/DEPS
index e29907d2835..067074d486a 100644
--- a/chromium/components/exo/wayland/DEPS
+++ b/chromium/components/exo/wayland/DEPS
@@ -1,4 +1,3 @@
include_rules = [
- "+ipc",
"+third_party/wayland/include",
]
diff --git a/chromium/components/exo/wayland/clients/motion_events.cc b/chromium/components/exo/wayland/clients/motion_events.cc
index 43bc6af6b3c..806e02a80b8 100644
--- a/chromium/components/exo/wayland/clients/motion_events.cc
+++ b/chromium/components/exo/wayland/clients/motion_events.cc
@@ -8,6 +8,7 @@
#include <fcntl.h>
#include <linux-dmabuf-unstable-v1-client-protocol.h>
+#include <presentation-time-client-protocol.h>
#include <wayland-client-core.h>
#include <wayland-client-protocol.h>
@@ -21,6 +22,7 @@
#include "base/command_line.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/memory/shared_memory.h"
#include "base/scoped_generic.h"
#include "base/strings/string_number_conversions.h"
@@ -69,6 +71,9 @@ DEFAULT_DELETER(wl_seat, wl_seat_destroy)
DEFAULT_DELETER(wl_pointer, wl_pointer_destroy)
DEFAULT_DELETER(wl_touch, wl_touch_destroy)
DEFAULT_DELETER(wl_callback, wl_callback_destroy)
+DEFAULT_DELETER(wp_presentation, wp_presentation_destroy)
+DEFAULT_DELETER(struct wp_presentation_feedback,
+ wp_presentation_feedback_destroy)
DEFAULT_DELETER(zwp_linux_buffer_params_v1, zwp_linux_buffer_params_v1_destroy)
DEFAULT_DELETER(zwp_linux_dmabuf_v1, zwp_linux_dmabuf_v1_destroy)
@@ -111,6 +116,7 @@ const int kBenchmarkWarmupFrames = 10;
struct Globals {
std::unique_ptr<wl_compositor> compositor;
std::unique_ptr<wl_shm> shm;
+ std::unique_ptr<wp_presentation> presentation;
std::unique_ptr<zwp_linux_dmabuf_v1> linux_dmabuf;
std::unique_ptr<wl_shell> shell;
std::unique_ptr<wl_seat> seat;
@@ -135,6 +141,9 @@ void RegistryHandler(void* data,
} else if (strcmp(interface, "wl_seat") == 0) {
globals->seat.reset(static_cast<wl_seat*>(
wl_registry_bind(registry, id, &wl_seat_interface, 5)));
+ } else if (strcmp(interface, "wp_presentation") == 0) {
+ globals->presentation.reset(static_cast<wp_presentation*>(
+ wl_registry_bind(registry, id, &wp_presentation_interface, 1)));
} else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
globals->linux_dmabuf.reset(static_cast<zwp_linux_dmabuf_v1*>(
wl_registry_bind(registry, id, &zwp_linux_dmabuf_v1_interface, 1)));
@@ -159,6 +168,14 @@ struct DeleteEglImageTraits {
}
};
using ScopedEglImage = base::ScopedGeneric<EGLImageKHR, DeleteEglImageTraits>;
+
+struct DeleteEglSyncTraits {
+ static EGLSyncKHR InvalidValue() { return 0; }
+ static void Free(EGLSyncKHR sync) {
+ eglDestroySyncKHR(eglGetCurrentDisplay(), sync);
+ }
+};
+using ScopedEglSync = base::ScopedGeneric<EGLSyncKHR, DeleteEglSyncTraits>;
#endif
struct Buffer {
@@ -167,6 +184,7 @@ struct Buffer {
#if defined(OZONE_PLATFORM_GBM)
std::unique_ptr<gbm_bo> bo;
std::unique_ptr<ScopedEglImage> egl_image;
+ std::unique_ptr<ScopedEglSync> egl_sync;
std::unique_ptr<ScopedTexture> texture;
#endif
std::unique_ptr<zwp_linux_buffer_params_v1> params;
@@ -262,17 +280,78 @@ void TouchFrame(void* data, wl_touch* touch) {}
void TouchCancel(void* data, wl_touch* touch) {}
-struct Frame {
+struct Schedule {
uint32_t time = 0;
bool callback_pending = false;
};
void FrameCallback(void* data, wl_callback* callback, uint32_t time) {
- Frame* frame = static_cast<Frame*>(data);
+ Schedule* schedule = static_cast<Schedule*>(data);
static uint32_t initial_time = time;
- frame->time = time - initial_time;
- frame->callback_pending = false;
+ schedule->time = time - initial_time;
+ schedule->callback_pending = false;
+}
+
+struct Frame {
+ Buffer* buffer = nullptr;
+ base::TimeDelta wall_time;
+ base::TimeDelta cpu_time;
+ std::vector<base::TimeTicks> event_times;
+ std::unique_ptr<struct wp_presentation_feedback> feedback;
+};
+
+struct Presentation {
+ std::deque<std::unique_ptr<Frame>> scheduled_frames;
+ base::TimeDelta wall_time;
+ base::TimeDelta cpu_time;
+ base::TimeDelta latency_time;
+ uint32_t num_frames_presented = 0;
+ uint32_t num_events_presented = 0;
+};
+
+void FeedbackSyncOutput(void* data,
+ struct wp_presentation_feedback* presentation_feedback,
+ wl_output* output) {}
+
+void FeedbackPresented(void* data,
+ struct wp_presentation_feedback* presentation_feedback,
+ uint32_t tv_sec_hi,
+ uint32_t tv_sec_lo,
+ uint32_t tv_nsec,
+ uint32_t refresh,
+ uint32_t seq_hi,
+ uint32_t seq_lo,
+ uint32_t flags) {
+ Presentation* presentation = static_cast<Presentation*>(data);
+ DCHECK_GT(presentation->scheduled_frames.size(), 0u);
+ std::unique_ptr<Frame> frame =
+ std::move(presentation->scheduled_frames.front());
+ presentation->scheduled_frames.pop_front();
+
+ presentation->wall_time += frame->wall_time;
+ presentation->cpu_time += frame->cpu_time;
+ ++presentation->num_frames_presented;
+
+ int64_t seconds = (static_cast<int64_t>(tv_sec_hi) << 32) + tv_sec_lo;
+ int64_t microseconds = seconds * base::Time::kMicrosecondsPerSecond +
+ tv_nsec / base::Time::kNanosecondsPerMicrosecond;
+ base::TimeTicks presentation_time =
+ base::TimeTicks::FromInternalValue(microseconds);
+ for (const auto& event_time : frame->event_times) {
+ presentation->latency_time += presentation_time - event_time;
+ ++presentation->num_events_presented;
+ }
+}
+
+void FeedbackDiscarded(void* data,
+ struct wp_presentation_feedback* presentation_feedback) {
+ Presentation* presentation = static_cast<Presentation*>(data);
+ DCHECK_GT(presentation->scheduled_frames.size(), 0u);
+ std::unique_ptr<Frame> frame =
+ std::move(presentation->scheduled_frames.front());
+ presentation->scheduled_frames.pop_front();
+ LOG(WARNING) << "Frame discarded";
}
#if defined(OZONE_PLATFORM_GBM)
@@ -336,8 +415,8 @@ class MotionEvents {
const base::TimeDelta benchmark_interval_;
const std::string* use_drm_;
- Globals globals_;
std::unique_ptr<wl_display> display_;
+ Globals globals_;
#if defined(OZONE_PLATFORM_GBM)
base::ScopedFD drm_fd_;
@@ -361,16 +440,6 @@ int MotionEvents::Run() {
return 1;
}
- bool gl_initialized = gl::init::InitializeGLOneOff();
- DCHECK(gl_initialized);
- gl_surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
- gl_context_ =
- gl::init::CreateGLContext(nullptr, // share_group
- gl_surface_.get(), gl::GLContextAttribs());
-
- make_current_.reset(
- new ui::ScopedMakeCurrent(gl_context_.get(), gl_surface_.get()));
-
wl_registry_listener registry_listener = {RegistryHandler, RegistryRemover};
wl_registry* registry = wl_display_get_registry(display_.get());
@@ -386,6 +455,10 @@ int MotionEvents::Run() {
LOG(ERROR) << "Can't find shm interface";
return 1;
}
+ if (!globals_.presentation) {
+ LOG(ERROR) << "Can't find presentation interface";
+ return 1;
+ }
if (use_drm_ && !globals_.linux_dmabuf) {
LOG(ERROR) << "Can't find linux_dmabuf interface";
return 1;
@@ -399,8 +472,9 @@ int MotionEvents::Run() {
return 1;
}
- EGLenum egl_sync_type = 0;
#if defined(OZONE_PLATFORM_GBM)
+ EGLenum egl_sync_type = 0;
+ sk_sp<const GrGLInterface> native_interface;
if (use_drm_) {
// Number of files to look for when discovering DRM devices.
const uint32_t kDrmMaxMinor = 15;
@@ -436,21 +510,31 @@ int MotionEvents::Run() {
LOG(ERROR) << "Can't create gbm device";
return 1;
}
- }
- sk_sp<const GrGLInterface> native_interface(GrGLCreateNativeInterface());
- DCHECK(native_interface);
- gr_context_ = sk_sp<GrContext>(GrContext::Create(
- kOpenGL_GrBackend,
- reinterpret_cast<GrBackendContext>(native_interface.get())));
- DCHECK(gr_context_);
+ bool gl_initialized = gl::init::InitializeGLOneOff();
+ DCHECK(gl_initialized);
+ gl_surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
+ gl_context_ =
+ gl::init::CreateGLContext(nullptr, // share_group
+ gl_surface_.get(), gl::GLContextAttribs());
- if (gl::GLSurfaceEGL::HasEGLExtension("EGL_EXT_image_flush_external") ||
- gl::GLSurfaceEGL::HasEGLExtension("EGL_ARM_implicit_external_sync")) {
- egl_sync_type = EGL_SYNC_FENCE_KHR;
- }
- if (gl::GLSurfaceEGL::HasEGLExtension("EGL_ANDROID_native_fence_sync")) {
- egl_sync_type = EGL_SYNC_NATIVE_FENCE_ANDROID;
+ make_current_.reset(
+ new ui::ScopedMakeCurrent(gl_context_.get(), gl_surface_.get()));
+
+ if (gl::GLSurfaceEGL::HasEGLExtension("EGL_EXT_image_flush_external") ||
+ gl::GLSurfaceEGL::HasEGLExtension("EGL_ARM_implicit_external_sync")) {
+ egl_sync_type = EGL_SYNC_FENCE_KHR;
+ }
+ if (gl::GLSurfaceEGL::HasEGLExtension("EGL_ANDROID_native_fence_sync")) {
+ egl_sync_type = EGL_SYNC_NATIVE_FENCE_ANDROID;
+ }
+
+ native_interface = sk_sp<const GrGLInterface>(GrGLCreateNativeInterface());
+ DCHECK(native_interface);
+ gr_context_ = sk_sp<GrContext>(GrContext::Create(
+ kOpenGL_GrBackend,
+ reinterpret_cast<GrBackendContext>(native_interface.get())));
+ DCHECK(gr_context_);
}
#endif
@@ -530,18 +614,20 @@ int MotionEvents::Run() {
TouchFrame, TouchCancel};
wl_touch_add_listener(touch.get(), &touch_listener, &event_times);
- Frame frame;
+ Schedule schedule;
std::unique_ptr<wl_callback> frame_callback;
wl_callback_listener frame_listener = {FrameCallback};
- std::deque<wl_buffer*> pending_frames;
- uint32_t frames = 0;
+ Presentation presentation;
+ std::deque<std::unique_ptr<Frame>> pending_frames;
+
size_t num_benchmark_runs_left = num_benchmark_runs_;
- base::TimeTicks benchmark_time;
- base::TimeDelta benchmark_wall_time;
- base::TimeDelta benchmark_cpu_time;
+ base::TimeTicks benchmark_start_time;
std::string fps_counter_text("??");
+ wp_presentation_feedback_listener feedback_listener = {
+ FeedbackSyncOutput, FeedbackPresented, FeedbackDiscarded};
+
SkPaint text_paint;
text_paint.setTextSize(32.0f);
text_paint.setColor(SK_ColorWHITE);
@@ -549,7 +635,7 @@ int MotionEvents::Run() {
int dispatch_status = 0;
do {
- bool enqueue_frame = frame.callback_pending
+ bool enqueue_frame = schedule.callback_pending
? pending_frames.size() < max_frames_pending_
: pending_frames.empty();
if (enqueue_frame) {
@@ -561,22 +647,28 @@ int MotionEvents::Run() {
return 1;
}
+ auto frame = base::MakeUnique<Frame>();
+ frame->buffer = buffer;
+
base::TimeTicks wall_time_start;
base::ThreadTicks cpu_time_start;
if (num_benchmark_runs_ || show_fps_counter_) {
wall_time_start = base::TimeTicks::Now();
- if (frames <= kBenchmarkWarmupFrames)
- benchmark_time = wall_time_start;
+ if (presentation.num_frames_presented <= kBenchmarkWarmupFrames)
+ benchmark_start_time = wall_time_start;
- if ((wall_time_start - benchmark_time) > benchmark_interval_) {
- uint32_t benchmark_frames = frames - kBenchmarkWarmupFrames;
+ base::TimeDelta benchmark_time = wall_time_start - benchmark_start_time;
+ if (benchmark_time > benchmark_interval_) {
+ uint32_t benchmark_frames =
+ presentation.num_frames_presented - kBenchmarkWarmupFrames;
if (num_benchmark_runs_left) {
- // Print benchmark statistics for the frames produced and exit.
- // Note: frames produced is not necessarily the same as frames
- // displayed.
+ // Print benchmark statistics for the frames presented and exit.
std::cout << benchmark_frames << '\t'
- << benchmark_wall_time.InMilliseconds() << '\t'
- << benchmark_cpu_time.InMilliseconds() << '\t'
+ << benchmark_time.InMilliseconds() << '\t'
+ << presentation.wall_time.InMilliseconds() << '\t'
+ << presentation.cpu_time.InMilliseconds() << '\t'
+ << presentation.num_events_presented << '\t'
+ << presentation.latency_time.InMilliseconds() << '\t'
<< std::endl;
if (!--num_benchmark_runs_left)
return 0;
@@ -586,10 +678,12 @@ int MotionEvents::Run() {
fps_counter_text = base::UintToString(
std::round(benchmark_frames / benchmark_interval_.InSecondsF()));
- frames = kBenchmarkWarmupFrames;
- benchmark_time = wall_time_start;
- benchmark_wall_time = base::TimeDelta();
- benchmark_cpu_time = base::TimeDelta();
+ benchmark_start_time = wall_time_start;
+ presentation.wall_time = base::TimeDelta();
+ presentation.cpu_time = base::TimeDelta();
+ presentation.latency_time = base::TimeDelta();
+ presentation.num_frames_presented = kBenchmarkWarmupFrames;
+ presentation.num_events_presented = 0;
}
cpu_time_start = base::ThreadTicks::Now();
@@ -613,6 +707,8 @@ int MotionEvents::Run() {
canvas->drawIRect(rect, paint);
std::string text = base::UintToString(event_times.back());
canvas->drawText(text.c_str(), text.length(), 8, y + 32, text_paint);
+ frame->event_times.push_back(base::TimeTicks::FromInternalValue(
+ event_times.back() * base::Time::kMicrosecondsPerMillisecond));
event_times.pop_back();
y += h;
}
@@ -624,7 +720,7 @@ int MotionEvents::Run() {
SkIRect rect = SkIRect::MakeXYWH(-SkScalarHalf(half_width),
-SkScalarHalf(half_height), half_width,
half_height);
- SkScalar rotation = SkScalarMulDiv(frame.time, kRotationSpeed, 1000);
+ SkScalar rotation = SkScalarMulDiv(schedule.time, kRotationSpeed, 1000);
canvas->save();
canvas->translate(half_width, half_height);
for (size_t i = 0; i < num_rects_; ++i) {
@@ -646,39 +742,56 @@ int MotionEvents::Run() {
if (gr_context_) {
gr_context_->flush();
- glFlush();
+#if defined(OZONE_PLATFORM_GBM)
if (egl_sync_type) {
- EGLSyncKHR sync =
- eglCreateSyncKHR(eglGetCurrentDisplay(), egl_sync_type, nullptr);
- DCHECK(sync != EGL_NO_SYNC_KHR);
- eglClientWaitSyncKHR(eglGetCurrentDisplay(), sync,
- EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
- EGL_FOREVER_KHR);
- eglDestroySyncKHR(eglGetCurrentDisplay(), sync);
+ buffer->egl_sync.reset(new ScopedEglSync(eglCreateSyncKHR(
+ eglGetCurrentDisplay(), egl_sync_type, nullptr)));
+ DCHECK(buffer->egl_sync->is_valid());
}
+#endif
+
+ glFlush();
}
buffer->busy = true;
- pending_frames.push_back(buffer->buffer.get());
- if (num_benchmark_runs_ || show_fps_counter_) {
- ++frames;
- benchmark_wall_time += base::TimeTicks::Now() - wall_time_start;
- benchmark_cpu_time += base::ThreadTicks::Now() - cpu_time_start;
+ if (num_benchmark_runs_) {
+ frame->wall_time = base::TimeTicks::Now() - wall_time_start;
+ frame->cpu_time = base::ThreadTicks::Now() - cpu_time_start;
}
+ pending_frames.push_back(std::move(frame));
continue;
}
- if (!frame.callback_pending) {
+ if (!schedule.callback_pending) {
DCHECK_GT(pending_frames.size(), 0u);
- wl_surface_set_buffer_scale(surface.get(), scale_);
- wl_surface_attach(surface.get(), pending_frames.front(), 0, 0);
+ std::unique_ptr<Frame> frame = std::move(pending_frames.front());
pending_frames.pop_front();
+ wl_surface_set_buffer_scale(surface.get(), scale_);
+ wl_surface_damage(surface.get(), 0, 0, width_ / scale_, height_ / scale_);
+ wl_surface_attach(surface.get(), frame->buffer->buffer.get(), 0, 0);
+
+#if defined(OZONE_PLATFORM_GBM)
+ if (frame->buffer->egl_sync) {
+ eglClientWaitSyncKHR(eglGetCurrentDisplay(),
+ frame->buffer->egl_sync->get(),
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR);
+ }
+#endif
+
frame_callback.reset(wl_surface_frame(surface.get()));
- wl_callback_add_listener(frame_callback.get(), &frame_listener, &frame);
- frame.callback_pending = true;
+ wl_callback_add_listener(frame_callback.get(), &frame_listener,
+ &schedule);
+ schedule.callback_pending = true;
+
+ frame->feedback.reset(
+ wp_presentation_feedback(globals_.presentation.get(), surface.get()));
+ wp_presentation_feedback_add_listener(frame->feedback.get(),
+ &feedback_listener, &presentation);
+ presentation.scheduled_frames.push_back(std::move(frame));
+
wl_surface_commit(surface.get());
wl_display_flush(display_.get());
continue;
@@ -869,9 +982,9 @@ int main(int argc, char* argv[]) {
size_t benchmark_interval_ms = 5000; // 5 seconds.
if (command_line->HasSwitch(switches::kBenchmarkInterval) &&
(!base::StringToSizeT(
- command_line->GetSwitchValueASCII(switches::kBenchmark),
+ command_line->GetSwitchValueASCII(switches::kBenchmarkInterval),
&benchmark_interval_ms))) {
- LOG(ERROR) << "Invalid value for " << switches::kBenchmark;
+ LOG(ERROR) << "Invalid value for " << switches::kBenchmarkInterval;
return 1;
}
diff --git a/chromium/components/exo/wayland/server.cc b/chromium/components/exo/wayland/server.cc
index b6ef4b4e11f..52ea9431c80 100644
--- a/chromium/components/exo/wayland/server.cc
+++ b/chromium/components/exo/wayland/server.cc
@@ -4,23 +4,23 @@
#include "components/exo/wayland/server.h"
+#include <alpha-compositing-unstable-v1-server-protocol.h>
+#include <gaming-input-unstable-v1-server-protocol.h>
#include <grp.h>
+#include <keyboard-configuration-unstable-v1-server-protocol.h>
#include <linux/input.h>
+#include <presentation-time-server-protocol.h>
+#include <remote-shell-unstable-v1-server-protocol.h>
+#include <secure-output-unstable-v1-server-protocol.h>
#include <stddef.h>
#include <stdint.h>
+#include <stylus-unstable-v1-server-protocol.h>
+#include <stylus-unstable-v2-server-protocol.h>
#include <viewporter-server-protocol.h>
+#include <vsync-feedback-unstable-v1-server-protocol.h>
#include <wayland-server-core.h>
#include <wayland-server-protocol-core.h>
-
-// Note: core wayland headers need to be included before protocol headers.
-#include <alpha-compositing-unstable-v1-server-protocol.h> // NOLINT
-#include <gaming-input-unstable-v1-server-protocol.h> // NOLINT
-#include <remote-shell-unstable-v1-server-protocol.h> // NOLINT
-#include <secure-output-unstable-v1-server-protocol.h> // NOLINT
-#include <stylus-unstable-v1-server-protocol.h> // NOLINT
-#include <stylus-unstable-v2-server-protocol.h> // NOLINT
-#include <vsync-feedback-unstable-v1-server-protocol.h> // NOLINT
-#include <xdg-shell-unstable-v5-server-protocol.h> // NOLINT
+#include <xdg-shell-unstable-v5-server-protocol.h>
#include <algorithm>
#include <cstdlib>
@@ -48,6 +48,7 @@
#include "components/exo/gamepad_delegate.h"
#include "components/exo/keyboard.h"
#include "components/exo/keyboard_delegate.h"
+#include "components/exo/keyboard_device_configuration_delegate.h"
#include "components/exo/notification_surface.h"
#include "components/exo/notification_surface_manager.h"
#include "components/exo/pointer.h"
@@ -61,10 +62,10 @@
#include "components/exo/touch_delegate.h"
#include "components/exo/touch_stylus_delegate.h"
#include "components/exo/wm_helper.h"
-#include "ipc/unix_domain_socket_util.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/aura/window_property.h"
#include "ui/base/hit_test.h"
+#include "ui/base/ui_features.h"
#include "ui/compositor/compositor_vsync_manager.h"
#include "ui/display/display_observer.h"
#include "ui/display/manager/managed_display_info.h"
@@ -74,6 +75,7 @@
#include "ui/gfx/buffer_types.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
+#include "ui/wm/core/coordinate_conversion.h"
#if defined(USE_OZONE)
#include <drm_fourcc.h>
@@ -81,7 +83,7 @@
#include <wayland-drm-server-protocol.h>
#endif
-#if defined(USE_XKBCOMMON)
+#if BUILDFLAG(USE_XKBCOMMON)
#include <xkbcommon/xkbcommon.h>
#include "ui/events/keycodes/scoped_xkb.h" // nogncheck
#endif
@@ -220,10 +222,10 @@ void surface_frame(wl_client* client,
wl_resource_create(client, &wl_callback_interface, 1, callback);
// base::Unretained is safe as the resource owns the callback.
- std::unique_ptr<base::CancelableCallback<void(base::TimeTicks)>>
- cancelable_callback(
- new base::CancelableCallback<void(base::TimeTicks)>(base::Bind(
- &HandleSurfaceFrameCallback, base::Unretained(callback_resource))));
+ auto cancelable_callback =
+ base::MakeUnique<base::CancelableCallback<void(base::TimeTicks)>>(
+ base::Bind(&HandleSurfaceFrameCallback,
+ base::Unretained(callback_resource)));
GetUserDataAs<Surface>(resource)
->RequestFrameCallback(cancelable_callback->callback());
@@ -596,6 +598,7 @@ const struct dmabuf_supported_format {
{DRM_FORMAT_ABGR8888, gfx::BufferFormat::RGBA_8888},
{DRM_FORMAT_XRGB8888, gfx::BufferFormat::BGRX_8888},
{DRM_FORMAT_ARGB8888, gfx::BufferFormat::BGRA_8888},
+ {DRM_FORMAT_NV12, gfx::BufferFormat::YUV_420_BIPLANAR},
{DRM_FORMAT_YVU420, gfx::BufferFormat::YVU_420}};
struct LinuxBufferParams {
@@ -696,8 +699,7 @@ void linux_buffer_params_create(wl_client* client,
}
LinuxBufferParams::Plane& plane = plane_it->second;
planes.emplace_back(plane.stride, plane.offset, 0, 0);
- if (plane.fd.is_valid())
- fds.push_back(std::move(plane.fd));
+ fds.push_back(std::move(plane.fd));
}
std::unique_ptr<Buffer> buffer =
@@ -850,37 +852,6 @@ void bind_subcompositor(wl_client* client,
////////////////////////////////////////////////////////////////////////////////
// wl_shell_surface_interface:
-class ShellSurfaceSizeObserver : public views::WidgetObserver {
- public:
- ShellSurfaceSizeObserver(wl_resource* shell_surface_resource,
- const gfx::Size& initial_size)
- : shell_surface_resource_(shell_surface_resource),
- old_size_(initial_size) {
- wl_shell_surface_send_configure(shell_surface_resource,
- WL_SHELL_SURFACE_RESIZE_NONE,
- old_size_.width(), old_size_.height());
- }
-
- // Overridden from view::WidgetObserver:
- void OnWidgetDestroyed(views::Widget* widget) override { delete this; }
- void OnWidgetBoundsChanged(views::Widget* widget,
- const gfx::Rect& new_bounds) override {
- if (old_size_ == new_bounds.size())
- return;
-
- wl_shell_surface_send_configure(shell_surface_resource_,
- WL_SHELL_SURFACE_RESIZE_NONE,
- new_bounds.width(), new_bounds.height());
- old_size_ = new_bounds.size();
- }
-
- private:
- wl_resource* shell_surface_resource_;
- gfx::Size old_size_;
-
- DISALLOW_COPY_AND_ASSIGN(ShellSurfaceSizeObserver);
-};
-
void shell_surface_pong(wl_client* client,
wl_resource* resource,
uint32_t serial) {
@@ -903,7 +874,13 @@ void shell_surface_resize(wl_client* client,
}
void shell_surface_set_toplevel(wl_client* client, wl_resource* resource) {
- GetUserDataAs<ShellSurface>(resource)->SetEnabled(true);
+ ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource);
+ if (shell_surface->enabled())
+ return;
+
+ shell_surface->SetFrame(true);
+ shell_surface->SetRectangularShadowEnabled(true);
+ shell_surface->SetEnabled(true);
}
void shell_surface_set_transient(wl_client* client,
@@ -912,7 +889,50 @@ void shell_surface_set_transient(wl_client* client,
int x,
int y,
uint32_t flags) {
- NOTIMPLEMENTED();
+ ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource);
+ if (shell_surface->enabled())
+ return;
+
+ // Parent widget can be found by locating the closest ancestor with a widget.
+ views::Widget* parent_widget = nullptr;
+ aura::Window* parent_window =
+ GetUserDataAs<Surface>(parent_resource)->window();
+ while (parent_window) {
+ parent_widget = views::Widget::GetWidgetForNativeWindow(parent_window);
+ if (parent_widget)
+ break;
+ parent_window = parent_window->parent();
+ }
+
+ DLOG_IF(WARNING, parent_resource && !!parent_widget)
+ << "Parent surface is not a visible shell surface";
+
+ gfx::Point origin(x, y);
+ ShellSurface* parent_shell_surface = nullptr;
+
+ // Set parent if found and it is associated with a shell surface.
+ if (parent_widget &&
+ ShellSurface::GetMainSurface(parent_widget->GetNativeWindow())) {
+ wm::ConvertPointToScreen(
+ ShellSurface::GetMainSurface(parent_widget->GetNativeWindow())
+ ->window(),
+ &origin);
+ // Shell surface widget delegate implementation of GetContentsView()
+ // returns a pointer to the shell surface instance.
+ parent_shell_surface = static_cast<ShellSurface*>(
+ parent_widget->widget_delegate()->GetContentsView());
+ }
+
+ if (flags & WL_SHELL_SURFACE_TRANSIENT_INACTIVE) {
+ shell_surface->SetOrigin(origin);
+ shell_surface->SetContainer(ash::kShellWindowId_SystemModalContainer);
+ shell_surface->SetActivatable(false);
+ } else {
+ shell_surface->SetFrame(true);
+ shell_surface->SetParent(parent_shell_surface);
+ }
+ shell_surface->SetRectangularShadowEnabled(true);
+ shell_surface->SetEnabled(true);
}
void shell_surface_set_fullscreen(wl_client* client,
@@ -926,10 +946,6 @@ void shell_surface_set_fullscreen(wl_client* client,
shell_surface->SetEnabled(true);
shell_surface->SetFullscreen(true);
-
- views::Widget* widget = shell_surface->GetWidget();
- widget->AddObserver(new ShellSurfaceSizeObserver(
- resource, widget->GetWindowBoundsInScreen().size()));
}
void shell_surface_set_popup(wl_client* client,
@@ -952,10 +968,6 @@ void shell_surface_set_maximized(wl_client* client,
shell_surface->SetEnabled(true);
shell_surface->Maximize();
-
- views::Widget* widget = shell_surface->GetWidget();
- widget->AddObserver(new ShellSurfaceSizeObserver(
- resource, widget->GetWindowBoundsInScreen().size()));
}
void shell_surface_set_title(wl_client* client,
@@ -981,6 +993,27 @@ const struct wl_shell_surface_interface shell_surface_implementation = {
////////////////////////////////////////////////////////////////////////////////
// wl_shell_interface:
+void HandleShellSurfaceCloseCallback(wl_resource* resource) {
+ // Shell surface interface doesn't have a close event. Just send a ping event
+ // for now.
+ uint32_t serial = wl_display_next_serial(
+ wl_client_get_display(wl_resource_get_client(resource)));
+ wl_shell_surface_send_ping(resource, serial);
+ wl_client_flush(wl_resource_get_client(resource));
+}
+
+uint32_t HandleShellSurfaceConfigureCallback(
+ wl_resource* resource,
+ const gfx::Size& size,
+ ash::wm::WindowStateType state_type,
+ bool resizing,
+ bool activated) {
+ wl_shell_surface_send_configure(resource, WL_SHELL_SURFACE_RESIZE_NONE,
+ size.width(), size.height());
+ wl_client_flush(wl_resource_get_client(resource));
+ return 0;
+}
+
void shell_get_shell_surface(wl_client* client,
wl_resource* resource,
uint32_t id,
@@ -1001,6 +1034,14 @@ void shell_get_shell_surface(wl_client* client,
// before they are enabled and can become visible.
shell_surface->SetEnabled(false);
+ shell_surface->set_close_callback(
+ base::Bind(&HandleShellSurfaceCloseCallback,
+ base::Unretained(shell_surface_resource)));
+
+ shell_surface->set_configure_callback(
+ base::Bind(&HandleShellSurfaceConfigureCallback,
+ base::Unretained(shell_surface_resource)));
+
shell_surface->set_surface_destroyed_callback(base::Bind(
&wl_resource_destroy, base::Unretained(shell_surface_resource)));
@@ -1460,14 +1501,15 @@ void remote_surface_set_scale(wl_client* client,
GetUserDataAs<ShellSurface>(resource)->SetScale(wl_fixed_to_double(scale));
}
-void remote_surface_set_rectangular_shadow(wl_client* client,
- wl_resource* resource,
- int32_t x,
- int32_t y,
- int32_t width,
- int32_t height) {
- GetUserDataAs<ShellSurface>(resource)->SetRectangularShadow(
- gfx::Rect(x, y, width, height));
+void remote_surface_set_rectangular_shadow_DEPRECATED(wl_client* client,
+ wl_resource* resource,
+ int32_t x,
+ int32_t y,
+ int32_t width,
+ int32_t height) {
+ ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource);
+ gfx::Rect content_bounds(x, y, width, height);
+ shell_surface->SetRectangularShadow_DEPRECATED(content_bounds);
}
void remote_surface_set_rectangular_shadow_background_opacity(
@@ -1536,12 +1578,37 @@ void remote_surface_unset_system_modal(wl_client* client,
GetUserDataAs<ShellSurface>(resource)->SetSystemModal(false);
}
+void remote_surface_set_rectangular_surface_shadow(wl_client* client,
+ wl_resource* resource,
+ int32_t x,
+ int32_t y,
+ int32_t width,
+ int32_t height) {
+ ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(resource);
+ gfx::Rect content_bounds(x, y, width, height);
+ shell_surface->SetRectangularSurfaceShadow(content_bounds);
+}
+
+void remote_surface_ack_configure(wl_client* client,
+ wl_resource* resource,
+ uint32_t serial) {
+ NOTIMPLEMENTED();
+}
+
+void remote_surface_set_moving(wl_client* client, wl_resource* resource) {
+ NOTIMPLEMENTED();
+}
+
+void remote_surface_unset_moving(wl_client* client, wl_resource* resource) {
+ NOTIMPLEMENTED();
+}
+
const struct zcr_remote_surface_v1_interface remote_surface_implementation = {
remote_surface_destroy,
remote_surface_set_app_id,
remote_surface_set_window_geometry,
remote_surface_set_scale,
- remote_surface_set_rectangular_shadow,
+ remote_surface_set_rectangular_shadow_DEPRECATED,
remote_surface_set_rectangular_shadow_background_opacity,
remote_surface_set_title,
remote_surface_set_top_inset,
@@ -1554,7 +1621,11 @@ const struct zcr_remote_surface_v1_interface remote_surface_implementation = {
remote_surface_pin,
remote_surface_unpin,
remote_surface_set_system_modal,
- remote_surface_unset_system_modal};
+ remote_surface_unset_system_modal,
+ remote_surface_set_rectangular_surface_shadow,
+ remote_surface_ack_configure,
+ remote_surface_set_moving,
+ remote_surface_unset_moving};
////////////////////////////////////////////////////////////////////////////////
// notification_surface_interface:
@@ -1841,7 +1912,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 = 1;
+const uint32_t remote_shell_version = 2;
void bind_remote_shell(wl_client* client,
void* data,
@@ -2173,7 +2244,7 @@ void pointer_release(wl_client* client, wl_resource* resource) {
const struct wl_pointer_interface pointer_implementation = {pointer_set_cursor,
pointer_release};
-#if defined(USE_XKBCOMMON)
+#if BUILDFLAG(USE_XKBCOMMON)
////////////////////////////////////////////////////////////////////////////////
// wl_keyboard_interface:
@@ -2418,7 +2489,7 @@ void seat_get_pointer(wl_client* client, wl_resource* resource, uint32_t id) {
}
void seat_get_keyboard(wl_client* client, wl_resource* resource, uint32_t id) {
-#if defined(USE_XKBCOMMON)
+#if BUILDFLAG(USE_XKBCOMMON)
uint32_t version = wl_resource_get_version(resource);
wl_resource* keyboard_resource =
wl_resource_create(client, &wl_keyboard_interface, version, id);
@@ -2463,7 +2534,7 @@ void bind_seat(wl_client* client, void* data, uint32_t version, uint32_t id) {
wl_seat_send_name(resource, "default");
uint32_t capabilities = WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_TOUCH;
-#if defined(USE_XKBCOMMON)
+#if BUILDFLAG(USE_XKBCOMMON)
capabilities |= WL_SEAT_CAPABILITY_KEYBOARD;
#endif
wl_seat_send_capabilities(resource, capabilities);
@@ -2604,6 +2675,71 @@ void bind_viewporter(wl_client* client,
}
////////////////////////////////////////////////////////////////////////////////
+// presentation_interface:
+
+void HandleSurfacePresentationCallback(wl_resource* resource,
+ base::TimeTicks presentation_time,
+ base::TimeDelta refresh) {
+ if (presentation_time.is_null()) {
+ wp_presentation_feedback_send_discarded(resource);
+ } else {
+ int64_t presentation_time_us = presentation_time.ToInternalValue();
+ int64_t seconds = presentation_time_us / base::Time::kMicrosecondsPerSecond;
+ int64_t microseconds =
+ presentation_time_us % base::Time::kMicrosecondsPerSecond;
+ wp_presentation_feedback_send_presented(
+ resource, seconds >> 32, seconds & 0xffffffff,
+ microseconds * base::Time::kNanosecondsPerMicrosecond,
+ refresh.InMicroseconds() * base::Time::kNanosecondsPerMicrosecond, 0, 0,
+ WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION);
+ }
+ wl_client_flush(wl_resource_get_client(resource));
+}
+
+void presentation_destroy(wl_client* client, wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+void presentation_feedback(wl_client* client,
+ wl_resource* resource,
+ wl_resource* surface_resource,
+ uint32_t id) {
+ wl_resource* presentation_feedback_resource =
+ wl_resource_create(client, &wp_presentation_feedback_interface,
+ wl_resource_get_version(resource), id);
+
+ // base::Unretained is safe as the resource owns the callback.
+ auto cancelable_callback = base::MakeUnique<
+ base::CancelableCallback<void(base::TimeTicks, base::TimeDelta)>>(
+ base::Bind(&HandleSurfacePresentationCallback,
+ base::Unretained(presentation_feedback_resource)));
+
+ GetUserDataAs<Surface>(surface_resource)
+ ->RequestPresentationCallback(cancelable_callback->callback());
+
+ SetImplementation(presentation_feedback_resource, nullptr,
+ std::move(cancelable_callback));
+}
+
+const struct wp_presentation_interface presentation_implementation = {
+ presentation_destroy, presentation_feedback};
+
+void bind_presentation(wl_client* client,
+ void* data,
+ uint32_t version,
+ uint32_t id) {
+ wl_resource* resource =
+ wl_resource_create(client, &wp_presentation_interface, 1, id);
+
+ wl_resource_set_implementation(resource, &presentation_implementation, data,
+ nullptr);
+
+ wp_presentation_send_clock_id(resource, CLOCK_MONOTONIC);
+}
+
+////////////////////////////////////////////////////////////////////////////////
// security_interface:
// Implements the security interface to a Surface. The "only visible on secure
@@ -2708,13 +2844,13 @@ class Blending : public SurfaceObserver {
~Blending() override {
if (surface_) {
surface_->RemoveSurfaceObserver(this);
- surface_->SetBlendMode(SkXfermode::kSrcOver_Mode);
+ surface_->SetBlendMode(SkBlendMode::kSrcOver);
surface_->SetAlpha(1.0f);
surface_->SetProperty(kSurfaceHasBlendingKey, false);
}
}
- void SetBlendMode(SkXfermode::Mode blend_mode) {
+ void SetBlendMode(SkBlendMode blend_mode) {
if (surface_)
surface_->SetBlendMode(blend_mode);
}
@@ -2745,11 +2881,10 @@ void blending_set_blending(wl_client* client,
uint32_t equation) {
switch (equation) {
case ZCR_BLENDING_V1_BLENDING_EQUATION_NONE:
- GetUserDataAs<Blending>(resource)->SetBlendMode(SkXfermode::kSrc_Mode);
+ GetUserDataAs<Blending>(resource)->SetBlendMode(SkBlendMode::kSrc);
break;
case ZCR_BLENDING_V1_BLENDING_EQUATION_PREMULT:
- GetUserDataAs<Blending>(resource)->SetBlendMode(
- SkXfermode::kSrcOver_Mode);
+ GetUserDataAs<Blending>(resource)->SetBlendMode(SkBlendMode::kSrcOver);
break;
case ZCR_BLENDING_V1_BLENDING_EQUATION_COVERAGE:
NOTIMPLEMENTED();
@@ -3028,6 +3163,91 @@ void bind_stylus_v1_DEPRECATED(wl_client* client,
data, nullptr);
}
+////////////////////////////////////////////////////////////////////////////////
+// keyboard_device_configuration interface:
+
+class WaylandKeyboardDeviceConfigurationDelegate
+ : public KeyboardDeviceConfigurationDelegate {
+ public:
+ WaylandKeyboardDeviceConfigurationDelegate(wl_resource* resource,
+ Keyboard* keyboard)
+ : resource_(resource), keyboard_(keyboard) {
+ keyboard_->SetDeviceConfigurationDelegate(this);
+ }
+ ~WaylandKeyboardDeviceConfigurationDelegate() override {
+ if (keyboard_)
+ keyboard_->SetDeviceConfigurationDelegate(nullptr);
+ }
+
+ void OnKeyboardDestroying(Keyboard* keyboard) override {
+ keyboard_ = nullptr;
+ }
+ void OnKeyboardTypeChanged(bool is_physical) override {
+ zcr_keyboard_device_configuration_v1_send_type_change(
+ resource_,
+ is_physical
+ ? ZCR_KEYBOARD_DEVICE_CONFIGURATION_V1_KEYBOARD_TYPE_PHYSICAL
+ : ZCR_KEYBOARD_DEVICE_CONFIGURATION_V1_KEYBOARD_TYPE_VIRTUAL);
+ }
+
+ private:
+ wl_resource* resource_;
+ Keyboard* keyboard_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaylandKeyboardDeviceConfigurationDelegate);
+};
+
+void keyboard_device_configuration_destroy(wl_client* client,
+ wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+const struct zcr_keyboard_device_configuration_v1_interface
+ keyboard_device_configuration_implementation = {
+ keyboard_device_configuration_destroy};
+
+////////////////////////////////////////////////////////////////////////////////
+// keyboard_configuration interface:
+
+void keyboard_configuration_get_keyboard_device_configuration(
+ wl_client* client,
+ wl_resource* resource,
+ uint32_t id,
+ wl_resource* keyboard_resource) {
+ Keyboard* keyboard = GetUserDataAs<Keyboard>(keyboard_resource);
+ if (keyboard->HasDeviceConfigurationDelegate()) {
+ wl_resource_post_error(
+ resource,
+ ZCR_KEYBOARD_CONFIGURATION_V1_ERROR_DEVICE_CONFIGURATION_EXISTS,
+ "keyboard has already been associated with a device configuration "
+ "object");
+ return;
+ }
+
+ wl_resource* keyboard_device_configuration_resource = wl_resource_create(
+ client, &zcr_keyboard_device_configuration_v1_interface, 1, id);
+
+ SetImplementation(
+ keyboard_device_configuration_resource,
+ &keyboard_device_configuration_implementation,
+ base::MakeUnique<WaylandKeyboardDeviceConfigurationDelegate>(
+ keyboard_device_configuration_resource, keyboard));
+}
+
+const struct zcr_keyboard_configuration_v1_interface
+ keyboard_configuration_implementation = {
+ keyboard_configuration_get_keyboard_device_configuration};
+
+void bind_keyboard_configuration(wl_client* client,
+ void* data,
+ uint32_t version,
+ uint32_t id) {
+ wl_resource* resource = wl_resource_create(
+ client, &zcr_keyboard_configuration_v1_interface, version, id);
+ wl_resource_set_implementation(
+ resource, &keyboard_configuration_implementation, data, nullptr);
+}
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -3060,6 +3280,8 @@ Server::Server(Display* display)
display_, bind_seat);
wl_global_create(wl_display_.get(), &wp_viewporter_interface, 1, display_,
bind_viewporter);
+ wl_global_create(wl_display_.get(), &wp_presentation_interface, 1, display_,
+ bind_presentation);
wl_global_create(wl_display_.get(), &zcr_secure_output_v1_interface, 1,
display_, bind_secure_output);
wl_global_create(wl_display_.get(), &zcr_alpha_compositing_v1_interface, 1,
@@ -3072,6 +3294,8 @@ Server::Server(Display* display)
bind_stylus_v1_DEPRECATED);
wl_global_create(wl_display_.get(), &zcr_stylus_v2_interface, 1, display_,
bind_stylus_v2);
+ wl_global_create(wl_display_.get(), &zcr_keyboard_configuration_v1_interface,
+ 2, display_, bind_keyboard_configuration);
}
Server::~Server() {}
diff --git a/chromium/components/exo/wm_helper.cc b/chromium/components/exo/wm_helper.cc
index 6be7aff4f67..ce51cbafdac 100644
--- a/chromium/components/exo/wm_helper.cc
+++ b/chromium/components/exo/wm_helper.cc
@@ -70,6 +70,15 @@ void WMHelper::RemoveAccessibilityObserver(AccessibilityObserver* observer) {
accessibility_observers_.RemoveObserver(observer);
}
+void WMHelper::AddInputDeviceEventObserver(InputDeviceEventObserver* observer) {
+ input_device_event_observers_.AddObserver(observer);
+}
+
+void WMHelper::RemoveInputDeviceEventObserver(
+ InputDeviceEventObserver* observer) {
+ input_device_event_observers_.RemoveObserver(observer);
+}
+
void WMHelper::NotifyWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) {
for (ActivationObserver& observer : activation_observers_)
@@ -107,4 +116,9 @@ void WMHelper::NotifyAccessibilityModeChanged() {
observer.OnAccessibilityModeChanged();
}
+void WMHelper::NotifyKeyboardDeviceConfigurationChanged() {
+ for (InputDeviceEventObserver& observer : input_device_event_observers_)
+ observer.OnKeyboardDeviceConfigurationChanged();
+}
+
} // namespace exo
diff --git a/chromium/components/exo/wm_helper.h b/chromium/components/exo/wm_helper.h
index 306c1f11c56..d814e5bcf9d 100644
--- a/chromium/components/exo/wm_helper.h
+++ b/chromium/components/exo/wm_helper.h
@@ -70,6 +70,14 @@ class WMHelper {
virtual ~AccessibilityObserver() {}
};
+ class InputDeviceEventObserver {
+ public:
+ virtual void OnKeyboardDeviceConfigurationChanged() = 0;
+
+ protected:
+ virtual ~InputDeviceEventObserver() {}
+ };
+
virtual ~WMHelper();
static void SetInstance(WMHelper* helper);
@@ -85,6 +93,8 @@ class WMHelper {
void RemoveMaximizeModeObserver(MaximizeModeObserver* observer);
void AddAccessibilityObserver(AccessibilityObserver* observer);
void RemoveAccessibilityObserver(AccessibilityObserver* observer);
+ void AddInputDeviceEventObserver(InputDeviceEventObserver* observer);
+ void RemoveInputDeviceEventObserver(InputDeviceEventObserver* observer);
virtual const display::ManagedDisplayInfo GetDisplayInfo(
int64_t display_id) const = 0;
@@ -113,6 +123,7 @@ class WMHelper {
void NotifyMaximizeModeStarted();
void NotifyMaximizeModeEnded();
void NotifyAccessibilityModeChanged();
+ void NotifyKeyboardDeviceConfigurationChanged();
private:
base::ObserverList<ActivationObserver> activation_observers_;
@@ -120,6 +131,7 @@ class WMHelper {
base::ObserverList<CursorObserver> cursor_observers_;
base::ObserverList<MaximizeModeObserver> maximize_mode_observers_;
base::ObserverList<AccessibilityObserver> accessibility_observers_;
+ base::ObserverList<InputDeviceEventObserver> input_device_event_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 f70155c7a56..5d117d10f2a 100644
--- a/chromium/components/exo/wm_helper_ash.cc
+++ b/chromium/components/exo/wm_helper_ash.cc
@@ -5,12 +5,14 @@
#include "components/exo/wm_helper_ash.h"
#include "ash/common/accessibility_delegate.h"
+#include "ash/common/system/tray/system_tray_notifier.h"
#include "ash/common/wm/maximize_mode/maximize_mode_controller.h"
#include "ash/common/wm_shell.h"
#include "ash/shell.h"
#include "base/memory/singleton.h"
#include "ui/aura/client/focus_client.h"
#include "ui/display/manager/display_manager.h"
+#include "ui/events/devices/device_data_manager.h"
#include "ui/wm/public/activation_client.h"
namespace exo {
@@ -24,6 +26,8 @@ WMHelperAsh::WMHelperAsh() {
aura::client::FocusClient* focus_client =
aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
focus_client->AddObserver(this);
+ ui::DeviceDataManager::GetInstance()->AddObserver(this);
+ ash::WmShell::Get()->system_tray_notifier()->AddAccessibilityObserver(this);
}
WMHelperAsh::~WMHelperAsh() {
@@ -34,6 +38,9 @@ WMHelperAsh::~WMHelperAsh() {
focus_client->RemoveObserver(this);
ash::Shell::GetInstance()->activation_client()->RemoveObserver(this);
ash::WmShell::Get()->RemoveShellObserver(this);
+ ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
+ ash::WmShell::Get()->system_tray_notifier()->RemoveAccessibilityObserver(
+ this);
}
////////////////////////////////////////////////////////////////////////////////
@@ -133,4 +140,8 @@ void WMHelperAsh::OnMaximizeModeEnded() {
NotifyMaximizeModeEnded();
}
+void WMHelperAsh::OnKeyboardDeviceConfigurationChanged() {
+ NotifyKeyboardDeviceConfigurationChanged();
+}
+
} // namespace exo
diff --git a/chromium/components/exo/wm_helper_ash.h b/chromium/components/exo/wm_helper_ash.h
index 64797897178..ce44f948eee 100644
--- a/chromium/components/exo/wm_helper_ash.h
+++ b/chromium/components/exo/wm_helper_ash.h
@@ -11,6 +11,7 @@
#include "components/exo/wm_helper.h"
#include "ui/aura/client/cursor_client_observer.h"
#include "ui/aura/client/focus_change_observer.h"
+#include "ui/events/devices/input_device_event_observer.h"
#include "ui/wm/public/activation_change_observer.h"
namespace exo {
@@ -21,7 +22,8 @@ class WMHelperAsh : public WMHelper,
public aura::client::FocusChangeObserver,
public aura::client::CursorClientObserver,
public ash::AccessibilityObserver,
- public ash::ShellObserver {
+ public ash::ShellObserver,
+ public ui::InputDeviceEventObserver {
public:
WMHelperAsh();
~WMHelperAsh() override;
@@ -64,6 +66,9 @@ class WMHelperAsh : public WMHelper,
void OnMaximizeModeStarted() override;
void OnMaximizeModeEnded() override;
+ // Overriden from ui::InputDeviceEventObserver:
+ void OnKeyboardDeviceConfigurationChanged() override;
+
private:
DISALLOW_COPY_AND_ASSIGN(WMHelperAsh);
};
diff --git a/chromium/components/exo/wm_helper_mus.cc b/chromium/components/exo/wm_helper_mus.cc
index 9a67809eae2..07a60cfdce7 100644
--- a/chromium/components/exo/wm_helper_mus.cc
+++ b/chromium/components/exo/wm_helper_mus.cc
@@ -4,41 +4,27 @@
#include "components/exo/wm_helper_mus.h"
-#include "services/ui/public/cpp/window_tree_client.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/display/manager/managed_display_info.h"
-#include "ui/views/mus/native_widget_mus.h"
-#include "ui/views/mus/window_manager_connection.h"
-#include "ui/views/widget/widget.h"
-
+#include "ui/wm/public/activation_client.h"
namespace exo {
-namespace {
-
-aura::Window* GetToplevelAuraWindow(ui::Window* window) {
- DCHECK(window);
- // We never create child ui::Window, so window->parent() should be null.
- DCHECK(!window->parent());
- views::Widget* widget = views::NativeWidgetMus::GetWidgetForWindow(window);
- if (!widget)
- return nullptr;
- return widget->GetNativeWindow();
-}
-
-} // namespace
////////////////////////////////////////////////////////////////////////////////
// WMHelperMus, public:
-WMHelperMus::WMHelperMus()
- : active_window_(WMHelperMus::GetActiveWindow()),
- focused_window_(WMHelperMus::GetFocusedWindow()) {
- views::WindowManagerConnection::Get()->client()->AddObserver(this);
+WMHelperMus::WMHelperMus() {
+ aura::Env* env = aura::Env::GetInstance();
+ SetActiveFocusClient(env->active_focus_client(),
+ env->active_focus_client_root());
+ env->AddObserver(this);
}
WMHelperMus::~WMHelperMus() {
- views::WindowManagerConnection::Get()->client()->RemoveObserver(this);
+ if (active_focus_client_)
+ active_focus_client_->RemoveObserver(this);
+ aura::Env::GetInstance()->RemoveObserver(this);
}
////////////////////////////////////////////////////////////////////////////////
@@ -56,18 +42,11 @@ aura::Window* WMHelperMus::GetContainer(int container_id) {
}
aura::Window* WMHelperMus::GetActiveWindow() const {
- ui::Window* window =
- views::WindowManagerConnection::Get()->client()->GetFocusedWindow();
- return window ? GetToplevelAuraWindow(window) : nullptr;
+ return active_window_;
}
aura::Window* WMHelperMus::GetFocusedWindow() const {
- aura::Window* active_window = GetActiveWindow();
- if (!active_window)
- return nullptr;
- aura::client::FocusClient* focus_client =
- aura::client::GetFocusClient(active_window);
- return focus_client->GetFocusedWindow();
+ return focused_window_;
}
ui::CursorSetType WMHelperMus::GetCursorSet() const {
@@ -109,43 +88,65 @@ void WMHelperMus::PlayEarcon(int sound_key) const {
NOTIMPLEMENTED();
}
-void WMHelperMus::OnWindowTreeFocusChanged(ui::Window* gained_focus,
- ui::Window* lost_focus) {
- aura::Window* gained_active =
- gained_focus ? GetToplevelAuraWindow(gained_focus) : nullptr;
- aura::Window* lost_active =
- lost_focus ? GetToplevelAuraWindow(lost_focus) : nullptr;
-
- // Because NativeWidgetMus uses separate FocusClient for every toplevel
- // window, we have to stop observering the FocusClient of the |lost_active|
- // and start observering the FocusClient of the |gained_active|.
- if (active_window_) {
- aura::client::FocusClient* focus_client =
- aura::client::GetFocusClient(active_window_);
- focus_client->RemoveObserver(this);
- }
-
- active_window_ = gained_active;
- NotifyWindowActivated(gained_active, lost_active);
-
- aura::Window* focused_window = nullptr;
- if (active_window_) {
- aura::client::FocusClient* focus_client =
- aura::client::GetFocusClient(active_window_);
- focus_client->AddObserver(this);
- focused_window = focus_client->GetFocusedWindow();
- }
+void WMHelperMus::OnWindowInitialized(aura::Window* window) {}
- // OnWindowFocused() will update |focused_window_|.
- OnWindowFocused(focused_window, focused_window_);
+void WMHelperMus::OnActiveFocusClientChanged(
+ aura::client::FocusClient* focus_client,
+ aura::Window* window) {
+ SetActiveFocusClient(focus_client, window);
}
void WMHelperMus::OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) {
- if (focused_window_ != gained_focus) {
- focused_window_ = gained_focus;
- NotifyWindowFocused(gained_focus, lost_focus);
+ if (focused_window_ == gained_focus)
+ return;
+
+ SetActiveWindow(GetActivationClient()->GetActiveWindow());
+ SetFocusedWindow(gained_focus);
+}
+
+void WMHelperMus::OnKeyboardDeviceConfigurationChanged() {
+ NotifyKeyboardDeviceConfigurationChanged();
+}
+
+void WMHelperMus::SetActiveFocusClient(aura::client::FocusClient* focus_client,
+ aura::Window* window) {
+ if (active_focus_client_)
+ active_focus_client_->RemoveObserver(this);
+ active_focus_client_ = focus_client;
+ root_with_active_focus_client_ = window;
+ if (active_focus_client_) {
+ active_focus_client_->AddObserver(this);
+ SetActiveWindow(GetActivationClient()->GetActiveWindow());
+ SetFocusedWindow(active_focus_client_->GetFocusedWindow());
+ } else {
+ SetActiveWindow(nullptr);
+ SetFocusedWindow(nullptr);
}
}
+void WMHelperMus::SetActiveWindow(aura::Window* window) {
+ if (active_window_ == window)
+ return;
+
+ aura::Window* lost_active = active_window_;
+ active_window_ = window;
+ NotifyWindowActivated(active_window_, lost_active);
+}
+
+void WMHelperMus::SetFocusedWindow(aura::Window* window) {
+ if (focused_window_ == window)
+ return;
+
+ aura::Window* lost_focus = focused_window_;
+ focused_window_ = window;
+ NotifyWindowFocused(focused_window_, lost_focus);
+}
+
+aura::client::ActivationClient* WMHelperMus::GetActivationClient() {
+ return root_with_active_focus_client_
+ ? aura::client::GetActivationClient(root_with_active_focus_client_)
+ : nullptr;
+}
+
} // namespace exo
diff --git a/chromium/components/exo/wm_helper_mus.h b/chromium/components/exo/wm_helper_mus.h
index 8ae5d3ab4ed..f1fb4cc7392 100644
--- a/chromium/components/exo/wm_helper_mus.h
+++ b/chromium/components/exo/wm_helper_mus.h
@@ -7,14 +7,22 @@
#include "base/macros.h"
#include "components/exo/wm_helper.h"
-#include "services/ui/public/cpp/window_tree_client_observer.h"
#include "ui/aura/client/focus_change_observer.h"
+#include "ui/aura/env_observer.h"
+#include "ui/events/devices/input_device_event_observer.h"
+
+namespace aura {
+namespace client {
+class ActivationClient;
+}
+}
namespace exo {
// A helper class for accessing WindowManager related features.
class WMHelperMus : public WMHelper,
- public ui::WindowTreeClientObserver,
+ public ui::InputDeviceEventObserver,
+ public aura::EnvObserver,
public aura::client::FocusChangeObserver {
public:
WMHelperMus();
@@ -36,17 +44,32 @@ class WMHelperMus : public WMHelper,
bool IsSpokenFeedbackEnabled() const override;
void PlayEarcon(int sound_key) const override;
- // Overriden from ui::WindowTreeClientObserver:
- void OnWindowTreeFocusChanged(ui::Window* gained_focus,
- ui::Window* lost_focus) override;
+ // Overriden from aura::EnvObserver:
+ void OnWindowInitialized(aura::Window* window) override;
+ void OnActiveFocusClientChanged(aura::client::FocusClient* focus_client,
+ aura::Window* window) override;
// Overriden from ui::client::FocusChangeObserver:
void OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) override;
+ // Overriden from ui::InputDeviceEventObserver:
+ void OnKeyboardDeviceConfigurationChanged() override;
+
private:
- aura::Window* active_window_;
- aura::Window* focused_window_;
+ void SetActiveFocusClient(aura::client::FocusClient* focus_client,
+ aura::Window* window);
+ void SetActiveWindow(aura::Window* window);
+ void SetFocusedWindow(aura::Window* window);
+
+ aura::client::ActivationClient* GetActivationClient();
+
+ // Current FocusClient.
+ aura::client::FocusClient* active_focus_client_ = nullptr;
+ // This is the window that |active_focus_client_| comes from (may be null).
+ aura::Window* root_with_active_focus_client_ = nullptr;
+ aura::Window* active_window_ = nullptr;
+ aura::Window* focused_window_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(WMHelperMus);
};
diff --git a/chromium/components/favicon/content/content_favicon_driver.h b/chromium/components/favicon/content/content_favicon_driver.h
index 70d3c4fdacf..76af5eed5aa 100644
--- a/chromium/components/favicon/content/content_favicon_driver.h
+++ b/chromium/components/favicon/content/content_favicon_driver.h
@@ -13,7 +13,6 @@
#include "url/gurl.h"
namespace content {
-struct FaviconStatus;
struct FaviconURL;
class WebContents;
}
diff --git a/chromium/components/favicon/core/fallback_icon_client.h b/chromium/components/favicon/core/fallback_icon_client.h
index 24ba582f6e3..8efa28c2e7d 100644
--- a/chromium/components/favicon/core/fallback_icon_client.h
+++ b/chromium/components/favicon/core/fallback_icon_client.h
@@ -10,8 +10,6 @@
#include "components/keyed_service/core/keyed_service.h"
-class GURL;
-
namespace favicon {
// This class abstracts operations that depend on the embedder's environment,
diff --git a/chromium/components/favicon/core/favicon_driver_impl.h b/chromium/components/favicon/core/favicon_driver_impl.h
index f6238490910..12fbd9494d9 100644
--- a/chromium/components/favicon/core/favicon_driver_impl.h
+++ b/chromium/components/favicon/core/favicon_driver_impl.h
@@ -19,7 +19,6 @@ class BookmarkModel;
}
namespace gfx {
-class Image;
class Size;
}
@@ -29,7 +28,6 @@ class HistoryService;
namespace favicon {
-class FaviconDriverObserver;
class FaviconHandler;
class FaviconService;
struct FaviconURL;
diff --git a/chromium/components/favicon/core/favicon_handler.h b/chromium/components/favicon/core/favicon_handler.h
index b70ca2a363f..bbd470c16ae 100644
--- a/chromium/components/favicon/core/favicon_handler.h
+++ b/chromium/components/favicon/core/favicon_handler.h
@@ -23,10 +23,6 @@
class SkBitmap;
-namespace base {
-class RefCountedMemory;
-}
-
namespace favicon {
class FaviconDriver;
diff --git a/chromium/components/favicon/ios/web_favicon_driver.h b/chromium/components/favicon/ios/web_favicon_driver.h
index ad900d9e443..b5209a1b7a0 100644
--- a/chromium/components/favicon/ios/web_favicon_driver.h
+++ b/chromium/components/favicon/ios/web_favicon_driver.h
@@ -11,7 +11,6 @@
#include "ios/web/public/web_state/web_state_user_data.h"
namespace web {
-struct FaviconStatus;
class WebState;
}
diff --git a/chromium/components/feedback/BUILD.gn b/chromium/components/feedback/BUILD.gn
index 49ee20401a4..adb30bcd3c1 100644
--- a/chromium/components/feedback/BUILD.gn
+++ b/chromium/components/feedback/BUILD.gn
@@ -57,8 +57,8 @@ source_set("unit_tests") {
"//base",
"//components/feedback/proto",
"//components/keyed_service/core",
- "//components/pref_registry:test_support",
"//components/prefs:test_support",
+ "//components/sync_preferences:test_support",
"//components/user_prefs",
"//components/variations/net",
"//content/test:test_support",
diff --git a/chromium/components/feedback/DEPS b/chromium/components/feedback/DEPS
index d1b80c15c79..754fa046d27 100644
--- a/chromium/components/feedback/DEPS
+++ b/chromium/components/feedback/DEPS
@@ -2,8 +2,8 @@ include_rules = [
"-content",
"+components/data_use_measurement/core",
"+components/keyed_service",
- "+components/pref_registry",
"+components/prefs",
+ "+components/sync_preferences",
"+components/user_prefs",
"+components/variations",
"+content/public/browser",
diff --git a/chromium/components/feedback/feedback_data.h b/chromium/components/feedback/feedback_data.h
index a56bfcd4594..0a3bcfa2e59 100644
--- a/chromium/components/feedback/feedback_data.h
+++ b/chromium/components/feedback/feedback_data.h
@@ -14,7 +14,6 @@
#include "url/gurl.h"
namespace base {
-class FilePath;
class RefCountedString;
}
namespace content {
diff --git a/chromium/components/feedback/feedback_uploader_chrome.cc b/chromium/components/feedback/feedback_uploader_chrome.cc
index 9b3cb82e9cf..0b18305d920 100644
--- a/chromium/components/feedback/feedback_uploader_chrome.cc
+++ b/chromium/components/feedback/feedback_uploader_chrome.cc
@@ -60,8 +60,8 @@ void FeedbackUploaderChrome::DispatchReport(const std::string& data) {
fetcher, data_use_measurement::DataUseUserData::FEEDBACK_UPLOADER);
// Tell feedback server about the variation state of this install.
net::HttpRequestHeaders headers;
- // Note: It's fine to pass in |is_signed_in| false, which does not affect
- // transmission of experiment ids coming from the variations server.
+ // 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(fetcher->GetOriginalURL(),
context_->IsOffTheRecord(), false,
diff --git a/chromium/components/feedback/feedback_uploader_unittest.cc b/chromium/components/feedback/feedback_uploader_unittest.cc
index c4bcb6189f4..17a6ec5b935 100644
--- a/chromium/components/feedback/feedback_uploader_unittest.cc
+++ b/chromium/components/feedback/feedback_uploader_unittest.cc
@@ -17,7 +17,7 @@
#include "build/build_config.h"
#include "components/feedback/feedback_uploader_chrome.h"
#include "components/feedback/feedback_uploader_factory.h"
-#include "components/pref_registry/testing_pref_service_syncable.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.h"
@@ -47,11 +47,11 @@ namespace feedback {
class FeedbackUploaderTest : public testing::Test {
protected:
FeedbackUploaderTest()
- : ui_thread_(content::BrowserThread::UI, &message_loop_),
- context_(new content::TestBrowserContext()),
- prefs_(new user_prefs::TestingPrefServiceSyncable()),
- dispatched_reports_count_(0),
- expected_reports_(0) {
+ : ui_thread_(content::BrowserThread::UI, &message_loop_),
+ context_(new content::TestBrowserContext()),
+ prefs_(new sync_preferences::TestingPrefServiceSyncable()),
+ dispatched_reports_count_(0),
+ expected_reports_(0) {
user_prefs::UserPrefs::Set(context_.get(), prefs_.get());
FeedbackUploaderFactory::GetInstance()->SetTestingFactory(
context_.get(), &CreateFeedbackUploaderService);
diff --git a/chromium/components/feedback/feedback_util.h b/chromium/components/feedback/feedback_util.h
index 1b6527827d1..7d50ee57ad7 100644
--- a/chromium/components/feedback/feedback_util.h
+++ b/chromium/components/feedback/feedback_util.h
@@ -17,12 +17,6 @@
#include "base/win/windows_version.h"
#endif
-class Profile;
-
-namespace content {
-class WebContents;
-}
-
namespace chrome {
extern const char kAppLauncherCategoryTag[];
} // namespace chrome
diff --git a/chromium/components/feedback/tracing_manager.h b/chromium/components/feedback/tracing_manager.h
index 9d944fc6281..b45d234a4ba 100644
--- a/chromium/components/feedback/tracing_manager.h
+++ b/chromium/components/feedback/tracing_manager.h
@@ -17,7 +17,6 @@
namespace base {
class RefCountedString;
-class FilePath;
}
// Callback used for getting the output of a trace.
diff --git a/chromium/components/filesystem/directory_impl.cc b/chromium/components/filesystem/directory_impl.cc
index 745bcf66aac..64114de9348 100644
--- a/chromium/components/filesystem/directory_impl.cc
+++ b/chromium/components/filesystem/directory_impl.cc
@@ -11,13 +11,12 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "components/filesystem/file_impl.h"
#include "components/filesystem/lock_table.h"
#include "components/filesystem/util.h"
-#include "mojo/common/common_type_converters.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "mojo/public/cpp/system/platform_handle.h"
namespace filesystem {
@@ -92,9 +91,9 @@ void DirectoryImpl::OpenFile(const std::string& raw_path,
void DirectoryImpl::OpenFileHandle(const std::string& raw_path,
uint32_t open_flags,
const OpenFileHandleCallback& callback) {
- mojom::FileError error = mojom::FileError::OK;
- mojo::ScopedHandle handle = OpenFileHandleImpl(raw_path, open_flags, &error);
- callback.Run(error, std::move(handle));
+ base::File file = OpenFileHandleImpl(raw_path, open_flags);
+ mojom::FileError error = GetError(file);
+ callback.Run(error, std::move(file));
}
void DirectoryImpl::OpenFileHandles(
@@ -105,8 +104,8 @@ void DirectoryImpl::OpenFileHandles(
for (const auto& detail : details) {
mojom::FileOpenResultPtr result(mojom::FileOpenResult::New());
result->path = detail->path;
- result->file_handle =
- OpenFileHandleImpl(detail->path, detail->open_flags, &result->error);
+ result->file_handle = OpenFileHandleImpl(detail->path, detail->open_flags);
+ result->error = GetError(result->file_handle);
results[i++] = std::move(result);
}
callback.Run(std::move(results));
@@ -224,7 +223,8 @@ void DirectoryImpl::IsWritable(const std::string& raw_path,
}
void DirectoryImpl::Flush(const FlushCallback& callback) {
- base::File file(directory_path_, base::File::FLAG_READ);
+ base::File file(directory_path_,
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) {
callback.Run(GetError(file));
return;
@@ -290,14 +290,14 @@ void DirectoryImpl::ReadEntireFile(const std::string& raw_path,
return;
}
- std::string contents;
+ std::vector<uint8_t> contents;
const int kBufferSize = 1 << 16;
std::unique_ptr<char[]> buf(new char[kBufferSize]);
int len;
while ((len = base_file.ReadAtCurrentPos(buf.get(), kBufferSize)) > 0)
- contents.append(buf.get(), len);
+ contents.insert(contents.end(), buf.get(), buf.get() + len);
- callback.Run(mojom::FileError::OK, mojo::Array<uint8_t>::From(contents));
+ callback.Run(mojom::FileError::OK, contents);
}
void DirectoryImpl::WriteFile(const std::string& raw_path,
@@ -335,31 +335,21 @@ void DirectoryImpl::WriteFile(const std::string& raw_path,
callback.Run(mojom::FileError::OK);
}
-mojo::ScopedHandle DirectoryImpl::OpenFileHandleImpl(
- const std::string& raw_path,
- uint32_t open_flags,
- mojom::FileError* error) {
+base::File DirectoryImpl::OpenFileHandleImpl(const std::string& raw_path,
+ uint32_t open_flags) {
base::FilePath path;
- *error = ValidatePath(raw_path, directory_path_, &path);
- if (*error != mojom::FileError::OK)
- return mojo::ScopedHandle();
+ mojom::FileError error = ValidatePath(raw_path, directory_path_, &path);
+ if (error != mojom::FileError::OK)
+ return base::File(static_cast<base::File::Error>(error));
if (base::DirectoryExists(path)) {
// We must not return directories as files. In the file abstraction, we
// can fetch raw file descriptors over mojo pipes, and passing a file
// descriptor to a directory is a sandbox escape on Windows.
- *error = mojom::FileError::NOT_A_FILE;
- return mojo::ScopedHandle();
- }
-
- base::File base_file(path, open_flags);
- if (!base_file.IsValid()) {
- *error = GetError(base_file);
- return mojo::ScopedHandle();
+ return base::File(base::File::FILE_ERROR_NOT_A_FILE);
}
- *error = mojom::FileError::OK;
- return mojo::WrapPlatformFile(base_file.TakePlatformFile());
+ return base::File(path, open_flags);
}
} // namespace filesystem
diff --git a/chromium/components/filesystem/directory_impl.h b/chromium/components/filesystem/directory_impl.h
index 9f1a141de49..e226b78508e 100644
--- a/chromium/components/filesystem/directory_impl.h
+++ b/chromium/components/filesystem/directory_impl.h
@@ -64,9 +64,8 @@ class DirectoryImpl : public mojom::Directory {
const WriteFileCallback& callback) override;
private:
- mojo::ScopedHandle OpenFileHandleImpl(const std::string& raw_path,
- uint32_t open_flags,
- mojom::FileError* error);
+ base::File OpenFileHandleImpl(const std::string& raw_path,
+ uint32_t open_flags);
base::FilePath directory_path_;
scoped_refptr<SharedTempDir> temp_dir_;
diff --git a/chromium/components/filesystem/directory_impl_unittest.cc b/chromium/components/filesystem/directory_impl_unittest.cc
index 836d21ae39c..38410a2f765 100644
--- a/chromium/components/filesystem/directory_impl_unittest.cc
+++ b/chromium/components/filesystem/directory_impl_unittest.cc
@@ -10,13 +10,14 @@
#include "base/macros.h"
#include "components/filesystem/files_test_base.h"
-#include "mojo/common/common_type_converters.h"
namespace filesystem {
namespace {
using DirectoryImplTest = FilesTestBase;
+constexpr char kData[] = "one two three";
+
TEST_F(DirectoryImplTest, Read) {
mojom::DirectoryPtr directory;
GetTemporaryRoot(&directory);
@@ -135,7 +136,7 @@ TEST_F(DirectoryImplTest, CantOpenDirectoriesAsFiles) {
mojom::DirectoryPtr my_file_directory;
error = mojom::FileError::FAILED;
bool handled = directory->OpenDirectory(
- "my_file", GetProxy(&my_file_directory),
+ "my_file", MakeRequest(&my_file_directory),
mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -146,7 +147,7 @@ TEST_F(DirectoryImplTest, CantOpenDirectoriesAsFiles) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagRead | mojom::kFlagOpen, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::NOT_A_FILE, error);
@@ -162,17 +163,16 @@ TEST_F(DirectoryImplTest, Clone) {
mojom::DirectoryPtr directory;
GetTemporaryRoot(&directory);
- directory->Clone(GetProxy(&clone_one));
- directory->Clone(GetProxy(&clone_two));
+ directory->Clone(MakeRequest(&clone_one));
+ directory->Clone(MakeRequest(&clone_two));
// Original temporary directory goes out of scope here; shouldn't be
// deleted since it has clones.
}
- std::string data("one two three");
+ std::vector<uint8_t> data(kData, kData + strlen(kData));
{
- bool handled =
- clone_one->WriteFile("data", mojo::Array<uint8_t>::From(data), &error);
+ bool handled = clone_one->WriteFile("data", data, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
}
@@ -183,8 +183,7 @@ TEST_F(DirectoryImplTest, Clone) {
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
- EXPECT_EQ(data,
- mojo::Array<uint8_t>(std::move(file_contents)).To<std::string>());
+ EXPECT_EQ(data, file_contents);
}
}
@@ -193,10 +192,9 @@ TEST_F(DirectoryImplTest, WriteFileReadFile) {
GetTemporaryRoot(&directory);
mojom::FileError error;
- std::string data("one two three");
+ std::vector<uint8_t> data(kData, kData + strlen(kData));
{
- bool handled =
- directory->WriteFile("data", mojo::Array<uint8_t>::From(data), &error);
+ bool handled = directory->WriteFile("data", data, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
}
@@ -207,8 +205,7 @@ TEST_F(DirectoryImplTest, WriteFileReadFile) {
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
- EXPECT_EQ(data,
- mojo::Array<uint8_t>(std::move(file_contents)).To<std::string>());
+ EXPECT_EQ(data, file_contents);
}
}
@@ -236,7 +233,7 @@ TEST_F(DirectoryImplTest, CantReadEntireFileOnADirectory) {
mojom::DirectoryPtr my_file_directory;
error = mojom::FileError::FAILED;
bool handled = directory->OpenDirectory(
- "my_dir", GetProxy(&my_file_directory),
+ "my_dir", MakeRequest(&my_file_directory),
mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -261,21 +258,32 @@ TEST_F(DirectoryImplTest, CantWriteFileOnADirectory) {
mojom::DirectoryPtr my_file_directory;
error = mojom::FileError::FAILED;
bool handled = directory->OpenDirectory(
- "my_dir", GetProxy(&my_file_directory),
+ "my_dir", MakeRequest(&my_file_directory),
mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
}
{
- std::string data("one two three");
- bool handled = directory->WriteFile(
- "my_dir", mojo::Array<uint8_t>::From(data), &error);
+ std::vector<uint8_t> data(kData, kData + strlen(kData));
+ bool handled = directory->WriteFile("my_dir", data, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::NOT_A_FILE, error);
}
}
+TEST_F(DirectoryImplTest, Flush) {
+ mojom::DirectoryPtr directory;
+ GetTemporaryRoot(&directory);
+ mojom::FileError error;
+
+ {
+ bool handled = directory->Flush(&error);
+ ASSERT_TRUE(handled);
+ EXPECT_EQ(mojom::FileError::OK, error);
+ }
+}
+
// TODO(vtl): Test delete flags.
} // namespace
diff --git a/chromium/components/filesystem/file_impl.cc b/chromium/components/filesystem/file_impl.cc
index 25a76f3d26b..a2c024f3922 100644
--- a/chromium/components/filesystem/file_impl.cc
+++ b/chromium/components/filesystem/file_impl.cc
@@ -12,19 +12,17 @@
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "components/filesystem/lock_table.h"
#include "components/filesystem/shared_temp_dir.h"
#include "components/filesystem/util.h"
-#include "mojo/common/common_type_converters.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "mojo/public/cpp/system/platform_handle.h"
static_assert(sizeof(off_t) <= sizeof(int64_t), "off_t too big");
static_assert(sizeof(size_t) >= sizeof(uint32_t), "size_t too small");
using base::Time;
-using mojo::ScopedHandle;
namespace filesystem {
namespace {
@@ -327,19 +325,19 @@ void FileImpl::Unlock(const UnlockCallback& callback) {
void FileImpl::AsHandle(const AsHandleCallback& callback) {
if (!file_.IsValid()) {
- callback.Run(GetError(file_), ScopedHandle());
+ callback.Run(GetError(file_), base::File());
return;
}
base::File new_file = file_.Duplicate();
if (!new_file.IsValid()) {
- callback.Run(GetError(new_file), ScopedHandle());
+ callback.Run(GetError(new_file), base::File());
return;
}
base::File::Info info;
if (!new_file.GetInfo(&info)) {
- callback.Run(mojom::FileError::FAILED, ScopedHandle());
+ callback.Run(mojom::FileError::FAILED, base::File());
return;
}
@@ -348,12 +346,11 @@ void FileImpl::AsHandle(const AsHandleCallback& callback) {
// passing a file descriptor to a directory is a sandbox escape on Windows,
// we should be absolutely paranoid.
if (info.is_directory) {
- callback.Run(mojom::FileError::NOT_A_FILE, ScopedHandle());
+ callback.Run(mojom::FileError::NOT_A_FILE, base::File());
return;
}
- callback.Run(mojom::FileError::OK,
- mojo::WrapPlatformFile(new_file.TakePlatformFile()));
+ callback.Run(mojom::FileError::OK, std::move(new_file));
}
} // namespace filesystem
diff --git a/chromium/components/filesystem/file_impl_unittest.cc b/chromium/components/filesystem/file_impl_unittest.cc
index 81544b488c4..492c243c647 100644
--- a/chromium/components/filesystem/file_impl_unittest.cc
+++ b/chromium/components/filesystem/file_impl_unittest.cc
@@ -11,7 +11,6 @@
#include "components/filesystem/files_test_base.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/type_converter.h"
-#include "mojo/public/cpp/system/platform_handle.h"
namespace filesystem {
namespace {
@@ -29,7 +28,7 @@ TEST_F(FileImplTest, CreateWriteCloseRenameOpenRead) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -67,7 +66,7 @@ TEST_F(FileImplTest, CreateWriteCloseRenameOpenRead) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("your_file", GetProxy(&file),
+ directory->OpenFile("your_file", MakeRequest(&file),
mojom::kFlagRead | mojom::kFlagOpen, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -105,7 +104,7 @@ TEST_F(FileImplTest, CantWriteInReadMode) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -131,7 +130,7 @@ TEST_F(FileImplTest, CantWriteInReadMode) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagRead | mojom::kFlagOpen, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -164,7 +163,7 @@ TEST_F(FileImplTest, OpenInAppendMode) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -196,7 +195,7 @@ TEST_F(FileImplTest, OpenInAppendMode) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagAppend | mojom::kFlagOpen, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -230,7 +229,7 @@ TEST_F(FileImplTest, OpenInAppendMode) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagRead | mojom::kFlagOpen, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -260,7 +259,7 @@ TEST_F(FileImplTest, OpenInTruncateMode) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -292,7 +291,7 @@ TEST_F(FileImplTest, OpenInTruncateMode) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled = directory->OpenFile(
- "my_file", GetProxy(&file),
+ "my_file", MakeRequest(&file),
mojom::kFlagWrite | mojom::kFlagOpenTruncated, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -326,7 +325,7 @@ TEST_F(FileImplTest, OpenInTruncateMode) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagRead | mojom::kFlagOpen, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -357,7 +356,7 @@ TEST_F(FileImplTest, StatTouch) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -428,7 +427,7 @@ TEST_F(FileImplTest, TellSeek) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -514,7 +513,7 @@ TEST_F(FileImplTest, Dup) {
mojom::FilePtr file1;
error = mojom::FileError::FAILED;
bool handled = directory->OpenFile(
- "my_file", GetProxy(&file1),
+ "my_file", MakeRequest(&file1),
mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -538,7 +537,7 @@ TEST_F(FileImplTest, Dup) {
// Dup it.
mojom::FilePtr file2;
error = mojom::FileError::FAILED;
- handled = file1->Dup(GetProxy(&file2), &error);
+ handled = file1->Dup(MakeRequest(&file2), &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -608,7 +607,7 @@ TEST_F(FileImplTest, Truncate) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file),
+ directory->OpenFile("my_file", MakeRequest(&file),
mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -658,26 +657,18 @@ TEST_F(FileImplTest, AsHandle) {
mojom::FilePtr file1;
error = mojom::FileError::FAILED;
bool handled = directory->OpenFile(
- "my_file", GetProxy(&file1),
+ "my_file", MakeRequest(&file1),
mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
- // Fetch the handle
+ // Fetch the file.
error = mojom::FileError::FAILED;
- mojo::ScopedHandle handle;
- handled = file1->AsHandle(&error, &handle);
+ base::File raw_file;
+ handled = file1->AsHandle(&error, &raw_file);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
- // Pull a file descriptor out of the scoped handle.
- base::PlatformFile platform_file;
- MojoResult unwrap_result = mojo::UnwrapPlatformFile(std::move(handle),
- &platform_file);
- EXPECT_EQ(MOJO_RESULT_OK, unwrap_result);
-
- // Pass this raw file descriptor to a base::File.
- base::File raw_file(platform_file);
ASSERT_TRUE(raw_file.IsValid());
EXPECT_EQ(5, raw_file.WriteAtCurrentPos("hello", 5));
}
@@ -687,7 +678,7 @@ TEST_F(FileImplTest, AsHandle) {
mojom::FilePtr file2;
error = mojom::FileError::FAILED;
bool handled =
- directory->OpenFile("my_file", GetProxy(&file2),
+ directory->OpenFile("my_file", MakeRequest(&file2),
mojom::kFlagRead | mojom::kFlagOpen, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -717,7 +708,7 @@ TEST_F(FileImplTest, SimpleLockUnlock) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled = directory->OpenFile(
- "my_file", GetProxy(&file),
+ "my_file", MakeRequest(&file),
mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -744,7 +735,7 @@ TEST_F(FileImplTest, CantDoubleLock) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled = directory->OpenFile(
- "my_file", GetProxy(&file),
+ "my_file", MakeRequest(&file),
mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -772,7 +763,7 @@ TEST_F(FileImplTest, ClosingFileClearsLock) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled = directory->OpenFile(
- "my_file", GetProxy(&file),
+ "my_file", MakeRequest(&file),
mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagOpenAlways, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
@@ -789,7 +780,7 @@ TEST_F(FileImplTest, ClosingFileClearsLock) {
mojom::FilePtr file;
error = mojom::FileError::FAILED;
bool handled = directory->OpenFile(
- "my_file", GetProxy(&file),
+ "my_file", MakeRequest(&file),
mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagOpenAlways, &error);
ASSERT_TRUE(handled);
EXPECT_EQ(mojom::FileError::OK, error);
diff --git a/chromium/components/filesystem/file_system_app.cc b/chromium/components/filesystem/file_system_app.cc
index ac41d092048..dd1d24b0108 100644
--- a/chromium/components/filesystem/file_system_app.cc
+++ b/chromium/components/filesystem/file_system_app.cc
@@ -9,6 +9,7 @@
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
+#include "base/memory/ptr_util.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "services/service_manager/public/cpp/connection.h"
#include "services/service_manager/public/cpp/connector.h"
diff --git a/chromium/components/filesystem/file_system_app.h b/chromium/components/filesystem/file_system_app.h
index 53ea98d976c..b3f399d8df0 100644
--- a/chromium/components/filesystem/file_system_app.h
+++ b/chromium/components/filesystem/file_system_app.h
@@ -14,10 +14,6 @@
#include "services/service_manager/public/cpp/service.h"
#include "services/tracing/public/cpp/provider.h"
-namespace mojo {
-class Connector;
-}
-
namespace filesystem {
class FileSystemApp
diff --git a/chromium/components/filesystem/file_system_impl.cc b/chromium/components/filesystem/file_system_impl.cc
index 1fbed03db25..138524d5394 100644
--- a/chromium/components/filesystem/file_system_impl.cc
+++ b/chromium/components/filesystem/file_system_impl.cc
@@ -14,6 +14,7 @@
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "components/filesystem/directory_impl.h"
#include "components/filesystem/lock_table.h"
diff --git a/chromium/components/filesystem/file_system_impl.h b/chromium/components/filesystem/file_system_impl.h
index 573fd31b4e8..7f41b357804 100644
--- a/chromium/components/filesystem/file_system_impl.h
+++ b/chromium/components/filesystem/file_system_impl.h
@@ -20,7 +20,6 @@ class Identity;
}
namespace filesystem {
-class FileSystemApp;
class LockTable;
diff --git a/chromium/components/filesystem/files_test_base.cc b/chromium/components/filesystem/files_test_base.cc
index 7be764b85ff..facb600b2e1 100644
--- a/chromium/components/filesystem/files_test_base.cc
+++ b/chromium/components/filesystem/files_test_base.cc
@@ -21,12 +21,12 @@ FilesTestBase::~FilesTestBase() {
void FilesTestBase::SetUp() {
ServiceTest::SetUp();
- connector()->ConnectToInterface("filesystem", &files_);
+ connector()->BindInterface("filesystem", &files_);
}
void FilesTestBase::GetTemporaryRoot(mojom::DirectoryPtr* directory) {
mojom::FileError error = mojom::FileError::FAILED;
- bool handled = files()->OpenTempDirectory(GetProxy(directory), &error);
+ bool handled = files()->OpenTempDirectory(MakeRequest(directory), &error);
ASSERT_TRUE(handled);
ASSERT_EQ(mojom::FileError::OK, error);
}
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
index 0a5d66091f6..25cf238d629 100644
--- a/chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc
+++ b/chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc
@@ -23,7 +23,6 @@
#include "base/time/default_clock.h"
#include "base/values.h"
#include "components/prefs/pref_filter.h"
-#include "mojo/common/common_type_converters.h"
namespace filesystem {
@@ -48,7 +47,7 @@ FilesystemJsonPrefStore::ReadResult::~ReadResult() {}
namespace {
PersistentPrefStore::PrefReadError HandleReadErrors(const base::Value* value) {
- if (!value->IsType(base::Value::TYPE_DICTIONARY))
+ if (!value->IsType(base::Value::Type::DICTIONARY))
return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
return PersistentPrefStore::PREF_READ_ERROR_NONE;
}
@@ -334,10 +333,10 @@ void FilesystemJsonPrefStore::PerformWrite() {
void FilesystemJsonPrefStore::OpenFilesystem(base::Closure callback) {
filesystem::mojom::FileSystemClientPtr client;
- binding_.Bind(GetProxy(&client));
+ binding_.Bind(MakeRequest(&client));
filesystem_->OpenFileSystem(
- "origin", GetProxy(&directory_), std::move(client),
+ "origin", MakeRequest(&directory_), std::move(client),
base::Bind(&FilesystemJsonPrefStore::OnOpenFilesystem, AsWeakPtr(),
callback));
}
@@ -366,8 +365,7 @@ void FilesystemJsonPrefStore::OnTempFileWriteStart() {
serializer.Serialize(*prefs_);
directory_->WriteFile(
- "tmp",
- mojo::Array<uint8_t>::From(output),
+ "tmp", std::vector<uint8_t>(output.begin(), output.end()),
Bind(&FilesystemJsonPrefStore::OnTempFileWrite, AsWeakPtr()));
}
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
index d52b0ec1c2f..ed04ae2b5e6 100644
--- a/chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.h
+++ b/chromium/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.h
@@ -31,9 +31,7 @@
class PrefFilter;
namespace base {
-class Clock;
class DictionaryValue;
-class FilePath;
class JsonPrefStoreLossyWriteTest;
class Value;
}
diff --git a/chromium/components/filesystem/public/cpp/prefs/pref_service_factory.cc b/chromium/components/filesystem/public/cpp/prefs/pref_service_factory.cc
index a1374783eec..e9ca2b95226 100644
--- a/chromium/components/filesystem/public/cpp/prefs/pref_service_factory.cc
+++ b/chromium/components/filesystem/public/cpp/prefs/pref_service_factory.cc
@@ -27,7 +27,7 @@ std::unique_ptr<PrefService> CreatePrefService(
service_manager::Connector* connector,
PrefRegistry* pref_registry) {
filesystem::mojom::FileSystemPtr filesystem;
- connector->ConnectToInterface("filesystem", &filesystem);
+ connector->BindInterface("filesystem", &filesystem);
scoped_refptr<FilesystemJsonPrefStore> user_prefs =
new FilesystemJsonPrefStore("preferences.json", std::move(filesystem),
diff --git a/chromium/components/filesystem/public/interfaces/BUILD.gn b/chromium/components/filesystem/public/interfaces/BUILD.gn
index a1ef7b67574..b65d3a797eb 100644
--- a/chromium/components/filesystem/public/interfaces/BUILD.gn
+++ b/chromium/components/filesystem/public/interfaces/BUILD.gn
@@ -11,4 +11,7 @@ mojom("interfaces") {
"file_system.mojom",
"types.mojom",
]
+ public_deps = [
+ "//mojo/common:common_custom_types",
+ ]
}
diff --git a/chromium/components/filesystem/public/interfaces/directory.mojom b/chromium/components/filesystem/public/interfaces/directory.mojom
index 104a62f5528..ac3370e8e45 100644
--- a/chromium/components/filesystem/public/interfaces/directory.mojom
+++ b/chromium/components/filesystem/public/interfaces/directory.mojom
@@ -6,6 +6,7 @@ module filesystem.mojom;
import "components/filesystem/public/interfaces/file.mojom";
import "components/filesystem/public/interfaces/types.mojom";
+import "mojo/common/file.mojom";
struct FileOpenDetails {
string path;
@@ -15,7 +16,7 @@ struct FileOpenDetails {
struct FileOpenResult {
string path;
FileError error;
- handle file_handle;
+ mojo.common.mojom.File? file_handle;
};
// This interface provides access to a directory in a "file system", providing
@@ -46,7 +47,7 @@ interface Directory {
// native file descriptor wrapped in a MojoHandle.
[Sync]
OpenFileHandle(string path, uint32 open_flags)
- => (FileError error, handle file_handle);
+ => (FileError error, mojo.common.mojom.File? file_handle);
// Like OpenFileHandle, but opens multiple files.
[Sync]
diff --git a/chromium/components/filesystem/public/interfaces/file.mojom b/chromium/components/filesystem/public/interfaces/file.mojom
index 05bce45cbf2..9a4a515c749 100644
--- a/chromium/components/filesystem/public/interfaces/file.mojom
+++ b/chromium/components/filesystem/public/interfaces/file.mojom
@@ -10,6 +10,7 @@
module filesystem.mojom;
import "components/filesystem/public/interfaces/types.mojom";
+import "mojo/common/file.mojom";
// TODO(vtl): Write comments.
interface File {
@@ -84,5 +85,5 @@ interface File {
// Returns a handle to the file for raw access.
[Sync]
- AsHandle() => (FileError error, handle file_handle);
+ AsHandle() => (FileError error, mojo.common.mojom.File? file_handle);
};
diff --git a/chromium/components/filesystem/util.cc b/chromium/components/filesystem/util.cc
index d9257cc0b2d..ecd3fc61c0e 100644
--- a/chromium/components/filesystem/util.cc
+++ b/chromium/components/filesystem/util.cc
@@ -14,7 +14,6 @@
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
-#include "mojo/public/cpp/bindings/string.h"
#if defined(OS_WIN)
#include "base/strings/utf_string_conversions.h"
diff --git a/chromium/components/filesystem/util.h b/chromium/components/filesystem/util.h
index b00fcf56f0d..70fe7e744f9 100644
--- a/chromium/components/filesystem/util.h
+++ b/chromium/components/filesystem/util.h
@@ -10,10 +10,6 @@
#include "base/files/file.h"
#include "components/filesystem/public/interfaces/types.mojom.h"
-namespace mojo {
-class String;
-}
-
namespace filesystem {
// Validation functions (typically used to check arguments; they return
diff --git a/chromium/components/font_service/font_service_app.cc b/chromium/components/font_service/font_service_app.cc
index f78b4c23b93..78f6d4a336b 100644
--- a/chromium/components/font_service/font_service_app.cc
+++ b/chromium/components/font_service/font_service_app.cc
@@ -28,18 +28,13 @@ static_assert(
namespace {
-mojo::ScopedHandle GetHandleForPath(const base::FilePath& path) {
+base::File GetFileForPath(const base::FilePath& path) {
if (path.empty())
- return mojo::ScopedHandle();
+ return base::File();
- mojo::ScopedHandle to_pass;
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
- if (!file.IsValid()) {
- LOG(WARNING) << "file not valid, path=" << path.value();
- return mojo::ScopedHandle();
- }
-
- return mojo::WrapPlatformFile(file.TakePlatformFile());
+ LOG_IF(WARNING, !file.IsValid()) << "file not valid, path=" << path.value();
+ return file;
}
} // namespace
@@ -109,12 +104,12 @@ void FontServiceApp::MatchFamilyName(const std::string& family_name,
void FontServiceApp::OpenStream(uint32_t id_number,
const OpenStreamCallback& callback) {
- mojo::ScopedHandle handle;
+ base::File file;
if (id_number < static_cast<uint32_t>(paths_.size())) {
- handle = GetHandleForPath(base::FilePath(paths_[id_number].c_str()));
+ file = GetFileForPath(base::FilePath(paths_[id_number].c_str()));
}
- callback.Run(std::move(handle));
+ callback.Run(std::move(file));
}
int FontServiceApp::FindOrAddPath(const SkString& path) {
diff --git a/chromium/components/font_service/public/cpp/font_loader.cc b/chromium/components/font_service/public/cpp/font_loader.cc
index a830164bfdb..877adb46dbd 100644
--- a/chromium/components/font_service/public/cpp/font_loader.cc
+++ b/chromium/components/font_service/public/cpp/font_loader.cc
@@ -15,7 +15,7 @@ namespace font_service {
FontLoader::FontLoader(service_manager::Connector* connector) {
mojom::FontServicePtr font_service;
- connector->ConnectToInterface("font_service", &font_service);
+ connector->BindInterface("font_service", &font_service);
thread_ = new internal::FontServiceThread(std::move(font_service));
}
diff --git a/chromium/components/font_service/public/cpp/font_service_thread.cc b/chromium/components/font_service/public/cpp/font_service_thread.cc
index 69aba5e1fa8..4c1473715e3 100644
--- a/chromium/components/font_service/public/cpp/font_service_thread.cc
+++ b/chromium/components/font_service/public/cpp/font_service_thread.cc
@@ -10,7 +10,6 @@
#include "base/files/file.h"
#include "base/synchronization/waitable_event.h"
#include "components/font_service/public/cpp/mapped_font_file.h"
-#include "mojo/public/cpp/system/platform_handle.h"
namespace font_service {
namespace internal {
@@ -158,15 +157,9 @@ void FontServiceThread::OpenStreamImpl(base::WaitableEvent* done_event,
void FontServiceThread::OnOpenStreamComplete(base::WaitableEvent* done_event,
base::File* output_file,
- mojo::ScopedHandle handle) {
+ base::File file) {
pending_waitable_events_.erase(done_event);
- if (handle.is_valid()) {
- base::PlatformFile platform_file;
- CHECK_EQ(mojo::UnwrapPlatformFile(std::move(handle), &platform_file),
- MOJO_RESULT_OK);
- *output_file = base::File(platform_file);
- }
-
+ *output_file = std::move(file);
done_event->Signal();
}
diff --git a/chromium/components/font_service/public/cpp/font_service_thread.h b/chromium/components/font_service/public/cpp/font_service_thread.h
index 9f698f836c0..6cfc8622b1a 100644
--- a/chromium/components/font_service/public/cpp/font_service_thread.h
+++ b/chromium/components/font_service/public/cpp/font_service_thread.h
@@ -80,7 +80,7 @@ class FontServiceThread : public base::Thread,
const uint32_t id_number);
void OnOpenStreamComplete(base::WaitableEvent* done_event,
base::File* output_file,
- mojo::ScopedHandle handle);
+ base::File file);
// Connection to |font_service_| has gone away. Called on the background
// thread.
diff --git a/chromium/components/font_service/public/interfaces/BUILD.gn b/chromium/components/font_service/public/interfaces/BUILD.gn
index e60d556129c..12efaaf4b99 100644
--- a/chromium/components/font_service/public/interfaces/BUILD.gn
+++ b/chromium/components/font_service/public/interfaces/BUILD.gn
@@ -8,4 +8,8 @@ mojom("interfaces") {
sources = [
"font_service.mojom",
]
+
+ public_deps = [
+ "//mojo/common:common_custom_types",
+ ]
}
diff --git a/chromium/components/font_service/public/interfaces/font_service.mojom b/chromium/components/font_service/public/interfaces/font_service.mojom
index ea913d48f00..942970689fe 100644
--- a/chromium/components/font_service/public/interfaces/font_service.mojom
+++ b/chromium/components/font_service/public/interfaces/font_service.mojom
@@ -4,6 +4,8 @@
module font_service.mojom;
+import "mojo/common/file.mojom";
+
enum TypefaceSlant {
ROMAN = 0,
ITALIC = 1,
@@ -39,5 +41,5 @@ interface FontService {
(FontIdentity? identity, string family_name, TypefaceStyle style);
// Returns a handle to the raw font specified by |id_number|.
- OpenStream(uint32 id_number) => (handle font_handle);
+ OpenStream(uint32 id_number) => (mojo.common.mojom.File? font_handle);
};
diff --git a/chromium/components/google/core/browser/BUILD.gn b/chromium/components/google/core/browser/BUILD.gn
index ad60061652e..67f502e105c 100644
--- a/chromium/components/google/core/browser/BUILD.gn
+++ b/chromium/components/google/core/browser/BUILD.gn
@@ -8,6 +8,7 @@ static_library("browser") {
"google_pref_names.h",
"google_switches.cc",
"google_switches.h",
+ "google_tld_list.h",
"google_url_tracker.cc",
"google_url_tracker.h",
"google_url_tracker_client.cc",
diff --git a/chromium/components/google/core/browser/google_tld_list.h b/chromium/components/google/core/browser/google_tld_list.h
new file mode 100644
index 00000000000..6a246cf38ec
--- /dev/null
+++ b/chromium/components/google/core/browser/google_tld_list.h
@@ -0,0 +1,48 @@
+// 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_GOOGLE_CORE_BROWSER_GOOGLE_TLD_LIST_H_
+#define COMPONENTS_GOOGLE_CORE_BROWSER_GOOGLE_TLD_LIST_H_
+
+// DO NOT EDIT. This file is generated by a script. See crbug.com/674712
+// for details.
+
+#define GOOGLE_TLD_LIST "ac", "ad", "ae", "af", "ag", "al", "am", "as", "at", \
+"aw", "az", "ba", "be", "bf", "bg", "bi", "biz", "bj", "bm", "bn", "bo", "bs", \
+"bt", "by", "bz", "ca", "cat", "cc", "cd", "cf", "cg", "ch", "ci", "cl", "cm", \
+"cn", "co", "co.ao", "co.at", "co.ba", "co.bi", "co.bw", "co.ci", "co.ck", \
+"co.cr", "co.gg", "co.gl", "co.gy", "co.hu", "co.id", "co.il", "co.im", \
+"co.in", "co.it", "co.je", "co.jp", "co.ke", "co.kr", "co.ls", "co.ma", \
+"co.mu", "co.mw", "co.mz", "co.nz", "co.pn", "co.rs", "co.th", "co.tt", \
+"co.tz", "co.ua", "co.ug", "co.uk", "co.uz", "co.ve", "co.vi", "co.za", \
+"co.zm", "co.zw", "com", "com.af", "com.ag", "com.ai", "com.ar", "com.au", \
+"com.az", "com.bd", "com.bh", "com.bi", "com.bn", "com.bo", "com.br", \
+"com.bs", "com.by", "com.bz", "com.cn", "com.co", "com.cu", "com.cy", \
+"com.do", "com.dz", "com.ec", "com.eg", "com.er", "com.et", "com.fj", \
+"com.ge", "com.gh", "com.gi", "com.gl", "com.gp", "com.gr", "com.gt", \
+"com.gy", "com.hk", "com.hn", "com.hr", "com.ht", "com.iq", "com.jm", \
+"com.jo", "com.kg", "com.kh", "com.ki", "com.kw", "com.kz", "com.lb", \
+"com.lc", "com.lk", "com.lv", "com.ly", "com.mk", "com.mm", "com.mt", \
+"com.mu", "com.mw", "com.mx", "com.my", "com.na", "com.nc", "com.nf", \
+"com.ng", "com.ni", "com.np", "com.nr", "com.om", "com.pa", "com.pe", \
+"com.pg", "com.ph", "com.pk", "com.pl", "com.pr", "com.ps", "com.pt", \
+"com.py", "com.qa", "com.ru", "com.sa", "com.sb", "com.sc", "com.sg", \
+"com.sl", "com.sv", "com.tj", "com.tm", "com.tn", "com.tr", "com.tt", \
+"com.tw", "com.ua", "com.uy", "com.uz", "com.vc", "com.ve", "com.vi", \
+"com.vn", "com.ws", "cv", "cx", "cz", "de", "dj", "dk", "dm", "do", "dz", \
+"ec", "ee", "es", "eu", "fi", "fm", "fr", "ga", "gd", "ge", "gf", "gg", "gl", \
+"gm", "gp", "gr", "gw", "gy", "hk", "hn", "hr", "ht", "hu", "ie", "im", "in", \
+"in.rs", "info", "io", "iq", "is", "it", "it.ao", "je", "jo", "jobs", "jp", \
+"kg", "ki", "kids.us", "km", "kn", "kr", "kz", "la", "li", "lk", "lt", "lu", \
+"lv", "ma", "md", "me", "mg", "mh", "mk", "ml", "mn", "mobi", "mr", "ms", \
+"mu", "mv", "mw", "mx", "name", "ne", "ne.jp", "net", "net.in", "net.nz", \
+"nf", "ng", "nl", "no", "nom.es", "nr", "nu", "off.ai", "org", "org.af", \
+"org.es", "org.in", "org.nz", "org.uk", "pf", "ph", "pk", "pl", "pn", "pr", \
+"pro", "ps", "pt", "qa", "re", "ro", "rs", "ru", "rw", "sc", "se", "sg", "sh", \
+"si", "sk", "sl", "sm", "sn", "so", "sr", "st", "sz", "td", "tel", "tg", "tk", \
+"tl", "tm", "tn", "to", "tt", "tv", "tw", "ua", "ug", "us", "uz", "vc", "vg", \
+"vn", "vu", "ws", "yt"
+
+#endif // COMPONENTS_GOOGLE_CORE_BROWSER_GOOGLE_TLD_LIST_H_
+
diff --git a/chromium/components/google/core/browser/google_url_tracker.h b/chromium/components/google/core/browser/google_url_tracker.h
index 05271526d41..27496a6af79 100644
--- a/chromium/components/google/core/browser/google_url_tracker.h
+++ b/chromium/components/google/core/browser/google_url_tracker.h
@@ -18,12 +18,6 @@
#include "net/url_request/url_fetcher_delegate.h"
#include "url/gurl.h"
-class PrefService;
-
-namespace infobars {
-class InfoBar;
-}
-
namespace user_prefs {
class PrefRegistrySyncable;
}
diff --git a/chromium/components/google/core/browser/google_util.cc b/chromium/components/google/core/browser/google_util.cc
index f3e8fd432b8..a16d55eaafd 100644
--- a/chromium/components/google/core/browser/google_util.cc
+++ b/chromium/components/google/core/browser/google_util.cc
@@ -17,6 +17,7 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/google/core/browser/google_switches.h"
+#include "components/google/core/browser/google_tld_list.h"
#include "components/google/core/browser/google_url_tracker.h"
#include "components/url_formatter/url_fixer.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
@@ -46,13 +47,16 @@ bool IsPathHomePageBase(base::StringPiece path) {
// True if the given canonical |host| is "[www.]<domain_in_lower_case>.<TLD>"
// with a valid TLD. If |subdomain_permission| is ALLOW_SUBDOMAIN, we check
-// against host "*.<domain_in_lower_case>.<TLD>" instead.
+// against host "*.<domain_in_lower_case>.<TLD>" instead. Will return the TLD
+// string in |tld|, if specified and the |host| can be parsed.
bool IsValidHostName(base::StringPiece host,
base::StringPiece domain_in_lower_case,
- SubdomainPermission subdomain_permission) {
+ SubdomainPermission subdomain_permission,
+ base::StringPiece* tld) {
// Fast path to avoid searching the registry set.
if (host.find(domain_in_lower_case) == base::StringPiece::npos)
return false;
+
size_t tld_length =
net::registry_controlled_domains::GetCanonicalHostRegistryLength(
host, net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
@@ -63,6 +67,10 @@ bool IsValidHostName(base::StringPiece host,
// Removes the tld and the preceding dot.
base::StringPiece host_minus_tld =
host.substr(0, host.length() - tld_length - 1);
+
+ if (tld)
+ *tld = host.substr(host.length() - tld_length);
+
if (base::LowerCaseEqualsASCII(host_minus_tld, domain_in_lower_case))
return true;
@@ -92,7 +100,13 @@ bool IsCanonicalHostGoogleHostname(base::StringPiece canonical_host,
if (base_url.is_valid() && (canonical_host == base_url.host_piece()))
return true;
- return IsValidHostName(canonical_host, "google", subdomain_permission);
+ base::StringPiece tld;
+ if (!IsValidHostName(canonical_host, "google", subdomain_permission, &tld))
+ return false;
+
+ CR_DEFINE_STATIC_LOCAL(std::set<std::string>, google_tlds,
+ ({GOOGLE_TLD_LIST}));
+ return base::ContainsKey(google_tlds, tld.as_string());
}
} // namespace
@@ -233,7 +247,8 @@ bool IsYoutubeDomainUrl(const GURL& url,
SubdomainPermission subdomain_permission,
PortPermission port_permission) {
return IsValidURL(url, port_permission) &&
- IsValidHostName(url.host_piece(), "youtube", subdomain_permission);
+ IsValidHostName(url.host_piece(), "youtube", subdomain_permission,
+ nullptr);
}
} // namespace google_util
diff --git a/chromium/components/google/core/browser/google_util_unittest.cc b/chromium/components/google/core/browser/google_util_unittest.cc
index f5bf1ab2d5c..d30d03b56a5 100644
--- a/chromium/components/google/core/browser/google_util_unittest.cc
+++ b/chromium/components/google/core/browser/google_util_unittest.cc
@@ -239,7 +239,7 @@ TEST(GoogleUtilTest, GoogleDomains) {
EXPECT_TRUE(IsGoogleDomainUrl(GURL("http://www.google.ca"),
google_util::ALLOW_SUBDOMAIN,
google_util::DISALLOW_NON_STANDARD_PORTS));
- EXPECT_TRUE(IsGoogleDomainUrl(GURL("http://www.google.biz.tj"),
+ EXPECT_TRUE(IsGoogleDomainUrl(GURL("http://www.google.off.ai"),
google_util::ALLOW_SUBDOMAIN,
google_util::DISALLOW_NON_STANDARD_PORTS));
EXPECT_TRUE(IsGoogleDomainUrl(GURL("http://www.google.com/search?q=thing"),
@@ -249,16 +249,22 @@ TEST(GoogleUtilTest, GoogleDomains) {
google_util::ALLOW_SUBDOMAIN,
google_util::DISALLOW_NON_STANDARD_PORTS));
- // Test some bad Google domains (invalid TLDs).
+ // Test some bad Google domains (invalid/non-Google TLDs).
EXPECT_FALSE(IsGoogleDomainUrl(GURL("http://www.google.notrealtld"),
google_util::ALLOW_SUBDOMAIN,
google_util::DISALLOW_NON_STANDARD_PORTS));
+ EXPECT_FALSE(IsGoogleDomainUrl(GURL("http://www.google.sd"),
+ google_util::ALLOW_SUBDOMAIN,
+ google_util::DISALLOW_NON_STANDARD_PORTS));
EXPECT_FALSE(IsGoogleDomainUrl(GURL("http://www.google.faketld/search?q=q"),
google_util::ALLOW_SUBDOMAIN,
google_util::DISALLOW_NON_STANDARD_PORTS));
EXPECT_FALSE(IsGoogleDomainUrl(GURL("http://www.yahoo.com"),
google_util::ALLOW_SUBDOMAIN,
google_util::DISALLOW_NON_STANDARD_PORTS));
+ EXPECT_FALSE(IsGoogleDomainUrl(GURL("http://www.google.biz.tj"),
+ google_util::ALLOW_SUBDOMAIN,
+ google_util::DISALLOW_NON_STANDARD_PORTS));
// Test subdomain checks.
EXPECT_TRUE(IsGoogleDomainUrl(GURL("http://images.google.com"),
diff --git a/chromium/components/grpc_support/bidirectional_stream_unittest.cc b/chromium/components/grpc_support/bidirectional_stream_unittest.cc
index 3ef83ffe9c1..84e5cb6c50c 100644
--- a/chromium/components/grpc_support/bidirectional_stream_unittest.cc
+++ b/chromium/components/grpc_support/bidirectional_stream_unittest.cc
@@ -555,7 +555,8 @@ TEST_P(BidirectionalStreamTest, StreamFailBeforeReadIsExecutedOnNetworkThread) {
"POST", &kTestHeadersArray, false);
test.BlockForDone();
ASSERT_EQ(TestBidirectionalStreamCallback::ON_FAILED, test.response_step);
- ASSERT_EQ(net::ERR_QUIC_PROTOCOL_ERROR, test.net_error);
+ ASSERT_TRUE(test.net_error == net::ERR_QUIC_PROTOCOL_ERROR ||
+ test.net_error == net::ERR_CONNECTION_REFUSED);
bidirectional_stream_destroy(test.stream);
}
diff --git a/chromium/components/guest_view/browser/guest_view_base.cc b/chromium/components/guest_view/browser/guest_view_base.cc
index 2d52bbf4fc3..98a25805dc9 100644
--- a/chromium/components/guest_view/browser/guest_view_base.cc
+++ b/chromium/components/guest_view/browser/guest_view_base.cc
@@ -689,9 +689,9 @@ bool GuestViewBase::ShouldFocusPageAfterCrash() {
bool GuestViewBase::PreHandleGestureEvent(WebContents* source,
const blink::WebGestureEvent& event) {
- return event.type == blink::WebGestureEvent::GesturePinchBegin ||
- event.type == blink::WebGestureEvent::GesturePinchUpdate ||
- event.type == blink::WebGestureEvent::GesturePinchEnd;
+ return event.type() == blink::WebGestureEvent::GesturePinchBegin ||
+ event.type() == blink::WebGestureEvent::GesturePinchUpdate ||
+ event.type() == blink::WebGestureEvent::GesturePinchEnd;
}
void GuestViewBase::UpdatePreferredSize(WebContents* target_web_contents,
diff --git a/chromium/components/guest_view/browser/guest_view_base.h b/chromium/components/guest_view/browser/guest_view_base.h
index 3e6eac1ede3..6c58eeff981 100644
--- a/chromium/components/guest_view/browser/guest_view_base.h
+++ b/chromium/components/guest_view/browser/guest_view_base.h
@@ -20,8 +20,6 @@
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
-struct RendererContentSettingRules;
-
namespace guest_view {
class GuestViewEvent;
diff --git a/chromium/components/guest_view/browser/guest_view_message_filter.h b/chromium/components/guest_view/browser/guest_view_message_filter.h
index 051bfc762ed..7c271d8a98c 100644
--- a/chromium/components/guest_view/browser/guest_view_message_filter.h
+++ b/chromium/components/guest_view/browser/guest_view_message_filter.h
@@ -20,11 +20,6 @@ class DictionaryValue;
namespace content {
class BrowserContext;
-class WebContents;
-}
-
-namespace gfx {
-class Size;
}
namespace guest_view {
diff --git a/chromium/components/guest_view/renderer/guest_view_container.h b/chromium/components/guest_view/renderer/guest_view_container.h
index e8f53d82df4..7025d95908f 100644
--- a/chromium/components/guest_view/renderer/guest_view_container.h
+++ b/chromium/components/guest_view/renderer/guest_view_container.h
@@ -66,6 +66,7 @@ class GuestViewContainer : public content::BrowserPluginDelegate {
// BrowserPluginGuestDelegate public implementation.
void SetElementInstanceID(int element_instance_id) final;
void DidResizeElement(const gfx::Size& new_size) override;
+ base::WeakPtr<BrowserPluginDelegate> GetWeakPtr() final;
protected:
~GuestViewContainer() override;
@@ -89,7 +90,6 @@ class GuestViewContainer : public content::BrowserPluginDelegate {
// BrowserPluginDelegate implementation.
void Ready() final;
void DidDestroyElement() final;
- base::WeakPtr<BrowserPluginDelegate> GetWeakPtr() final;
int element_instance_id_;
content::RenderFrame* render_frame_;
diff --git a/chromium/components/guest_view/renderer/guest_view_container_dispatcher.h b/chromium/components/guest_view/renderer/guest_view_container_dispatcher.h
index 8b6b3277d24..4c19da141fb 100644
--- a/chromium/components/guest_view/renderer/guest_view_container_dispatcher.h
+++ b/chromium/components/guest_view/renderer/guest_view_container_dispatcher.h
@@ -6,7 +6,6 @@
#define COMPONENTS_GUEST_VIEW_RENDERER_GUEST_VIEW_CONTAINER_DISPATCHER_H_
#include "base/macros.h"
-#include "base/memory/linked_ptr.h"
#include "content/public/renderer/render_thread_observer.h"
#include "ipc/ipc_message.h"
diff --git a/chromium/components/handoff/handoff_manager.h b/chromium/components/handoff/handoff_manager.h
index 19bde22ee54..fedaf9b9572 100644
--- a/chromium/components/handoff/handoff_manager.h
+++ b/chromium/components/handoff/handoff_manager.h
@@ -5,9 +5,8 @@
#ifndef COMPONENTS_HANDOFF_HANDOFF_MANAGER_H_
#define COMPONENTS_HANDOFF_HANDOFF_MANAGER_H_
-#include <Foundation/Foundation.h>
+#import <Foundation/Foundation.h>
-#include "base/mac/objc_property_releaser.h"
#include "build/build_config.h"
#include "components/handoff/handoff_utility.h"
#include "url/gurl.h"
@@ -22,14 +21,7 @@ class PrefRegistrySyncable;
// Maintains all of the state relevant to the Handoff feature. Allows Chrome to
// hand off the current active URL to other devices.
-@interface HandoffManager : NSObject {
- @private
- base::mac::ObjCPropertyReleaser _propertyReleaser_HandoffManager;
-
- GURL _activeURL;
- NSUserActivity* _userActivity;
- handoff::Origin _origin;
-}
+@interface HandoffManager : NSObject
#if defined(OS_IOS)
// Registers preferences related to Handoff.
diff --git a/chromium/components/handoff/handoff_manager.mm b/chromium/components/handoff/handoff_manager.mm
index e7816ad5a5e..cdcb8f16901 100644
--- a/chromium/components/handoff/handoff_manager.mm
+++ b/chromium/components/handoff/handoff_manager.mm
@@ -5,6 +5,7 @@
#include "components/handoff/handoff_manager.h"
#include "base/logging.h"
+#include "base/mac/objc_property_releaser.h"
#include "base/mac/scoped_nsobject.h"
#include "net/base/mac/url_conversions.h"
@@ -32,7 +33,12 @@
@end
-@implementation HandoffManager
+@implementation HandoffManager {
+ base::mac::ObjCPropertyReleaser _propertyReleaser_HandoffManager;
+ GURL _activeURL;
+ NSUserActivity* _userActivity;
+ handoff::Origin _origin;
+}
@synthesize userActivity = _userActivity;
diff --git a/chromium/components/history/core/browser/BUILD.gn b/chromium/components/history/core/browser/BUILD.gn
index b3406b54a44..03e359e9289 100644
--- a/chromium/components/history/core/browser/BUILD.gn
+++ b/chromium/components/history/core/browser/BUILD.gn
@@ -100,6 +100,7 @@ static_library("browser") {
"//components/signin/core/browser",
"//components/sync",
"//components/url_formatter",
+ "//components/variations",
"//components/version_info",
"//google_apis",
"//net",
diff --git a/chromium/components/history/core/browser/DEPS b/chromium/components/history/core/browser/DEPS
new file mode 100644
index 00000000000..80f6f908e13
--- /dev/null
+++ b/chromium/components/history/core/browser/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/variations",
+]
diff --git a/chromium/components/history/core/browser/history_backend.cc b/chromium/components/history/core/browser/history_backend.cc
index 1565486d534..08cacf54212 100644
--- a/chromium/components/history/core/browser/history_backend.cc
+++ b/chromium/components/history/core/browser/history_backend.cc
@@ -1168,11 +1168,16 @@ void HistoryBackend::QueryDownloads(std::vector<DownloadRow>* rows) {
}
// Update a particular download entry.
-void HistoryBackend::UpdateDownload(const DownloadRow& data) {
+void HistoryBackend::UpdateDownload(
+ const DownloadRow& data,
+ bool should_commit_immediately) {
if (!db_)
return;
db_->UpdateDownload(data);
- ScheduleCommit();
+ if (should_commit_immediately)
+ Commit();
+ else
+ ScheduleCommit();
}
bool HistoryBackend::CreateDownload(const DownloadRow& history_info) {
diff --git a/chromium/components/history/core/browser/history_backend.h b/chromium/components/history/core/browser/history_backend.h
index 130d9b7b614..6959994ab1a 100644
--- a/chromium/components/history/core/browser/history_backend.h
+++ b/chromium/components/history/core/browser/history_backend.h
@@ -34,11 +34,8 @@
#include "components/history/core/browser/visit_tracker.h"
#include "sql/init_status.h"
-class HistoryURLProvider;
-struct HistoryURLProviderParams;
class SkBitmap;
class TestingProfile;
-struct ThumbnailScore;
namespace base {
class SingleThreadTaskRunner;
@@ -53,11 +50,11 @@ class HistoryBackendObserver;
class HistoryBackendTest;
class HistoryDatabase;
struct HistoryDatabaseParams;
-struct HistoryDetails;
class HistoryDBTask;
class InMemoryHistoryBackend;
class TypedUrlSyncableService;
class HistoryBackendHelper;
+class URLDatabase;
// The maximum number of icons URLs per page which can be stored in the
// thumbnail database.
@@ -337,7 +334,7 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
uint32_t GetNextDownloadId();
void QueryDownloads(std::vector<DownloadRow>* rows);
- void UpdateDownload(const DownloadRow& data);
+ void UpdateDownload(const DownloadRow& data, bool should_commit_immediately);
bool CreateDownload(const DownloadRow& history_info);
void RemoveDownloads(const std::set<uint32_t>& ids);
diff --git a/chromium/components/history/core/browser/history_client.h b/chromium/components/history/core/browser/history_client.h
index 4d6b7e522b9..4a5b53371c0 100644
--- a/chromium/components/history/core/browser/history_client.h
+++ b/chromium/components/history/core/browser/history_client.h
@@ -12,17 +12,10 @@
class GURL;
-namespace base {
-class FilePath;
-}
-
namespace history {
-class HistoryBackend;
class HistoryBackendClient;
-class HistoryDatabase;
class HistoryService;
-class ThumbnailDatabase;
// This class abstracts operations that depend on the embedder's environment,
// e.g. Chrome.
diff --git a/chromium/components/history/core/browser/history_database.h b/chromium/components/history/core/browser/history_database.h
index b1b490cf198..9a26ae98194 100644
--- a/chromium/components/history/core/browser/history_database.h
+++ b/chromium/components/history/core/browser/history_database.h
@@ -29,7 +29,6 @@ namespace base {
class FilePath;
}
-class HistoryQuickProviderTest;
class InMemoryURLIndexTest;
namespace history {
diff --git a/chromium/components/history/core/browser/history_delete_directives_data_type_controller.cc b/chromium/components/history/core/browser/history_delete_directives_data_type_controller.cc
index f067ab9a636..f7a40626a4b 100644
--- a/chromium/components/history/core/browser/history_delete_directives_data_type_controller.cc
+++ b/chromium/components/history/core/browser/history_delete_directives_data_type_controller.cc
@@ -4,6 +4,7 @@
#include "components/history/core/browser/history_delete_directives_data_type_controller.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "components/sync/driver/sync_client.h"
#include "components/sync/driver/sync_service.h"
@@ -12,9 +13,11 @@ namespace browser_sync {
HistoryDeleteDirectivesDataTypeController::
HistoryDeleteDirectivesDataTypeController(const base::Closure& dump_stack,
syncer::SyncClient* sync_client)
- : syncer::UIDataTypeController(syncer::HISTORY_DELETE_DIRECTIVES,
- dump_stack,
- sync_client),
+ : syncer::AsyncDirectoryTypeController(syncer::HISTORY_DELETE_DIRECTIVES,
+ dump_stack,
+ sync_client,
+ syncer::GROUP_UI,
+ base::ThreadTaskRunnerHandle::Get()),
sync_client_(sync_client) {}
HistoryDeleteDirectivesDataTypeController::
diff --git a/chromium/components/history/core/browser/history_delete_directives_data_type_controller.h b/chromium/components/history/core/browser/history_delete_directives_data_type_controller.h
index 07016e763b6..a000181eec4 100644
--- a/chromium/components/history/core/browser/history_delete_directives_data_type_controller.h
+++ b/chromium/components/history/core/browser/history_delete_directives_data_type_controller.h
@@ -7,15 +7,15 @@
#include "base/macros.h"
#include "components/sync/device_info/local_device_info_provider.h"
+#include "components/sync/driver/async_directory_type_controller.h"
#include "components/sync/driver/sync_service_observer.h"
-#include "components/sync/driver/ui_data_type_controller.h"
namespace browser_sync {
// A controller for delete directives, which cannot sync when full encryption
// is enabled.
class HistoryDeleteDirectivesDataTypeController
- : public syncer::UIDataTypeController,
+ : public syncer::AsyncDirectoryTypeController,
public syncer::SyncServiceObserver {
public:
// |dump_stack| is called when an unrecoverable error occurs.
@@ -23,7 +23,7 @@ class HistoryDeleteDirectivesDataTypeController
syncer::SyncClient* sync_client);
~HistoryDeleteDirectivesDataTypeController() override;
- // UIDataTypeController override.
+ // AsyncDirectoryTypeController override.
bool ReadyForStart() const override;
bool StartModels() override;
void StopModels() override;
diff --git a/chromium/components/history/core/browser/history_service.cc b/chromium/components/history/core/browser/history_service.cc
index a97677666b6..b154b3237e1 100644
--- a/chromium/components/history/core/browser/history_service.cc
+++ b/chromium/components/history/core/browser/history_service.cc
@@ -28,6 +28,8 @@
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
+#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
@@ -49,6 +51,7 @@
#include "components/history/core/browser/web_history_service.h"
#include "components/history/core/common/thumbnail_score.h"
#include "components/sync/model/sync_error_factory.h"
+#include "components/variations/variations_associated_data.h"
#include "third_party/skia/include/core/SkBitmap.h"
#if defined(OS_IOS)
@@ -182,19 +185,15 @@ class HistoryService::BackendDelegate : public HistoryBackend::Delegate {
const scoped_refptr<base::SequencedTaskRunner> service_task_runner_;
};
-// The history thread is intentionally not a BrowserThread because the
-// sync integration unit tests depend on being able to create more than one
-// history thread.
-HistoryService::HistoryService()
- : thread_(new base::Thread(kHistoryThreadName)),
- history_client_(nullptr),
- backend_loaded_(false),
- weak_ptr_factory_(this) {
-}
+HistoryService::HistoryService() : HistoryService(nullptr, nullptr) {}
HistoryService::HistoryService(std::unique_ptr<HistoryClient> history_client,
std::unique_ptr<VisitDelegate> visit_delegate)
- : thread_(new base::Thread(kHistoryThreadName)),
+ : thread_(variations::GetVariationParamValue("BrowserScheduler",
+ "RedirectHistoryService") ==
+ "true"
+ ? nullptr
+ : new base::Thread(kHistoryThreadName)),
history_client_(std::move(history_client)),
visit_delegate_(std::move(visit_delegate)),
backend_loaded_(false),
@@ -213,7 +212,7 @@ bool HistoryService::BackendLoaded() {
#if defined(OS_IOS)
void HistoryService::HandleBackgrounding() {
- if (!thread_ || !history_backend_.get())
+ if (!backend_task_runner_ || !history_backend_.get())
return;
ScheduleTask(PRIORITY_NORMAL,
@@ -223,7 +222,7 @@ void HistoryService::HandleBackgrounding() {
#endif
void HistoryService::ClearCachedDataForContextID(ContextID context_id) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::Bind(&HistoryBackend::ClearCachedDataForContextID,
@@ -247,7 +246,7 @@ void HistoryService::Shutdown() {
void HistoryService::SetKeywordSearchTermsForURL(const GURL& url,
KeywordID keyword_id,
const base::string16& term) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_UI,
base::Bind(&HistoryBackend::SetKeywordSearchTermsForURL,
@@ -255,7 +254,7 @@ void HistoryService::SetKeywordSearchTermsForURL(const GURL& url,
}
void HistoryService::DeleteAllSearchTermsForKeyword(KeywordID keyword_id) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (in_memory_backend_)
@@ -267,7 +266,7 @@ void HistoryService::DeleteAllSearchTermsForKeyword(KeywordID keyword_id) {
}
void HistoryService::DeleteKeywordSearchTermForURL(const GURL& url) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_UI,
base::Bind(&HistoryBackend::DeleteKeywordSearchTermForURL,
@@ -276,7 +275,7 @@ void HistoryService::DeleteKeywordSearchTermForURL(const GURL& url) {
void HistoryService::DeleteMatchingURLsForKeyword(KeywordID keyword_id,
const base::string16& term) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_UI,
base::Bind(&HistoryBackend::DeleteMatchingURLsForKeyword,
@@ -284,7 +283,7 @@ void HistoryService::DeleteMatchingURLsForKeyword(KeywordID keyword_id,
}
void HistoryService::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::Bind(&HistoryBackend::URLsNoLongerBookmarked,
@@ -304,7 +303,7 @@ void HistoryService::RemoveObserver(HistoryServiceObserver* observer) {
base::CancelableTaskTracker::TaskId HistoryService::ScheduleDBTask(
std::unique_ptr<HistoryDBTask> task,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
base::CancelableTaskTracker::IsCanceledCallback is_canceled;
base::CancelableTaskTracker::TaskId task_id =
@@ -312,20 +311,20 @@ base::CancelableTaskTracker::TaskId HistoryService::ScheduleDBTask(
// Use base::ThreadTaskRunnerHandler::Get() to get a task runner for
// the current message loop so that we can forward the call to the method
// HistoryDBTask::DoneRunOnMainThread() in the correct thread.
- thread_->task_runner()->PostTask(
- FROM_HERE, base::Bind(&HistoryBackend::ProcessDBTask,
- history_backend_, base::Passed(&task),
+ backend_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&HistoryBackend::ProcessDBTask, history_backend_,
+ base::Passed(&task),
base::ThreadTaskRunnerHandle::Get(), is_canceled));
return task_id;
}
void HistoryService::FlushForTest(const base::Closure& flushed) {
- thread_->task_runner()->PostTaskAndReply(
- FROM_HERE, base::Bind(&base::DoNothing), flushed);
+ backend_task_runner_->PostTaskAndReply(FROM_HERE,
+ base::Bind(&base::DoNothing), flushed);
}
void HistoryService::SetOnBackendDestroyTask(const base::Closure& task) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(
PRIORITY_NORMAL,
@@ -335,10 +334,10 @@ void HistoryService::SetOnBackendDestroyTask(const base::Closure& task) {
void HistoryService::TopHosts(size_t num_hosts,
const TopHostsCallback& callback) const {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
PostTaskAndReplyWithResult(
- thread_->task_runner().get(), FROM_HERE,
+ backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::TopHosts, history_backend_, num_hosts),
callback);
}
@@ -346,10 +345,10 @@ void HistoryService::TopHosts(size_t num_hosts,
void HistoryService::GetCountsAndLastVisitForOrigins(
const std::set<GURL>& origins,
const GetCountsAndLastVisitForOriginsCallback& callback) const {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
PostTaskAndReplyWithResult(
- thread_->task_runner().get(), FROM_HERE,
+ backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::GetCountsAndLastVisitForOrigins,
history_backend_, origins),
callback);
@@ -358,12 +357,12 @@ void HistoryService::GetCountsAndLastVisitForOrigins(
void HistoryService::HostRankIfAvailable(
const GURL& url,
const base::Callback<void(int)>& callback) const {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
- PostTaskAndReplyWithResult(thread_->task_runner().get(), FROM_HERE,
- base::Bind(&HistoryBackend::HostRankIfAvailable,
- history_backend_, url),
- callback);
+ PostTaskAndReplyWithResult(
+ backend_task_runner_.get(), FROM_HERE,
+ base::Bind(&HistoryBackend::HostRankIfAvailable, history_backend_, url),
+ callback);
}
void HistoryService::AddPage(const GURL& url,
@@ -391,7 +390,7 @@ void HistoryService::AddPage(const GURL& url,
}
void HistoryService::AddPage(const HistoryAddPageArgs& add_page_args) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
// Filter out unwanted URLs. We don't add auto-subframe URLs. They are a
@@ -422,7 +421,7 @@ void HistoryService::AddPage(const HistoryAddPageArgs& add_page_args) {
void HistoryService::AddPageNoVisitForBookmark(const GURL& url,
const base::string16& title) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (history_client_ && !history_client_->CanAddURL(url))
return;
@@ -434,7 +433,7 @@ void HistoryService::AddPageNoVisitForBookmark(const GURL& url,
void HistoryService::SetPageTitle(const GURL& url,
const base::string16& title) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL, base::Bind(&HistoryBackend::SetPageTitle,
history_backend_, url, title));
@@ -444,7 +443,7 @@ void HistoryService::UpdateWithPageEndTime(ContextID context_id,
int nav_entry_id,
const GURL& url,
Time end_ts) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(
PRIORITY_NORMAL,
@@ -459,7 +458,7 @@ void HistoryService::AddPageWithDetails(const GURL& url,
Time last_visit,
bool hidden,
VisitSource visit_source) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
// Filter out unwanted URLs.
if (history_client_ && !history_client_->CanAddURL(url))
@@ -486,7 +485,7 @@ void HistoryService::AddPageWithDetails(const GURL& url,
void HistoryService::AddPagesWithDetails(const URLRows& info,
VisitSource visit_source) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
// Inform the VisitDelegate of the URLs
@@ -510,14 +509,14 @@ base::CancelableTaskTracker::TaskId HistoryService::GetFavicons(
const favicon_base::FaviconResultsCallback& callback,
base::CancelableTaskTracker* tracker) {
TRACE_EVENT0("browser", "HistoryService::GetFavicons");
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<favicon_base::FaviconRawBitmapResult>* results =
new std::vector<favicon_base::FaviconRawBitmapResult>();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&HistoryBackend::GetFavicons, history_backend_,
- icon_urls, icon_types, desired_sizes, results),
+ backend_task_runner_.get(), FROM_HERE,
+ base::Bind(&HistoryBackend::GetFavicons, history_backend_, icon_urls,
+ icon_types, desired_sizes, results),
base::Bind(&RunWithFaviconResults, callback, base::Owned(results)));
}
@@ -528,14 +527,14 @@ base::CancelableTaskTracker::TaskId HistoryService::GetFaviconsForURL(
const favicon_base::FaviconResultsCallback& callback,
base::CancelableTaskTracker* tracker) {
TRACE_EVENT0("browser", "HistoryService::GetFaviconsForURL");
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<favicon_base::FaviconRawBitmapResult>* results =
new std::vector<favicon_base::FaviconRawBitmapResult>();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&HistoryBackend::GetFaviconsForURL, history_backend_,
- page_url, icon_types, desired_sizes, results),
+ backend_task_runner_.get(), FROM_HERE,
+ base::Bind(&HistoryBackend::GetFaviconsForURL, history_backend_, page_url,
+ icon_types, desired_sizes, results),
base::Bind(&RunWithFaviconResults, callback, base::Owned(results)));
}
@@ -545,15 +544,14 @@ base::CancelableTaskTracker::TaskId HistoryService::GetLargestFaviconForURL(
int minimum_size_in_pixels,
const favicon_base::FaviconRawBitmapCallback& callback,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
favicon_base::FaviconRawBitmapResult* result =
new favicon_base::FaviconRawBitmapResult();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&HistoryBackend::GetLargestFaviconForURL,
- history_backend_, page_url, icon_types,
- minimum_size_in_pixels, result),
+ backend_task_runner_.get(), FROM_HERE,
+ base::Bind(&HistoryBackend::GetLargestFaviconForURL, history_backend_,
+ page_url, icon_types, minimum_size_in_pixels, result),
base::Bind(&RunWithFaviconResult, callback, base::Owned(result)));
}
@@ -563,14 +561,14 @@ base::CancelableTaskTracker::TaskId HistoryService::GetFaviconForID(
const favicon_base::FaviconResultsCallback& callback,
base::CancelableTaskTracker* tracker) {
TRACE_EVENT0("browser", "HistoryService::GetFaviconForID");
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<favicon_base::FaviconRawBitmapResult>* results =
new std::vector<favicon_base::FaviconRawBitmapResult>();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&HistoryBackend::GetFaviconForID, history_backend_,
- favicon_id, desired_size, results),
+ backend_task_runner_.get(), FROM_HERE,
+ base::Bind(&HistoryBackend::GetFaviconForID, history_backend_, favicon_id,
+ desired_size, results),
base::Bind(&RunWithFaviconResults, callback, base::Owned(results)));
}
@@ -583,12 +581,12 @@ HistoryService::UpdateFaviconMappingsAndFetch(
const favicon_base::FaviconResultsCallback& callback,
base::CancelableTaskTracker* tracker) {
TRACE_EVENT0("browser", "HistoryService::UpdateFaviconMappingsAndFetch");
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<favicon_base::FaviconRawBitmapResult>* results =
new std::vector<favicon_base::FaviconRawBitmapResult>();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
+ backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::UpdateFaviconMappingsAndFetch,
history_backend_, page_url, icon_urls, icon_types,
desired_sizes, results),
@@ -602,7 +600,7 @@ void HistoryService::MergeFavicon(
scoped_refptr<base::RefCountedMemory> bitmap_data,
const gfx::Size& pixel_size) {
TRACE_EVENT0("browser", "HistoryService::MergeFavicon");
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (history_client_ && !history_client_->CanAddURL(page_url))
return;
@@ -617,7 +615,7 @@ void HistoryService::SetFavicons(const GURL& page_url,
favicon_base::IconType icon_type,
const GURL& icon_url,
const std::vector<SkBitmap>& bitmaps) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (history_client_ && !history_client_->CanAddURL(page_url))
return;
@@ -628,7 +626,7 @@ void HistoryService::SetFavicons(const GURL& page_url,
}
void HistoryService::SetFaviconsOutOfDateForPage(const GURL& page_url) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::Bind(&HistoryBackend::SetFaviconsOutOfDateForPage,
@@ -637,7 +635,7 @@ void HistoryService::SetFaviconsOutOfDateForPage(const GURL& page_url) {
void HistoryService::SetImportedFavicons(
const favicon_base::FaviconUsageDataList& favicon_usage) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::Bind(&HistoryBackend::SetImportedFavicons,
@@ -649,13 +647,13 @@ base::CancelableTaskTracker::TaskId HistoryService::QueryURL(
bool want_visits,
const QueryURLCallback& callback,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
QueryURLResult* query_url_result = new QueryURLResult();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&HistoryBackend::QueryURL, history_backend_, url,
- want_visits, base::Unretained(query_url_result)),
+ 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)));
}
@@ -667,14 +665,12 @@ base::CancelableTaskTracker::TaskId HistoryService::GetHistoryCount(
const Time& end_time,
const GetHistoryCountCallback& callback,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
return tracker->PostTaskAndReplyWithResult(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&HistoryBackend::GetHistoryCount,
- history_backend_,
- begin_time,
+ backend_task_runner_.get(), FROM_HERE,
+ base::Bind(&HistoryBackend::GetHistoryCount, history_backend_, begin_time,
end_time),
callback);
}
@@ -686,19 +682,19 @@ base::CancelableTaskTracker::TaskId HistoryService::GetHistoryCount(
void HistoryService::CreateDownload(
const DownloadRow& create_info,
const HistoryService::DownloadCreateCallback& callback) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
- PostTaskAndReplyWithResult(thread_->task_runner().get(), FROM_HERE,
+ PostTaskAndReplyWithResult(backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::CreateDownload,
history_backend_, create_info),
callback);
}
void HistoryService::GetNextDownloadId(const DownloadIdCallback& callback) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
PostTaskAndReplyWithResult(
- thread_->task_runner().get(), FROM_HERE,
+ backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::GetNextDownloadId, history_backend_),
callback);
}
@@ -706,7 +702,7 @@ void HistoryService::GetNextDownloadId(const DownloadIdCallback& callback) {
// Handle queries for a list of all downloads in the history database's
// 'downloads' table.
void HistoryService::QueryDownloads(const DownloadQueryCallback& callback) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<DownloadRow>* rows = new std::vector<DownloadRow>();
std::unique_ptr<std::vector<DownloadRow>> scoped_rows(rows);
@@ -714,7 +710,7 @@ void HistoryService::QueryDownloads(const DownloadQueryCallback& callback) {
// base::Passed(&scoped_rows) nullifies |scoped_rows|, and compilers do not
// guarantee that the first Bind's arguments are evaluated before the second
// Bind's arguments.
- thread_->task_runner()->PostTaskAndReply(
+ backend_task_runner_->PostTaskAndReply(
FROM_HERE,
base::Bind(&HistoryBackend::QueryDownloads, history_backend_, rows),
base::Bind(callback, base::Passed(&scoped_rows)));
@@ -722,15 +718,18 @@ void HistoryService::QueryDownloads(const DownloadQueryCallback& callback) {
// Handle updates for a particular download. This is a 'fire and forget'
// operation, so we don't need to be called back.
-void HistoryService::UpdateDownload(const DownloadRow& data) {
- DCHECK(thread_) << "History service being called after cleanup";
+void HistoryService::UpdateDownload(
+ const DownloadRow& data,
+ bool should_commit_immediately) {
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL, base::Bind(&HistoryBackend::UpdateDownload,
- history_backend_, data));
+ history_backend_, data,
+ should_commit_immediately));
}
void HistoryService::RemoveDownloads(const std::set<uint32_t>& ids) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL, base::Bind(&HistoryBackend::RemoveDownloads,
history_backend_, ids));
@@ -741,13 +740,13 @@ base::CancelableTaskTracker::TaskId HistoryService::QueryHistory(
const QueryOptions& options,
const QueryHistoryCallback& callback,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
QueryResults* query_results = new QueryResults();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&HistoryBackend::QueryHistory, history_backend_,
- text_query, options, base::Unretained(query_results)),
+ backend_task_runner_.get(), FROM_HERE,
+ base::Bind(&HistoryBackend::QueryHistory, history_backend_, text_query,
+ options, base::Unretained(query_results)),
base::Bind(callback, base::Owned(query_results)));
}
@@ -755,11 +754,11 @@ base::CancelableTaskTracker::TaskId HistoryService::QueryRedirectsFrom(
const GURL& from_url,
const QueryRedirectsCallback& callback,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
RedirectList* result = new RedirectList();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
+ backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::QueryRedirectsFrom, history_backend_,
from_url, base::Unretained(result)),
base::Bind(callback, base::Owned(result)));
@@ -769,13 +768,13 @@ base::CancelableTaskTracker::TaskId HistoryService::QueryRedirectsTo(
const GURL& to_url,
const QueryRedirectsCallback& callback,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
RedirectList* result = new RedirectList();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&HistoryBackend::QueryRedirectsTo, history_backend_,
- to_url, base::Unretained(result)),
+ backend_task_runner_.get(), FROM_HERE,
+ base::Bind(&HistoryBackend::QueryRedirectsTo, history_backend_, to_url,
+ base::Unretained(result)),
base::Bind(callback, base::Owned(result)));
}
@@ -783,13 +782,13 @@ base::CancelableTaskTracker::TaskId HistoryService::GetVisibleVisitCountToHost(
const GURL& url,
const GetVisibleVisitCountToHostCallback& callback,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
VisibleVisitCountToHostResult* result = new VisibleVisitCountToHostResult();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
- base::Bind(&HistoryBackend::GetVisibleVisitCountToHost,
- history_backend_, url, base::Unretained(result)),
+ backend_task_runner_.get(), FROM_HERE,
+ base::Bind(&HistoryBackend::GetVisibleVisitCountToHost, history_backend_,
+ url, base::Unretained(result)),
base::Bind(&RunWithVisibleVisitCountToHostResult, callback,
base::Owned(result)));
}
@@ -799,11 +798,11 @@ base::CancelableTaskTracker::TaskId HistoryService::QueryMostVisitedURLs(
int days_back,
const QueryMostVisitedURLsCallback& callback,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
MostVisitedURLList* result = new MostVisitedURLList();
return tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
+ backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::QueryMostVisitedURLs, history_backend_,
result_count, days_back, base::Unretained(result)),
base::Bind(callback, base::Owned(result)));
@@ -811,7 +810,7 @@ base::CancelableTaskTracker::TaskId HistoryService::QueryMostVisitedURLs(
void HistoryService::Cleanup() {
DCHECK(thread_checker_.CalledOnValidThread());
- if (!thread_) {
+ if (!backend_task_runner_) {
// We've already cleaned up.
return;
}
@@ -856,15 +855,14 @@ void HistoryService::Cleanup() {
closing_task.Reset();
HistoryBackend* raw_ptr = history_backend_.get();
history_backend_ = nullptr;
- thread_->task_runner()->ReleaseSoon(FROM_HERE, raw_ptr);
+ backend_task_runner_->ReleaseSoon(FROM_HERE, raw_ptr);
}
- // Delete the thread, which joins with the background thread. We defensively
- // nullptr the pointer before deleting it in case somebody tries to use it
- // during shutdown, but this shouldn't happen.
- base::Thread* thread = thread_;
- thread_ = nullptr;
- delete thread;
+ // Clear |backend_task_runner_| to make sure it's not used after Cleanup().
+ backend_task_runner_ = nullptr;
+
+ // Join the background thread, if any.
+ thread_.reset();
}
bool HistoryService::Init(
@@ -872,14 +870,24 @@ bool HistoryService::Init(
const HistoryDatabaseParams& history_database_params) {
TRACE_EVENT0("browser,startup", "HistoryService::Init")
SCOPED_UMA_HISTOGRAM_TIMER("History.HistoryServiceInitTime");
- DCHECK(thread_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!backend_task_runner_);
- base::Thread::Options options;
- options.timer_slack = base::TIMER_SLACK_MAXIMUM;
- if (!thread_->StartWithOptions(options)) {
- Cleanup();
- return false;
+ if (thread_) {
+ base::Thread::Options options;
+ options.timer_slack = base::TIMER_SLACK_MAXIMUM;
+ if (!thread_->StartWithOptions(options)) {
+ Cleanup();
+ return false;
+ }
+ backend_task_runner_ = thread_->task_runner();
+ } else {
+ backend_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
+ base::TaskTraits()
+ .WithPriority(base::TaskPriority::USER_BLOCKING)
+ .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN)
+ .MayBlock()
+ .WithBaseSyncPrimitives());
}
// Create the history backend.
@@ -887,7 +895,7 @@ bool HistoryService::Init(
new BackendDelegate(weak_ptr_factory_.GetWeakPtr(),
base::ThreadTaskRunnerHandle::Get()),
history_client_ ? history_client_->CreateBackendClient() : nullptr,
- thread_->task_runner()));
+ backend_task_runner_));
history_backend_.swap(backend);
ScheduleTask(PRIORITY_UI,
@@ -913,10 +921,9 @@ void HistoryService::ScheduleAutocomplete(
void HistoryService::ScheduleTask(SchedulePriority priority,
const base::Closure& task) {
DCHECK(thread_checker_.CalledOnValidThread());
- CHECK(thread_);
- CHECK(thread_->message_loop());
+ CHECK(backend_task_runner_);
// TODO(brettw): Do prioritization.
- thread_->task_runner()->PostTask(FROM_HERE, task);
+ backend_task_runner_->PostTask(FROM_HERE, task);
}
base::WeakPtr<HistoryService> HistoryService::AsWeakPtr() {
@@ -968,7 +975,7 @@ void HistoryService::SetInMemoryBackend(
std::unique_ptr<InMemoryHistoryBackend> mem_backend) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!in_memory_backend_) << "Setting mem DB twice";
- in_memory_backend_.reset(mem_backend.release());
+ in_memory_backend_ = std::move(mem_backend);
// The database requires additional initialization once we own it.
in_memory_backend_->AttachToHistoryService(this);
@@ -982,7 +989,7 @@ void HistoryService::NotifyProfileError(sql::InitStatus init_status,
}
void HistoryService::DeleteURL(const GURL& url) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
// We will update the visited links when we observe the delete notifications.
ScheduleTask(PRIORITY_NORMAL, base::Bind(&HistoryBackend::DeleteURL,
@@ -990,7 +997,7 @@ void HistoryService::DeleteURL(const GURL& url) {
}
void HistoryService::DeleteURLsForTest(const std::vector<GURL>& urls) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
// We will update the visited links when we observe the delete
// notifications.
@@ -1004,10 +1011,10 @@ void HistoryService::ExpireHistoryBetween(
Time end_time,
const base::Closure& callback,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
+ backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::ExpireHistoryBetween, history_backend_,
restrict_urls, begin_time, end_time),
callback);
@@ -1017,10 +1024,10 @@ void HistoryService::ExpireHistory(
const std::vector<ExpireHistoryArgs>& expire_list,
const base::Closure& callback,
base::CancelableTaskTracker* tracker) {
- DCHECK(thread_) << "History service being called after cleanup";
+ DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
tracker->PostTaskAndReply(
- thread_->task_runner().get(), FROM_HERE,
+ backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::ExpireHistory, history_backend_, expire_list),
callback);
}
@@ -1078,7 +1085,7 @@ void HistoryService::NotifyURLsDeleted(bool all_history,
const URLRows& deleted_rows,
const std::set<GURL>& favicon_urls) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (!thread_)
+ if (!backend_task_runner_)
return;
// Inform the VisitDelegate of the deleted URLs. We will inform the delegate
diff --git a/chromium/components/history/core/browser/history_service.h b/chromium/components/history/core/browser/history_service.h
index 43003713385..8e34834e22a 100644
--- a/chromium/components/history/core/browser/history_service.h
+++ b/chromium/components/history/core/browser/history_service.h
@@ -22,6 +22,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "base/sequenced_task_runner.h"
#include "base/strings/string16.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/threading/thread_checker.h"
@@ -41,7 +42,6 @@
class GURL;
class HistoryQuickProviderTest;
class HistoryURLProvider;
-class HistoryURLProviderTest;
class InMemoryURLIndexTest;
class SkBitmap;
class SyncBookmarkDataTypeControllerTest;
@@ -63,13 +63,11 @@ struct HistoryAddPageArgs;
class HistoryBackend;
class HistoryClient;
class HistoryDBTask;
-class HistoryDatabase;
struct HistoryDatabaseParams;
class HistoryQueryTest;
class HistoryServiceObserver;
class HistoryServiceTest;
class InMemoryHistoryBackend;
-struct KeywordSearchTermVisit;
class URLDatabase;
class VisitDelegate;
class WebHistoryService;
@@ -421,7 +419,7 @@ class HistoryService : public syncer::SyncableService, public KeyedService {
// Called to update the history service about the current state of a download.
// This is a 'fire and forget' query, so just pass the relevant state info to
// the database with no need for a callback.
- void UpdateDownload(const DownloadRow& data);
+ void UpdateDownload(const DownloadRow& data, bool should_commit_immediately);
// Permanently remove some downloads from the history system. This is a 'fire
// and forget' operation.
@@ -795,9 +793,15 @@ class HistoryService : public syncer::SyncableService, public KeyedService {
base::ThreadChecker thread_checker_;
- // The thread used by the history service to run complicated operations.
- // |thread_| is null once Cleanup() is called.
- base::Thread* thread_;
+ // The thread used by the history service to run HistoryBackend operations.
+ // Intentionally not a BrowserThread because the sync integration unit tests
+ // need to create multiple HistoryServices which each have their own thread.
+ // Nullptr if TaskScheduler is used for HistoryBackend operations.
+ std::unique_ptr<base::Thread> thread_;
+
+ // The TaskRunner to which HistoryBackend tasks are posted. Nullptr once
+ // Cleanup() is called.
+ scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
// This class has most of the implementation and runs on the 'thread_'.
// You MUST communicate with this class ONLY through the thread_'s
diff --git a/chromium/components/history/core/browser/history_types.h b/chromium/components/history/core/browser/history_types.h
index 64f34c30416..e560ed7c020 100644
--- a/chromium/components/history/core/browser/history_types.h
+++ b/chromium/components/history/core/browser/history_types.h
@@ -35,7 +35,6 @@ namespace history {
// Forward declaration for friend statements.
class HistoryBackend;
class PageUsageData;
-class URLDatabase;
// Container for a list of URLs.
typedef std::vector<GURL> RedirectList;
diff --git a/chromium/components/history/core/browser/in_memory_history_backend.h b/chromium/components/history/core/browser/in_memory_history_backend.h
index 6bcb7470cab..2462543c512 100644
--- a/chromium/components/history/core/browser/in_memory_history_backend.h
+++ b/chromium/components/history/core/browser/in_memory_history_backend.h
@@ -39,7 +39,6 @@ class HistoryBackendTestBase;
class HistoryService;
class InMemoryDatabase;
class InMemoryHistoryBackendTest;
-class URLDatabase;
class URLRow;
class InMemoryHistoryBackend : public HistoryServiceObserver {
diff --git a/chromium/components/history/core/browser/page_usage_data.h b/chromium/components/history/core/browser/page_usage_data.h
index 4ce691df9e5..4371dcb3001 100644
--- a/chromium/components/history/core/browser/page_usage_data.h
+++ b/chromium/components/history/core/browser/page_usage_data.h
@@ -9,8 +9,6 @@
#include "components/history/core/browser/history_types.h"
#include "url/gurl.h"
-class SkBitmap;
-
namespace history {
/////////////////////////////////////////////////////////////////////////////
diff --git a/chromium/components/history/core/browser/top_sites.h b/chromium/components/history/core/browser/top_sites.h
index 8e826058f3e..2b5e080f4b5 100644
--- a/chromium/components/history/core/browser/top_sites.h
+++ b/chromium/components/history/core/browser/top_sites.h
@@ -10,7 +10,6 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/observer_list.h"
-#include "base/task/cancelable_task_tracker.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/top_sites_observer.h"
#include "components/history/core/common/thumbnail_score.h"
@@ -21,14 +20,11 @@
class GURL;
namespace base {
-class FilePath;
-class RefCountedBytes;
class RefCountedMemory;
}
namespace history {
-class TopSitesCache;
class TopSitesObserver;
// PrepopulatedPage stores information for prepopulated page for the
@@ -68,12 +64,6 @@ class TopSites : public RefcountedKeyedService {
const gfx::Image& thumbnail,
const ThumbnailScore& score) = 0;
- // While testing the history system, we want to set the thumbnail to a piece
- // of static memory.
- virtual bool SetPageThumbnailToJPEGBytes(const GURL& url,
- const base::RefCountedMemory* memory,
- const ThumbnailScore& score) = 0;
-
typedef base::Callback<void(const MostVisitedURLList&)>
GetMostVisitedURLsCallback;
@@ -131,20 +121,10 @@ class TopSites : public RefcountedKeyedService {
// Clear the blacklist. Should be called from the UI thread.
virtual void ClearBlacklistedURLs() = 0;
- // Query history service for the list of available thumbnails. Returns the
- // task id for the request, or |base::CancelableTaskTracker::kBadTaskId| if a
- // request could not be made. Public only for testing purposes.
- virtual base::CancelableTaskTracker::TaskId StartQueryForMostVisited() = 0;
-
// Returns true if the given URL is known to the top sites service.
// This function also returns false if TopSites isn't loaded yet.
virtual bool IsKnownURL(const GURL& url) = 0;
- // Follows the cached redirect chain to convert any URL to its
- // canonical version. If no redirect chain is known for the URL,
- // return it without modification.
- virtual const std::string& GetCanonicalURLString(const GURL& url) const = 0;
-
// Returns true if the top sites list of non-forced URLs is full (i.e. we
// already have the maximum number of non-forced top sites). This function
// also returns false if TopSites isn't loaded yet.
diff --git a/chromium/components/history/core/browser/top_sites_database.cc b/chromium/components/history/core/browser/top_sites_database.cc
index 191b36d547d..7e9fc819a0a 100644
--- a/chromium/components/history/core/browser/top_sites_database.cc
+++ b/chromium/components/history/core/browser/top_sites_database.cc
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <utility>
+#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_macros.h"
diff --git a/chromium/components/history/core/browser/top_sites_impl.cc b/chromium/components/history/core/browser/top_sites_impl.cc
index 57e72498d65..b9ae71596b5 100644
--- a/chromium/components/history/core/browser/top_sites_impl.cc
+++ b/chromium/components/history/core/browser/top_sites_impl.cc
@@ -172,41 +172,6 @@ bool TopSitesImpl::SetPageThumbnail(const GURL& url,
return SetPageThumbnailEncoded(url, thumbnail_data.get(), score);
}
-bool TopSitesImpl::SetPageThumbnailToJPEGBytes(
- const GURL& url,
- const base::RefCountedMemory* memory,
- 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()) {
- add_temp_thumbnail = true;
- } else {
- return false; // This URL is not known to us.
- }
- }
-
- if (!can_add_url_to_history_.Run(url))
- return false; // It's not a real webpage.
-
- 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, memory, score);
- return true;
- }
-
- return SetPageThumbnailEncoded(url, memory, score);
-}
-
// WARNING: this function may be invoked on any thread.
void TopSitesImpl::GetMostVisitedURLs(
const GetMostVisitedURLsCallback& callback,
@@ -366,6 +331,79 @@ void TopSitesImpl::ClearBlacklistedURLs() {
NotifyTopSitesChanged(TopSitesObserver::ChangeReason::BLACKLIST);
}
+bool TopSitesImpl::IsKnownURL(const GURL& url) {
+ return loaded_ && cache_->IsKnownURL(url);
+}
+
+bool TopSitesImpl::IsNonForcedFull() {
+ return loaded_ && cache_->GetNumNonForcedURLs() >= kNonForcedTopSitesNumber;
+}
+
+bool TopSitesImpl::IsForcedFull() {
+ return loaded_ && cache_->GetNumForcedURLs() >= kForcedTopSitesNumber;
+}
+
+PrepopulatedPageList TopSitesImpl::GetPrepopulatedPages() {
+ return prepopulated_pages_;
+}
+
+bool TopSitesImpl::loaded() const {
+ return loaded_;
+}
+
+bool TopSitesImpl::AddForcedURL(const GURL& url, const base::Time& time) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!loaded_) {
+ // Optimally we could cache this and apply it after the load completes, but
+ // in practice it's not an issue since AddForcedURL will be called again
+ // next time the user hits the NTP.
+ return false;
+ }
+
+ size_t num_forced = cache_->GetNumForcedURLs();
+ MostVisitedURLList new_list(cache_->top_sites());
+ MostVisitedURL new_url;
+
+ if (cache_->IsKnownURL(url)) {
+ size_t index = cache_->GetURLIndex(url);
+ // Do nothing if we currently have that URL as non-forced.
+ if (new_list[index].last_forced_time.is_null())
+ return false;
+
+ // Update the |last_forced_time| of the already existing URL. Delete it and
+ // reinsert it at the right location.
+ new_url = new_list[index];
+ new_list.erase(new_list.begin() + index);
+ num_forced--;
+ } else {
+ new_url.url = url;
+ new_url.redirects.push_back(url);
+ }
+ new_url.last_forced_time = time;
+ // Add forced URLs and sort. Added to the end of the list of forced URLs
+ // since this is almost always where it needs to go, unless the user's local
+ // clock is fiddled with.
+ MostVisitedURLList::iterator mid = new_list.begin() + num_forced;
+ new_list.insert(mid, new_url);
+ mid = new_list.begin() + num_forced; // Mid was invalidated.
+ std::inplace_merge(new_list.begin(), mid, mid + 1, ForcedURLComparator);
+ SetTopSites(new_list, CALL_LOCATION_FROM_FORCED_URLS);
+ return true;
+}
+
+void TopSitesImpl::OnNavigationCommitted(const GURL& url) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!loaded_ || IsNonForcedFull())
+ return;
+
+ if (!cache_->IsKnownURL(url) && can_add_url_to_history_.Run(url)) {
+ // To avoid slamming history we throttle requests when the url updates. To
+ // do otherwise negatively impacts perf tests.
+ RestartQueryForTopSitesTimer(GetUpdateDelay());
+ }
+}
+
void TopSitesImpl::ShutdownOnUIThread() {
history_service_ = nullptr;
history_service_observer_.RemoveAll();
@@ -382,6 +420,20 @@ void TopSitesImpl::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(kMostVisitedURLsBlacklist);
}
+TopSitesImpl::~TopSitesImpl() = default;
+
+void TopSitesImpl::StartQueryForMostVisited() {
+ DCHECK(loaded_);
+ if (!history_service_)
+ return;
+
+ history_service_->QueryMostVisitedURLs(
+ num_results_to_request_from_history(), kDaysOfHistory,
+ base::Bind(&TopSitesImpl::OnTopSitesAvailableFromHistory,
+ base::Unretained(this)),
+ &cancelable_task_tracker_);
+}
+
// static
void TopSitesImpl::DiffMostVisited(const MostVisitedURLList& old_list,
const MostVisitedURLList& new_list,
@@ -444,37 +496,6 @@ void TopSitesImpl::DiffMostVisited(const MostVisitedURLList& old_list,
}
}
-base::CancelableTaskTracker::TaskId TopSitesImpl::StartQueryForMostVisited() {
- DCHECK(loaded_);
- if (!history_service_)
- return base::CancelableTaskTracker::kBadTaskId;
-
- return history_service_->QueryMostVisitedURLs(
- num_results_to_request_from_history(), kDaysOfHistory,
- base::Bind(&TopSitesImpl::OnTopSitesAvailableFromHistory,
- base::Unretained(this)),
- &cancelable_task_tracker_);
-}
-
-bool TopSitesImpl::IsKnownURL(const GURL& url) {
- return loaded_ && cache_->IsKnownURL(url);
-}
-
-const std::string& TopSitesImpl::GetCanonicalURLString(const GURL& url) const {
- return cache_->GetCanonicalURL(url).spec();
-}
-
-bool TopSitesImpl::IsNonForcedFull() {
- return loaded_ && cache_->GetNumNonForcedURLs() >= kNonForcedTopSitesNumber;
-}
-
-bool TopSitesImpl::IsForcedFull() {
- return loaded_ && cache_->GetNumForcedURLs() >= kForcedTopSitesNumber;
-}
-
-TopSitesImpl::~TopSitesImpl() {
-}
-
bool TopSitesImpl::SetPageThumbnailNoDB(
const GURL& url,
const base::RefCountedMemory* thumbnail_data,
@@ -581,67 +602,6 @@ int TopSitesImpl::GetRedirectDistanceForURL(const MostVisitedURL& most_visited,
return 0;
}
-PrepopulatedPageList TopSitesImpl::GetPrepopulatedPages() {
- return prepopulated_pages_;
-}
-
-bool TopSitesImpl::loaded() const {
- return loaded_;
-}
-
-bool TopSitesImpl::AddForcedURL(const GURL& url, const base::Time& time) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- if (!loaded_) {
- // Optimally we could cache this and apply it after the load completes, but
- // in practice it's not an issue since AddForcedURL will be called again
- // next time the user hits the NTP.
- return false;
- }
-
- size_t num_forced = cache_->GetNumForcedURLs();
- MostVisitedURLList new_list(cache_->top_sites());
- MostVisitedURL new_url;
-
- if (cache_->IsKnownURL(url)) {
- size_t index = cache_->GetURLIndex(url);
- // Do nothing if we currently have that URL as non-forced.
- if (new_list[index].last_forced_time.is_null())
- return false;
-
- // Update the |last_forced_time| of the already existing URL. Delete it and
- // reinsert it at the right location.
- new_url = new_list[index];
- new_list.erase(new_list.begin() + index);
- num_forced--;
- } else {
- new_url.url = url;
- new_url.redirects.push_back(url);
- }
- new_url.last_forced_time = time;
- // Add forced URLs and sort. Added to the end of the list of forced URLs
- // since this is almost always where it needs to go, unless the user's local
- // clock is fiddled with.
- MostVisitedURLList::iterator mid = new_list.begin() + num_forced;
- new_list.insert(mid, new_url);
- mid = new_list.begin() + num_forced; // Mid was invalidated.
- std::inplace_merge(new_list.begin(), mid, mid + 1, ForcedURLComparator);
- SetTopSites(new_list, CALL_LOCATION_FROM_FORCED_URLS);
- return true;
-}
-
-void TopSitesImpl::OnNavigationCommitted(const GURL& url) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (!loaded_ || IsNonForcedFull())
- return;
-
- if (!cache_->IsKnownURL(url) && can_add_url_to_history_.Run(url)) {
- // To avoid slamming history we throttle requests when the url updates. To
- // do otherwise negatively impacts perf tests.
- RestartQueryForTopSitesTimer(GetUpdateDelay());
- }
-}
-
bool TopSitesImpl::AddPrepopulatedPages(MostVisitedURLList* urls,
size_t num_forced_urls) {
bool added = false;
diff --git a/chromium/components/history/core/browser/top_sites_impl.h b/chromium/components/history/core/browser/top_sites_impl.h
index 338c2a5e3fa..c83cb0a883a 100644
--- a/chromium/components/history/core/browser/top_sites_impl.h
+++ b/chromium/components/history/core/browser/top_sites_impl.h
@@ -73,9 +73,6 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver {
bool SetPageThumbnail(const GURL& url,
const gfx::Image& thumbnail,
const ThumbnailScore& score) override;
- bool SetPageThumbnailToJPEGBytes(const GURL& url,
- const base::RefCountedMemory* memory,
- const ThumbnailScore& score) override;
void GetMostVisitedURLs(const GetMostVisitedURLsCallback& callback,
bool include_forced_urls) override;
bool GetPageThumbnail(const GURL& url,
@@ -90,9 +87,7 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver {
void RemoveBlacklistedURL(const GURL& url) override;
bool IsBlacklisted(const GURL& url) override;
void ClearBlacklistedURLs() override;
- base::CancelableTaskTracker::TaskId StartQueryForMostVisited() override;
bool IsKnownURL(const GURL& url) override;
- const std::string& GetCanonicalURLString(const GURL& url) const override;
bool IsNonForcedFull() override;
bool IsForcedFull() override;
PrepopulatedPageList GetPrepopulatedPages() override;
@@ -137,6 +132,8 @@ class TopSitesImpl : public TopSites, public HistoryServiceObserver {
typedef std::list<TempImage> TempImages;
typedef std::vector<PendingCallback> PendingCallbacks;
+ void StartQueryForMostVisited();
+
// Generates the diff of things that happened between "old" and "new."
//
// This treats forced URLs separately than non-forced URLs.
diff --git a/chromium/components/history/core/browser/typed_url_data_type_controller.cc b/chromium/components/history/core/browser/typed_url_data_type_controller.cc
index 8e99a0032b1..d27a4f61a7e 100644
--- a/chromium/components/history/core/browser/typed_url_data_type_controller.cc
+++ b/chromium/components/history/core/browser/typed_url_data_type_controller.cc
@@ -51,7 +51,11 @@ TypedUrlDataTypeController::TypedUrlDataTypeController(
const base::Closure& dump_stack,
syncer::SyncClient* sync_client,
const char* history_disabled_pref_name)
- : NonUIDataTypeController(syncer::TYPED_URLS, dump_stack, sync_client),
+ : AsyncDirectoryTypeController(syncer::TYPED_URLS,
+ dump_stack,
+ sync_client,
+ syncer::GROUP_HISTORY,
+ nullptr),
history_disabled_pref_name_(history_disabled_pref_name),
sync_client_(sync_client) {
pref_registrar_.Init(sync_client->GetPrefService());
@@ -62,10 +66,6 @@ TypedUrlDataTypeController::TypedUrlDataTypeController(
base::AsWeakPtr(this)));
}
-syncer::ModelSafeGroup TypedUrlDataTypeController::model_safe_group() const {
- return syncer::GROUP_HISTORY;
-}
-
bool TypedUrlDataTypeController::ReadyForStart() const {
DCHECK(CalledOnValidThread());
return !sync_client_->GetPrefService()->GetBoolean(
@@ -79,7 +79,7 @@ void TypedUrlDataTypeController::OnSavingBrowserHistoryDisabledChanged() {
// generate an unrecoverable error. This can be fixed by restarting
// Chrome (on restart, typed urls will not be a registered type).
if (state() != NOT_RUNNING && state() != STOPPING) {
- PostTaskOnBackendThread(
+ PostTaskOnModelThread(
FROM_HERE,
base::Bind(&syncer::DataTypeErrorHandler::OnUnrecoverableError,
base::Passed(CreateErrorHandler()),
@@ -90,7 +90,7 @@ void TypedUrlDataTypeController::OnSavingBrowserHistoryDisabledChanged() {
}
}
-bool TypedUrlDataTypeController::PostTaskOnBackendThread(
+bool TypedUrlDataTypeController::PostTaskOnModelThread(
const tracked_objects::Location& from_here,
const base::Closure& task) {
DCHECK(CalledOnValidThread());
diff --git a/chromium/components/history/core/browser/typed_url_data_type_controller.h b/chromium/components/history/core/browser/typed_url_data_type_controller.h
index b3bf9a36f37..2f5e1d06bc7 100644
--- a/chromium/components/history/core/browser/typed_url_data_type_controller.h
+++ b/chromium/components/history/core/browser/typed_url_data_type_controller.h
@@ -11,19 +11,13 @@
#include "base/macros.h"
#include "base/task/cancelable_task_tracker.h"
#include "components/prefs/pref_change_registrar.h"
-#include "components/sync/driver/non_ui_data_type_controller.h"
+#include "components/sync/driver/async_directory_type_controller.h"
#include "components/sync/driver/sync_api_component_factory.h"
-namespace history {
-class HistoryBackend;
-}
-
namespace browser_sync {
-class ControlTask;
-
// A class that manages the startup and shutdown of typed_url sync.
-class TypedUrlDataTypeController : public syncer::NonUIDataTypeController {
+class TypedUrlDataTypeController : public syncer::AsyncDirectoryTypeController {
public:
// |dump_stack| is called when an unrecoverable error occurs.
TypedUrlDataTypeController(const base::Closure& dump_stack,
@@ -31,14 +25,13 @@ class TypedUrlDataTypeController : public syncer::NonUIDataTypeController {
const char* history_disabled_pref_name);
~TypedUrlDataTypeController() override;
- // NonUIDataTypeController implementation
- syncer::ModelSafeGroup model_safe_group() const override;
+ // AsyncDirectoryTypeController implementation.
bool ReadyForStart() const override;
protected:
- // NonUIDataTypeController interface.
- bool PostTaskOnBackendThread(const tracked_objects::Location& from_here,
- const base::Closure& task) override;
+ // AsyncDirectoryTypeController implementation.
+ bool PostTaskOnModelThread(const tracked_objects::Location& from_here,
+ const base::Closure& task) override;
private:
void OnSavingBrowserHistoryDisabledChanged();
diff --git a/chromium/components/history/core/browser/typed_url_syncable_service.cc b/chromium/components/history/core/browser/typed_url_syncable_service.cc
index 0397d59dba9..deaf6b27d76 100644
--- a/chromium/components/history/core/browser/typed_url_syncable_service.cc
+++ b/chromium/components/history/core/browser/typed_url_syncable_service.cc
@@ -71,7 +71,7 @@ TypedUrlSyncableService::TypedUrlSyncableService(
num_db_errors_(0),
history_backend_observer_(this) {
DCHECK(history_backend_);
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(sequence_checker_.CalledOnValidSequence());
}
TypedUrlSyncableService::~TypedUrlSyncableService() {
@@ -82,7 +82,7 @@ syncer::SyncMergeResult TypedUrlSyncableService::MergeDataAndStartSyncing(
const syncer::SyncDataList& initial_sync_data,
std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
std::unique_ptr<syncer::SyncErrorFactory> error_handler) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(!sync_processor_.get());
DCHECK(sync_processor.get());
DCHECK(error_handler.get());
@@ -211,7 +211,7 @@ syncer::SyncMergeResult TypedUrlSyncableService::MergeDataAndStartSyncing(
}
void TypedUrlSyncableService::StopSyncing(syncer::ModelType type) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK_EQ(type, syncer::TYPED_URLS);
// Clear cache of server state.
@@ -227,7 +227,7 @@ void TypedUrlSyncableService::StopSyncing(syncer::ModelType type) {
syncer::SyncDataList TypedUrlSyncableService::GetAllSyncData(
syncer::ModelType type) const {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(sequence_checker_.CalledOnValidSequence());
syncer::SyncDataList list;
// TODO(sync): Add implementation
@@ -238,7 +238,7 @@ syncer::SyncDataList TypedUrlSyncableService::GetAllSyncData(
syncer::SyncError TypedUrlSyncableService::ProcessSyncChanges(
const tracked_objects::Location& from_here,
const syncer::SyncChangeList& change_list) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(sequence_checker_.CalledOnValidSequence());
std::vector<GURL> pending_deleted_urls;
history::URLRows new_synced_urls;
@@ -288,7 +288,7 @@ syncer::SyncError TypedUrlSyncableService::ProcessSyncChanges(
void TypedUrlSyncableService::OnURLsModified(
history::HistoryBackend* history_backend,
const history::URLRows& changed_urls) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(sequence_checker_.CalledOnValidSequence());
if (processing_syncer_changes_)
return; // These are changes originating from us, ignore.
@@ -318,7 +318,7 @@ void TypedUrlSyncableService::OnURLVisited(
const history::URLRow& row,
const history::RedirectList& redirects,
base::Time visit_time) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(sequence_checker_.CalledOnValidSequence());
if (processing_syncer_changes_)
return; // These are changes originating from us, ignore.
@@ -343,7 +343,7 @@ void TypedUrlSyncableService::OnURLsDeleted(
bool expired,
const history::URLRows& deleted_rows,
const std::set<GURL>& favicon_urls) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(sequence_checker_.CalledOnValidSequence());
if (processing_syncer_changes_)
return; // These are changes originating from us, ignore.
diff --git a/chromium/components/history/core/browser/typed_url_syncable_service.h b/chromium/components/history/core/browser/typed_url_syncable_service.h
index bf2f3302069..838e83efc6d 100644
--- a/chromium/components/history/core/browser/typed_url_syncable_service.h
+++ b/chromium/components/history/core/browser/typed_url_syncable_service.h
@@ -12,7 +12,7 @@
#include "base/macros.h"
#include "base/scoped_observer.h"
-#include "base/threading/thread_checker.h"
+#include "base/sequence_checker.h"
#include "components/history/core/browser/history_backend_observer.h"
#include "components/history/core/browser/history_types.h"
#include "components/sync/model/sync_change.h"
@@ -24,10 +24,6 @@
class GURL;
-namespace base {
-class MessageLoop;
-};
-
namespace sync_pb {
class TypedUrlSpecifics;
};
@@ -236,7 +232,7 @@ class TypedUrlSyncableService : public syncer::SyncableService,
int num_db_accesses_;
int num_db_errors_;
- base::ThreadChecker thread_checker_;
+ base::SequenceChecker sequence_checker_;
ScopedObserver<history::HistoryBackend, history::HistoryBackendObserver>
history_backend_observer_;
diff --git a/chromium/components/history/core/browser/web_history_service.cc b/chromium/components/history/core/browser/web_history_service.cc
index abe202c87cd..e90d3b884e4 100644
--- a/chromium/components/history/core/browser/web_history_service.cc
+++ b/chromium/components/history/core/browser/web_history_service.cc
@@ -275,11 +275,15 @@ GURL GetQueryUrl(const base::string16& text_query,
url = net::AppendQueryParameter(url, "titles", "1");
// Take |begin_time|, |end_time|, and |max_count| from the original query
- // options, and convert them to the equivalent URL parameters.
+ // options, and convert them to the equivalent URL parameters. Note that
+ // QueryOptions uses exclusive |end_time| while the history.google.com API
+ // uses it inclusively, so we subtract 1us during conversion.
base::Time end_time =
- std::min(base::Time::FromInternalValue(options.EffectiveEndTime()),
- base::Time::Now());
+ options.end_time.is_null()
+ ? base::Time::Now()
+ : std::min(options.end_time - base::TimeDelta::FromMicroseconds(1),
+ base::Time::Now());
url = net::AppendQueryParameter(url, "max", ServerTimeString(end_time));
if (!options.begin_time.is_null()) {
@@ -360,7 +364,7 @@ std::unique_ptr<base::DictionaryValue> WebHistoryService::ReadResponse(
if (request->GetResponseCode() == net::HTTP_OK) {
std::unique_ptr<base::Value> value =
base::JSONReader::Read(request->GetResponseBody());
- if (value.get() && value.get()->IsType(base::Value::TYPE_DICTIONARY))
+ if (value.get() && value.get()->IsType(base::Value::Type::DICTIONARY))
result.reset(static_cast<base::DictionaryValue*>(value.release()));
else
DLOG(WARNING) << "Non-JSON response received from history server.";
diff --git a/chromium/components/infobars/core/confirm_infobar_delegate.h b/chromium/components/infobars/core/confirm_infobar_delegate.h
index 4cbced45fd5..f76d7c19609 100644
--- a/chromium/components/infobars/core/confirm_infobar_delegate.h
+++ b/chromium/components/infobars/core/confirm_infobar_delegate.h
@@ -13,7 +13,6 @@
namespace infobars {
class InfoBar;
-class InfoBarManager;
}
// An interface derived from InfoBarDelegate implemented by objects wishing to
diff --git a/chromium/components/infobars/core/infobar_container.cc b/chromium/components/infobars/core/infobar_container.cc
index c3bd8e2a982..5013260b46c 100644
--- a/chromium/components/infobars/core/infobar_container.cc
+++ b/chromium/components/infobars/core/infobar_container.cc
@@ -123,7 +123,8 @@ void InfoBarContainer::OnInfoBarAdded(InfoBar* infobar) {
}
void InfoBarContainer::OnInfoBarRemoved(InfoBar* infobar, bool animate) {
- infobar->Hide(animate);
+ DCHECK(infobar_manager_);
+ infobar->Hide(infobar_manager_->animations_enabled() && animate);
UpdateInfoBarArrowTargetHeights();
}
@@ -154,7 +155,8 @@ void InfoBarContainer::AddInfoBar(InfoBar* infobar,
UpdateInfoBarArrowTargetHeights();
PlatformSpecificAddInfoBar(infobar, position);
infobar->set_container(this);
- infobar->Show(animate);
+ DCHECK(infobar_manager_);
+ infobar->Show(infobar_manager_->animations_enabled() && animate);
// Record the infobar being displayed.
DCHECK_NE(InfoBarDelegate::INVALID, infobar->delegate()->GetIdentifier());
diff --git a/chromium/components/infobars/core/infobar_delegate.h b/chromium/components/infobars/core/infobar_delegate.h
index 33aacada9e1..6b476a64e55 100644
--- a/chromium/components/infobars/core/infobar_delegate.h
+++ b/chromium/components/infobars/core/infobar_delegate.h
@@ -34,8 +34,6 @@ namespace translate {
class TranslateInfoBarDelegate;
}
-class SearchGeolocationDisclosureInfoBarDelegate;
-
namespace gfx {
class Image;
enum class VectorIconId;
@@ -146,6 +144,7 @@ class InfoBarDelegate {
GROUPED_PERMISSION_INFOBAR_DELEGATE_ANDROID = 70,
OFFLINE_PAGE_INFOBAR_DELEGATE = 71,
SEARCH_GEOLOCATION_DISCLOSURE_INFOBAR_DELEGATE = 72,
+ AUTOMATION_INFOBAR_DELEGATE = 73,
};
// Describes navigation events, used to decide whether infobars should be
diff --git a/chromium/components/infobars/core/infobar_manager.cc b/chromium/components/infobars/core/infobar_manager.cc
index 06d1b2861c9..1de3cbbd9cb 100644
--- a/chromium/components/infobars/core/infobar_manager.cc
+++ b/chromium/components/infobars/core/infobar_manager.cc
@@ -100,8 +100,7 @@ void InfoBarManager::RemoveObserver(Observer* obs) {
observer_list_.RemoveObserver(obs);
}
-InfoBarManager::InfoBarManager()
- : infobars_enabled_(true) {
+InfoBarManager::InfoBarManager() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableInfoBars))
infobars_enabled_ = false;
diff --git a/chromium/components/infobars/core/infobar_manager.h b/chromium/components/infobars/core/infobar_manager.h
index 72f7a743206..9097dfc2d75 100644
--- a/chromium/components/infobars/core/infobar_manager.h
+++ b/chromium/components/infobars/core/infobar_manager.h
@@ -17,10 +17,6 @@
class ConfirmInfoBarDelegate;
class GURL;
-namespace content {
-class WebContents;
-}
-
namespace infobars {
class InfoBar;
@@ -97,6 +93,8 @@ class InfoBarManager {
void AddObserver(Observer* obs);
void RemoveObserver(Observer* obs);
+ bool animations_enabled() const { return animations_enabled_; }
+
// Returns the active entry ID.
virtual int GetActiveEntryID() = 0;
@@ -108,6 +106,10 @@ class InfoBarManager {
virtual void OpenURL(const GURL& url, WindowOpenDisposition disposition) = 0;
protected:
+ void set_animations_enabled(bool animations_enabled) {
+ animations_enabled_ = animations_enabled;
+ }
+
// Notifies the observer in |observer_list_|.
// TODO(droger): Absorb these methods back into their callers once virtual
// overrides are removed (see http://crbug.com/354380).
@@ -125,7 +127,8 @@ class InfoBarManager {
void RemoveInfoBarInternal(InfoBar* infobar, bool animate);
InfoBars infobars_;
- bool infobars_enabled_;
+ bool infobars_enabled_ = true;
+ bool animations_enabled_ = true;
base::ObserverList<Observer, true> observer_list_;
diff --git a/chromium/components/invalidation/impl/BUILD.gn b/chromium/components/invalidation/impl/BUILD.gn
index 98934dd2eb1..4b782332d26 100644
--- a/chromium/components/invalidation/impl/BUILD.gn
+++ b/chromium/components/invalidation/impl/BUILD.gn
@@ -138,8 +138,8 @@ source_set("unit_tests") {
]
deps += [
"//components/gcm_driver:test_support",
- "//components/pref_registry:test_support",
"//components/signin/core/browser:test_support",
+ "//components/sync_preferences:test_support",
"//google_apis:test_support",
"//net",
]
@@ -234,6 +234,7 @@ if (is_android) {
"//base:base_java_test_support",
"//components/signin/core/browser/android:java",
"//components/sync/android:sync_java",
+ "//third_party/android_support_test_runner:runner_java",
"//third_party/cacheinvalidation:cacheinvalidation_javalib",
"//third_party/cacheinvalidation:cacheinvalidation_proto_java",
]
diff --git a/chromium/components/json_schema/json_schema_validator.cc b/chromium/components/json_schema/json_schema_validator.cc
index cf3b3dadac6..695b8c96dc8 100644
--- a/chromium/components/json_schema/json_schema_validator.cc
+++ b/chromium/components/json_schema/json_schema_validator.cc
@@ -79,25 +79,25 @@ bool IsValidSchema(const base::DictionaryValue* dict,
// binary search.
static const ExpectedType kExpectedTypes[] = {
// Note: kRef == "$ref", kSchema == "$schema"
- { schema::kRef, base::Value::TYPE_STRING },
- { schema::kSchema, base::Value::TYPE_STRING },
-
- { schema::kAdditionalProperties, base::Value::TYPE_DICTIONARY },
- { schema::kChoices, base::Value::TYPE_LIST },
- { schema::kDescription, base::Value::TYPE_STRING },
- { schema::kEnum, base::Value::TYPE_LIST },
- { schema::kId, base::Value::TYPE_STRING },
- { schema::kMaxItems, base::Value::TYPE_INTEGER },
- { schema::kMaxLength, base::Value::TYPE_INTEGER },
- { schema::kMaximum, base::Value::TYPE_DOUBLE },
- { schema::kMinItems, base::Value::TYPE_INTEGER },
- { schema::kMinLength, base::Value::TYPE_INTEGER },
- { schema::kMinimum, base::Value::TYPE_DOUBLE },
- { schema::kOptional, base::Value::TYPE_BOOLEAN },
- { schema::kPattern, base::Value::TYPE_STRING },
- { schema::kPatternProperties, base::Value::TYPE_DICTIONARY },
- { schema::kProperties, base::Value::TYPE_DICTIONARY },
- { schema::kTitle, base::Value::TYPE_STRING },
+ { schema::kRef, base::Value::Type::STRING },
+ { schema::kSchema, base::Value::Type::STRING },
+
+ { schema::kAdditionalProperties, base::Value::Type::DICTIONARY },
+ { schema::kChoices, base::Value::Type::LIST },
+ { schema::kDescription, base::Value::Type::STRING },
+ { schema::kEnum, base::Value::Type::LIST },
+ { schema::kId, base::Value::Type::STRING },
+ { schema::kMaxItems, base::Value::Type::INTEGER },
+ { schema::kMaxLength, base::Value::Type::INTEGER },
+ { schema::kMaximum, base::Value::Type::DOUBLE },
+ { schema::kMinItems, base::Value::Type::INTEGER },
+ { schema::kMinLength, base::Value::Type::INTEGER },
+ { schema::kMinimum, base::Value::Type::DOUBLE },
+ { schema::kOptional, base::Value::Type::BOOLEAN },
+ { schema::kPattern, base::Value::Type::STRING },
+ { schema::kPatternProperties, base::Value::Type::DICTIONARY },
+ { schema::kProperties, base::Value::Type::DICTIONARY },
+ { schema::kTitle, base::Value::Type::STRING },
};
bool has_type_or_ref = false;
@@ -109,14 +109,14 @@ bool IsValidSchema(const base::DictionaryValue* dict,
// Validate the "type" attribute, which may be a string or a list.
if (it.key() == schema::kType) {
switch (it.value().GetType()) {
- case base::Value::TYPE_STRING:
+ case base::Value::Type::STRING:
it.value().GetAsString(&string_value);
if (!IsValidType(string_value)) {
*error = "Invalid value for type attribute";
return false;
}
break;
- case base::Value::TYPE_LIST:
+ case base::Value::Type::LIST:
it.value().GetAsList(&list_value);
for (size_t i = 0; i < list_value->GetSize(); ++i) {
if (!list_value->GetString(i, &string_value) ||
@@ -174,16 +174,16 @@ bool IsValidSchema(const base::DictionaryValue* dict,
// Integer can be converted to double.
if (!(it.value().IsType(entry->type) ||
- (it.value().IsType(base::Value::TYPE_INTEGER) &&
- entry->type == base::Value::TYPE_DOUBLE))) {
+ (it.value().IsType(base::Value::Type::INTEGER) &&
+ entry->type == base::Value::Type::DOUBLE))) {
*error = base::StringPrintf("Invalid value for %s attribute",
it.key().c_str());
return false;
}
- // base::Value::TYPE_INTEGER attributes must be >= 0.
+ // base::Value::Type::INTEGER attributes must be >= 0.
// This applies to "minItems", "maxItems", "minLength" and "maxLength".
- if (it.value().IsType(base::Value::TYPE_INTEGER)) {
+ if (it.value().IsType(base::Value::Type::INTEGER)) {
int integer_value;
it.value().GetAsInteger(&integer_value);
if (integer_value < 0) {
@@ -251,11 +251,11 @@ bool IsValidSchema(const base::DictionaryValue* dict,
return false;
}
switch (value->GetType()) {
- case base::Value::TYPE_NULL:
- case base::Value::TYPE_BOOLEAN:
- case base::Value::TYPE_INTEGER:
- case base::Value::TYPE_DOUBLE:
- case base::Value::TYPE_STRING:
+ case base::Value::Type::NONE:
+ case base::Value::Type::BOOLEAN:
+ case base::Value::Type::INTEGER:
+ case base::Value::Type::DOUBLE:
+ case base::Value::Type::STRING:
break;
default:
*error = "Invalid value in enum attribute";
@@ -344,13 +344,13 @@ const char JSONSchemaValidator::kInvalidRegex[] =
// static
std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) {
switch (value->GetType()) {
- case base::Value::TYPE_NULL:
+ case base::Value::Type::NONE:
return schema::kNull;
- case base::Value::TYPE_BOOLEAN:
+ case base::Value::Type::BOOLEAN:
return schema::kBoolean;
- case base::Value::TYPE_INTEGER:
+ case base::Value::Type::INTEGER:
return schema::kInteger;
- case base::Value::TYPE_DOUBLE: {
+ case base::Value::Type::DOUBLE: {
double double_value = 0;
value->GetAsDouble(&double_value);
if (std::abs(double_value) <= std::pow(2.0, DBL_MANT_DIG) &&
@@ -360,11 +360,11 @@ std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) {
return schema::kNumber;
}
}
- case base::Value::TYPE_STRING:
+ case base::Value::Type::STRING:
return schema::kString;
- case base::Value::TYPE_DICTIONARY:
+ case base::Value::Type::DICTIONARY:
return schema::kObject;
- case base::Value::TYPE_LIST:
+ case base::Value::Type::LIST:
return schema::kArray;
default:
NOTREACHED() << "Unexpected value type: " << value->GetType();
@@ -507,8 +507,9 @@ void JSONSchemaValidator::Validate(const base::Value* instance,
ValidateArray(static_cast<const base::ListValue*>(instance),
schema, path);
} else if (type == schema::kString) {
- // Intentionally NOT downcasting to StringValue*. TYPE_STRING only implies
- // GetAsString() can safely be carried out, not that it's a StringValue.
+ // Intentionally NOT downcasting to StringValue*. Type::STRING only
+ // implies GetAsString() can safely be carried out, not that it's a
+ // StringValue.
ValidateString(instance, schema, path);
} else if (type == schema::kNumber || type == schema::kInteger) {
ValidateNumber(instance, schema, path);
@@ -554,17 +555,17 @@ void JSONSchemaValidator::ValidateEnum(const base::Value* instance,
NOTREACHED();
}
switch (choice->GetType()) {
- case base::Value::TYPE_NULL:
- case base::Value::TYPE_BOOLEAN:
- case base::Value::TYPE_STRING:
+ case base::Value::Type::NONE:
+ case base::Value::Type::BOOLEAN:
+ case base::Value::Type::STRING:
if (instance->Equals(choice))
return;
break;
- case base::Value::TYPE_INTEGER:
- case base::Value::TYPE_DOUBLE:
- if (instance->IsType(base::Value::TYPE_INTEGER) ||
- instance->IsType(base::Value::TYPE_DOUBLE)) {
+ case base::Value::Type::INTEGER:
+ case base::Value::Type::DOUBLE:
+ if (instance->IsType(base::Value::Type::INTEGER) ||
+ instance->IsType(base::Value::Type::DOUBLE)) {
if (GetNumberValue(choice) == GetNumberValue(instance))
return;
}
@@ -715,7 +716,7 @@ void JSONSchemaValidator::ValidateTuple(const base::ListValue* instance,
CHECK(tuple_type->GetDictionary(i, &item_schema));
const base::Value* item_value = NULL;
instance->Get(i, &item_value);
- if (item_value && item_value->GetType() != base::Value::TYPE_NULL) {
+ if (item_value && item_value->GetType() != base::Value::Type::NONE) {
Validate(item_value, item_schema, item_path);
} else {
bool is_optional = false;
diff --git a/chromium/components/json_schema/json_schema_validator.h b/chromium/components/json_schema/json_schema_validator.h
index d50d8fbc89c..f2cb841ff2f 100644
--- a/chromium/components/json_schema/json_schema_validator.h
+++ b/chromium/components/json_schema/json_schema_validator.h
@@ -15,7 +15,6 @@
namespace base {
class DictionaryValue;
class ListValue;
-class StringValue;
class Value;
}
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 a1da991f954..6442fb31d58 100644
--- a/chromium/components/json_schema/json_schema_validator_unittest_base.cc
+++ b/chromium/components/json_schema/json_schema_validator_unittest_base.cc
@@ -58,12 +58,12 @@ base::Value* LoadValue(const std::string& filename, base::Value::Type type) {
base::ListValue* LoadList(const std::string& filename) {
return static_cast<base::ListValue*>(
- LoadValue(filename, base::Value::TYPE_LIST));
+ LoadValue(filename, base::Value::Type::LIST));
}
base::DictionaryValue* LoadDictionary(const std::string& filename) {
return static_cast<base::DictionaryValue*>(
- LoadValue(filename, base::Value::TYPE_DICTIONARY));
+ LoadValue(filename, base::Value::Type::DICTIONARY));
}
} // namespace
diff --git a/chromium/components/keyed_service/core/refcounted_keyed_service.cc b/chromium/components/keyed_service/core/refcounted_keyed_service.cc
index 6bc47584c5a..04a1ea57288 100644
--- a/chromium/components/keyed_service/core/refcounted_keyed_service.cc
+++ b/chromium/components/keyed_service/core/refcounted_keyed_service.cc
@@ -4,15 +4,13 @@
#include "components/keyed_service/core/refcounted_keyed_service.h"
-#include "base/location.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include <utility>
namespace impl {
// static
void RefcountedKeyedServiceTraits::Destruct(const RefcountedKeyedService* obj) {
- if (obj->task_runner_.get() != nullptr &&
- obj->task_runner_.get() != base::ThreadTaskRunnerHandle::Get()) {
+ if (obj->task_runner_ && !obj->task_runner_->RunsTasksOnCurrentThread()) {
obj->task_runner_->DeleteSoon(FROM_HERE, obj);
} else {
delete obj;
@@ -25,9 +23,7 @@ RefcountedKeyedService::RefcountedKeyedService() : task_runner_(nullptr) {
}
RefcountedKeyedService::RefcountedKeyedService(
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
- : task_runner_(task_runner) {
-}
+ scoped_refptr<base::SequencedTaskRunner> task_runner)
+ : task_runner_(std::move(task_runner)) {}
-RefcountedKeyedService::~RefcountedKeyedService() {
-}
+RefcountedKeyedService::~RefcountedKeyedService() = default;
diff --git a/chromium/components/keyed_service/core/refcounted_keyed_service.h b/chromium/components/keyed_service/core/refcounted_keyed_service.h
index 891fc289866..1cf7f8cc977 100644
--- a/chromium/components/keyed_service/core/refcounted_keyed_service.h
+++ b/chromium/components/keyed_service/core/refcounted_keyed_service.h
@@ -6,8 +6,8 @@
#define COMPONENTS_KEYED_SERVICE_CORE_REFCOUNTED_KEYED_SERVICE_H_
#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
#include "base/sequenced_task_runner_helpers.h"
-#include "base/single_thread_task_runner.h"
#include "components/keyed_service/core/keyed_service_export.h"
class RefcountedKeyedService;
@@ -29,7 +29,7 @@ struct KEYED_SERVICE_EXPORT RefcountedKeyedServiceTraits {
// be after the corresponding BrowserContext has been destroyed.
//
// Optionally, if you initialize your service with the constructor that takes a
-// single thread task runner, your service will be deleted on that thread. We
+// SequencedTaskRunner, your service will be deleted on that sequence. We
// can't use content::DeleteOnThread<> directly because RefcountedKeyedService
// must not depend on //content.
class KEYED_SERVICE_EXPORT RefcountedKeyedService
@@ -44,19 +44,19 @@ class KEYED_SERVICE_EXPORT RefcountedKeyedService
virtual void ShutdownOnUIThread() = 0;
protected:
- // If your service does not need to be deleted on a specific thread, use the
+ // If your service does not need to be deleted on a specific sequence, use the
// default constructor.
RefcountedKeyedService();
- // If you need your service to be deleted on a specific thread (for example,
+ // If you need your service to be deleted on a specific sequence (for example,
// you're converting a service that used content::DeleteOnThread<IO>), then
- // use this constructor with a reference to the SingleThreadTaskRunner (you
+ // use this constructor with a reference to the SequencedTaskRunner (e.g., you
// can get it from content::BrowserThread::GetTaskRunnerForThread).
explicit RefcountedKeyedService(
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+ scoped_refptr<base::SequencedTaskRunner> task_runner);
// The second pass destruction can happen anywhere unless you specify which
- // thread this service must be destroyed on by using the second constructor.
+ // sequence this service must be destroyed on by using the second constructor.
virtual ~RefcountedKeyedService();
private:
@@ -65,8 +65,8 @@ class KEYED_SERVICE_EXPORT RefcountedKeyedService
friend class base::RefCountedThreadSafe<RefcountedKeyedService,
impl::RefcountedKeyedServiceTraits>;
- // Do we have to delete this object on a specific thread?
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ // Do we have to delete this object on a specific sequence?
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
};
#endif // COMPONENTS_KEYED_SERVICE_CORE_REFCOUNTED_KEYED_SERVICE_H_
diff --git a/chromium/components/leveldb/leveldb_database_impl.h b/chromium/components/leveldb/leveldb_database_impl.h
index 79d531b8ece..f621c5f5057 100644
--- a/chromium/components/leveldb/leveldb_database_impl.h
+++ b/chromium/components/leveldb/leveldb_database_impl.h
@@ -14,8 +14,6 @@
namespace leveldb {
-class MojoEnv;
-
// The backing to a database object that we pass to our called.
class LevelDBDatabaseImpl : public mojom::LevelDBDatabase {
public:
diff --git a/chromium/components/leveldb/leveldb_mojo_proxy.cc b/chromium/components/leveldb/leveldb_mojo_proxy.cc
index 67c0521d652..74446206302 100644
--- a/chromium/components/leveldb/leveldb_mojo_proxy.cc
+++ b/chromium/components/leveldb/leveldb_mojo_proxy.cc
@@ -10,7 +10,6 @@
#include "base/callback.h"
#include "base/single_thread_task_runner.h"
#include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/system/platform_handle.h"
namespace leveldb {
@@ -183,24 +182,16 @@ void LevelDBMojoProxy::OpenFileHandleImpl(OpaqueDir* dir,
std::string name,
uint32_t open_flags,
base::File* output_file) {
- mojo::ScopedHandle handle;
+ base::File file;
filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED;
bool completed =
- dir->directory->OpenFileHandle(name, open_flags, &error, &handle);
+ dir->directory->OpenFileHandle(name, open_flags, &error, &file);
DCHECK(completed);
if (error != filesystem::mojom::FileError::OK) {
*output_file = base::File(static_cast<base::File::Error>(error));
} else {
- base::PlatformFile platform_file;
- MojoResult unwrap_result = mojo::UnwrapPlatformFile(std::move(handle),
- &platform_file);
- if (unwrap_result == MOJO_RESULT_OK) {
- *output_file = base::File(platform_file);
- } else {
- NOTREACHED();
- *output_file = base::File(base::File::Error::FILE_ERROR_FAILED);
- }
+ *output_file = std::move(file);
}
}
@@ -210,7 +201,7 @@ void LevelDBMojoProxy::SyncDirectoryImpl(
filesystem::mojom::FileError* out_error) {
filesystem::mojom::DirectoryPtr target;
bool completed = dir->directory->OpenDirectory(
- name, GetProxy(&target),
+ name, MakeRequest(&target),
filesystem::mojom::kFlagRead | filesystem::mojom::kFlagWrite, out_error);
DCHECK(completed);
@@ -235,7 +226,7 @@ void LevelDBMojoProxy::GetChildrenImpl(
std::vector<std::string>* out_contents,
filesystem::mojom::FileError* out_error) {
filesystem::mojom::DirectoryPtr target;
- filesystem::mojom::DirectoryRequest proxy = GetProxy(&target);
+ filesystem::mojom::DirectoryRequest proxy(&target);
bool completed = dir->directory->OpenDirectory(
name, std::move(proxy),
filesystem::mojom::kFlagRead | filesystem::mojom::kFlagWrite, out_error);
@@ -301,7 +292,7 @@ void LevelDBMojoProxy::LockFileImpl(OpaqueDir* dir,
// Since a lock is associated with a file descriptor, we need to open and
// have a persistent file on the other side of the connection.
filesystem::mojom::FilePtr target;
- filesystem::mojom::FileRequest proxy = GetProxy(&target);
+ filesystem::mojom::FileRequest proxy(&target);
bool completed = dir->directory->OpenFile(path, std::move(proxy),
filesystem::mojom::kFlagOpenAlways |
filesystem::mojom::kFlagRead |
diff --git a/chromium/components/leveldb/leveldb_service_impl.cc b/chromium/components/leveldb/leveldb_service_impl.cc
index 6df7ad0b9cc..f2f02287271 100644
--- a/chromium/components/leveldb/leveldb_service_impl.cc
+++ b/chromium/components/leveldb/leveldb_service_impl.cc
@@ -10,7 +10,7 @@
#include "components/leveldb/env_mojo.h"
#include "components/leveldb/leveldb_database_impl.h"
#include "components/leveldb/public/cpp/util.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/strong_associated_binding.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
@@ -26,10 +26,11 @@ LevelDBServiceImpl::LevelDBServiceImpl(
LevelDBServiceImpl::~LevelDBServiceImpl() {}
-void LevelDBServiceImpl::Open(filesystem::mojom::DirectoryPtr directory,
- const std::string& dbname,
- leveldb::mojom::LevelDBDatabaseRequest database,
- const OpenCallback& callback) {
+void LevelDBServiceImpl::Open(
+ filesystem::mojom::DirectoryPtr directory,
+ const std::string& dbname,
+ leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
+ const OpenCallback& callback) {
OpenWithOptions(leveldb::mojom::OpenOptions::New(), std::move(directory),
dbname, std::move(database), callback);
}
@@ -38,7 +39,7 @@ void LevelDBServiceImpl::OpenWithOptions(
leveldb::mojom::OpenOptionsPtr open_options,
filesystem::mojom::DirectoryPtr directory,
const std::string& dbname,
- leveldb::mojom::LevelDBDatabaseRequest database,
+ leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
const OpenCallback& callback) {
leveldb::Options options;
options.create_if_missing = open_options->create_if_missing;
@@ -61,16 +62,17 @@ void LevelDBServiceImpl::OpenWithOptions(
leveldb::Status s = leveldb::DB::Open(options, dbname, &db);
if (s.ok()) {
- mojo::MakeStrongBinding(base::MakeUnique<LevelDBDatabaseImpl>(
- std::move(env_mojo), base::WrapUnique(db)),
- std::move(database));
+ mojo::MakeStrongAssociatedBinding(
+ base::MakeUnique<LevelDBDatabaseImpl>(std::move(env_mojo),
+ base::WrapUnique(db)),
+ std::move(database));
}
callback.Run(LeveldbStatusToError(s));
}
void LevelDBServiceImpl::OpenInMemory(
- leveldb::mojom::LevelDBDatabaseRequest database,
+ leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
const OpenCallback& callback) {
leveldb::Options options;
options.create_if_missing = true;
@@ -84,12 +86,24 @@ void LevelDBServiceImpl::OpenInMemory(
leveldb::Status s = leveldb::DB::Open(options, "", &db);
if (s.ok()) {
- mojo::MakeStrongBinding(base::MakeUnique<LevelDBDatabaseImpl>(
- std::move(env), base::WrapUnique(db)),
- std::move(database));
+ mojo::MakeStrongAssociatedBinding(base::MakeUnique<LevelDBDatabaseImpl>(
+ std::move(env), base::WrapUnique(db)),
+ std::move(database));
}
callback.Run(LeveldbStatusToError(s));
}
+void LevelDBServiceImpl::Destroy(filesystem::mojom::DirectoryPtr directory,
+ const std::string& dbname,
+ const DestroyCallback& callback) {
+ leveldb::Options options;
+ // Register our directory with the file thread.
+ LevelDBMojoProxy::OpaqueDir* dir =
+ thread_->RegisterDirectory(std::move(directory));
+ std::unique_ptr<MojoEnv> env_mojo(new MojoEnv(thread_, dir));
+ options.env = env_mojo.get();
+ callback.Run(LeveldbStatusToError(leveldb::DestroyDB(dbname, options)));
+}
+
} // namespace leveldb
diff --git a/chromium/components/leveldb/leveldb_service_impl.h b/chromium/components/leveldb/leveldb_service_impl.h
index bf413dbfa5b..a9f5befff73 100644
--- a/chromium/components/leveldb/leveldb_service_impl.h
+++ b/chromium/components/leveldb/leveldb_service_impl.h
@@ -21,15 +21,19 @@ class LevelDBServiceImpl : public mojom::LevelDBService {
// Overridden from LevelDBService:
void Open(filesystem::mojom::DirectoryPtr directory,
const std::string& dbname,
- leveldb::mojom::LevelDBDatabaseRequest database,
+ leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
const OpenCallback& callback) override;
- void OpenWithOptions(leveldb::mojom::OpenOptionsPtr open_options,
- filesystem::mojom::DirectoryPtr directory,
- const std::string& dbname,
- leveldb::mojom::LevelDBDatabaseRequest database,
- const OpenCallback& callback) override;
- void OpenInMemory(leveldb::mojom::LevelDBDatabaseRequest database,
+ void OpenWithOptions(
+ leveldb::mojom::OpenOptionsPtr open_options,
+ filesystem::mojom::DirectoryPtr directory,
+ const std::string& dbname,
+ leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
+ const OpenCallback& callback) override;
+ void OpenInMemory(leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
const OpenInMemoryCallback& callback) override;
+ void Destroy(filesystem::mojom::DirectoryPtr directory,
+ const std::string& dbname,
+ const DestroyCallback& callback) override;
private:
// Thread to own the mojo message pipe. Because leveldb spawns multiple
diff --git a/chromium/components/leveldb/leveldb_service_unittest.cc b/chromium/components/leveldb/leveldb_service_unittest.cc
index 05e38d92b0b..28f37da1c2f 100644
--- a/chromium/components/leveldb/leveldb_service_unittest.cc
+++ b/chromium/components/leveldb/leveldb_service_unittest.cc
@@ -95,7 +95,7 @@ void DatabaseSyncDeletePrefixed(mojom::LevelDBDatabase* database,
}
void LevelDBSyncOpenInMemory(mojom::LevelDBService* leveldb,
- mojom::LevelDBDatabaseRequest database,
+ mojom::LevelDBDatabaseAssociatedRequest database,
mojom::DatabaseError* out_error) {
base::RunLoop run_loop;
leveldb->OpenInMemory(std::move(database),
@@ -112,8 +112,8 @@ class LevelDBServiceTest : public service_manager::test::ServiceTest {
// Overridden from mojo::test::ApplicationTestBase:
void SetUp() override {
ServiceTest::SetUp();
- connector()->ConnectToInterface("filesystem", &files_);
- connector()->ConnectToInterface("leveldb", &leveldb_);
+ connector()->BindInterface("filesystem", &files_);
+ connector()->BindInterface("leveldb", &leveldb_);
}
void TearDown() override {
@@ -126,7 +126,7 @@ class LevelDBServiceTest : public service_manager::test::ServiceTest {
// since |ASSERT_...()| doesn't work with return values.
void GetTempDirectory(filesystem::mojom::DirectoryPtr* directory) {
FileError error = FileError::FAILED;
- bool handled = files()->OpenTempDirectory(GetProxy(directory), &error);
+ bool handled = files()->OpenTempDirectory(MakeRequest(directory), &error);
ASSERT_TRUE(handled);
ASSERT_EQ(FileError::OK, error);
}
@@ -143,8 +143,10 @@ class LevelDBServiceTest : public service_manager::test::ServiceTest {
TEST_F(LevelDBServiceTest, Basic) {
mojom::DatabaseError error;
- mojom::LevelDBDatabasePtr database;
- LevelDBSyncOpenInMemory(leveldb().get(), GetProxy(&database), &error);
+ mojom::LevelDBDatabaseAssociatedPtr database;
+ LevelDBSyncOpenInMemory(leveldb().get(),
+ MakeRequest(&database, leveldb().associated_group()),
+ &error);
EXPECT_EQ(mojom::DatabaseError::OK, error);
// Write a key to the database.
@@ -177,8 +179,10 @@ TEST_F(LevelDBServiceTest, Basic) {
TEST_F(LevelDBServiceTest, WriteBatch) {
mojom::DatabaseError error;
- mojom::LevelDBDatabasePtr database;
- LevelDBSyncOpenInMemory(leveldb().get(), GetProxy(&database), &error);
+ mojom::LevelDBDatabaseAssociatedPtr database;
+ LevelDBSyncOpenInMemory(leveldb().get(),
+ MakeRequest(&database, leveldb().associated_group()),
+ &error);
EXPECT_EQ(mojom::DatabaseError::OK, error);
// Write a key to the database.
@@ -257,16 +261,17 @@ TEST_F(LevelDBServiceTest, Reconnect) {
{
filesystem::mojom::DirectoryPtr directory;
- temp_directory->Clone(GetProxy(&directory));
+ temp_directory->Clone(MakeRequest(&directory));
- mojom::LevelDBDatabasePtr database;
+ mojom::LevelDBDatabaseAssociatedPtr database;
leveldb::mojom::OpenOptionsPtr options = leveldb::mojom::OpenOptions::New();
options->error_if_exists = true;
options->create_if_missing = true;
base::RunLoop run_loop;
- leveldb()->OpenWithOptions(std::move(options), std::move(directory), "test",
- GetProxy(&database),
- Capture(&error, run_loop.QuitClosure()));
+ leveldb()->OpenWithOptions(
+ std::move(options), std::move(directory), "test",
+ MakeRequest(&database, leveldb().associated_group()),
+ Capture(&error, run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(mojom::DatabaseError::OK, error);
@@ -280,12 +285,13 @@ TEST_F(LevelDBServiceTest, Reconnect) {
{
filesystem::mojom::DirectoryPtr directory;
- temp_directory->Clone(GetProxy(&directory));
+ temp_directory->Clone(MakeRequest(&directory));
// Reconnect to the database.
- mojom::LevelDBDatabasePtr database;
+ mojom::LevelDBDatabaseAssociatedPtr database;
base::RunLoop run_loop;
- leveldb()->Open(std::move(directory), "test", GetProxy(&database),
+ leveldb()->Open(std::move(directory), "test",
+ MakeRequest(&database, leveldb().associated_group()),
Capture(&error, run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(mojom::DatabaseError::OK, error);
@@ -299,10 +305,81 @@ TEST_F(LevelDBServiceTest, Reconnect) {
}
}
+TEST_F(LevelDBServiceTest, Destroy) {
+ mojom::DatabaseError error;
+
+ filesystem::mojom::DirectoryPtr temp_directory;
+ GetTempDirectory(&temp_directory);
+
+ {
+ filesystem::mojom::DirectoryPtr directory;
+ temp_directory->Clone(MakeRequest(&directory));
+
+ mojom::LevelDBDatabaseAssociatedPtr database;
+ leveldb::mojom::OpenOptionsPtr options = leveldb::mojom::OpenOptions::New();
+ options->error_if_exists = true;
+ options->create_if_missing = true;
+ base::RunLoop run_loop;
+ leveldb()->OpenWithOptions(
+ std::move(options), std::move(directory), "test",
+ MakeRequest(&database, leveldb().associated_group()),
+ Capture(&error, run_loop.QuitClosure()));
+ run_loop.Run();
+ EXPECT_EQ(mojom::DatabaseError::OK, error);
+
+ // Write a key to the database.
+ error = mojom::DatabaseError::INVALID_ARGUMENT;
+ DatabaseSyncPut(database.get(), "key", "value", &error);
+ EXPECT_EQ(mojom::DatabaseError::OK, error);
+
+ // The database should go out of scope here.
+ }
+
+ {
+ filesystem::mojom::DirectoryPtr directory;
+ temp_directory->Clone(MakeRequest(&directory));
+
+ // Destroy the database.
+ base::RunLoop run_loop;
+ leveldb()->Destroy(std::move(directory), "test",
+ Capture(&error, run_loop.QuitClosure()));
+ run_loop.Run();
+ EXPECT_EQ(mojom::DatabaseError::OK, error);
+ }
+
+ {
+ filesystem::mojom::DirectoryPtr directory;
+ temp_directory->Clone(MakeRequest(&directory));
+
+ // Reconnect to the database should fail.
+ mojom::LevelDBDatabaseAssociatedPtr database;
+ base::RunLoop run_loop;
+ leveldb()->Open(std::move(directory), "test",
+ MakeRequest(&database, leveldb().associated_group()),
+ Capture(&error, run_loop.QuitClosure()));
+ run_loop.Run();
+ EXPECT_EQ(mojom::DatabaseError::INVALID_ARGUMENT, error);
+ }
+
+ {
+ filesystem::mojom::DirectoryPtr directory;
+ temp_directory->Clone(MakeRequest(&directory));
+
+ // Destroying a non-existant database should still succeed.
+ base::RunLoop run_loop;
+ leveldb()->Destroy(std::move(directory), "test",
+ Capture(&error, run_loop.QuitClosure()));
+ run_loop.Run();
+ EXPECT_EQ(mojom::DatabaseError::OK, error);
+ }
+}
+
TEST_F(LevelDBServiceTest, GetSnapshotSimple) {
mojom::DatabaseError error;
- mojom::LevelDBDatabasePtr database;
- LevelDBSyncOpenInMemory(leveldb().get(), GetProxy(&database), &error);
+ mojom::LevelDBDatabaseAssociatedPtr database;
+ LevelDBSyncOpenInMemory(leveldb().get(),
+ MakeRequest(&database, leveldb().associated_group()),
+ &error);
EXPECT_EQ(mojom::DatabaseError::OK, error);
base::UnguessableToken snapshot;
@@ -314,8 +391,10 @@ TEST_F(LevelDBServiceTest, GetSnapshotSimple) {
TEST_F(LevelDBServiceTest, GetFromSnapshots) {
mojom::DatabaseError error;
- mojom::LevelDBDatabasePtr database;
- LevelDBSyncOpenInMemory(leveldb().get(), GetProxy(&database), &error);
+ mojom::LevelDBDatabaseAssociatedPtr database;
+ LevelDBSyncOpenInMemory(leveldb().get(),
+ MakeRequest(&database, leveldb().associated_group()),
+ &error);
EXPECT_EQ(mojom::DatabaseError::OK, error);
// Write a key to the database.
@@ -355,9 +434,11 @@ TEST_F(LevelDBServiceTest, GetFromSnapshots) {
}
TEST_F(LevelDBServiceTest, InvalidArgumentOnInvalidSnapshot) {
- mojom::LevelDBDatabasePtr database;
+ mojom::LevelDBDatabaseAssociatedPtr database;
mojom::DatabaseError error = mojom::DatabaseError::INVALID_ARGUMENT;
- LevelDBSyncOpenInMemory(leveldb().get(), GetProxy(&database), &error);
+ LevelDBSyncOpenInMemory(leveldb().get(),
+ MakeRequest(&database, leveldb().associated_group()),
+ &error);
EXPECT_EQ(mojom::DatabaseError::OK, error);
base::UnguessableToken invalid_snapshot = base::UnguessableToken::Create();
@@ -373,9 +454,11 @@ TEST_F(LevelDBServiceTest, InvalidArgumentOnInvalidSnapshot) {
}
TEST_F(LevelDBServiceTest, MemoryDBReadWrite) {
- mojom::LevelDBDatabasePtr database;
+ mojom::LevelDBDatabaseAssociatedPtr database;
mojom::DatabaseError error = mojom::DatabaseError::INVALID_ARGUMENT;
- LevelDBSyncOpenInMemory(leveldb().get(), GetProxy(&database), &error);
+ LevelDBSyncOpenInMemory(leveldb().get(),
+ MakeRequest(&database, leveldb().associated_group()),
+ &error);
EXPECT_EQ(mojom::DatabaseError::OK, error);
// Write a key to the database.
@@ -409,8 +492,10 @@ TEST_F(LevelDBServiceTest, MemoryDBReadWrite) {
TEST_F(LevelDBServiceTest, Prefixed) {
// Open an in memory database for speed.
mojom::DatabaseError error = mojom::DatabaseError::INVALID_ARGUMENT;
- mojom::LevelDBDatabasePtr database;
- LevelDBSyncOpenInMemory(leveldb().get(), GetProxy(&database), &error);
+ mojom::LevelDBDatabaseAssociatedPtr database;
+ LevelDBSyncOpenInMemory(leveldb().get(),
+ MakeRequest(&database, leveldb().associated_group()),
+ &error);
EXPECT_EQ(mojom::DatabaseError::OK, error);
const std::string prefix("prefix");
diff --git a/chromium/components/leveldb/public/interfaces/leveldb.mojom b/chromium/components/leveldb/public/interfaces/leveldb.mojom
index de4ca94ad9c..11e56709553 100644
--- a/chromium/components/leveldb/public/interfaces/leveldb.mojom
+++ b/chromium/components/leveldb/public/interfaces/leveldb.mojom
@@ -5,7 +5,7 @@
module leveldb.mojom;
import "components/filesystem/public/interfaces/directory.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/unguessable_token.mojom";
enum DatabaseError {
OK,
@@ -62,15 +62,25 @@ struct OpenOptions {
// Service which hands out databases.
interface LevelDBService {
+ // Open the database with the specified "name" in the specified "directory".
+ // Fails if the database doesn't already exist.
Open(filesystem.mojom.Directory directory,
string dbname,
- LevelDBDatabase& database) => (DatabaseError status);
+ 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,
- LevelDBDatabase& database) => (DatabaseError status);
+ associated LevelDBDatabase& database) => (DatabaseError status);
+
+ // Opens a database stored purely in memory.
+ OpenInMemory(associated LevelDBDatabase& database) => (DatabaseError status);
- OpenInMemory(LevelDBDatabase& database) => (DatabaseError status);
+ // Destroys the contents of the specified database. Returns OK if the database
+ // already didn't exist.
+ Destroy(filesystem.mojom.Directory directory,
+ string dbname) => (DatabaseError status);
};
// A leveldb database.
diff --git a/chromium/components/leveldb/remote_iterator_unittest.cc b/chromium/components/leveldb/remote_iterator_unittest.cc
index ff57341500c..f67b140a0b6 100644
--- a/chromium/components/leveldb/remote_iterator_unittest.cc
+++ b/chromium/components/leveldb/remote_iterator_unittest.cc
@@ -48,12 +48,13 @@ class RemoteIteratorTest : public service_manager::test::ServiceTest {
// Overridden from mojo::test::ApplicationTestBase:
void SetUp() override {
ServiceTest::SetUp();
- connector()->ConnectToInterface("leveldb", &leveldb_);
+ connector()->BindInterface("leveldb", &leveldb_);
mojom::DatabaseError error;
base::RunLoop run_loop;
- leveldb()->OpenInMemory(GetProxy(&database_),
- Capture(&error, run_loop.QuitClosure()));
+ leveldb()->OpenInMemory(
+ MakeRequest(&database_, leveldb().associated_group()),
+ Capture(&error, run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(mojom::DatabaseError::OK, error);
@@ -78,11 +79,11 @@ class RemoteIteratorTest : public service_manager::test::ServiceTest {
}
mojom::LevelDBServicePtr& leveldb() { return leveldb_; }
- mojom::LevelDBDatabasePtr& database() { return database_; }
+ mojom::LevelDBDatabaseAssociatedPtr& database() { return database_; }
private:
mojom::LevelDBServicePtr leveldb_;
- mojom::LevelDBDatabasePtr database_;
+ mojom::LevelDBDatabaseAssociatedPtr database_;
DISALLOW_COPY_AND_ASSIGN(RemoteIteratorTest);
};
diff --git a/chromium/components/leveldb_proto/BUILD.gn b/chromium/components/leveldb_proto/BUILD.gn
index 88541d50bbb..8c4a637a96a 100644
--- a/chromium/components/leveldb_proto/BUILD.gn
+++ b/chromium/components/leveldb_proto/BUILD.gn
@@ -6,6 +6,7 @@ static_library("leveldb_proto") {
sources = [
"leveldb_database.cc",
"leveldb_database.h",
+ "options.h",
"proto_database.h",
"proto_database_impl.h",
]
diff --git a/chromium/components/leveldb_proto/leveldb_database.cc b/chromium/components/leveldb_proto/leveldb_database.cc
index 753e4cb9115..9791c19d5ad 100644
--- a/chromium/components/leveldb_proto/leveldb_database.cc
+++ b/chromium/components/leveldb_proto/leveldb_database.cc
@@ -21,6 +21,7 @@
#include "base/trace_event/process_memory_dump.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
+#include "third_party/leveldatabase/src/include/leveldb/cache.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/env.h"
#include "third_party/leveldatabase/src/include/leveldb/iterator.h"
@@ -85,17 +86,23 @@ bool LevelDB::InitWithOptions(const base::FilePath& database_dir,
return false;
}
-bool LevelDB::Init(const base::FilePath& database_dir) {
- leveldb::Options options;
- options.create_if_missing = true;
- options.max_open_files = 0; // Use minimum.
- options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
- if (database_dir.empty()) {
+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;
+ 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();
+ }
+ if (options.database_dir.empty()) {
env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
- options.env = env_.get();
+ leveldb_options.env = env_.get();
}
- return InitWithOptions(database_dir, options);
+ return InitWithOptions(options.database_dir, leveldb_options);
}
bool LevelDB::Save(const base::StringPairs& entries_to_save,
@@ -187,14 +194,16 @@ bool LevelDB::OnMemoryDump(const base::trace_event::MemoryDumpArgs& dump_args,
res = base::StringToUint64(value, &size);
DCHECK(res);
- std::string dump_name = "leveldb/leveldb_proto";
- if (!client_name_.empty())
- dump_name += "/" + client_name_;
base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(
- base::StringPrintf("%s/0x%" PRIXPTR, dump_name.c_str(),
+ base::StringPrintf("leveldb/leveldb_proto/0x%" PRIXPTR,
reinterpret_cast<uintptr_t>(db_.get())));
dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes, size);
+ if (!client_name_.empty() &&
+ dump_args.level_of_detail !=
+ base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
+ dump->AddString("client_name", "", client_name_);
+ }
// Memory is allocated from system allocator (malloc).
const char* system_allocator_pool_name =
diff --git a/chromium/components/leveldb_proto/leveldb_database.h b/chromium/components/leveldb_proto/leveldb_database.h
index 8c984b797fc..562e6a54dff 100644
--- a/chromium/components/leveldb_proto/leveldb_database.h
+++ b/chromium/components/leveldb_proto/leveldb_database.h
@@ -9,17 +9,20 @@
#include <string>
#include <vector>
-#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/strings/string_split.h"
#include "base/threading/thread_collision_warner.h"
#include "base/trace_event/memory_dump_provider.h"
+#include "components/leveldb_proto/options.h"
namespace base {
+class FilePath;
class HistogramBase;
} // namespace base
namespace leveldb {
+class Cache;
class DB;
class Env;
struct Options;
@@ -30,7 +33,7 @@ namespace leveldb_proto {
// Interacts with the LevelDB third party module.
// Once constructed, function calls and destruction should all occur on the
// same thread (not necessarily the same as the constructor).
-class LevelDB : base::trace_event::MemoryDumpProvider {
+class LevelDB : public base::trace_event::MemoryDumpProvider {
public:
// Constructor. Does *not* open a leveldb - only initialize this class.
// |client_name| is the name of the "client" that owns this instance. Used
@@ -39,9 +42,10 @@ class LevelDB : base::trace_event::MemoryDumpProvider {
explicit LevelDB(const char* client_name);
~LevelDB() override;
- virtual bool InitWithOptions(const base::FilePath& database_dir,
- const leveldb::Options& options);
- virtual bool Init(const base::FilePath& database_dir);
+ // Initializes a leveldb with the given options. If |options.database_dir| is
+ // empty, this opens an in-memory db.
+ virtual bool Init(const leveldb_proto::Options& options);
+
virtual bool Save(const base::StringPairs& pairs_to_save,
const std::vector<std::string>& keys_to_remove);
virtual bool Load(std::vector<std::string>* entries);
@@ -54,12 +58,19 @@ class LevelDB : base::trace_event::MemoryDumpProvider {
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& dump_args,
base::trace_event::ProcessMemoryDump* pmd) override;
+ protected:
+ virtual bool InitWithOptions(const base::FilePath& database_dir,
+ const leveldb::Options& options);
+
private:
+ FRIEND_TEST_ALL_PREFIXES(ProtoDatabaseImplLevelDBTest, TestDBInitFail);
+
DFAKE_MUTEX(thread_checker_);
// The declaration order of these members matters: |db_| depends on |env_| and
- // therefore has to be destructed first.
+ // |custom_block_cache_| and therefore has to be destructed first.
std::unique_ptr<leveldb::Env> env_;
+ std::unique_ptr<leveldb::Cache> custom_block_cache_;
std::unique_ptr<leveldb::DB> db_;
base::HistogramBase* open_histogram_;
// Name of the client shown in chrome://tracing.
diff --git a/chromium/components/leveldb_proto/options.h b/chromium/components/leveldb_proto/options.h
new file mode 100644
index 00000000000..0778ade68a6
--- /dev/null
+++ b/chromium/components/leveldb_proto/options.h
@@ -0,0 +1,29 @@
+// 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_LEVELDB_PROTO_OPTIONS_H_
+#define COMPONENTS_LEVELDB_PROTO_OPTIONS_H_
+
+#include "base/files/file_path.h"
+
+namespace leveldb_proto {
+
+struct Options {
+ Options(const base::FilePath& database_dir)
+ : database_dir(database_dir), write_buffer_size(0), read_cache_size(0) {}
+ Options(const base::FilePath& database_dir,
+ size_t write_buffer_size,
+ size_t read_cache_size)
+ : database_dir(database_dir),
+ write_buffer_size(write_buffer_size),
+ read_cache_size(read_cache_size) {}
+
+ base::FilePath database_dir;
+ size_t write_buffer_size;
+ size_t read_cache_size;
+};
+
+} // namespace leveldb_proto
+
+#endif // COMPONENTS_LEVELDB_PROTO_OPTIONS_H_
diff --git a/chromium/components/leveldb_proto/proto_database.h b/chromium/components/leveldb_proto/proto_database.h
index 081233c9ab1..d2a7c29fdfa 100644
--- a/chromium/components/leveldb_proto/proto_database.h
+++ b/chromium/components/leveldb_proto/proto_database.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/callback_forward.h"
+#include "components/leveldb_proto/options.h"
namespace base {
class FilePath;
@@ -38,11 +39,18 @@ class ProtoDatabase {
virtual ~ProtoDatabase() {}
- // Asynchronously initializes the object. |callback| will be invoked on the
- // calling thread when complete.
- virtual void Init(const char* client_name,
- const base::FilePath& database_dir,
- const InitCallback& callback) = 0;
+ // Asynchronously initializes the object with default options. |callback| will
+ // be invoked on the calling thread when complete.
+ void Init(const char* client_name,
+ const base::FilePath& database_dir,
+ const InitCallback& callback) {
+ InitWithOptions(client_name, Options(database_dir), callback);
+ }
+
+ // Similar to Init, but takes additional options.
+ virtual void InitWithOptions(const char* client_name,
+ const Options& options,
+ const InitCallback& callback) = 0;
// Asynchronously saves |entries_to_save| and deletes entries from
// |keys_to_remove| from the database. |callback| will be invoked on the
diff --git a/chromium/components/leveldb_proto/proto_database_impl.h b/chromium/components/leveldb_proto/proto_database_impl.h
index 9997d382877..d5db97b9cfe 100644
--- a/chromium/components/leveldb_proto/proto_database_impl.h
+++ b/chromium/components/leveldb_proto/proto_database_impl.h
@@ -43,9 +43,10 @@ class ProtoDatabaseImpl : public ProtoDatabase<T> {
// ProtoDatabase implementation.
// TODO(cjhopman): Perhaps Init() shouldn't be exposed to users and not just
// part of the constructor
- void Init(const char* client_name,
- const base::FilePath& database_dir,
- const typename ProtoDatabase<T>::InitCallback& callback) override;
+ void InitWithOptions(
+ const char* client_name,
+ const Options& options,
+ const typename ProtoDatabase<T>::InitCallback& callback) override;
void UpdateEntries(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
entries_to_save,
@@ -64,7 +65,7 @@ class ProtoDatabaseImpl : public ProtoDatabase<T> {
// Allow callers to provide their own Database implementation.
void InitWithDatabase(
std::unique_ptr<LevelDB> database,
- const base::FilePath& database_dir,
+ const Options& options,
const typename ProtoDatabase<T>::InitCallback& callback);
private:
@@ -125,12 +126,12 @@ void RunDestroyCallback(
}
inline void InitFromTaskRunner(LevelDB* database,
- const base::FilePath& database_dir,
+ const Options& options,
bool* success) {
DCHECK(success);
// TODO(cjhopman): Histogram for database size.
- *success = database->Init(database_dir);
+ *success = database->Init(options);
}
inline void DestroyFromTaskRunner(const base::FilePath& database_dir,
@@ -181,9 +182,9 @@ void LoadEntriesFromTaskRunner(LevelDB* database,
}
}
-void LoadKeysFromTaskRunner(LevelDB* database,
- std::vector<std::string>* keys,
- bool* success) {
+inline void LoadKeysFromTaskRunner(LevelDB* database,
+ std::vector<std::string>* keys,
+ bool* success) {
DCHECK(success);
DCHECK(keys);
keys->clear();
@@ -225,13 +226,13 @@ ProtoDatabaseImpl<T>::~ProtoDatabaseImpl() {
}
template <typename T>
-void ProtoDatabaseImpl<T>::Init(
+void ProtoDatabaseImpl<T>::InitWithOptions(
const char* client_name,
- const base::FilePath& database_dir,
+ const Options& options,
const typename ProtoDatabase<T>::InitCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
- database_dir_ = database_dir;
- InitWithDatabase(base::WrapUnique(new LevelDB(client_name)), database_dir,
+ database_dir_ = options.database_dir;
+ InitWithDatabase(base::WrapUnique(new LevelDB(client_name)), options,
callback);
}
@@ -259,16 +260,16 @@ void ProtoDatabaseImpl<T>::Destroy(
template <typename T>
void ProtoDatabaseImpl<T>::InitWithDatabase(
std::unique_ptr<LevelDB> database,
- const base::FilePath& database_dir,
+ const Options& options,
const typename ProtoDatabase<T>::InitCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!db_);
DCHECK(database);
- db_.reset(database.release());
+ db_ = std::move(database);
bool* success = new bool(false);
task_runner_->PostTaskAndReply(
FROM_HERE, base::Bind(InitFromTaskRunner, base::Unretained(db_.get()),
- database_dir, success),
+ options, success),
base::Bind(RunInitCallback<T>, callback, base::Owned(success)));
}
diff --git a/chromium/components/leveldb_proto/proto_database_impl_unittest.cc b/chromium/components/leveldb_proto/proto_database_impl_unittest.cc
index 63f9f8836d4..50f81677389 100644
--- a/chromium/components/leveldb_proto/proto_database_impl_unittest.cc
+++ b/chromium/components/leveldb_proto/proto_database_impl_unittest.cc
@@ -42,7 +42,7 @@ const char kTestLevelDBClientName[] = "Test";
class MockDB : public LevelDB {
public:
- MOCK_METHOD1(Init, bool(const base::FilePath&));
+ MOCK_METHOD1(Init, bool(const leveldb_proto::Options& options));
MOCK_METHOD2(Save, bool(const KeyValueVector&, const KeyVector&));
MOCK_METHOD1(Load, bool(std::vector<std::string>*));
MOCK_METHOD3(Get, bool(const std::string&, bool*, std::string*));
@@ -68,6 +68,11 @@ class MockDatabaseCaller {
} // namespace
+bool operator==(const Options& lhs, const Options& rhs) {
+ return lhs.database_dir == rhs.database_dir &&
+ lhs.write_buffer_size == rhs.write_buffer_size;
+}
+
EntryMap GetSmallModel() {
EntryMap model;
@@ -119,7 +124,7 @@ TEST_F(ProtoDatabaseImplTest, TestDBInitSuccess) {
base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
MockDB* mock_db = new MockDB();
- EXPECT_CALL(*mock_db, Init(path)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_db, Init(Options(path))).WillOnce(Return(true));
MockDatabaseCaller caller;
EXPECT_CALL(caller, InitCallback(true));
@@ -135,7 +140,7 @@ TEST_F(ProtoDatabaseImplTest, TestDBInitFailure) {
base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
MockDB* mock_db = new MockDB();
- EXPECT_CALL(*mock_db, Init(path)).WillOnce(Return(false));
+ EXPECT_CALL(*mock_db, Init(Options(path))).WillOnce(Return(false));
MockDatabaseCaller caller;
EXPECT_CALL(caller, InitCallback(false));
diff --git a/chromium/components/leveldb_proto/testing/fake_db.h b/chromium/components/leveldb_proto/testing/fake_db.h
index bd9efb571a4..7a5e5dd4901 100644
--- a/chromium/components/leveldb_proto/testing/fake_db.h
+++ b/chromium/components/leveldb_proto/testing/fake_db.h
@@ -29,9 +29,10 @@ class FakeDB : public ProtoDatabase<T> {
~FakeDB() override;
// ProtoDatabase implementation.
- void Init(const char* client_name,
- const base::FilePath& database_dir,
- const typename ProtoDatabase<T>::InitCallback& callback) override;
+ void InitWithOptions(
+ const char* client_name,
+ const Options& options,
+ const typename ProtoDatabase<T>::InitCallback& callback) override;
void UpdateEntries(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
entries_to_save,
@@ -95,10 +96,11 @@ template <typename T>
FakeDB<T>::~FakeDB() {}
template <typename T>
-void FakeDB<T>::Init(const char* client_name,
- const base::FilePath& database_dir,
- const typename ProtoDatabase<T>::InitCallback& callback) {
- dir_ = database_dir;
+void FakeDB<T>::InitWithOptions(
+ const char* client_name,
+ const Options& options,
+ const typename ProtoDatabase<T>::InitCallback& callback) {
+ dir_ = options.database_dir;
init_callback_ = callback;
}
diff --git a/chromium/components/login/screens/screen_context.h b/chromium/components/login/screens/screen_context.h
index e7ae775d018..665f79894e6 100644
--- a/chromium/components/login/screens/screen_context.h
+++ b/chromium/components/login/screens/screen_context.h
@@ -10,7 +10,6 @@
#include "base/logging.h"
#include "base/macros.h"
-#include "base/memory/linked_ptr.h"
#include "base/strings/string16.h"
#include "base/threading/non_thread_safe.h"
#include "base/values.h"
diff --git a/chromium/components/metrics/BUILD.gn b/chromium/components/metrics/BUILD.gn
index ca83e679417..1d082459a11 100644
--- a/chromium/components/metrics/BUILD.gn
+++ b/chromium/components/metrics/BUILD.gn
@@ -2,17 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-declare_args() {
- # Overrides os name in uma metrics log to "Blimp".
- metrics_use_blimp = false
-}
-
-# These are only used by the blimp team, which has entirely migrated to gn,
-# so this logic is not replicated in the gyp file.
-if (metrics_use_blimp) {
- defines = [ "OVERRIDE_OS_NAME_TO_BLIMP" ]
-}
-
static_library("metrics") {
sources = [
"call_stack_profile_metrics_provider.cc",
@@ -57,6 +46,10 @@ static_library("metrics") {
"metrics_reporting_default_state.h",
"metrics_reporting_scheduler.cc",
"metrics_reporting_scheduler.h",
+ "metrics_rotation_scheduler.cc",
+ "metrics_rotation_scheduler.h",
+ "metrics_scheduler.cc",
+ "metrics_scheduler.h",
"metrics_service.cc",
"metrics_service.h",
"metrics_service_accessor.cc",
@@ -67,8 +60,13 @@ static_library("metrics") {
"metrics_state_manager.h",
"metrics_switches.cc",
"metrics_switches.h",
+ "metrics_upload_scheduler.cc",
+ "metrics_upload_scheduler.h",
"persisted_logs.cc",
"persisted_logs.h",
+ "persisted_logs_metrics.h",
+ "persisted_logs_metrics_impl.cc",
+ "persisted_logs_metrics_impl.h",
"stability_metrics_helper.cc",
"stability_metrics_helper.h",
"system_memory_stats_recorder.h",
@@ -85,6 +83,8 @@ static_library("metrics") {
":call_stack_profile_params",
"//base",
"//base:base_static",
+ "//components/browser_watcher:stability",
+ "//components/browser_watcher:stability_data",
"//components/prefs",
"//components/variations",
"//third_party/zlib:compression_utils",
@@ -314,6 +314,7 @@ source_set("unit_tests") {
"metrics_service_unittest.cc",
"metrics_state_manager_unittest.cc",
"net/net_metrics_log_uploader_unittest.cc",
+ "net/network_metrics_provider_unittest.cc",
"persisted_logs_unittest.cc",
"profiler/profiler_metrics_provider_unittest.cc",
"profiler/tracking_synchronizer_unittest.cc",
@@ -347,7 +348,10 @@ source_set("unit_tests") {
}
if (is_chromeos) {
- deps += [ "leak_detector:unit_tests" ]
+ deps += [
+ "leak_detector:unit_tests",
+ "//chromeos",
+ ]
}
# iOS is not supported by the profiler and the ios-simulator bot chokes on
diff --git a/chromium/components/metrics/DEPS b/chromium/components/metrics/DEPS
index 29757f463b3..3156f34603d 100644
--- a/chromium/components/metrics/DEPS
+++ b/chromium/components/metrics/DEPS
@@ -2,6 +2,7 @@
# dependencies to a minimal set.
include_rules = [
"-components",
+ "+components/browser_watcher",
"+components/compression",
"+components/metrics",
"+components/prefs",
diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider.cc b/chromium/components/metrics/call_stack_profile_metrics_provider.cc
index 6e320c82067..9bd823b34e9 100644
--- a/chromium/components/metrics/call_stack_profile_metrics_provider.cc
+++ b/chromium/components/metrics/call_stack_profile_metrics_provider.cc
@@ -33,6 +33,22 @@ namespace metrics {
namespace {
+// 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
+// with the protobuf -- even if there are ever more than 32 defined proto
+// values, though never more than 32 could be in-use in a given C++ version
+// of the code.
+const ProcessPhase
+ kProtoPhases[CallStackProfileMetricsProvider::MILESTONES_MAX_VALUE] = {
+ ProcessPhase::MAIN_LOOP_START,
+ ProcessPhase::MAIN_NAVIGATION_START,
+ ProcessPhase::MAIN_NAVIGATION_FINISHED,
+ ProcessPhase::FIRST_NONEMPTY_PAINT,
+
+ ProcessPhase::SHUTDOWN_START,
+};
+
// ProfilesState --------------------------------------------------------------
// A set of profiles and the CallStackProfileMetricsProvider state associated
@@ -223,7 +239,7 @@ void CopySampleToProto(
const StackSamplingProfiler::Sample& sample,
const std::vector<StackSamplingProfiler::Module>& modules,
CallStackProfile::Sample* proto_sample) {
- for (const StackSamplingProfiler::Frame& frame : sample) {
+ for (const StackSamplingProfiler::Frame& frame : sample.frames) {
CallStackProfile::Entry* entry = proto_sample->add_entry();
// A frame may not have a valid module. If so, we can't compute the
// instruction pointer offset, and we don't want to send bare pointers, so
@@ -239,6 +255,27 @@ void CopySampleToProto(
}
}
+// Transcode Sample annotations into protobuf fields. The C++ code uses a bit-
+// field with each bit corresponding to an entry in an enumeration while the
+// protobuf uses a repeated field of individual values. Conversion tables
+// allow for arbitrary mapping, though no more than 32 in any given version
+// of the code.
+void CopyAnnotationsToProto(uint32_t new_milestones,
+ CallStackProfile::Sample* sample_proto) {
+ for (size_t bit = 0; new_milestones != 0 && bit < sizeof(new_milestones) * 8;
+ ++bit) {
+ const uint32_t flag = 1U << bit;
+ if (new_milestones & flag) {
+ if (bit >= arraysize(kProtoPhases)) {
+ NOTREACHED();
+ continue;
+ }
+ sample_proto->add_process_phase(kProtoPhases[bit]);
+ new_milestones ^= flag; // Bit is set so XOR will clear it.
+ }
+ }
+}
+
// Transcode |profile| into |proto_profile|.
void CopyProfileToProto(
const StackSamplingProfiler::CallStackProfile& profile,
@@ -250,11 +287,17 @@ void CopyProfileToProto(
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
+ // 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);
}
@@ -262,15 +305,21 @@ void CopyProfileToProto(
} 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.
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);
diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider.h b/chromium/components/metrics/call_stack_profile_metrics_provider.h
index bd0b721c809..5c4f0db7f94 100644
--- a/chromium/components/metrics/call_stack_profile_metrics_provider.h
+++ b/chromium/components/metrics/call_stack_profile_metrics_provider.h
@@ -19,6 +19,20 @@ class ChromeUserMetricsExtension;
// Performs metrics logging for the stack sampling profiler.
class CallStackProfileMetricsProvider : public MetricsProvider {
public:
+ // These milestones of a process lifetime can be passed as process "mile-
+ // stones" to StackSmaplingProfile::SetProcessMilestone(). Be sure to update
+ // the translation constants at the top of the .cc file when this is changed.
+ enum Milestones : int {
+ MAIN_LOOP_START,
+ MAIN_NAVIGATION_START,
+ MAIN_NAVIGATION_FINISHED,
+ FIRST_NONEMPTY_PAINT,
+
+ SHUTDOWN_START,
+
+ MILESTONES_MAX_VALUE
+ };
+
CallStackProfileMetricsProvider();
~CallStackProfileMetricsProvider() override;
diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc b/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc
index 3e515b5c2db..c81e23922ac 100644
--- a/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc
+++ b/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc
@@ -27,6 +27,94 @@ using Profile = StackSamplingProfiler::CallStackProfile;
using Profiles = StackSamplingProfiler::CallStackProfiles;
using Sample = StackSamplingProfiler::Sample;
+namespace {
+
+struct ExpectedProtoModule {
+ const char* build_id;
+ uint64_t name_md5;
+ uint64_t base_address;
+};
+
+struct ExpectedProtoEntry {
+ int32_t module_index;
+ uint64_t address;
+};
+
+struct ExpectedProtoSample {
+ uint32_t process_milestones; // Bit-field of expected milestones.
+ const ExpectedProtoEntry* entries;
+ int entry_count;
+ int64_t entry_repeats;
+};
+
+struct ExpectedProtoProfile {
+ int32_t duration_ms;
+ int32_t period_ms;
+ const ExpectedProtoModule* modules;
+ int module_count;
+ const ExpectedProtoSample* samples;
+ int sample_count;
+};
+
+class ProfilesFactory {
+ public:
+ ProfilesFactory(){};
+ ~ProfilesFactory(){};
+
+ ProfilesFactory& AddMilestone(int milestone);
+ ProfilesFactory& NewProfile(int duration_ms, int interval_ms);
+ ProfilesFactory& NewSample();
+ ProfilesFactory& AddFrame(size_t module, uintptr_t offset);
+ ProfilesFactory& DefineModule(const char* name,
+ const base::FilePath& path,
+ uintptr_t base);
+
+ Profiles Build();
+
+ private:
+ Profiles profiles_;
+ uint32_t process_milestones_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(ProfilesFactory);
+};
+
+ProfilesFactory& ProfilesFactory::AddMilestone(int milestone) {
+ process_milestones_ |= 1 << milestone;
+ return *this;
+}
+
+ProfilesFactory& ProfilesFactory::NewProfile(int duration_ms, int interval_ms) {
+ profiles_.push_back(Profile());
+ Profile* profile = &profiles_.back();
+ profile->profile_duration = base::TimeDelta::FromMilliseconds(duration_ms);
+ profile->sampling_period = base::TimeDelta::FromMilliseconds(interval_ms);
+ return *this;
+}
+
+ProfilesFactory& ProfilesFactory::NewSample() {
+ profiles_.back().samples.push_back(Sample());
+ profiles_.back().samples.back().process_milestones = process_milestones_;
+ return *this;
+}
+
+ProfilesFactory& ProfilesFactory::AddFrame(size_t module, uintptr_t offset) {
+ profiles_.back().samples.back().frames.push_back(Frame(offset, module));
+ return *this;
+}
+
+ProfilesFactory& ProfilesFactory::DefineModule(const char* name,
+ const base::FilePath& path,
+ uintptr_t base) {
+ profiles_.back().modules.push_back(Module(base, name, path));
+ return *this;
+}
+
+Profiles ProfilesFactory::Build() {
+ return std::move(profiles_);
+}
+
+} // namespace
+
namespace metrics {
// This test fixture enables the field trial that
@@ -49,6 +137,9 @@ class CallStackProfileMetricsProviderTest : public testing::Test {
std::move(profiles));
}
+ void VerifyProfileProto(const ExpectedProtoProfile& expected,
+ const SampledProfile& proto);
+
private:
// Exposes field trial/group names from the CallStackProfileMetricsProvider.
class TestState : public CallStackProfileMetricsProvider {
@@ -63,54 +154,66 @@ class CallStackProfileMetricsProviderTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(CallStackProfileMetricsProviderTest);
};
+void CallStackProfileMetricsProviderTest::VerifyProfileProto(
+ const ExpectedProtoProfile& expected,
+ const SampledProfile& proto) {
+ ASSERT_TRUE(proto.has_call_stack_profile());
+ const CallStackProfile& stack = proto.call_stack_profile();
+
+ ASSERT_TRUE(stack.has_profile_duration_ms());
+ EXPECT_EQ(expected.duration_ms, stack.profile_duration_ms());
+ ASSERT_TRUE(stack.has_sampling_period_ms());
+ EXPECT_EQ(expected.period_ms, stack.sampling_period_ms());
+
+ ASSERT_EQ(expected.module_count, stack.module_id().size());
+ for (int m = 0; m < expected.module_count; ++m) {
+ SCOPED_TRACE("module " + base::IntToString(m));
+ const CallStackProfile::ModuleIdentifier& module_id =
+ stack.module_id().Get(m);
+ ASSERT_TRUE(module_id.has_build_id());
+ EXPECT_EQ(expected.modules[m].build_id, module_id.build_id());
+ ASSERT_TRUE(module_id.has_name_md5_prefix());
+ EXPECT_EQ(expected.modules[m].name_md5, module_id.name_md5_prefix());
+ }
+
+ ASSERT_EQ(expected.sample_count, stack.sample().size());
+ for (int s = 0; s < expected.sample_count; ++s) {
+ SCOPED_TRACE("sample " + base::IntToString(s));
+ const CallStackProfile::Sample& proto_sample = stack.sample().Get(s);
+
+ uint32_t process_milestones = 0;
+ for (int i = 0; i < proto_sample.process_phase().size(); ++i)
+ process_milestones |= 1U << proto_sample.process_phase().Get(i);
+ EXPECT_EQ(expected.samples[s].process_milestones, process_milestones);
+
+ ASSERT_EQ(expected.samples[s].entry_count, proto_sample.entry().size());
+ ASSERT_TRUE(proto_sample.has_count());
+ EXPECT_EQ(expected.samples[s].entry_repeats, proto_sample.count());
+ for (int e = 0; e < expected.samples[s].entry_count; ++e) {
+ SCOPED_TRACE("entry " + base::SizeTToString(e));
+ const CallStackProfile::Entry& entry = proto_sample.entry().Get(e);
+ if (expected.samples[s].entries[e].module_index >= 0) {
+ ASSERT_TRUE(entry.has_module_id_index());
+ EXPECT_EQ(expected.samples[s].entries[e].module_index,
+ entry.module_id_index());
+ ASSERT_TRUE(entry.has_address());
+ EXPECT_EQ(expected.samples[s].entries[e].address, entry.address());
+ } else {
+ EXPECT_FALSE(entry.has_module_id_index());
+ EXPECT_FALSE(entry.has_address());
+ }
+ }
+ }
+}
+
// Checks that all properties from multiple profiles are filled as expected.
TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) {
- const uintptr_t module1_base_address = 0x1000;
- const uintptr_t module2_base_address = 0x2000;
- const uintptr_t module3_base_address = 0x3000;
-
- const Module profile_modules[][2] = {
- {
- Module(
- module1_base_address,
- "ABCD",
-#if defined(OS_WIN)
- base::FilePath(L"c:\\some\\path\\to\\chrome.exe")
-#else
- base::FilePath("/some/path/to/chrome")
-#endif
- ),
- Module(
- module2_base_address,
- "EFGH",
-#if defined(OS_WIN)
- base::FilePath(L"c:\\some\\path\\to\\third_party.dll")
-#else
- base::FilePath("/some/path/to/third_party.so")
-#endif
- ),
- },
- {
- Module(
- module3_base_address,
- "MNOP",
-#if defined(OS_WIN)
- base::FilePath(L"c:\\some\\path\\to\\third_party2.dll")
-#else
- base::FilePath("/some/path/to/third_party2.so")
-#endif
- ),
- Module( // Repeated from the first profile.
- module1_base_address,
- "ABCD",
-#if defined(OS_WIN)
- base::FilePath(L"c:\\some\\path\\to\\chrome.exe")
-#else
- base::FilePath("/some/path/to/chrome")
-#endif
- )
- }
- };
+ const uintptr_t moduleA_base_address = 0x1000;
+ const uintptr_t moduleB_base_address = 0x2000;
+ const uintptr_t moduleC_base_address = 0x3000;
+ const char* moduleA_name = "ABCD";
+ const char* moduleB_name = "EFGH";
+ const char* moduleC_name = "MNOP";
// Values for Windows generated with:
// perl -MDigest::MD5=md5 -MEncode=encode
@@ -121,21 +224,21 @@ TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) {
// perl -MDigest::MD5=md5
// -e 'for(@ARGV){printf "%x\n", unpack "Q>", md5 $_}'
// chrome third_party.so third_party2.so
- const uint64_t profile_expected_name_md5_prefixes[][2] = {
- {
#if defined(OS_WIN)
- 0x46c3e4166659ac02ULL, 0x7e2b8bfddeae1abaULL
+ uint64_t moduleA_md5 = 0x46C3E4166659AC02ULL;
+ uint64_t moduleB_md5 = 0x7E2B8BFDDEAE1ABAULL;
+ uint64_t moduleC_md5 = 0x87B66F4573A4D5CAULL;
+ base::FilePath moduleA_path(L"c:\\some\\path\\to\\chrome.exe");
+ base::FilePath moduleB_path(L"c:\\some\\path\\to\\third_party.dll");
+ base::FilePath moduleC_path(L"c:\\some\\path\\to\\third_party2.dll");
#else
- 0x554838a8451ac36cUL, 0x843661148659c9f8UL
+ uint64_t moduleA_md5 = 0x554838A8451AC36CULL;
+ uint64_t moduleB_md5 = 0x843661148659C9F8ULL;
+ uint64_t moduleC_md5 = 0xB4647E539FA6EC9EULL;
+ base::FilePath moduleA_path("/some/path/to/chrome");
+ base::FilePath moduleB_path("/some/path/to/third_party.so");
+ base::FilePath moduleC_path("/some/path/to/third_party2.so");
#endif
- },
- {
-#if defined(OS_WIN)
- 0x87b66f4573a4d5caULL, 0x46c3e4166659ac02ULL
-#else
- 0xb4647e539fa6ec9eUL, 0x554838a8451ac36cUL
-#endif
- }};
// Represents two stack samples for each of two profiles, where each stack
// contains three frames. Each frame contains an instruction pointer and a
@@ -146,63 +249,97 @@ TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) {
// of 0x10 from the module's base address, the next-to-top frame in module 1
// at an offset of 0x20 from the module's base address, and the bottom frame
// in module 0 at an offset of 0x30 from the module's base address
- const Frame profile_sample_frames[][2][3] = {
- {
+ Profiles profiles = ProfilesFactory()
+ .NewProfile(100, 10)
+ .DefineModule(moduleA_name, moduleA_path, moduleA_base_address)
+ .DefineModule(moduleB_name, moduleB_path, moduleB_base_address)
+
+ .NewSample()
+ .AddFrame(0, moduleA_base_address + 0x10)
+ .AddFrame(1, moduleB_base_address + 0x20)
+ .AddFrame(0, moduleA_base_address + 0x30)
+ .NewSample()
+ .AddFrame(1, moduleB_base_address + 0x10)
+ .AddFrame(0, moduleA_base_address + 0x20)
+ .AddFrame(1, moduleB_base_address + 0x30)
+
+ .NewProfile(200, 20)
+ .DefineModule(moduleC_name, moduleC_path, moduleC_base_address)
+ .DefineModule(moduleA_name, moduleA_path, moduleA_base_address)
+
+ .NewSample()
+ .AddFrame(0, moduleC_base_address + 0x10)
+ .AddFrame(1, moduleA_base_address + 0x20)
+ .AddFrame(0, moduleC_base_address + 0x30)
+ .NewSample()
+ .AddFrame(1, moduleA_base_address + 0x10)
+ .AddFrame(0, moduleC_base_address + 0x20)
+ .AddFrame(1, moduleA_base_address + 0x30)
+
+ .Build();
+
+ const ExpectedProtoModule expected_proto_modules1[] = {
+ { moduleA_name, moduleA_md5, moduleA_base_address },
+ { moduleB_name, moduleB_md5, moduleB_base_address }
+ };
+
+ const ExpectedProtoEntry expected_proto_entries11[] = {
+ { 0, 0x10 },
+ { 1, 0x20 },
+ { 0, 0x30 },
+ };
+ const ExpectedProtoEntry expected_proto_entries12[] = {
+ { 1, 0x10 },
+ { 0, 0x20 },
+ { 1, 0x30 },
+ };
+ const ExpectedProtoSample expected_proto_samples1[] = {
{
- Frame(module1_base_address + 0x10, 0),
- Frame(module2_base_address + 0x20, 1),
- Frame(module1_base_address + 0x30, 0)
+ 0, expected_proto_entries11, arraysize(expected_proto_entries11), 1,
},
{
- Frame(module2_base_address + 0x10, 1),
- Frame(module1_base_address + 0x20, 0),
- Frame(module2_base_address + 0x30, 1)
- }
- },
- {
- {
- Frame(module3_base_address + 0x10, 0),
- Frame(module1_base_address + 0x20, 1),
- Frame(module3_base_address + 0x30, 0)
+ 0, expected_proto_entries12, arraysize(expected_proto_entries12), 1,
},
- {
- Frame(module1_base_address + 0x10, 1),
- Frame(module3_base_address + 0x20, 0),
- Frame(module1_base_address + 0x30, 1)
- }
- }
};
- base::TimeDelta profile_durations[2] = {
- base::TimeDelta::FromMilliseconds(100),
- base::TimeDelta::FromMilliseconds(200)
+ const ExpectedProtoModule expected_proto_modules2[] = {
+ { moduleC_name, moduleC_md5, moduleC_base_address },
+ { moduleA_name, moduleA_md5, moduleA_base_address }
};
- base::TimeDelta profile_sampling_periods[2] = {
- base::TimeDelta::FromMilliseconds(10),
- base::TimeDelta::FromMilliseconds(20)
+ const ExpectedProtoEntry expected_proto_entries21[] = {
+ { 0, 0x10 },
+ { 1, 0x20 },
+ { 0, 0x30 },
+ };
+ const ExpectedProtoEntry expected_proto_entries22[] = {
+ { 1, 0x10 },
+ { 0, 0x20 },
+ { 1, 0x30 },
+ };
+ const ExpectedProtoSample expected_proto_samples2[] = {
+ {
+ 0, expected_proto_entries11, arraysize(expected_proto_entries21), 1,
+ },
+ {
+ 0, expected_proto_entries12, arraysize(expected_proto_entries22), 1,
+ },
};
- std::vector<Profile> profiles;
- for (size_t i = 0; i < arraysize(profile_sample_frames); ++i) {
- Profile profile;
- profile.modules.insert(
- profile.modules.end(), &profile_modules[i][0],
- &profile_modules[i][0] + arraysize(profile_modules[i]));
-
- for (size_t j = 0; j < arraysize(profile_sample_frames[i]); ++j) {
- profile.samples.push_back(Sample());
- Sample& sample = profile.samples.back();
- sample.insert(sample.end(), &profile_sample_frames[i][j][0],
- &profile_sample_frames[i][j][0] +
- arraysize(profile_sample_frames[i][j]));
- }
-
- profile.profile_duration = profile_durations[i];
- profile.sampling_period = profile_sampling_periods[i];
+ const ExpectedProtoProfile expected_proto_profiles[] = {
+ {
+ 100, 10,
+ expected_proto_modules1, arraysize(expected_proto_modules1),
+ expected_proto_samples1, arraysize(expected_proto_samples1),
+ },
+ {
+ 200, 20,
+ expected_proto_modules2, arraysize(expected_proto_modules2),
+ expected_proto_samples2, arraysize(expected_proto_samples2),
+ },
+ };
- profiles.push_back(std::move(profile));
- }
+ ASSERT_EQ(arraysize(expected_proto_profiles), profiles.size());
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
@@ -215,68 +352,12 @@ TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) {
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
- ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames)),
+ ASSERT_EQ(static_cast<int>(arraysize(expected_proto_profiles)),
uma_proto.sampled_profile().size());
- for (size_t i = 0; i < arraysize(profile_sample_frames); ++i) {
- SCOPED_TRACE("profile " + base::SizeTToString(i));
- const SampledProfile& sampled_profile = uma_proto.sampled_profile().Get(i);
- ASSERT_TRUE(sampled_profile.has_call_stack_profile());
- const CallStackProfile& call_stack_profile =
- sampled_profile.call_stack_profile();
-
- ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames[i])),
- call_stack_profile.sample().size());
- for (size_t j = 0; j < arraysize(profile_sample_frames[i]); ++j) {
- SCOPED_TRACE("sample " + base::SizeTToString(j));
- const CallStackProfile::Sample& proto_sample =
- call_stack_profile.sample().Get(j);
- ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames[i][j])),
- proto_sample.entry().size());
- ASSERT_TRUE(proto_sample.has_count());
- EXPECT_EQ(1u, proto_sample.count());
- for (size_t k = 0; k < arraysize(profile_sample_frames[i][j]); ++k) {
- SCOPED_TRACE("frame " + base::SizeTToString(k));
- const CallStackProfile::Entry& entry = proto_sample.entry().Get(k);
- ASSERT_TRUE(entry.has_address());
- const char* instruction_pointer = reinterpret_cast<const char*>(
- profile_sample_frames[i][j][k].instruction_pointer);
- const char* module_base_address = reinterpret_cast<const char*>(
- profile_modules[i][profile_sample_frames[i][j][k].module_index]
- .base_address);
- EXPECT_EQ(
- static_cast<uint64_t>(instruction_pointer - module_base_address),
- entry.address());
- ASSERT_TRUE(entry.has_module_id_index());
- EXPECT_EQ(profile_sample_frames[i][j][k].module_index,
- static_cast<size_t>(entry.module_id_index()));
- }
- }
-
- ASSERT_EQ(static_cast<int>(arraysize(profile_modules[i])),
- call_stack_profile.module_id().size());
- for (size_t j = 0; j < arraysize(profile_modules[i]); ++j) {
- SCOPED_TRACE("module " + base::SizeTToString(j));
- const CallStackProfile::ModuleIdentifier& module_identifier =
- call_stack_profile.module_id().Get(j);
- ASSERT_TRUE(module_identifier.has_build_id());
- EXPECT_EQ(profile_modules[i][j].id, module_identifier.build_id());
- ASSERT_TRUE(module_identifier.has_name_md5_prefix());
- EXPECT_EQ(profile_expected_name_md5_prefixes[i][j],
- module_identifier.name_md5_prefix());
- }
-
- ASSERT_TRUE(call_stack_profile.has_profile_duration_ms());
- EXPECT_EQ(profile_durations[i].InMilliseconds(),
- call_stack_profile.profile_duration_ms());
- ASSERT_TRUE(call_stack_profile.has_sampling_period_ms());
- EXPECT_EQ(profile_sampling_periods[i].InMilliseconds(),
- call_stack_profile.sampling_period_ms());
- ASSERT_TRUE(sampled_profile.has_process());
- EXPECT_EQ(BROWSER_PROCESS, sampled_profile.process());
- ASSERT_TRUE(sampled_profile.has_thread());
- EXPECT_EQ(UI_THREAD, sampled_profile.thread());
- ASSERT_TRUE(sampled_profile.has_trigger_event());
- EXPECT_EQ(SampledProfile::PROCESS_STARTUP, sampled_profile.trigger_event());
+ 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));
}
}
@@ -284,42 +365,66 @@ TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) {
// preserve_sample_ordering = false.
TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
const uintptr_t module_base_address = 0x1000;
+ const char* module_name = "ABCD";
- const Module modules[] = {
- Module(
- module_base_address,
- "ABCD",
#if defined(OS_WIN)
- base::FilePath(L"c:\\some\\path\\to\\chrome.exe")
+ uint64_t module_md5 = 0x46C3E4166659AC02ULL;
+ base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe");
#else
- base::FilePath("/some/path/to/chrome")
+ uint64_t module_md5 = 0x554838A8451AC36CULL;
+ base::FilePath module_path("/some/path/to/chrome");
#endif
- )
- };
- // Duplicate samples in slots 0, 2, and 3.
- const Frame sample_frames[][1] = {
- { Frame(module_base_address + 0x10, 0), },
- { Frame(module_base_address + 0x20, 0), },
- { Frame(module_base_address + 0x10, 0), },
- { Frame(module_base_address + 0x10, 0) }
+ 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 },
};
- Profile profile;
- profile.modules.insert(profile.modules.end(), &modules[0],
- &modules[0] + arraysize(modules));
+ 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 },
+ };
- for (size_t i = 0; i < arraysize(sample_frames); ++i) {
- profile.samples.push_back(Sample());
- Sample& sample = profile.samples.back();
- sample.insert(sample.end(), &sample_frames[i][0],
- &sample_frames[i][0] + arraysize(sample_frames[i]));
- }
+ const ExpectedProtoProfile expected_proto_profiles[] = {
+ {
+ 100, 10,
+ expected_proto_modules, arraysize(expected_proto_modules),
+ expected_proto_samples, arraysize(expected_proto_samples),
+ },
+ };
- profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
- profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
- Profiles profiles;
- profiles.push_back(std::move(profile));
+ ASSERT_EQ(arraysize(expected_proto_profiles), profiles.size());
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
@@ -332,36 +437,12 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
- ASSERT_EQ(1, uma_proto.sampled_profile().size());
- const SampledProfile& sampled_profile = uma_proto.sampled_profile().Get(0);
- ASSERT_TRUE(sampled_profile.has_call_stack_profile());
- const CallStackProfile& call_stack_profile =
- sampled_profile.call_stack_profile();
-
- ASSERT_EQ(2, call_stack_profile.sample().size());
- for (int i = 0; i < 2; ++i) {
- SCOPED_TRACE("sample " + base::IntToString(i));
- const CallStackProfile::Sample& proto_sample =
- call_stack_profile.sample().Get(i);
- ASSERT_EQ(static_cast<int>(arraysize(sample_frames[i])),
- proto_sample.entry().size());
- ASSERT_TRUE(proto_sample.has_count());
- EXPECT_EQ(i == 0 ? 3u : 1u, proto_sample.count());
- for (size_t j = 0; j < arraysize(sample_frames[i]); ++j) {
- SCOPED_TRACE("frame " + base::SizeTToString(j));
- const CallStackProfile::Entry& entry = proto_sample.entry().Get(j);
- ASSERT_TRUE(entry.has_address());
- const char* instruction_pointer = reinterpret_cast<const char*>(
- sample_frames[i][j].instruction_pointer);
- const char* module_base_address = reinterpret_cast<const char*>(
- modules[sample_frames[i][j].module_index].base_address);
- EXPECT_EQ(
- static_cast<uint64_t>(instruction_pointer - module_base_address),
- entry.address());
- ASSERT_TRUE(entry.has_module_id_index());
- EXPECT_EQ(sample_frames[i][j].module_index,
- static_cast<size_t>(entry.module_id_index()));
- }
+ 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));
}
}
@@ -369,42 +450,68 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
// preserve_sample_ordering = true.
TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
const uintptr_t module_base_address = 0x1000;
+ const char* module_name = "ABCD";
- const Module modules[] = {
- Module(
- module_base_address,
- "ABCD",
#if defined(OS_WIN)
- base::FilePath(L"c:\\some\\path\\to\\chrome.exe")
+ uint64_t module_md5 = 0x46C3E4166659AC02ULL;
+ base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe");
#else
- base::FilePath("/some/path/to/chrome")
+ uint64_t module_md5 = 0x554838A8451AC36CULL;
+ base::FilePath module_path("/some/path/to/chrome");
#endif
- )
- };
- // Duplicate samples in slots 0, 2, and 3.
- const Frame sample_frames[][1] = {
- { Frame(module_base_address + 0x10, 0), },
- { Frame(module_base_address + 0x20, 0), },
- { Frame(module_base_address + 0x10, 0), },
- { Frame(module_base_address + 0x10, 0) }
+ 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 },
};
- Profile profile;
- profile.modules.insert(profile.modules.end(), &modules[0],
- &modules[0] + arraysize(modules));
+ const ExpectedProtoEntry expected_proto_entries[] = {
+ { 0, 0x10 },
+ { 0, 0x20 },
+ };
+ const ExpectedProtoSample expected_proto_samples[] = {
+ { 1, &expected_proto_entries[0], 1, 1 },
+ { 0, &expected_proto_entries[1], 1, 1 },
+ { 0, &expected_proto_entries[0], 1, 2 },
+ { 2, &expected_proto_entries[0], 1, 1 },
+ { 0, &expected_proto_entries[1], 1, 1 },
+ { 0, &expected_proto_entries[0], 1, 2 },
+ };
- for (size_t i = 0; i < arraysize(sample_frames); ++i) {
- profile.samples.push_back(Sample());
- Sample& sample = profile.samples.back();
- sample.insert(sample.end(), &sample_frames[i][0],
- &sample_frames[i][0] + arraysize(sample_frames[i]));
- }
+ const ExpectedProtoProfile expected_proto_profiles[] = {
+ {
+ 100, 10,
+ expected_proto_modules, arraysize(expected_proto_modules),
+ expected_proto_samples, arraysize(expected_proto_samples),
+ },
+ };
- profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
- profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
- Profiles profiles;
- profiles.push_back(std::move(profile));
+ ASSERT_EQ(arraysize(expected_proto_profiles), profiles.size());
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
@@ -416,51 +523,39 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
- ASSERT_EQ(1, uma_proto.sampled_profile().size());
- const SampledProfile& sampled_profile = uma_proto.sampled_profile().Get(0);
- ASSERT_TRUE(sampled_profile.has_call_stack_profile());
- const CallStackProfile& call_stack_profile =
- sampled_profile.call_stack_profile();
-
- ASSERT_EQ(3, call_stack_profile.sample().size());
- for (int i = 0; i < 3; ++i) {
- SCOPED_TRACE("sample " + base::IntToString(i));
- const CallStackProfile::Sample& proto_sample =
- call_stack_profile.sample().Get(i);
- ASSERT_EQ(static_cast<int>(arraysize(sample_frames[i])),
- proto_sample.entry().size());
- ASSERT_TRUE(proto_sample.has_count());
- EXPECT_EQ(i == 2 ? 2u : 1u, proto_sample.count());
- for (size_t j = 0; j < arraysize(sample_frames[i]); ++j) {
- SCOPED_TRACE("frame " + base::SizeTToString(j));
- const CallStackProfile::Entry& entry = proto_sample.entry().Get(j);
- ASSERT_TRUE(entry.has_address());
- const char* instruction_pointer = reinterpret_cast<const char*>(
- sample_frames[i][j].instruction_pointer);
- const char* module_base_address = reinterpret_cast<const char*>(
- modules[sample_frames[i][j].module_index].base_address);
- EXPECT_EQ(
- static_cast<uint64_t>(instruction_pointer - module_base_address),
- entry.address());
- ASSERT_TRUE(entry.has_module_id_index());
- EXPECT_EQ(sample_frames[i][j].module_index,
- static_cast<size_t>(entry.module_id_index()));
- }
+ 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));
}
}
// Checks that unknown modules produce an empty Entry.
TEST_F(CallStackProfileMetricsProviderTest, UnknownModule) {
- const Frame frame(0x1000, Frame::kUnknownModuleIndex);
-
- Profile profile;
+ Profiles profiles = ProfilesFactory()
+ .NewProfile(100, 10)
+ .NewSample()
+ .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
+ .Build();
+
+ const ExpectedProtoEntry expected_proto_entries[] = {
+ { -1, 0 },
+ };
+ const ExpectedProtoSample expected_proto_samples[] = {
+ { 0, &expected_proto_entries[0], 1, 1 },
+ };
- profile.samples.push_back(Sample(1, frame));
+ const ExpectedProtoProfile expected_proto_profiles[] = {
+ {
+ 100, 10,
+ nullptr, 0,
+ expected_proto_samples, arraysize(expected_proto_samples),
+ },
+ };
- profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
- profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
- Profiles profiles;
- profiles.push_back(std::move(profile));
+ ASSERT_EQ(arraysize(expected_proto_profiles), profiles.size());
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
@@ -473,38 +568,30 @@ TEST_F(CallStackProfileMetricsProviderTest, UnknownModule) {
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
- ASSERT_EQ(1, uma_proto.sampled_profile().size());
- const SampledProfile& sampled_profile = uma_proto.sampled_profile().Get(0);
- ASSERT_TRUE(sampled_profile.has_call_stack_profile());
- const CallStackProfile& call_stack_profile =
- sampled_profile.call_stack_profile();
-
- ASSERT_EQ(1, call_stack_profile.sample().size());
- const CallStackProfile::Sample& proto_sample =
- call_stack_profile.sample().Get(0);
- ASSERT_EQ(1, proto_sample.entry().size());
- ASSERT_TRUE(proto_sample.has_count());
- EXPECT_EQ(1u, proto_sample.count());
- const CallStackProfile::Entry& entry = proto_sample.entry().Get(0);
- EXPECT_FALSE(entry.has_address());
- EXPECT_FALSE(entry.has_module_id_index());
+ 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));
+ }
}
// Checks that pending profiles are only passed back to ProvideGeneralMetrics
// once.
TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) {
CallStackProfileMetricsProvider provider;
- for (int i = 0; i < 2; ++i) {
- Profile profile;
- profile.samples.push_back(
- Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex)));
+ for (int r = 0; r < 2; ++r) {
+ Profiles profiles = ProfilesFactory()
+ // Use the sampling period to distinguish the two profiles.
+ .NewProfile(100, r)
+ .NewSample()
+ .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
+ .Build();
- profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
- // Use the sampling period to distinguish the two profiles.
- profile.sampling_period = base::TimeDelta::FromMilliseconds(i);
- Profiles profiles;
- profiles.push_back(std::move(profile));
+ ASSERT_EQ(1U, profiles.size());
+ CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
AppendProfiles(
CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
@@ -521,7 +608,7 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) {
const CallStackProfile& call_stack_profile =
sampled_profile.call_stack_profile();
ASSERT_TRUE(call_stack_profile.has_sampling_period_ms());
- EXPECT_EQ(i, call_stack_profile.sampling_period_ms());
+ EXPECT_EQ(r, call_stack_profile.sampling_period_ms());
}
}
@@ -529,14 +616,13 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) {
// when collected before CallStackProfileMetricsProvider is instantiated.
TEST_F(CallStackProfileMetricsProviderTest,
ProfilesProvidedWhenCollectedBeforeInstantiation) {
- Profile profile;
- profile.samples.push_back(
- Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex)));
+ Profiles profiles = ProfilesFactory()
+ .NewProfile(100, 10)
+ .NewSample()
+ .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
+ .Build();
- profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
- profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
- Profiles profiles;
- profiles.push_back(std::move(profile));
+ ASSERT_EQ(1U, profiles.size());
AppendProfiles(
CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
@@ -556,14 +642,13 @@ TEST_F(CallStackProfileMetricsProviderTest,
// Checks that pending profiles are not provided to ProvideGeneralMetrics
// while recording is disabled.
TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedWhileDisabled) {
- Profile profile;
- profile.samples.push_back(
- Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex)));
+ Profiles profiles = ProfilesFactory()
+ .NewProfile(100, 10)
+ .NewSample()
+ .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
+ .Build();
- profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
- profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
- Profiles profiles;
- profiles.push_back(std::move(profile));
+ ASSERT_EQ(1U, profiles.size());
CallStackProfileMetricsProvider provider;
provider.OnRecordingDisabled();
@@ -583,13 +668,6 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedWhileDisabled) {
// if recording is disabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,
ProfilesNotProvidedAfterChangeToDisabled) {
- Profile profile;
- profile.samples.push_back(
- Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex)));
-
- profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
- profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
-
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
base::StackSamplingProfiler::CompletedCallback callback =
@@ -598,10 +676,13 @@ TEST_F(CallStackProfileMetricsProviderTest,
CallStackProfileParams::UI_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE));
-
provider.OnRecordingDisabled();
- Profiles profiles;
- profiles.push_back(std::move(profile));
+
+ Profiles profiles = ProfilesFactory()
+ .NewProfile(100, 10)
+ .NewSample()
+ .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
+ .Build();
callback.Run(std::move(profiles));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
@@ -613,13 +694,6 @@ TEST_F(CallStackProfileMetricsProviderTest,
// recording is enabled, but then disabled and reenabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,
ProfilesNotProvidedAfterChangeToDisabledThenEnabled) {
- Profile profile;
- profile.samples.push_back(
- Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex)));
-
- profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
- profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
-
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
base::StackSamplingProfiler::CompletedCallback callback =
@@ -628,11 +702,14 @@ TEST_F(CallStackProfileMetricsProviderTest,
CallStackProfileParams::UI_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE));
-
provider.OnRecordingDisabled();
provider.OnRecordingEnabled();
- Profiles profiles;
- profiles.push_back(std::move(profile));
+
+ Profiles profiles = ProfilesFactory()
+ .NewProfile(100, 10)
+ .NewSample()
+ .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
+ .Build();
callback.Run(std::move(profiles));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
@@ -644,13 +721,6 @@ TEST_F(CallStackProfileMetricsProviderTest,
// if recording is disabled, but then enabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,
ProfilesNotProvidedAfterChangeFromDisabled) {
- Profile profile;
- profile.samples.push_back(
- Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex)));
-
- profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
- profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
-
CallStackProfileMetricsProvider provider;
provider.OnRecordingDisabled();
base::StackSamplingProfiler::CompletedCallback callback =
@@ -659,10 +729,13 @@ TEST_F(CallStackProfileMetricsProviderTest,
CallStackProfileParams::UI_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE));
-
provider.OnRecordingEnabled();
- Profiles profiles;
- profiles.push_back(std::move(profile));
+
+ Profiles profiles = ProfilesFactory()
+ .NewProfile(100, 10)
+ .NewSample()
+ .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
+ .Build();
callback.Run(std::move(profiles));
ChromeUserMetricsExtension uma_proto;
provider.ProvideGeneralMetrics(&uma_proto);
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 af48975b378..9a3a67af92d 100644
--- a/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc
+++ b/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc
@@ -52,7 +52,7 @@ class ChildCallStackProfileCollectorTest : public testing::Test {
};
ChildCallStackProfileCollectorTest()
- : receiver_impl_(new Receiver(GetProxy(&receiver_))) {}
+ : receiver_impl_(new Receiver(MakeRequest(&receiver_))) {}
void CollectEmptyProfiles(
const CallStackProfileParams& params,
diff --git a/chromium/components/metrics/data_use_tracker.cc b/chromium/components/metrics/data_use_tracker.cc
index d97462aa918..6081facf5d6 100644
--- a/chromium/components/metrics/data_use_tracker.cc
+++ b/chromium/components/metrics/data_use_tracker.cc
@@ -22,24 +22,10 @@ namespace {
const int kDefaultUMAWeeklyQuotaBytes = 204800;
const double kDefaultUMARatio = 0.05;
-// This function is for forwarding metrics usage pref changes to the appropriate
-// callback on the appropriate thread.
-// TODO(gayane): Reduce the frequency of posting tasks from IO to UI thread.
-void UpdateMetricsUsagePrefs(
- const UpdateUsagePrefCallbackType& update_on_ui_callback,
- scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
- const std::string& service_name,
- int message_size,
- bool is_cellular) {
- ui_task_runner->PostTask(
- FROM_HERE, base::Bind(update_on_ui_callback, service_name, message_size,
- is_cellular));
-}
-
} // namespace
DataUseTracker::DataUseTracker(PrefService* local_state)
- : local_state_(local_state), weak_ptr_factory_(this) {}
+ : local_state_(local_state) {}
DataUseTracker::~DataUseTracker() {}
@@ -59,15 +45,17 @@ void DataUseTracker::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(metrics::prefs::kUmaCellDataUse);
}
-UpdateUsagePrefCallbackType DataUseTracker::GetDataUseForwardingCallback(
- scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
- DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
+void DataUseTracker::UpdateMetricsUsagePrefs(const std::string& service_name,
+ int message_size,
+ bool is_cellular) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!is_cellular)
+ return;
- return base::Bind(
- &UpdateMetricsUsagePrefs,
- base::Bind(&DataUseTracker::UpdateMetricsUsagePrefsOnUIThread,
- weak_ptr_factory_.GetWeakPtr()),
- ui_task_runner);
+ UpdateUsagePref(prefs::kUserCellDataUse, message_size);
+ if (service_name == "UMA")
+ UpdateUsagePref(prefs::kUmaCellDataUse, message_size);
}
bool DataUseTracker::ShouldUploadLogOnCellular(int log_bytes) {
@@ -98,20 +86,6 @@ bool DataUseTracker::ShouldUploadLogOnCellular(int log_bytes) {
uma_ratio;
}
-void DataUseTracker::UpdateMetricsUsagePrefsOnUIThread(
- const std::string& service_name,
- int message_size,
- bool is_celllular) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- if (!is_celllular)
- return;
-
- UpdateUsagePref(prefs::kUserCellDataUse, message_size);
- if (service_name == "UMA")
- UpdateUsagePref(prefs::kUmaCellDataUse, message_size);
-}
-
void DataUseTracker::UpdateUsagePref(const std::string& pref_name,
int message_size) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -144,8 +118,8 @@ void DataUseTracker::RemoveExpiredEntriesForPref(const std::string& pref_name) {
for (base::DictionaryValue::Iterator it(*user_pref_dict); !it.IsAtEnd();
it.Advance()) {
base::Time key_date;
- base::Time::FromUTCString(it.key().c_str(), &key_date);
- if (key_date > week_ago)
+ if (base::Time::FromUTCString(it.key().c_str(), &key_date) &&
+ key_date > week_ago)
user_pref_new_dict.Set(it.key(), it.value().CreateDeepCopy());
}
local_state_->Set(pref_name, user_pref_new_dict);
diff --git a/chromium/components/metrics/data_use_tracker.h b/chromium/components/metrics/data_use_tracker.h
index b6441ec7980..e5eb2ba25fd 100644
--- a/chromium/components/metrics/data_use_tracker.h
+++ b/chromium/components/metrics/data_use_tracker.h
@@ -10,9 +10,6 @@
#include "base/callback.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/sequenced_task_runner.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "components/prefs/pref_registry_simple.h"
@@ -38,10 +35,10 @@ class DataUseTracker {
// Registers data use prefs using provided |registry|.
static void RegisterPrefs(PrefRegistrySimple* registry);
- // Returns a callback to data use pref updating function. Should be called on
- // UI thread.
- UpdateUsagePrefCallbackType GetDataUseForwardingCallback(
- scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
+ // Updates data usage tracking prefs with the specified values.
+ void UpdateMetricsUsagePrefs(const std::string& service_name,
+ int message_size,
+ bool is_cellular);
// Returns whether a log with provided |log_bytes| can be uploaded according
// to data use ratio and UMA quota provided by variations.
@@ -53,12 +50,6 @@ class DataUseTracker {
FRIEND_TEST_ALL_PREFIXES(DataUseTrackerTest, CheckComputeTotalDataUse);
FRIEND_TEST_ALL_PREFIXES(DataUseTrackerTest, CheckCanUploadUMALog);
- // Updates data usage prefs on UI thread according to what Prefservice
- // expects.
- void UpdateMetricsUsagePrefsOnUIThread(const std::string& service_name,
- int message_size,
- bool is_cellular);
-
// Updates provided |pref_name| for a current date with the given message
// size.
void UpdateUsagePref(const std::string& pref_name, int message_size);
@@ -90,8 +81,6 @@ class DataUseTracker {
base::ThreadChecker thread_checker_;
- base::WeakPtrFactory<DataUseTracker> weak_ptr_factory_;
-
DISALLOW_COPY_AND_ASSIGN(DataUseTracker);
};
diff --git a/chromium/components/metrics/data_use_tracker_unittest.cc b/chromium/components/metrics/data_use_tracker_unittest.cc
index ed32a4a2024..3e3e73c268b 100644
--- a/chromium/components/metrics/data_use_tracker_unittest.cc
+++ b/chromium/components/metrics/data_use_tracker_unittest.cc
@@ -51,7 +51,7 @@ class FakeDataUseTracker : public DataUseTracker {
base::Time GetCurrentMeasurementDate() const override {
base::Time today_for_test;
- base::Time::FromUTCString(kTodayStr, &today_for_test);
+ EXPECT_TRUE(base::Time::FromUTCString(kTodayStr, &today_for_test));
return today_for_test;
}
@@ -109,7 +109,7 @@ TEST(DataUseTrackerTest, CheckUpdateUsagePref) {
int user_pref_value = 0;
int uma_pref_value = 0;
- data_use_tracker.UpdateMetricsUsagePrefsOnUIThread("", 2 * 100, true);
+ data_use_tracker.UpdateMetricsUsagePrefs("", 2 * 100, true);
local_state.GetDictionary(prefs::kUserCellDataUse)
->GetInteger(kTodayStr, &user_pref_value);
EXPECT_EQ(2 * 100, user_pref_value);
@@ -117,7 +117,7 @@ TEST(DataUseTrackerTest, CheckUpdateUsagePref) {
->GetInteger(kTodayStr, &uma_pref_value);
EXPECT_EQ(0, uma_pref_value);
- data_use_tracker.UpdateMetricsUsagePrefsOnUIThread("UMA", 100, true);
+ data_use_tracker.UpdateMetricsUsagePrefs("UMA", 100, true);
local_state.GetDictionary(prefs::kUserCellDataUse)
->GetInteger(kTodayStr, &user_pref_value);
EXPECT_EQ(3 * 100, user_pref_value);
diff --git a/chromium/components/metrics/drive_metrics_provider_linux.cc b/chromium/components/metrics/drive_metrics_provider_linux.cc
index 35c505a3492..194e1ee4419 100644
--- a/chromium/components/metrics/drive_metrics_provider_linux.cc
+++ b/chromium/components/metrics/drive_metrics_provider_linux.cc
@@ -11,6 +11,7 @@
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
@@ -33,9 +34,11 @@ const char kRotationalFormat[] = "/sys/block/sd%c/queue/rotational";
bool DriveMetricsProvider::HasSeekPenalty(const base::FilePath& path,
bool* has_seek_penalty) {
#if defined(OS_CHROMEOS)
- std::string board = base::SysInfo::GetLsbReleaseBoard();
- if (board != "unknown" && board != "parrot") {
- // All ChromeOS devices have SSDs. Except some parrots.
+ std::string board = base::SysInfo::GetStrippedReleaseBoard();
+ // There are "parrot", "parrot_ivb" and "parrot_freon" boards that have
+ // devices with rotating disks. All other ChromeOS devices have SSDs.
+ if (board != "unknown" &&
+ !base::StartsWith(board, "parrot", base::CompareCase::SENSITIVE)) {
*has_seek_penalty = false;
return true;
}
diff --git a/chromium/components/metrics/drive_metrics_provider_mac.mm b/chromium/components/metrics/drive_metrics_provider_mac.mm
index a6a37614d0c..421419b75a0 100644
--- a/chromium/components/metrics/drive_metrics_provider_mac.mm
+++ b/chromium/components/metrics/drive_metrics_provider_mac.mm
@@ -6,7 +6,7 @@
#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
-#include <Foundation/Foundation.h>
+#import <Foundation/Foundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
#include <stdlib.h>
diff --git a/chromium/components/metrics/file_metrics_provider.cc b/chromium/components/metrics/file_metrics_provider.cc
index aa81a0cd1f0..33874efdd88 100644
--- a/chromium/components/metrics/file_metrics_provider.cc
+++ b/chromium/components/metrics/file_metrics_provider.cc
@@ -22,6 +22,7 @@
#include "components/metrics/metrics_service.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
+#include "components/variations/variations_associated_data.h"
namespace metrics {
@@ -471,6 +472,12 @@ void FileMetricsProvider::OnDidCreateMetricsLog() {
bool FileMetricsProvider::HasInitialStabilityMetrics() {
DCHECK(thread_checker_.CalledOnValidThread());
+ // Check if there is an experiment that disables stability metrics.
+ std::string unreported = variations::GetVariationParamValueByFeature(
+ base::kPersistentHistogramsFeature, "send_unreported_metrics");
+ if (unreported == "no")
+ sources_for_previous_run_.clear();
+
// 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
// the initial showing of the browser window so it's important to know the
diff --git a/chromium/components/metrics/file_metrics_provider.h b/chromium/components/metrics/file_metrics_provider.h
index 71d7cbdd4d7..ba2e9f9571a 100644
--- a/chromium/components/metrics/file_metrics_provider.h
+++ b/chromium/components/metrics/file_metrics_provider.h
@@ -21,8 +21,6 @@ class PrefRegistrySimple;
class PrefService;
namespace base {
-class MemoryMappedFile;
-class PersistentMemoryAllocator;
class TaskRunner;
}
diff --git a/chromium/components/metrics/metrics_log.cc b/chromium/components/metrics/metrics_log.cc
index 4106601817d..56701d561ab 100644
--- a/chromium/components/metrics/metrics_log.cc
+++ b/chromium/components/metrics/metrics_log.cc
@@ -390,12 +390,8 @@ std::string MetricsLog::RecordEnvironment(
#endif
SystemProfileProto::OS* os = system_profile->mutable_os();
-#if defined(OVERRIDE_OS_NAME_TO_BLIMP)
- os->set_name("Blimp");
-#else
std::string os_name = base::SysInfo::OperatingSystemName();
os->set_name(os_name);
-#endif
os->set_version(base::SysInfo::OperatingSystemVersion());
#if defined(OS_ANDROID)
diff --git a/chromium/components/metrics/metrics_log.h b/chromium/components/metrics/metrics_log.h
index 3dddfc9ce7a..2150ea4d111 100644
--- a/chromium/components/metrics/metrics_log.h
+++ b/chromium/components/metrics/metrics_log.h
@@ -22,14 +22,9 @@ class PrefRegistrySimple;
class PrefService;
namespace base {
-class DictionaryValue;
class HistogramSamples;
}
-namespace content {
-struct WebPluginInfo;
-}
-
namespace variations {
struct ActiveGroupId;
}
diff --git a/chromium/components/metrics/metrics_log_manager.cc b/chromium/components/metrics/metrics_log_manager.cc
index d06b5ae0ada..cc35bbd5346 100644
--- a/chromium/components/metrics/metrics_log_manager.cc
+++ b/chromium/components/metrics/metrics_log_manager.cc
@@ -10,6 +10,7 @@
#include "base/strings/string_util.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/persisted_logs_metrics_impl.h"
namespace metrics {
@@ -40,15 +41,17 @@ const size_t kStorageByteLimitPerLogType = 300000;
MetricsLogManager::MetricsLogManager(PrefService* local_state,
size_t max_ongoing_log_size)
: unsent_logs_loaded_(false),
- initial_log_queue_(local_state,
+ initial_log_queue_(std::unique_ptr<PersistedLogsMetricsImpl>(
+ new PersistedLogsMetricsImpl()),
+ local_state,
prefs::kMetricsInitialLogs,
- prefs::kDeprecatedMetricsInitialLogs,
kInitialLogsPersistLimit,
kStorageByteLimitPerLogType,
0),
- ongoing_log_queue_(local_state,
+ ongoing_log_queue_(std::unique_ptr<PersistedLogsMetricsImpl>(
+ new PersistedLogsMetricsImpl()),
+ local_state,
prefs::kMetricsOngoingLogs,
- prefs::kDeprecatedMetricsOngoingLogs,
kOngoingLogsPersistLimit,
kStorageByteLimitPerLogType,
max_ongoing_log_size) {}
@@ -94,12 +97,12 @@ void MetricsLogManager::DiscardCurrentLog() {
void MetricsLogManager::PauseCurrentLog() {
DCHECK(!paused_log_.get());
- paused_log_.reset(current_log_.release());
+ paused_log_ = std::move(current_log_);
}
void MetricsLogManager::ResumePausedLog() {
DCHECK(!current_log_.get());
- current_log_.reset(paused_log_.release());
+ current_log_ = std::move(paused_log_);
}
void MetricsLogManager::StoreLog(const std::string& log_data,
diff --git a/chromium/components/metrics/metrics_log_manager_unittest.cc b/chromium/components/metrics/metrics_log_manager_unittest.cc
index fbd1dd5d2b5..e30a16c50c9 100644
--- a/chromium/components/metrics/metrics_log_manager_unittest.cc
+++ b/chromium/components/metrics/metrics_log_manager_unittest.cc
@@ -13,6 +13,7 @@
#include "base/memory/ptr_util.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/persisted_logs_metrics_impl.h"
#include "components/metrics/test_metrics_service_client.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
@@ -163,8 +164,10 @@ TEST(MetricsLogManagerTest, StoreAndLoad) {
// Simulate a log having already been unsent from a previous session.
{
std::string log("proto");
- PersistedLogs ongoing_logs(&pref_service, prefs::kMetricsOngoingLogs,
- prefs::kDeprecatedMetricsOngoingLogs, 1, 1, 0);
+ PersistedLogs ongoing_logs(std::unique_ptr<PersistedLogsMetricsImpl>(
+ new PersistedLogsMetricsImpl()),
+ &pref_service, prefs::kMetricsOngoingLogs,
+ 1, 1, 0);
ongoing_logs.StoreLog(log);
ongoing_logs.SerializeLogs();
}
diff --git a/chromium/components/metrics/metrics_pref_names.cc b/chromium/components/metrics/metrics_pref_names.cc
index 1d5a7b63e84..c5ad9c6abf5 100644
--- a/chromium/components/metrics/metrics_pref_names.cc
+++ b/chromium/components/metrics/metrics_pref_names.cc
@@ -7,21 +7,6 @@
namespace metrics {
namespace prefs {
-// Array of strings that are each UMA logs that were supposed to be sent in the
-// first minute of a browser session. These logs include things like crash count
-// info, etc.
-// Deprecated by kMetricsInitialLogs.
-const char kDeprecatedMetricsInitialLogs[] =
- "user_experience_metrics.initial_logs_list";
-
-// Array of strings that are each UMA logs that were not sent because the
-// browser terminated before these accumulated metrics could be sent. These
-// logs typically include histograms and memory reports, as well as ongoing
-// user activities.
-// Deprecated by kMetricsOngoingLogs.
-const char kDeprecatedMetricsOngoingLogs[] =
- "user_experience_metrics.ongoing_logs_list";
-
// Set once, to the current epoch time, on the first run of chrome on this
// machine. Attached to metrics reports forever thereafter.
const char kInstallDate[] = "uninstall_metrics.installation_date2";
diff --git a/chromium/components/metrics/metrics_provider.h b/chromium/components/metrics/metrics_provider.h
index d76b6772ccd..21cd17f995a 100644
--- a/chromium/components/metrics/metrics_provider.h
+++ b/chromium/components/metrics/metrics_provider.h
@@ -15,7 +15,6 @@ namespace metrics {
class ChromeUserMetricsExtension;
class SystemProfileProto;
-class SystemProfileProto_Stability;
// MetricsProvider is an interface allowing different parts of the UMA protos to
// be filled out by different classes.
diff --git a/chromium/components/metrics/metrics_reporting_scheduler.cc b/chromium/components/metrics/metrics_reporting_scheduler.cc
index ab117ad01e9..625d546fea2 100644
--- a/chromium/components/metrics/metrics_reporting_scheduler.cc
+++ b/chromium/components/metrics/metrics_reporting_scheduler.cc
@@ -46,25 +46,6 @@ const double kBackoffMultiplier = 1.1;
// The maximum backoff multiplier.
const int kMaxBackoffMultiplier = 10;
-enum InitSequence {
- TIMER_FIRED_FIRST,
- INIT_TASK_COMPLETED_FIRST,
- INIT_SEQUENCE_ENUM_SIZE,
-};
-
-void LogMetricsInitSequence(InitSequence sequence) {
- UMA_HISTOGRAM_ENUMERATION("UMA.InitSequence", sequence,
- INIT_SEQUENCE_ENUM_SIZE);
-}
-
-void LogActualUploadInterval(TimeDelta interval) {
- UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.ActualLogUploadInterval",
- interval.InMinutes(),
- 1,
- base::TimeDelta::FromHours(12).InMinutes(),
- 50);
-}
-
} // anonymous namespace
MetricsReportingScheduler::MetricsReportingScheduler(
@@ -136,6 +117,19 @@ void MetricsReportingScheduler::SetUploadIntervalForTesting(
upload_interval_ = interval;
}
+void MetricsReportingScheduler::LogMetricsInitSequence(InitSequence sequence) {
+ UMA_HISTOGRAM_ENUMERATION("UMA.InitSequence", sequence,
+ INIT_SEQUENCE_ENUM_SIZE);
+}
+
+void MetricsReportingScheduler::LogActualUploadInterval(TimeDelta interval) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.ActualLogUploadInterval",
+ interval.InMinutes(),
+ 1,
+ base::TimeDelta::FromHours(12).InMinutes(),
+ 50);
+}
+
void MetricsReportingScheduler::TriggerUpload() {
// If the timer fired before the init task has completed, don't trigger the
// upload yet - wait for the init task to complete and do it then.
diff --git a/chromium/components/metrics/metrics_reporting_scheduler.h b/chromium/components/metrics/metrics_reporting_scheduler.h
index f4e3dd11b3c..e2a84634592 100644
--- a/chromium/components/metrics/metrics_reporting_scheduler.h
+++ b/chromium/components/metrics/metrics_reporting_scheduler.h
@@ -15,6 +15,7 @@
namespace metrics {
// Scheduler task to drive a MetricsService object's uploading.
+// TODO(holte): Remove this once we've switched to split schedulers.
class MetricsReportingScheduler {
public:
// Creates MetricsServiceScheduler object with the given |upload_callback|
@@ -24,7 +25,7 @@ class MetricsReportingScheduler {
MetricsReportingScheduler(
const base::Closure& upload_callback,
const base::Callback<base::TimeDelta(void)>& upload_interval_callback);
- ~MetricsReportingScheduler();
+ virtual ~MetricsReportingScheduler();
// Starts scheduling uploads. This in a no-op if the scheduler is already
// running, so it is safe to call more than once.
@@ -46,7 +47,20 @@ class MetricsReportingScheduler {
// Sets the upload interval to a specific value, exposed for unit tests.
void SetUploadIntervalForTesting(base::TimeDelta interval);
+ protected:
+ enum InitSequence {
+ TIMER_FIRED_FIRST,
+ INIT_TASK_COMPLETED_FIRST,
+ INIT_SEQUENCE_ENUM_SIZE,
+ };
+
private:
+ // Record the init sequence order histogram.
+ virtual void LogMetricsInitSequence(InitSequence sequence);
+
+ // Record the upload interval time.
+ virtual void LogActualUploadInterval(base::TimeDelta interval);
+
// Timer callback indicating it's time for the MetricsService to upload
// metrics.
void TriggerUpload();
diff --git a/chromium/components/metrics/metrics_rotation_scheduler.cc b/chromium/components/metrics/metrics_rotation_scheduler.cc
new file mode 100644
index 00000000000..c760b4a525a
--- /dev/null
+++ b/chromium/components/metrics/metrics_rotation_scheduler.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/metrics/metrics_rotation_scheduler.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
+
+namespace metrics {
+
+MetricsRotationScheduler::MetricsRotationScheduler(
+ const base::Closure& upload_callback,
+ const base::Callback<base::TimeDelta(void)>& upload_interval_callback)
+ : MetricsScheduler(upload_callback),
+ init_task_complete_(false),
+ waiting_for_init_task_complete_(false),
+ upload_interval_callback_(upload_interval_callback) {}
+
+MetricsRotationScheduler::~MetricsRotationScheduler() {}
+
+void MetricsRotationScheduler::InitTaskComplete() {
+ DCHECK(!init_task_complete_);
+ init_task_complete_ = true;
+ if (waiting_for_init_task_complete_) {
+ waiting_for_init_task_complete_ = false;
+ TriggerTask();
+ } else {
+ LogMetricsInitSequence(INIT_TASK_COMPLETED_FIRST);
+ }
+}
+
+void MetricsRotationScheduler::RotationFinished() {
+ TaskDone(upload_interval_callback_.Run());
+}
+
+void MetricsRotationScheduler::LogMetricsInitSequence(InitSequence sequence) {
+ UMA_HISTOGRAM_ENUMERATION("UMA.InitSequence", sequence,
+ INIT_SEQUENCE_ENUM_SIZE);
+}
+
+void MetricsRotationScheduler::TriggerTask() {
+ // If the timer fired before the init task has completed, don't trigger the
+ // upload yet - wait for the init task to complete and do it then.
+ if (!init_task_complete_) {
+ LogMetricsInitSequence(TIMER_FIRED_FIRST);
+ waiting_for_init_task_complete_ = true;
+ return;
+ }
+ MetricsScheduler::TriggerTask();
+}
+
+} // namespace metrics
diff --git a/chromium/components/metrics/metrics_rotation_scheduler.h b/chromium/components/metrics/metrics_rotation_scheduler.h
new file mode 100644
index 00000000000..20a2a4499fc
--- /dev/null
+++ b/chromium/components/metrics/metrics_rotation_scheduler.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_METRICS_METRICS_ROTATION_SCHEDULER_H_
+#define COMPONENTS_METRICS_METRICS_ROTATION_SCHEDULER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "components/metrics/metrics_scheduler.h"
+
+namespace metrics {
+
+// Scheduler task to drive a MetricsService object's uploading.
+class MetricsRotationScheduler : public MetricsScheduler {
+ public:
+ // Creates MetricsRotationScheduler object with the given |rotation_callback|
+ // callback to call when log rotation should happen and |interval_callback|
+ // to determine the interval between rotations in steady state.
+ // |rotation_callback| must arrange to call RotationFinished on completion.
+ MetricsRotationScheduler(
+ const base::Closure& rotation_callback,
+ const base::Callback<base::TimeDelta(void)>& interval_callback);
+ ~MetricsRotationScheduler() override;
+
+ // Callback from MetricsService when the startup init task has completed.
+ void InitTaskComplete();
+
+ // Callback from MetricsService when a triggered rotation finishes.
+ void RotationFinished();
+
+ protected:
+ enum InitSequence {
+ TIMER_FIRED_FIRST,
+ INIT_TASK_COMPLETED_FIRST,
+ INIT_SEQUENCE_ENUM_SIZE,
+ };
+
+ private:
+ // Record the init sequence order histogram.
+ virtual void LogMetricsInitSequence(InitSequence sequence);
+
+ // MetricsScheduler:
+ void TriggerTask() override;
+
+ // Whether the InitTaskComplete() callback has been called.
+ bool init_task_complete_;
+
+ // Whether the initial scheduled upload timer has fired before the init task
+ // has been completed.
+ bool waiting_for_init_task_complete_;
+
+ // Callback function used to get the standard upload time.
+ base::Callback<base::TimeDelta(void)> upload_interval_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsRotationScheduler);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_METRICS_ROTATION_SCHEDULER_H_
diff --git a/chromium/components/metrics/metrics_scheduler.cc b/chromium/components/metrics/metrics_scheduler.cc
new file mode 100644
index 00000000000..86d11579c91
--- /dev/null
+++ b/chromium/components/metrics/metrics_scheduler.cc
@@ -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.
+
+#include "components/metrics/metrics_scheduler.h"
+
+#include "build/build_config.h"
+
+namespace metrics {
+
+namespace {
+
+// The delay, in seconds, after startup before sending the first log message.
+#if defined(OS_ANDROID) || defined(OS_IOS)
+// Sessions are more likely to be short on a mobile device, so handle the
+// initial log quickly.
+const int kInitialIntervalSeconds = 15;
+#else
+const int kInitialIntervalSeconds = 60;
+#endif
+
+} // namespace
+
+MetricsScheduler::MetricsScheduler(const base::Closure& task_callback)
+ : task_callback_(task_callback),
+ interval_(base::TimeDelta::FromSeconds(kInitialIntervalSeconds)),
+ running_(false),
+ callback_pending_(false) {}
+
+MetricsScheduler::~MetricsScheduler() {}
+
+void MetricsScheduler::Start() {
+ running_ = true;
+ ScheduleNextTask();
+}
+
+void MetricsScheduler::Stop() {
+ running_ = false;
+ if (timer_.IsRunning())
+ timer_.Stop();
+}
+
+void MetricsScheduler::TaskDone(base::TimeDelta next_interval) {
+ DCHECK(callback_pending_);
+ callback_pending_ = false;
+ interval_ = next_interval;
+ if (running_)
+ ScheduleNextTask();
+}
+
+void MetricsScheduler::TriggerTask() {
+ callback_pending_ = true;
+ task_callback_.Run();
+}
+
+void MetricsScheduler::ScheduleNextTask() {
+ DCHECK(running_);
+ if (timer_.IsRunning() || callback_pending_)
+ return;
+
+ timer_.Start(FROM_HERE, interval_, this, &MetricsScheduler::TriggerTask);
+}
+
+} // namespace metrics
diff --git a/chromium/components/metrics/metrics_scheduler.h b/chromium/components/metrics/metrics_scheduler.h
new file mode 100644
index 00000000000..fc031fb8ddc
--- /dev/null
+++ b/chromium/components/metrics/metrics_scheduler.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_METRICS_METRICS_SCHEDULER_H_
+#define COMPONENTS_METRICS_METRICS_SCHEDULER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace metrics {
+
+// Scheduler task to drive a MetricsService object's uploading.
+class MetricsScheduler {
+ public:
+ // Creates MetricsScheduler object with the given |task_callback|
+ // callback to call when a task should happen.
+ explicit MetricsScheduler(const base::Closure& task_callback);
+ virtual ~MetricsScheduler();
+
+ // Starts scheduling uploads. This in a no-op if the scheduler is already
+ // running, so it is safe to call more than once.
+ void Start();
+
+ // Stops scheduling uploads.
+ void Stop();
+
+ protected:
+ // Subclasses should provide task_callback with a wrapper to call this with.
+ // This indicates the triggered task was completed/cancelled and the next
+ // call can be scheduled.
+ void TaskDone(base::TimeDelta next_interval);
+
+ // Called by the Timer when it's time to run the task.
+ virtual void TriggerTask();
+
+ private:
+ // Schedules a future call to TriggerTask if one isn't already pending.
+ void ScheduleNextTask();
+
+ // The method to call when task should happen.
+ const base::Closure task_callback_;
+
+ // Uses a one-shot timer rather than a repeating one because the task may be
+ // async, and the length of the interval may change.
+ base::OneShotTimer timer_;
+
+ // The interval between being told an task is done and starting the next task.
+ base::TimeDelta interval_;
+
+ // Indicates that the scheduler is running (i.e., that Start has been called
+ // more recently than Stop).
+ bool running_;
+
+ // Indicates that the last triggered task hasn't resolved yet.
+ bool callback_pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsScheduler);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_METRICS_SCHEDULER_H_
diff --git a/chromium/components/metrics/metrics_service.cc b/chromium/components/metrics/metrics_service.cc
index b896440857a..7a9b0400671 100644
--- a/chromium/components/metrics/metrics_service.cc
+++ b/chromium/components/metrics/metrics_service.cc
@@ -131,6 +131,7 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/feature_list.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_base.h"
@@ -149,14 +150,19 @@
#include "base/time/time.h"
#include "base/tracked_objects.h"
#include "build/build_config.h"
+#include "components/browser_watcher/stability_data_names.h"
+#include "components/browser_watcher/stability_debugging.h"
#include "components/metrics/data_use_tracker.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/metrics_log_manager.h"
#include "components/metrics/metrics_log_uploader.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_reporting_scheduler.h"
+#include "components/metrics/metrics_rotation_scheduler.h"
#include "components/metrics/metrics_service_client.h"
#include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/metrics_upload_scheduler.h"
+#include "components/metrics/url_constants.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/variations/entropy_provider.h"
@@ -165,6 +171,17 @@ namespace metrics {
namespace {
+// This feature moves the upload schedule to a seperate schedule from the
+// log rotation schedule. This may change upload timing slightly, but
+// would allow some compartmentalization of uploader logic to allow more
+// code reuse between different metrics services.
+const base::Feature kUploadSchedulerFeature{"UMAUploadScheduler",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool HasUploadScheduler() {
+ return base::FeatureList::IsEnabled(kUploadSchedulerFeature);
+}
+
// Check to see that we're being called on only one thread.
bool IsSingleThreaded() {
static base::PlatformThreadId thread_id = 0;
@@ -222,6 +239,7 @@ ResponseStatus ResponseCodeToStatus(int response_code) {
void MarkAppCleanShutdownAndCommit(CleanExitBeacon* clean_exit_beacon,
PrefService* local_state) {
clean_exit_beacon->WriteBeaconValue(true);
+ // Note: the in-memory MetricsService::execution_phase_ is not updated.
local_state->SetInteger(prefs::kStabilityExecutionPhase,
MetricsService::SHUTDOWN_COMPLETE);
// Start writing right away (write happens on a different thread).
@@ -304,13 +322,25 @@ void MetricsService::InitializeMetricsRecordingState() {
base::Closure upload_callback =
base::Bind(&MetricsService::StartScheduledUpload,
self_ptr_factory_.GetWeakPtr());
- scheduler_.reset(
- new MetricsReportingScheduler(
- upload_callback,
- // MetricsServiceClient outlives MetricsService, and
- // MetricsReportingScheduler is tied to the lifetime of |this|.
- base::Bind(&MetricsServiceClient::GetStandardUploadInterval,
- base::Unretained(client_))));
+
+ if (HasUploadScheduler()) {
+ rotation_scheduler_.reset(new MetricsRotationScheduler(
+ upload_callback,
+ // MetricsServiceClient outlives MetricsService, and
+ // MetricsRotationScheduler is tied to the lifetime of |this|.
+ base::Bind(&MetricsServiceClient::GetStandardUploadInterval,
+ base::Unretained(client_))));
+ base::Closure send_next_log_callback = base::Bind(
+ &MetricsService::SendNextLog, self_ptr_factory_.GetWeakPtr());
+ upload_scheduler_.reset(new MetricsUploadScheduler(send_next_log_callback));
+ } else {
+ scheduler_.reset(new MetricsReportingScheduler(
+ upload_callback,
+ // MetricsServiceClient outlives MetricsService, and
+ // MetricsReportingScheduler is tied to the lifetime of |this|.
+ base::Bind(&MetricsServiceClient::GetStandardUploadInterval,
+ base::Unretained(client_))));
+ }
for (MetricsProvider* provider : metrics_providers_)
provider->Init();
@@ -445,18 +475,21 @@ void MetricsService::OnApplicationNotIdle() {
}
void MetricsService::RecordStartOfSessionEnd() {
- LogCleanShutdown();
- RecordBooleanPrefValue(prefs::kStabilitySessionEndCompleted, false);
+ LogCleanShutdown(false);
}
void MetricsService::RecordCompletedSessionEnd() {
- LogCleanShutdown();
- RecordBooleanPrefValue(prefs::kStabilitySessionEndCompleted, true);
+ LogCleanShutdown(true);
}
#if defined(OS_ANDROID) || defined(OS_IOS)
void MetricsService::OnAppEnterBackground() {
- scheduler_->Stop();
+ if (upload_scheduler_) {
+ rotation_scheduler_->Stop();
+ upload_scheduler_->Stop();
+ } else {
+ scheduler_->Stop();
+ }
MarkAppCleanShutdownAndCommit(&clean_exit_beacon_, local_state_);
@@ -480,6 +513,9 @@ void MetricsService::OnAppEnterBackground() {
void MetricsService::OnAppEnterForeground() {
clean_exit_beacon_.WriteBeaconValue(false);
+ // Restore the execution phase stored in prefs. It was altered in
+ // OnAppEnterBackground by a call to MarkAppCleanShutdownAndCommit.
+ local_state_->SetInteger(prefs::kStabilityExecutionPhase, execution_phase_);
StartSchedulerIfNecessary();
}
#else
@@ -495,6 +531,8 @@ void MetricsService::SetExecutionPhase(ExecutionPhase execution_phase,
PrefService* local_state) {
execution_phase_ = execution_phase;
local_state->SetInteger(prefs::kStabilityExecutionPhase, execution_phase_);
+ browser_watcher::SetStabilityDataInt(
+ browser_watcher::kStabilityExecutionPhase, execution_phase_);
}
void MetricsService::RecordBreakpadRegistration(bool success) {
@@ -521,8 +559,6 @@ void MetricsService::ClearSavedStabilityMetrics() {
local_state_->SetInteger(prefs::kStabilityCrashCount, 0);
local_state_->SetInteger(prefs::kStabilityDebuggerPresent, 0);
local_state_->SetInteger(prefs::kStabilityDebuggerNotPresent, 0);
- local_state_->SetInteger(prefs::kStabilityExecutionPhase,
- UNINITIALIZED_PHASE);
local_state_->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0);
local_state_->SetInteger(prefs::kStabilityLaunchCount, 0);
local_state_->SetBoolean(prefs::kStabilitySessionEndCompleted, true);
@@ -536,14 +572,15 @@ void MetricsService::PushExternalLog(const std::string& log) {
log_manager_.StoreLog(log, MetricsLog::ONGOING_LOG);
}
-UpdateUsagePrefCallbackType MetricsService::GetDataUseForwardingCallback() {
+void MetricsService::UpdateMetricsUsagePrefs(const std::string& service_name,
+ int message_size,
+ bool is_cellular) {
DCHECK(IsSingleThreaded());
-
if (data_use_tracker_) {
- return data_use_tracker_->GetDataUseForwardingCallback(
- base::ThreadTaskRunnerHandle::Get());
+ data_use_tracker_->UpdateMetricsUsagePrefs(service_name,
+ message_size,
+ is_cellular);
}
- return UpdateUsagePrefCallbackType();
}
void MetricsService::MergeHistogramDeltas() {
@@ -582,6 +619,13 @@ void MetricsService::InitializeMetricsState() {
// Reset flag, and wait until we call LogNeedForCleanShutdown() before
// monitoring.
clean_exit_beacon_.WriteBeaconValue(true);
+ // TODO(rtenneti): On windows, consider saving/getting execution_phase from
+ // the registry.
+ int execution_phase =
+ local_state_->GetInteger(prefs::kStabilityExecutionPhase);
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Chrome.Browser.CrashedExecutionPhase",
+ execution_phase);
+ SetExecutionPhase(UNINITIALIZED_PHASE, local_state_);
}
// ProvidersHaveInitialStabilityMetrics is called first to ensure it is never
@@ -591,13 +635,6 @@ void MetricsService::InitializeMetricsState() {
!clean_exit_beacon_.exited_cleanly();
bool has_initial_stability_log = false;
if (is_initial_stability_log_required) {
- // TODO(rtenneti): On windows, consider saving/getting execution_phase from
- // the registry.
- int execution_phase =
- local_state_->GetInteger(prefs::kStabilityExecutionPhase);
- UMA_HISTOGRAM_SPARSE_SLOWLY("Chrome.Browser.CrashedExecutionPhase",
- execution_phase);
-
// If the previous session didn't exit cleanly, or if any provider
// explicitly requests it, prepare an initial stability log -
// provided UMA is enabled.
@@ -682,7 +719,11 @@ void MetricsService::FinishedInitTask() {
NotifyOnDidCreateMetricsLog();
}
- scheduler_->InitTaskComplete();
+ if (upload_scheduler_) {
+ rotation_scheduler_->InitTaskComplete();
+ } else {
+ scheduler_->InitTaskComplete();
+ }
}
void MetricsService::GetUptimes(PrefService* pref,
@@ -818,7 +859,12 @@ void MetricsService::StartSchedulerIfNecessary() {
// persisted on shutdown or backgrounding.
if (recording_active() &&
(reporting_active() || state_ < SENDING_LOGS)) {
- scheduler_->Start();
+ if (upload_scheduler_) {
+ rotation_scheduler_->Start();
+ upload_scheduler_->Start();
+ } else {
+ scheduler_->Start();
+ }
}
}
@@ -835,15 +881,25 @@ void MetricsService::StartScheduledUpload() {
if (idle_since_last_transmission_ ||
!recording_active() ||
(!reporting_active() && state_ >= SENDING_LOGS)) {
- scheduler_->Stop();
- scheduler_->UploadCancelled();
+ if (upload_scheduler_) {
+ rotation_scheduler_->Stop();
+ rotation_scheduler_->RotationFinished();
+ } else {
+ scheduler_->Stop();
+ scheduler_->UploadCancelled();
+ }
return;
}
// If there are unsent logs, send the next one. If not, start the asynchronous
// process of finalizing the current log for upload.
if (state_ == SENDING_LOGS && log_manager_.has_unsent_logs()) {
- SendNextLog();
+ if (upload_scheduler_) {
+ upload_scheduler_->Start();
+ rotation_scheduler_->RotationFinished();
+ } else {
+ SendNextLog();
+ }
} else {
// There are no logs left to send, so start creating a new one.
client_->CollectFinalMetricsForLog(
@@ -862,8 +918,13 @@ void MetricsService::OnFinalLogInfoCollectionDone() {
// Abort if metrics were turned off during the final info gathering.
if (!recording_active()) {
- scheduler_->Stop();
- scheduler_->UploadCancelled();
+ if (upload_scheduler_) {
+ rotation_scheduler_->Stop();
+ rotation_scheduler_->RotationFinished();
+ } else {
+ scheduler_->Stop();
+ scheduler_->UploadCancelled();
+ }
return;
}
@@ -874,21 +935,37 @@ void MetricsService::OnFinalLogInfoCollectionDone() {
CloseCurrentLog();
OpenNewLog();
}
- SendNextLog();
+ if (upload_scheduler_) {
+ upload_scheduler_->Start();
+ rotation_scheduler_->RotationFinished();
+ } else {
+ SendNextLog();
+ }
}
void MetricsService::SendNextLog() {
DCHECK_EQ(SENDING_LOGS, state_);
if (!reporting_active()) {
- scheduler_->Stop();
- scheduler_->UploadCancelled();
+ if (upload_scheduler_) {
+ upload_scheduler_->Stop();
+ upload_scheduler_->UploadCancelled();
+ } else {
+ scheduler_->Stop();
+ scheduler_->UploadCancelled();
+ }
return;
}
if (!log_manager_.has_unsent_logs()) {
// Should only get here if serializing the log failed somehow.
- // Just tell the scheduler it was uploaded and wait for the next log
- // interval.
- scheduler_->UploadFinished(true, log_manager_.has_unsent_logs());
+ if (upload_scheduler_) {
+ upload_scheduler_->Stop();
+ // Reset backoff interval
+ upload_scheduler_->UploadFinished(true);
+ } else {
+ // Just tell the scheduler it was uploaded and wait for the next log
+ // interval.
+ scheduler_->UploadFinished(true, log_manager_.has_unsent_logs());
+ }
return;
}
if (!log_manager_.has_staged_log())
@@ -901,7 +978,11 @@ void MetricsService::SendNextLog() {
if (is_cellular_logic && data_use_tracker_ &&
!data_use_tracker_->ShouldUploadLogOnCellular(
log_manager_.staged_log_hash().size())) {
- scheduler_->UploadCancelled();
+ if (upload_scheduler_) {
+ upload_scheduler_->UploadOverDataUsageCap();
+ } else {
+ scheduler_->UploadCancelled();
+ }
upload_canceled = true;
} else {
SendStagedLog();
@@ -914,12 +995,14 @@ void MetricsService::SendNextLog() {
bool MetricsService::ProvidersHaveInitialStabilityMetrics() {
// Check whether any metrics provider has initial stability metrics.
- for (MetricsProvider* provider : metrics_providers_) {
- if (provider->HasInitialStabilityMetrics())
- return true;
- }
+ // All providers are queried (rather than stopping after the first "true"
+ // response) in case they do any kind of setup work in preparation for
+ // the later call to RecordInitialHistogramSnapshots().
+ bool has_stability_metrics = false;
+ for (MetricsProvider* provider : metrics_providers_)
+ has_stability_metrics |= provider->HasInitialStabilityMetrics();
- return false;
+ return has_stability_metrics;
}
bool MetricsService::PrepareInitialStabilityLog(
@@ -1002,6 +1085,8 @@ void MetricsService::SendStagedLog() {
if (!log_uploader_) {
log_uploader_ = client_->CreateUploader(
+ client_->GetMetricsServerUrl(),
+ metrics::kDefaultMetricsMimeType,
base::Bind(&MetricsService::OnLogUploadComplete,
self_ptr_factory_.GetWeakPtr()));
}
@@ -1050,10 +1135,15 @@ void MetricsService::OnLogUploadComplete(int response_code) {
// Error 400 indicates a problem with the log, not with the server, so
// don't consider that a sign that the server is in trouble.
bool server_is_healthy = upload_succeeded || response_code == 400;
- scheduler_->UploadFinished(server_is_healthy, log_manager_.has_unsent_logs());
-
- if (server_is_healthy)
- client_->OnLogUploadComplete();
+ if (upload_scheduler_) {
+ if (!log_manager_.has_unsent_logs()) {
+ upload_scheduler_->Stop();
+ }
+ upload_scheduler_->UploadFinished(server_is_healthy);
+ } else {
+ scheduler_->UploadFinished(server_is_healthy,
+ log_manager_.has_unsent_logs());
+ }
}
void MetricsService::IncrementPrefValue(const char* path) {
@@ -1204,20 +1294,16 @@ void MetricsService::RecordCurrentStabilityHistograms() {
provider->RecordInitialHistogramSnapshots(&histogram_snapshot_manager_);
}
-void MetricsService::LogCleanShutdown() {
+void MetricsService::LogCleanShutdown(bool end_completed) {
+ DCHECK(IsSingleThreaded());
// 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);
RecordCurrentState(local_state_);
- local_state_->SetInteger(prefs::kStabilityExecutionPhase,
- MetricsService::SHUTDOWN_COMPLETE);
-}
-
-void MetricsService::RecordBooleanPrefValue(const char* path, bool value) {
- DCHECK(IsSingleThreaded());
- local_state_->SetBoolean(path, value);
+ SetExecutionPhase(MetricsService::SHUTDOWN_COMPLETE, local_state_);
+ local_state_->SetBoolean(prefs::kStabilitySessionEndCompleted, end_completed);
RecordCurrentState(local_state_);
}
@@ -1226,10 +1312,4 @@ void MetricsService::RecordCurrentState(PrefService* pref) {
base::Time::Now().ToTimeT());
}
-void MetricsService::SkipAndDiscardUpload() {
- log_manager_.DiscardStagedLog();
- scheduler_->UploadCancelled();
- log_upload_in_progress_ = false;
-}
-
} // namespace metrics
diff --git a/chromium/components/metrics/metrics_service.h b/chromium/components/metrics/metrics_service.h
index 3f19a5b3490..e890442e2cf 100644
--- a/chromium/components/metrics/metrics_service.h
+++ b/chromium/components/metrics/metrics_service.h
@@ -38,7 +38,6 @@ class PrefService;
class PrefRegistrySimple;
namespace base {
-class DictionaryValue;
class HistogramSamples;
class PrefService;
}
@@ -47,14 +46,12 @@ namespace variations {
struct ActiveGroupId;
}
-namespace net {
-class URLFetcher;
-}
-
namespace metrics {
class MetricsLogUploader;
class MetricsReportingScheduler;
+class MetricsRotationScheduler;
+class MetricsUploadScheduler;
class MetricsServiceAccessor;
class MetricsServiceClient;
class MetricsStateManager;
@@ -206,9 +203,10 @@ class MetricsService : public base::HistogramFlattener {
// Pushes a log that has been generated by an external component.
void PushExternalLog(const std::string& log);
- // Returns a callback to data use pref updating function which can be called
- // from any thread, but this function should be called on UI thread.
- UpdateUsagePrefCallbackType GetDataUseForwardingCallback();
+ // Updates data usage tracking prefs with the specified values.
+ void UpdateMetricsUsagePrefs(const std::string& service_name,
+ int message_size,
+ bool is_cellular);
// Merge any data from metrics providers into the global StatisticsRecorder.
void MergeHistogramDeltas();
@@ -277,7 +275,7 @@ class MetricsService : public base::HistogramFlattener {
// call to this function. Also updates the cumulative uptime metric (stored
// as a pref) for uninstall. Uptimes are measured using TimeTicks, which
// guarantees that it is monotonic and does not jump if the user changes
- // his/her clock. The TimeTicks implementation also makes the clock not
+ // their clock. The TimeTicks implementation also makes the clock not
// count time the computer is suspended.
void GetUptimes(PrefService* pref,
base::TimeDelta* incremental_uptime,
@@ -365,15 +363,12 @@ class MetricsService : public base::HistogramFlattener {
void IncrementLongPrefsValue(const char* path);
// Records that the browser was shut down cleanly.
- void LogCleanShutdown();
+ void LogCleanShutdown(bool end_completed);
// Records state that should be periodically saved, like uptime and
// buffered plugin stability statistics.
void RecordCurrentState(PrefService* pref);
- // Sets the value of the specified path in prefs and schedules a save.
- void RecordBooleanPrefValue(const char* path, bool value);
-
// Notifies observers on a synthetic trial list change.
void NotifySyntheticTrialObservers();
@@ -396,10 +391,6 @@ class MetricsService : public base::HistogramFlattener {
// i.e., histograms with the |kUmaStabilityHistogramFlag| flag set.
void RecordCurrentStabilityHistograms();
- // Skips staged upload and discards the log. Used in case of unsuccessful
- // upload or intentional sampling of logs.
- void SkipAndDiscardUpload();
-
// Manager for the various in-flight logs.
MetricsLogManager log_manager_;
@@ -455,8 +446,13 @@ class MetricsService : public base::HistogramFlattener {
// A number that identifies the how many times the app has been launched.
int session_id_;
- // The scheduler for determining when uploads should happen.
+ // The scheduler for determining when log rotations+uploads should happen.
+ // TODO(holte): Remove this once we've switched to split schedulers.
std::unique_ptr<MetricsReportingScheduler> scheduler_;
+ // The scheduler for determining when log rotations should happen.
+ std::unique_ptr<MetricsRotationScheduler> rotation_scheduler_;
+ // The scheduler for determining when uploads should happen.
+ std::unique_ptr<MetricsUploadScheduler> upload_scheduler_;
// Stores the time of the first call to |GetUptimes()|.
base::TimeTicks first_updated_time_;
diff --git a/chromium/components/metrics/metrics_service_client.cc b/chromium/components/metrics/metrics_service_client.cc
index f4c38685f45..1bb75e52dbe 100644
--- a/chromium/components/metrics/metrics_service_client.cc
+++ b/chromium/components/metrics/metrics_service_client.cc
@@ -4,6 +4,8 @@
#include "components/metrics/metrics_service_client.h"
+#include "components/metrics/url_constants.h"
+
namespace metrics {
base::string16 MetricsServiceClient::GetRegistryBackupKey() {
@@ -22,4 +24,8 @@ bool MetricsServiceClient::IsUMACellularUploadLogicEnabled() {
return false;
}
+std::string MetricsServiceClient::GetMetricsServerUrl() {
+ return metrics::kDefaultMetricsServerUrl;
+}
+
} // namespace metrics
diff --git a/chromium/components/metrics/metrics_service_client.h b/chromium/components/metrics/metrics_service_client.h
index ea675b3521a..c1c28457a67 100644
--- a/chromium/components/metrics/metrics_service_client.h
+++ b/chromium/components/metrics/metrics_service_client.h
@@ -65,8 +65,8 @@ class MetricsServiceClient {
// ownership.
virtual void OnEnvironmentUpdate(std::string* serialized_environment) {}
- // Called by the metrics service when a log has been uploaded.
- virtual void OnLogUploadComplete() = 0;
+ // Called by the metrics service to record a clean shutdown.
+ virtual void OnLogCleanShutdown() {}
// Gathers metrics that will be filled into the system profile protobuf,
// calling |done_callback| when complete.
@@ -79,9 +79,14 @@ class MetricsServiceClient {
virtual void CollectFinalMetricsForLog(
const base::Closure& done_callback) = 0;
+ // Get the URL of the metrics server.
+ virtual std::string GetMetricsServerUrl();
+
// Creates a MetricsLogUploader with the specified parameters (see comments on
// MetricsLogUploader for details).
virtual std::unique_ptr<MetricsLogUploader> CreateUploader(
+ const std::string& server_url,
+ const std::string& mime_type,
const base::Callback<void(int)>& on_upload_complete) = 0;
// Returns the standard interval between upload attempts.
diff --git a/chromium/components/metrics/metrics_upload_scheduler.cc b/chromium/components/metrics/metrics_upload_scheduler.cc
new file mode 100644
index 00000000000..45e713540a7
--- /dev/null
+++ b/chromium/components/metrics/metrics_upload_scheduler.cc
@@ -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.
+
+#include "components/metrics/metrics_upload_scheduler.h"
+
+#include <stdint.h>
+
+#include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
+#include "components/metrics/metrics_scheduler.h"
+
+namespace metrics {
+
+namespace {
+
+// The delay, in seconds, between uploading when there are queued logs from
+// previous sessions to send. Sending in a burst is better on a mobile device,
+// since keeping the radio on is very expensive, so prefer to keep this short.
+const int kUnsentLogsIntervalSeconds = 3;
+
+// When uploading metrics to the server fails, we progressively wait longer and
+// longer before sending the next log. This backoff process helps reduce load
+// on a server that is having issues.
+// The following is the multiplier we use to expand that inter-log duration.
+const double kBackoffMultiplier = 1.1;
+
+// The maximum backoff interval in minutes.
+const int kMaxBackoffIntervalMinutes = 10;
+
+// Minutes to wait if we are unable to upload due to data usage cap.
+const int kOverDataUsageIntervalMinutes = 5;
+
+// Increases the upload interval each time it's called, to handle the case
+// where the server is having issues.
+base::TimeDelta BackOffUploadInterval(base::TimeDelta interval) {
+ DCHECK_GT(kBackoffMultiplier, 1.0);
+ interval = base::TimeDelta::FromMicroseconds(static_cast<int64_t>(
+ kBackoffMultiplier * interval.InMicroseconds()));
+
+ base::TimeDelta max_interval =
+ base::TimeDelta::FromMinutes(kMaxBackoffIntervalMinutes);
+ if (interval > max_interval || interval.InSeconds() < 0) {
+ interval = max_interval;
+ }
+ return interval;
+}
+
+} // namespace
+
+MetricsUploadScheduler::MetricsUploadScheduler(
+ const base::Closure& upload_callback)
+ : MetricsScheduler(upload_callback),
+ upload_interval_(
+ base::TimeDelta::FromSeconds(kUnsentLogsIntervalSeconds)) {}
+
+MetricsUploadScheduler::~MetricsUploadScheduler() {}
+
+void MetricsUploadScheduler::UploadFinished(bool server_is_healthy) {
+ // If the server is having issues, back off. Otherwise, reset to default
+ // (unless there are more logs to send, in which case the next upload should
+ // happen sooner).
+ if (!server_is_healthy) {
+ upload_interval_ = BackOffUploadInterval(upload_interval_);
+ } else {
+ upload_interval_ = base::TimeDelta::FromSeconds(kUnsentLogsIntervalSeconds);
+ }
+ TaskDone(upload_interval_);
+}
+
+void MetricsUploadScheduler::UploadCancelled() {
+ TaskDone(upload_interval_);
+}
+
+void MetricsUploadScheduler::UploadOverDataUsageCap() {
+ TaskDone(base::TimeDelta::FromMinutes(kOverDataUsageIntervalMinutes));
+}
+
+void MetricsUploadScheduler::LogActualUploadInterval(base::TimeDelta interval) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.ActualLogUploadInterval",
+ interval.InMinutes(), 1,
+ base::TimeDelta::FromHours(12).InMinutes(), 50);
+}
+
+void MetricsUploadScheduler::TriggerTask() {
+ if (!last_upload_finish_time_.is_null()) {
+ LogActualUploadInterval(base::TimeTicks::Now() - last_upload_finish_time_);
+ last_upload_finish_time_ = base::TimeTicks();
+ }
+ MetricsScheduler::TriggerTask();
+}
+
+} // namespace metrics
diff --git a/chromium/components/metrics/metrics_upload_scheduler.h b/chromium/components/metrics/metrics_upload_scheduler.h
new file mode 100644
index 00000000000..e175e06fb96
--- /dev/null
+++ b/chromium/components/metrics/metrics_upload_scheduler.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_METRICS_METRICS_UPLOAD_SCHEDULER_H_
+#define COMPONENTS_METRICS_METRICS_UPLOAD_SCHEDULER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "components/metrics/metrics_scheduler.h"
+
+namespace metrics {
+
+// Scheduler task to drive a MetricsService object's uploading.
+class MetricsUploadScheduler : public MetricsScheduler {
+ public:
+ // Creates MetricsUploadScheduler object with the given |upload_callback|
+ // callback to call when uploading should happen. The callback must
+ // arrange to call either UploadFinished or UploadCancelled on completion.
+ explicit MetricsUploadScheduler(const base::Closure& upload_callback);
+ ~MetricsUploadScheduler() override;
+
+ // Callback from MetricsService when a triggered upload finishes.
+ void UploadFinished(bool server_is_healthy);
+
+ // Callback from MetricsService when an upload is cancelled.
+ void UploadCancelled();
+
+ // Callback from MetricsService when an upload is cancelled because it would
+ // be over the allowed data usage cap.
+ void UploadOverDataUsageCap();
+
+ private:
+ // Record the upload interval time.
+ virtual void LogActualUploadInterval(base::TimeDelta interval);
+
+ // MetricsScheduler:
+ void TriggerTask() override;
+
+ // The tick count of the last time log upload has been finished and null if no
+ // upload has been done yet.
+ base::TimeTicks last_upload_finish_time_;
+
+ // Time to wait for the next upload attempt.
+ base::TimeDelta upload_interval_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsUploadScheduler);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_METRICS_UPLOAD_SCHEDULER_H_
diff --git a/chromium/components/metrics/net/DEPS b/chromium/components/metrics/net/DEPS
index f0349cc54c1..df18a66dd19 100644
--- a/chromium/components/metrics/net/DEPS
+++ b/chromium/components/metrics/net/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+chromeos/dbus",
"+chromeos/network",
"+components/data_use_measurement/core",
"+components/variations",
diff --git a/chromium/components/metrics/net/net_metrics_log_uploader.cc b/chromium/components/metrics/net/net_metrics_log_uploader.cc
index 7f76da2c082..5b2c95e5c14 100644
--- a/chromium/components/metrics/net/net_metrics_log_uploader.cc
+++ b/chromium/components/metrics/net/net_metrics_log_uploader.cc
@@ -39,9 +39,9 @@ void NetMetricsLogUploader::UploadLog(const std::string& compressed_log_data,
DCHECK(!log_hash.empty());
current_fetch_->AddExtraRequestHeader("X-Chrome-UMA-Log-SHA1: " + log_hash);
- // We already drop cookies server-side, but we might as well strip them out
- // client-side as well.
+ // Drop cookies and auth data.
current_fetch_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA |
net::LOAD_DO_NOT_SEND_COOKIES);
current_fetch_->Start();
}
diff --git a/chromium/components/metrics/net/network_metrics_provider.cc b/chromium/components/metrics/net/network_metrics_provider.cc
index aa83846b851..bf565300894 100644
--- a/chromium/components/metrics/net/network_metrics_provider.cc
+++ b/chromium/components/metrics/net/network_metrics_provider.cc
@@ -9,6 +9,8 @@
#include <string>
#include <vector>
+#include "base/bind_helpers.h"
+#include "base/callback_forward.h"
#include "base/compiler_specific.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
@@ -18,6 +20,7 @@
#include "base/task_runner_util.h"
#include "build/build_config.h"
#include "net/base/net_errors.h"
+#include "net/nqe/network_quality_estimator.h"
#if defined(OS_CHROMEOS)
#include "components/metrics/net/wifi_access_point_info_provider_chromeos.h"
@@ -25,25 +28,145 @@
namespace metrics {
+namespace {
+
+// Gets the network quality estimator from |network_quality_estimator_provider|,
+// and provides it to the |callback|.
+void GetAndSetNetworkQualityEstimator(
+ const base::Callback<void(net::NetworkQualityEstimator*)>& callback,
+ NetworkMetricsProvider::NetworkQualityEstimatorProvider*
+ network_quality_estimator_provider) {
+ callback.Run(
+ network_quality_estimator_provider->GetNetworkQualityEstimator());
+}
+
+} // namespace
+
+// Listens to the changes in the effective conection type.
+class NetworkMetricsProvider::EffectiveConnectionTypeObserver
+ : public net::NetworkQualityEstimator::EffectiveConnectionTypeObserver {
+ public:
+ // |network_quality_estimator| is used to provide the network quality
+ // estimates. Guaranteed to be non-null. |callback| is run on
+ // |callback_task_runner|, and provides notifications about the changes in the
+ // effective connection type.
+ EffectiveConnectionTypeObserver(
+ base::Callback<void(net::EffectiveConnectionType)> callback,
+ const scoped_refptr<base::SequencedTaskRunner>& callback_task_runner)
+ : network_quality_estimator_(nullptr),
+ callback_(callback),
+ callback_task_runner_(callback_task_runner) {
+ DCHECK(callback_);
+ DCHECK(callback_task_runner_);
+ // |this| is initialized and used on the IO thread using
+ // |network_quality_task_runner_|.
+ thread_checker_.DetachFromThread();
+ }
+
+ ~EffectiveConnectionTypeObserver() override {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (network_quality_estimator_)
+ network_quality_estimator_->RemoveEffectiveConnectionTypeObserver(this);
+ }
+
+ // Initializes |this| on IO thread using |network_quality_task_runner_|. This
+ // is the same thread on which |network_quality_estimator| lives.
+ void Init(net::NetworkQualityEstimator* network_quality_estimator) {
+ network_quality_estimator_ = network_quality_estimator;
+ if (network_quality_estimator_)
+ network_quality_estimator_->AddEffectiveConnectionTypeObserver(this);
+ }
+
+ private:
+ // net::EffectiveConnectionTypeObserver:
+ void OnEffectiveConnectionTypeChanged(
+ net::EffectiveConnectionType type) override {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback_, type));
+ }
+
+ // Notifies |this| when there is a change in the effective connection type.
+ net::NetworkQualityEstimator* network_quality_estimator_;
+
+ // Called when the effective connection type is changed.
+ base::Callback<void(net::EffectiveConnectionType)> callback_;
+
+ // Task runner on which |callback_| is run.
+ scoped_refptr<base::SequencedTaskRunner> callback_task_runner_;
+
+ base::ThreadChecker thread_checker_;
+
+ 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),
wifi_phy_layer_protocol_is_ambiguous_(false),
wifi_phy_layer_protocol_(net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN),
total_aborts_(0),
total_codes_(0),
+ network_quality_estimator_provider_(
+ std::move(network_quality_estimator_provider)),
+ effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
+ effective_connection_type_is_ambiguous_(false),
weak_ptr_factory_(this) {
net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
connection_type_ = net::NetworkChangeNotifier::GetConnectionType();
ProbeWifiPHYLayerProtocol();
+
+ if (network_quality_estimator_provider_) {
+ network_quality_task_runner_ =
+ network_quality_estimator_provider_->GetTaskRunner();
+ DCHECK(network_quality_task_runner_);
+ effective_connection_type_observer_.reset(
+ new EffectiveConnectionTypeObserver(
+ base::Bind(
+ &NetworkMetricsProvider::OnEffectiveConnectionTypeChanged,
+ base::Unretained(this)),
+ base::ThreadTaskRunnerHandle::Get()));
+
+ // Get the network quality estimator and initialize
+ // |effective_connection_type_observer_| on the same task runner on which
+ // the network quality estimator lives. It is safe to use base::Unretained
+ // here since both |network_quality_estimator_provider_| and
+ // |effective_connection_type_observer_| are owned by |this|, and are
+ // deleted on the |network_quality_task_runner_|.
+ network_quality_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&GetAndSetNetworkQualityEstimator,
+ base::Bind(&EffectiveConnectionTypeObserver::Init,
+ base::Unretained(
+ effective_connection_type_observer_.get())),
+ network_quality_estimator_provider_.get()));
+ }
}
NetworkMetricsProvider::~NetworkMetricsProvider() {
+ DCHECK(thread_checker_.CalledOnValidThread());
net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
+ if (effective_connection_type_observer_ &&
+ !network_quality_task_runner_->DeleteSoon(
+ FROM_HERE, effective_connection_type_observer_.release())) {
+ NOTREACHED() << " ECT observer was not deleted successfully";
+ }
+ if (network_quality_estimator_provider_ &&
+ !network_quality_task_runner_->DeleteSoon(
+ FROM_HERE, network_quality_estimator_provider_.release())) {
+ NOTREACHED()
+ << " Network quality estimate provider was not deleted successfully";
+ }
}
void NetworkMetricsProvider::ProvideGeneralMetrics(
ChromeUserMetricsExtension*) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// ProvideGeneralMetrics is called on the main thread, at the time a metrics
// record is being finalized.
net::NetworkChangeNotifier::FinalizingMetricsLogRecord();
@@ -52,12 +175,14 @@ void NetworkMetricsProvider::ProvideGeneralMetrics(
void NetworkMetricsProvider::ProvideSystemProfileMetrics(
SystemProfileProto* system_profile) {
+ DCHECK(thread_checker_.CalledOnValidThread());
SystemProfileProto::Network* network = system_profile->mutable_network();
network->set_connection_type_is_ambiguous(connection_type_is_ambiguous_);
network->set_connection_type(GetConnectionType());
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());
// 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
@@ -67,6 +192,7 @@ 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;
if (!wifi_access_point_info_provider_.get()) {
#if defined(OS_CHROMEOS)
@@ -86,6 +212,7 @@ void NetworkMetricsProvider::ProvideSystemProfileMetrics(
void NetworkMetricsProvider::OnConnectionTypeChanged(
net::NetworkChangeNotifier::ConnectionType type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// To avoid reporting an ambiguous connection type for users on flaky
// connections, ignore transitions to the "none" state. Note that the
// connection type is refreshed in ProvideSystemProfileMetrics() each time a
@@ -98,6 +225,7 @@ 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;
@@ -106,6 +234,7 @@ void NetworkMetricsProvider::OnConnectionTypeChanged(
SystemProfileProto::Network::ConnectionType
NetworkMetricsProvider::GetConnectionType() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
switch (connection_type_) {
case net::NetworkChangeNotifier::CONNECTION_NONE:
return SystemProfileProto::Network::CONNECTION_NONE;
@@ -130,6 +259,7 @@ NetworkMetricsProvider::GetConnectionType() const {
SystemProfileProto::Network::WifiPHYLayerProtocol
NetworkMetricsProvider::GetWifiPHYLayerProtocol() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
switch (wifi_phy_layer_protocol_) {
case net::WIFI_PHY_LAYER_PROTOCOL_NONE:
return SystemProfileProto::Network::WIFI_PHY_LAYER_PROTOCOL_NONE;
@@ -150,7 +280,36 @@ 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_,
FROM_HERE,
@@ -161,6 +320,7 @@ void NetworkMetricsProvider::ProbeWifiPHYLayerProtocol() {
void NetworkMetricsProvider::OnWifiPHYLayerProtocolResult(
net::WifiPHYLayerProtocol mode) {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (wifi_phy_layer_protocol_ != net::WIFI_PHY_LAYER_PROTOCOL_UNKNOWN &&
mode != wifi_phy_layer_protocol_) {
wifi_phy_layer_protocol_is_ambiguous_ = true;
@@ -171,6 +331,7 @@ void NetworkMetricsProvider::OnWifiPHYLayerProtocolResult(
void NetworkMetricsProvider::WriteWifiAccessPointProto(
const WifiAccessPointInfoProvider::WifiAccessPointInfo& info,
SystemProfileProto::Network* network_proto) {
+ DCHECK(thread_checker_.CalledOnValidThread());
SystemProfileProto::Network::WifiAccessPoint* access_point_info =
network_proto->mutable_access_point_info();
SystemProfileProto::Network::WifiAccessPoint::SecurityMode security =
@@ -246,6 +407,7 @@ void NetworkMetricsProvider::WriteWifiAccessPointProto(
}
void NetworkMetricsProvider::LogAggregatedMetrics() {
+ DCHECK(thread_checker_.CalledOnValidThread());
base::HistogramBase* error_codes = base::SparseHistogram::FactoryGet(
"Net.ErrorCodesForMainFrame3",
base::HistogramBase::kUmaTargetedHistogramFlag);
@@ -263,4 +425,15 @@ 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;
+}
+
} // namespace metrics
diff --git a/chromium/components/metrics/net/network_metrics_provider.h b/chromium/components/metrics/net/network_metrics_provider.h
index da47689d274..1c23bea907e 100644
--- a/chromium/components/metrics/net/network_metrics_provider.h
+++ b/chromium/components/metrics/net/network_metrics_provider.h
@@ -10,11 +10,17 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_base.h"
+#include "base/threading/thread_checker.h"
#include "components/metrics/metrics_provider.h"
#include "components/metrics/net/wifi_access_point_info_provider.h"
#include "components/metrics/proto/system_profile.pb.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_interfaces.h"
+#include "net/nqe/effective_connection_type.h"
+
+namespace net {
+class NetworkQualityEstimator;
+}
namespace metrics {
@@ -24,12 +30,46 @@ class NetworkMetricsProvider
: public MetricsProvider,
public net::NetworkChangeNotifier::ConnectionTypeObserver {
public:
+ // Class that provides |this| with the network quality estimator.
+ class NetworkQualityEstimatorProvider {
+ public:
+ virtual ~NetworkQualityEstimatorProvider() {}
+
+ // Returns the network quality estimator. May be nullptr.
+ virtual net::NetworkQualityEstimator* GetNetworkQualityEstimator() = 0;
+
+ // Returns the task runner on which |this| should be used and destroyed.
+ virtual scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() = 0;
+
+ protected:
+ NetworkQualityEstimatorProvider() {}
+
+ private:
+ 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);
+
~NetworkMetricsProvider() override;
private:
+ FRIEND_TEST_ALL_PREFIXES(NetworkMetricsProviderTest, EffectiveConnectionType);
+ FRIEND_TEST_ALL_PREFIXES(NetworkMetricsProviderTest,
+ ECTAmbiguousOnConnectionTypeChange);
+
+ // Listens to the changes in the effective conection type.
+ class EffectiveConnectionTypeObserver;
+
// MetricsProvider:
void ProvideGeneralMetrics(ChromeUserMetricsExtension* uma_proto) override;
void ProvideSystemProfileMetrics(SystemProfileProto* system_profile) override;
@@ -41,6 +81,8 @@ 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();
@@ -57,6 +99,10 @@ class NetworkMetricsProvider
// Logs metrics that are functions of other metrics being uploaded.
void LogAggregatedMetrics();
+ // Notifies |this| that the effective connection type of the current network
+ // has changed to |type|.
+ void OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType type);
+
// Task runner used for blocking file I/O.
base::TaskRunner* io_task_runner_;
@@ -79,6 +125,28 @@ class NetworkMetricsProvider
base::HistogramBase::Count total_aborts_;
base::HistogramBase::Count total_codes_;
+ // Provides the network quality estimator. May be null.
+ std::unique_ptr<NetworkQualityEstimatorProvider>
+ network_quality_estimator_provider_;
+
+ // Listens to the changes in the effective connection type. Initialized and
+ // destroyed using |network_quality_task_runner_|. May be null.
+ std::unique_ptr<EffectiveConnectionTypeObserver>
+ effective_connection_type_observer_;
+
+ // Task runner using which |effective_connection_type_observer_| is
+ // initialized and destroyed. May be null.
+ scoped_refptr<base::SequencedTaskRunner> network_quality_task_runner_;
+
+ // 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_;
+
+ base::ThreadChecker thread_checker_;
+
base::WeakPtrFactory<NetworkMetricsProvider> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(NetworkMetricsProvider);
diff --git a/chromium/components/metrics/net/network_metrics_provider_unittest.cc b/chromium/components/metrics/net/network_metrics_provider_unittest.cc
new file mode 100644
index 00000000000..7006ee146be
--- /dev/null
+++ b/chromium/components/metrics/net/network_metrics_provider_unittest.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/metrics/net/network_metrics_provider.h"
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/metrics/proto/system_profile.pb.h"
+#include "net/base/network_change_notifier.h"
+#include "net/nqe/network_quality_estimator_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/network/network_handler.h"
+#endif // OS_CHROMEOS
+
+namespace metrics {
+
+namespace {
+
+class TestNetworkQualityEstimatorProvider
+ : public NetworkMetricsProvider::NetworkQualityEstimatorProvider {
+ public:
+ explicit TestNetworkQualityEstimatorProvider(
+ net::TestNetworkQualityEstimator* estimator)
+ : estimator_(estimator) {}
+ ~TestNetworkQualityEstimatorProvider() override {}
+
+ private:
+ // NetworkMetricsProvider::NetworkQualityEstimatorProvider:
+ scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() override {
+ return base::ThreadTaskRunnerHandle::Get();
+ }
+
+ net::NetworkQualityEstimator* GetNetworkQualityEstimator() override {
+ return estimator_;
+ }
+
+ net::TestNetworkQualityEstimator* estimator_;
+ DISALLOW_COPY_AND_ASSIGN(TestNetworkQualityEstimatorProvider);
+};
+
+} // namespace
+
+class NetworkMetricsProviderTest : public testing::Test {
+ protected:
+ NetworkMetricsProviderTest() : loop_(base::MessageLoop::TYPE_IO) {
+#if defined(OS_CHROMEOS)
+ chromeos::DBusThreadManager::Initialize();
+ chromeos::NetworkHandler::Initialize();
+#endif // OS_CHROMEOS
+ }
+
+ private:
+ base::MessageLoop loop_;
+};
+
+// Verifies that the effective connection type is correctly set.
+TEST_F(NetworkMetricsProviderTest, EffectiveConnectionType) {
+ net::TestNetworkQualityEstimator estimator;
+ std::unique_ptr<NetworkMetricsProvider::NetworkQualityEstimatorProvider>
+ estimator_provider(base::WrapUnique(
+ new TestNetworkQualityEstimatorProvider(&estimator)));
+ SystemProfileProto system_profile;
+ NetworkMetricsProvider network_metrics_provider(
+ std::move(estimator_provider), base::ThreadTaskRunnerHandle::Get().get());
+
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ network_metrics_provider.effective_connection_type_);
+ network_metrics_provider.ProvideSystemProfileMetrics(&system_profile);
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ system_profile.network().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_);
+ // 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_);
+ network_metrics_provider.ProvideSystemProfileMetrics(&system_profile);
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G,
+ system_profile.network().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));
+ estimator.set_start_time_null_http_rtt(
+ base::TimeDelta::FromMilliseconds(3000));
+ // Running a request would cause the effective connection type to be computed
+ // as SLOW_2G, and observers to be notified.
+ estimator.RunOneRequest();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
+ network_metrics_provider.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.
+ network_metrics_provider.ProvideSystemProfileMetrics(&system_profile);
+ EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
+ system_profile.network().effective_connection_type());
+}
+
+// Verifies that the effective connection type is set to AMBIGUOUS when there is
+// a change in the connection type.
+TEST_F(NetworkMetricsProviderTest, ECTAmbiguousOnConnectionTypeChange) {
+ net::TestNetworkQualityEstimator estimator;
+ std::unique_ptr<NetworkMetricsProvider::NetworkQualityEstimatorProvider>
+ estimator_provider(base::WrapUnique(
+ new TestNetworkQualityEstimatorProvider(&estimator)));
+ SystemProfileProto system_profile;
+ NetworkMetricsProvider network_metrics_provider(
+ std::move(estimator_provider), base::ThreadTaskRunnerHandle::Get().get());
+
+ EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ network_metrics_provider.effective_connection_type_);
+ EXPECT_FALSE(
+ network_metrics_provider.effective_connection_type_is_ambiguous_);
+
+ 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());
+}
+
+} // namespace metrics \ No newline at end of file
diff --git a/chromium/components/metrics/persisted_logs.cc b/chromium/components/metrics/persisted_logs.cc
index 12d3a682188..b7aa8f003e3 100644
--- a/chromium/components/metrics/persisted_logs.cc
+++ b/chromium/components/metrics/persisted_logs.cc
@@ -14,6 +14,7 @@
#include "base/sha1.h"
#include "base/strings/string_number_conversions.h"
#include "base/timer/elapsed_timer.h"
+#include "components/metrics/persisted_logs_metrics.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "third_party/zlib/google/compression_utils.h"
@@ -26,24 +27,6 @@ const char kLogHashKey[] = "hash";
const char kLogTimestampKey[] = "timestamp";
const char kLogDataKey[] = "data";
-PersistedLogs::LogReadStatus MakeRecallStatusHistogram(
- PersistedLogs::LogReadStatus status) {
- UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs",
- status, PersistedLogs::END_RECALL_STATUS);
- return status;
-}
-
-// Reads the value at |index| from |list_value| as a string and Base64-decodes
-// it into |result|. Returns true on success.
-bool ReadBase64String(const base::ListValue& list_value,
- size_t index,
- std::string* result) {
- std::string base64_result;
- if (!list_value.GetString(index, &base64_result))
- return false;
- return base::Base64Decode(base64_result, result);
-}
-
std::string EncodeToBase64(const std::string& to_convert) {
std::string base64_result;
base::Base64Encode(to_convert, &base64_result);
@@ -58,7 +41,8 @@ std::string DecodeFromBase64(const std::string& to_convert) {
} // namespace
-void PersistedLogs::LogInfo::Init(const std::string& log_data,
+void PersistedLogs::LogInfo::Init(PersistedLogsMetrics* metrics,
+ const std::string& log_data,
const std::string& log_timestamp) {
DCHECK(!log_data.empty());
@@ -67,23 +51,21 @@ void PersistedLogs::LogInfo::Init(const std::string& log_data,
return;
}
- UMA_HISTOGRAM_PERCENTAGE(
- "UMA.ProtoCompressionRatio",
- static_cast<int>(100 * compressed_log_data.size() / log_data.size()));
+ metrics->RecordCompressionRatio(compressed_log_data.size(), log_data.size());
hash = base::SHA1HashString(log_data);
timestamp = log_timestamp;
}
-PersistedLogs::PersistedLogs(PrefService* local_state,
+PersistedLogs::PersistedLogs(std::unique_ptr<PersistedLogsMetrics> metrics,
+ PrefService* local_state,
const char* pref_name,
- const char* outdated_pref_name,
size_t min_log_count,
size_t min_log_bytes,
size_t max_log_size)
- : local_state_(local_state),
+ : metrics_(std::move(metrics)),
+ local_state_(local_state),
pref_name_(pref_name),
- outdated_pref_name_(outdated_pref_name),
min_log_count_(min_log_count),
min_log_bytes_(min_log_bytes),
max_log_size_(max_log_size != 0 ? max_log_size : static_cast<size_t>(-1)),
@@ -98,26 +80,17 @@ PersistedLogs::~PersistedLogs() {}
void PersistedLogs::SerializeLogs() const {
ListPrefUpdate update(local_state_, pref_name_);
WriteLogsToPrefList(update.Get());
-
- // After writing all the logs to the new pref remove old outdated pref.
- // TODO(gayane): Remove when all users are migrated. crbug.com/649440
- if (local_state_->HasPrefPath(outdated_pref_name_))
- local_state_->ClearPref(outdated_pref_name_);
}
PersistedLogs::LogReadStatus PersistedLogs::DeserializeLogs() {
- // TODO(gayane): Remove the code for reading logs from outdated pref when all
- // users are migrated. crbug.com/649440
- if (local_state_->HasPrefPath(outdated_pref_name_)) {
- return ReadLogsFromOldFormatPrefList(
- *local_state_->GetList(outdated_pref_name_));
- }
return ReadLogsFromPrefList(*local_state_->GetList(pref_name_));
}
void PersistedLogs::StoreLog(const std::string& log_data) {
list_.push_back(LogInfo());
- list_.back().Init(log_data, base::Int64ToString(base::Time::Now().ToTimeT()));
+ list_.back().Init(metrics_.get(),
+ log_data,
+ base::Int64ToString(base::Time::Now().ToTimeT()));
}
void PersistedLogs::StageLog() {
@@ -139,7 +112,7 @@ void PersistedLogs::DiscardStagedLog() {
PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList(
const base::ListValue& list_value) {
if (list_value.empty())
- return MakeRecallStatusHistogram(LIST_EMPTY);
+ return metrics_->RecordLogReadStatus(LIST_EMPTY);
const size_t log_count = list_value.GetSize();
@@ -152,7 +125,7 @@ PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList(
!dict->GetString(kLogDataKey, &list_[i].compressed_log_data) ||
!dict->GetString(kLogHashKey, &list_[i].hash)) {
list_.clear();
- return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION);
+ return metrics_->RecordLogReadStatus(LOG_STRING_CORRUPTION);
}
list_[i].compressed_log_data =
@@ -165,7 +138,7 @@ PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList(
dict->GetString(kLogTimestampKey, &list_[i].timestamp);
}
- return MakeRecallStatusHistogram(RECALL_SUCCESS);
+ return metrics_->RecordLogReadStatus(RECALL_SUCCESS);
}
void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) const {
@@ -194,8 +167,7 @@ void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) const {
for (size_t i = start; i < list_.size(); ++i) {
size_t log_size = list_[i].compressed_log_data.length();
if (log_size > max_log_size_) {
- UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted",
- static_cast<int>(log_size));
+ metrics_->RecordDroppedLogSize(log_size);
dropped_logs_num++;
continue;
}
@@ -208,32 +180,7 @@ void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) const {
list_value->Append(std::move(dict_value));
}
if (dropped_logs_num > 0)
- UMA_HISTOGRAM_COUNTS("UMA.UnsentLogs.Dropped", dropped_logs_num);
-}
-
-PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromOldFormatPrefList(
- const base::ListValue& list_value) {
- if (list_value.empty())
- return MakeRecallStatusHistogram(LIST_EMPTY);
-
- // For each log, there's two entries in the list (the data and the hash).
- DCHECK_EQ(0U, list_value.GetSize() % 2);
- const size_t log_count = list_value.GetSize() / 2;
-
- // Resize |list_| ahead of time, so that values can be decoded directly into
- // the elements of the list.
- DCHECK(list_.empty());
- list_.resize(log_count);
-
- for (size_t i = 0; i < log_count; ++i) {
- if (!ReadBase64String(list_value, i * 2, &list_[i].compressed_log_data) ||
- !ReadBase64String(list_value, i * 2 + 1, &list_[i].hash)) {
- list_.clear();
- return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION);
- }
- }
-
- return MakeRecallStatusHistogram(RECALL_SUCCESS);
+ metrics_->RecordDroppedLogsNum(dropped_logs_num);
}
} // namespace metrics
diff --git a/chromium/components/metrics/persisted_logs.h b/chromium/components/metrics/persisted_logs.h
index 479924fd0fd..18a80008dbe 100644
--- a/chromium/components/metrics/persisted_logs.h
+++ b/chromium/components/metrics/persisted_logs.h
@@ -7,6 +7,7 @@
#include <stddef.h>
+#include <memory>
#include <string>
#include <vector>
@@ -18,6 +19,8 @@ class PrefService;
namespace metrics {
+class PersistedLogsMetrics;
+
// Maintains a list of unsent logs that are written and restored from disk.
class PersistedLogs {
public:
@@ -49,9 +52,9 @@ class PersistedLogs {
//
// If the optional |max_log_size| parameter is non-zero, all logs larger than
// that limit will be skipped when writing to disk.
- PersistedLogs(PrefService* local_state,
+ PersistedLogs(std::unique_ptr<PersistedLogsMetrics> metrics,
+ PrefService* local_state,
const char* pref_name,
- const char* outdated_pref_name,
size_t min_log_count,
size_t min_log_bytes,
size_t max_log_size);
@@ -107,8 +110,8 @@ class PersistedLogs {
// Reads the list from the ListValue.
LogReadStatus ReadLogsFromPrefList(const base::ListValue& list);
- // Reads the list from the ListValue in the old Log-hash pair format.
- LogReadStatus ReadLogsFromOldFormatPrefList(const base::ListValue& list);
+ // An object for recording UMA metrics.
+ std::unique_ptr<PersistedLogsMetrics> metrics_;
// A weak pointer to the PrefService object to read and write the preference
// from. Calling code should ensure this object continues to exist for the
@@ -118,10 +121,6 @@ class PersistedLogs {
// The name of the preference to serialize logs to/from.
const char* pref_name_;
- // The name of the preference to serialize logs to/from which may contain log
- // in the old formatting.
- const char* outdated_pref_name_;
-
// We will keep at least this |min_log_count_| logs or |min_log_bytes_| bytes
// of logs, whichever is greater, when writing to disk. These apply after
// skipping logs greater than |max_log_size_|.
@@ -134,7 +133,10 @@ class PersistedLogs {
struct LogInfo {
// Initializes the members based on uncompressed |log_data| and
// |log_timestamp|.
- void Init(const std::string& log_data, const std::string& log_timestamp);
+ // |metrics| is the parent's metrics_ object, and should not be held.
+ void Init(PersistedLogsMetrics* metrics,
+ const std::string& log_data,
+ const std::string& log_timestamp);
// Compressed log data - a serialized protobuf that's been gzipped.
std::string compressed_log_data;
diff --git a/chromium/components/metrics/persisted_logs_metrics.h b/chromium/components/metrics/persisted_logs_metrics.h
new file mode 100644
index 00000000000..5263bd3edc6
--- /dev/null
+++ b/chromium/components/metrics/persisted_logs_metrics.h
@@ -0,0 +1,35 @@
+// 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_METRICS_PERSISTED_LOGS_METRICS_H_
+#define COMPONENTS_METRICS_PERSISTED_LOGS_METRICS_H_
+
+#include "base/macros.h"
+#include "components/metrics/persisted_logs.h"
+
+namespace metrics {
+
+// Interface for recording metrics from PersistedLogs.
+class PersistedLogsMetrics {
+ public:
+ PersistedLogsMetrics() {}
+ ~PersistedLogsMetrics() {}
+
+ virtual PersistedLogs::LogReadStatus RecordLogReadStatus(
+ PersistedLogs::LogReadStatus status) = 0;
+
+ virtual void RecordCompressionRatio(
+ size_t compressed_size, size_t original_size) {}
+
+ virtual void RecordDroppedLogSize(size_t size) {}
+
+ virtual void RecordDroppedLogsNum(int dropped_logs_num) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PersistedLogsMetrics);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_PERSISTED_LOGS_METRICS_H_
diff --git a/chromium/components/metrics/persisted_logs_metrics_impl.cc b/chromium/components/metrics/persisted_logs_metrics_impl.cc
new file mode 100644
index 00000000000..877ad28864e
--- /dev/null
+++ b/chromium/components/metrics/persisted_logs_metrics_impl.cc
@@ -0,0 +1,35 @@
+// 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/metrics/persisted_logs_metrics_impl.h"
+
+#include "base/metrics/histogram_macros.h"
+
+namespace metrics {
+
+PersistedLogs::LogReadStatus
+PersistedLogsMetricsImpl::RecordLogReadStatus(
+ PersistedLogs::LogReadStatus status) {
+ UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs",
+ status, PersistedLogs::END_RECALL_STATUS);
+ return status;
+}
+
+void PersistedLogsMetricsImpl::RecordCompressionRatio(
+ size_t compressed_size, size_t original_size) {
+ UMA_HISTOGRAM_PERCENTAGE(
+ "UMA.ProtoCompressionRatio",
+ static_cast<int>(100 * compressed_size / original_size));
+}
+
+void PersistedLogsMetricsImpl::RecordDroppedLogSize(size_t size) {
+ UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted",
+ static_cast<int>(size));
+}
+
+void PersistedLogsMetricsImpl::RecordDroppedLogsNum(int dropped_logs_num) {
+ UMA_HISTOGRAM_COUNTS("UMA.UnsentLogs.Dropped", dropped_logs_num);
+}
+
+} // namespace metrics
diff --git a/chromium/components/metrics/persisted_logs_metrics_impl.h b/chromium/components/metrics/persisted_logs_metrics_impl.h
new file mode 100644
index 00000000000..e7f4a2d9cc5
--- /dev/null
+++ b/chromium/components/metrics/persisted_logs_metrics_impl.h
@@ -0,0 +1,35 @@
+// 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_METRICS_PERSISTED_LOGS_METRICS_IMPL_H_
+#define COMPONENTS_METRICS_PERSISTED_LOGS_METRICS_IMPL_H_
+
+#include "base/macros.h"
+#include "components/metrics/persisted_logs_metrics.h"
+
+namespace metrics {
+
+// Implementation for recording metrics from PersistedLogs.
+class PersistedLogsMetricsImpl : public PersistedLogsMetrics {
+ public:
+ PersistedLogsMetricsImpl() {}
+ ~PersistedLogsMetricsImpl() {}
+
+ PersistedLogs::LogReadStatus RecordLogReadStatus(
+ PersistedLogs::LogReadStatus status) override;
+
+ void RecordCompressionRatio(
+ size_t compressed_size, size_t original_size) override;
+
+ void RecordDroppedLogSize(size_t size) override;
+
+ void RecordDroppedLogsNum(int dropped_logs_num) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PersistedLogsMetricsImpl);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_PERSISTED_LOGS_METRICS_IMPL_H_
diff --git a/chromium/components/metrics/persisted_logs_unittest.cc b/chromium/components/metrics/persisted_logs_unittest.cc
index 94b8b7e97de..e1b0548fa4c 100644
--- a/chromium/components/metrics/persisted_logs_unittest.cc
+++ b/chromium/components/metrics/persisted_logs_unittest.cc
@@ -11,6 +11,7 @@
#include "base/rand_util.h"
#include "base/sha1.h"
#include "base/values.h"
+#include "components/metrics/persisted_logs_metrics_impl.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/prefs/testing_pref_service.h"
@@ -22,7 +23,6 @@ namespace metrics {
namespace {
const char kTestPrefName[] = "TestPref";
-const char kTestOutdatedPrefName[] = "OutdatedTestPref";
const size_t kLogCountLimit = 3;
const size_t kLogByteLimit = 1000;
@@ -64,9 +64,10 @@ class PersistedLogsTest : public testing::Test {
class TestPersistedLogs : public PersistedLogs {
public:
TestPersistedLogs(PrefService* service, size_t min_log_bytes)
- : PersistedLogs(service,
+ : PersistedLogs(std::unique_ptr<PersistedLogsMetricsImpl>(
+ new PersistedLogsMetricsImpl()),
+ service,
kTestPrefName,
- kTestOutdatedPrefName,
kLogCountLimit,
min_log_bytes,
0) {}
diff --git a/chromium/components/metrics/profiler/profiler_metrics_provider.cc b/chromium/components/metrics/profiler/profiler_metrics_provider.cc
index 2ab8c0314f6..3c3ffbad6aa 100644
--- a/chromium/components/metrics/profiler/profiler_metrics_provider.cc
+++ b/chromium/components/metrics/profiler/profiler_metrics_provider.cc
@@ -4,7 +4,6 @@
#include "components/metrics/profiler/profiler_metrics_provider.h"
-#include <ctype.h>
#include <stddef.h>
#include <string>
#include <vector>
@@ -16,23 +15,6 @@
namespace metrics {
namespace {
-// Maps a thread name by replacing trailing sequence of digits with "*".
-// Examples:
-// 1. "BrowserBlockingWorker1/23857" => "BrowserBlockingWorker1/*"
-// 2. "Chrome_IOThread" => "Chrome_IOThread"
-std::string MapThreadName(const std::string& thread_name) {
- size_t i = thread_name.length();
-
- while (i > 0 && isdigit(thread_name[i - 1])) {
- --i;
- }
-
- if (i == thread_name.length())
- return thread_name;
-
- return thread_name.substr(0, i) + '*';
-}
-
// Normalizes a source filename (which is platform- and build-method-dependent)
// by extracting the last component of the full file name.
// Example: "c:\b\build\slave\win\build\src\chrome\app\chrome_main.cc" =>
@@ -52,9 +34,9 @@ void WriteProfilerData(
ProfilerEventProto::TrackedObject* tracked_object =
performance_profile->add_tracked_object();
tracked_object->set_birth_thread_name_hash(
- MetricsLog::Hash(MapThreadName(task.birth.thread_name)));
+ MetricsLog::Hash(task.birth.sanitized_thread_name));
tracked_object->set_exec_thread_name_hash(
- MetricsLog::Hash(MapThreadName(task.death_thread_name)));
+ MetricsLog::Hash(task.death_sanitized_thread_name));
tracked_object->set_source_file_name_hash(
MetricsLog::Hash(NormalizeFileName(task.birth.location.file_name)));
tracked_object->set_source_function_name_hash(
diff --git a/chromium/components/metrics/profiler/profiler_metrics_provider_unittest.cc b/chromium/components/metrics/profiler/profiler_metrics_provider_unittest.cc
index e2e31b922eb..050a87ad9fa 100644
--- a/chromium/components/metrics/profiler/profiler_metrics_provider_unittest.cc
+++ b/chromium/components/metrics/profiler/profiler_metrics_provider_unittest.cc
@@ -32,7 +32,8 @@ TEST(ProfilerMetricsProviderTest, RecordData) {
process_data_phase.tasks.back().birth.location.file_name = "a/b/file.h";
process_data_phase.tasks.back().birth.location.function_name = "function";
process_data_phase.tasks.back().birth.location.line_number = 1337;
- process_data_phase.tasks.back().birth.thread_name = "birth_thread";
+ process_data_phase.tasks.back().birth.sanitized_thread_name =
+ "birth_thread";
process_data_phase.tasks.back().death_data.count = 37;
process_data_phase.tasks.back().death_data.run_duration_sum = 31;
process_data_phase.tasks.back().death_data.run_duration_max = 17;
@@ -40,12 +41,13 @@ TEST(ProfilerMetricsProviderTest, RecordData) {
process_data_phase.tasks.back().death_data.queue_duration_sum = 8;
process_data_phase.tasks.back().death_data.queue_duration_max = 5;
process_data_phase.tasks.back().death_data.queue_duration_sample = 3;
- process_data_phase.tasks.back().death_thread_name = "Still_Alive";
+ process_data_phase.tasks.back().death_sanitized_thread_name = "Still_Alive";
process_data_phase.tasks.push_back(TaskSnapshot());
process_data_phase.tasks.back().birth.location.file_name = "c\\d\\file2";
process_data_phase.tasks.back().birth.location.function_name = "function2";
process_data_phase.tasks.back().birth.location.line_number = 1773;
- process_data_phase.tasks.back().birth.thread_name = "birth_thread2";
+ process_data_phase.tasks.back().birth.sanitized_thread_name =
+ "birth_thread*";
process_data_phase.tasks.back().death_data.count = 19;
process_data_phase.tasks.back().death_data.run_duration_sum = 23;
process_data_phase.tasks.back().death_data.run_duration_max = 11;
@@ -53,7 +55,8 @@ TEST(ProfilerMetricsProviderTest, RecordData) {
process_data_phase.tasks.back().death_data.queue_duration_sum = 0;
process_data_phase.tasks.back().death_data.queue_duration_max = 0;
process_data_phase.tasks.back().death_data.queue_duration_sample = 0;
- process_data_phase.tasks.back().death_thread_name = "death_thread";
+ process_data_phase.tasks.back().death_sanitized_thread_name =
+ "death_thread";
profiler_metrics_provider.RecordProfilerData(
process_data_phase, 177, ProfilerEventProto::TrackedObject::BROWSER, 0,
@@ -68,7 +71,8 @@ TEST(ProfilerMetricsProviderTest, RecordData) {
process_data_phase.tasks.back().birth.location.file_name = "a/b/file10.h";
process_data_phase.tasks.back().birth.location.function_name = "function10";
process_data_phase.tasks.back().birth.location.line_number = 101337;
- process_data_phase.tasks.back().birth.thread_name = "birth_thread_ten";
+ process_data_phase.tasks.back().birth.sanitized_thread_name =
+ "birth_thread_ten";
process_data_phase.tasks.back().death_data.count = 1037;
process_data_phase.tasks.back().death_data.run_duration_sum = 1031;
process_data_phase.tasks.back().death_data.run_duration_max = 1017;
@@ -76,13 +80,15 @@ TEST(ProfilerMetricsProviderTest, RecordData) {
process_data_phase.tasks.back().death_data.queue_duration_sum = 108;
process_data_phase.tasks.back().death_data.queue_duration_max = 105;
process_data_phase.tasks.back().death_data.queue_duration_sample = 103;
- process_data_phase.tasks.back().death_thread_name = "Already_Dead";
+ process_data_phase.tasks.back().death_sanitized_thread_name =
+ "Already_Dead";
process_data_phase.tasks.push_back(TaskSnapshot());
process_data_phase.tasks.back().birth.location.file_name = "c\\d\\file210";
process_data_phase.tasks.back().birth.location.function_name =
"function210";
process_data_phase.tasks.back().birth.location.line_number = 101773;
- process_data_phase.tasks.back().birth.thread_name = "birth_thread_ten2";
+ process_data_phase.tasks.back().birth.sanitized_thread_name =
+ "birth_thread_ten*";
process_data_phase.tasks.back().death_data.count = 1019;
process_data_phase.tasks.back().death_data.run_duration_sum = 1023;
process_data_phase.tasks.back().death_data.run_duration_max = 1011;
@@ -90,7 +96,8 @@ TEST(ProfilerMetricsProviderTest, RecordData) {
process_data_phase.tasks.back().death_data.queue_duration_sum = 100;
process_data_phase.tasks.back().death_data.queue_duration_max = 100;
process_data_phase.tasks.back().death_data.queue_duration_sample = 100;
- process_data_phase.tasks.back().death_thread_name = "death_thread_ten";
+ process_data_phase.tasks.back().death_sanitized_thread_name =
+ "death_thread_ten";
profiler_metrics_provider.RecordProfilerData(
process_data_phase, 177, ProfilerEventProto::TrackedObject::BROWSER, 1,
@@ -105,7 +112,8 @@ TEST(ProfilerMetricsProviderTest, RecordData) {
process_data_phase.tasks.back().birth.location.file_name = "file3";
process_data_phase.tasks.back().birth.location.function_name = "function3";
process_data_phase.tasks.back().birth.location.line_number = 7331;
- process_data_phase.tasks.back().birth.thread_name = "birth_thread3";
+ process_data_phase.tasks.back().birth.sanitized_thread_name =
+ "birth_thread*";
process_data_phase.tasks.back().death_data.count = 137;
process_data_phase.tasks.back().death_data.run_duration_sum = 131;
process_data_phase.tasks.back().death_data.run_duration_max = 117;
@@ -113,12 +121,13 @@ TEST(ProfilerMetricsProviderTest, RecordData) {
process_data_phase.tasks.back().death_data.queue_duration_sum = 108;
process_data_phase.tasks.back().death_data.queue_duration_max = 105;
process_data_phase.tasks.back().death_data.queue_duration_sample = 103;
- process_data_phase.tasks.back().death_thread_name = "death_thread3";
+ process_data_phase.tasks.back().death_sanitized_thread_name =
+ "death_thread*";
process_data_phase.tasks.push_back(TaskSnapshot());
process_data_phase.tasks.back().birth.location.file_name = "";
process_data_phase.tasks.back().birth.location.function_name = "";
process_data_phase.tasks.back().birth.location.line_number = 7332;
- process_data_phase.tasks.back().birth.thread_name = "";
+ process_data_phase.tasks.back().birth.sanitized_thread_name = "";
process_data_phase.tasks.back().death_data.count = 138;
process_data_phase.tasks.back().death_data.run_duration_sum = 132;
process_data_phase.tasks.back().death_data.run_duration_max = 118;
@@ -126,7 +135,7 @@ TEST(ProfilerMetricsProviderTest, RecordData) {
process_data_phase.tasks.back().death_data.queue_duration_sum = 109;
process_data_phase.tasks.back().death_data.queue_duration_max = 106;
process_data_phase.tasks.back().death_data.queue_duration_sample = 104;
- process_data_phase.tasks.back().death_thread_name = "";
+ process_data_phase.tasks.back().death_sanitized_thread_name = "";
profiler_metrics_provider.RecordProfilerData(
process_data_phase, 1177, ProfilerEventProto::TrackedObject::RENDERER,
diff --git a/chromium/components/metrics/profiler/tracking_synchronizer_observer.h b/chromium/components/metrics/profiler/tracking_synchronizer_observer.h
index c35de35cef2..69ff063187e 100644
--- a/chromium/components/metrics/profiler/tracking_synchronizer_observer.h
+++ b/chromium/components/metrics/profiler/tracking_synchronizer_observer.h
@@ -12,10 +12,6 @@
#include "base/time/time.h"
#include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
-namespace base {
-class TimeDelta;
-}
-
namespace tracked_objects {
struct ProcessDataPhaseSnapshot;
}
diff --git a/chromium/components/metrics/profiler/tracking_synchronizer_unittest.cc b/chromium/components/metrics/profiler/tracking_synchronizer_unittest.cc
index cae6cb208fe..792d52f32bf 100644
--- a/chromium/components/metrics/profiler/tracking_synchronizer_unittest.cc
+++ b/chromium/components/metrics/profiler/tracking_synchronizer_unittest.cc
@@ -69,8 +69,8 @@ class TestObserver : public TrackingSynchronizerObserver {
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromMilliseconds(333),
attributes.phase_finish);
- EXPECT_EQ("death_thread0",
- process_data_phase.tasks[0].death_thread_name);
+ EXPECT_EQ("death_threadA",
+ process_data_phase.tasks[0].death_sanitized_thread_name);
EXPECT_EQ(0u, past_events.size());
break;
@@ -83,8 +83,8 @@ class TestObserver : public TrackingSynchronizerObserver {
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromMilliseconds(777),
attributes.phase_finish);
- EXPECT_EQ("death_thread1",
- process_data_phase.tasks[0].death_thread_name);
+ EXPECT_EQ("death_threadB",
+ process_data_phase.tasks[0].death_sanitized_thread_name);
ASSERT_EQ(1u, past_events.size());
EXPECT_EQ(ProfilerEventProto::EVENT_FIRST_NONEMPTY_PAINT,
past_events[0]);
@@ -135,12 +135,12 @@ TEST(TrackingSynchronizerTest, ProfilerData) {
tracked_objects::ProcessDataSnapshot profiler_data;
ProcessDataPhaseSnapshot snapshot0;
tracked_objects::TaskSnapshot task_snapshot0;
- task_snapshot0.death_thread_name = "death_thread0";
+ task_snapshot0.death_sanitized_thread_name = "death_threadA";
snapshot0.tasks.push_back(task_snapshot0);
ProcessDataPhaseSnapshot snapshot1;
profiler_data.phased_snapshots[0] = snapshot0;
tracked_objects::TaskSnapshot task_snapshot1;
- task_snapshot1.death_thread_name = "death_thread1";
+ task_snapshot1.death_sanitized_thread_name = "death_threadB";
snapshot1.tasks.push_back(task_snapshot1);
profiler_data.phased_snapshots[1] = snapshot1;
profiler_data.process_id = 239;
diff --git a/chromium/components/metrics/proto/call_stack_profile.proto b/chromium/components/metrics/proto/call_stack_profile.proto
index e41b3390224..4c601c49b2d 100644
--- a/chromium/components/metrics/proto/call_stack_profile.proto
+++ b/chromium/components/metrics/proto/call_stack_profile.proto
@@ -12,6 +12,8 @@ option java_package = "org.chromium.components.metrics";
package metrics;
+import "execution_context.proto";
+
// Next tag: 5
message CallStackProfile {
// Describes an entry in the callstack.
@@ -32,6 +34,12 @@ message CallStackProfile {
// Number of times this stack signature occurs.
optional int64 count = 2;
+
+ // This repeating field indicates the current phase of the system such as
+ // whether it is in startup, general operation, or shutdown. It is a full
+ // list with the first sample and after that each indicates only the new
+ // phases that are achieved.
+ repeated ProcessPhase process_phase = 3;
}
// Uniquely identifies a module.
diff --git a/chromium/components/metrics/proto/execution_context.proto b/chromium/components/metrics/proto/execution_context.proto
index 3d8679fe9db..64dfa6e3816 100644
--- a/chromium/components/metrics/proto/execution_context.proto
+++ b/chromium/components/metrics/proto/execution_context.proto
@@ -47,3 +47,29 @@ enum Thread {
RENDER_THREAD = 9;
UTILITY_THREAD = 10;
}
+
+// Process phases, or where in the lifetime of the process it is such as
+// startup, normal operation, shutdown, etc. These don't necessarily occur in
+// the order defined here so it's fine to add new ones to further segregrate
+// the lifetime of a process.
+enum ProcessPhase {
+ // The browser's main message loop has been started.
+ // Based on histogram Startup.BrowserMessageLoopStartTime.
+ MAIN_LOOP_START = 0;
+
+ // The beginning of navigation in the first web contents' main frame.
+ // Based on histogram Startup.FirstWebContents.MainNavigationStart.
+ MAIN_NAVIGATION_START = 1;
+
+ // The navigation is committed (first bytes received) in the first web
+ // contents' main frame.
+ // Based on histogram Startup.FirstWebContents.MainNavigationFinished.
+ MAIN_NAVIGATION_FINISHED = 2;
+
+ // First non-empty paint of the first web contents.
+ // Based on histogram Startup.FirstWebContents.NonEmptyPaint2.
+ FIRST_NONEMPTY_PAINT = 3;
+
+ // Process shutdown has begun.
+ SHUTDOWN_START = 4;
+}
diff --git a/chromium/components/metrics/proto/system_profile.proto b/chromium/components/metrics/proto/system_profile.proto
index c116a0a1d19..34975295da9 100644
--- a/chromium/components/metrics/proto/system_profile.proto
+++ b/chromium/components/metrics/proto/system_profile.proto
@@ -281,6 +281,7 @@ message SystemProfileProto {
optional Hardware hardware = 6;
// Information about the network connection.
+ // Next tag: 7
message Network {
// Set to true if connection_type changed during the lifetime of the log.
optional bool connection_type_is_ambiguous = 1;
@@ -366,6 +367,27 @@ message SystemProfileProto {
}
// Information of the wireless AP that device is connected to.
optional WifiAccessPoint access_point_info = 5;
+
+ // Derived from net::NetworkQualityEstimator::EffectiveConnectionType
+ // 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;
+ 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.
+ // 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 Network network = 13;
diff --git a/chromium/components/metrics/public/cpp/call_stack_profile.typemap b/chromium/components/metrics/public/cpp/call_stack_profile.typemap
index b189a585661..611148c8255 100644
--- a/chromium/components/metrics/public/cpp/call_stack_profile.typemap
+++ b/chromium/components/metrics/public/cpp/call_stack_profile.typemap
@@ -17,6 +17,7 @@ deps = [
type_mappings = [
"metrics.mojom.CallStackModule=base::StackSamplingProfiler::Module",
"metrics.mojom.CallStackFrame=base::StackSamplingProfiler::Frame",
+ "metrics.mojom.CallStackSample=base::StackSamplingProfiler::Sample[move_only]",
"metrics.mojom.CallStackProfile=base::StackSamplingProfiler::CallStackProfile[move_only]",
"metrics.mojom.CallStackProfileParams=metrics::CallStackProfileParams",
"metrics.mojom.Process=metrics::CallStackProfileParams::Process",
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 d2ec654205d..ddf78d5f3c9 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
@@ -34,13 +34,9 @@ struct StructTraits<metrics::mojom::CallStackModuleDataView,
static bool Read(metrics::mojom::CallStackModuleDataView data,
base::StackSamplingProfiler::Module* out) {
- // Linux has the longest build id at 40 bytes.
- static const size_t kMaxIDSize = 40;
-
std::string id;
base::FilePath filename;
- if (!data.ReadId(&id) || id.size() > kMaxIDSize ||
- !data.ReadFilename(&filename))
+ if (!data.ReadId(&id) || !data.ReadFilename(&filename))
return false;
*out =
@@ -80,6 +76,31 @@ struct StructTraits<metrics::mojom::CallStackFrameDataView,
};
template <>
+struct StructTraits<metrics::mojom::CallStackSampleDataView,
+ base::StackSamplingProfiler::Sample> {
+ static const std::vector<base::StackSamplingProfiler::Frame>& frames(
+ const base::StackSamplingProfiler::Sample& sample) {
+ return sample.frames;
+ }
+ static int32_t process_milestones(
+ const base::StackSamplingProfiler::Sample& sample) {
+ return sample.process_milestones;
+ }
+
+ static bool Read(metrics::mojom::CallStackSampleDataView data,
+ base::StackSamplingProfiler::Sample* out) {
+ std::vector<base::StackSamplingProfiler::Frame> frames;
+ if (!data.ReadFrames(&frames))
+ return false;
+
+ *out = base::StackSamplingProfiler::Sample();
+ out->frames = std::move(frames);
+ out->process_milestones = data.process_milestones();
+ return true;
+ }
+};
+
+template <>
struct StructTraits<metrics::mojom::CallStackProfileDataView,
base::StackSamplingProfiler::CallStackProfile> {
static const std::vector<base::StackSamplingProfiler::Module>& modules(
@@ -103,7 +124,7 @@ struct StructTraits<metrics::mojom::CallStackProfileDataView,
std::vector<base::StackSamplingProfiler::Sample> samples,
size_t module_count) {
for (const base::StackSamplingProfiler::Sample& sample : samples) {
- for (const base::StackSamplingProfiler::Frame& frame : sample) {
+ for (const base::StackSamplingProfiler::Frame& frame : sample.frames) {
if (frame.module_index >= module_count &&
frame.module_index !=
base::StackSamplingProfiler::Frame::kUnknownModuleIndex)
@@ -115,10 +136,21 @@ struct StructTraits<metrics::mojom::CallStackProfileDataView,
static bool Read(metrics::mojom::CallStackProfileDataView data,
base::StackSamplingProfiler::CallStackProfile* out) {
- return data.ReadModules(&out->modules) && data.ReadSamples(&out->samples) &&
- data.ReadProfileDuration(&out->profile_duration) &&
- data.ReadSamplingPeriod(&out->sampling_period) &&
- ValidateSamples(out->samples, out->modules.size());
+ std::vector<base::StackSamplingProfiler::Module> modules;
+ std::vector<base::StackSamplingProfiler::Sample> samples;
+ base::TimeDelta profile_duration, sampling_period;
+ if (!data.ReadModules(&modules) || !data.ReadSamples(&samples) ||
+ !data.ReadProfileDuration(&profile_duration) ||
+ !data.ReadSamplingPeriod(&sampling_period) ||
+ !ValidateSamples(samples, modules.size()))
+ return false;
+
+ *out = base::StackSamplingProfiler::CallStackProfile();
+ out->modules = std::move(modules);
+ out->samples = std::move(samples);
+ out->profile_duration = profile_duration;
+ out->sampling_period = sampling_period;
+ return true;
}
};
diff --git a/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc b/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc
index 22265a61822..c7c99c982cf 100644
--- a/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc
+++ b/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc
@@ -89,7 +89,7 @@ class CallStackProfileCollectorTestImpl
class CallStackProfileStructTraitsTest : public testing::Test {
public:
- CallStackProfileStructTraitsTest() : impl_(GetProxy(&proxy_)) {}
+ CallStackProfileStructTraitsTest() : impl_(MakeRequest(&proxy_)) {}
protected:
base::MessageLoop message_loop_;
@@ -130,18 +130,6 @@ TEST_F(CallStackProfileStructTraitsTest, Module) {
Module(0x10, "", base::FilePath(base::FilePath::kCurrentDirectory)),
true
},
- // Module id at the length limit.
- {
- Module(0x10, std::string(40, ' '),
- base::FilePath(base::FilePath::kCurrentDirectory)),
- true
- },
- // Module id beyond the length limit.
- {
- Module(0x10, std::string(41, ' '),
- base::FilePath(base::FilePath::kCurrentDirectory)),
- false
- },
};
for (const SerializeCase& input : serialize_cases) {
@@ -224,11 +212,11 @@ TEST_F(CallStackProfileStructTraitsTest, Profile) {
Module(0x4100, "b", base::FilePath()),
},
{
- {
- Frame(0x4010, 0),
- Frame(0x4110, 1),
- Frame(0x4110, Frame::kUnknownModuleIndex),
- }
+ Sample({
+ Frame(0x4010, 0),
+ Frame(0x4110, 1),
+ Frame(0x4110, Frame::kUnknownModuleIndex),
+ }),
},
base::TimeDelta::FromSeconds(1),
base::TimeDelta::FromSeconds(2)),
@@ -242,15 +230,15 @@ TEST_F(CallStackProfileStructTraitsTest, Profile) {
Module(0x4100, "b", base::FilePath()),
},
{
- {
- Frame(0x4010, 0),
- Frame(0x4110, 1),
- Frame(0x4110, Frame::kUnknownModuleIndex),
- },
- {
- Frame(0x4010, 0),
- Frame(0x4110, 2),
- },
+ Sample({
+ Frame(0x4010, 0),
+ Frame(0x4110, 1),
+ Frame(0x4110, Frame::kUnknownModuleIndex),
+ }),
+ Sample({
+ Frame(0x4010, 0),
+ Frame(0x4110, 2),
+ }),
},
base::TimeDelta::FromSeconds(1),
base::TimeDelta::FromSeconds(2)),
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 6c8f04216c6..ca3fe794e3b 100644
--- a/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom
+++ b/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom
@@ -4,7 +4,8 @@
module metrics.mojom;
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/file_path.mojom";
+import "mojo/common/time.mojom";
// These structs mirror the corresponding types in base::StackSamplingProfiler.
@@ -19,9 +20,14 @@ struct CallStackFrame {
uint64 module_index;
};
+struct CallStackSample {
+ array<CallStackFrame> frames;
+ uint32 process_milestones;
+};
+
struct CallStackProfile {
array<CallStackModule> modules;
- array<array<CallStackFrame>> samples;
+ array<CallStackSample> samples;
mojo.common.mojom.TimeDelta profile_duration;
mojo.common.mojom.TimeDelta sampling_period;
};
diff --git a/chromium/components/metrics/test_metrics_service_client.cc b/chromium/components/metrics/test_metrics_service_client.cc
index 7c7ccc68e07..656fecc7f74 100644
--- a/chromium/components/metrics/test_metrics_service_client.cc
+++ b/chromium/components/metrics/test_metrics_service_client.cc
@@ -52,9 +52,6 @@ std::string TestMetricsServiceClient::GetVersionString() {
return version_string_;
}
-void TestMetricsServiceClient::OnLogUploadComplete() {
-}
-
void TestMetricsServiceClient::InitializeSystemProfileMetrics(
const base::Closure& done_callback) {
done_callback.Run();
@@ -66,6 +63,8 @@ void TestMetricsServiceClient::CollectFinalMetricsForLog(
}
std::unique_ptr<MetricsLogUploader> TestMetricsServiceClient::CreateUploader(
+ const std::string& server_url,
+ const std::string& mime_type,
const base::Callback<void(int)>& on_upload_complete) {
return std::unique_ptr<MetricsLogUploader>();
}
diff --git a/chromium/components/metrics/test_metrics_service_client.h b/chromium/components/metrics/test_metrics_service_client.h
index 355e7c7c5ae..a014c3dbec8 100644
--- a/chromium/components/metrics/test_metrics_service_client.h
+++ b/chromium/components/metrics/test_metrics_service_client.h
@@ -31,11 +31,12 @@ class TestMetricsServiceClient : public MetricsServiceClient {
bool GetBrand(std::string* brand_code) override;
SystemProfileProto::Channel GetChannel() override;
std::string GetVersionString() override;
- void OnLogUploadComplete() override;
void InitializeSystemProfileMetrics(
const base::Closure& done_callback) override;
void CollectFinalMetricsForLog(const base::Closure& done_callback) override;
std::unique_ptr<MetricsLogUploader> CreateUploader(
+ const std::string& server_url,
+ const std::string& mime_type,
const base::Callback<void(int)>& on_upload_complete) override;
base::TimeDelta GetStandardUploadInterval() override;
bool IsReportingPolicyManaged() override;
diff --git a/chromium/components/metrics_services_manager/metrics_services_manager.cc b/chromium/components/metrics_services_manager/metrics_services_manager.cc
index 652edef82c8..c64edc83f5c 100644
--- a/chromium/components/metrics_services_manager/metrics_services_manager.cc
+++ b/chromium/components/metrics_services_manager/metrics_services_manager.cc
@@ -11,7 +11,7 @@
#include "components/metrics/metrics_service_client.h"
#include "components/metrics/metrics_state_manager.h"
#include "components/metrics_services_manager/metrics_services_manager_client.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
#include "components/variations/service/variations_service.h"
namespace metrics_services_manager {
@@ -34,10 +34,10 @@ metrics::MetricsService* MetricsServicesManager::GetMetricsService() {
return GetMetricsServiceClient()->GetMetricsService();
}
-rappor::RapporService* MetricsServicesManager::GetRapporService() {
+rappor::RapporServiceImpl* MetricsServicesManager::GetRapporServiceImpl() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!rappor_service_) {
- rappor_service_ = client_->CreateRapporService();
+ rappor_service_ = client_->CreateRapporServiceImpl();
rappor_service_->Initialize(client_->GetURLRequestContext());
}
return rappor_service_.get();
@@ -70,7 +70,7 @@ MetricsServicesManager::GetMetricsServiceClient() {
void MetricsServicesManager::UpdatePermissions(bool may_record,
bool may_upload) {
DCHECK(thread_checker_.CalledOnValidThread());
- // Stash the current permissions so that we can update the RapporService
+ // Stash the current permissions so that we can update the RapporServiceImpl
// correctly when the Rappor preference changes. The metrics recording
// preference partially determines the initial rappor setting, and also
// controls whether FINE metrics are sent.
@@ -85,7 +85,7 @@ void MetricsServicesManager::UpdateRunningServices() {
if (client_->OnlyDoMetricsRecording()) {
metrics->StartRecordingForTests();
- GetRapporService()->Update(
+ GetRapporServiceImpl()->Update(
rappor::UMA_RAPPOR_GROUP | rappor::SAFEBROWSING_RAPPOR_GROUP, false);
return;
}
@@ -119,7 +119,7 @@ void MetricsServicesManager::UpdateRunningServices() {
if (client_->IsSafeBrowsingEnabled(on_safe_browsing_update_callback))
recording_groups |= rappor::SAFEBROWSING_RAPPOR_GROUP;
#endif // defined(GOOGLE_CHROME_BUILD)
- GetRapporService()->Update(recording_groups, may_upload_);
+ GetRapporServiceImpl()->Update(recording_groups, may_upload_);
}
void MetricsServicesManager::UpdateUploadPermissions(bool may_upload) {
diff --git a/chromium/components/metrics_services_manager/metrics_services_manager.h b/chromium/components/metrics_services_manager/metrics_services_manager.h
index e70d6489066..19245c14059 100644
--- a/chromium/components/metrics_services_manager/metrics_services_manager.h
+++ b/chromium/components/metrics_services_manager/metrics_services_manager.h
@@ -22,7 +22,7 @@ class MetricsStateManager;
}
namespace rappor {
-class RapporService;
+class RapporServiceImpl;
}
namespace variations {
@@ -35,7 +35,7 @@ class MetricsServicesManagerClient;
// MetricsServicesManager is a helper class for embedders that use the various
// metrics-related services in a Chrome-like fashion: MetricsService (via its
-// client), RapporService and VariationsService.
+// client), RapporServiceImpl and VariationsService.
class MetricsServicesManager {
public:
// Creates the MetricsServicesManager with the given client.
@@ -57,8 +57,8 @@ class MetricsServicesManager {
// additionally creating the MetricsServiceClient in that case).
metrics::MetricsService* GetMetricsService();
- // Returns the RapporService, creating it if it hasn't been created yet.
- rappor::RapporService* GetRapporService();
+ // Returns the RapporServiceImpl, creating it if it hasn't been created yet.
+ rappor::RapporServiceImpl* GetRapporServiceImpl();
// Returns the VariationsService, creating it if it hasn't been created yet.
variations::VariationsService* GetVariationsService();
@@ -80,7 +80,7 @@ class MetricsServicesManager {
private:
// Update the managed services when permissions for recording/uploading
// metrics change.
- void UpdateRapporService();
+ void UpdateRapporServiceImpl();
// Returns the MetricsServiceClient, creating it if it hasn't been
// created yet (and additionally creating the MetricsService in that case).
@@ -106,8 +106,8 @@ class MetricsServicesManager {
// The MetricsServiceClient. Owns the MetricsService.
std::unique_ptr<metrics::MetricsServiceClient> metrics_service_client_;
- // The RapporService, for RAPPOR metric uploads.
- std::unique_ptr<rappor::RapporService> rappor_service_;
+ // The RapporServiceImpl, for RAPPOR metric uploads.
+ std::unique_ptr<rappor::RapporServiceImpl> rappor_service_;
// The VariationsService, for server-side experiments infrastructure.
std::unique_ptr<variations::VariationsService> variations_service_;
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 7a5779c81a8..b53d39dbc55 100644
--- a/chromium/components/metrics_services_manager/metrics_services_manager_client.h
+++ b/chromium/components/metrics_services_manager/metrics_services_manager_client.h
@@ -19,7 +19,7 @@ class URLRequestContextGetter;
}
namespace rappor {
-class RapporService;
+class RapporServiceImpl;
}
namespace variations {
@@ -35,7 +35,8 @@ class MetricsServicesManagerClient {
virtual ~MetricsServicesManagerClient() {}
// Methods that create the various services in the context of the embedder.
- virtual std::unique_ptr<rappor::RapporService> CreateRapporService() = 0;
+ virtual std::unique_ptr<rappor::RapporServiceImpl>
+ CreateRapporServiceImpl() = 0;
virtual std::unique_ptr<variations::VariationsService>
CreateVariationsService() = 0;
virtual std::unique_ptr<metrics::MetricsServiceClient>
diff --git a/chromium/components/minidump_uploader/BUILD.gn b/chromium/components/minidump_uploader/BUILD.gn
index a73f1683ad3..1da865adc5a 100644
--- a/chromium/components/minidump_uploader/BUILD.gn
+++ b/chromium/components/minidump_uploader/BUILD.gn
@@ -25,6 +25,8 @@ android_library("minidump_uploader_javatests") {
":minidump_uploader_java",
"//base:base_java",
"//base:base_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/junit",
]
java_files = [
"android/javatests/src/org/chromium/components/minidump_uploader/CrashFileManagerTest.java",
diff --git a/chromium/components/nacl/broker/BUILD.gn b/chromium/components/nacl/broker/BUILD.gn
index 08e44891874..3a2b3607918 100644
--- a/chromium/components/nacl/broker/BUILD.gn
+++ b/chromium/components/nacl/broker/BUILD.gn
@@ -4,6 +4,7 @@
import("//build/config/features.gni")
import("//build/config/compiler/compiler.gni")
+import("//services/service_manager/public/service_manifest.gni")
# This file builds nacl64.exe, which is a 64-bit x86 Windows executable
# used only in the 32-bit x86 Windows build. The :broker code runs both
@@ -29,6 +30,7 @@ source_set("broker") {
"//ipc",
"//mojo/edk/system",
"//sandbox",
+ "//services/service_manager/public/cpp",
]
if (current_cpu == target_cpu) {
@@ -36,6 +38,10 @@ source_set("broker") {
} else {
deps += [ ":content_dummy" ]
}
+
+ data_deps = [
+ ":nacl_broker_manifest",
+ ]
}
# This exists just to make 'gn check' happy with :broker. It can't depend
@@ -169,3 +175,8 @@ if (current_cpu == "x86") {
]
}
}
+
+service_manifest("nacl_broker_manifest") {
+ name = "nacl_broker"
+ source = "nacl_broker_manifest.json"
+}
diff --git a/chromium/components/nacl/common/BUILD.gn b/chromium/components/nacl/common/BUILD.gn
index 2114eafcecf..de3d4694f42 100644
--- a/chromium/components/nacl/common/BUILD.gn
+++ b/chromium/components/nacl/common/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/config/features.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
if (enable_nacl) {
# This is separate so it can be used by ../broker:nacl64.
@@ -15,9 +16,9 @@ if (enable_nacl) {
"nacl_messages.cc",
"nacl_messages.h",
"nacl_process_type.h",
- "nacl_renderer_messages.cc",
- "nacl_renderer_messages.h",
"nacl_sandbox_type.h",
+ "nacl_service.cc",
+ "nacl_service.h",
"nacl_types.cc",
"nacl_types.h",
"nacl_types_param_traits.cc",
@@ -25,14 +26,19 @@ if (enable_nacl) {
]
public_deps = [
+ ":minimal_content_dummy",
":switches",
]
deps = [
- ":minimal_content_dummy",
+ ":nacl_error_code",
"//base",
"//base:base_static",
+ "//content/public/common:service_names",
"//ipc",
+ "//ipc:mojom",
+ "//mojo/edk/system",
+ "//services/service_manager/public/cpp",
]
}
@@ -43,10 +49,17 @@ if (enable_nacl) {
source_set("minimal_content_dummy") {
check_includes = false
sources = [
+ "//content/public/common/content_descriptors.h",
"//content/public/common/content_switches.h",
+ "//content/public/common/mojo_channel_switches.h",
"//content/public/common/process_type.h",
"//content/public/common/sandbox_type.h",
]
+
+ # Deps required by the above headers.
+ deps = [
+ "//media:media_features",
+ ]
}
static_library("common") {
@@ -61,6 +74,7 @@ if (enable_nacl) {
public_deps = [
":minimal",
+ ":mojo_bindings",
":switches",
]
@@ -91,6 +105,18 @@ if (enable_nacl) {
"//base",
]
}
+
+ source_set("nacl_error_code") {
+ sources = [
+ "//native_client/src/trusted/service_runtime/nacl_error_code.h",
+ ]
+ }
+
+ mojom("mojo_bindings") {
+ sources = [
+ "nacl.mojom",
+ ]
+ }
}
# Depending on this allows targets to unconditionally include
diff --git a/chromium/components/nacl/common/nacl.mojom b/chromium/components/nacl/common/nacl.mojom
new file mode 100644
index 00000000000..70b1286953e
--- /dev/null
+++ b/chromium/components/nacl/common/nacl.mojom
@@ -0,0 +1,26 @@
+// 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 nacl.mojom;
+
+[Native]
+enum NaClErrorCode;
+
+interface NaClRendererHost {
+ // This message must be synchronous to ensure that the exit status is sent
+ // from NaCl to the renderer before the NaCl process exits very soon after.
+ [Sync]
+ ReportExitStatus(int32 exit_status) => ();
+
+ // This message must be synchronous to ensure that the load status is sent
+ // from NaCl to the renderer before the NaCl process exits very soon after.
+ [Sync]
+ ReportLoadStatus(NaClErrorCode load_status) => ();
+
+ ProvideExitControl(NaClExitControl exit_control);
+};
+
+// When this interface is closed, it indicates that the NaCl loader process
+// should exit.
+interface NaClExitControl {};
diff --git a/chromium/components/nacl/common/nacl.typemap b/chromium/components/nacl/common/nacl.typemap
new file mode 100644
index 00000000000..019d805c18a
--- /dev/null
+++ b/chromium/components/nacl/common/nacl.typemap
@@ -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.
+
+mojom = "//components/nacl/common/nacl.mojom"
+public_headers =
+ [ "//native_client/src/trusted/service_runtime/nacl_error_code.h" ]
+traits_headers = [ "//components/nacl/common/nacl_types_param_traits.h" ]
+deps = [
+ "//components/nacl/common:minimal",
+ "//components/nacl/common:nacl_error_code",
+ "//ipc",
+]
+
+type_mappings = [ "nacl.mojom.NaClErrorCode=::NaClErrorCode" ]
diff --git a/chromium/components/nacl/loader/BUILD.gn b/chromium/components/nacl/loader/BUILD.gn
index cef8842020d..e770d17b42f 100644
--- a/chromium/components/nacl/loader/BUILD.gn
+++ b/chromium/components/nacl/loader/BUILD.gn
@@ -4,6 +4,7 @@
import("//build/config/features.gni")
import("//build/config/nacl/config.gni")
+import("//services/service_manager/public/service_manifest.gni")
import("//testing/test.gni")
assert(enable_nacl)
@@ -31,6 +32,7 @@ source_set("minimal") {
":minimal_content_dummy",
"//base",
"//components/nacl/common:minimal",
+ "//components/nacl/common:mojo_bindings",
"//crypto",
"//ipc",
"//mojo/edk/system",
@@ -38,6 +40,7 @@ source_set("minimal") {
"//ppapi/c",
"//ppapi/proxy:ipc",
"//sandbox",
+ "//services/service_manager/public/cpp",
]
}
@@ -49,12 +52,14 @@ source_set("minimal_content_dummy") {
check_includes = false
sources = [
"//content/public/common/child_process_sandbox_support_linux.h",
- "//content/public/common/content_descriptors.h",
- "//content/public/common/content_switches.h",
"//content/public/common/main_function_params.h",
- "//content/public/common/mojo_channel_switches.h",
"//content/public/common/sandbox_init.h",
]
+
+ # Deps required by the above headers.
+ deps = [
+ "//media:media_features",
+ ]
}
source_set("loader") {
@@ -65,9 +70,11 @@ source_set("loader") {
"//components/nacl/common",
"//content/public/common",
"//ppapi/shared_impl",
+ "//services/service_manager/public/cpp",
]
data_deps = [
+ ":nacl_loader_manifest",
"//ppapi/native_client:irt",
"//ppapi/native_client/src/untrusted/pnacl_support_extension",
]
@@ -190,13 +197,6 @@ if (is_nacl_nonsfi) {
output_name = "nacl_helper_nonsfi"
set_sources_assignment_filter([])
sources = [
- # TODO(brettw) can this just depend on //components/nacl/common?
- "../common/nacl_messages.cc",
- "../common/nacl_messages.h",
- "../common/nacl_types.cc",
- "../common/nacl_types.h",
- "../common/nacl_types_param_traits.cc",
- "../common/nacl_types_param_traits.h",
"nacl_helper_linux.cc",
"nacl_helper_linux.h",
"nacl_trusted_listener.cc",
@@ -209,6 +209,8 @@ if (is_nacl_nonsfi) {
deps = [
":nacl_helper_nonsfi_sandbox",
"//base",
+ "//components/nacl/common:minimal",
+ "//components/nacl/common:mojo_bindings",
"//components/nacl/common:switches",
"//components/tracing",
"//content",
@@ -225,6 +227,7 @@ if (is_nacl_nonsfi) {
"//native_client/src/untrusted/nacl",
"//ppapi/proxy",
"//sandbox/linux:sandbox",
+ "//services/service_manager/public/cpp",
]
}
@@ -238,6 +241,7 @@ if (is_nacl_nonsfi) {
]
deps = [
"//base",
+ "//components/nacl/common:minimal",
"//components/nacl/common:switches",
"//content",
"//sandbox/linux:sandbox",
@@ -290,3 +294,8 @@ if (is_nacl_nonsfi) {
]
}
}
+
+service_manifest("nacl_loader_manifest") {
+ name = "nacl_loader"
+ source = "nacl_loader_manifest.json"
+}
diff --git a/chromium/components/nacl/loader/sandbox_linux/BUILD.gn b/chromium/components/nacl/loader/sandbox_linux/BUILD.gn
index ce1d40447ff..8f61844d0bc 100644
--- a/chromium/components/nacl/loader/sandbox_linux/BUILD.gn
+++ b/chromium/components/nacl/loader/sandbox_linux/BUILD.gn
@@ -24,14 +24,11 @@ source_set("sandbox_linux") {
"//crypto",
"//ipc",
"//sandbox",
+ "//sandbox:sandbox_features",
"//sandbox/linux:sandbox_services_headers",
]
if (use_glib) {
configs += [ "//build/config/linux:glib" ]
}
-
- if (use_seccomp_bpf) {
- defines += [ "USE_SECCOMP_BPF" ]
- }
}
diff --git a/chromium/components/nacl/renderer/plugin/BUILD.gn b/chromium/components/nacl/renderer/plugin/BUILD.gn
index 873422e0021..a0025956c75 100644
--- a/chromium/components/nacl/renderer/plugin/BUILD.gn
+++ b/chromium/components/nacl/renderer/plugin/BUILD.gn
@@ -16,6 +16,7 @@ static_library("nacl_trusted_plugin") {
deps = [
"//base",
+ "//components/nacl/common:nacl_error_code",
"//content/public/common",
"//media:shared_memory_support",
"//ppapi/c",
diff --git a/chromium/components/navigation_interception/intercept_navigation_delegate.h b/chromium/components/navigation_interception/intercept_navigation_delegate.h
index e8e0ecb2875..c7015c5fc5d 100644
--- a/chromium/components/navigation_interception/intercept_navigation_delegate.h
+++ b/chromium/components/navigation_interception/intercept_navigation_delegate.h
@@ -11,8 +11,6 @@
#include "base/macros.h"
#include "base/supports_user_data.h"
-class GURL;
-
namespace content {
class NavigationHandle;
class NavigationThrottle;
diff --git a/chromium/components/navigation_interception/intercept_navigation_throttle.h b/chromium/components/navigation_interception/intercept_navigation_throttle.h
index 2367c5b921e..b70c007e0d6 100644
--- a/chromium/components/navigation_interception/intercept_navigation_throttle.h
+++ b/chromium/components/navigation_interception/intercept_navigation_throttle.h
@@ -12,8 +12,6 @@
#include "base/memory/weak_ptr.h"
#include "content/public/browser/navigation_throttle.h"
-class GURL;
-
namespace content {
class NavigationHandle;
class WebContents;
diff --git a/chromium/components/neterror/resources/neterror.js b/chromium/components/neterror/resources/neterror.js
index 1926b5c06f6..16175091a1d 100644
--- a/chromium/components/neterror/resources/neterror.js
+++ b/chromium/components/neterror/resources/neterror.js
@@ -22,17 +22,17 @@ function toggleHelpBox() {
}
function diagnoseErrors() {
-<if expr="not chromeos">
+// <if expr="not chromeos">
if (window.errorPageController)
errorPageController.diagnoseErrorsButtonClick();
-</if>
-<if expr="chromeos">
+// </if>
+// <if expr="chromeos">
var extensionId = 'idddmepepmjcgiedknnmlbadcokidhoa';
var diagnoseFrame = document.getElementById('diagnose-frame');
diagnoseFrame.innerHTML =
'<iframe src="chrome-extension://' + extensionId +
'/index.html"></iframe>';
-</if>
+// </if>
}
// Subframes use a different layout but the same html file. This is to make it
@@ -150,9 +150,9 @@ function setUpCachedButton(buttonStrings) {
}
var primaryControlOnLeft = true;
-<if expr="is_macosx or is_ios or is_linux or is_android">
+// <if expr="is_macosx or is_ios or is_linux or is_android">
primaryControlOnLeft = false;
-</if>
+// </if>
function onDocumentLoad() {
var controlButtonDiv = document.getElementById('control-buttons');
diff --git a/chromium/components/neterror/resources/offline.js b/chromium/components/neterror/resources/offline.js
index 63336038b6f..79ffa5c4da8 100644
--- a/chromium/components/neterror/resources/offline.js
+++ b/chromium/components/neterror/resources/offline.js
@@ -89,11 +89,10 @@ var FPS = 60;
var IS_HIDPI = window.devicePixelRatio > 1;
/** @const */
-var IS_IOS = window.navigator.userAgent.indexOf('CriOS') > -1 ||
- window.navigator.userAgent == 'UIWebViewForStaticFileContent';
+var IS_IOS = /iPad|iPhone|iPod/.test(window.navigator.platform);
/** @const */
-var IS_MOBILE = window.navigator.userAgent.indexOf('Mobi') > -1 || IS_IOS;
+var IS_MOBILE = /Android/.test(window.navigator.userAgent) || IS_IOS;
/** @const */
var IS_TOUCH_ENABLED = 'ontouchstart' in window;
diff --git a/chromium/components/network_session_configurator/network_session_configurator.cc b/chromium/components/network_session_configurator/network_session_configurator.cc
index 593da58040c..4e9757f98e2 100644
--- a/chromium/components/network_session_configurator/network_session_configurator.cc
+++ b/chromium/components/network_session_configurator/network_session_configurator.cc
@@ -5,6 +5,7 @@
#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"
@@ -15,8 +16,9 @@
#include "components/variations/variations_associated_data.h"
#include "components/version_info/version_info.h"
#include "net/http/http_stream_factory.h"
-#include "net/quic/core/quic_protocol.h"
-#include "net/quic/core/quic_utils.h"
+#include "net/quic/chromium/quic_utils_chromium.h"
+#include "net/quic/core/quic_packets.h"
+#include "net/spdy/spdy_protocol.h"
#include "net/url_request/url_fetcher.h"
namespace {
@@ -53,11 +55,40 @@ void ConfigureTCPFastOpenParams(base::StringPiece tfo_trial_group,
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,
@@ -103,7 +134,7 @@ net::QuicTagVector GetQuicConnectionOptions(
return net::QuicTagVector();
}
- return net::QuicUtils::ParseQuicConnectionOptions(it->second);
+ return net::ParseQuicConnectionOptions(it->second);
}
bool ShouldQuicAlwaysRequireHandshakeConfirmation(
@@ -211,16 +242,13 @@ int GetQuicPacketReaderYieldAfterDurationMilliseconds(
bool ShouldQuicRaceCertVerification(
const VariationParameters& quic_trial_params) {
- return base::LowerCaseEqualsASCII(
- GetVariationParam(quic_trial_params, "race_cert_verification"),
- "true");
+ 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 ShouldQuicDoNotFragment(const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params, "do_not_fragment"), "true");
}
bool ShouldQuicDisablePreConnectIfZeroRtt(
@@ -316,8 +344,6 @@ void ConfigureQuicParams(base::StringPiece quic_trial_group,
ShouldQuicDisableDiskCache(quic_trial_params);
params->quic_prefer_aes = ShouldQuicPreferAes(quic_trial_params);
params->quic_force_hol_blocking = ShouldForceHolBlocking(quic_trial_params);
- // Default to disabling port selection on all channels.
- params->enable_quic_port_selection = false;
params->quic_connection_options =
GetQuicConnectionOptions(quic_trial_params);
params->quic_close_sessions_on_ip_change =
@@ -370,6 +396,14 @@ void ConfigureQuicParams(base::StringPiece quic_trial_group,
}
}
+void ConfigureOptimizePreconnectsToProxiesParams(
+ const std::map<std::string, std::string>& proxy_preconnects_trial_params,
+ net::HttpNetworkSession::Params* params) {
+ params->restrict_to_one_preconnect_for_proxies =
+ GetVariationParam(proxy_preconnects_trial_params,
+ "restrict_to_one_preconnect_for_proxies") == "true";
+}
+
} // anonymous namespace
namespace network_session_configurator {
@@ -401,11 +435,21 @@ void ParseFieldTrials(bool is_quic_force_disabled,
std::string http2_trial_group =
base::FieldTrialList::FindFullName(kHttp2FieldTrialName);
- ConfigureHttp2Params(http2_trial_group, params);
+ 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);
+
+ std::map<std::string, std::string> proxy_preconnects_trial_params;
+ variations::GetVariationParams("NetProxyPreconnects",
+ &proxy_preconnects_trial_params);
+ ConfigureOptimizePreconnectsToProxiesParams(proxy_preconnects_trial_params,
+ params);
}
} // namespace network_session_configurator
diff --git a/chromium/components/network_session_configurator/network_session_configurator_unittest.cc b/chromium/components/network_session_configurator/network_session_configurator_unittest.cc
index 26e5ad9ebff..09fd36038f9 100644
--- a/chromium/components/network_session_configurator/network_session_configurator_unittest.cc
+++ b/chromium/components/network_session_configurator/network_session_configurator_unittest.cc
@@ -12,7 +12,8 @@
#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_protocol.h"
+#include "net/quic/core/quic_packets.h"
+#include "net/spdy/spdy_protocol.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace test {
@@ -46,6 +47,7 @@ TEST_F(NetworkSessionConfiguratorTest, Defaults) {
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_TRUE(params_.enable_quic_alternative_service_with_different_host);
EXPECT_FALSE(params_.enable_quic);
@@ -295,6 +297,20 @@ TEST_F(NetworkSessionConfiguratorTest,
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,
QuicAlwaysRequireHandshakeConfirmationFromFieldTrialParams) {
std::map<std::string, std::string> field_trial_params;
diff --git a/chromium/components/network_time/network_time_tracker.cc b/chromium/components/network_time/network_time_tracker.cc
index 89d2ad7f9d2..f1017741e32 100644
--- a/chromium/components/network_time/network_time_tracker.cc
+++ b/chromium/components/network_time/network_time_tracker.cc
@@ -534,8 +534,19 @@ bool NetworkTimeTracker::UpdateTimeFromResponse() {
base::TimeDelta resolution =
base::TimeDelta::FromMilliseconds(1) +
base::TimeDelta::FromSeconds(kTimeServerMaxSkewSeconds);
+
+ // Record histograms for the latency of the time query and the time delta
+ // between time fetches.
base::TimeDelta latency = tick_clock_->NowTicks() - fetch_started_;
UMA_HISTOGRAM_TIMES("NetworkTimeTracker.TimeQueryLatency", latency);
+ if (!last_fetched_time_.is_null()) {
+ UMA_HISTOGRAM_CUSTOM_TIMES("NetworkTimeTracker.TimeBetweenFetches",
+ current_time - last_fetched_time_,
+ base::TimeDelta::FromHours(1),
+ base::TimeDelta::FromDays(7), 50);
+ }
+ last_fetched_time_ = current_time;
+
UpdateNetworkTime(current_time, resolution, latency, tick_clock_->NowTicks());
return true;
}
diff --git a/chromium/components/network_time/network_time_tracker.h b/chromium/components/network_time/network_time_tracker.h
index ea5bbc5b91a..92336473678 100644
--- a/chromium/components/network_time/network_time_tracker.h
+++ b/chromium/components/network_time/network_time_tracker.h
@@ -217,6 +217,12 @@ class NetworkTimeTracker : public net::URLFetcherDelegate {
// this NetworkTimeTracker's lifetime.
bool time_query_completed_;
+ // The time that was received from the last network time fetch made by
+ // CheckTime(). Unlike |network_time_at_least_measurement_|, this time
+ // is not updated when UpdateNetworkTime() is called. Used for UMA
+ // metrics.
+ base::Time last_fetched_time_;
+
// Callbacks to run when the in-progress time fetch completes.
std::vector<base::Closure> fetch_completion_callbacks_;
diff --git a/chromium/components/network_time/network_time_tracker_unittest.cc b/chromium/components/network_time/network_time_tracker_unittest.cc
index 391d87a3e4a..747a38ae12b 100644
--- a/chromium/components/network_time/network_time_tracker_unittest.cc
+++ b/chromium/components/network_time/network_time_tracker_unittest.cc
@@ -38,6 +38,8 @@ const char kClockDivergenceNegativeHistogram[] =
"NetworkTimeTracker.ClockDivergence.Negative";
const char kWallClockBackwardsHistogram[] =
"NetworkTimeTracker.WallClockRanBackwards";
+const char kTimeBetweenFetchesHistogram[] =
+ "NetworkTimeTracker.TimeBetweenFetches";
} // namespace
class NetworkTimeTrackerTest : public ::testing::Test {
@@ -813,4 +815,133 @@ TEST_F(NetworkTimeTrackerTest, UpdateFromNetworkSubseqeuntSyncPending) {
tracker_->WaitForFetchForTesting(123123123);
}
+namespace {
+
+// NetworkTimeTrackerTest.TimeBetweenFetchesHistogram needs to make several time
+// queries that return different times. MultipleGoodTimeResponseHandler is like
+// GoodTimeResponseHandler, but returning different times on each of three
+// requests that happen in sequence.
+//
+// See comments inline for how to update the times that are returned.
+class MultipleGoodTimeResponseHandler {
+ public:
+ MultipleGoodTimeResponseHandler() {}
+ ~MultipleGoodTimeResponseHandler() {}
+
+ std::unique_ptr<net::test_server::HttpResponse> ResponseHandler(
+ const net::test_server::HttpRequest& request);
+
+ // Returns the time that is returned in the (i-1)'th response handled by
+ // ResponseHandler(), or null base::Time() if too many responses have been
+ // handled.
+ base::Time GetTimeAtIndex(unsigned int i);
+
+ private:
+ // |kJsTimes|, |kTimeResponseBodies|, and |kTimeProofHeaders| contain signed
+ // responses for three subsequent time queries served by
+ // MultipleGoodTimeResponseHandler. (That is, kJsTimes[i] is the timestamp
+ // contained in kTimeResponseBodies[i] with signature in kTimeProofHeader[i].)
+ // NetworkTimeTrackerTest.TimeBetweenFetchesHistogram expects that each
+ // timestamp is greater than the one before it.
+ //
+ // Update as follows:
+ //
+ // curl -v http://clients2.google.com/time/1/current?cup2key=1:123123123
+ //
+ // where 1 is the key version and 123123123 is the nonce. Copy the
+ // response and the x-cup-server-proof header into
+ // |kTimeResponseBodies| and |kTimeProofHeaders| respectively, and the
+ // 'current_time_millis' value of the response into |kJsTimes|.
+ static const double kJsTimes[];
+ static const char* kTimeResponseBodies[];
+ static const char* kTimeProofHeaders[];
+
+ // The index into |kJsTimes|, |kTimeResponseBodies|, and
+ // |kTimeProofHeaders| that will be used in the response in the next
+ // ResponseHandler() call.
+ unsigned int next_time_index_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(MultipleGoodTimeResponseHandler);
+};
+
+const double MultipleGoodTimeResponseHandler::kJsTimes[] = {1481653709754,
+ 1481653820879};
+const char* MultipleGoodTimeResponseHandler::kTimeResponseBodies[] = {
+ ")]}'\n"
+ "{\"current_time_millis\":1481653709754,\"server_nonce\":-2."
+ "7144232419525693E172}",
+ ")]}'\n"
+ "{\"current_time_millis\":1481653820879,\"server_nonce\":1."
+ "8874633267958474E185}"};
+const char* MultipleGoodTimeResponseHandler::kTimeProofHeaders[] = {
+ "3045022006fdfa882460cd43e15b11d7d35cfc3805b0662c558f6efe54f9bf0c38e80650"
+ "0221009777817152b6cc1c2b2ea765104a1ab6b87a4da1e87686ae0641c25b23161ea8:"
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "3045022100b6ebcf0f2f5c42bb18bd097a60c4204dd2ed29cad4992b5fdfcf1b32bdfdc6"
+ "58022005b378c27dd3ddb6edacce39edc8b4ecf189dff5b64ce99975859f6cdc984e20:"
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"};
+
+std::unique_ptr<net::test_server::HttpResponse>
+MultipleGoodTimeResponseHandler::ResponseHandler(
+ const net::test_server::HttpRequest& request) {
+ net::test_server::BasicHttpResponse* response =
+ new net::test_server::BasicHttpResponse();
+
+ if (next_time_index_ >=
+ arraysize(MultipleGoodTimeResponseHandler::kJsTimes)) {
+ response->set_code(net::HTTP_BAD_REQUEST);
+ return std::unique_ptr<net::test_server::HttpResponse>(response);
+ }
+
+ response->set_code(net::HTTP_OK);
+ response->set_content(kTimeResponseBodies[next_time_index_]);
+ response->AddCustomHeader("x-cup-server-proof",
+ kTimeProofHeaders[next_time_index_]);
+ next_time_index_++;
+ return std::unique_ptr<net::test_server::HttpResponse>(response);
+}
+
+base::Time MultipleGoodTimeResponseHandler::GetTimeAtIndex(unsigned int i) {
+ if (i >= arraysize(kJsTimes))
+ return base::Time();
+ return base::Time::FromJsTime(kJsTimes[i]);
+}
+
+} // namespace
+
+TEST_F(NetworkTimeTrackerTest, TimeBetweenFetchesHistogram) {
+ MultipleGoodTimeResponseHandler response_handler;
+ base::HistogramTester histograms;
+ histograms.ExpectTotalCount(kTimeBetweenFetchesHistogram, 0);
+
+ test_server_->RegisterRequestHandler(
+ base::Bind(&MultipleGoodTimeResponseHandler::ResponseHandler,
+ base::Unretained(&response_handler)));
+ EXPECT_TRUE(test_server_->Start());
+ tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
+ tracker_->WaitForFetchForTesting(123123123);
+
+ base::Time out_network_time;
+ EXPECT_EQ(NetworkTimeTracker::NETWORK_TIME_AVAILABLE,
+ tracker_->GetNetworkTime(&out_network_time, nullptr));
+ // After the first query, there should be no histogram value because
+ // there was no delta to record.
+ histograms.ExpectTotalCount(kTimeBetweenFetchesHistogram, 0);
+
+ // Trigger a second query, which should cause the delta from the first
+ // query to be recorded.
+ clock_->Advance(base::TimeDelta::FromHours(1));
+ EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
+ tracker_->WaitForFetchForTesting(123123123);
+ EXPECT_EQ(NetworkTimeTracker::NETWORK_TIME_AVAILABLE,
+ tracker_->GetNetworkTime(&out_network_time, nullptr));
+ histograms.ExpectTotalCount(kTimeBetweenFetchesHistogram, 1);
+ histograms.ExpectBucketCount(
+ kTimeBetweenFetchesHistogram,
+ (response_handler.GetTimeAtIndex(1) - response_handler.GetTimeAtIndex(0))
+ .InMilliseconds(),
+ 1);
+}
+
} // namespace network_time
diff --git a/chromium/components/new_or_sad_tab_strings.grdp b/chromium/components/new_or_sad_tab_strings.grdp
index b04b83d57f9..a88f66630c4 100644
--- a/chromium/components/new_or_sad_tab_strings.grdp
+++ b/chromium/components/new_or_sad_tab_strings.grdp
@@ -31,6 +31,11 @@
<message name="IDS_SAD_TAB_RELOAD_LABEL" desc="Button label in the sad tab page for reloading a page." formatter_data="android_java">
Reload
</message>
+ <if expr="is_android">
+ <message name="IDS_SAD_TAB_SEND_FEEDBACK_LABEL" desc="Button label in the sad tab page for sending feedback. This label replaces the reload button after a crash happens twice in a row." formatter_data="android_java">
+ Send Feedback
+ </message>
+ </if>
<!-- New Tab -->
<message name="IDS_NEW_TAB_TITLE"
diff --git a/chromium/components/ntp_snippets/BUILD.gn b/chromium/components/ntp_snippets/BUILD.gn
index b65805c63ae..00db4c012cc 100644
--- a/chromium/components/ntp_snippets/BUILD.gn
+++ b/chromium/components/ntp_snippets/BUILD.gn
@@ -18,10 +18,13 @@ static_library("ntp_snippets") {
"callbacks.h",
"category.cc",
"category.h",
- "category_factory.cc",
- "category_factory.h",
"category_info.cc",
"category_info.h",
+ "category_rankers/category_ranker.h",
+ "category_rankers/click_based_category_ranker.cc",
+ "category_rankers/click_based_category_ranker.h",
+ "category_rankers/constant_category_ranker.cc",
+ "category_rankers/constant_category_ranker.h",
"category_status.cc",
"category_status.h",
"content_suggestion.cc",
@@ -44,19 +47,28 @@ static_library("ntp_snippets") {
"pref_names.h",
"pref_util.cc",
"pref_util.h",
+ "remote/json_request.cc",
+ "remote/json_request.h",
"remote/ntp_snippet.cc",
"remote/ntp_snippet.h",
- "remote/ntp_snippets_fetcher.cc",
- "remote/ntp_snippets_fetcher.h",
- "remote/ntp_snippets_scheduler.h",
- "remote/ntp_snippets_status_service.cc",
- "remote/ntp_snippets_status_service.h",
+ "remote/persistent_scheduler.h",
"remote/remote_suggestions_database.cc",
"remote/remote_suggestions_database.h",
+ "remote/remote_suggestions_fetcher.cc",
+ "remote/remote_suggestions_fetcher.h",
"remote/remote_suggestions_provider.cc",
"remote/remote_suggestions_provider.h",
+ "remote/remote_suggestions_provider_impl.cc",
+ "remote/remote_suggestions_provider_impl.h",
+ "remote/remote_suggestions_scheduler.h",
+ "remote/remote_suggestions_status_service.cc",
+ "remote/remote_suggestions_status_service.h",
+ "remote/request_params.cc",
+ "remote/request_params.h",
"remote/request_throttler.cc",
"remote/request_throttler.h",
+ "remote/scheduling_remote_suggestions_provider.cc",
+ "remote/scheduling_remote_suggestions_provider.h",
"sessions/foreign_sessions_suggestions_provider.cc",
"sessions/foreign_sessions_suggestions_provider.h",
"sessions/tab_delegate_sync_adapter.cc",
@@ -74,6 +86,7 @@ static_library("ntp_snippets") {
"//components/keyed_service/core",
"//components/leveldb_proto",
"//components/prefs",
+ "//components/resources",
"//components/signin/core/browser",
"//components/sync",
"//google_apis",
@@ -89,7 +102,8 @@ static_library("ntp_snippets") {
"//components/image_fetcher",
"//components/metrics",
"//components/ntp_snippets/remote/proto",
- "//components/offline_pages",
+ "//components/offline_pages/core",
+ "//components/physical_web/data_source",
"//components/sessions",
"//components/strings",
"//components/sync_sessions",
@@ -115,18 +129,24 @@ source_set("unit_tests") {
testonly = true
sources = [
"bookmarks/bookmark_last_visit_utils_unittest.cc",
- "category_factory_unittest.cc",
+ "bookmarks/bookmark_suggestions_provider_unittest.cc",
+ "category_rankers/click_based_category_ranker_unittest.cc",
+ "category_rankers/constant_category_ranker_unittest.cc",
+ "category_unittest.cc",
+ "content_suggestions_metrics_unittest.cc",
"content_suggestions_service_unittest.cc",
"offline_pages/recent_tab_suggestions_provider_unittest.cc",
"physical_web_pages/physical_web_page_suggestions_provider_unittest.cc",
+ "remote/json_request_unittest.cc",
"remote/ntp_snippet_unittest.cc",
- "remote/ntp_snippets_fetcher_unittest.cc",
- "remote/ntp_snippets_status_service_unittest.cc",
- "remote/ntp_snippets_test_utils.cc",
- "remote/ntp_snippets_test_utils.h",
"remote/remote_suggestions_database_unittest.cc",
- "remote/remote_suggestions_provider_unittest.cc",
+ "remote/remote_suggestions_fetcher_unittest.cc",
+ "remote/remote_suggestions_provider_impl_unittest.cc",
+ "remote/remote_suggestions_status_service_unittest.cc",
"remote/request_throttler_unittest.cc",
+ "remote/scheduling_remote_suggestions_provider_unittest.cc",
+ "remote/test_utils.cc",
+ "remote/test_utils.h",
"sessions/foreign_sessions_suggestions_provider_unittest.cc",
"sessions/tab_delegate_sync_adapter_unittest.cc",
]
@@ -141,8 +161,9 @@ source_set("unit_tests") {
"//components/image_fetcher",
"//components/leveldb_proto:test_support",
"//components/ntp_snippets/remote/proto",
- "//components/offline_pages",
- "//components/offline_pages:test_support",
+ "//components/offline_pages/core",
+ "//components/offline_pages/core:test_support",
+ "//components/physical_web/data_source:test_support",
"//components/sessions",
"//components/sessions:test_support",
"//components/signin/core/browser:test_support",
@@ -150,7 +171,7 @@ source_set("unit_tests") {
"//components/strings",
"//components/sync:test_support_driver",
"//components/sync_sessions",
- "//components/variations",
+ "//components/variations:test_support",
"//net:test_support",
"//testing/gtest",
"//third_party/icu/",
@@ -161,6 +182,14 @@ source_set("unit_tests") {
source_set("test_support") {
testonly = true
sources = [
+ "category_rankers/fake_category_ranker.cc",
+ "category_rankers/fake_category_ranker.h",
+ "category_rankers/mock_category_ranker.cc",
+ "category_rankers/mock_category_ranker.h",
+ "fake_content_suggestions_provider_observer.cc",
+ "fake_content_suggestions_provider_observer.h",
+ "mock_content_suggestions_provider.cc",
+ "mock_content_suggestions_provider.h",
"mock_content_suggestions_provider_observer.cc",
"mock_content_suggestions_provider_observer.h",
"offline_pages/offline_pages_test_utils.cc",
@@ -170,8 +199,9 @@ source_set("test_support") {
deps = [
":ntp_snippets",
"//base",
- "//components/offline_pages",
- "//components/offline_pages:test_support",
+ "//components/offline_pages/core",
+ "//components/offline_pages/core:test_support",
"//testing/gmock",
+ "//testing/gtest",
]
}
diff --git a/chromium/components/ntp_snippets/OWNERS b/chromium/components/ntp_snippets/OWNERS
index 9e1ec3b70af..d4be5146e84 100644
--- a/chromium/components/ntp_snippets/OWNERS
+++ b/chromium/components/ntp_snippets/OWNERS
@@ -1,4 +1,11 @@
-noyau@chromium.org
+# Main owners:
bauerb@chromium.org
+jkrcal@chromium.org
treib@chromium.org
+
+# For ios:
+noyau@chromium.org
+
+# Fallback, in case all of the above are OOO:
markusheintz@chromium.org
+vitaliii@chromium.org
diff --git a/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.cc b/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.cc
index fda9da75b37..21ecd8112fc 100644
--- a/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.cc
+++ b/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.cc
@@ -27,7 +27,6 @@ namespace {
struct RecentBookmark {
const bookmarks::BookmarkNode* node;
base::Time last_visited;
- bool visited_recently;
};
const char* kBookmarksURLBlacklist[] = {"chrome://newtab/",
@@ -43,15 +42,17 @@ std::string FormatLastVisitDate(const base::Time& date) {
}
bool ExtractLastVisitDate(const BookmarkNode& node,
- const std::string& meta_info_key,
- base::Time* out) {
+ const std::string& meta_info_key,
+ base::Time* out) {
std::string last_visit_date_string;
- if (!node.GetMetaInfo(meta_info_key, &last_visit_date_string))
+ if (!node.GetMetaInfo(meta_info_key, &last_visit_date_string)) {
return false;
+ }
int64_t date = 0;
- if (!base::StringToInt64(last_visit_date_string, &date) || date < 0)
+ if (!base::StringToInt64(last_visit_date_string, &date) || date < 0) {
return false;
+ }
*out = base::Time::FromInternalValue(date);
return true;
@@ -59,23 +60,22 @@ bool ExtractLastVisitDate(const BookmarkNode& node,
bool IsBlacklisted(const GURL& url) {
for (const char* blacklisted : kBookmarksURLBlacklist) {
- if (url.spec() == blacklisted)
+ if (url.spec() == blacklisted) {
return true;
+ }
}
return false;
}
std::vector<const BookmarkNode*>::const_iterator FindMostRecentBookmark(
const std::vector<const BookmarkNode*>& bookmarks,
- bool creation_date_fallback,
bool consider_visits_from_desktop) {
auto most_recent = bookmarks.end();
base::Time most_recent_last_visited = base::Time::UnixEpoch();
for (auto iter = bookmarks.begin(); iter != bookmarks.end(); ++iter) {
base::Time last_visited;
- if (GetLastVisitDateForNTPBookmark(*iter, creation_date_fallback,
- consider_visits_from_desktop,
+ if (GetLastVisitDateForNTPBookmark(**iter, consider_visits_from_desktop,
&last_visited) &&
most_recent_last_visited <= last_visited) {
most_recent = iter;
@@ -92,14 +92,16 @@ void UpdateBookmarkOnURLVisitedInMainFrame(BookmarkModel* bookmark_model,
const GURL& url,
bool is_mobile_platform) {
// Skip URLs that are blacklisted.
- if (IsBlacklisted(url))
+ if (IsBlacklisted(url)) {
return;
+ }
// Skip URLs that are not bookmarked.
std::vector<const BookmarkNode*> bookmarks_for_url;
bookmark_model->GetNodesByURL(url, &bookmarks_for_url);
- if (bookmarks_for_url.empty())
+ if (bookmarks_for_url.empty()) {
return;
+ }
// If there are bookmarks for |url|, set their last visit date to now.
std::string now = FormatLastVisitDate(base::Time::Now());
@@ -114,21 +116,20 @@ void UpdateBookmarkOnURLVisitedInMainFrame(BookmarkModel* bookmark_model,
}
}
-bool GetLastVisitDateForNTPBookmark(const BookmarkNode* node,
- bool creation_date_fallback,
+bool GetLastVisitDateForNTPBookmark(const BookmarkNode& node,
bool consider_visits_from_desktop,
base::Time* out) {
- if (!node || IsDismissedFromNTPForBookmark(node)) {
+ if (IsDismissedFromNTPForBookmark(node)) {
return false;
}
bool got_mobile_date =
- ExtractLastVisitDate(*node, kBookmarkLastVisitDateOnMobileKey, out);
+ ExtractLastVisitDate(node, kBookmarkLastVisitDateOnMobileKey, out);
if (consider_visits_from_desktop) {
// Consider the later visit from these two platform groups.
base::Time last_visit_desktop;
- if (ExtractLastVisitDate(*node, kBookmarkLastVisitDateOnDesktopKey,
+ if (ExtractLastVisitDate(node, kBookmarkLastVisitDateOnDesktopKey,
&last_visit_desktop)) {
if (!got_mobile_date) {
*out = last_visit_desktop;
@@ -139,28 +140,21 @@ bool GetLastVisitDateForNTPBookmark(const BookmarkNode* node,
}
}
- if (!got_mobile_date && creation_date_fallback) {
- *out = node->date_added();
- return true;
- }
-
return got_mobile_date;
}
void MarkBookmarksDismissed(BookmarkModel* bookmark_model, const GURL& url) {
std::vector<const BookmarkNode*> nodes;
bookmark_model->GetNodesByURL(url, &nodes);
- for (const BookmarkNode* node : nodes)
+ for (const BookmarkNode* node : nodes) {
bookmark_model->SetNodeMetaInfo(node, kBookmarkDismissedFromNTP, "1");
+ }
}
-bool IsDismissedFromNTPForBookmark(const BookmarkNode* node) {
- if (!node)
- return false;
-
+bool IsDismissedFromNTPForBookmark(const BookmarkNode& node) {
std::string dismissed_from_ntp;
bool result =
- node->GetMetaInfo(kBookmarkDismissedFromNTP, &dismissed_from_ntp);
+ node.GetMetaInfo(kBookmarkDismissedFromNTP, &dismissed_from_ntp);
DCHECK(!result || dismissed_from_ntp == "1");
return result;
}
@@ -174,24 +168,22 @@ void MarkAllBookmarksUndismissed(BookmarkModel* bookmark_model) {
for (const BookmarkModel::URLAndTitle& bookmark : bookmarks) {
std::vector<const BookmarkNode*> nodes;
bookmark_model->GetNodesByURL(bookmark.url, &nodes);
- for (const BookmarkNode* node : nodes)
+ for (const BookmarkNode* node : nodes) {
bookmark_model->DeleteNodeMetaInfo(node, kBookmarkDismissedFromNTP);
+ }
}
}
std::vector<const BookmarkNode*> GetRecentlyVisitedBookmarks(
BookmarkModel* bookmark_model,
- int min_count,
int max_count,
const base::Time& min_visit_time,
- bool creation_date_fallback,
bool consider_visits_from_desktop) {
// Get all the bookmark URLs.
std::vector<BookmarkModel::URLAndTitle> bookmark_urls;
bookmark_model->GetBookmarks(&bookmark_urls);
std::vector<RecentBookmark> bookmarks;
- int recently_visited_count = 0;
// Find for each bookmark the most recently visited BookmarkNode and find out
// whether it is visited since |min_visit_time|.
for (const BookmarkModel::URLAndTitle& url_and_title : bookmark_urls) {
@@ -207,72 +199,39 @@ std::vector<const BookmarkNode*> GetRecentlyVisitedBookmarks(
// Find the most recently visited node for the given URL.
auto most_recent =
- FindMostRecentBookmark(bookmarks_for_url, creation_date_fallback,
- consider_visits_from_desktop);
+ FindMostRecentBookmark(bookmarks_for_url, consider_visits_from_desktop);
if (most_recent == bookmarks_for_url.end()) {
continue;
}
// Extract the last visit of the node to use later for sorting.
- base::Time last_visit;
- if (!GetLastVisitDateForNTPBookmark(*most_recent, creation_date_fallback,
- consider_visits_from_desktop,
- &last_visit)) {
+ base::Time last_visit_time;
+ if (!GetLastVisitDateForNTPBookmark(
+ **most_recent, consider_visits_from_desktop, &last_visit_time) ||
+ last_visit_time <= min_visit_time) {
continue;
}
- // Has it been _visited_ recently enough (not considering creation date)?
- base::Time last_real_visit;
- if (GetLastVisitDateForNTPBookmark(
- *most_recent, /*creation_date_fallback=*/false,
- consider_visits_from_desktop, &last_real_visit) &&
- min_visit_time < last_real_visit) {
- recently_visited_count++;
- bookmarks.push_back(
- {*most_recent, last_visit, /*visited_recently=*/true});
- } else {
- bookmarks.push_back(
- {*most_recent, last_visit, /*visited_recently=*/false});
- }
- }
-
- if (recently_visited_count < min_count) {
- // There aren't enough recently-visited bookmarks. Fill the list up to
- // |min_count| with older bookmarks (in particular those with only a
- // creation date, if creation_date_fallback is true).
- max_count = min_count;
- } else {
- // Remove the bookmarks that are not recently visited; we do not need them.
- // (We might end up with fewer than |min_count| bookmarks if all the recent
- // ones are dismissed.)
- bookmarks.erase(
- std::remove_if(bookmarks.begin(), bookmarks.end(),
- [](const RecentBookmark& bookmark) {
- return !bookmark.visited_recently;
- }),
- bookmarks.end());
+ bookmarks.push_back({*most_recent, last_visit_time});
}
- // Sort the remaining entries by date.
- std::sort(bookmarks.begin(), bookmarks.end(),
- [creation_date_fallback, consider_visits_from_desktop](
- const RecentBookmark& a, const RecentBookmark& b) {
- return a.last_visited > b.last_visited;
- });
+ // Sort the entries by date, getting the |max_count| most recent bookmarks
+ // to the front.
+ size_t count_to_sort =
+ std::min(bookmarks.size(), static_cast<size_t>(max_count));
+ std::partial_sort(bookmarks.begin(), bookmarks.begin() + count_to_sort,
+ bookmarks.end(),
+ [](const RecentBookmark& a, const RecentBookmark& b) {
+ return a.last_visited > b.last_visited;
+ });
// Insert the first |max_count| items from |bookmarks| into |result|.
std::vector<const BookmarkNode*> result;
for (const RecentBookmark& bookmark : bookmarks) {
- // TODO(jkrcal): The following break rule is in conflict with the comment
- // and code above that we give at least |min_count| bookmarks (irrespective
- // of creation_date_fallback). Discuss with treib@ and clear up.
- if (!creation_date_fallback && bookmark.last_visited < min_visit_time) {
- break;
- }
-
result.push_back(bookmark.node);
- if (result.size() >= static_cast<size_t>(max_count))
+ if (result.size() >= static_cast<size_t>(max_count)) {
break;
+ }
}
return result;
}
@@ -293,8 +252,9 @@ std::vector<const BookmarkNode*> GetDismissedBookmarksForDebugging(
DCHECK(!bookmarks_for_url.empty());
for (const BookmarkNode* node : bookmarks_for_url) {
- if (!IsDismissedFromNTPForBookmark(node))
+ if (!IsDismissedFromNTPForBookmark(*node)) {
return true;
+ }
}
return false;
}),
@@ -309,21 +269,44 @@ std::vector<const BookmarkNode*> GetDismissedBookmarksForDebugging(
return result;
}
-void RemoveAllLastVisitDates(bookmarks::BookmarkModel* bookmark_model) {
+namespace {
+
+void ClearLastVisitedMetadataIfBetween(bookmarks::BookmarkModel* model,
+ const BookmarkNode& node,
+ const base::Time& begin,
+ const base::Time& end,
+ const std::string& meta_key) {
+ base::Time last_visit_time;
+ if (ExtractLastVisitDate(node, meta_key, &last_visit_time) &&
+ begin <= last_visit_time && last_visit_time <= end) {
+ model->DeleteNodeMetaInfo(&node, meta_key);
+ }
+}
+
+} // namespace
+
+void RemoveLastVisitedDatesBetween(const base::Time& begin,
+ const base::Time& end,
+ base::Callback<bool(const GURL& url)> filter,
+ bookmarks::BookmarkModel* bookmark_model) {
// Get all the bookmark URLs.
std::vector<BookmarkModel::URLAndTitle> bookmark_urls;
bookmark_model->GetBookmarks(&bookmark_urls);
for (const BookmarkModel::URLAndTitle& url_and_title : bookmark_urls) {
+ if (!filter.Run(url_and_title.url)) {
+ continue;
+ }
// Get all bookmarks for the given URL.
std::vector<const BookmarkNode*> bookmarks_for_url;
bookmark_model->GetNodesByURL(url_and_title.url, &bookmarks_for_url);
for (const BookmarkNode* bookmark : bookmarks_for_url) {
- bookmark_model->DeleteNodeMetaInfo(bookmark,
- kBookmarkLastVisitDateOnMobileKey);
- bookmark_model->DeleteNodeMetaInfo(bookmark,
- kBookmarkLastVisitDateOnDesktopKey);
+ // The dismissal metadata is managed by the BookmarkSuggestionsProvider.
+ ClearLastVisitedMetadataIfBetween(bookmark_model, *bookmark, begin, end,
+ kBookmarkLastVisitDateOnMobileKey);
+ ClearLastVisitedMetadataIfBetween(bookmark_model, *bookmark, begin, end,
+ kBookmarkLastVisitDateOnDesktopKey);
}
}
}
diff --git a/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.h b/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.h
index 7b59832397f..128a0012734 100644
--- a/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.h
+++ b/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils.h
@@ -7,6 +7,8 @@
#include <vector>
+#include "base/callback.h"
+
class GURL;
namespace base {
@@ -34,12 +36,8 @@ void UpdateBookmarkOnURLVisitedInMainFrame(
// which also includes the case that the bookmark was dismissed from the NTP.
// As visits, we primarily understand visits on Android (the visit when the
// bookmark is created also counts). Visits on desktop platforms are considered
-// only if |consider_visits_from_desktop|. If no info about last visit date is
-// present and |creation_date_fallback| is true, the date when the bookmarks is
-// created is used as the alst visit date (most likely, it is the date when the
-// bookmark was synced to this device).
-bool GetLastVisitDateForNTPBookmark(const bookmarks::BookmarkNode* node,
- bool creation_date_fallback,
+// only if |consider_visits_from_desktop|.
+bool GetLastVisitDateForNTPBookmark(const bookmarks::BookmarkNode& node,
bool consider_visits_from_desktop,
base::Time* out);
@@ -48,7 +46,7 @@ void MarkBookmarksDismissed(bookmarks::BookmarkModel* bookmark_model,
const GURL& url);
// Gets the dismissed flag for a given bookmark |node|. Defaults to false.
-bool IsDismissedFromNTPForBookmark(const bookmarks::BookmarkNode* node);
+bool IsDismissedFromNTPForBookmark(const bookmarks::BookmarkNode& node);
// Removes the dismissed flag from all bookmarks (only for debugging).
void MarkAllBookmarksUndismissed(bookmarks::BookmarkModel* bookmark_model);
@@ -57,25 +55,27 @@ void MarkAllBookmarksUndismissed(bookmarks::BookmarkModel* bookmark_model);
// For each bookmarked URL, it returns the most recently created bookmark.
// The result is ordered by visit time (the most recent first). Only bookmarks
// visited after |min_visit_time| are considered, at most |max_count| bookmarks
-// are returned. If this results into less than |min_count| bookmarks, the list
-// is filled up with older bookmarks sorted by their last visit / creation date.
+// are returned.
// If |consider_visits_from_desktop|, also visits to bookmarks on synced desktop
// platforms are considered (and not only on this and other synced Android
// devices).
std::vector<const bookmarks::BookmarkNode*> GetRecentlyVisitedBookmarks(
bookmarks::BookmarkModel* bookmark_model,
- int min_count,
int max_count,
const base::Time& min_visit_time,
- bool creation_date_fallback,
bool consider_visits_from_desktop);
// Returns the list of all dismissed bookmarks. Only used for debugging.
std::vector<const bookmarks::BookmarkNode*> GetDismissedBookmarksForDebugging(
bookmarks::BookmarkModel* bookmark_model);
-// Removes last visited date metadata for all bookmarks.
-void RemoveAllLastVisitDates(bookmarks::BookmarkModel* bookmark_model);
+// Removes last-visited data (incl. any other metadata managed by content
+// suggestions) for bookmarks within the provided time range.
+// TODO(tschumann): Implement URL filtering.
+void RemoveLastVisitedDatesBetween(const base::Time& begin,
+ const base::Time& end,
+ base::Callback<bool(const GURL& url)> filter,
+ bookmarks::BookmarkModel* bookmark_model);
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils_unittest.cc b/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils_unittest.cc
index adc2120c565..6f0652717f9 100644
--- a/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils_unittest.cc
+++ b/chromium/components/ntp_snippets/bookmarks/bookmark_last_visit_utils_unittest.cc
@@ -4,8 +4,10 @@
#include "components/ntp_snippets/bookmarks/bookmark_last_visit_utils.h"
+#include <memory>
#include <string>
+#include "base/callback.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
@@ -19,6 +21,7 @@
using bookmarks::BookmarkModel;
using bookmarks::BookmarkNode;
+using testing::Eq;
using testing::IsEmpty;
using testing::SizeIs;
@@ -48,44 +51,35 @@ void AddBookmarks(BookmarkModel* model,
void AddBookmarksRecentOnMobile(BookmarkModel* model,
int num,
- const base::Time& threshold_time) {
- base::TimeDelta week = base::TimeDelta::FromDays(7);
- base::Time recent_time = threshold_time + week;
- std::string recent_time_string =
- base::Int64ToString(recent_time.ToInternalValue());
-
+ const base::Time& visit_time) {
AddBookmarks(model, num, kBookmarkLastVisitDateOnMobileKey,
- recent_time_string);
+ base::Int64ToString(visit_time.ToInternalValue()));
}
void AddBookmarksRecentOnDesktop(BookmarkModel* model,
int num,
- const base::Time& threshold_time) {
- base::TimeDelta week = base::TimeDelta::FromDays(7);
- base::Time recent_time = threshold_time + week;
- std::string recent_time_string =
- base::Int64ToString(recent_time.ToInternalValue());
-
+ const base::Time& visit_time) {
AddBookmarks(model, num, kBookmarkLastVisitDateOnDesktopKey,
- recent_time_string);
-}
-
-void AddBookmarksNonRecentOnMobile(BookmarkModel* model,
- int num,
- const base::Time& threshold_time) {
- base::TimeDelta week = base::TimeDelta::FromDays(7);
- base::Time nonrecent_time = threshold_time - week;
- std::string nonrecent_time_string =
- base::Int64ToString(nonrecent_time.ToInternalValue());
-
- AddBookmarks(model, num, kBookmarkLastVisitDateOnMobileKey,
- nonrecent_time_string);
+ base::Int64ToString(visit_time.ToInternalValue()));
}
void AddBookmarksNonVisited(BookmarkModel* model, int num) {
AddBookmarks(model, num, std::string(), std::string());
}
+const BookmarkNode* AddSingleBookmark(BookmarkModel* model,
+ const std::string& url,
+ const std::string& last_visit_key,
+ const base::Time& visit_time) {
+ base::string16 title =
+ base::ASCIIToUTF16(base::StringPrintf("title-%s", url.c_str()));
+ const BookmarkNode* node =
+ model->AddURL(model->bookmark_bar_node(), 0, title, GURL(url));
+ model->SetNodeMetaInfo(node, last_visit_key,
+ base::Int64ToString(visit_time.ToInternalValue()));
+ return node;
+}
+
} // namespace
class GetRecentlyVisitedBookmarksTest : public testing::Test {
@@ -97,73 +91,39 @@ class GetRecentlyVisitedBookmarksTest : public testing::Test {
const base::Time& threshold_time() const { return threshold_time_; }
+ base::Time GetRecentTime() const {
+ return threshold_time_ + base::TimeDelta::FromDays(7);
+ }
+
private:
base::Time threshold_time_;
DISALLOW_COPY_AND_ASSIGN(GetRecentlyVisitedBookmarksTest);
};
-TEST_F(GetRecentlyVisitedBookmarksTest,
- WithoutDateFallbackShouldNotReturnMissing) {
+TEST_F(GetRecentlyVisitedBookmarksTest, ShouldNotReturnMissing) {
const int number_of_bookmarks = 3;
std::unique_ptr<BookmarkModel> model =
bookmarks::TestBookmarkClient::CreateModel();
AddBookmarksNonVisited(model.get(), number_of_bookmarks);
std::vector<const bookmarks::BookmarkNode*> result =
- GetRecentlyVisitedBookmarks(model.get(), 0, number_of_bookmarks,
+ GetRecentlyVisitedBookmarks(model.get(), number_of_bookmarks,
threshold_time(),
- /*creation_date_fallback=*/false,
/*consider_visits_from_desktop=*/false);
EXPECT_THAT(result, IsEmpty());
}
-TEST_F(GetRecentlyVisitedBookmarksTest,
- WithDateFallbackShouldReturnMissingUpToMinCount) {
- const int number_of_bookmarks = 3;
- std::unique_ptr<BookmarkModel> model =
- bookmarks::TestBookmarkClient::CreateModel();
- AddBookmarksNonVisited(model.get(), number_of_bookmarks);
-
- const int min_count = number_of_bookmarks - 1;
- const int max_count = min_count + 10;
- std::vector<const bookmarks::BookmarkNode*> result =
- GetRecentlyVisitedBookmarks(model.get(), min_count, max_count,
- threshold_time(),
- /*creation_date_fallback=*/true,
- /*consider_visits_from_desktop=*/false);
- EXPECT_THAT(result, SizeIs(min_count));
-}
-
-TEST_F(GetRecentlyVisitedBookmarksTest,
- WithDateFallbackShouldReturnNonRecentUpToMinCount) {
- const int number_of_bookmarks = 3;
- std::unique_ptr<BookmarkModel> model =
- bookmarks::TestBookmarkClient::CreateModel();
- AddBookmarksNonRecentOnMobile(model.get(), number_of_bookmarks,
- threshold_time());
-
- const int min_count = number_of_bookmarks - 1;
- const int max_count = min_count + 10;
- std::vector<const bookmarks::BookmarkNode*> result =
- GetRecentlyVisitedBookmarks(model.get(), min_count, max_count,
- threshold_time(),
- /*creation_date_fallback=*/true,
- /*consider_visits_from_desktop=*/false);
- EXPECT_THAT(result, SizeIs(min_count));
-}
-
TEST_F(GetRecentlyVisitedBookmarksTest, ShouldNotConsiderDesktopVisits) {
const int number_of_bookmarks = 3;
std::unique_ptr<BookmarkModel> model =
bookmarks::TestBookmarkClient::CreateModel();
AddBookmarksRecentOnDesktop(model.get(), number_of_bookmarks,
- threshold_time());
+ GetRecentTime());
std::vector<const bookmarks::BookmarkNode*> result =
- GetRecentlyVisitedBookmarks(model.get(), 0, number_of_bookmarks,
+ GetRecentlyVisitedBookmarks(model.get(), number_of_bookmarks,
threshold_time(),
- /*creation_date_fallback=*/false,
/*consider_visits_from_desktop=*/false);
EXPECT_THAT(result, IsEmpty());
}
@@ -174,14 +134,13 @@ TEST_F(GetRecentlyVisitedBookmarksTest, ShouldConsiderDesktopVisits) {
std::unique_ptr<BookmarkModel> model =
bookmarks::TestBookmarkClient::CreateModel();
AddBookmarksRecentOnDesktop(model.get(), number_of_recent_desktop,
- threshold_time());
+ GetRecentTime());
AddBookmarksNonVisited(model.get(),
number_of_bookmarks - number_of_recent_desktop);
std::vector<const bookmarks::BookmarkNode*> result =
- GetRecentlyVisitedBookmarks(model.get(), 0, number_of_bookmarks,
+ GetRecentlyVisitedBookmarks(model.get(), number_of_bookmarks,
threshold_time(),
- /*creation_date_fallback=*/false,
/*consider_visits_from_desktop=*/true);
EXPECT_THAT(result, SizeIs(number_of_recent_desktop));
}
@@ -190,16 +149,151 @@ TEST_F(GetRecentlyVisitedBookmarksTest, ShouldReturnNotMoreThanMaxCount) {
const int number_of_bookmarks = 3;
std::unique_ptr<BookmarkModel> model =
bookmarks::TestBookmarkClient::CreateModel();
- AddBookmarksRecentOnMobile(model.get(), number_of_bookmarks,
- threshold_time());
+ AddBookmarksRecentOnMobile(model.get(), number_of_bookmarks, GetRecentTime());
const int max_count = number_of_bookmarks - 1;
std::vector<const bookmarks::BookmarkNode*> result =
- GetRecentlyVisitedBookmarks(model.get(), max_count, max_count,
- threshold_time(),
- /*creation_date_fallback=*/false,
+ GetRecentlyVisitedBookmarks(model.get(), max_count, threshold_time(),
/*consider_visits_from_desktop=*/false);
EXPECT_THAT(result, SizeIs(max_count));
}
+namespace {
+
+base::Callback<bool(const GURL& url)> DeleteAllFilter() {
+ return base::Bind([] (const GURL& url) { return true; });
+}
+
+base::Callback<bool(const GURL& url)> DeleteOneURLFilter(
+ const GURL& to_delete) {
+ return base::Bind(
+ [](const GURL& to_delete, const GURL& url) { return url == to_delete; },
+ to_delete);
+}
+
+} // namespace
+
+TEST(RemoveLastVisitedDatesBetween, ShouldRemoveTimestampsWithinTimeRange) {
+ const base::Time delete_begin =
+ base::Time::Now() - base::TimeDelta::FromDays(2);
+ const base::Time delete_end = base::Time::Max();
+
+ std::unique_ptr<BookmarkModel> model =
+ bookmarks::TestBookmarkClient::CreateModel();
+ AddSingleBookmark(model.get(), "http://url-1.com",
+ kBookmarkLastVisitDateOnMobileKey,
+ delete_begin + base::TimeDelta::FromSeconds(1));
+ AddSingleBookmark(model.get(), "http://url-1.com",
+ kBookmarkLastVisitDateOnDesktopKey,
+ delete_begin + base::TimeDelta::FromSeconds(1));
+ ASSERT_THAT(
+ GetRecentlyVisitedBookmarks(model.get(), 20, base::Time(),
+ /*consider_visits_from_desktop=*/true),
+ SizeIs(1));
+
+ RemoveLastVisitedDatesBetween(delete_begin, delete_end, DeleteAllFilter(),
+ model.get());
+
+ EXPECT_THAT(
+ GetRecentlyVisitedBookmarks(model.get(), 20, base::Time(),
+ /*consider_visits_from_desktop=*/true),
+ IsEmpty());
+ // Verify that the bookmark model nodes themselve still exist.
+ std::vector<const BookmarkNode*> remaining_nodes;
+ model->GetNodesByURL(GURL("http://url-1.com"), &remaining_nodes);
+ EXPECT_THAT(remaining_nodes, SizeIs(2));
+}
+
+TEST(RemoveLastVisitedDatesBetween,
+ ShouldHandleMetadataFromOtherDeviceTypesSeparately) {
+ const base::Time delete_begin =
+ base::Time::Now() - base::TimeDelta::FromDays(2);
+ const base::Time delete_end = base::Time::Max();
+
+ std::unique_ptr<BookmarkModel> model =
+ bookmarks::TestBookmarkClient::CreateModel();
+ // Create a bookmark with last visited times from both, mobile and desktop.
+ // The mobile one is within the deletion interval, the desktop one outside.
+ // Only the mobile one should get deleted.
+ const BookmarkNode* node = AddSingleBookmark(
+ model.get(), "http://url-1.com", kBookmarkLastVisitDateOnMobileKey,
+ delete_begin + base::TimeDelta::FromSeconds(1));
+ model->SetNodeMetaInfo(
+ node, kBookmarkLastVisitDateOnDesktopKey,
+ base::Int64ToString(
+ (delete_begin - base::TimeDelta::FromSeconds(1)).ToInternalValue()));
+ ASSERT_THAT(
+ GetRecentlyVisitedBookmarks(model.get(), 20, base::Time(),
+ /*consider_visits_from_desktop=*/true),
+ SizeIs(1));
+
+ RemoveLastVisitedDatesBetween(delete_begin, delete_end, DeleteAllFilter(),
+ model.get());
+
+ EXPECT_THAT(
+ GetRecentlyVisitedBookmarks(model.get(), 20, base::Time(),
+ /*consider_visits_from_desktop=*/false),
+ IsEmpty());
+ EXPECT_THAT(
+ GetRecentlyVisitedBookmarks(model.get(), 20, base::Time(),
+ /*consider_visits_from_desktop=*/true),
+ SizeIs(1));
+}
+
+TEST(RemoveLastVisitedDatesBetween, ShouldNotRemoveTimestampsOutsideTimeRange) {
+ const base::Time delete_begin =
+ base::Time::Now() - base::TimeDelta::FromDays(2);
+ const base::Time delete_end = delete_begin + base::TimeDelta::FromDays(5);
+
+ std::unique_ptr<BookmarkModel> model =
+ bookmarks::TestBookmarkClient::CreateModel();
+ AddSingleBookmark(model.get(), "http://url-1.com",
+ kBookmarkLastVisitDateOnMobileKey,
+ delete_begin - base::TimeDelta::FromSeconds(1));
+ AddSingleBookmark(model.get(), "http://url-2.com",
+ kBookmarkLastVisitDateOnDesktopKey,
+ delete_end + base::TimeDelta::FromSeconds(1));
+ ASSERT_THAT(
+ GetRecentlyVisitedBookmarks(model.get(), 20, base::Time(),
+ /*consider_visits_from_desktop=*/true),
+ SizeIs(2));
+
+ RemoveLastVisitedDatesBetween(delete_begin, delete_end, DeleteAllFilter(),
+ model.get());
+
+ EXPECT_THAT(
+ GetRecentlyVisitedBookmarks(model.get(), 20, base::Time(),
+ /*consider_visits_from_desktop=*/true),
+ SizeIs(2));
+}
+
+TEST(RemoveLastVisitedDatesBetween, ShouldOnlyRemoveURLsWithinFilter) {
+ const base::Time delete_begin =
+ base::Time::Now() - base::TimeDelta::FromDays(2);
+ const base::Time delete_end = base::Time::Max();
+
+ std::unique_ptr<BookmarkModel> model =
+ bookmarks::TestBookmarkClient::CreateModel();
+ AddSingleBookmark(model.get(), "http://url-1.com",
+ kBookmarkLastVisitDateOnMobileKey,
+ delete_begin + base::TimeDelta::FromSeconds(1));
+ AddSingleBookmark(model.get(), "http://url-2.com",
+ kBookmarkLastVisitDateOnMobileKey,
+ delete_begin + base::TimeDelta::FromSeconds(1));
+ ASSERT_THAT(
+ GetRecentlyVisitedBookmarks(model.get(), 20, base::Time(),
+ /*consider_visits_from_desktop=*/false),
+ SizeIs(2));
+
+ RemoveLastVisitedDatesBetween(delete_begin, delete_end,
+ DeleteOneURLFilter(GURL("http://url-2.com")),
+ model.get());
+
+ std::vector<const bookmarks::BookmarkNode*> remaining_nodes =
+ GetRecentlyVisitedBookmarks(model.get(), 20, base::Time(),
+ /*consider_visits_from_desktop=*/false);
+ EXPECT_THAT(remaining_nodes, SizeIs(1));
+ EXPECT_THAT(remaining_nodes[0]->url(), Eq(GURL("http://url-1.com")));
+}
+
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc
index c507e7c33f7..0b452a0e165 100644
--- a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc
+++ b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.cc
@@ -15,7 +15,7 @@
#include "base/time/time.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/ntp_snippets/bookmarks/bookmark_last_visit_utils.h"
-#include "components/ntp_snippets/category_factory.h"
+#include "components/ntp_snippets/category.h"
#include "components/ntp_snippets/content_suggestion.h"
#include "components/ntp_snippets/features.h"
#include "components/ntp_snippets/pref_names.h"
@@ -34,87 +34,55 @@ namespace ntp_snippets {
namespace {
const int kMaxBookmarks = 10;
-const int kMinBookmarks = 3;
const int kMaxBookmarkAgeInDays = 42;
-const int kUseCreationDateFallbackForDays = 0;
const char* kMaxBookmarksParamName = "bookmarks_max_count";
-const char* kMinBookmarksParamName = "bookmarks_min_count";
const char* kMaxBookmarkAgeInDaysParamName = "bookmarks_max_age_in_days";
-const char* kUseCreationDateFallbackForDaysParamName =
- "bookmarks_creation_date_fallback_days";
const char* kConsiderDesktopVisitsParamName =
"bookmarks_consider_desktop_visits";
+// TODO(treib,jkrcal): Remove this after M57.
+const char kDeprecatedBookmarksFirstM54StartPref[] =
+ "ntp_suggestions.bookmarks.first_M54_start";
+
// Any bookmark created or visited after this time will be considered recent.
// Note that bookmarks can be shown that do not meet this threshold.
base::Time GetThresholdTime() {
return base::Time::Now() -
- base::TimeDelta::FromDays(GetParamAsInt(
+ base::TimeDelta::FromDays(variations::GetVariationParamByFeatureAsInt(
ntp_snippets::kBookmarkSuggestionsFeature,
kMaxBookmarkAgeInDaysParamName, kMaxBookmarkAgeInDays));
}
-// The number of days used as a threshold where if this is larger than the time
-// since M54 started, then the creation timestamp of a bookmark be used as a
-// fallback if no last visited timestamp is present.
-int UseCreationDateFallbackForDays() {
- return GetParamAsInt(ntp_snippets::kBookmarkSuggestionsFeature,
- kUseCreationDateFallbackForDaysParamName,
- kUseCreationDateFallbackForDays);
-}
-
// The maximum number of suggestions ever provided.
int GetMaxCount() {
- return GetParamAsInt(ntp_snippets::kBookmarkSuggestionsFeature,
- kMaxBookmarksParamName, kMaxBookmarks);
-}
-
-// The minimum number of suggestions to try to provide. Depending on other
-// parameters this may or not be respected. Currently creation date fallback
-// must be active in order for older bookmarks to be incorporated to meet this
-// min.
-int GetMinCount() {
- return GetParamAsInt(ntp_snippets::kBookmarkSuggestionsFeature,
- kMinBookmarksParamName, kMinBookmarks);
+ return variations::GetVariationParamByFeatureAsInt(
+ ntp_snippets::kBookmarkSuggestionsFeature, kMaxBookmarksParamName,
+ kMaxBookmarks);
}
bool AreDesktopVisitsConsidered() {
- return GetParamAsBool(ntp_snippets::kBookmarkSuggestionsFeature,
- kConsiderDesktopVisitsParamName, false);
+ return variations::GetVariationParamByFeatureAsBool(
+ ntp_snippets::kBookmarkSuggestionsFeature,
+ kConsiderDesktopVisitsParamName, false);
}
} // namespace
BookmarkSuggestionsProvider::BookmarkSuggestionsProvider(
ContentSuggestionsProvider::Observer* observer,
- CategoryFactory* category_factory,
bookmarks::BookmarkModel* bookmark_model,
PrefService* pref_service)
- : ContentSuggestionsProvider(observer, category_factory),
+ : ContentSuggestionsProvider(observer),
category_status_(CategoryStatus::AVAILABLE_LOADING),
provided_category_(
- category_factory->FromKnownCategory(KnownCategories::BOOKMARKS)),
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS)),
bookmark_model_(bookmark_model),
fetch_requested_(false),
end_of_list_last_visit_date_(GetThresholdTime()),
consider_bookmark_visits_from_desktop_(AreDesktopVisitsConsidered()) {
observer->OnCategoryStatusChanged(this, provided_category_, category_status_);
- base::Time first_m54_start;
- base::Time now = base::Time::Now();
- if (pref_service->HasPrefPath(prefs::kBookmarksFirstM54Start)) {
- first_m54_start = base::Time::FromInternalValue(
- pref_service->GetInt64(prefs::kBookmarksFirstM54Start));
- } else {
- first_m54_start = now;
- pref_service->SetInt64(prefs::kBookmarksFirstM54Start,
- first_m54_start.ToInternalValue());
- }
- base::TimeDelta time_since_first_m54_start = now - first_m54_start;
- // Note: Setting the fallback timeout to zero effectively turns off the
- // fallback entirely.
- creation_date_fallback_ =
- time_since_first_m54_start.InDays() < UseCreationDateFallbackForDays();
+ pref_service->ClearPref(kDeprecatedBookmarksFirstM54StartPref);
bookmark_model_->AddObserver(this);
FetchBookmarks();
}
@@ -126,7 +94,7 @@ BookmarkSuggestionsProvider::~BookmarkSuggestionsProvider() {
// static
void BookmarkSuggestionsProvider::RegisterProfilePrefs(
PrefRegistrySimple* registry) {
- registry->RegisterInt64Pref(prefs::kBookmarksFirstM54Start, 0);
+ registry->RegisterInt64Pref(kDeprecatedBookmarksFirstM54StartPref, 0);
}
////////////////////////////////////////////////////////////////////////////////
@@ -181,17 +149,15 @@ void BookmarkSuggestionsProvider::ClearHistory(
base::Time begin,
base::Time end,
const base::Callback<bool(const GURL& url)>& filter) {
- // The last visit dates are not "owned" by the bookmark suggestion provider so
- // it is cleared directly from browsing_data_remover.cc.
+ // To avoid race conditions with the history-removal of the last-visited
+ // timestamps we also trigger a deletion here. The problem is that we need to
+ // update the bookmarks data here and otherwise (depending on the order in
+ // which the code runs) could pick up to-be-deleted data again.
+ if (bookmark_model_->loaded()) {
+ RemoveLastVisitedDatesBetween(begin, end, filter, bookmark_model_);
+ }
ClearDismissedSuggestionsForDebugging(provided_category_);
FetchBookmarks();
- // Temporarily enter an "explicitly disabled" state, so that any open UIs
- // will clear the suggestions too.
- if (category_status_ != CategoryStatus::CATEGORY_EXPLICITLY_DISABLED) {
- CategoryStatus old_category_status = category_status_;
- NotifyStatusChanged(CategoryStatus::CATEGORY_EXPLICITLY_DISABLED);
- NotifyStatusChanged(old_category_status);
- }
}
void BookmarkSuggestionsProvider::ClearCachedSuggestions(Category category) {
@@ -208,7 +174,7 @@ void BookmarkSuggestionsProvider::GetDismissedSuggestionsForDebugging(
std::vector<ContentSuggestion> suggestions;
for (const BookmarkNode* bookmark : bookmarks) {
- ConvertBookmark(bookmark, &suggestions);
+ ConvertBookmark(*bookmark, &suggestions);
}
callback.Run(std::move(suggestions));
}
@@ -216,8 +182,9 @@ void BookmarkSuggestionsProvider::GetDismissedSuggestionsForDebugging(
void BookmarkSuggestionsProvider::ClearDismissedSuggestionsForDebugging(
Category category) {
DCHECK_EQ(category, provided_category_);
- if (!bookmark_model_->loaded())
+ if (!bookmark_model_->loaded()) {
return;
+ }
MarkAllBookmarksUndismissed(bookmark_model_);
}
@@ -235,7 +202,7 @@ void BookmarkSuggestionsProvider::OnWillChangeBookmarkMetaInfo(
BookmarkModel* model,
const BookmarkNode* node) {
// Store the last visit date of the node that is about to change.
- if (!GetLastVisitDateForNTPBookmark(node, creation_date_fallback_,
+ if (!GetLastVisitDateForNTPBookmark(*node,
consider_bookmark_visits_from_desktop_,
&node_to_change_last_visit_date_)) {
node_to_change_last_visit_date_ = base::Time::UnixEpoch();
@@ -246,9 +213,8 @@ void BookmarkSuggestionsProvider::BookmarkMetaInfoChanged(
BookmarkModel* model,
const BookmarkNode* node) {
base::Time time;
- if (!GetLastVisitDateForNTPBookmark(node, creation_date_fallback_,
- consider_bookmark_visits_from_desktop_,
- &time)) {
+ if (!GetLastVisitDateForNTPBookmark(
+ *node, consider_bookmark_visits_from_desktop_, &time)) {
// Error in loading the last visit date after the change. This happens when
// the bookmark just got dismissed. We must not update the suggestion in
// such a case.
@@ -272,9 +238,8 @@ void BookmarkSuggestionsProvider::BookmarkNodeRemoved(
const bookmarks::BookmarkNode* node,
const std::set<GURL>& no_longer_bookmarked) {
base::Time time;
- if (GetLastVisitDateForNTPBookmark(node, creation_date_fallback_,
- consider_bookmark_visits_from_desktop_,
- &time) &&
+ if (GetLastVisitDateForNTPBookmark(
+ *node, consider_bookmark_visits_from_desktop_, &time) &&
time < end_of_list_last_visit_date_) {
// We know the node is too old to influence the list.
return;
@@ -289,9 +254,9 @@ void BookmarkSuggestionsProvider::BookmarkNodeAdded(
const bookmarks::BookmarkNode* parent,
int index) {
base::Time time;
- if (!GetLastVisitDateForNTPBookmark(
- parent->GetChild(index), creation_date_fallback_,
- consider_bookmark_visits_from_desktop_, &time) ||
+ if (!GetLastVisitDateForNTPBookmark(*parent->GetChild(index),
+ consider_bookmark_visits_from_desktop_,
+ &time) ||
time < end_of_list_last_visit_date_) {
// The new node has no last visited info or is too old to get into the list.
return;
@@ -302,21 +267,20 @@ void BookmarkSuggestionsProvider::BookmarkNodeAdded(
}
void BookmarkSuggestionsProvider::ConvertBookmark(
- const BookmarkNode* bookmark,
+ const BookmarkNode& bookmark,
std::vector<ContentSuggestion>* suggestions) {
base::Time publish_date;
- if (!GetLastVisitDateForNTPBookmark(bookmark, creation_date_fallback_,
- consider_bookmark_visits_from_desktop_,
- &publish_date)) {
+ if (!GetLastVisitDateForNTPBookmark(
+ bookmark, consider_bookmark_visits_from_desktop_, &publish_date)) {
return;
}
- ContentSuggestion suggestion(provided_category_, bookmark->url().spec(),
- bookmark->url());
- suggestion.set_title(bookmark->GetTitle());
+ ContentSuggestion suggestion(provided_category_, bookmark.url().spec(),
+ bookmark.url());
+ suggestion.set_title(bookmark.GetTitle());
suggestion.set_snippet_text(base::string16());
suggestion.set_publish_date(publish_date);
- suggestion.set_publisher_name(base::UTF8ToUTF16(bookmark->url().host()));
+ suggestion.set_publisher_name(base::UTF8ToUTF16(bookmark.url().host()));
suggestions->emplace_back(std::move(suggestion));
}
@@ -328,12 +292,12 @@ void BookmarkSuggestionsProvider::FetchBookmarksInternal() {
base::Time threshold_time = GetThresholdTime();
std::vector<const BookmarkNode*> bookmarks = GetRecentlyVisitedBookmarks(
- bookmark_model_, GetMinCount(), GetMaxCount(), threshold_time,
- creation_date_fallback_, consider_bookmark_visits_from_desktop_);
+ bookmark_model_, GetMaxCount(), threshold_time,
+ consider_bookmark_visits_from_desktop_);
std::vector<ContentSuggestion> suggestions;
for (const BookmarkNode* bookmark : bookmarks) {
- ConvertBookmark(bookmark, &suggestions);
+ ConvertBookmark(*bookmark, &suggestions);
}
if (suggestions.empty()) {
@@ -341,22 +305,23 @@ void BookmarkSuggestionsProvider::FetchBookmarksInternal() {
} else {
end_of_list_last_visit_date_ = suggestions.back().publish_date();
}
-
observer()->OnNewSuggestions(this, provided_category_,
std::move(suggestions));
}
void BookmarkSuggestionsProvider::FetchBookmarks() {
- if (bookmark_model_->loaded())
+ if (bookmark_model_->loaded()) {
FetchBookmarksInternal();
- else
+ } else {
fetch_requested_ = true;
+ }
}
void BookmarkSuggestionsProvider::NotifyStatusChanged(
CategoryStatus new_status) {
- if (category_status_ == new_status)
+ if (category_status_ == new_status) {
return;
+ }
category_status_ = new_status;
observer()->OnCategoryStatusChanged(this, provided_category_, new_status);
}
diff --git a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h
index c1301cb386d..19d30bbe2aa 100644
--- a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h
+++ b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h
@@ -18,10 +18,6 @@
class PrefRegistrySimple;
class PrefService;
-namespace gfx {
-class Image;
-} // namespace gfx
-
namespace ntp_snippets {
// Provides content suggestions from the bookmarks model.
@@ -29,7 +25,6 @@ class BookmarkSuggestionsProvider : public ContentSuggestionsProvider,
public bookmarks::BookmarkModelObserver {
public:
BookmarkSuggestionsProvider(ContentSuggestionsProvider::Observer* observer,
- CategoryFactory* category_factory,
bookmarks::BookmarkModel* bookmark_model,
PrefService* pref_service);
~BookmarkSuggestionsProvider() override;
@@ -91,7 +86,7 @@ class BookmarkSuggestionsProvider : public ContentSuggestionsProvider,
bookmarks::BookmarkModel* model,
const std::set<GURL>& removed_urls) override {}
- void ConvertBookmark(const bookmarks::BookmarkNode* bookmark,
+ void ConvertBookmark(const bookmarks::BookmarkNode& bookmark,
std::vector<ContentSuggestion>* suggestions);
// The actual method to fetch bookmarks - follows each call to FetchBookmarks
@@ -114,11 +109,6 @@ class BookmarkSuggestionsProvider : public ContentSuggestionsProvider,
base::Time node_to_change_last_visit_date_;
base::Time end_of_list_last_visit_date_;
- // TODO(jkrcal): Remove this field and the pref after M55.
- // For six weeks after first installing M54, this is true and the
- // fallback implemented in BookmarkLastVisitUtils is activated.
- bool creation_date_fallback_;
-
// By default, only visits to bookmarks on Android are considered when
// deciding which bookmarks to suggest. Should we also consider visits on
// desktop platforms?
diff --git a/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider_unittest.cc b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider_unittest.cc
new file mode 100644
index 00000000000..4c0cda9054a
--- /dev/null
+++ b/chromium/components/ntp_snippets/bookmarks/bookmark_suggestions_provider_unittest.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/ntp_snippets/bookmarks/bookmark_suggestions_provider.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/bookmark_node.h"
+#include "components/bookmarks/test/test_bookmark_client.h"
+#include "components/ntp_snippets/bookmarks/bookmark_last_visit_utils.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/mock_content_suggestions_provider_observer.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace ntp_snippets {
+
+namespace {
+
+using ::testing::StrictMock;
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::Property;
+using ::testing::UnorderedElementsAre;
+
+class BookmarkSuggestionsProviderTest : public ::testing::Test {
+ public:
+ BookmarkSuggestionsProviderTest()
+ : model_(bookmarks::TestBookmarkClient::CreateModel()) {
+ EXPECT_CALL(observer_, OnNewSuggestions(_, Category::FromKnownCategory(
+ KnownCategories::BOOKMARKS),
+ IsEmpty()))
+ .RetiresOnSaturation();
+ EXPECT_CALL(observer_,
+ OnCategoryStatusChanged(
+ _, Category::FromKnownCategory(KnownCategories::BOOKMARKS),
+ CategoryStatus::AVAILABLE_LOADING))
+ .RetiresOnSaturation();
+ EXPECT_CALL(observer_,
+ OnCategoryStatusChanged(
+ _, Category::FromKnownCategory(KnownCategories::BOOKMARKS),
+ CategoryStatus::AVAILABLE))
+ .RetiresOnSaturation();
+ BookmarkSuggestionsProvider::RegisterProfilePrefs(test_prefs_.registry());
+ provider_ = base::MakeUnique<BookmarkSuggestionsProvider>(
+ &observer_, model_.get(), &test_prefs_);
+ }
+
+ protected:
+ std::unique_ptr<bookmarks::BookmarkModel> model_;
+ StrictMock<MockContentSuggestionsProviderObserver> observer_;
+ TestingPrefServiceSimple test_prefs_;
+ std::unique_ptr<BookmarkSuggestionsProvider> provider_;
+};
+
+TEST_F(BookmarkSuggestionsProviderTest,
+ ShouldProvideBookmarkSuggestions) {
+ GURL url("http://my-new-bookmarked.url");
+ // Note, this update to the model does not trigger OnNewSuggestions() on the
+ // observer as the provider realizes no new nodes were added.
+ // don't have new data.
+ model_->AddURL(model_->bookmark_bar_node(), 0,
+ base::ASCIIToUTF16("cool page's title"), url);
+
+ // Once we provided the last-visited meta information, an update with the
+ // suggestion containing the bookmark should follow.
+ EXPECT_CALL(
+ observer_,
+ OnNewSuggestions(
+ _, Category::FromKnownCategory(KnownCategories::BOOKMARKS),
+ UnorderedElementsAre(Property(&ContentSuggestion::url, GURL(url)))));
+ UpdateBookmarkOnURLVisitedInMainFrame(model_.get(), url,
+ /*is_mobile_platform=*/true);
+}
+
+TEST_F(BookmarkSuggestionsProviderTest,
+ ShouldEnsureToBeClearedBookmarksDontAppearAfterClear) {
+ // Set up the provider with 2 entries: one dismissed and one active.
+
+ // Add one bookmark (the one to be not dismissed) -- this will trigger a
+ // notification.
+ GURL active_bookmark("http://my-active-bookmarked.url");
+ EXPECT_CALL(observer_,
+ OnNewSuggestions(
+ _, Category::FromKnownCategory(KnownCategories::BOOKMARKS),
+ UnorderedElementsAre(Property(&ContentSuggestion::url,
+ GURL(active_bookmark)))));
+ model_->AddURL(model_->bookmark_bar_node(), 0,
+ base::ASCIIToUTF16("cool page's title"), active_bookmark);
+ UpdateBookmarkOnURLVisitedInMainFrame(model_.get(), active_bookmark,
+ /*is_mobile_platform=*/true);
+
+ // Add the other bookmark -- this will trigger another notification. Then
+ // marks it was dismissed.
+ GURL dismissed_bookmark("http://my-dismissed-bookmark.url");
+ EXPECT_CALL(
+ observer_,
+ OnNewSuggestions(
+ _, Category::FromKnownCategory(KnownCategories::BOOKMARKS),
+ UnorderedElementsAre(
+ Property(&ContentSuggestion::url, GURL(active_bookmark)),
+ Property(&ContentSuggestion::url, GURL(dismissed_bookmark)))));
+ const bookmarks::BookmarkNode* dismissed_node = model_->AddURL(
+ model_->bookmark_bar_node(), 1, base::ASCIIToUTF16("cool page's title"),
+ dismissed_bookmark);
+ UpdateBookmarkOnURLVisitedInMainFrame(model_.get(), dismissed_bookmark,
+ /*is_mobile_platform=*/true);
+ // According to the ContentSugestionsProvider contract, solely dismissing an
+ // item should not result in another OnNewSuggestions() call.
+ static_cast<ContentSuggestionsProvider*>(provider_.get())
+ ->DismissSuggestion(ContentSuggestion::ID(
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS),
+ dismissed_bookmark.spec()));
+ EXPECT_THAT(IsDismissedFromNTPForBookmark(*dismissed_node), Eq(true));
+
+ // Clear history and make sure the suggestions actually get removed.
+ EXPECT_CALL(observer_, OnNewSuggestions(_, Category::FromKnownCategory(
+ KnownCategories::BOOKMARKS),
+ IsEmpty()));
+ static_cast<ContentSuggestionsProvider*>(provider_.get())
+ ->ClearHistory(base::Time(), base::Time::Max(),
+ base::Bind([] (const GURL& url) { return true; }));
+
+ // Verify the dismissed marker is gone.
+ EXPECT_THAT(IsDismissedFromNTPForBookmark(*dismissed_node), Eq(false));
+}
+
+// TODO(tschumann): There are plenty of test cases missing. Most importantly:
+// -- Remove a bookmark from the model
+// -- verifying handling of threshold time
+// -- dealing with fetches before the model is loaded.
+
+} // namespace
+} // namespace ntp_snippets
+
diff --git a/chromium/components/ntp_snippets/category.cc b/chromium/components/ntp_snippets/category.cc
index abc86ab9f2c..7274a0105c4 100644
--- a/chromium/components/ntp_snippets/category.cc
+++ b/chromium/components/ntp_snippets/category.cc
@@ -8,6 +8,31 @@
namespace ntp_snippets {
+// static
+Category Category::FromKnownCategory(KnownCategories known_category) {
+ return FromIDValue(static_cast<int>(known_category));
+}
+
+// static
+Category Category::FromRemoteCategory(int remote_category) {
+ DCHECK_GT(remote_category, 0);
+ return Category(static_cast<int>(KnownCategories::REMOTE_CATEGORIES_OFFSET) +
+ remote_category);
+}
+
+// static
+Category Category::FromIDValue(int id) {
+ DCHECK(IsValidIDValue(id)) << "Not a valid ID: " << id;
+ return Category(id);
+}
+
+// static
+bool Category::IsValidIDValue(int id) {
+ return (id >= 0) &&
+ ((id < static_cast<int>(KnownCategories::LOCAL_CATEGORIES_COUNT) ||
+ id > static_cast<int>(KnownCategories::REMOTE_CATEGORIES_OFFSET)));
+}
+
Category::Category(int id) : id_(id) {}
bool Category::IsKnownCategory(KnownCategories known_category) const {
diff --git a/chromium/components/ntp_snippets/category.h b/chromium/components/ntp_snippets/category.h
index 0ae86c8b16f..6e36a3aa47e 100644
--- a/chromium/components/ntp_snippets/category.h
+++ b/chromium/components/ntp_snippets/category.h
@@ -9,8 +9,6 @@
namespace ntp_snippets {
-class CategoryFactory;
-
// These are the categories that the client knows about.
// The values before LOCAL_CATEGORIES_COUNT are the categories that are provided
// locally on the device. Categories provided by the server (IDs strictly larger
@@ -59,6 +57,25 @@ class Category {
// a std::map, but should not be used to order categories for other purposes.
struct CompareByID;
+ // Creates a category from a KnownCategory value. The passed |known_category|
+ // must not be one of the special values (LOCAL_CATEGORIES_COUNT or
+ // REMOTE_CATEGORIES_OFFSET).
+ static Category FromKnownCategory(KnownCategories known_category);
+
+ // Creates a category from a category identifier delivered by the server.
+ // |remote_category| must be positive.
+ static Category FromRemoteCategory(int remote_category);
+
+ // Creates a category from an ID as returned by |id()|. |id| must be a
+ // non-negative value. Callers should make sure this is a valid id (if in
+ // doubt, call IsValidIDValue()).
+ static Category FromIDValue(int id);
+
+ // Verifies if |id| is a valid ID value. Only checks that the value is within
+ // a valid range -- not that the system actually knows about the corresponding
+ // category.
+ static bool IsValidIDValue(int id);
+
// Returns a non-negative identifier that is unique for the category and can
// be converted back to a Category instance using
// |CategoryFactory::FromIDValue(id)|.
@@ -68,8 +85,6 @@ class Category {
bool IsKnownCategory(KnownCategories known_category) const;
private:
- friend class CategoryFactory;
-
explicit Category(int id);
int id_;
diff --git a/chromium/components/ntp_snippets/category_factory.cc b/chromium/components/ntp_snippets/category_factory.cc
deleted file mode 100644
index a8e0740b619..00000000000
--- a/chromium/components/ntp_snippets/category_factory.cc
+++ /dev/null
@@ -1,89 +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/category_factory.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-
-namespace ntp_snippets {
-
-CategoryFactory::CategoryFactory() {
- // Add all local categories in a fixed order.
- AddKnownCategory(KnownCategories::DOWNLOADS);
- AddKnownCategory(KnownCategories::RECENT_TABS);
- AddKnownCategory(KnownCategories::FOREIGN_TABS);
- AddKnownCategory(KnownCategories::BOOKMARKS);
- AddKnownCategory(KnownCategories::PHYSICAL_WEB_PAGES);
-
- DCHECK_EQ(static_cast<size_t>(KnownCategories::LOCAL_CATEGORIES_COUNT),
- ordered_categories_.size());
-
- // Known remote categories come after. Other remote categories will be ordered
- // after these depending on when providers notify us about a related change.
- AddKnownCategory(KnownCategories::ARTICLES);
-}
-
-CategoryFactory::~CategoryFactory() = default;
-
-Category CategoryFactory::FromKnownCategory(KnownCategories known_category) {
- if (known_category < KnownCategories::LOCAL_CATEGORIES_COUNT) {
- // Local categories should have been added already.
- DCHECK(CategoryExists(static_cast<int>(known_category)));
- } else {
- DCHECK_GT(known_category, KnownCategories::REMOTE_CATEGORIES_OFFSET);
- }
- return InternalFromID(static_cast<int>(known_category));
-}
-
-Category CategoryFactory::FromRemoteCategory(int remote_category) {
- DCHECK_GT(remote_category, 0);
- return InternalFromID(
- static_cast<int>(KnownCategories::REMOTE_CATEGORIES_OFFSET) +
- remote_category);
-}
-
-Category CategoryFactory::FromIDValue(int id) {
- DCHECK_GE(id, 0);
- DCHECK(id < static_cast<int>(KnownCategories::LOCAL_CATEGORIES_COUNT) ||
- id > static_cast<int>(KnownCategories::REMOTE_CATEGORIES_OFFSET));
- return InternalFromID(id);
-}
-
-bool CategoryFactory::CompareCategories(const Category& left,
- const Category& right) const {
- if (left == right)
- return false;
- return std::find(ordered_categories_.begin(), ordered_categories_.end(),
- left) < std::find(ordered_categories_.begin(),
- ordered_categories_.end(), right);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Private methods
-
-bool CategoryFactory::CategoryExists(int id) {
- return std::find(ordered_categories_.begin(), ordered_categories_.end(),
- Category(id)) != ordered_categories_.end();
-}
-
-void CategoryFactory::AddKnownCategory(KnownCategories known_category) {
- InternalFromID(static_cast<int>(known_category));
-}
-
-Category CategoryFactory::InternalFromID(int id) {
- auto it = std::find(ordered_categories_.begin(), ordered_categories_.end(),
- Category(id));
- if (it != ordered_categories_.end())
- return *it;
-
- Category category(id);
- ordered_categories_.push_back(category);
- return category;
-}
-
-} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/category_factory.h b/chromium/components/ntp_snippets/category_factory.h
deleted file mode 100644
index c3941eaea2f..00000000000
--- a/chromium/components/ntp_snippets/category_factory.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_SNIPPETS_CATEGORY_FACTORY_H_
-#define COMPONENTS_NTP_SNIPPETS_CATEGORY_FACTORY_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "components/ntp_snippets/category.h"
-
-namespace ntp_snippets {
-
-// Creates and orders Category instances.
-class CategoryFactory {
- public:
- CategoryFactory();
- ~CategoryFactory();
-
- // Creates a category from a KnownCategory value. The passed |known_category|
- // must not be one of the special values (LOCAL_CATEGORIES_COUNT or
- // REMOTE_CATEGORIES_OFFSET).
- Category FromKnownCategory(KnownCategories known_category);
-
- // Creates a category from a category identifier delivered by the server.
- // |remote_category| must be positive.
- // Note that remote categories are ordered in the order in which they were
- // first created by calling this method.
- Category FromRemoteCategory(int remote_category);
-
- // Creates a category from an ID as returned by |Category::id()|.
- // |id| must be a non-negative value.
- Category FromIDValue(int id);
-
- // Compares the given categories according to a strict ordering, returning
- // true if and only if |left| is strictly less than |right|.
- // This method satisfies the "Compare" contract required by sort algorithms.
- // The order is determined as follows: All local categories go first, in a
- // specific order hard-coded in the |CategoryFactory| constructor. All remote
- // categories follow in the order in which they were first created through
- // |FromRemoteCategory|.
- bool CompareCategories(const Category& left, const Category& right) const;
-
- private:
- bool CategoryExists(int id);
- void AddKnownCategory(KnownCategories known_category);
- Category InternalFromID(int id);
-
- // Stores all known categories in the order which is also returned by
- // |CompareCategories|.
- std::vector<Category> ordered_categories_;
-
- DISALLOW_COPY_AND_ASSIGN(CategoryFactory);
-};
-
-} // namespace ntp_snippets
-
-#endif // COMPONENTS_NTP_SNIPPETS_CATEGORY_FACTORY_H_
diff --git a/chromium/components/ntp_snippets/category_factory_unittest.cc b/chromium/components/ntp_snippets/category_factory_unittest.cc
deleted file mode 100644
index 3e592d1bdcc..00000000000
--- a/chromium/components/ntp_snippets/category_factory_unittest.cc
+++ /dev/null
@@ -1,129 +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/category_factory.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "components/ntp_snippets/category.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ntp_snippets {
-
-class CategoryFactoryTest : public testing::Test {
- public:
- CategoryFactoryTest()
- : unused_remote_category_id_(
- static_cast<int>(KnownCategories::LAST_KNOWN_REMOTE_CATEGORY) + 1) {
- }
-
- int GetUnusedRemoteCategoryID() { return unused_remote_category_id_++; }
-
- bool CompareCategories(const Category& left, const Category& right) {
- return factory()->CompareCategories(left, right);
- }
-
- void AddDummyRemoteCategories(int quantity) {
- for (int i = 0; i < quantity; ++i) {
- factory()->FromRemoteCategory(GetUnusedRemoteCategoryID());
- }
- }
-
- CategoryFactory* factory() { return &factory_; }
-
- private:
- CategoryFactory factory_;
- int unused_remote_category_id_;
-
- DISALLOW_COPY_AND_ASSIGN(CategoryFactoryTest);
-};
-
-TEST_F(CategoryFactoryTest,
- FromKnownCategoryShouldReturnSameIdForSameCategories) {
- const KnownCategories known_category = KnownCategories::BOOKMARKS;
- Category first = factory()->FromKnownCategory(known_category);
- Category second = factory()->FromKnownCategory(known_category);
- EXPECT_EQ(first, second);
-}
-
-TEST_F(CategoryFactoryTest,
- FromRemoteCategoryShouldReturnSameIdForSameCategories) {
- const int remote_category_id = GetUnusedRemoteCategoryID();
- Category first = factory()->FromRemoteCategory(remote_category_id);
- Category second = factory()->FromRemoteCategory(remote_category_id);
- EXPECT_EQ(first, second);
-}
-
-TEST_F(CategoryFactoryTest, FromRemoteCategoryOrder) {
- const int small_id = GetUnusedRemoteCategoryID();
- const int large_id = GetUnusedRemoteCategoryID();
- // Categories are added in decreasing id order to test that they are not
- // compared by id.
- Category added_first = factory()->FromRemoteCategory(large_id);
- Category added_second = factory()->FromRemoteCategory(small_id);
- EXPECT_TRUE(CompareCategories(added_first, added_second));
- EXPECT_FALSE(CompareCategories(added_second, added_first));
-}
-
-TEST_F(CategoryFactoryTest, FromIDValueReturnsSameKnownCategory) {
- Category known_category =
- factory()->FromKnownCategory(KnownCategories::BOOKMARKS);
- Category known_category_by_id = factory()->FromIDValue(known_category.id());
- EXPECT_EQ(known_category, known_category_by_id);
-}
-
-TEST_F(CategoryFactoryTest, FromIDValueReturnsSameRemoteCategory) {
- const int remote_category_id = GetUnusedRemoteCategoryID();
- Category remote_category = factory()->FromRemoteCategory(remote_category_id);
- Category remote_category_by_id = factory()->FromIDValue(remote_category.id());
- EXPECT_EQ(remote_category, remote_category_by_id);
-}
-
-TEST_F(CategoryFactoryTest, CompareCategoriesLocalBeforeRemote) {
- const int remote_category_id = GetUnusedRemoteCategoryID();
- Category remote_category = factory()->FromRemoteCategory(remote_category_id);
- Category local_category =
- factory()->FromKnownCategory(KnownCategories::BOOKMARKS);
- EXPECT_TRUE(CompareCategories(local_category, remote_category));
- EXPECT_FALSE(CompareCategories(remote_category, local_category));
-}
-
-TEST_F(CategoryFactoryTest, CompareCategoriesSame) {
- const int remote_category_id = GetUnusedRemoteCategoryID();
- Category remote_category = factory()->FromRemoteCategory(remote_category_id);
- EXPECT_FALSE(CompareCategories(remote_category, remote_category));
-
- Category local_category =
- factory()->FromKnownCategory(KnownCategories::BOOKMARKS);
- EXPECT_FALSE(CompareCategories(local_category, local_category));
-}
-
-TEST_F(CategoryFactoryTest, CompareCategoriesAfterAddingNew) {
- AddDummyRemoteCategories(3);
-
- Category consequtive_first =
- factory()->FromRemoteCategory(GetUnusedRemoteCategoryID());
- Category consequtive_second =
- factory()->FromRemoteCategory(GetUnusedRemoteCategoryID());
-
- AddDummyRemoteCategories(3);
-
- Category nonconsequtive_first =
- factory()->FromRemoteCategory(GetUnusedRemoteCategoryID());
- AddDummyRemoteCategories(3);
- Category nonconsequtive_second =
- factory()->FromRemoteCategory(GetUnusedRemoteCategoryID());
-
- AddDummyRemoteCategories(3);
-
- EXPECT_TRUE(CompareCategories(consequtive_first, consequtive_second));
- EXPECT_FALSE(CompareCategories(consequtive_second, consequtive_first));
-
- EXPECT_TRUE(CompareCategories(nonconsequtive_first, nonconsequtive_second));
- EXPECT_FALSE(CompareCategories(nonconsequtive_second, nonconsequtive_first));
-}
-
-} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/category_rankers/category_ranker.h b/chromium/components/ntp_snippets/category_rankers/category_ranker.h
new file mode 100644
index 00000000000..c403625e616
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/category_ranker.h
@@ -0,0 +1,46 @@
+// 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_CATEGORY_RANKERS_CATEGORY_RANKER_H_
+#define COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CATEGORY_RANKER_H_
+
+#include "base/time/time.h"
+#include "components/ntp_snippets/category.h"
+
+namespace ntp_snippets {
+
+// TODO(vitaliii): Ensure that changes in the order are propagated to the UI.
+// (crbug.com/673743)
+
+// Orders categories.
+// The order may be dynamic and change at any time.
+class CategoryRanker {
+ public:
+ virtual ~CategoryRanker() = default;
+
+ // Compares two categories. True means that |left| precedes |right|, i.e. it
+ // must be located earlier (above) on the NTP. This method must satisfy
+ // "Compare" contract required by sort algorithms.
+ virtual bool Compare(Category left, Category right) const = 0;
+
+ // Deletes all history related data between |begin| and |end|. After this
+ // call, the category order may not depend on this history range anymore.
+ virtual void ClearHistory(base::Time begin, base::Time end) = 0;
+
+ // If |category| has not been added previously, it is added after all already
+ // known categories, otherwise nothing is changed.
+ virtual void AppendCategoryIfNecessary(Category category) = 0;
+
+ // Feedback data from the user to update the ranking.
+
+ // Called whenever a suggestion is opened by the user.
+ virtual void OnSuggestionOpened(Category category) = 0;
+
+ // Called whenever a category is dismissed by the user.
+ virtual void OnCategoryDismissed(Category category) = 0;
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CATEGORY_RANKER_H_
diff --git a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
new file mode 100644
index 00000000000..81b8ec0028f
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.cc
@@ -0,0 +1,482 @@
+// 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/category_rankers/click_based_category_ranker.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "components/ntp_snippets/category_rankers/constant_category_ranker.h"
+#include "components/ntp_snippets/content_suggestions_metrics.h"
+#include "components/ntp_snippets/features.h"
+#include "components/ntp_snippets/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace ntp_snippets {
+
+namespace {
+
+// In order to increase stability and predictability of the order, an extra
+// level of "confidence" is required before moving a category upwards. In other
+// words, the category is moved not when it reaches the previous one, but rather
+// when it leads by some amount. We refer to this required extra "confidence" as
+// a passing margin. Each position has its own passing margin. The category is
+// moved upwards (i.e. passes another category) when it has at least passing
+// margin of the previous category position more clicks.
+const int kPassingMargin = 5;
+
+// The first categories get more attention and, therefore, here more stability
+// is needed. The passing margin of such categories is increased and they are
+// referred to as top categories (with extra margin). Only category position
+// defines whether a category is top, but not its content.
+const int kNumTopCategoriesWithExtraMargin = 3;
+
+// The increase of passing margin for each top category compared to the next
+// category (e.g. the first top category has passing margin larger by this value
+// than the second top category, the last top category has it larger by this
+// value than the first non-top category).
+const int kExtraPassingMargin = 2;
+
+// The ranker must "forget" history with time, so that changes in the user
+// behavior are reflected by the order in reasonable time. This is done using
+// click count decay with time. However, if there is not enough data, there is
+// no need in "forgetting" it. This value defines how many total clicks (across
+// categories) are considered enough to decay.
+const int kMinNumClicksToDecay = 30;
+
+// Time between two consecutive decays (assuming enough clicks).
+const base::TimeDelta kTimeBetweenDecays = base::TimeDelta::FromDays(1);
+
+// Decay factor as a fraction. The current value approximates the seventh root
+// of 0.5. This yields a 50% decay per seven decays. Seven weak decays are used
+// instead of one 50% decay in order to decrease difference of click weight in
+// time.
+const int kDecayFactorNumerator = 91;
+const int kDecayFactorDenominator = 100; // pow(0.91, 7) = 0.517
+
+// Number of positions by which a dismissed category is downgraded.
+const int kDefaultDismissedCategoryPenalty = 1;
+const char* kDismissedCategoryPenaltyParamName =
+ "click_based_category_ranker-dismissed_category_penalty";
+
+const char kCategoryIdKey[] = "category";
+const char kClicksKey[] = "clicks";
+const char kLastDismissedKey[] = "last_dismissed";
+const char kContentSuggestionsPromotedCategory[] =
+ "click_based_category_ranker-promoted_category";
+
+int GetDismissedCategoryPenaltyVariationValue() {
+ return variations::GetVariationParamByFeatureAsInt(
+ kCategoryRanker, kDismissedCategoryPenaltyParamName,
+ kDefaultDismissedCategoryPenalty);
+}
+
+base::Optional<Category> GetPromotedCategoryFromVariations() {
+ int category_id = variations::GetVariationParamByFeatureAsInt(
+ kCategoryRanker, kContentSuggestionsPromotedCategory, -1);
+ if (category_id < 0) {
+ return base::nullopt;
+ }
+ if (!Category::IsValidIDValue(category_id)) {
+ LOG(WARNING) << "Received invalid category ID for promotion: "
+ << category_id << ". Ignoring promotion.";
+ return base::nullopt;
+ }
+ return Category::FromIDValue(category_id);
+}
+
+} // namespace
+
+ClickBasedCategoryRanker::ClickBasedCategoryRanker(
+ PrefService* pref_service,
+ std::unique_ptr<base::Clock> clock)
+ : pref_service_(pref_service), clock_(std::move(clock)) {
+ if (!ReadOrderFromPrefs(&ordered_categories_)) {
+ // TODO(crbug.com/676273): Handle adding new hardcoded KnownCategories to
+ // existing order from prefs. Currently such new category is completely
+ // ignored and may be never shown.
+ RestoreDefaultOrder();
+ }
+
+ if (ReadLastDecayTimeFromPrefs() == base::Time::FromInternalValue(0)) {
+ StoreLastDecayTimeToPrefs(clock_->Now());
+ }
+ promoted_category_ = DeterminePromotedCategory();
+}
+
+// |ordered_categories_| needs to be properly initialized before calling
+// this method.
+base::Optional<Category> ClickBasedCategoryRanker::DeterminePromotedCategory() {
+ base::Optional<Category> promoted = GetPromotedCategoryFromVariations();
+ if (!promoted.has_value()) {
+ return base::nullopt;
+ }
+ auto promoted_it = FindCategory(promoted.value());
+ if (promoted_it != ordered_categories_.end() &&
+ promoted_it->last_dismissed >
+ clock_->Now() - base::TimeDelta::FromDays(14)) {
+ // Only promote categories to the top if they weren't dismissed within the
+ // last 2 weeks.
+ return base::nullopt;
+ }
+ return promoted;
+}
+
+ClickBasedCategoryRanker::~ClickBasedCategoryRanker() = default;
+
+bool ClickBasedCategoryRanker::Compare(Category left, Category right) const {
+ if (!ContainsCategory(left)) {
+ LOG(DFATAL) << "The category with ID " << left.id()
+ << " has not been added using AppendCategoryIfNecessary.";
+ }
+ if (!ContainsCategory(right)) {
+ LOG(DFATAL) << "The category with ID " << right.id()
+ << " has not been added using AppendCategoryIfNecessary.";
+ }
+ if (left == right) {
+ return false;
+ }
+ if (promoted_category_.has_value() && left == *promoted_category_) {
+ return true;
+ }
+ if (promoted_category_.has_value() && right == *promoted_category_) {
+ return false;
+ }
+ for (const RankedCategory& ranked_category : ordered_categories_) {
+ if (ranked_category.category == left) {
+ return true;
+ }
+ if (ranked_category.category == right) {
+ return false;
+ }
+ }
+ // This fallback is provided only to satisfy "Compare" contract if by mistake
+ // categories are not added using AppendCategoryIfNecessary. One should not
+ // rely on this, instead the order must be defined explicitly using
+ // AppendCategoryIfNecessary.
+ return left.id() < right.id();
+}
+
+void ClickBasedCategoryRanker::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;
+ }
+
+ StoreLastDecayTimeToPrefs(base::Time::FromInternalValue(0));
+
+ // The categories added through |AppendCategoryIfNecessary| cannot be
+ // completely removed, since no one is required to reregister them. Instead
+ // they are preserved in the default order (sorted by id).
+ std::vector<RankedCategory> old_categories = ordered_categories_;
+ RestoreDefaultOrder();
+
+ std::vector<Category> added_categories;
+ for (const RankedCategory& old_category : old_categories) {
+ auto it =
+ std::find_if(ordered_categories_.begin(), ordered_categories_.end(),
+ [old_category](const RankedCategory& other) {
+ return other.category == old_category.category;
+ });
+ if (it == ordered_categories_.end()) {
+ added_categories.push_back(old_category.category);
+ }
+ }
+
+ // Sort added categories by id to make their order history independent.
+ std::sort(added_categories.begin(), added_categories.end(),
+ Category::CompareByID());
+
+ for (Category added_category : added_categories) {
+ ordered_categories_.push_back(RankedCategory(
+ added_category, /*clicks=*/0, /*last_dismissed=*/base::Time()));
+ }
+
+ StoreOrderToPrefs(ordered_categories_);
+}
+
+void ClickBasedCategoryRanker::AppendCategoryIfNecessary(Category category) {
+ if (!ContainsCategory(category)) {
+ ordered_categories_.push_back(RankedCategory(
+ category, /*clicks=*/0, /*last_dismissed=*/base::Time()));
+ }
+}
+
+void ClickBasedCategoryRanker::OnSuggestionOpened(Category category) {
+ if (!ContainsCategory(category)) {
+ LOG(DFATAL) << "The category with ID " << category.id()
+ << " has not been added using AppendCategoryIfNecessary.";
+ return;
+ }
+
+ DecayClicksIfNeeded();
+
+ std::vector<RankedCategory>::iterator current = FindCategory(category);
+ DCHECK_GE(current->clicks, 0);
+ // The overflow is ignored. It is unlikely to happen, because of click count
+ // decay.
+ current->clicks++;
+
+ // Move the category up if appropriate.
+ if (current != ordered_categories_.begin()) {
+ std::vector<RankedCategory>::iterator previous = current - 1;
+ const int passing_margin = GetPositionPassingMargin(previous);
+ if (current->clicks >= previous->clicks + passing_margin) {
+ const int new_index = previous - ordered_categories_.begin();
+ ntp_snippets::metrics::OnCategoryMovedUp(new_index);
+ // It is intended to move only by one position per click in order to avoid
+ // dramatic changes, which could confuse the user.
+ std::swap(*current, *previous);
+ }
+ }
+
+ StoreOrderToPrefs(ordered_categories_);
+}
+
+void ClickBasedCategoryRanker::OnCategoryDismissed(Category category) {
+ if (!ContainsCategory(category)) {
+ LOG(DFATAL) << "The category with ID " << category.id()
+ << " has not been added using AppendCategoryIfNecessary.";
+ return;
+ }
+
+ const int penalty = GetDismissedCategoryPenaltyVariationValue();
+ if (penalty != 0) { // Dismissed category penalty is turned on?
+ std::vector<RankedCategory>::iterator current = FindCategory(category);
+ for (int downgrade = 0; downgrade < penalty; ++downgrade) {
+ std::vector<RankedCategory>::iterator next = current + 1;
+ if (next == ordered_categories_.end()) {
+ break;
+ }
+ std::swap(*current, *next);
+ current = next;
+ }
+
+ DCHECK(current != ordered_categories_.begin());
+ std::vector<RankedCategory>::iterator previous = current - 1;
+ int new_clicks = std::max(previous->clicks - kPassingMargin, 0);
+ // The previous category may have more clicks (but not enough to pass the
+ // margin, this is possible when penalty >= 2), therefore, we ensure that
+ // for this category we don't increase clicks.
+ current->clicks = std::min(current->clicks, new_clicks);
+ }
+ FindCategory(category)->last_dismissed = clock_->Now();
+ if (promoted_category_.has_value() && category == *promoted_category_) {
+ promoted_category_.reset();
+ }
+ StoreOrderToPrefs(ordered_categories_);
+}
+
+base::Time ClickBasedCategoryRanker::GetLastDecayTime() const {
+ return ReadLastDecayTimeFromPrefs();
+}
+
+// static
+void ClickBasedCategoryRanker::RegisterProfilePrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterListPref(prefs::kClickBasedCategoryRankerOrderWithClicks);
+ registry->RegisterInt64Pref(prefs::kClickBasedCategoryRankerLastDecayTime,
+ /*default_value=*/0);
+}
+
+// static
+int ClickBasedCategoryRanker::GetPassingMargin() {
+ return kPassingMargin;
+}
+
+// static
+int ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin() {
+ return kNumTopCategoriesWithExtraMargin;
+}
+
+// static
+int ClickBasedCategoryRanker::GetDismissedCategoryPenalty() {
+ return GetDismissedCategoryPenaltyVariationValue();
+}
+
+ClickBasedCategoryRanker::RankedCategory::RankedCategory(
+ Category category,
+ int clicks,
+ const base::Time& last_dismissed)
+ : category(category), clicks(clicks), last_dismissed(last_dismissed) {}
+
+// Returns passing margin for a given position taking into account whether it is
+// a top category.
+int ClickBasedCategoryRanker::GetPositionPassingMargin(
+ std::vector<RankedCategory>::const_iterator category_position) const {
+ int index = category_position - ordered_categories_.cbegin();
+ int passing_margin_increase = 0;
+ if (index < kNumTopCategoriesWithExtraMargin) {
+ passing_margin_increase =
+ kExtraPassingMargin * (kNumTopCategoriesWithExtraMargin - index);
+ }
+ return kPassingMargin + passing_margin_increase;
+}
+
+void ClickBasedCategoryRanker::RestoreDefaultOrder() {
+ ordered_categories_.clear();
+
+ std::vector<KnownCategories> ordered_known_categories =
+ ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+
+ for (KnownCategories known_category : ordered_known_categories) {
+ AppendKnownCategory(known_category);
+ }
+
+ StoreOrderToPrefs(ordered_categories_);
+}
+
+void ClickBasedCategoryRanker::AppendKnownCategory(
+ KnownCategories known_category) {
+ Category category = Category::FromKnownCategory(known_category);
+ DCHECK(!ContainsCategory(category));
+ ordered_categories_.push_back(RankedCategory(
+ category, /*clicks=*/0, /*last_dismissed=*/base::Time()));
+}
+
+namespace {
+
+base::Time ParseLastDismissedDate(const base::DictionaryValue& value) {
+ // We don't expect the last-dismissed value to be present in all cases (we
+ // added this after the fact).
+ std::string serialized_value;
+ int64_t parsed_value;
+ if (value.GetString(kLastDismissedKey, &serialized_value) &&
+ base::StringToInt64(serialized_value, &parsed_value)) {
+ return base::Time::FromInternalValue(parsed_value);
+ }
+ return base::Time();
+}
+
+} // namespace
+
+bool ClickBasedCategoryRanker::ReadOrderFromPrefs(
+ std::vector<RankedCategory>* result_categories) const {
+ result_categories->clear();
+ const base::ListValue* list =
+ pref_service_->GetList(prefs::kClickBasedCategoryRankerOrderWithClicks);
+ if (!list || list->GetSize() == 0) {
+ return false;
+ }
+
+ for (const std::unique_ptr<base::Value>& value : *list) {
+ base::DictionaryValue* dictionary;
+ if (!value->GetAsDictionary(&dictionary)) {
+ LOG(DFATAL) << "Failed to parse category data from prefs param "
+ << prefs::kClickBasedCategoryRankerOrderWithClicks
+ << " into dictionary.";
+ return false;
+ }
+ int category_id, clicks;
+ if (!dictionary->GetInteger(kCategoryIdKey, &category_id)) {
+ LOG(DFATAL) << "Dictionary does not have '" << kCategoryIdKey << "' key.";
+ return false;
+ }
+ if (!dictionary->GetInteger(kClicksKey, &clicks)) {
+ LOG(DFATAL) << "Dictionary does not have '" << kClicksKey << "' key.";
+ return false;
+ }
+ base::Time last_dismissed = ParseLastDismissedDate(*dictionary);
+ Category category = Category::FromIDValue(category_id);
+ result_categories->push_back(
+ RankedCategory(category, clicks, last_dismissed));
+ }
+ return true;
+}
+
+void ClickBasedCategoryRanker::StoreOrderToPrefs(
+ const std::vector<RankedCategory>& ordered_categories) {
+ base::ListValue list;
+ for (const RankedCategory& category : ordered_categories) {
+ auto dictionary = base::MakeUnique<base::DictionaryValue>();
+ dictionary->SetInteger(kCategoryIdKey, category.category.id());
+ dictionary->SetInteger(kClicksKey, category.clicks);
+ dictionary->SetString(
+ kLastDismissedKey,
+ base::Int64ToString(category.last_dismissed.ToInternalValue()));
+ list.Append(std::move(dictionary));
+ }
+ pref_service_->Set(prefs::kClickBasedCategoryRankerOrderWithClicks, list);
+}
+
+std::vector<ClickBasedCategoryRanker::RankedCategory>::iterator
+ClickBasedCategoryRanker::FindCategory(Category category) {
+ return std::find_if(ordered_categories_.begin(), ordered_categories_.end(),
+ [category](const RankedCategory& ranked_category) {
+ return category == ranked_category.category;
+ });
+}
+
+bool ClickBasedCategoryRanker::ContainsCategory(Category category) const {
+ for (const auto& ranked_category : ordered_categories_) {
+ if (category == ranked_category.category) {
+ return true;
+ }
+ }
+ return false;
+}
+
+base::Time ClickBasedCategoryRanker::ReadLastDecayTimeFromPrefs() const {
+ return base::Time::FromInternalValue(
+ pref_service_->GetInt64(prefs::kClickBasedCategoryRankerLastDecayTime));
+}
+
+void ClickBasedCategoryRanker::StoreLastDecayTimeToPrefs(
+ base::Time last_decay_time) {
+ pref_service_->SetInt64(prefs::kClickBasedCategoryRankerLastDecayTime,
+ last_decay_time.ToInternalValue());
+}
+
+bool ClickBasedCategoryRanker::IsEnoughClicksToDecay() const {
+ int64_t num_clicks = 0;
+ for (const RankedCategory& ranked_category : ordered_categories_) {
+ num_clicks += ranked_category.clicks;
+ }
+ return num_clicks >= kMinNumClicksToDecay;
+}
+
+bool ClickBasedCategoryRanker::DecayClicksIfNeeded() {
+ base::Time now = clock_->Now();
+ base::Time last_decay = ReadLastDecayTimeFromPrefs();
+ if (last_decay == base::Time::FromInternalValue(0)) {
+ // No last decay time, start from now.
+ StoreLastDecayTimeToPrefs(clock_->Now());
+ return false;
+ }
+ DCHECK_LE(last_decay, now);
+
+ int num_pending_decays = (now - last_decay) / kTimeBetweenDecays;
+ int executed_decays = 0;
+ while (executed_decays < num_pending_decays && IsEnoughClicksToDecay()) {
+ for (RankedCategory& ranked_category : ordered_categories_) {
+ DCHECK_GE(ranked_category.clicks, 0);
+ const int64_t old_clicks = static_cast<int64_t>(ranked_category.clicks);
+ ranked_category.clicks =
+ old_clicks * kDecayFactorNumerator / kDecayFactorDenominator;
+ }
+
+ ++executed_decays;
+ }
+
+ // No matter how many decays were actually executed, all of them are marked
+ // done. Even if some were ignored due to absense of clicks, they would have
+ // no effect anyway for the same reason.
+ StoreLastDecayTimeToPrefs(last_decay +
+ num_pending_decays * kTimeBetweenDecays);
+
+ if (executed_decays > 0) {
+ StoreOrderToPrefs(ordered_categories_);
+ return true;
+ }
+ return false;
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.h b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.h
new file mode 100644
index 00000000000..0042ffe36d4
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker.h
@@ -0,0 +1,95 @@
+// 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_CATEGORY_RANKERS_CLICK_BASED_CATEGORY_RANKER_H_
+#define COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CLICK_BASED_CATEGORY_RANKER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/optional.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_rankers/category_ranker.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace ntp_snippets {
+
+// An implementation of a CategoryRanker based on a number of clicks per
+// category. Initial order is hardcoded, but sections with more clicks are moved
+// to the top. The new remote categories must be registered using
+// AppendCategoryIfNecessary. All other categories must be hardcoded in the
+// initial order. The order and category usage data are persisted in prefs and
+// reloaded on startup. TODO(crbug.com/675929): Remove unused categories from
+// prefs.
+class ClickBasedCategoryRanker : public CategoryRanker {
+ public:
+ explicit ClickBasedCategoryRanker(PrefService* pref_service,
+ std::unique_ptr<base::Clock> clock);
+ ~ClickBasedCategoryRanker() override;
+
+ // CategoryRanker implementation.
+ bool Compare(Category left, Category right) const override;
+ void ClearHistory(base::Time begin, base::Time end) override;
+ void AppendCategoryIfNecessary(Category category) override;
+ void OnSuggestionOpened(Category category) override;
+ void OnCategoryDismissed(Category category) override;
+
+ // Returns time when last decay occured. For testing only.
+ base::Time GetLastDecayTime() const;
+
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ // Returns passing margin, i.e. a number of extra clicks required to move a
+ // category upwards. For testing only.
+ static int GetPassingMargin();
+
+ // Returns number of top categories with extra margin (i.e. with increased
+ // passing margin). For testing only.
+ static int GetNumTopCategoriesWithExtraMargin();
+
+ // Returns number of positions by which a dismissed category is downgraded.
+ // For testing only.
+ static int GetDismissedCategoryPenalty();
+
+ private:
+ struct RankedCategory {
+ Category category;
+ int clicks;
+ base::Time last_dismissed;
+
+ RankedCategory(Category category,
+ int clicks,
+ const base::Time& last_dismissed);
+ };
+
+ base::Optional<Category> DeterminePromotedCategory();
+ int GetPositionPassingMargin(
+ std::vector<RankedCategory>::const_iterator category_position) const;
+ void RestoreDefaultOrder();
+ void AppendKnownCategory(KnownCategories known_category);
+ bool ReadOrderFromPrefs(std::vector<RankedCategory>* result_categories) const;
+ void StoreOrderToPrefs(const std::vector<RankedCategory>& ordered_categories);
+ std::vector<RankedCategory>::iterator FindCategory(Category category);
+ bool ContainsCategory(Category category) const;
+
+ base::Time ReadLastDecayTimeFromPrefs() const;
+ void StoreLastDecayTimeToPrefs(base::Time last_decay_time);
+ bool IsEnoughClicksToDecay() const;
+ bool DecayClicksIfNeeded();
+
+ std::vector<RankedCategory> ordered_categories_;
+ PrefService* pref_service_;
+ std::unique_ptr<base::Clock> clock_;
+ base::Optional<Category> promoted_category_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRanker);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_CLICK_BASED_CATEGORY_RANKER_H_
diff --git a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
new file mode 100644
index 00000000000..172789bfa10
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
@@ -0,0 +1,749 @@
+// 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/category_rankers/click_based_category_ranker.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
+#include "base/time/default_clock.h"
+#include "base/time/time.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_rankers/constant_category_ranker.h"
+#include "components/ntp_snippets/features.h"
+#include "components/ntp_snippets/ntp_snippets_constants.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/variations/variations_params_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+
+namespace ntp_snippets {
+
+namespace {
+
+const char kHistogramMovedUpCategoryNewIndex[] =
+ "NewTabPage.ContentSuggestions.MovedUpCategoryNewIndex";
+
+} // namespace
+
+class ClickBasedCategoryRankerTest : public testing::Test {
+ public:
+ ClickBasedCategoryRankerTest()
+ : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()),
+ unused_remote_category_id_(
+ static_cast<int>(KnownCategories::LAST_KNOWN_REMOTE_CATEGORY) + 1) {
+ ClickBasedCategoryRanker::RegisterProfilePrefs(pref_service_->registry());
+
+ ranker_ = base::MakeUnique<ClickBasedCategoryRanker>(
+ pref_service_.get(), base::MakeUnique<base::DefaultClock>());
+ }
+
+ int GetUnusedRemoteCategoryID() { return unused_remote_category_id_++; }
+
+ Category GetUnusedRemoteCategory() {
+ return Category::FromIDValue(GetUnusedRemoteCategoryID());
+ }
+
+ bool CompareCategories(const Category& left, const Category& right) {
+ return ranker()->Compare(left, right);
+ }
+
+ Category AddUnusedRemoteCategory() {
+ Category category = GetUnusedRemoteCategory();
+ ranker()->AppendCategoryIfNecessary(category);
+ return category;
+ }
+
+ void AddUnusedRemoteCategories(int quantity) {
+ for (int i = 0; i < quantity; ++i) {
+ AddUnusedRemoteCategory();
+ }
+ }
+
+ void ResetRanker(std::unique_ptr<base::Clock> clock) {
+ ranker_ = base::MakeUnique<ClickBasedCategoryRanker>(pref_service_.get(),
+ std::move(clock));
+ }
+
+ void NotifyOnSuggestionOpened(int times, Category category) {
+ for (int i = 0; i < times; ++i) {
+ ranker()->OnSuggestionOpened(category);
+ }
+ }
+
+ void NotifyOnCategoryDismissed(Category category) {
+ ranker()->OnCategoryDismissed(category);
+ }
+
+ void SetDismissedCategoryPenaltyVariationParam(int value) {
+ variation_params_manager_.SetVariationParamsWithFeatureAssociations(
+ ntp_snippets::kStudyName,
+ {{"click_based_category_ranker-dismissed_category_penalty",
+ base::IntToString(value)}},
+ {kCategoryRanker.name});
+ }
+
+ void SetPromotedCategoryVariationParam(int value) {
+ variation_params_manager_.SetVariationParamsWithFeatureAssociations(
+ ntp_snippets::kStudyName,
+ {{"click_based_category_ranker-promoted_category",
+ base::IntToString(value)}},
+ {kCategoryRanker.name});
+ }
+
+ ClickBasedCategoryRanker* ranker() { return ranker_.get(); }
+
+ private:
+ std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+ int unused_remote_category_id_;
+ std::unique_ptr<ClickBasedCategoryRanker> ranker_;
+ variations::testing::VariationParamsManager variation_params_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClickBasedCategoryRankerTest);
+};
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldSortRemoteCategoriesByWhenAdded) {
+ const Category first = GetUnusedRemoteCategory();
+ const Category second = GetUnusedRemoteCategory();
+ // Categories are added in decreasing id order to test that they are not
+ // compared by id.
+ ranker()->AppendCategoryIfNecessary(second);
+ ranker()->AppendCategoryIfNecessary(first);
+ EXPECT_TRUE(CompareCategories(second, first));
+ EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldSortLocalCategoriesBeforeRemote) {
+ const Category remote_category = AddUnusedRemoteCategory();
+ const Category local_category =
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS);
+ EXPECT_TRUE(CompareCategories(local_category, remote_category));
+ EXPECT_FALSE(CompareCategories(remote_category, local_category));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ CompareShouldReturnFalseForSameCategories) {
+ const Category remote_category = AddUnusedRemoteCategory();
+ EXPECT_FALSE(CompareCategories(remote_category, remote_category));
+
+ const Category local_category =
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS);
+ EXPECT_FALSE(CompareCategories(local_category, local_category));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ AddingMoreRemoteCategoriesShouldNotChangePreviousOrder) {
+ AddUnusedRemoteCategories(3);
+
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+
+ ASSERT_TRUE(CompareCategories(first, second));
+ ASSERT_FALSE(CompareCategories(second, first));
+
+ AddUnusedRemoteCategories(3);
+
+ EXPECT_TRUE(CompareCategories(first, second));
+ EXPECT_FALSE(CompareCategories(second, first));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldChangeOrderOfNonTopCategories) {
+ // Add dummy remote categories to ensure that the following categories are not
+ // in the top anymore.
+ AddUnusedRemoteCategories(
+ ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+
+ ASSERT_TRUE(CompareCategories(first, second));
+ ASSERT_FALSE(CompareCategories(second, first));
+
+ NotifyOnSuggestionOpened(
+ /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
+
+ EXPECT_TRUE(CompareCategories(second, first));
+ EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldNotChangeOrderRightAfterOrderChange) {
+ // Add dummy remote categories to ensure that the following categories are not
+ // in the top anymore.
+ AddUnusedRemoteCategories(
+ ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+ // Two non-top categories are added.
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+ ASSERT_TRUE(CompareCategories(first, second));
+ ASSERT_FALSE(CompareCategories(second, first));
+ // Their order is changed.
+ NotifyOnSuggestionOpened(
+ /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
+ ASSERT_TRUE(CompareCategories(second, first));
+ ASSERT_FALSE(CompareCategories(first, second));
+
+ // Click on the lower category.
+ NotifyOnSuggestionOpened(/*times=*/1, first);
+
+ // Order should not change.
+ EXPECT_TRUE(CompareCategories(second, first));
+ EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldNotMoveCategoryMoreThanOncePerClick) {
+ // Add dummy remote categories to ensure that the following categories are not
+ // in the top anymore.
+ AddUnusedRemoteCategories(
+ ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+ // Non-top categories are added.
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+ Category third = AddUnusedRemoteCategory();
+
+ // Move the third category up.
+ NotifyOnSuggestionOpened(
+ /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), third);
+ EXPECT_TRUE(CompareCategories(third, second));
+ // But only on one position even though the first category has low counts.
+ EXPECT_TRUE(CompareCategories(first, third));
+ // However, another click must move it further.
+ NotifyOnSuggestionOpened(/*times=*/1, third);
+ EXPECT_TRUE(CompareCategories(third, first));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldNotMoveTopCategoryRightAfterThreshold) {
+ ASSERT_GE(ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin(), 1);
+
+ // At least one top category is added from the default order.
+ std::vector<KnownCategories> default_order =
+ ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+ Category first = Category::FromKnownCategory(default_order[0]);
+ Category second = Category::FromKnownCategory(default_order[1]);
+
+ // Try to move the second category up as if the first category was non-top.
+ NotifyOnSuggestionOpened(
+ /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
+
+ // Nothing should change, because the first category is top.
+ EXPECT_TRUE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldPersistOrderAndClicksWhenRestarted) {
+ // Add dummy remote categories to ensure that the following categories are not
+ // in the top anymore.
+ AddUnusedRemoteCategories(
+ ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+ // Non-top categories are added.
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+ Category third = AddUnusedRemoteCategory();
+
+ // Change the order.
+ NotifyOnSuggestionOpened(
+ /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), third);
+ ASSERT_TRUE(CompareCategories(third, second));
+ ASSERT_TRUE(CompareCategories(first, third));
+
+ // Simulate Chrome restart.
+ ResetRanker(base::MakeUnique<base::DefaultClock>());
+
+ // The old order must be preserved.
+ EXPECT_TRUE(CompareCategories(third, second));
+
+ // Clicks must be preserved as well.
+ NotifyOnSuggestionOpened(/*times=*/1, third);
+ EXPECT_TRUE(CompareCategories(third, first));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldDecayClickCountsWithTime) {
+ // Add dummy remote categories to ensure that the following categories are not
+ // in the top anymore.
+ AddUnusedRemoteCategories(
+ ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+ // Non-top categories are added.
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+
+ const int first_clicks = 10 * ClickBasedCategoryRanker::GetPassingMargin();
+
+ // Simulate the user using the first category for a long time (and not using
+ // anything else).
+ NotifyOnSuggestionOpened(/*times=*/first_clicks, first);
+
+ // Let multiple years pass by.
+ auto test_clock = base::MakeUnique<base::SimpleTestClock>();
+ base::SimpleTestClock* raw_test_clock = test_clock.get();
+ raw_test_clock->SetNow(base::Time::Now() + base::TimeDelta::FromDays(1000));
+ // Reset the ranker to pick up the new clock.
+ ResetRanker(std::move(test_clock));
+
+ // The user behavior changes and they start using the second category instead.
+ // According to our requirenments after such a long time it should take less
+ // than |first_clicks| for the second category to outperfom the first one.
+ int second_clicks = 0;
+ while (CompareCategories(first, second) && second_clicks < first_clicks) {
+ NotifyOnSuggestionOpened(/*times=*/1, second);
+ second_clicks++;
+ }
+ EXPECT_THAT(second_clicks, testing::Lt(first_clicks));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldDecayAfterClearHistory) {
+ std::vector<KnownCategories> default_order =
+ ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+ Category first = Category::FromKnownCategory(default_order[0]);
+ Category second = Category::FromKnownCategory(default_order[1]);
+
+ // The user clears entire history.
+ ranker()->ClearHistory(/*begin=*/base::Time(),
+ /*end=*/base::Time::Max());
+
+ // Check whether decay happens by clicking on the first category and
+ // waiting.
+ const int first_clicks = 10 * ClickBasedCategoryRanker::GetPassingMargin();
+ NotifyOnSuggestionOpened(/*times=*/first_clicks, first);
+
+ // Let multiple years pass by.
+ auto test_clock = base::MakeUnique<base::SimpleTestClock>();
+ base::SimpleTestClock* raw_test_clock = test_clock.get();
+ raw_test_clock->SetNow(base::Time::Now() + base::TimeDelta::FromDays(1000));
+ // Reset the ranker to pick up the new clock.
+ ResetRanker(std::move(test_clock));
+
+ // It should take less than |first_clicks| for the second category to
+ // overtake because of decays.
+ int second_clicks = 0;
+ while (CompareCategories(first, second) && second_clicks < first_clicks) {
+ NotifyOnSuggestionOpened(/*times=*/1, second);
+ second_clicks++;
+ }
+ EXPECT_THAT(second_clicks, testing::Lt(first_clicks));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldRemoveLastDecayTimeOnClearHistory) {
+ ASSERT_NE(ranker()->GetLastDecayTime(), base::Time::FromInternalValue(0));
+
+ // The user clears entire history.
+ ranker()->ClearHistory(/*begin=*/base::Time(),
+ /*end=*/base::Time::Max());
+
+ EXPECT_EQ(ranker()->GetLastDecayTime(), base::Time::FromInternalValue(0));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldPersistLastDecayTimeWhenRestarted) {
+ base::Time before = ranker()->GetLastDecayTime();
+ ASSERT_NE(before, base::Time::FromInternalValue(0));
+
+ // Ensure that |Now()| is different from |before| by injecting our clock.
+ auto test_clock = base::MakeUnique<base::SimpleTestClock>();
+ test_clock->SetNow(base::Time::Now() + base::TimeDelta::FromSeconds(10));
+ ResetRanker(std::move(test_clock));
+
+ EXPECT_EQ(before, ranker()->GetLastDecayTime());
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldMoveCategoryDownWhenDismissed) {
+ SetDismissedCategoryPenaltyVariationParam(2);
+
+ // Take top categories.
+ std::vector<KnownCategories> default_order =
+ ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+ Category first = Category::FromKnownCategory(default_order[0]);
+ Category second = Category::FromKnownCategory(default_order[1]);
+
+ ASSERT_TRUE(CompareCategories(first, second));
+ NotifyOnCategoryDismissed(first);
+ EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldMoveSecondToLastCategoryDownWhenDismissed) {
+ SetDismissedCategoryPenaltyVariationParam(2);
+
+ // Add categories to the bottom.
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+
+ ASSERT_TRUE(CompareCategories(first, second));
+ NotifyOnCategoryDismissed(first);
+ EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldNotMoveCategoryTooMuchDownWhenDismissed) {
+ SetDismissedCategoryPenaltyVariationParam(2);
+
+ // Add enough categories to the end.
+ std::vector<Category> categories;
+ const int penalty = ClickBasedCategoryRanker::GetDismissedCategoryPenalty();
+ for (int i = 0; i < 2 * penalty + 10; ++i) {
+ categories.push_back(AddUnusedRemoteCategory());
+ }
+
+ const int target = penalty + 1;
+ Category target_category = categories[target];
+ for (int i = 0; i < static_cast<int>(categories.size()); ++i) {
+ ASSERT_EQ(i < target, CompareCategories(categories[i], target_category));
+ }
+
+ // This should move exactly |penalty| categories up.
+ NotifyOnCategoryDismissed(categories[target]);
+
+ // Reflect expected change in |categories|.
+ const int expected = target + penalty;
+ for (int i = target; i + 1 <= expected; ++i) {
+ std::swap(categories[i], categories[i + 1]);
+ }
+
+ for (int i = 0; i < static_cast<int>(categories.size()); ++i) {
+ EXPECT_EQ(i < expected, CompareCategories(categories[i], target_category));
+ }
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldNotChangeOrderOfOtherCategoriesWhenDismissed) {
+ SetDismissedCategoryPenaltyVariationParam(2);
+
+ // Add enough categories to the end.
+ std::vector<Category> categories;
+ const int penalty = ClickBasedCategoryRanker::GetDismissedCategoryPenalty();
+ for (int i = 0; i < 2 * penalty + 10; ++i) {
+ categories.push_back(AddUnusedRemoteCategory());
+ }
+
+ int target = penalty + 1;
+ // This should not change order of all other categories.
+ NotifyOnCategoryDismissed(categories[target]);
+
+ categories.erase(categories.begin() + target);
+ for (int first = 0; first < static_cast<int>(categories.size()); ++first) {
+ for (int second = 0; second < static_cast<int>(categories.size());
+ ++second) {
+ EXPECT_EQ(first < second,
+ CompareCategories(categories[first], categories[second]));
+ }
+ }
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldNotMoveLastCategoryWhenDismissed) {
+ SetDismissedCategoryPenaltyVariationParam(2);
+
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+
+ ASSERT_TRUE(CompareCategories(first, second));
+ NotifyOnCategoryDismissed(second);
+ EXPECT_TRUE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldReduceLastCategoryClicksWhenDismissed) {
+ SetDismissedCategoryPenaltyVariationParam(2);
+
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+
+ ASSERT_TRUE(CompareCategories(first, second));
+
+ NotifyOnSuggestionOpened(/*times=*/1, second);
+
+ // This should reduce the click count back to 0.
+ NotifyOnCategoryDismissed(second);
+
+ // Try to move the second category up assuming that the previous click is
+ // still there.
+ NotifyOnSuggestionOpened(
+ /*times=*/ClickBasedCategoryRanker::GetPassingMargin() - 1, second);
+
+ EXPECT_TRUE(CompareCategories(first, second));
+
+ NotifyOnSuggestionOpened(/*times=*/1, second);
+ EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldTakeVariationValueForDismissedCategoryPenalty) {
+ const int penalty = 10203;
+ SetDismissedCategoryPenaltyVariationParam(penalty);
+ EXPECT_EQ(penalty, ClickBasedCategoryRanker::GetDismissedCategoryPenalty());
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldDoNothingWhenCategoryDismissedIfPenaltyIsZero) {
+ SetDismissedCategoryPenaltyVariationParam(0);
+
+ // Add dummy remote categories to ensure that the following categories are not
+ // in the top anymore.
+ AddUnusedRemoteCategories(
+ ClickBasedCategoryRanker::GetNumTopCategoriesWithExtraMargin());
+
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+ Category third = AddUnusedRemoteCategory();
+
+ NotifyOnSuggestionOpened(/*times=*/1, second);
+
+ // This should be ignored, because the penalty is set to 0.
+ NotifyOnCategoryDismissed(second);
+
+ // The second category should stay where it was.
+ EXPECT_TRUE(CompareCategories(first, second));
+ EXPECT_TRUE(CompareCategories(second, third));
+
+ // Try to move the second category up assuming that the previous click is
+ // still there.
+ NotifyOnSuggestionOpened(
+ /*times=*/ClickBasedCategoryRanker::GetPassingMargin() - 1, second);
+
+ // It should overtake the first category, because the dismissal should be
+ // ignored and the click should remain.
+ EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldRestoreDefaultOrderOnClearHistory) {
+ std::vector<KnownCategories> default_order =
+ ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+ Category first = Category::FromKnownCategory(default_order[0]);
+ Category second = Category::FromKnownCategory(default_order[1]);
+
+ ASSERT_TRUE(CompareCategories(first, second));
+
+ // Change the order.
+ while (CompareCategories(first, second)) {
+ NotifyOnSuggestionOpened(
+ /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
+ }
+
+ ASSERT_FALSE(CompareCategories(first, second));
+
+ // The user clears history.
+ ranker()->ClearHistory(/*begin=*/base::Time(),
+ /*end=*/base::Time::Max());
+
+ // The default order must be restored.
+ EXPECT_TRUE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldPreserveRemoteCategoriesOnClearHistory) {
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+
+ ASSERT_TRUE(CompareCategories(first, second));
+
+ // The user clears history.
+ ranker()->ClearHistory(/*begin=*/base::Time(),
+ /*end=*/base::Time::Max());
+
+ // The order does not matter, but the ranker should not die.
+ CompareCategories(first, second);
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldIgnorePartialClearHistory) {
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+
+ ASSERT_TRUE(CompareCategories(first, second));
+
+ // Change the order.
+ while (CompareCategories(first, second)) {
+ NotifyOnSuggestionOpened(
+ /*times=*/ClickBasedCategoryRanker::GetPassingMargin(), second);
+ }
+
+ ASSERT_FALSE(CompareCategories(first, second));
+
+ // The user partially clears history.
+ base::Time begin = base::Time::Now() - base::TimeDelta::FromHours(1),
+ end = base::Time::Max();
+ ranker()->ClearHistory(begin, end);
+
+ // The order should not be cleared.
+ EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldPromoteCategory) {
+ const Category downloads =
+ Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ const Category bookmarks =
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS);
+ const Category articles =
+ Category::FromKnownCategory(KnownCategories::ARTICLES);
+ ASSERT_TRUE(CompareCategories(downloads, bookmarks));
+ ASSERT_TRUE(CompareCategories(bookmarks, articles));
+ SetPromotedCategoryVariationParam(articles.id());
+ ResetRanker(base::MakeUnique<base::DefaultClock>());
+ EXPECT_TRUE(CompareCategories(articles, downloads));
+ EXPECT_TRUE(CompareCategories(articles, bookmarks));
+ EXPECT_FALSE(CompareCategories(downloads, articles));
+ EXPECT_FALSE(CompareCategories(bookmarks, articles));
+ EXPECT_FALSE(CompareCategories(articles, articles));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldHandleInvalidCategoryIDForPromotion) {
+ SetPromotedCategoryVariationParam(
+ static_cast<int>(KnownCategories::LOCAL_CATEGORIES_COUNT));
+ ResetRanker(base::MakeUnique<base::DefaultClock>());
+ // Make sure we have the default order.
+ EXPECT_TRUE(CompareCategories(
+ Category::FromKnownCategory(KnownCategories::PHYSICAL_WEB_PAGES),
+ Category::FromKnownCategory(KnownCategories::DOWNLOADS)));
+ EXPECT_TRUE(CompareCategories(
+ Category::FromKnownCategory(KnownCategories::DOWNLOADS),
+ Category::FromKnownCategory(KnownCategories::RECENT_TABS)));
+ EXPECT_TRUE(CompareCategories(
+ Category::FromKnownCategory(KnownCategories::RECENT_TABS),
+ Category::FromKnownCategory(KnownCategories::FOREIGN_TABS)));
+ EXPECT_TRUE(CompareCategories(
+ Category::FromKnownCategory(KnownCategories::FOREIGN_TABS),
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS)));
+ EXPECT_TRUE(CompareCategories(
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS),
+ Category::FromKnownCategory(KnownCategories::ARTICLES)));
+}
+
+TEST_F(ClickBasedCategoryRankerTest, ShouldEndPromotionOnSectionDismissal) {
+ const Category physical_web =
+ Category::FromKnownCategory(KnownCategories::PHYSICAL_WEB_PAGES);
+ const Category articles =
+ Category::FromKnownCategory(KnownCategories::ARTICLES);
+ ASSERT_TRUE(CompareCategories(physical_web, articles));
+
+ SetPromotedCategoryVariationParam(articles.id());
+ ResetRanker(base::MakeUnique<base::DefaultClock>());
+
+ ASSERT_TRUE(CompareCategories(articles, physical_web));
+
+ ranker()->OnCategoryDismissed(articles);
+ EXPECT_FALSE(CompareCategories(articles, physical_web));
+ EXPECT_TRUE(CompareCategories(physical_web, articles));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldResumePromotionAfter2WeeksSinceDismissal) {
+ const Category downloads =
+ Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ const Category recent_tabs =
+ Category::FromKnownCategory(KnownCategories::RECENT_TABS);
+ ASSERT_TRUE(CompareCategories(downloads, recent_tabs));
+
+ SetPromotedCategoryVariationParam(recent_tabs.id());
+ ResetRanker(base::MakeUnique<base::DefaultClock>());
+ ASSERT_TRUE(CompareCategories(recent_tabs, downloads));
+
+ ranker()->OnCategoryDismissed(recent_tabs);
+ ASSERT_FALSE(CompareCategories(recent_tabs, downloads));
+
+ // Simulate a little over 2 weeks of time passing.
+ auto test_clock = base::MakeUnique<base::SimpleTestClock>();
+ test_clock->SetNow(base::Time::Now() + base::TimeDelta::FromDays(15));
+ ResetRanker(std::move(test_clock));
+ EXPECT_TRUE(CompareCategories(recent_tabs, downloads));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldEmitNewIndexWhenCategoryMovedUpDueToClick) {
+ base::HistogramTester histogram_tester;
+
+ std::vector<KnownCategories> default_order =
+ ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+ Category first = Category::FromKnownCategory(default_order[0]);
+ Category second = Category::FromKnownCategory(default_order[1]);
+
+ ASSERT_TRUE(CompareCategories(first, second));
+
+ // Increase the score of |second| until the order changes.
+ while (CompareCategories(first, second)) {
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
+ IsEmpty());
+ ranker()->OnSuggestionOpened(second);
+ }
+ ASSERT_FALSE(CompareCategories(first, second));
+ EXPECT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldNotEmitNewIndexWhenCategoryDismissed) {
+ base::HistogramTester histogram_tester;
+
+ std::vector<KnownCategories> default_order =
+ ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+ Category category = Category::FromKnownCategory(default_order[0]);
+
+ ASSERT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
+ IsEmpty());
+
+ NotifyOnCategoryDismissed(category);
+
+ EXPECT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
+ IsEmpty());
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldNotEmitNewIndexOfMovedUpCategoryWhenHistoryCleared) {
+ std::vector<KnownCategories> default_order =
+ ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+ Category first = Category::FromKnownCategory(default_order[0]);
+ Category second = Category::FromKnownCategory(default_order[1]);
+
+ ASSERT_TRUE(CompareCategories(first, second));
+
+ // Increase the score of |second| until the order changes.
+ while (CompareCategories(first, second)) {
+ ranker()->OnSuggestionOpened(second);
+ }
+ ASSERT_FALSE(CompareCategories(first, second));
+
+ // The histogram tester is created here to ignore previous events.
+ base::HistogramTester histogram_tester;
+ ranker()->ClearHistory(/*begin=*/base::Time(),
+ /*end=*/base::Time::Max());
+
+ // ClearHistory should restore the default order.
+ ASSERT_TRUE(CompareCategories(first, second));
+
+ EXPECT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
+ IsEmpty());
+}
+
+TEST_F(ClickBasedCategoryRankerTest,
+ ShouldNotEmitNewIndexWhenCategoryPromoted) {
+ base::HistogramTester histogram_tester;
+
+ std::vector<KnownCategories> default_order =
+ ConstantCategoryRanker::GetKnownCategoriesDefaultOrder();
+ Category first = Category::FromKnownCategory(default_order[0]);
+ Category second = Category::FromKnownCategory(default_order[1]);
+
+ ASSERT_TRUE(CompareCategories(first, second));
+
+ ASSERT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
+ IsEmpty());
+
+ SetPromotedCategoryVariationParam(second.id());
+ ResetRanker(base::MakeUnique<base::DefaultClock>());
+
+ ASSERT_FALSE(CompareCategories(first, second));
+
+ EXPECT_THAT(histogram_tester.GetAllSamples(kHistogramMovedUpCategoryNewIndex),
+ IsEmpty());
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.cc b/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.cc
new file mode 100644
index 00000000000..60f040dcf53
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.cc
@@ -0,0 +1,99 @@
+// 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/category_rankers/constant_category_ranker.h"
+
+#include "base/stl_util.h"
+
+namespace ntp_snippets {
+
+ConstantCategoryRanker::ConstantCategoryRanker() {
+ std::vector<KnownCategories> ordered_known_categories =
+ GetKnownCategoriesDefaultOrder();
+ for (KnownCategories known_category : ordered_known_categories) {
+ AppendKnownCategory(known_category);
+ }
+}
+
+ConstantCategoryRanker::~ConstantCategoryRanker() = default;
+
+bool ConstantCategoryRanker::Compare(Category left, Category right) const {
+ if (!base::ContainsValue(ordered_categories_, left)) {
+ LOG(DFATAL) << "The category with ID " << left.id()
+ << " has not been added using AppendCategoryIfNecessary.";
+ }
+ if (!base::ContainsValue(ordered_categories_, right)) {
+ LOG(DFATAL) << "The category with ID " << right.id()
+ << " has not been added using AppendCategoryIfNecessary.";
+ }
+ if (left == right) {
+ return false;
+ }
+ for (Category category : ordered_categories_) {
+ if (category == left) {
+ return true;
+ }
+ if (category == right) {
+ return false;
+ }
+ }
+ // This fallback is provided only to satisfy "Compare" contract if by mistake
+ // categories are not added using AppendCategoryIfNecessary. One should not
+ // rely on this, instead the order must be defined explicitly using
+ // AppendCategoryIfNecessary.
+ return left.id() < right.id();
+}
+
+void ConstantCategoryRanker::ClearHistory(base::Time begin, base::Time end) {
+ // Ignored, because this implementation doesn't store any history-related
+ // data.
+}
+
+void ConstantCategoryRanker::AppendCategoryIfNecessary(Category category) {
+ if (!base::ContainsValue(ordered_categories_, category)) {
+ ordered_categories_.push_back(category);
+ }
+}
+
+void ConstantCategoryRanker::OnSuggestionOpened(Category category) {
+ // Ignored. The order is constant.
+}
+
+void ConstantCategoryRanker::OnCategoryDismissed(Category category) {
+ // Ignored. The order is constant.
+}
+
+// static
+std::vector<KnownCategories>
+ConstantCategoryRanker::GetKnownCategoriesDefaultOrder() {
+ std::vector<KnownCategories> categories;
+
+ // Add all local categories in a fixed order.
+ categories.push_back(KnownCategories::PHYSICAL_WEB_PAGES);
+ categories.push_back(KnownCategories::DOWNLOADS);
+ categories.push_back(KnownCategories::RECENT_TABS);
+ categories.push_back(KnownCategories::FOREIGN_TABS);
+ categories.push_back(KnownCategories::BOOKMARKS);
+
+ DCHECK_EQ(static_cast<size_t>(KnownCategories::LOCAL_CATEGORIES_COUNT),
+ categories.size());
+
+ // Known remote categories come after. Other remote categories will be ordered
+ // after these depending on when providers notify us about them using
+ // AppendCategoryIfNecessary.
+ // TODO(treib): Consider not adding ARTICLES here, so that providers can
+ // define the order themselves.
+ categories.push_back(KnownCategories::ARTICLES);
+
+ return categories;
+}
+
+void ConstantCategoryRanker::AppendKnownCategory(
+ KnownCategories known_category) {
+ Category category = Category::FromKnownCategory(known_category);
+ DCHECK(!base::ContainsValue(ordered_categories_, category));
+ ordered_categories_.push_back(category);
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.h b/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.h
new file mode 100644
index 00000000000..67786b208b3
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/constant_category_ranker.h
@@ -0,0 +1,45 @@
+// 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_SECTION_RANKERS_CONSTANT_SECTION_RANKER_H_
+#define COMPONENTS_NTP_SNIPPETS_SECTION_RANKERS_CONSTANT_SECTION_RANKER_H_
+
+#include <vector>
+
+#include "base/time/time.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_rankers/category_ranker.h"
+
+namespace ntp_snippets {
+
+// Simple implementation of a CategoryRanker that never changes the order. For
+// KnownCategories, the order is hardcoded. Remote categories must be added
+// using |AppendCategoryIfNecessary|. They are sorted after all known
+// categories. Among themselves their order is the same as the order they were
+// added in.
+class ConstantCategoryRanker : public CategoryRanker {
+ public:
+ ConstantCategoryRanker();
+ ~ConstantCategoryRanker() override;
+
+ // CategoryRanker implementation.
+ bool Compare(Category left, Category right) const override;
+ void ClearHistory(base::Time begin, base::Time end) override;
+ void AppendCategoryIfNecessary(Category category) override;
+ void OnSuggestionOpened(Category category) override;
+ void OnCategoryDismissed(Category category) override;
+
+ static std::vector<KnownCategories> GetKnownCategoriesDefaultOrder();
+
+ private:
+ void AppendKnownCategory(KnownCategories known_category);
+
+ std::vector<Category> ordered_categories_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConstantCategoryRanker);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_SECTION_RANKERS_CONSTANT_SECTION_RANKER_H_
diff --git a/chromium/components/ntp_snippets/category_rankers/constant_category_ranker_unittest.cc b/chromium/components/ntp_snippets/category_rankers/constant_category_ranker_unittest.cc
new file mode 100644
index 00000000000..e0286d49715
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/constant_category_ranker_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/ntp_snippets/category_rankers/constant_category_ranker.h"
+
+#include "components/ntp_snippets/category.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp_snippets {
+
+class ConstantCategoryRankerTest : public testing::Test {
+ public:
+ ConstantCategoryRankerTest()
+ : unused_remote_category_id_(
+ static_cast<int>(KnownCategories::LAST_KNOWN_REMOTE_CATEGORY) + 1) {
+ }
+
+ int GetUnusedRemoteCategoryID() { return unused_remote_category_id_++; }
+
+ Category GetUnusedRemoteCategory() {
+ return Category::FromIDValue(GetUnusedRemoteCategoryID());
+ }
+
+ bool CompareCategories(const Category& left, const Category& right) {
+ return ranker()->Compare(left, right);
+ }
+
+ Category AddUnusedRemoteCategory() {
+ Category category = GetUnusedRemoteCategory();
+ ranker()->AppendCategoryIfNecessary(category);
+ return category;
+ }
+
+ void AddUnusedRemoteCategories(int quantity) {
+ for (int i = 0; i < quantity; ++i) {
+ AddUnusedRemoteCategory();
+ }
+ }
+
+ ConstantCategoryRanker* ranker() { return &ranker_; }
+
+ private:
+ ConstantCategoryRanker ranker_;
+ int unused_remote_category_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConstantCategoryRankerTest);
+};
+
+TEST_F(ConstantCategoryRankerTest, ShouldSortRemoteCategoriesByWhenAdded) {
+ const Category first = GetUnusedRemoteCategory();
+ const Category second = GetUnusedRemoteCategory();
+ // Categories are added in decreasing id order to test that they are not
+ // compared by id.
+ ranker()->AppendCategoryIfNecessary(second);
+ ranker()->AppendCategoryIfNecessary(first);
+ EXPECT_TRUE(CompareCategories(second, first));
+ EXPECT_FALSE(CompareCategories(first, second));
+}
+
+TEST_F(ConstantCategoryRankerTest, ShouldSortLocalCategoriesBeforeRemote) {
+ const Category remote_category = AddUnusedRemoteCategory();
+ const Category local_category =
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS);
+ EXPECT_TRUE(CompareCategories(local_category, remote_category));
+ EXPECT_FALSE(CompareCategories(remote_category, local_category));
+}
+
+TEST_F(ConstantCategoryRankerTest, CompareShouldReturnFalseForSameCategories) {
+ const Category remote_category = AddUnusedRemoteCategory();
+ EXPECT_FALSE(CompareCategories(remote_category, remote_category));
+
+ const Category local_category =
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS);
+ EXPECT_FALSE(CompareCategories(local_category, local_category));
+}
+
+TEST_F(ConstantCategoryRankerTest,
+ AddingMoreRemoteCategoriesShouldNotChangePreviousOrder) {
+ AddUnusedRemoteCategories(3);
+
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+
+ ASSERT_TRUE(CompareCategories(first, second));
+ ASSERT_FALSE(CompareCategories(second, first));
+
+ AddUnusedRemoteCategories(3);
+
+ EXPECT_TRUE(CompareCategories(first, second));
+ EXPECT_FALSE(CompareCategories(second, first));
+}
+
+TEST_F(ConstantCategoryRankerTest,
+ AddingSameCategoryTwiceShouldNotChangeOrder) {
+ Category first = AddUnusedRemoteCategory();
+ Category second = AddUnusedRemoteCategory();
+
+ ASSERT_TRUE(CompareCategories(first, second));
+ ASSERT_FALSE(CompareCategories(second, first));
+
+ ranker()->AppendCategoryIfNecessary(first);
+
+ EXPECT_TRUE(CompareCategories(first, second));
+ EXPECT_FALSE(CompareCategories(second, first));
+}
+
+TEST_F(ConstantCategoryRankerTest, ShouldSortNonConsequtiveRemoteCategories) {
+ AddUnusedRemoteCategories(3);
+
+ Category first = AddUnusedRemoteCategory();
+
+ AddUnusedRemoteCategories(3);
+
+ Category second = AddUnusedRemoteCategory();
+
+ AddUnusedRemoteCategories(3);
+
+ EXPECT_TRUE(CompareCategories(first, second));
+ EXPECT_FALSE(CompareCategories(second, first));
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.cc b/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.cc
new file mode 100644
index 00000000000..4c34840a61c
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.cc
@@ -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.
+
+#include "components/ntp_snippets/category_rankers/fake_category_ranker.h"
+
+#include <algorithm>
+
+#include "base/stl_util.h"
+
+namespace ntp_snippets {
+
+FakeCategoryRanker::FakeCategoryRanker() = default;
+
+FakeCategoryRanker::~FakeCategoryRanker() = default;
+
+bool FakeCategoryRanker::Compare(Category left, Category right) const {
+ DCHECK(base::ContainsValue(categories_, left));
+ DCHECK(base::ContainsValue(categories_, right));
+
+ return std::find(categories_.begin(), categories_.end(), left) <
+ std::find(categories_.begin(), categories_.end(), right);
+}
+
+void FakeCategoryRanker::ClearHistory(base::Time begin, base::Time end) {
+ // Ignored.
+}
+
+void FakeCategoryRanker::AppendCategoryIfNecessary(Category category) {
+ // Ignored.
+}
+
+void FakeCategoryRanker::OnSuggestionOpened(Category category) {
+ // Ignored.
+}
+
+void FakeCategoryRanker::OnCategoryDismissed(Category category) {
+ // Ignored.
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.h b/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.h
new file mode 100644
index 00000000000..314f0f238ea
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/fake_category_ranker.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_NTP_SNIPPETS_CATEGORY_RANKERS_FAKE_CATEGORY_RANKER_H_
+#define COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_FAKE_CATEGORY_RANKER_H_
+
+#include <vector>
+
+#include "base/time/time.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_rankers/category_ranker.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace ntp_snippets {
+
+class FakeCategoryRanker : public CategoryRanker {
+ public:
+ FakeCategoryRanker();
+ ~FakeCategoryRanker() override;
+
+ void SetOrder(const std::vector<Category>& new_order) {
+ categories_ = new_order;
+ }
+
+ // CategoryRanker implementation.
+ bool Compare(Category left, Category right) const override;
+ void ClearHistory(base::Time begin, base::Time end) override;
+ void AppendCategoryIfNecessary(Category category) override;
+ void OnSuggestionOpened(Category category) override;
+ void OnCategoryDismissed(Category category) override;
+
+ private:
+ std::vector<Category> categories_;
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_FAKE_CATEGORY_RANKER_H_
diff --git a/chromium/components/ntp_snippets/category_rankers/mock_category_ranker.cc b/chromium/components/ntp_snippets/category_rankers/mock_category_ranker.cc
new file mode 100644
index 00000000000..dc9e9f70a56
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/mock_category_ranker.cc
@@ -0,0 +1,13 @@
+// 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/category_rankers/mock_category_ranker.h"
+
+namespace ntp_snippets {
+
+MockCategoryRanker::MockCategoryRanker() = default;
+
+MockCategoryRanker::~MockCategoryRanker() = default;
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/category_rankers/mock_category_ranker.h b/chromium/components/ntp_snippets/category_rankers/mock_category_ranker.h
new file mode 100644
index 00000000000..5ec50ca7b32
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_rankers/mock_category_ranker.h
@@ -0,0 +1,29 @@
+// 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_CATEGORY_RANKERS_MOCK_CATEGORY_RANKER_H_
+#define COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_MOCK_CATEGORY_RANKER_H_
+
+#include "base/time/time.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_rankers/category_ranker.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace ntp_snippets {
+
+class MockCategoryRanker : public CategoryRanker {
+ public:
+ MockCategoryRanker();
+ ~MockCategoryRanker() override;
+
+ MOCK_CONST_METHOD2(Compare, bool(Category left, Category right));
+ MOCK_METHOD2(ClearHistory, void(base::Time begin, base::Time end));
+ MOCK_METHOD1(AppendCategoryIfNecessary, void(Category category));
+ MOCK_METHOD1(OnSuggestionOpened, void(Category category));
+ MOCK_METHOD1(OnCategoryDismissed, void(Category Category));
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_CATEGORY_RANKERS_MOCK_CATEGORY_RANKER_H_
diff --git a/chromium/components/ntp_snippets/category_unittest.cc b/chromium/components/ntp_snippets/category_unittest.cc
new file mode 100644
index 00000000000..0129231b87d
--- /dev/null
+++ b/chromium/components/ntp_snippets/category_unittest.cc
@@ -0,0 +1,54 @@
+// 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/category.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp_snippets {
+
+TEST(CategoryTest, FromKnownCategoryShouldReturnSameCategoryForSameInput) {
+ const KnownCategories known_category = KnownCategories::BOOKMARKS;
+ Category first = Category::FromKnownCategory(known_category);
+ Category second = Category::FromKnownCategory(known_category);
+ EXPECT_EQ(first, second);
+}
+
+TEST(CategoryTest, ShouldIdentifyValidIDValues) {
+ EXPECT_TRUE(
+ Category::IsValidIDValue(static_cast<int>(KnownCategories::ARTICLES)));
+ EXPECT_FALSE(Category::IsValidIDValue(-1));
+ EXPECT_TRUE(Category::IsValidIDValue(0));
+ EXPECT_FALSE(Category::IsValidIDValue(
+ static_cast<int>(KnownCategories::REMOTE_CATEGORIES_OFFSET)));
+ EXPECT_TRUE(Category::IsValidIDValue(
+ static_cast<int>(KnownCategories::REMOTE_CATEGORIES_OFFSET) + 5));
+ EXPECT_FALSE(Category::IsValidIDValue(
+ static_cast<int>(KnownCategories::LOCAL_CATEGORIES_COUNT)));
+}
+
+TEST(CategoryFactoryTest,
+ FromRemoteCategoryShouldReturnSameCategoryForSameInput) {
+ const int remote_category_id = 123;
+ Category first = Category::FromRemoteCategory(remote_category_id);
+ Category second = Category::FromRemoteCategory(remote_category_id);
+ EXPECT_EQ(first, second);
+}
+
+TEST(CategoryFactoryTest, FromIDValueShouldReturnSameKnownCategory) {
+ Category known_category =
+ Category::FromKnownCategory(KnownCategories::BOOKMARKS);
+ Category known_category_by_id = Category::FromIDValue(known_category.id());
+ EXPECT_EQ(known_category, known_category_by_id);
+}
+
+TEST(CategoryFactoryTest, FromIDValueShouldReturnSameRemoteCategory) {
+ const int remote_category_id = 123;
+ Category remote_category = Category::FromRemoteCategory(remote_category_id);
+ Category remote_category_by_id = Category::FromIDValue(remote_category.id());
+ EXPECT_EQ(remote_category, remote_category_by_id);
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/content_suggestion.cc b/chromium/components/ntp_snippets/content_suggestion.cc
index a1a1d4e8007..3b007a4f1c2 100644
--- a/chromium/components/ntp_snippets/content_suggestion.cc
+++ b/chromium/components/ntp_snippets/content_suggestion.cc
@@ -52,4 +52,9 @@ void ContentSuggestion::set_recent_tab_suggestion_extra(
recent_tab_suggestion_extra_ = std::move(recent_tab_suggestion_extra);
}
+void ContentSuggestion::set_notification_extra(
+ std::unique_ptr<NotificationExtra> notification_extra) {
+ notification_extra_ = std::move(notification_extra);
+}
+
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/content_suggestion.h b/chromium/components/ntp_snippets/content_suggestion.h
index b6f0e4b2d7a..fa653b1a8a1 100644
--- a/chromium/components/ntp_snippets/content_suggestion.h
+++ b/chromium/components/ntp_snippets/content_suggestion.h
@@ -40,7 +40,15 @@ struct RecentTabSuggestionExtra {
// Corresponding tab identifier.
std::string tab_id;
// Underlying offline page identifier.
- int64_t offline_page_id;
+ int64_t offline_page_id = 0;
+};
+
+// Contains additional data for notification-worthy suggestions.
+struct NotificationExtra {
+ // Deadline for showing notification. If the deadline is past, the
+ // notification is no longer fresh and no notification should be sent. If the
+ // deadline passes while a notification is up, it should be canceled.
+ base::Time deadline;
};
// A content suggestion for the new tab page, which can be an article or an
@@ -82,15 +90,10 @@ class ContentSuggestion {
// An ID for identifying the suggestion. The ID is unique application-wide.
const ID& id() const { return id_; }
- // The normal content URL where the content referenced by the suggestion can
- // be accessed.
+ // The URL where the content referenced by the suggestion can be accessed.
+ // This may be an AMP URL.
const GURL& url() const { return url_; }
- // If available, this contains an URL to an AMP version of the same content.
- // Otherwise, this is an empty GURL().
- const GURL& amp_url() const { return amp_url_; }
- void set_amp_url(const GURL& amp_url) { amp_url_ = amp_url; }
-
// Title of the suggestion.
const base::string16& title() const { return title_; }
void set_title(const base::string16& title) { title_ = title; }
@@ -138,10 +141,19 @@ class ContentSuggestion {
void set_recent_tab_suggestion_extra(
std::unique_ptr<RecentTabSuggestionExtra> recent_tab_suggestion_extra);
+ // Extra information for notifications. When absent, no notification should be
+ // sent for this suggestion. When present, a notification should be sent,
+ // unless other factors disallow it (examples: the extra parameters say to;
+ // notifications are disabled; Chrome is in the foreground).
+ NotificationExtra* notification_extra() const {
+ return notification_extra_.get();
+ }
+ void set_notification_extra(
+ std::unique_ptr<NotificationExtra> notification_extra);
+
private:
ID id_;
GURL url_;
- GURL amp_url_;
base::string16 title_;
base::string16 snippet_text_;
base::Time publish_date_;
@@ -149,6 +161,7 @@ class ContentSuggestion {
float score_;
std::unique_ptr<DownloadSuggestionExtra> download_suggestion_extra_;
std::unique_ptr<RecentTabSuggestionExtra> recent_tab_suggestion_extra_;
+ std::unique_ptr<NotificationExtra> notification_extra_;
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 2cbb1b858fe..38860864bc8 100644
--- a/chromium/components/ntp_snippets/content_suggestions_metrics.cc
+++ b/chromium/components/ntp_snippets/content_suggestions_metrics.cc
@@ -4,10 +4,12 @@
#include "components/ntp_snippets/content_suggestions_metrics.h"
+#include <cmath>
#include <string>
#include <type_traits>
#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/stringprintf.h"
@@ -20,23 +22,27 @@ namespace {
const int kMaxSuggestionsPerCategory = 10;
const int kMaxSuggestionsTotal = 50;
+const int kMaxCategories = 10;
const char kHistogramCountOnNtpOpened[] =
"NewTabPage.ContentSuggestions.CountOnNtpOpened";
const char kHistogramShown[] = "NewTabPage.ContentSuggestions.Shown";
const char kHistogramShownAge[] = "NewTabPage.ContentSuggestions.ShownAge";
-const char kHistogramShownScore[] = "NewTabPage.ContentSuggestions.ShownScore";
+const char kHistogramShownScore[] =
+ "NewTabPage.ContentSuggestions.ShownScoreNormalized";
const char kHistogramOpened[] = "NewTabPage.ContentSuggestions.Opened";
const char kHistogramOpenedAge[] = "NewTabPage.ContentSuggestions.OpenedAge";
+const char kHistogramOpenedCategoryIndex[] =
+ "NewTabPage.ContentSuggestions.OpenedCategoryIndex";
const char kHistogramOpenedScore[] =
- "NewTabPage.ContentSuggestions.OpenedScore";
+ "NewTabPage.ContentSuggestions.OpenedScoreNormalized";
const char kHistogramOpenDisposition[] =
"NewTabPage.ContentSuggestions.OpenDisposition";
const char kHistogramMenuOpened[] = "NewTabPage.ContentSuggestions.MenuOpened";
const char kHistogramMenuOpenedAge[] =
"NewTabPage.ContentSuggestions.MenuOpenedAge";
const char kHistogramMenuOpenedScore[] =
- "NewTabPage.ContentSuggestions.MenuOpenedScore";
+ "NewTabPage.ContentSuggestions.MenuOpenedScoreNormalized";
const char kHistogramDismissedUnvisited[] =
"NewTabPage.ContentSuggestions.DismissedUnvisited";
const char kHistogramDismissedVisited[] =
@@ -49,8 +55,12 @@ const char kHistogramMoreButtonShown[] =
"NewTabPage.ContentSuggestions.MoreButtonShown";
const char kHistogramMoreButtonClicked[] =
"NewTabPage.ContentSuggestions.MoreButtonClicked";
+const char kHistogramMovedUpCategoryNewIndex[] =
+ "NewTabPage.ContentSuggestions.MovedUpCategoryNewIndex";
const char kHistogramCategoryDismissed[] =
"NewTabPage.ContentSuggestions.CategoryDismissed";
+const char kHistogramContentSuggestionsTimeSinceLastBackgroundFetch[] =
+ "NewTabPage.ContentSuggestions.TimeSinceLastBackgroundFetch";
const char kPerCategoryHistogramFormat[] = "%s.%s";
@@ -58,7 +68,7 @@ const char kPerCategoryHistogramFormat[] = "%s.%s";
// and contains exactly the values to be recorded in UMA. Don't remove or
// reorder elements, only add new ones at the end (before COUNT), and keep in
// sync with ContentSuggestionsCategory in histograms.xml.
-enum class HistogramCategories {
+enum HistogramCategories {
EXPERIMENTAL,
RECENT_TABS,
DOWNLOADS,
@@ -79,7 +89,7 @@ HistogramCategories GetHistogramCategory(Category category) {
// cast from int to KnownCategories, even if the given value isn't listed in
// the enumeration. The switch still makes sure that all known values are
// listed here.
- KnownCategories known_category = static_cast<KnownCategories>(category.id());
+ auto known_category = static_cast<KnownCategories>(category.id());
switch (known_category) {
case KnownCategories::RECENT_TABS:
return HistogramCategories::RECENT_TABS;
@@ -133,27 +143,6 @@ std::string GetCategoryHistogramName(const char* base_name, Category category) {
GetCategorySuffix(category).c_str());
}
-// This corresponds to UMA_HISTOGRAM_ENUMERATION, for use with dynamic histogram
-// names.
-void UmaHistogramEnumeration(const std::string& name,
- int value,
- int boundary_value) {
- base::LinearHistogram::FactoryGet(
- name, 1, boundary_value, boundary_value + 1,
- base::HistogramBase::kUmaTargetedHistogramFlag)
- ->Add(value);
-}
-
-// This corresponds to UMA_HISTOGRAM_LONG_TIMES for use with dynamic histogram
-// names.
-void UmaHistogramLongTimes(const std::string& name,
- const base::TimeDelta& value) {
- base::Histogram::FactoryTimeGet(
- name, base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1),
- 50, base::HistogramBase::kUmaTargetedHistogramFlag)
- ->AddTime(value);
-}
-
// This corresponds to UMA_HISTOGRAM_CUSTOM_TIMES (with min/max appropriate
// for the age of suggestions) for use with dynamic histogram names.
void UmaHistogramAge(const std::string& name, const base::TimeDelta& value) {
@@ -163,29 +152,13 @@ void UmaHistogramAge(const std::string& name, const base::TimeDelta& value) {
->AddTime(value);
}
-// This corresponds to UMA_HISTOGRAM_CUSTOM_COUNTS (with min/max appropriate
-// for the score of suggestions) for use with dynamic histogram names.
-void UmaHistogramScore(const std::string& name, float value) {
- base::Histogram::FactoryGet(name, 1, 100000, 50,
- base::HistogramBase::kUmaTargetedHistogramFlag)
- ->Add(value);
-}
-
-void LogCategoryHistogramEnumeration(const char* base_name,
- Category category,
- int value,
- int boundary_value) {
+void LogCategoryHistogramPosition(const char* base_name,
+ Category category,
+ int position,
+ int max_position) {
std::string name = GetCategoryHistogramName(base_name, category);
// Since the histogram name is dynamic, we can't use the regular macro.
- UmaHistogramEnumeration(name, value, boundary_value);
-}
-
-void LogCategoryHistogramLongTimes(const char* base_name,
- Category category,
- const base::TimeDelta& value) {
- std::string name = GetCategoryHistogramName(base_name, category);
- // Since the histogram name is dynamic, we can't use the regular macro.
- UmaHistogramLongTimes(name, value);
+ base::UmaHistogramExactLinear(name, position, max_position);
}
void LogCategoryHistogramAge(const char* base_name,
@@ -200,8 +173,12 @@ void LogCategoryHistogramScore(const char* base_name,
Category category,
float score) {
std::string name = GetCategoryHistogramName(base_name, category);
- // Since the histogram name is dynamic, we can't use the regular macro.
- UmaHistogramScore(name, score);
+ // Scores are typically reported in a range of (0,1]. As UMA does not support
+ // floats, we put them on a discrete scale of [1,10]. We keep the extra bucket
+ // 11 for unexpected over-flows as we want to distinguish them from scores
+ // close to 1. For instance, the discrete value 1 represents score values
+ // within (0.0, 0.1].
+ base::UmaHistogramExactLinear(name, ceil(score * 10), 11);
}
// Records ContentSuggestions usage. Therefore the day is sliced into 20min
@@ -213,11 +190,16 @@ void RecordContentSuggestionsUsage() {
base::Time::Exploded now_exploded;
base::Time::Now().LocalExplode(&now_exploded);
- size_t bucket =
- (now_exploded.hour * 60 + now_exploded.minute) / kBucketSizeMins;
+ int bucket = (now_exploded.hour * 60 + now_exploded.minute) / kBucketSizeMins;
- UMA_HISTOGRAM_ENUMERATION(kHistogramArticlesUsageTimeLocal, bucket,
- kNumBuckets);
+ const char* kWeekdayNames[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"};
+ std::string histogram_name(
+ base::StringPrintf("%s.%s", kHistogramArticlesUsageTimeLocal,
+ kWeekdayNames[now_exploded.day_of_week]));
+ base::UmaHistogramExactLinear(histogram_name, bucket, kNumBuckets);
+ UMA_HISTOGRAM_EXACT_LINEAR(kHistogramArticlesUsageTimeLocal, bucket,
+ kNumBuckets);
base::RecordAction(
base::UserMetricsAction("NewTabPage_ContentSuggestions_ArticlesUsage"));
@@ -229,59 +211,78 @@ void OnPageShown(
const std::vector<std::pair<Category, int>>& suggestions_per_category) {
int suggestions_total = 0;
for (const std::pair<Category, int>& item : suggestions_per_category) {
- LogCategoryHistogramEnumeration(kHistogramCountOnNtpOpened, item.first,
- item.second, kMaxSuggestionsPerCategory);
+ LogCategoryHistogramPosition(kHistogramCountOnNtpOpened, item.first,
+ item.second, kMaxSuggestionsPerCategory);
suggestions_total += item.second;
}
-
- UMA_HISTOGRAM_ENUMERATION(kHistogramCountOnNtpOpened, suggestions_total,
- kMaxSuggestionsTotal);
+ UMA_HISTOGRAM_EXACT_LINEAR(kHistogramCountOnNtpOpened, suggestions_total,
+ kMaxSuggestionsTotal);
}
void OnSuggestionShown(int global_position,
Category category,
- int category_position,
+ int position_in_category,
base::Time publish_date,
+ base::Time last_background_fetch_time,
float score) {
- UMA_HISTOGRAM_ENUMERATION(kHistogramShown, global_position,
- kMaxSuggestionsTotal);
- LogCategoryHistogramEnumeration(kHistogramShown, category, category_position,
- kMaxSuggestionsPerCategory);
+ UMA_HISTOGRAM_EXACT_LINEAR(kHistogramShown, global_position,
+ kMaxSuggestionsTotal);
+ LogCategoryHistogramPosition(kHistogramShown, category, position_in_category,
+ kMaxSuggestionsPerCategory);
base::TimeDelta age = base::Time::Now() - publish_date;
LogCategoryHistogramAge(kHistogramShownAge, category, age);
LogCategoryHistogramScore(kHistogramShownScore, category, score);
+ // TODO(markusheintz): Discuss whether the code below should be move into a
+ // separate method called OnSuggestionsListShown.
// When the first of the articles suggestions is shown, then we count this as
// a single usage of content suggestions.
if (category.IsKnownCategory(KnownCategories::ARTICLES) &&
- category_position == 0) {
+ position_in_category == 0) {
RecordContentSuggestionsUsage();
+
+ // Records the time since the last background fetch of the remote content
+ // suggestions.
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ kHistogramContentSuggestionsTimeSinceLastBackgroundFetch,
+ base::Time::Now() - last_background_fetch_time,
+ base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(7),
+ /*bucket_count=*/100);
}
}
void OnSuggestionOpened(int global_position,
Category category,
- int category_position,
+ int category_index,
+ int position_in_category,
base::Time publish_date,
float score,
WindowOpenDisposition disposition) {
- UMA_HISTOGRAM_ENUMERATION(kHistogramOpened, global_position,
- kMaxSuggestionsTotal);
- LogCategoryHistogramEnumeration(kHistogramOpened, category, category_position,
- kMaxSuggestionsPerCategory);
+ UMA_HISTOGRAM_EXACT_LINEAR(kHistogramOpenedCategoryIndex, category_index,
+ kMaxCategories);
+ LogCategoryHistogramPosition(kHistogramOpenedCategoryIndex, category,
+ category_index, kMaxCategories);
+
+ UMA_HISTOGRAM_EXACT_LINEAR(kHistogramOpened, global_position,
+ kMaxSuggestionsTotal);
+ LogCategoryHistogramPosition(kHistogramOpened, category, position_in_category,
+ kMaxSuggestionsPerCategory);
base::TimeDelta age = base::Time::Now() - publish_date;
LogCategoryHistogramAge(kHistogramOpenedAge, category, age);
LogCategoryHistogramScore(kHistogramOpenedScore, category, score);
- UMA_HISTOGRAM_ENUMERATION(
+ // We use WindowOpenDisposition::MAX_VALUE + 1 for |value_max| since MAX_VALUE
+ // itself is a valid (and used) enum value.
+ UMA_HISTOGRAM_EXACT_LINEAR(
kHistogramOpenDisposition, static_cast<int>(disposition),
static_cast<int>(WindowOpenDisposition::MAX_VALUE) + 1);
- LogCategoryHistogramEnumeration(
- kHistogramOpenDisposition, category, static_cast<int>(disposition),
+ base::UmaHistogramExactLinear(
+ GetCategoryHistogramName(kHistogramOpenDisposition, category),
+ static_cast<int>(disposition),
static_cast<int>(WindowOpenDisposition::MAX_VALUE) + 1);
if (category.IsKnownCategory(KnownCategories::ARTICLES)) {
@@ -291,14 +292,14 @@ void OnSuggestionOpened(int global_position,
void OnSuggestionMenuOpened(int global_position,
Category category,
- int category_position,
+ int position_in_category,
base::Time publish_date,
float score) {
- UMA_HISTOGRAM_ENUMERATION(kHistogramMenuOpened, global_position,
- kMaxSuggestionsTotal);
- LogCategoryHistogramEnumeration(kHistogramMenuOpened, category,
- category_position,
- kMaxSuggestionsPerCategory);
+ UMA_HISTOGRAM_EXACT_LINEAR(kHistogramMenuOpened, global_position,
+ kMaxSuggestionsTotal);
+ LogCategoryHistogramPosition(kHistogramMenuOpened, category,
+ position_in_category,
+ kMaxSuggestionsPerCategory);
base::TimeDelta age = base::Time::Now() - publish_date;
LogCategoryHistogramAge(kHistogramMenuOpenedAge, category, age);
@@ -308,45 +309,52 @@ void OnSuggestionMenuOpened(int global_position,
void OnSuggestionDismissed(int global_position,
Category category,
- int category_position,
+ int position_in_category,
bool visited) {
if (visited) {
- UMA_HISTOGRAM_ENUMERATION(kHistogramDismissedVisited, global_position,
- kMaxSuggestionsTotal);
- LogCategoryHistogramEnumeration(kHistogramDismissedVisited, category,
- category_position,
- kMaxSuggestionsPerCategory);
+ UMA_HISTOGRAM_EXACT_LINEAR(kHistogramDismissedVisited, global_position,
+ kMaxSuggestionsTotal);
+ LogCategoryHistogramPosition(kHistogramDismissedVisited, category,
+ position_in_category,
+ kMaxSuggestionsPerCategory);
} else {
- UMA_HISTOGRAM_ENUMERATION(kHistogramDismissedUnvisited, global_position,
- kMaxSuggestionsTotal);
- LogCategoryHistogramEnumeration(kHistogramDismissedUnvisited, category,
- category_position,
- kMaxSuggestionsPerCategory);
+ UMA_HISTOGRAM_EXACT_LINEAR(kHistogramDismissedUnvisited, global_position,
+ kMaxSuggestionsTotal);
+ LogCategoryHistogramPosition(kHistogramDismissedUnvisited, category,
+ position_in_category,
+ kMaxSuggestionsPerCategory);
}
}
void OnSuggestionTargetVisited(Category category, base::TimeDelta visit_time) {
- LogCategoryHistogramLongTimes(kHistogramVisitDuration, category, visit_time);
+ std::string name =
+ GetCategoryHistogramName(kHistogramVisitDuration, category);
+ base::UmaHistogramLongTimes(name, visit_time);
+}
+
+void OnCategoryMovedUp(int new_index) {
+ UMA_HISTOGRAM_EXACT_LINEAR(kHistogramMovedUpCategoryNewIndex, new_index,
+ kMaxCategories);
}
void OnMoreButtonShown(Category category, int position) {
// The "more" card can appear in addition to the actual suggestions, so add
// one extra bucket to this histogram.
- LogCategoryHistogramEnumeration(kHistogramMoreButtonShown, category, position,
- kMaxSuggestionsPerCategory + 1);
+ LogCategoryHistogramPosition(kHistogramMoreButtonShown, category, position,
+ kMaxSuggestionsPerCategory + 1);
}
void OnMoreButtonClicked(Category category, int position) {
// The "more" card can appear in addition to the actual suggestions, so add
// one extra bucket to this histogram.
- LogCategoryHistogramEnumeration(kHistogramMoreButtonClicked, category,
- position, kMaxSuggestionsPerCategory + 1);
+ LogCategoryHistogramPosition(kHistogramMoreButtonClicked, category, position,
+ kMaxSuggestionsPerCategory + 1);
}
void OnCategoryDismissed(Category category) {
UMA_HISTOGRAM_ENUMERATION(kHistogramCategoryDismissed,
- static_cast<int>(GetHistogramCategory(category)),
- static_cast<int>(HistogramCategories::COUNT));
+ GetHistogramCategory(category),
+ HistogramCategories::COUNT);
}
} // namespace metrics
diff --git a/chromium/components/ntp_snippets/content_suggestions_metrics.h b/chromium/components/ntp_snippets/content_suggestions_metrics.h
index 4be245ffa83..6a79e5e8601 100644
--- a/chromium/components/ntp_snippets/content_suggestions_metrics.h
+++ b/chromium/components/ntp_snippets/content_suggestions_metrics.h
@@ -21,30 +21,36 @@ void OnPageShown(
// Should only be called once per NTP for each suggestion.
void OnSuggestionShown(int global_position,
Category category,
- int category_position,
+ int position_in_category,
base::Time publish_date,
+ base::Time last_background_fetch_time,
float score);
+// TODO(crbug.com/682160): Take struct, so that one could not mix up the
+// order of arguments.
void OnSuggestionOpened(int global_position,
Category category,
- int category_position,
+ int category_index,
+ int position_in_category,
base::Time publish_date,
float score,
WindowOpenDisposition disposition);
void OnSuggestionMenuOpened(int global_position,
Category category,
- int category_position,
+ int position_in_category,
base::Time publish_date,
float score);
void OnSuggestionDismissed(int global_position,
Category category,
- int category_position,
+ int position_in_category,
bool visited);
void OnSuggestionTargetVisited(Category category, base::TimeDelta visit_time);
+void OnCategoryMovedUp(int new_index);
+
// Should only be called once per NTP for each "more" button.
void OnMoreButtonShown(Category category, int position);
diff --git a/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc b/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc
new file mode 100644
index 00000000000..2424d503e8b
--- /dev/null
+++ b/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc
@@ -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.
+
+#include "components/ntp_snippets/content_suggestions_metrics.h"
+
+#include "base/test/histogram_tester.h"
+#include "base/time/time.h"
+#include "components/ntp_snippets/category.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp_snippets {
+namespace metrics {
+namespace {
+
+using testing::ElementsAre;
+
+TEST(ContentSuggestionsMetricsTest, ShouldLogOnSuggestionsShown) {
+ base::HistogramTester histogram_tester;
+ OnSuggestionShown(/*global_position=*/1,
+ Category::FromKnownCategory(KnownCategories::ARTICLES),
+ /*category_position=*/3,
+ base::Time::Now(),
+ base::Time::Now() - base::TimeDelta::FromHours(2),
+ 0.01f);
+ // Test corner cases for score.
+ OnSuggestionShown(/*global_position=*/1,
+ Category::FromKnownCategory(KnownCategories::ARTICLES),
+ /*category_position=*/3, base::Time::Now(),
+ base::Time::Now() - base::TimeDelta::FromHours(2), 0.0f);
+ OnSuggestionShown(/*global_position=*/1,
+ Category::FromKnownCategory(KnownCategories::ARTICLES),
+ /*category_position=*/3, base::Time::Now(),
+ base::Time::Now() - base::TimeDelta::FromHours(2), 1.0f);
+ OnSuggestionShown(/*global_position=*/1,
+ Category::FromKnownCategory(KnownCategories::ARTICLES),
+ /*category_position=*/3, base::Time::Now(),
+ base::Time::Now() - base::TimeDelta::FromHours(2), 8.0f);
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "NewTabPage.ContentSuggestions.ShownScoreNormalized.Articles"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
+ base::Bucket(/*min=*/1, /*count=*/1),
+ base::Bucket(/*min=*/10, /*count=*/1),
+ base::Bucket(/*min=*/11, /*count=*/1)));
+}
+
+} // namespace
+} // namespace metrics
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/content_suggestions_provider.cc b/chromium/components/ntp_snippets/content_suggestions_provider.cc
index 34896577b98..23536c683b7 100644
--- a/chromium/components/ntp_snippets/content_suggestions_provider.cc
+++ b/chromium/components/ntp_snippets/content_suggestions_provider.cc
@@ -4,14 +4,10 @@
#include "components/ntp_snippets/content_suggestions_provider.h"
-#include "components/ntp_snippets/category_factory.h"
-
namespace ntp_snippets {
-ContentSuggestionsProvider::ContentSuggestionsProvider(
- Observer* observer,
- CategoryFactory* category_factory)
- : observer_(observer), category_factory_(category_factory) {}
+ContentSuggestionsProvider::ContentSuggestionsProvider(Observer* observer)
+ : observer_(observer) {}
ContentSuggestionsProvider::~ContentSuggestionsProvider() = default;
diff --git a/chromium/components/ntp_snippets/content_suggestions_provider.h b/chromium/components/ntp_snippets/content_suggestions_provider.h
index ddb6b468a88..dea83e7cc5b 100644
--- a/chromium/components/ntp_snippets/content_suggestions_provider.h
+++ b/chromium/components/ntp_snippets/content_suggestions_provider.h
@@ -82,7 +82,7 @@ class ContentSuggestionsProvider {
// Dismisses the suggestion with the given ID. A provider needs to ensure that
// a once-dismissed suggestion is never delivered again (through the
- // Observer). The provider must not call Observer::OnSuggestionsChanged if the
+ // Observer). The provider must not call Observer::OnNewSuggestions if the
// removal of the dismissed suggestion is the only change.
virtual void DismissSuggestion(
const ContentSuggestion::ID& suggestion_id) = 0;
@@ -104,6 +104,12 @@ class ContentSuggestionsProvider {
const std::set<std::string>& known_suggestion_ids,
const FetchDoneCallback& callback) = 0;
+ // Reloads suggestions from all categories. If the suggestions change, the
+ // observer must be notified via OnNewSuggestions();
+ // TODO(jkcal): make pure virtual (involves touching all providers) or remove
+ // by resolving the pull/push dichotomy.
+ virtual void ReloadSuggestions() {}
+
// Removes history from the specified time range where the URL matches the
// |filter|. The data removed depends on the provider. Note that the
// data outside the time range may be deleted, for example suggestions, which
@@ -119,6 +125,12 @@ class ContentSuggestionsProvider {
// from scratch.
virtual void ClearCachedSuggestions(Category category) = 0;
+ // Called when the sign in state has changed. Should be used instead of
+ // directly registering with the SignInManager so that the
+ // ContentSuggestionService can control the order of the updates between
+ // the providers and the observers.
+ virtual void OnSignInStateChanged() {}
+
// Used only for debugging purposes. Retrieves suggestions for the given
// |category| that have previously been dismissed and are still stored in the
// provider. If the provider doesn't store dismissed suggestions for the given
@@ -136,15 +148,11 @@ class ContentSuggestionsProvider {
virtual void ClearDismissedSuggestionsForDebugging(Category category) = 0;
protected:
- ContentSuggestionsProvider(Observer* observer,
- CategoryFactory* category_factory);
+ ContentSuggestionsProvider(Observer* observer);
Observer* observer() const { return observer_; }
- CategoryFactory* category_factory() const { return category_factory_; }
-
private:
Observer* observer_;
- CategoryFactory* category_factory_;
};
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/content_suggestions_service.cc b/chromium/components/ntp_snippets/content_suggestions_service.cc
index 42890a7e6af..48098c7afe3 100644
--- a/chromium/components/ntp_snippets/content_suggestions_service.cc
+++ b/chromium/components/ntp_snippets/content_suggestions_service.cc
@@ -23,16 +23,26 @@ namespace ntp_snippets {
ContentSuggestionsService::ContentSuggestionsService(
State state,
+ SigninManagerBase* signin_manager,
history::HistoryService* history_service,
- PrefService* pref_service)
+ PrefService* pref_service,
+ std::unique_ptr<CategoryRanker> category_ranker)
: state_(state),
+ signin_observer_(this),
history_service_observer_(this),
- ntp_snippets_service_(nullptr),
+ remote_suggestions_provider_(nullptr),
+ remote_suggestions_scheduler_(nullptr),
pref_service_(pref_service),
- user_classifier_(pref_service) {
+ user_classifier_(pref_service),
+ category_ranker_(std::move(category_ranker)) {
// Can be null in tests.
- if (history_service)
+ if (signin_manager) {
+ signin_observer_.Add(signin_manager);
+ }
+
+ if (history_service) {
history_service_observer_.Add(history_service);
+ }
RestoreDismissedCategoriesFromPrefs();
}
@@ -40,14 +50,16 @@ ContentSuggestionsService::ContentSuggestionsService(
ContentSuggestionsService::~ContentSuggestionsService() = default;
void ContentSuggestionsService::Shutdown() {
- ntp_snippets_service_ = nullptr;
+ remote_suggestions_provider_ = nullptr;
+ remote_suggestions_scheduler_ = nullptr;
suggestions_by_category_.clear();
providers_by_category_.clear();
categories_.clear();
providers_.clear();
state_ = State::DISABLED;
- for (Observer& observer : observers_)
+ for (Observer& observer : observers_) {
observer.ContentSuggestionsServiceShutdown();
+ }
}
// static
@@ -56,6 +68,15 @@ void ContentSuggestionsService::RegisterProfilePrefs(
registry->RegisterListPref(prefs::kDismissedCategories);
}
+std::vector<Category> ContentSuggestionsService::GetCategories() const {
+ std::vector<Category> sorted_categories = categories_;
+ std::sort(sorted_categories.begin(), sorted_categories.end(),
+ [this](const Category& left, const Category& right) {
+ return category_ranker_->Compare(left, right);
+ });
+ return sorted_categories;
+}
+
CategoryStatus ContentSuggestionsService::GetCategoryStatus(
Category category) const {
if (state_ == State::DISABLED) {
@@ -63,8 +84,9 @@ CategoryStatus ContentSuggestionsService::GetCategoryStatus(
}
auto iterator = providers_by_category_.find(category);
- if (iterator == providers_by_category_.end())
+ if (iterator == providers_by_category_.end()) {
return CategoryStatus::NOT_PROVIDED;
+ }
return iterator->second->GetCategoryStatus(category);
}
@@ -72,16 +94,18 @@ CategoryStatus ContentSuggestionsService::GetCategoryStatus(
base::Optional<CategoryInfo> ContentSuggestionsService::GetCategoryInfo(
Category category) const {
auto iterator = providers_by_category_.find(category);
- if (iterator == providers_by_category_.end())
+ if (iterator == providers_by_category_.end()) {
return base::Optional<CategoryInfo>();
+ }
return iterator->second->GetCategoryInfo(category);
}
const std::vector<ContentSuggestion>&
ContentSuggestionsService::GetSuggestionsForCategory(Category category) const {
auto iterator = suggestions_by_category_.find(category);
- if (iterator == suggestions_by_category_.end())
+ if (iterator == suggestions_by_category_.end()) {
return no_suggestions_;
+ }
return iterator->second;
}
@@ -106,6 +130,12 @@ void ContentSuggestionsService::ClearHistory(
for (const auto& provider : providers_) {
provider->ClearHistory(begin, end, filter);
}
+ category_ranker_->ClearHistory(begin, end);
+ // This potentially removed personalized data which we shouldn't display
+ // anymore.
+ for (Observer& observer : observers_) {
+ observer.OnFullRefreshRequired();
+ }
}
void ContentSuggestionsService::ClearAllCachedSuggestions() {
@@ -113,33 +143,37 @@ void ContentSuggestionsService::ClearAllCachedSuggestions() {
for (const auto& category_provider_pair : providers_by_category_) {
category_provider_pair.second->ClearCachedSuggestions(
category_provider_pair.first);
- for (Observer& observer : observers_)
+ for (Observer& observer : observers_) {
observer.OnNewSuggestions(category_provider_pair.first);
+ }
}
}
void ContentSuggestionsService::ClearCachedSuggestions(Category category) {
suggestions_by_category_[category].clear();
auto iterator = providers_by_category_.find(category);
- if (iterator != providers_by_category_.end())
+ if (iterator != providers_by_category_.end()) {
iterator->second->ClearCachedSuggestions(category);
+ }
}
void ContentSuggestionsService::GetDismissedSuggestionsForDebugging(
Category category,
const DismissedSuggestionsCallback& callback) {
auto iterator = providers_by_category_.find(category);
- if (iterator != providers_by_category_.end())
+ if (iterator != providers_by_category_.end()) {
iterator->second->GetDismissedSuggestionsForDebugging(category, callback);
- else
+ } else {
callback.Run(std::vector<ContentSuggestion>());
+ }
}
void ContentSuggestionsService::ClearDismissedSuggestionsForDebugging(
Category category) {
auto iterator = providers_by_category_.find(category);
- if (iterator != providers_by_category_.end())
+ if (iterator != providers_by_category_.end()) {
iterator->second->ClearDismissedSuggestionsForDebugging(category);
+ }
}
void ContentSuggestionsService::DismissSuggestion(
@@ -152,23 +186,25 @@ void ContentSuggestionsService::DismissSuggestion(
providers_by_category_[suggestion_id.category()]->DismissSuggestion(
suggestion_id);
- // Remove the suggestion locally.
- bool removed = RemoveSuggestionByID(suggestion_id);
- DCHECK(removed) << "The dismissed suggestion " << suggestion_id
- << " has already been removed. Providers must not call"
- << " OnNewSuggestions in response to DismissSuggestion.";
+ // Remove the suggestion locally if it is present. A suggestion may be missing
+ // localy e.g. if it was sent to UI through |Fetch| or it has been dismissed
+ // from a different NTP.
+ RemoveSuggestionByID(suggestion_id);
}
void ContentSuggestionsService::DismissCategory(Category category) {
auto providers_it = providers_by_category_.find(category);
- if (providers_it == providers_by_category_.end())
+ if (providers_it == providers_by_category_.end()) {
return;
+ }
ContentSuggestionsProvider* provider = providers_it->second;
UnregisterCategory(category, provider);
dismissed_providers_by_category_[category] = provider;
StoreDismissedCategoriesToPrefs();
+
+ category_ranker_->OnCategoryDismissed(category);
}
void ContentSuggestionsService::RestoreDismissedCategories() {
@@ -201,12 +237,19 @@ void ContentSuggestionsService::Fetch(
const std::set<std::string>& known_suggestion_ids,
const FetchDoneCallback& callback) {
auto providers_it = providers_by_category_.find(category);
- if (providers_it == providers_by_category_.end())
+ if (providers_it == providers_by_category_.end()) {
return;
+ }
providers_it->second->Fetch(category, known_suggestion_ids, callback);
}
+void ContentSuggestionsService::ReloadSuggestions() {
+ for (const auto& provider : providers_) {
+ provider->ReloadSuggestions();
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// Private methods
@@ -223,8 +266,9 @@ void ContentSuggestionsService::OnNewSuggestions(
} else if (IsCategoryDismissed(category)) {
// The category has been registered as a dismissed one. We need to
// check if the dismissal can be cleared now that we received new data.
- if (suggestions.empty())
+ if (suggestions.empty()) {
return;
+ }
RestoreDismissedCategory(category);
StoreDismissedCategoriesToPrefs();
@@ -240,8 +284,9 @@ void ContentSuggestionsService::OnNewSuggestions(
suggestions_by_category_[category] = std::move(suggestions);
- for (Observer& observer : observers_)
+ for (Observer& observer : observers_) {
observer.OnNewSuggestions(category);
+ }
}
void ContentSuggestionsService::OnCategoryStatusChanged(
@@ -251,22 +296,38 @@ void ContentSuggestionsService::OnCategoryStatusChanged(
if (new_status == CategoryStatus::NOT_PROVIDED) {
UnregisterCategory(category, provider);
} else {
- if (!IsCategoryStatusAvailable(new_status))
+ if (!IsCategoryStatusAvailable(new_status)) {
suggestions_by_category_.erase(category);
+ }
TryRegisterProviderForCategory(provider, category);
DCHECK_EQ(new_status, provider->GetCategoryStatus(category));
}
- if (!IsCategoryDismissed(category))
+ if (!IsCategoryDismissed(category)) {
NotifyCategoryStatusChanged(category);
+ }
}
void ContentSuggestionsService::OnSuggestionInvalidated(
ContentSuggestionsProvider* provider,
const ContentSuggestion::ID& suggestion_id) {
RemoveSuggestionByID(suggestion_id);
- for (Observer& observer : observers_)
+ for (Observer& observer : observers_) {
observer.OnSuggestionInvalidated(suggestion_id);
+ }
+}
+
+// SigninManagerBase::Observer implementation
+void ContentSuggestionsService::GoogleSigninSucceeded(
+ const std::string& account_id,
+ const std::string& username,
+ const std::string& password) {
+ OnSignInStateChanged();
+}
+
+void ContentSuggestionsService::GoogleSignedOut(const std::string& account_id,
+ const std::string& username) {
+ OnSignInStateChanged();
}
// history::HistoryServiceObserver implementation.
@@ -277,28 +338,26 @@ void ContentSuggestionsService::OnURLsDeleted(
const history::URLRows& deleted_rows,
const std::set<GURL>& favicon_urls) {
// We don't care about expired entries.
- if (expired)
+ if (expired) {
return;
+ }
- // Redirect to ClearHistory().
if (all_history) {
- base::Time begin = base::Time();
- base::Time end = base::Time::Max();
base::Callback<bool(const GURL& url)> filter =
base::Bind([](const GURL& url) { return true; });
- ClearHistory(begin, end, filter);
+ ClearHistory(base::Time(), base::Time::Max(), filter);
} else {
- if (deleted_rows.empty())
+ // If a user deletes a single URL, we don't consider this a clear user
+ // intend to clear our data.
+ // TODO(tschumann): Single URL deletions should be handled on a case-by-case
+ // basis. However this depends on the provider's details and thus cannot be
+ // done here. Introduce a OnURLsDeleted() method on the providers to move
+ // this decision further down.
+ if (deleted_rows.size() < 2) {
return;
-
- base::Time begin = deleted_rows[0].last_visit();
- base::Time end = deleted_rows[0].last_visit();
+ }
std::set<GURL> deleted_urls;
for (const history::URLRow& row : deleted_rows) {
- if (row.last_visit() < begin)
- begin = row.last_visit();
- if (row.last_visit() > end)
- end = row.last_visit();
deleted_urls.insert(row.url());
}
base::Callback<bool(const GURL& url)> filter = base::Bind(
@@ -306,7 +365,10 @@ void ContentSuggestionsService::OnURLsDeleted(
return set.count(url) != 0;
},
deleted_urls);
- ClearHistory(begin, end, filter);
+ // We usually don't have any time-related information (the URLRow objects
+ // usually don't provide a |last_visit()| timestamp. Hence we simply clear
+ // the whole history for the selected URLs.
+ ClearHistory(base::Time(), base::Time::Max(), filter);
}
}
@@ -349,7 +411,6 @@ void ContentSuggestionsService::RegisterCategory(
providers_by_category_[category] = provider;
categories_.push_back(category);
- SortCategories();
if (IsCategoryStatusAvailable(provider->GetCategoryStatus(category))) {
suggestions_by_category_.insert(
std::make_pair(category, std::vector<ContentSuggestion>()));
@@ -381,23 +442,31 @@ bool ContentSuggestionsService::RemoveSuggestionByID(
[&suggestion_id](const ContentSuggestion& suggestion) {
return suggestion_id == suggestion.id();
});
- if (position == suggestions->end())
+ if (position == suggestions->end()) {
return false;
+ }
suggestions->erase(position);
return true;
}
void ContentSuggestionsService::NotifyCategoryStatusChanged(Category category) {
- for (Observer& observer : observers_)
+ for (Observer& observer : observers_) {
observer.OnCategoryStatusChanged(category, GetCategoryStatus(category));
+ }
}
-void ContentSuggestionsService::SortCategories() {
- std::sort(categories_.begin(), categories_.end(),
- [this](const Category& left, const Category& right) {
- return category_factory_.CompareCategories(left, right);
- });
+void ContentSuggestionsService::OnSignInStateChanged() {
+ // First notify the providers, so they can make the required changes.
+ for (const auto& provider : providers_) {
+ provider->OnSignInStateChanged();
+ }
+
+ // Finally notify the observers so they refresh only after the backend is
+ // ready.
+ for (Observer& observer : observers_) {
+ observer.OnFullRefreshRequired();
+ }
}
bool ContentSuggestionsService::IsCategoryDismissed(Category category) const {
@@ -413,8 +482,9 @@ void ContentSuggestionsService::RestoreDismissedCategory(Category category) {
ContentSuggestionsProvider* provider = dismissed_it->second;
dismissed_providers_by_category_.erase(dismissed_it);
- if (provider)
+ if (provider) {
RegisterCategory(category, provider);
+ }
}
void ContentSuggestionsService::RestoreDismissedCategoriesFromPrefs() {
@@ -432,8 +502,7 @@ void ContentSuggestionsService::RestoreDismissedCategoriesFromPrefs() {
}
// When the provider is registered, it will be stored in this map.
- dismissed_providers_by_category_[category_factory()->FromIDValue(id)] =
- nullptr;
+ dismissed_providers_by_category_[Category::FromIDValue(id)] = nullptr;
}
}
diff --git a/chromium/components/ntp_snippets/content_suggestions_service.h b/chromium/components/ntp_snippets/content_suggestions_service.h
index 9559dccb25e..3896eda4be4 100644
--- a/chromium/components/ntp_snippets/content_suggestions_service.h
+++ b/chromium/components/ntp_snippets/content_suggestions_service.h
@@ -20,10 +20,12 @@
#include "components/history/core/browser/history_service_observer.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/ntp_snippets/callbacks.h"
-#include "components/ntp_snippets/category_factory.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_rankers/category_ranker.h"
#include "components/ntp_snippets/category_status.h"
#include "components/ntp_snippets/content_suggestions_provider.h"
#include "components/ntp_snippets/user_classifier.h"
+#include "components/signin/core/browser/signin_manager.h"
class PrefService;
class PrefRegistrySimple;
@@ -31,11 +33,13 @@ class PrefRegistrySimple;
namespace ntp_snippets {
class RemoteSuggestionsProvider;
+class RemoteSuggestionsScheduler;
// 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 ContentSuggestionsProvider::Observer,
+ public SigninManagerBase::Observer,
public history::HistoryServiceObserver {
public:
class Observer {
@@ -64,6 +68,11 @@ class ContentSuggestionsService : public KeyedService,
virtual void OnSuggestionInvalidated(
const ContentSuggestion::ID& suggestion_id) = 0;
+ // Fired when the previously sent data is not valid anymore and a refresh
+ // of all the suggestions is required. Called for example when the sign in
+ // state changes and personalised suggestions have to be shown or discarded.
+ virtual void OnFullRefreshRequired() = 0;
+
// Sent when the service is shutting down. After the service has shut down,
// it will not provide any data anymore, though calling the getters is still
// safe.
@@ -79,8 +88,10 @@ class ContentSuggestionsService : public KeyedService,
};
ContentSuggestionsService(State state,
+ SigninManagerBase* signin_manager,
history::HistoryService* history_service,
- PrefService* pref_service);
+ PrefService* pref_service,
+ std::unique_ptr<CategoryRanker> category_ranker);
~ContentSuggestionsService() override;
// Inherited from KeyedService.
@@ -90,9 +101,10 @@ class ContentSuggestionsService : public KeyedService,
State state() { return state_; }
- // Gets all categories for which a provider is registered. The categories
- // may or may not be available, see |GetCategoryStatus()|.
- const std::vector<Category>& GetCategories() const { return categories_; }
+ // Gets all categories for which a provider is registered. The categories may
+ // or may not be available, see |GetCategoryStatus()|. The order in which the
+ // categories are returned is the order in which they should be displayed.
+ std::vector<Category> GetCategories() const;
// Gets the status of a category.
CategoryStatus GetCategoryStatus(Category category) const;
@@ -114,7 +126,8 @@ class ContentSuggestionsService : public KeyedService,
const ImageFetchedCallback& callback);
// Dismisses the suggestion with the given |suggestion_id|, if it exists.
- // This will not trigger an update through the observers.
+ // This will not trigger an update through the observers (i.e. providers must
+ // not call |Observer::OnNewSuggestions|).
void DismissSuggestion(const ContentSuggestion::ID& suggestion_id);
// Dismisses the given |category|, if it exists.
@@ -131,10 +144,21 @@ class ContentSuggestionsService : public KeyedService,
// Fetches additional contents for the given |category|. If the fetch was
// completed, the given |callback| is called with the updated content.
// This includes new and old data.
+ // TODO(jkrcal): Consider either renaming this to FetchMore or unify the ways
+ // to get suggestions to just this async Fetch() API.
void Fetch(const Category& category,
const std::set<std::string>& known_suggestion_ids,
const FetchDoneCallback& callback);
+ // Reloads suggestions from all categories, from all providers. If a provider
+ // naturally has some ability to generate fresh suggestions, it may provide a
+ // completely new set of suggestions. If the provider has no ability to
+ // generate fresh suggestions on demand, it may only fill in any vacant space
+ // by suggestions that were previously not included due to space limits (there
+ // may be vacant space because of the user dismissing suggestions in the
+ // meantime).
+ void ReloadSuggestions();
+
// Observer accessors.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
@@ -182,20 +206,33 @@ class ContentSuggestionsService : public KeyedService,
// supports it).
void ClearDismissedSuggestionsForDebugging(Category category);
- CategoryFactory* category_factory() { return &category_factory_; }
+ // The reference to the RemoteSuggestionsProvider provider should
+ // only be set by the factory and only used for debugging.
+ // TODO(jkrcal) The way we deal with the circular dependency feels wrong.
+ // Consider swapping the dependencies: first constructing all providers, then
+ // constructing the service (passing the remote provider as arg), finally
+ // registering the service as an observer of all providers?
+ void set_remote_suggestions_provider(
+ RemoteSuggestionsProvider* remote_suggestions_provider) {
+ remote_suggestions_provider_ = remote_suggestions_provider;
+ }
+ RemoteSuggestionsProvider* remote_suggestions_provider_for_debugging() {
+ return remote_suggestions_provider_;
+ }
- // The reference to the RemoteSuggestionsProvider provider should only be set
- // by the factory and only be used for scheduling, periodic fetching and
- // debugging.
- RemoteSuggestionsProvider* ntp_snippets_service() {
- return ntp_snippets_service_;
+ // The reference to RemoteSuggestionsScheduler should only be set by the
+ // factory. The interface is suited for informing about external events that
+ // have influence on scheduling remote fetches.
+ void set_remote_suggestions_scheduler(
+ ntp_snippets::RemoteSuggestionsScheduler* remote_suggestions_scheduler) {
+ remote_suggestions_scheduler_ = remote_suggestions_scheduler;
}
- void set_ntp_snippets_service(
- RemoteSuggestionsProvider* ntp_snippets_service) {
- ntp_snippets_service_ = ntp_snippets_service;
+ RemoteSuggestionsScheduler* remote_suggestions_scheduler() {
+ return remote_suggestions_scheduler_;
}
UserClassifier* user_classifier() { return &user_classifier_; }
+ CategoryRanker* category_ranker() { return category_ranker_.get(); }
private:
friend class ContentSuggestionsServiceTest;
@@ -211,6 +248,13 @@ class ContentSuggestionsService : public KeyedService,
ContentSuggestionsProvider* provider,
const ContentSuggestion::ID& suggestion_id) override;
+ // SigninManagerBase::Observer implementation
+ void GoogleSigninSucceeded(const std::string& account_id,
+ const std::string& username,
+ const std::string& password) override;
+ void GoogleSignedOut(const std::string& account_id,
+ const std::string& username) override;
+
// history::HistoryServiceObserver implementation.
void OnURLsDeleted(history::HistoryService* history_service,
bool all_history,
@@ -237,7 +281,7 @@ class ContentSuggestionsService : public KeyedService,
// Fires the OnCategoryStatusChanged event for the given |category|.
void NotifyCategoryStatusChanged(Category category);
- void SortCategories();
+ void OnSignInStateChanged();
// Re-enables a dismissed category, making querying its provider possible.
void RestoreDismissedCategory(Category category);
@@ -248,9 +292,6 @@ class ContentSuggestionsService : public KeyedService,
// Whether the content suggestions feature is enabled.
State state_;
- // Provides new and existing categories and an order for them.
- CategoryFactory category_factory_;
-
// All registered providers, owned by the service.
std::vector<std::unique_ptr<ContentSuggestionsProvider>> providers_;
@@ -268,9 +309,8 @@ class ContentSuggestionsService : public KeyedService,
std::map<Category, ContentSuggestionsProvider*, Category::CompareByID>
dismissed_providers_by_category_;
- // All current suggestion categories, in an order determined by the
- // |category_factory_|. This vector contains exactly the same categories as
- // |providers_by_category_|.
+ // All current suggestion categories in arbitrary order. This vector contains
+ // exactly the same categories as |providers_by_category_|.
std::vector<Category> categories_;
// All current suggestions grouped by category. This contains an entry for
@@ -280,6 +320,11 @@ class ContentSuggestionsService : public KeyedService,
std::map<Category, std::vector<ContentSuggestion>, Category::CompareByID>
suggestions_by_category_;
+ // Observer for the SigninManager. All observers are notified when the signin
+ // state changes so that they can refresh their list of suggestions.
+ ScopedObserver<SigninManagerBase, SigninManagerBase::Observer>
+ signin_observer_;
+
// Observer for the HistoryService. All providers are notified when history is
// deleted.
ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
@@ -289,16 +334,22 @@ class ContentSuggestionsService : public KeyedService,
const std::vector<ContentSuggestion> no_suggestions_;
- // Keep a direct reference to this special provider to redirect scheduling,
- // background fetching and debugging calls to it. If the
- // RemoteSuggestionsProvider is loaded, it is also present in |providers_|,
- // otherwise this is a nullptr.
- RemoteSuggestionsProvider* ntp_snippets_service_;
+ // Keep a direct reference to this special provider to redirect debugging
+ // calls to it. If the RemoteSuggestionsProvider is loaded, it is also present
+ // in |providers_|, otherwise this is a nullptr.
+ RemoteSuggestionsProvider* remote_suggestions_provider_;
+
+ // Interface for informing about external events that have influence on
+ // scheduling remote fetches. Not owned.
+ RemoteSuggestionsScheduler* remote_suggestions_scheduler_;
PrefService* pref_service_;
UserClassifier user_classifier_;
+ // Provides order for categories.
+ std::unique_ptr<CategoryRanker> category_ranker_;
+
DISALLOW_COPY_AND_ASSIGN(ContentSuggestionsService);
};
diff --git a/chromium/components/ntp_snippets/content_suggestions_service_unittest.cc b/chromium/components/ntp_snippets/content_suggestions_service_unittest.cc
index fb982d4ab6d..29ddca3131e 100644
--- a/chromium/components/ntp_snippets/content_suggestions_service_unittest.cc
+++ b/chromium/components/ntp_snippets/content_suggestions_service_unittest.cc
@@ -14,101 +14,35 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
#include "components/ntp_snippets/category_info.h"
+#include "components/ntp_snippets/category_rankers/constant_category_ranker.h"
+#include "components/ntp_snippets/category_rankers/fake_category_ranker.h"
+#include "components/ntp_snippets/category_rankers/mock_category_ranker.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/mock_content_suggestions_provider.h"
#include "components/ntp_snippets/user_classifier.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image.h"
+using testing::_;
using testing::ElementsAre;
using testing::Eq;
using testing::InvokeWithoutArgs;
using testing::IsEmpty;
using testing::Mock;
using testing::Property;
-using testing::_;
+using testing::Return;
+using testing::StrictMock;
+using testing::UnorderedElementsAre;
namespace ntp_snippets {
namespace {
-class MockProvider : public ContentSuggestionsProvider {
- public:
- MockProvider(Observer* observer,
- CategoryFactory* category_factory,
- const std::vector<Category>& provided_categories)
- : ContentSuggestionsProvider(observer, category_factory) {
- SetProvidedCategories(provided_categories);
- }
-
- void SetProvidedCategories(const std::vector<Category>& provided_categories) {
- statuses_.clear();
- provided_categories_ = provided_categories;
- for (Category category : provided_categories) {
- statuses_[category.id()] = CategoryStatus::AVAILABLE;
- }
- }
-
- CategoryStatus GetCategoryStatus(Category category) override {
- return statuses_[category.id()];
- }
-
- CategoryInfo GetCategoryInfo(Category category) override {
- return CategoryInfo(base::ASCIIToUTF16("Section title"),
- ContentSuggestionsCardLayout::FULL_CARD, true, false,
- true, false,
- base::ASCIIToUTF16("No suggestions message"));
- }
-
- void FireSuggestionsChanged(
- Category category,
- std::vector<ContentSuggestion> suggestions) {
- observer()->OnNewSuggestions(this, category, std::move(suggestions));
- }
-
- void FireCategoryStatusChanged(Category category, CategoryStatus new_status) {
- statuses_[category.id()] = new_status;
- observer()->OnCategoryStatusChanged(this, category, new_status);
- }
-
- void FireCategoryStatusChangedWithCurrentStatus(Category category) {
- observer()->OnCategoryStatusChanged(this, category,
- statuses_[category.id()]);
- }
-
- void FireSuggestionInvalidated(const ContentSuggestion::ID& suggestion_id) {
- observer()->OnSuggestionInvalidated(this, suggestion_id);
- }
-
- MOCK_METHOD3(ClearHistory,
- void(base::Time begin,
- base::Time end,
- const base::Callback<bool(const GURL& url)>& filter));
- MOCK_METHOD3(Fetch,
- void(const Category&,
- const std::set<std::string>&,
- const FetchDoneCallback&));
- MOCK_METHOD1(ClearCachedSuggestions, void(Category category));
- MOCK_METHOD2(GetDismissedSuggestionsForDebugging,
- void(Category category,
- const DismissedSuggestionsCallback& callback));
- MOCK_METHOD1(ClearDismissedSuggestionsForDebugging, void(Category category));
- MOCK_METHOD1(DismissSuggestion,
- void(const ContentSuggestion::ID& suggestion_id));
- MOCK_METHOD2(FetchSuggestionImage,
- void(const ContentSuggestion::ID& suggestion_id,
- const ImageFetchedCallback& callback));
-
- private:
- std::vector<Category> provided_categories_;
- std::map<int, CategoryStatus> statuses_;
-};
-
class MockServiceObserver : public ContentSuggestionsService::Observer {
public:
MockServiceObserver() = default;
@@ -119,6 +53,7 @@ class MockServiceObserver : public ContentSuggestionsService::Observer {
void(Category changed_category, CategoryStatus new_status));
MOCK_METHOD1(OnSuggestionInvalidated,
void(const ContentSuggestion::ID& suggestion_id));
+ MOCK_METHOD0(OnFullRefreshRequired, void());
MOCK_METHOD0(ContentSuggestionsServiceShutdown, void());
private:
@@ -130,7 +65,8 @@ class MockServiceObserver : public ContentSuggestionsService::Observer {
class ContentSuggestionsServiceTest : public testing::Test {
public:
ContentSuggestionsServiceTest()
- : pref_service_(new TestingPrefServiceSimple()) {}
+ : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()),
+ category_ranker_(base::MakeUnique<ConstantCategoryRanker>()) {}
void SetUp() override {
RegisterPrefs();
@@ -179,29 +115,26 @@ class ContentSuggestionsServiceTest : public testing::Test {
return service()->dismissed_providers_by_category_;
}
- CategoryFactory* category_factory() { return service()->category_factory(); }
-
- Category FromKnownCategory(KnownCategories known_category) {
- return service()->category_factory()->FromKnownCategory(known_category);
- }
-
- Category FromRemoteCategory(int remote_category) {
- return service()->category_factory()->FromRemoteCategory(remote_category);
+ MockContentSuggestionsProvider* MakeRegisteredMockProvider(
+ Category provided_category) {
+ return MakeRegisteredMockProvider(
+ std::vector<Category>({provided_category}));
}
- MockProvider* RegisterProvider(Category provided_category) {
- return RegisterProvider(std::vector<Category>({provided_category}));
- }
-
- MockProvider* RegisterProvider(
+ MockContentSuggestionsProvider* MakeRegisteredMockProvider(
const std::vector<Category>& provided_categories) {
- std::unique_ptr<MockProvider> provider = base::MakeUnique<MockProvider>(
- service(), category_factory(), provided_categories);
- MockProvider* result = provider.get();
+ auto provider =
+ base::MakeUnique<testing::StrictMock<MockContentSuggestionsProvider>>(
+ service(), provided_categories);
+ MockContentSuggestionsProvider* result = provider.get();
service()->RegisterProvider(std::move(provider));
return result;
}
+ void SetCategoryRanker(std::unique_ptr<CategoryRanker> category_ranker) {
+ category_ranker_ = std::move(category_ranker);
+ }
+
MOCK_METHOD1(OnImageFetched, void(const gfx::Image&));
protected:
@@ -213,8 +146,9 @@ class ContentSuggestionsServiceTest : public testing::Test {
void CreateContentSuggestionsService(
ContentSuggestionsService::State enabled) {
ASSERT_FALSE(service_);
- service_.reset(new ContentSuggestionsService(
- enabled, /*history_service=*/nullptr, pref_service_.get()));
+ service_ = base::MakeUnique<ContentSuggestionsService>(
+ enabled, /*signin_manager=*/nullptr, /*history_service=*/nullptr,
+ pref_service_.get(), std::move(category_ranker_));
}
void ResetService() {
@@ -245,6 +179,7 @@ class ContentSuggestionsServiceTest : public testing::Test {
private:
std::unique_ptr<ContentSuggestionsService> service_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+ std::unique_ptr<CategoryRanker> category_ranker_;
DISALLOW_COPY_AND_ASSIGN(ContentSuggestionsServiceTest);
};
@@ -261,9 +196,10 @@ class ContentSuggestionsServiceDisabledTest
TEST_F(ContentSuggestionsServiceTest, ShouldRegisterProviders) {
EXPECT_THAT(service()->state(),
Eq(ContentSuggestionsService::State::ENABLED));
- Category articles_category = FromKnownCategory(KnownCategories::ARTICLES);
+ Category articles_category =
+ Category::FromKnownCategory(KnownCategories::ARTICLES);
Category offline_pages_category =
- FromKnownCategory(KnownCategories::DOWNLOADS);
+ Category::FromKnownCategory(KnownCategories::DOWNLOADS);
ASSERT_THAT(providers(), IsEmpty());
EXPECT_THAT(service()->GetCategories(), IsEmpty());
EXPECT_THAT(service()->GetCategoryStatus(articles_category),
@@ -271,19 +207,22 @@ TEST_F(ContentSuggestionsServiceTest, ShouldRegisterProviders) {
EXPECT_THAT(service()->GetCategoryStatus(offline_pages_category),
Eq(CategoryStatus::NOT_PROVIDED));
- MockProvider* provider1 = RegisterProvider(articles_category);
+ MockContentSuggestionsProvider* provider1 =
+ MakeRegisteredMockProvider(articles_category);
provider1->FireCategoryStatusChangedWithCurrentStatus(articles_category);
EXPECT_THAT(providers().count(offline_pages_category), Eq(0ul));
ASSERT_THAT(providers().count(articles_category), Eq(1ul));
EXPECT_THAT(providers().at(articles_category), Eq(provider1));
EXPECT_THAT(providers().size(), Eq(1ul));
- EXPECT_THAT(service()->GetCategories(), ElementsAre(articles_category));
+ EXPECT_THAT(service()->GetCategories(),
+ UnorderedElementsAre(articles_category));
EXPECT_THAT(service()->GetCategoryStatus(articles_category),
Eq(CategoryStatus::AVAILABLE));
EXPECT_THAT(service()->GetCategoryStatus(offline_pages_category),
Eq(CategoryStatus::NOT_PROVIDED));
- MockProvider* provider2 = RegisterProvider(offline_pages_category);
+ MockContentSuggestionsProvider* provider2 =
+ MakeRegisteredMockProvider(offline_pages_category);
provider2->FireCategoryStatusChangedWithCurrentStatus(offline_pages_category);
ASSERT_THAT(providers().count(offline_pages_category), Eq(1ul));
EXPECT_THAT(providers().at(articles_category), Eq(provider1));
@@ -291,7 +230,7 @@ TEST_F(ContentSuggestionsServiceTest, ShouldRegisterProviders) {
EXPECT_THAT(providers().at(offline_pages_category), Eq(provider2));
EXPECT_THAT(providers().size(), Eq(2ul));
EXPECT_THAT(service()->GetCategories(),
- ElementsAre(offline_pages_category, articles_category));
+ UnorderedElementsAre(offline_pages_category, articles_category));
EXPECT_THAT(service()->GetCategoryStatus(articles_category),
Eq(CategoryStatus::AVAILABLE));
EXPECT_THAT(service()->GetCategoryStatus(offline_pages_category),
@@ -299,9 +238,10 @@ TEST_F(ContentSuggestionsServiceTest, ShouldRegisterProviders) {
}
TEST_F(ContentSuggestionsServiceDisabledTest, ShouldDoNothingWhenDisabled) {
- Category articles_category = FromKnownCategory(KnownCategories::ARTICLES);
+ Category articles_category =
+ Category::FromKnownCategory(KnownCategories::ARTICLES);
Category offline_pages_category =
- FromKnownCategory(KnownCategories::DOWNLOADS);
+ Category::FromKnownCategory(KnownCategories::DOWNLOADS);
EXPECT_THAT(service()->state(),
Eq(ContentSuggestionsService::State::DISABLED));
EXPECT_THAT(providers(), IsEmpty());
@@ -315,11 +255,14 @@ TEST_F(ContentSuggestionsServiceDisabledTest, ShouldDoNothingWhenDisabled) {
}
TEST_F(ContentSuggestionsServiceTest, ShouldRedirectFetchSuggestionImage) {
- Category articles_category = FromKnownCategory(KnownCategories::ARTICLES);
+ Category articles_category =
+ Category::FromKnownCategory(KnownCategories::ARTICLES);
Category offline_pages_category =
- FromKnownCategory(KnownCategories::DOWNLOADS);
- MockProvider* provider1 = RegisterProvider(articles_category);
- MockProvider* provider2 = RegisterProvider(offline_pages_category);
+ Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MockContentSuggestionsProvider* provider1 =
+ MakeRegisteredMockProvider(articles_category);
+ MockContentSuggestionsProvider* provider2 =
+ MakeRegisteredMockProvider(offline_pages_category);
provider1->FireSuggestionsChanged(articles_category,
CreateSuggestions(articles_category, {1}));
@@ -339,8 +282,7 @@ TEST_F(ContentSuggestionsServiceTest,
base::RunLoop run_loop;
// Assuming there will never be a category with the id below.
- ContentSuggestion::ID suggestion_id(category_factory()->FromIDValue(21563),
- "TestID");
+ ContentSuggestion::ID suggestion_id(Category::FromIDValue(21563), "TestID");
EXPECT_CALL(*this, OnImageFetched(Property(&gfx::Image::IsEmpty, Eq(true))))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
service()->FetchSuggestionImage(
@@ -350,11 +292,14 @@ TEST_F(ContentSuggestionsServiceTest,
}
TEST_F(ContentSuggestionsServiceTest, ShouldRedirectDismissSuggestion) {
- Category articles_category = FromKnownCategory(KnownCategories::ARTICLES);
+ Category articles_category =
+ Category::FromKnownCategory(KnownCategories::ARTICLES);
Category offline_pages_category =
- FromKnownCategory(KnownCategories::DOWNLOADS);
- MockProvider* provider1 = RegisterProvider(articles_category);
- MockProvider* provider2 = RegisterProvider(offline_pages_category);
+ Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MockContentSuggestionsProvider* provider1 =
+ MakeRegisteredMockProvider(articles_category);
+ MockContentSuggestionsProvider* provider2 =
+ MakeRegisteredMockProvider(offline_pages_category);
provider2->FireSuggestionsChanged(
offline_pages_category, CreateSuggestions(offline_pages_category, {11}));
@@ -366,9 +311,11 @@ TEST_F(ContentSuggestionsServiceTest, ShouldRedirectDismissSuggestion) {
}
TEST_F(ContentSuggestionsServiceTest, ShouldRedirectSuggestionInvalidated) {
- Category articles_category = FromKnownCategory(KnownCategories::ARTICLES);
+ Category articles_category =
+ Category::FromKnownCategory(KnownCategories::ARTICLES);
- MockProvider* provider = RegisterProvider(articles_category);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(articles_category);
MockServiceObserver observer;
service()->AddObserver(&observer);
@@ -395,14 +342,17 @@ TEST_F(ContentSuggestionsServiceTest, ShouldRedirectSuggestionInvalidated) {
}
TEST_F(ContentSuggestionsServiceTest, ShouldForwardSuggestions) {
- Category articles_category = FromKnownCategory(KnownCategories::ARTICLES);
+ Category articles_category =
+ Category::FromKnownCategory(KnownCategories::ARTICLES);
Category offline_pages_category =
- FromKnownCategory(KnownCategories::DOWNLOADS);
+ Category::FromKnownCategory(KnownCategories::DOWNLOADS);
// Create and register providers
- MockProvider* provider1 = RegisterProvider(articles_category);
+ MockContentSuggestionsProvider* provider1 =
+ MakeRegisteredMockProvider(articles_category);
provider1->FireCategoryStatusChangedWithCurrentStatus(articles_category);
- MockProvider* provider2 = RegisterProvider(offline_pages_category);
+ MockContentSuggestionsProvider* provider2 =
+ MakeRegisteredMockProvider(offline_pages_category);
provider2->FireCategoryStatusChangedWithCurrentStatus(offline_pages_category);
ASSERT_THAT(providers().count(articles_category), Eq(1ul));
EXPECT_THAT(providers().at(articles_category), Eq(provider1));
@@ -467,14 +417,15 @@ TEST_F(ContentSuggestionsServiceTest, ShouldForwardSuggestions) {
TEST_F(ContentSuggestionsServiceTest,
ShouldNotReturnCategoryInfoForNonexistentCategory) {
- Category category = FromKnownCategory(KnownCategories::DOWNLOADS);
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
base::Optional<CategoryInfo> result = service()->GetCategoryInfo(category);
EXPECT_FALSE(result.has_value());
}
TEST_F(ContentSuggestionsServiceTest, ShouldReturnCategoryInfo) {
- Category category = FromKnownCategory(KnownCategories::DOWNLOADS);
- MockProvider* provider = RegisterProvider(category);
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
provider->FireCategoryStatusChangedWithCurrentStatus(category);
base::Optional<CategoryInfo> result = service()->GetCategoryInfo(category);
ASSERT_TRUE(result.has_value());
@@ -489,8 +440,9 @@ TEST_F(ContentSuggestionsServiceTest, ShouldReturnCategoryInfo) {
TEST_F(ContentSuggestionsServiceTest,
ShouldRegisterNewCategoryOnNewSuggestions) {
- Category category = FromKnownCategory(KnownCategories::DOWNLOADS);
- MockProvider* provider = RegisterProvider(category);
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
provider->FireCategoryStatusChangedWithCurrentStatus(category);
MockServiceObserver observer;
service()->AddObserver(&observer);
@@ -498,7 +450,8 @@ TEST_F(ContentSuggestionsServiceTest,
// Provider starts providing |new_category| without calling
// |OnCategoryStatusChanged|. This is supported for now until further
// reconsideration.
- Category new_category = FromKnownCategory(KnownCategories::ARTICLES);
+ Category new_category =
+ Category::FromKnownCategory(KnownCategories::ARTICLES);
provider->SetProvidedCategories(
std::vector<Category>({category, new_category}));
@@ -523,15 +476,17 @@ TEST_F(ContentSuggestionsServiceTest,
TEST_F(ContentSuggestionsServiceTest,
ShouldRegisterNewCategoryOnCategoryStatusChanged) {
- Category category = FromKnownCategory(KnownCategories::DOWNLOADS);
- MockProvider* provider = RegisterProvider(category);
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
provider->FireCategoryStatusChangedWithCurrentStatus(category);
MockServiceObserver observer;
service()->AddObserver(&observer);
// Provider starts providing |new_category| and calls
// |OnCategoryStatusChanged|, but the category is not yet available.
- Category new_category = FromKnownCategory(KnownCategories::ARTICLES);
+ Category new_category =
+ Category::FromKnownCategory(KnownCategories::ARTICLES);
provider->SetProvidedCategories(
std::vector<Category>({category, new_category}));
EXPECT_CALL(observer, OnCategoryStatusChanged(new_category,
@@ -545,14 +500,15 @@ TEST_F(ContentSuggestionsServiceTest,
EXPECT_THAT(service()->GetCategoryStatus(new_category),
Eq(CategoryStatus::INITIALIZING));
EXPECT_THAT(service()->GetCategories(),
- Eq(std::vector<Category>({category, new_category})));
+ UnorderedElementsAre(category, new_category));
service()->RemoveObserver(&observer);
}
TEST_F(ContentSuggestionsServiceTest, ShouldRemoveCategoryWhenNotProvided) {
- Category category = FromKnownCategory(KnownCategories::DOWNLOADS);
- MockProvider* provider = RegisterProvider(category);
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
MockServiceObserver observer;
service()->AddObserver(&observer);
@@ -572,20 +528,117 @@ TEST_F(ContentSuggestionsServiceTest, ShouldRemoveCategoryWhenNotProvided) {
service()->RemoveObserver(&observer);
}
-TEST_F(ContentSuggestionsServiceTest, ShouldForwardClearHistory) {
- Category category = FromKnownCategory(KnownCategories::DOWNLOADS);
- MockProvider* provider = RegisterProvider(category);
+TEST_F(ContentSuggestionsServiceTest, ShouldForwardClearHistoryToProviders) {
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
+ base::Time begin = base::Time::FromTimeT(123);
+ base::Time end = base::Time::FromTimeT(456);
+ EXPECT_CALL(*provider, ClearHistory(begin, end, _));
+ base::Callback<bool(const GURL& url)> filter;
+ service()->ClearHistory(begin, end, filter);
+}
+
+TEST_F(ContentSuggestionsServiceTest,
+ ShouldForwardClearHistoryToCategoryRanker) {
+ auto mock_ranker = base::MakeUnique<MockCategoryRanker>();
+ MockCategoryRanker* raw_mock_ranker = mock_ranker.get();
+ SetCategoryRanker(std::move(mock_ranker));
+
+ // The service is recreated to pick up the new ranker.
+ ResetService();
+
base::Time begin = base::Time::FromTimeT(123),
end = base::Time::FromTimeT(456);
- EXPECT_CALL(*provider, ClearHistory(begin, end, _));
+ EXPECT_CALL(*raw_mock_ranker, ClearHistory(begin, end));
base::Callback<bool(const GURL& url)> filter;
service()->ClearHistory(begin, end, filter);
}
+TEST_F(ContentSuggestionsServiceTest, ShouldForwardDeleteAllHistoryURLs) {
+ StrictMock<MockServiceObserver> observer;
+ service()->AddObserver(&observer);
+ EXPECT_CALL(observer, OnFullRefreshRequired());
+
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
+ EXPECT_CALL(*provider, ClearHistory(base::Time(), base::Time::Max(), _));
+ static_cast<history::HistoryServiceObserver*>(service())->OnURLsDeleted(
+ /*history_service=*/nullptr, /*all_history=*/true, /*expired=*/false,
+ history::URLRows(), /*favicon_urls=*/std::set<GURL>());
+
+ service()->RemoveObserver(&observer);
+}
+
+TEST_F(ContentSuggestionsServiceTest, ShouldIgnoreExpiredURLDeletions) {
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MakeRegisteredMockProvider(category);
+ // Create an observer to make sure OnFullRefresh() does not get triggered.
+ StrictMock<MockServiceObserver> observer;
+ service()->AddObserver(&observer);
+
+ static_cast<history::HistoryServiceObserver*>(service())->OnURLsDeleted(
+ /*history_service=*/nullptr, /*all_history=*/true,
+ /*expired=*/true, history::URLRows(),
+ /*favicon_urls=*/std::set<GURL>());
+ static_cast<history::HistoryServiceObserver*>(service())->OnURLsDeleted(
+ /*history_service=*/nullptr, /*all_history=*/false,
+ /*expired=*/true,
+ history::URLRows({history::URLRow(GURL("http://google.com")),
+ history::URLRow(GURL("http://maps.google.com"))}),
+ /*favicon_urls=*/std::set<GURL>());
+
+ service()->RemoveObserver(&observer);
+}
+
+TEST_F(ContentSuggestionsServiceTest, ShouldIgnoreEmptyURLDeletions) {
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MakeRegisteredMockProvider(category);
+ static_cast<history::HistoryServiceObserver*>(service())->OnURLsDeleted(
+ /*history_service=*/nullptr, /*all_history=*/false,
+ /*expired=*/false, history::URLRows(),
+ /*favicon_urls=*/std::set<GURL>());
+}
+
+TEST_F(ContentSuggestionsServiceTest,
+ ShouldNotClearHistoryOnSingleURLDeletion) {
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MakeRegisteredMockProvider(category);
+ // Single URLs should not trigger
+ static_cast<history::HistoryServiceObserver*>(service())->OnURLsDeleted(
+ /*history_service=*/nullptr, /*all_history=*/false,
+ /*expired=*/false,
+ history::URLRows({history::URLRow(GURL("http://google.com"))}),
+ /*favicon_urls=*/std::set<GURL>());
+}
+
+TEST_F(ContentSuggestionsServiceTest,
+ ShouldClearHistoryForMultipleURLDeletion) {
+ StrictMock<MockServiceObserver> observer;
+ service()->AddObserver(&observer);
+ EXPECT_CALL(observer, OnFullRefreshRequired());
+
+ Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
+ EXPECT_CALL(*provider, ClearHistory(base::Time(), base::Time::Max(), _));
+
+ static_cast<history::HistoryServiceObserver*>(service())->OnURLsDeleted(
+ /*history_service=*/nullptr, /*all_history=*/false,
+ /*expired=*/false,
+ history::URLRows({history::URLRow(GURL("http://google.com")),
+ history::URLRow(GURL("http://youtube.com"))}),
+ /*favicon_urls=*/std::set<GURL>());
+
+ service()->RemoveObserver(&observer);
+}
+
TEST_F(ContentSuggestionsServiceTest, ShouldForwardFetch) {
- Category category = FromKnownCategory(KnownCategories::ARTICLES);
+ Category category = Category::FromKnownCategory(KnownCategories::ARTICLES);
std::set<std::string> known_suggestions;
- MockProvider* provider = RegisterProvider(category);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
provider->FireCategoryStatusChangedWithCurrentStatus(category);
EXPECT_CALL(*provider, Fetch(category, known_suggestions, _));
service()->Fetch(category, known_suggestions, FetchDoneCallback());
@@ -593,12 +646,13 @@ TEST_F(ContentSuggestionsServiceTest, ShouldForwardFetch) {
TEST_F(ContentSuggestionsServiceTest, DismissAndRestoreCategory) {
// Register a category with one suggestion.
- Category category = FromKnownCategory(KnownCategories::ARTICLES);
- MockProvider* provider = RegisterProvider(category);
+ Category category = Category::FromKnownCategory(KnownCategories::ARTICLES);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
provider->FireCategoryStatusChangedWithCurrentStatus(category);
provider->FireSuggestionsChanged(category, CreateSuggestions(category, {42}));
- EXPECT_THAT(service()->GetCategories(), ElementsAre(category));
+ EXPECT_THAT(service()->GetCategories(), UnorderedElementsAre(category));
EXPECT_THAT(service()->GetCategoryStatus(category),
Eq(CategoryStatus::AVAILABLE));
ExpectThatSuggestionsAre(category, {42});
@@ -619,7 +673,7 @@ TEST_F(ContentSuggestionsServiceTest, DismissAndRestoreCategory) {
// empty.
service()->RestoreDismissedCategories();
- EXPECT_THAT(service()->GetCategories(), ElementsAre(category));
+ EXPECT_THAT(service()->GetCategories(), UnorderedElementsAre(category));
EXPECT_THAT(service()->GetCategoryStatus(category),
Eq(CategoryStatus::AVAILABLE));
EXPECT_THAT(service()->GetSuggestionsForCategory(category), IsEmpty());
@@ -629,11 +683,12 @@ TEST_F(ContentSuggestionsServiceTest, DismissAndRestoreCategory) {
TEST_F(ContentSuggestionsServiceTest, ShouldRestoreDismissedCategories) {
// Create and register provider.
- Category category1 = service()->category_factory()->FromIDValue(1);
- Category category2 = service()->category_factory()->FromIDValue(2);
+ Category category1 = Category::FromIDValue(1);
+ Category category2 = Category::FromIDValue(2);
// Setup and verify initial state.
- MockProvider* provider = RegisterProvider({category1, category2});
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider({category1, category2});
provider->FireCategoryStatusChangedWithCurrentStatus(category1);
provider->FireCategoryStatusChangedWithCurrentStatus(category2);
@@ -679,8 +734,9 @@ TEST_F(ContentSuggestionsServiceTest, ShouldRestoreDismissedCategories) {
TEST_F(ContentSuggestionsServiceTest, ShouldRestoreDismissalsFromPrefs) {
// Register a category with one suggestion.
- Category category = FromKnownCategory(KnownCategories::ARTICLES);
- MockProvider* provider = RegisterProvider(category);
+ Category category = Category::FromKnownCategory(KnownCategories::ARTICLES);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
provider->FireCategoryStatusChangedWithCurrentStatus(category);
// For a regular initialisation, the category is not dismissed.
@@ -696,7 +752,7 @@ TEST_F(ContentSuggestionsServiceTest, ShouldRestoreDismissalsFromPrefs) {
// Ensure that the provider registered at initialisation is used after
// restoration.
- provider = RegisterProvider(category);
+ provider = MakeRegisteredMockProvider(category);
provider->FireCategoryStatusChangedWithCurrentStatus(category);
EXPECT_TRUE(service()->IsCategoryDismissed(category));
@@ -705,4 +761,51 @@ TEST_F(ContentSuggestionsServiceTest, ShouldRestoreDismissalsFromPrefs) {
EXPECT_THAT(providers().find(category)->second, Eq(provider));
}
+TEST_F(ContentSuggestionsServiceTest, ShouldReturnCategoriesInOrderToDisplay) {
+ const Category first_category = Category::FromRemoteCategory(1);
+ const Category second_category = Category::FromRemoteCategory(2);
+
+ auto fake_ranker = base::MakeUnique<FakeCategoryRanker>();
+ FakeCategoryRanker* raw_fake_ranker = fake_ranker.get();
+ SetCategoryRanker(std::move(fake_ranker));
+
+ raw_fake_ranker->SetOrder({first_category, second_category});
+
+ // The service is recreated to pick up the new ranker.
+ ResetService();
+
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider({first_category, second_category});
+ provider->FireCategoryStatusChangedWithCurrentStatus(first_category);
+ provider->FireCategoryStatusChangedWithCurrentStatus(second_category);
+
+ EXPECT_THAT(service()->GetCategories(),
+ ElementsAre(first_category, second_category));
+
+ // The order to display (in the ranker) changes.
+ raw_fake_ranker->SetOrder({second_category, first_category});
+
+ // Categories order should reflect the new order.
+ EXPECT_THAT(service()->GetCategories(),
+ ElementsAre(second_category, first_category));
+}
+
+TEST_F(ContentSuggestionsServiceTest,
+ ShouldForwardDismissedCategoryToCategoryRanker) {
+ auto mock_ranker = base::MakeUnique<MockCategoryRanker>();
+ MockCategoryRanker* raw_mock_ranker = mock_ranker.get();
+ SetCategoryRanker(std::move(mock_ranker));
+
+ // The service is recreated to pick up the new ranker.
+ ResetService();
+
+ Category category = Category::FromKnownCategory(KnownCategories::BOOKMARKS);
+ MockContentSuggestionsProvider* provider =
+ MakeRegisteredMockProvider(category);
+ provider->FireCategoryStatusChangedWithCurrentStatus(category);
+
+ EXPECT_CALL(*raw_mock_ranker, OnCategoryDismissed(category));
+ service()->DismissCategory(category);
+}
+
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/fake_content_suggestions_provider_observer.cc b/chromium/components/ntp_snippets/fake_content_suggestions_provider_observer.cc
new file mode 100644
index 00000000000..977f45a11ee
--- /dev/null
+++ b/chromium/components/ntp_snippets/fake_content_suggestions_provider_observer.cc
@@ -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.
+
+#include "components/ntp_snippets/fake_content_suggestions_provider_observer.h"
+
+#include <utility>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp_snippets {
+
+using testing::Eq;
+using testing::Not;
+
+FakeContentSuggestionsProviderObserver::
+ FakeContentSuggestionsProviderObserver() = default;
+
+FakeContentSuggestionsProviderObserver::
+ ~FakeContentSuggestionsProviderObserver() = default;
+
+void FakeContentSuggestionsProviderObserver::OnNewSuggestions(
+ ContentSuggestionsProvider* provider,
+ Category category,
+ std::vector<ContentSuggestion> suggestions) {
+ suggestions_[category] = std::move(suggestions);
+}
+
+void FakeContentSuggestionsProviderObserver::OnCategoryStatusChanged(
+ ContentSuggestionsProvider* provider,
+ Category category,
+ CategoryStatus new_status) {
+ statuses_[category] = new_status;
+}
+
+void FakeContentSuggestionsProviderObserver::OnSuggestionInvalidated(
+ ContentSuggestionsProvider* provider,
+ const ContentSuggestion::ID& suggestion_id) {
+ FAIL() << "not implemented.";
+}
+
+const std::map<Category, CategoryStatus, Category::CompareByID>&
+FakeContentSuggestionsProviderObserver::statuses() const {
+ return statuses_;
+}
+
+CategoryStatus FakeContentSuggestionsProviderObserver::StatusForCategory(
+ Category category) const {
+ auto it = statuses_.find(category);
+ EXPECT_THAT(it, Not(Eq(statuses_.end())));
+ return it->second;
+}
+
+const std::vector<ContentSuggestion>&
+FakeContentSuggestionsProviderObserver::SuggestionsForCategory(
+ Category category) {
+ return suggestions_[category];
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/fake_content_suggestions_provider_observer.h b/chromium/components/ntp_snippets/fake_content_suggestions_provider_observer.h
new file mode 100644
index 00000000000..60ff2bf48a6
--- /dev/null
+++ b/chromium/components/ntp_snippets/fake_content_suggestions_provider_observer.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_NTP_SNIPPETS_FAKE_CONTENT_SUGGESTIONS_PROVIDER_OBSERVER_H_
+#define COMPONENTS_NTP_SNIPPETS_FAKE_CONTENT_SUGGESTIONS_PROVIDER_OBSERVER_H_
+
+#include <map>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/content_suggestion.h"
+#include "components/ntp_snippets/content_suggestions_provider.h"
+
+namespace ntp_snippets {
+
+class FakeContentSuggestionsProviderObserver
+ : public ContentSuggestionsProvider::Observer {
+ public:
+ FakeContentSuggestionsProviderObserver();
+ ~FakeContentSuggestionsProviderObserver();
+
+ void OnNewSuggestions(ContentSuggestionsProvider* provider,
+ Category category,
+ std::vector<ContentSuggestion> suggestions) override;
+
+ void OnCategoryStatusChanged(ContentSuggestionsProvider* provider,
+ Category category,
+ CategoryStatus new_status) override;
+
+ void OnSuggestionInvalidated(
+ ContentSuggestionsProvider* provider,
+ const ContentSuggestion::ID& suggestion_id) override;
+
+ const std::map<Category, CategoryStatus, Category::CompareByID>& statuses()
+ const;
+
+ CategoryStatus StatusForCategory(Category category) const;
+
+ const std::vector<ContentSuggestion>& SuggestionsForCategory(
+ Category category);
+
+ private:
+ std::map<Category, CategoryStatus, Category::CompareByID> statuses_;
+ std::map<Category, std::vector<ContentSuggestion>, Category::CompareByID>
+ suggestions_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeContentSuggestionsProviderObserver);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_FAKE_CONTENT_SUGGESTIONS_PROVIDER_OBSERVER_H_
diff --git a/chromium/components/ntp_snippets/features.cc b/chromium/components/ntp_snippets/features.cc
index e6fbd13a028..4128b136337 100644
--- a/chromium/components/ntp_snippets/features.cc
+++ b/chromium/components/ntp_snippets/features.cc
@@ -4,7 +4,10 @@
#include "components/ntp_snippets/features.h"
-#include "base/strings/string_number_conversions.h"
+#include "base/memory/ptr_util.h"
+#include "base/time/clock.h"
+#include "components/ntp_snippets/category_rankers/click_based_category_ranker.h"
+#include "components/ntp_snippets/category_rankers/constant_category_ranker.h"
#include "components/variations/variations_associated_data.h"
namespace ntp_snippets {
@@ -42,44 +45,52 @@ const base::Feature kForeignSessionsSuggestionsFeature{
const base::Feature kFetchMoreFeature{"NTPSuggestionsFetchMore",
base::FEATURE_ENABLED_BY_DEFAULT};
-int GetParamAsInt(const base::Feature& feature,
- const std::string& param_name,
- const int default_value) {
- std::string value_as_string =
- variations::GetVariationParamValueByFeature(feature, param_name);
- int value_as_int = 0;
- if (!base::StringToInt(value_as_string, &value_as_int)) {
- if (!value_as_string.empty()) {
- LOG(WARNING) << "Failed to parse variation param " << param_name
- << " with string value " << value_as_string
- << " under feature " << feature.name
- << " into an int. Falling back to default value of "
- << default_value;
- }
- value_as_int = default_value;
+const base::Feature kPreferAmpUrlsFeature{"NTPPreferAmpUrls",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kCategoryRanker{"ContentSuggestionsCategoryRanker",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+const char kCategoryRankerParameter[] = "category_ranker";
+const char kCategoryRankerConstantRanker[] = "constant";
+const char kCategoryRankerClickBasedRanker[] = "click_based";
+
+CategoryRankerChoice GetSelectedCategoryRanker() {
+ std::string category_ranker_value =
+ variations::GetVariationParamValueByFeature(kCategoryRanker,
+ kCategoryRankerParameter);
+
+ if (category_ranker_value.empty()) {
+ // Default, Enabled or Disabled.
+ return CategoryRankerChoice::CONSTANT;
+ }
+ if (category_ranker_value == kCategoryRankerConstantRanker) {
+ return CategoryRankerChoice::CONSTANT;
+ }
+ if (category_ranker_value == kCategoryRankerClickBasedRanker) {
+ return CategoryRankerChoice::CLICK_BASED;
}
- return value_as_int;
-}
+ NOTREACHED() << "The " << kCategoryRankerParameter << " parameter value is '"
+ << category_ranker_value << "'";
+ return CategoryRankerChoice::CONSTANT;
+}
-bool GetParamAsBool(const base::Feature& feature,
- const std::string& param_name,
- bool default_value) {
- std::string value_as_string =
- variations::GetVariationParamValueByFeature(feature, param_name);
- if (value_as_string == "true")
- return true;
- if (value_as_string == "false")
- return false;
-
- if (!value_as_string.empty()) {
- LOG(WARNING) << "Failed to parse variation param " << param_name
- << " with string value " << value_as_string
- << " under feature " << feature.name
- << " into a bool. Falling back to default value of "
- << default_value;
+std::unique_ptr<CategoryRanker> BuildSelectedCategoryRanker(
+ PrefService* pref_service,
+ std::unique_ptr<base::Clock> clock) {
+ CategoryRankerChoice choice = ntp_snippets::GetSelectedCategoryRanker();
+ switch (choice) {
+ case CategoryRankerChoice::CONSTANT:
+ return base::MakeUnique<ConstantCategoryRanker>();
+ case CategoryRankerChoice::CLICK_BASED:
+ return base::MakeUnique<ClickBasedCategoryRanker>(pref_service,
+ std::move(clock));
+ default:
+ NOTREACHED() << "The category ranker choice value is "
+ << static_cast<int>(choice);
}
- return default_value;
+ return nullptr;
}
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/features.h b/chromium/components/ntp_snippets/features.h
index 25a3a295aa3..6ae8eabbefc 100644
--- a/chromium/components/ntp_snippets/features.h
+++ b/chromium/components/ntp_snippets/features.h
@@ -5,9 +5,16 @@
#ifndef COMPONENTS_NTP_SNIPPETS_FEATURES_H_
#define COMPONENTS_NTP_SNIPPETS_FEATURES_H_
+#include <memory>
#include <string>
#include "base/feature_list.h"
+#include "components/ntp_snippets/category_rankers/category_ranker.h"
+#include "components/prefs/pref_service.h"
+
+namespace base {
+class Clock;
+}
namespace ntp_snippets {
@@ -38,16 +45,30 @@ extern const base::Feature kIncreasedVisibility;
// Feature to enable the Fetch More action
extern const base::Feature kFetchMoreFeature;
-// Returns a feature param as an int instead of a string.
-int GetParamAsInt(const base::Feature& feature,
- const std::string& param_name,
- int default_value);
+// Feature to prefer AMP URLs over regular URLs when available.
+extern const base::Feature kPreferAmpUrlsFeature;
+
+// Feature to choose a category ranker.
+extern const base::Feature kCategoryRanker;
+
+// Parameter for a kCategoryRanker feature flag.
+extern const char kCategoryRankerParameter[];
+// Possible values of the parameter above.
+extern const char kCategoryRankerConstantRanker[];
+extern const char kCategoryRankerClickBasedRanker[];
+
+enum class CategoryRankerChoice {
+ CONSTANT,
+ CLICK_BASED,
+};
+
+// Returns which CategoryRanker to use according to kCategoryRanker feature.
+CategoryRankerChoice GetSelectedCategoryRanker();
-// Returns a feature param as a bool instead of a string.
-// TODO(jkrcal): Use this function in other code in the ntp_snippets component.
-bool GetParamAsBool(const base::Feature& feature,
- const std::string& param_name,
- bool default_value);
+// Builds a CategoryRanker according to kCategoryRanker feature.
+std::unique_ptr<CategoryRanker> BuildSelectedCategoryRanker(
+ PrefService* pref_service,
+ std::unique_ptr<base::Clock> clock);
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/mock_content_suggestions_provider.cc b/chromium/components/ntp_snippets/mock_content_suggestions_provider.cc
new file mode 100644
index 00000000000..02c99b65a27
--- /dev/null
+++ b/chromium/components/ntp_snippets/mock_content_suggestions_provider.cc
@@ -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.
+
+#include "components/ntp_snippets/mock_content_suggestions_provider.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace ntp_snippets {
+
+MockContentSuggestionsProvider::MockContentSuggestionsProvider(
+ Observer* observer,
+ const std::vector<Category>& provided_categories)
+ : ContentSuggestionsProvider(observer) {
+ SetProvidedCategories(provided_categories);
+}
+
+MockContentSuggestionsProvider::~MockContentSuggestionsProvider() {}
+
+void MockContentSuggestionsProvider::SetProvidedCategories(
+ const std::vector<Category>& provided_categories) {
+ statuses_.clear();
+ provided_categories_ = provided_categories;
+ for (Category category : provided_categories) {
+ statuses_[category.id()] = CategoryStatus::AVAILABLE;
+ }
+}
+
+CategoryStatus MockContentSuggestionsProvider::GetCategoryStatus(
+ Category category) {
+ return statuses_[category.id()];
+}
+
+CategoryInfo MockContentSuggestionsProvider::GetCategoryInfo(
+ Category category) {
+ return CategoryInfo(base::ASCIIToUTF16("Section title"),
+ ContentSuggestionsCardLayout::FULL_CARD, true, false,
+ true, false,
+ base::ASCIIToUTF16("No suggestions message"));
+}
+
+void MockContentSuggestionsProvider::FireSuggestionsChanged(
+ Category category,
+ std::vector<ContentSuggestion> suggestions) {
+ observer()->OnNewSuggestions(this, category, std::move(suggestions));
+}
+
+void MockContentSuggestionsProvider::FireCategoryStatusChanged(
+ Category category,
+ CategoryStatus new_status) {
+ statuses_[category.id()] = new_status;
+ observer()->OnCategoryStatusChanged(this, category, new_status);
+}
+
+void MockContentSuggestionsProvider::FireCategoryStatusChangedWithCurrentStatus(
+ Category category) {
+ observer()->OnCategoryStatusChanged(this, category, statuses_[category.id()]);
+}
+
+void MockContentSuggestionsProvider::FireSuggestionInvalidated(
+ const ContentSuggestion::ID& suggestion_id) {
+ observer()->OnSuggestionInvalidated(this, suggestion_id);
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/mock_content_suggestions_provider.h b/chromium/components/ntp_snippets/mock_content_suggestions_provider.h
new file mode 100644
index 00000000000..17cfeadfb81
--- /dev/null
+++ b/chromium/components/ntp_snippets/mock_content_suggestions_provider.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_NTP_SNIPPETS_MOCK_CONTENT_SUGGESTIONS_PROVIDER_H_
+#define COMPONENTS_NTP_SNIPPETS_MOCK_CONTENT_SUGGESTIONS_PROVIDER_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/ntp_snippets/content_suggestions_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace ntp_snippets {
+
+// TODO(treib): This is a weird combination of a mock and a fake. Fix this.
+class MockContentSuggestionsProvider : public ContentSuggestionsProvider {
+ public:
+ MockContentSuggestionsProvider(
+ Observer* observer,
+ const std::vector<Category>& provided_categories);
+ ~MockContentSuggestionsProvider();
+
+ void SetProvidedCategories(const std::vector<Category>& provided_categories);
+
+ // Returns the status for |category|. The initial status in
+ // CatgoryStatus::AVAILABLE. Will be updated on FireCategoryStatusChanged
+ // events.
+ CategoryStatus GetCategoryStatus(Category category) override;
+
+ // Returns a hard-coded category info object.
+ CategoryInfo GetCategoryInfo(Category category) override;
+
+ // Forwards events to the underlying oberservers.
+ // TODO(tschumann): This functionality does not belong here. Whoever injected
+ // the observer into the constructor can as well notify the observer itself.
+ void FireSuggestionsChanged(Category category,
+ std::vector<ContentSuggestion> suggestions);
+ void FireCategoryStatusChanged(Category category, CategoryStatus new_status);
+ void FireCategoryStatusChangedWithCurrentStatus(Category category);
+ void FireSuggestionInvalidated(const ContentSuggestion::ID& suggestion_id);
+
+ MOCK_METHOD3(ClearHistory,
+ void(base::Time begin,
+ base::Time end,
+ const base::Callback<bool(const GURL& url)>& filter));
+ MOCK_METHOD3(Fetch,
+ void(const Category&,
+ const std::set<std::string>&,
+ const FetchDoneCallback&));
+ MOCK_METHOD1(ClearCachedSuggestions, void(Category category));
+ MOCK_METHOD2(GetDismissedSuggestionsForDebugging,
+ void(Category category,
+ const DismissedSuggestionsCallback& callback));
+ MOCK_METHOD1(ClearDismissedSuggestionsForDebugging, void(Category category));
+ MOCK_METHOD1(DismissSuggestion,
+ void(const ContentSuggestion::ID& suggestion_id));
+ MOCK_METHOD2(FetchSuggestionImage,
+ void(const ContentSuggestion::ID& suggestion_id,
+ const ImageFetchedCallback& callback));
+
+ private:
+ std::vector<Category> provided_categories_;
+ std::map<int, CategoryStatus> statuses_;
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_MOCK_CONTENT_SUGGESTIONS_PROVIDER_H_
diff --git a/chromium/components/ntp_snippets/offline_pages/offline_pages_test_utils.cc b/chromium/components/ntp_snippets/offline_pages/offline_pages_test_utils.cc
index 428911d6c0d..81d84f73454 100644
--- a/chromium/components/ntp_snippets/offline_pages/offline_pages_test_utils.cc
+++ b/chromium/components/ntp_snippets/offline_pages/offline_pages_test_utils.cc
@@ -24,15 +24,16 @@ FakeOfflinePageModel::FakeOfflinePageModel() {
is_loaded_ = true;
}
-FakeOfflinePageModel::~FakeOfflinePageModel() {}
+FakeOfflinePageModel::~FakeOfflinePageModel() = default;
void FakeOfflinePageModel::GetPagesMatchingQuery(
std::unique_ptr<offline_pages::OfflinePageModelQuery> query,
const MultipleOfflinePageItemCallback& callback) {
MultipleOfflinePageItemResult filtered_result;
for (auto& item : items_) {
- if (query->Matches(item))
+ if (query->Matches(item)) {
filtered_result.emplace_back(item);
+ }
}
callback.Run(filtered_result);
}
diff --git a/chromium/components/ntp_snippets/offline_pages/offline_pages_test_utils.h b/chromium/components/ntp_snippets/offline_pages/offline_pages_test_utils.h
index 56f71a314c4..caa4f9c689f 100644
--- a/chromium/components/ntp_snippets/offline_pages/offline_pages_test_utils.h
+++ b/chromium/components/ntp_snippets/offline_pages/offline_pages_test_utils.h
@@ -6,8 +6,8 @@
#define COMPONENTS_NTP_SNIPPETS_OFFLINE_PAGES_OFFLINE_PAGES_TEST_UTILS_H_
#include "components/ntp_snippets/content_suggestion.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/stub_offline_page_model.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
namespace ntp_snippets {
namespace test {
diff --git a/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc b/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc
index eea6875456b..760785e7568 100644
--- a/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc
+++ b/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc
@@ -12,13 +12,15 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "components/ntp_snippets/features.h"
#include "components/ntp_snippets/pref_names.h"
#include "components/ntp_snippets/pref_util.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model_query.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model_query.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
+#include "components/variations/variations_associated_data.h"
#include "grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image.h"
@@ -32,12 +34,30 @@ namespace ntp_snippets {
namespace {
-const int kMaxSuggestionsCount = 5;
+const int kDefaultMaxSuggestionsCount = 5;
-struct OrderOfflinePagesByMostRecentlyVisitedFirst {
+const char* kMaxSuggestionsCountParamName = "recent_tabs_max_count";
+
+int GetMaxSuggestionsCount() {
+ return variations::GetVariationParamByFeatureAsInt(
+ kRecentOfflineTabSuggestionsFeature, kMaxSuggestionsCountParamName,
+ kDefaultMaxSuggestionsCount);
+}
+
+struct OrderOfflinePagesByMostRecentlyCreatedFirst {
+ bool operator()(const OfflinePageItem* left,
+ const OfflinePageItem* right) const {
+ return left->creation_time > right->creation_time;
+ }
+};
+
+struct OrderOfflinePagesByUrlAndThenMostRecentlyCreatedFirst {
bool operator()(const OfflinePageItem* left,
const OfflinePageItem* right) const {
- return left->last_access_time > right->last_access_time;
+ if (left->url != right->url) {
+ return left->url < right->url;
+ }
+ return left->creation_time > right->creation_time;
}
};
@@ -49,17 +69,22 @@ std::unique_ptr<OfflinePageModelQuery> BuildRecentTabsQuery(
return builder.Build(model->GetPolicyController());
}
+bool IsRecentTab(offline_pages::ClientPolicyController* policy_controller,
+ const OfflinePageItem& offline_page) {
+ return policy_controller->IsShownAsRecentlyVisitedSite(
+ offline_page.client_id.name_space);
+}
+
} // namespace
RecentTabSuggestionsProvider::RecentTabSuggestionsProvider(
ContentSuggestionsProvider::Observer* observer,
- CategoryFactory* category_factory,
offline_pages::OfflinePageModel* offline_page_model,
PrefService* pref_service)
- : ContentSuggestionsProvider(observer, category_factory),
+ : ContentSuggestionsProvider(observer),
category_status_(CategoryStatus::AVAILABLE_LOADING),
provided_category_(
- category_factory->FromKnownCategory(KnownCategories::RECENT_TABS)),
+ Category::FromKnownCategory(KnownCategories::RECENT_TABS)),
offline_page_model_(offline_page_model),
pref_service_(pref_service),
weak_ptr_factory_(this) {
@@ -74,8 +99,9 @@ RecentTabSuggestionsProvider::~RecentTabSuggestionsProvider() {
CategoryStatus RecentTabSuggestionsProvider::GetCategoryStatus(
Category category) {
- if (category == provided_category_)
+ if (category == provided_category_) {
return category_status_;
+ }
NOTREACHED() << "Unknown category " << category.id();
return CategoryStatus::NOT_PROVIDED;
}
@@ -89,9 +115,7 @@ CategoryInfo RecentTabSuggestionsProvider::GetCategoryInfo(Category category) {
/*has_reload_action=*/false,
/*has_view_all_action=*/false,
/*show_if_empty=*/false,
- l10n_util::GetStringUTF16(IDS_NTP_SUGGESTIONS_SECTION_EMPTY));
- // TODO(vitaliii): Replace IDS_NTP_SUGGESTIONS_SECTION_EMPTY with a
- // category-specific string.
+ l10n_util::GetStringUTF16(IDS_NTP_RECENT_TAB_SUGGESTIONS_SECTION_EMPTY));
}
void RecentTabSuggestionsProvider::DismissSuggestion(
@@ -142,9 +166,12 @@ void RecentTabSuggestionsProvider::GetDismissedSuggestionsForDebugging(
const DismissedSuggestionsCallback& callback) {
DCHECK_EQ(provided_category_, category);
- // TODO(vitaliii): Query all pages instead by using an empty query.
+ // Offline pages which are not related to recent tabs are also queried here,
+ // so that they can be returned if they happen to be dismissed (e.g. due to a
+ // bug).
+ OfflinePageModelQueryBuilder query_builder;
offline_page_model_->GetPagesMatchingQuery(
- BuildRecentTabsQuery(offline_page_model_),
+ query_builder.Build(offline_page_model_->GetPolicyController()),
base::Bind(&RecentTabSuggestionsProvider::
GetPagesMatchingQueryCallbackForGetDismissedSuggestions,
weak_ptr_factory_.GetWeakPtr(), callback));
@@ -173,8 +200,9 @@ void RecentTabSuggestionsProvider::
std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs();
std::vector<ContentSuggestion> suggestions;
for (const OfflinePageItem& item : offline_pages) {
- if (!dismissed_ids.count(base::IntToString(item.offline_id)))
+ if (!dismissed_ids.count(base::IntToString(item.offline_id))) {
continue;
+ }
suggestions.push_back(ConvertOfflinePage(item));
}
@@ -184,10 +212,13 @@ void RecentTabSuggestionsProvider::
void RecentTabSuggestionsProvider::OfflinePageModelLoaded(
offline_pages::OfflinePageModel* model) {}
-void RecentTabSuggestionsProvider::OfflinePageModelChanged(
- offline_pages::OfflinePageModel* model) {
+void RecentTabSuggestionsProvider::OfflinePageAdded(
+ offline_pages::OfflinePageModel* model,
+ const offline_pages::OfflinePageItem& added_page) {
DCHECK_EQ(offline_page_model_, model);
- FetchRecentTabs();
+ if (IsRecentTab(model->GetPolicyController(), added_page)) {
+ FetchRecentTabs();
+ }
}
void RecentTabSuggestionsProvider::
@@ -199,17 +230,19 @@ void RecentTabSuggestionsProvider::
std::vector<const OfflinePageItem*> recent_tab_items;
for (const OfflinePageItem& item : offline_pages) {
std::string offline_page_id = base::IntToString(item.offline_id);
- if (old_dismissed_ids.count(offline_page_id))
+ if (old_dismissed_ids.count(offline_page_id)) {
new_dismissed_ids.insert(offline_page_id);
- else
+ } else {
recent_tab_items.push_back(&item);
+ }
}
observer()->OnNewSuggestions(
this, provided_category_,
- GetMostRecentlyVisited(std::move(recent_tab_items)));
- if (new_dismissed_ids.size() != old_dismissed_ids.size())
+ GetMostRecentlyCreatedWithoutDuplicates(std::move(recent_tab_items)));
+ if (new_dismissed_ids.size() != old_dismissed_ids.size()) {
StoreDismissedIDsToPrefs(new_dismissed_ids);
+ }
}
void RecentTabSuggestionsProvider::OfflinePageDeleted(
@@ -218,8 +251,9 @@ void RecentTabSuggestionsProvider::OfflinePageDeleted(
// Because we never switch to NOT_PROVIDED dynamically, there can be no open
// UI containing an invalidated suggestion unless the status is something
// other than NOT_PROVIDED, so only notify invalidation in that case.
- if (category_status_ != CategoryStatus::NOT_PROVIDED)
+ if (category_status_ != CategoryStatus::NOT_PROVIDED) {
InvalidateSuggestion(offline_id);
+ }
}
void RecentTabSuggestionsProvider::FetchRecentTabs() {
@@ -233,8 +267,9 @@ void RecentTabSuggestionsProvider::FetchRecentTabs() {
void RecentTabSuggestionsProvider::NotifyStatusChanged(
CategoryStatus new_status) {
DCHECK_NE(CategoryStatus::NOT_PROVIDED, category_status_);
- if (category_status_ == new_status)
+ if (category_status_ == new_status) {
return;
+ }
category_status_ = new_status;
observer()->OnCategoryStatusChanged(this, provided_category_, new_status);
}
@@ -263,15 +298,27 @@ ContentSuggestion RecentTabSuggestionsProvider::ConvertOfflinePage(
}
std::vector<ContentSuggestion>
-RecentTabSuggestionsProvider::GetMostRecentlyVisited(
+RecentTabSuggestionsProvider::GetMostRecentlyCreatedWithoutDuplicates(
std::vector<const OfflinePageItem*> offline_page_items) const {
+ // |std::unique| only removes duplicates that immediately follow each other.
+ // Thus, first, we have to sort by URL and creation time and only then remove
+ // duplicates and sort the remaining items by creation time.
+ std::sort(offline_page_items.begin(), offline_page_items.end(),
+ OrderOfflinePagesByUrlAndThenMostRecentlyCreatedFirst());
+ std::vector<const OfflinePageItem*>::iterator new_end = std::unique(
+ offline_page_items.begin(), offline_page_items.end(),
+ [](const OfflinePageItem* left, const OfflinePageItem* right) {
+ return left->url == right->url;
+ });
+ offline_page_items.erase(new_end, offline_page_items.end());
std::sort(offline_page_items.begin(), offline_page_items.end(),
- OrderOfflinePagesByMostRecentlyVisitedFirst());
+ OrderOfflinePagesByMostRecentlyCreatedFirst());
std::vector<ContentSuggestion> suggestions;
for (const OfflinePageItem* offline_page_item : offline_page_items) {
suggestions.push_back(ConvertOfflinePage(*offline_page_item));
- if (suggestions.size() == kMaxSuggestionsCount)
+ if (static_cast<int>(suggestions.size()) == GetMaxSuggestionsCount()) {
break;
+ }
}
return suggestions;
}
diff --git a/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h b/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h
index 7d57fba3ac2..6a4e71dc31e 100644
--- a/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h
+++ b/chromium/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h
@@ -14,30 +14,23 @@
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/ntp_snippets/category.h"
-#include "components/ntp_snippets/category_factory.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/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_model.h"
class PrefRegistrySimple;
class PrefService;
-namespace gfx {
-class Image;
-} // namespace gfx
-
namespace ntp_snippets {
-// Provides recent tabs content suggestions from the offline pages model
-// obtaining the data through OfflinePageProxy.
+// Provides recent tabs content suggestions from the offline pages model.
class RecentTabSuggestionsProvider
: public ContentSuggestionsProvider,
public offline_pages::OfflinePageModel::Observer {
public:
RecentTabSuggestionsProvider(
ContentSuggestionsProvider::Observer* observer,
- CategoryFactory* category_factory,
offline_pages::OfflinePageModel* offline_page_model,
PrefService* pref_service);
~RecentTabSuggestionsProvider() override;
@@ -72,7 +65,9 @@ class RecentTabSuggestionsProvider
// OfflinePageModel::Observer implementation.
void OfflinePageModelLoaded(offline_pages::OfflinePageModel* model) override;
- void OfflinePageModelChanged(offline_pages::OfflinePageModel* model) override;
+ void OfflinePageAdded(
+ offline_pages::OfflinePageModel* model,
+ const offline_pages::OfflinePageItem& added_page) override;
void OfflinePageDeleted(int64_t offline_id,
const offline_pages::ClientId& client_id) override;
@@ -91,15 +86,16 @@ class RecentTabSuggestionsProvider
ContentSuggestion ConvertOfflinePage(
const offline_pages::OfflinePageItem& offline_page) const;
- // Gets the |kMaxSuggestionsCount| most recently visited OfflinePageItems from
- // the list, orders them by last visit date and converts them to
- // ContentSuggestions for the |provided_category_|.
- std::vector<ContentSuggestion> GetMostRecentlyVisited(
+ // Removes duplicates for the same URL leaving only the most recently created
+ // items, returns at most |GetMaxSuggestionsCount()| ContentSuggestions
+ // corresponding to the remaining items, sorted by creation time (newer
+ // first).
+ std::vector<ContentSuggestion> GetMostRecentlyCreatedWithoutDuplicates(
std::vector<const offline_pages::OfflinePageItem*> offline_page_items)
const;
// Fires the |OnSuggestionInvalidated| event for the suggestion corresponding
- // to the given |offline_id| and clears it from the dismissed IDs list, if
+ // to the given |offline_id| and deletes it from the dismissed IDs list, if
// necessary.
void InvalidateSuggestion(int64_t offline_id);
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 3eb6b1cd47d..862b24bfecf 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
@@ -12,13 +12,12 @@
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "components/ntp_snippets/category.h"
-#include "components/ntp_snippets/category_factory.h"
#include "components/ntp_snippets/content_suggestions_provider.h"
#include "components/ntp_snippets/mock_content_suggestions_provider_observer.h"
#include "components/ntp_snippets/offline_pages/offline_pages_test_utils.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/stub_offline_page_model.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -54,6 +53,7 @@ std::vector<OfflinePageItem> CreateDummyRecentTabs(
OfflinePageItem CreateDummyRecentTab(int id, base::Time time) {
OfflinePageItem item = CreateDummyRecentTab(id);
+ item.creation_time = time;
item.last_access_time = time;
return item;
}
@@ -67,24 +67,29 @@ class RecentTabSuggestionsProviderTest : public testing::Test {
RecentTabSuggestionsProvider::RegisterProfilePrefs(
pref_service()->registry());
- provider_.reset(new RecentTabSuggestionsProvider(
- &observer_, &category_factory_, &model_, pref_service()));
+ provider_.reset(
+ new RecentTabSuggestionsProvider(&observer_, &model_, pref_service()));
}
Category recent_tabs_category() {
- return category_factory_.FromKnownCategory(KnownCategories::RECENT_TABS);
+ return Category::FromKnownCategory(KnownCategories::RECENT_TABS);
}
ContentSuggestion::ID GetDummySuggestionId(int id) {
return ContentSuggestion::ID(recent_tabs_category(), base::IntToString(id));
}
- void FireOfflinePageModelChanged(const std::vector<OfflinePageItem>& items) {
- *(model_.mutable_items()) = items;
- provider_->OfflinePageModelChanged(&model_);
+ void AddOfflinePageToModel(const OfflinePageItem& item) {
+ model_.mutable_items()->push_back(item);
+ provider_->OfflinePageAdded(&model_, item);
}
void FireOfflinePageDeleted(const OfflinePageItem& item) {
+ auto iter = std::remove(model_.mutable_items()->begin(),
+ model_.mutable_items()->end(), item);
+ auto end = model_.mutable_items()->end();
+ model_.mutable_items()->erase(iter, end);
+
provider_->OfflinePageDeleted(item.offline_id, item.client_id);
}
@@ -100,7 +105,6 @@ class RecentTabSuggestionsProviderTest : public testing::Test {
private:
FakeOfflinePageModel model_;
MockContentSuggestionsProviderObserver observer_;
- CategoryFactory category_factory_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
// Last so that the dependencies are deleted after the provider.
std::unique_ptr<RecentTabSuggestionsProvider> provider_;
@@ -109,6 +113,11 @@ class RecentTabSuggestionsProviderTest : public testing::Test {
};
TEST_F(RecentTabSuggestionsProviderTest, ShouldConvertToSuggestions) {
+ auto recent_tabs_list = CreateDummyRecentTabs({1, 2});
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(2);
+ for (OfflinePageItem& recent_tab : recent_tabs_list)
+ AddOfflinePageToModel(recent_tab);
+
EXPECT_CALL(
*observer(),
OnNewSuggestions(_, recent_tabs_category(),
@@ -119,19 +128,39 @@ TEST_F(RecentTabSuggestionsProviderTest, ShouldConvertToSuggestions) {
GURL("http://dummy.com/2")),
Property(&ContentSuggestion::url,
GURL("http://dummy.com/3")))));
- FireOfflinePageModelChanged(CreateDummyRecentTabs({1, 2, 3}));
+ AddOfflinePageToModel(CreateDummyRecentTab(3));
}
-TEST_F(RecentTabSuggestionsProviderTest, ShouldSortByMostRecentlyVisited) {
+TEST_F(RecentTabSuggestionsProviderTest, ShouldSortByCreationTime) {
base::Time now = base::Time::Now();
base::Time yesterday = now - base::TimeDelta::FromDays(1);
base::Time tomorrow = now + base::TimeDelta::FromDays(1);
+
std::vector<OfflinePageItem> offline_pages = {
CreateDummyRecentTab(1, now), CreateDummyRecentTab(2, yesterday),
CreateDummyRecentTab(3, tomorrow)};
EXPECT_CALL(
*observer(),
+ OnNewSuggestions(_, recent_tabs_category(),
+ ElementsAre(Property(&ContentSuggestion::url,
+ GURL("http://dummy.com/1")))));
+ AddOfflinePageToModel(CreateDummyRecentTab(1, now));
+
+ EXPECT_CALL(
+ *observer(),
+ OnNewSuggestions(
+ _, recent_tabs_category(),
+ ElementsAre(
+ Property(&ContentSuggestion::url, GURL("http://dummy.com/1")),
+ Property(&ContentSuggestion::url, GURL("http://dummy.com/2")))));
+ AddOfflinePageToModel(CreateDummyRecentTab(2, yesterday));
+
+ offline_pages[1].last_access_time =
+ offline_pages[0].last_access_time + base::TimeDelta::FromHours(1);
+
+ EXPECT_CALL(
+ *observer(),
OnNewSuggestions(
_, recent_tabs_category(),
ElementsAre(Property(&ContentSuggestion::url,
@@ -140,7 +169,7 @@ TEST_F(RecentTabSuggestionsProviderTest, ShouldSortByMostRecentlyVisited) {
GURL("http://dummy.com/1")),
Property(&ContentSuggestion::url,
GURL("http://dummy.com/2")))));
- FireOfflinePageModelChanged(offline_pages);
+ AddOfflinePageToModel(CreateDummyRecentTab(3, tomorrow));
}
TEST_F(RecentTabSuggestionsProviderTest, ShouldDeliverCorrectCategoryInfo) {
@@ -153,8 +182,14 @@ TEST_F(RecentTabSuggestionsProviderTest, ShouldDeliverCorrectCategoryInfo) {
.has_view_all_action());
}
+// TODO(vitaliii): Break this test into multiple tests. Currently if it fails,
+// it takes long time to find which part of it actually fails.
TEST_F(RecentTabSuggestionsProviderTest, ShouldDismiss) {
- FireOfflinePageModelChanged(CreateDummyRecentTabs({1, 2, 3, 4}));
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3);
+ auto recent_tabs_list = CreateDummyRecentTabs({1, 2, 3});
+ for (OfflinePageItem& recent_tab : recent_tabs_list) {
+ AddOfflinePageToModel(recent_tab);
+ }
// Dismiss 2 and 3.
EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0);
@@ -165,14 +200,13 @@ TEST_F(RecentTabSuggestionsProviderTest, ShouldDismiss) {
// They should disappear from the reported suggestions.
EXPECT_CALL(
*observer(),
- OnNewSuggestions(_, recent_tabs_category(),
- UnorderedElementsAre(
- Property(&ContentSuggestion::url,
- GURL("http://dummy.com/1")),
- Property(&ContentSuggestion::url,
- GURL("http://dummy.com/4")))));
+ OnNewSuggestions(
+ _, recent_tabs_category(),
+ UnorderedElementsAre(
+ Property(&ContentSuggestion::url, GURL("http://dummy.com/1")),
+ Property(&ContentSuggestion::url, GURL("http://dummy.com/4")))));
- FireOfflinePageModelChanged(model()->items());
+ AddOfflinePageToModel(CreateDummyRecentTab(4));
Mock::VerifyAndClearExpectations(observer());
// And appear in the dismissed suggestions.
@@ -199,15 +233,17 @@ TEST_F(RecentTabSuggestionsProviderTest, ShouldDismiss) {
// And appear in the reported suggestions for the category again.
EXPECT_CALL(*observer(),
- OnNewSuggestions(_, recent_tabs_category(), SizeIs(4)));
- FireOfflinePageModelChanged(model()->items());
+ OnNewSuggestions(_, recent_tabs_category(), SizeIs(5)));
+ AddOfflinePageToModel(CreateDummyRecentTab(5));
Mock::VerifyAndClearExpectations(observer());
}
TEST_F(RecentTabSuggestionsProviderTest,
ShouldInvalidateWhenOfflinePageDeleted) {
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3);
std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3});
- FireOfflinePageModelChanged(offline_pages);
+ for (OfflinePageItem& recent_tab : offline_pages)
+ AddOfflinePageToModel(recent_tab);
// Invalidation of suggestion 2 should be forwarded.
EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(2)));
@@ -215,8 +251,10 @@ TEST_F(RecentTabSuggestionsProviderTest,
}
TEST_F(RecentTabSuggestionsProviderTest, ShouldClearDismissedOnInvalidate) {
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3);
std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3});
- FireOfflinePageModelChanged(offline_pages);
+ for (OfflinePageItem& recent_tab : offline_pages)
+ AddOfflinePageToModel(recent_tab);
EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty());
provider()->DismissSuggestion(GetDummySuggestionId(2));
@@ -227,17 +265,72 @@ TEST_F(RecentTabSuggestionsProviderTest, ShouldClearDismissedOnInvalidate) {
}
TEST_F(RecentTabSuggestionsProviderTest, ShouldClearDismissedOnFetch) {
- FireOfflinePageModelChanged(CreateDummyRecentTabs({1, 2, 3}));
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3);
+ std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3});
+ for (OfflinePageItem& recent_tab : offline_pages)
+ AddOfflinePageToModel(recent_tab);
provider()->DismissSuggestion(GetDummySuggestionId(2));
provider()->DismissSuggestion(GetDummySuggestionId(3));
EXPECT_THAT(ReadDismissedIDsFromPrefs(), SizeIs(2));
- FireOfflinePageModelChanged(CreateDummyRecentTabs({2}));
+ FireOfflinePageDeleted(offline_pages[0]);
+ FireOfflinePageDeleted(offline_pages[2]);
EXPECT_THAT(ReadDismissedIDsFromPrefs(), SizeIs(1));
- FireOfflinePageModelChanged(std::vector<OfflinePageItem>());
+ FireOfflinePageDeleted(offline_pages[1]);
EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty());
}
+TEST_F(RecentTabSuggestionsProviderTest, ShouldNotShowSameUrlMutlipleTimes) {
+ base::Time now = base::Time::Now();
+ base::Time yesterday = now - base::TimeDelta::FromDays(1);
+ base::Time tomorrow = now + base::TimeDelta::FromDays(1);
+ std::vector<OfflinePageItem> offline_pages = {
+ CreateDummyRecentTab(1, yesterday), CreateDummyRecentTab(2, now),
+ CreateDummyRecentTab(3, tomorrow)};
+
+ // We leave IDs different, but make the URLs the same.
+ offline_pages[2].url = offline_pages[0].url;
+
+ AddOfflinePageToModel(offline_pages[0]);
+ AddOfflinePageToModel(offline_pages[1]);
+ Mock::VerifyAndClearExpectations(observer());
+ EXPECT_CALL(*observer(),
+ OnNewSuggestions(
+ _, recent_tabs_category(),
+ UnorderedElementsAre(
+ Property(&ContentSuggestion::publish_date, now),
+ Property(&ContentSuggestion::publish_date, tomorrow))));
+
+ AddOfflinePageToModel(offline_pages[2]);
+}
+
+TEST_F(RecentTabSuggestionsProviderTest,
+ ShouldNotFetchIfAddedOfflinePageIsNotRecentTab) {
+ // The provider is not notified about the first recent tab yet.
+ model()->mutable_items()->push_back(CreateDummyRecentTab(1));
+ // It should not fetch when not a recent tab is added, thus, it should not
+ // report the first recent tab (which it is not aware about).
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0);
+ AddOfflinePageToModel(ntp_snippets::test::CreateDummyOfflinePageItem(
+ 2, offline_pages::kDefaultNamespace));
+}
+
+TEST_F(RecentTabSuggestionsProviderTest,
+ ShouldFetchIfAddedOfflinePageIsRecentTab) {
+ // The provider is not notified about the first recent tab yet.
+ model()->mutable_items()->push_back(CreateDummyRecentTab(1));
+ // However, it must return the first recent tab (i.e. manually fetch it) even
+ // when notified about a different recent tab.
+ EXPECT_CALL(
+ *observer(),
+ OnNewSuggestions(
+ _, recent_tabs_category(),
+ UnorderedElementsAre(
+ Property(&ContentSuggestion::url, GURL("http://dummy.com/1")),
+ Property(&ContentSuggestion::url, GURL("http://dummy.com/2")))));
+ AddOfflinePageToModel(CreateDummyRecentTab(2));
+}
+
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/physical_web_pages/DEPS b/chromium/components/ntp_snippets/physical_web_pages/DEPS
index 179f98f1617..ea8c01fdba6 100644
--- a/chromium/components/ntp_snippets/physical_web_pages/DEPS
+++ b/chromium/components/ntp_snippets/physical_web_pages/DEPS
@@ -1,2 +1,4 @@
include_rules = [
-] \ No newline at end of file
+ "+components/grit",
+ "+components/physical_web/data_source",
+]
diff --git a/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc b/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc
index dc150909c41..27cfb982005 100644
--- a/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc
+++ b/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.cc
@@ -4,16 +4,29 @@
#include "components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h"
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <string>
#include <utility>
#include "base/bind.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/threading/thread_task_runner_handle.h"
+#include "components/grit/components_scaled_resources.h"
+#include "components/ntp_snippets/pref_names.h"
+#include "components/ntp_snippets/pref_util.h"
+#include "components/physical_web/data_source/physical_web_data_source.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
#include "grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h"
+#include "url/gurl.h"
namespace ntp_snippets {
@@ -21,46 +34,77 @@ namespace {
const size_t kMaxSuggestionsCount = 10;
-} // namespace
+std::string GetPageId(const physical_web::Metadata& page_metadata) {
+ return page_metadata.resolved_url.spec();
+}
-// TODO(vitaliii): remove when Physical Web C++ interface is provided.
-UrlInfo::UrlInfo() = default;
-UrlInfo::~UrlInfo() = default;
-UrlInfo::UrlInfo(const UrlInfo& other) = default;
+bool CompareByDistance(const physical_web::Metadata& left,
+ const physical_web::Metadata& right) {
+ // When there is no estimate, the value is <= 0, so we implicitly treat it as
+ // infinity.
+ bool is_left_estimated = left.distance_estimate > 0;
+ bool is_right_estimated = right.distance_estimate > 0;
-PhysicalWebPageSuggestionsProvider::PhysicalWebPageSuggestionsProvider(
- ContentSuggestionsProvider::Observer* observer,
- CategoryFactory* category_factory)
- : ContentSuggestionsProvider(observer, category_factory),
- category_status_(CategoryStatus::AVAILABLE_LOADING),
- provided_category_(category_factory->FromKnownCategory(
- KnownCategories::PHYSICAL_WEB_PAGES)) {
- observer->OnCategoryStatusChanged(this, provided_category_, category_status_);
+ if (is_left_estimated != is_right_estimated)
+ return is_left_estimated;
+ return left.distance_estimate < right.distance_estimate;
}
-PhysicalWebPageSuggestionsProvider::~PhysicalWebPageSuggestionsProvider() =
- default;
+void FilterOutByGroupId(
+ physical_web::MetadataList& page_metadata_list) {
+ // |std::unique| only removes duplicates that immediately follow each other.
+ // Thus, first, we have to sort by group_id and distance and only then remove
+ // duplicates.
+ std::sort(page_metadata_list.begin(), page_metadata_list.end(),
+ [](const physical_web::Metadata& left,
+ const physical_web::Metadata& right) {
+ if (left.group_id != right.group_id) {
+ return left.group_id < right.group_id;
+ }
-void PhysicalWebPageSuggestionsProvider::OnDisplayableUrlsChanged(
- const std::vector<UrlInfo>& urls) {
- NotifyStatusChanged(CategoryStatus::AVAILABLE);
- std::vector<ContentSuggestion> suggestions;
+ // We want closest pages first, so in case of same group_id we
+ // sort by distance.
+ return CompareByDistance(left, right);
+ });
- for (const UrlInfo& url_info : urls) {
- if (suggestions.size() >= kMaxSuggestionsCount) break;
+ // Each empty group_id must be treated as unique, so we do not apply
+ // std::unique to them at all.
+ auto nonempty_group_id_begin = std::find_if(
+ page_metadata_list.begin(), page_metadata_list.end(),
+ [](const physical_web::Metadata& page) {
+ return !page.group_id.empty();
+ });
- ContentSuggestion suggestion(provided_category_, url_info.site_url.spec(),
- url_info.site_url);
+ auto new_end = std::unique(
+ nonempty_group_id_begin, page_metadata_list.end(),
+ [](const physical_web::Metadata& left,
+ const physical_web::Metadata& right) {
+ return left.group_id == right.group_id;
+ });
- suggestion.set_title(base::UTF8ToUTF16(url_info.title));
- suggestion.set_snippet_text(base::UTF8ToUTF16(url_info.description));
- suggestion.set_publish_date(url_info.scan_time);
- suggestion.set_publisher_name(base::UTF8ToUTF16(url_info.site_url.host()));
- suggestions.push_back(std::move(suggestion));
- }
+ page_metadata_list.erase(new_end, page_metadata_list.end());
+}
- observer()->OnNewSuggestions(this, provided_category_,
- std::move(suggestions));
+} // namespace
+
+PhysicalWebPageSuggestionsProvider::PhysicalWebPageSuggestionsProvider(
+ ContentSuggestionsProvider::Observer* observer,
+ physical_web::PhysicalWebDataSource* physical_web_data_source,
+ PrefService* pref_service)
+ : ContentSuggestionsProvider(observer),
+ category_status_(CategoryStatus::AVAILABLE),
+ provided_category_(
+ Category::FromKnownCategory(KnownCategories::PHYSICAL_WEB_PAGES)),
+ physical_web_data_source_(physical_web_data_source),
+ pref_service_(pref_service) {
+ observer->OnCategoryStatusChanged(this, provided_category_, category_status_);
+ physical_web_data_source_->RegisterListener(this);
+ // TODO(vitaliii): Rewrite initial fetch once crbug.com/667754 is resolved.
+ FetchPhysicalWebPages();
+}
+
+PhysicalWebPageSuggestionsProvider::~PhysicalWebPageSuggestionsProvider() {
+ physical_web_data_source_->UnregisterListener(this);
}
CategoryStatus PhysicalWebPageSuggestionsProvider::GetCategoryStatus(
@@ -70,43 +114,51 @@ CategoryStatus PhysicalWebPageSuggestionsProvider::GetCategoryStatus(
CategoryInfo PhysicalWebPageSuggestionsProvider::GetCategoryInfo(
Category category) {
- // TODO(vitaliii): Use the proper strings once they've been agreed on.
- return CategoryInfo(
- base::ASCIIToUTF16("Physical web pages"),
- ContentSuggestionsCardLayout::MINIMAL_CARD,
- /*has_more_action=*/false,
- /*has_reload_action=*/false,
- /*has_view_all_action=*/false,
- /*show_if_empty=*/false,
- l10n_util::GetStringUTF16(IDS_NTP_SUGGESTIONS_SECTION_EMPTY));
+ return CategoryInfo(l10n_util::GetStringUTF16(
+ IDS_NTP_PHYSICAL_WEB_PAGE_SUGGESTIONS_SECTION_HEADER),
+ ContentSuggestionsCardLayout::FULL_CARD,
+ /*has_more_action=*/false,
+ /*has_reload_action=*/false,
+ /*has_view_all_action=*/false,
+ /*show_if_empty=*/false,
+ l10n_util::GetStringUTF16(
+ IDS_NTP_PHYSICAL_WEB_PAGE_SUGGESTIONS_SECTION_EMPTY));
}
void PhysicalWebPageSuggestionsProvider::DismissSuggestion(
const ContentSuggestion::ID& suggestion_id) {
- // TODO(vitaliii): Implement this and then
- // ClearDismissedSuggestionsForDebugging.
+ DCHECK_EQ(provided_category_, suggestion_id.category());
+ std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs();
+ dismissed_ids.insert(suggestion_id.id_within_category());
+ StoreDismissedIDsToPrefs(dismissed_ids);
}
void PhysicalWebPageSuggestionsProvider::FetchSuggestionImage(
const ContentSuggestion::ID& suggestion_id,
const ImageFetchedCallback& callback) {
- // TODO(vitaliii): Implement.
+ ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
+ base::StringPiece raw_data = resource_bundle.GetRawDataResourceForScale(
+ IDR_PHYSICAL_WEB_LOGO_WITH_PADDING, resource_bundle.GetMaxScaleFactor());
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(callback, gfx::Image()));
+ FROM_HERE,
+ base::Bind(callback,
+ gfx::Image::CreateFrom1xPNGBytes(
+ reinterpret_cast<const unsigned char*>(raw_data.data()),
+ raw_data.size())));
}
void PhysicalWebPageSuggestionsProvider::Fetch(
const Category& category,
const std::set<std::string>& known_suggestion_ids,
const FetchDoneCallback& callback) {
- LOG(DFATAL)
- << "PhysicalWebPageSuggestionsProvider has no |Fetch| functionality!";
+ DCHECK_EQ(category, provided_category_);
+ std::vector<ContentSuggestion> suggestions =
+ GetMostRecentPhysicalWebPagesWithFilter(kMaxSuggestionsCount,
+ known_suggestion_ids);
+ AppendToShownScannedUrls(suggestions);
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::Bind(callback, Status(StatusCode::PERMANENT_ERROR,
- "PhysicalWebPageSuggestionsProvider "
- "has no |Fetch| functionality!"),
- base::Passed(std::vector<ContentSuggestion>())));
+ FROM_HERE, base::Bind(callback, Status::Success(),
+ base::Passed(std::move(suggestions))));
}
void PhysicalWebPageSuggestionsProvider::ClearHistory(
@@ -124,24 +176,194 @@ void PhysicalWebPageSuggestionsProvider::ClearCachedSuggestions(
void PhysicalWebPageSuggestionsProvider::GetDismissedSuggestionsForDebugging(
Category category,
const DismissedSuggestionsCallback& callback) {
- // Not implemented.
- callback.Run(std::vector<ContentSuggestion>());
+ DCHECK_EQ(provided_category_, category);
+ std::unique_ptr<physical_web::MetadataList> page_metadata_list =
+ physical_web_data_source_->GetMetadataList();
+ const std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs();
+ std::vector<ContentSuggestion> suggestions;
+ for (const auto& page_metadata : *page_metadata_list) {
+ if (dismissed_ids.count(GetPageId(page_metadata))) {
+ suggestions.push_back(ConvertPhysicalWebPage(page_metadata));
+ }
+ }
+
+ callback.Run(std::move(suggestions));
}
void PhysicalWebPageSuggestionsProvider::ClearDismissedSuggestionsForDebugging(
Category category) {
- // TODO(vitaliii): Implement when dismissed suggestions are supported.
+ DCHECK_EQ(provided_category_, category);
+ StoreDismissedIDsToPrefs(std::set<std::string>());
+ FetchPhysicalWebPages();
+}
+
+// static
+void PhysicalWebPageSuggestionsProvider::RegisterProfilePrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterListPref(prefs::kDismissedPhysicalWebPageSuggestions);
}
////////////////////////////////////////////////////////////////////////////////
// Private methods
-// Updates the |category_status_| and notifies the |observer_|, if necessary.
void PhysicalWebPageSuggestionsProvider::NotifyStatusChanged(
CategoryStatus new_status) {
- if (category_status_ == new_status) return;
+ if (category_status_ == new_status) {
+ return;
+ }
category_status_ = new_status;
observer()->OnCategoryStatusChanged(this, provided_category_, new_status);
}
+void PhysicalWebPageSuggestionsProvider::FetchPhysicalWebPages() {
+ DCHECK_EQ(CategoryStatus::AVAILABLE, category_status_);
+ std::vector<ContentSuggestion> suggestions =
+ GetMostRecentPhysicalWebPagesWithFilter(
+ kMaxSuggestionsCount,
+ /*excluded_ids=*/std::set<std::string>());
+ shown_resolved_urls_by_scanned_url_.clear();
+ AppendToShownScannedUrls(suggestions);
+ observer()->OnNewSuggestions(this, provided_category_,
+ std::move(suggestions));
+}
+
+std::vector<ContentSuggestion>
+PhysicalWebPageSuggestionsProvider::GetMostRecentPhysicalWebPagesWithFilter(
+ int max_count,
+ const std::set<std::string>& excluded_ids) {
+ std::unique_ptr<physical_web::MetadataList> page_metadata_list =
+ physical_web_data_source_->GetMetadataList();
+
+ // These is to filter out dismissed suggestions and at the same time prune the
+ // dismissed IDs list removing nonavailable pages (this is needed since some
+ // OnLost() calls may have been missed).
+ const std::set<std::string> old_dismissed_ids = ReadDismissedIDsFromPrefs();
+ std::set<std::string> new_dismissed_ids;
+ physical_web::MetadataList filtered_metadata_list;
+ for (const auto& page_metadata : *page_metadata_list) {
+ const std::string page_id = GetPageId(page_metadata);
+ if (!excluded_ids.count(page_id) && !old_dismissed_ids.count(page_id)) {
+ filtered_metadata_list.push_back(page_metadata);
+ }
+
+ if (old_dismissed_ids.count(page_id)) {
+ new_dismissed_ids.insert(page_id);
+ }
+ }
+
+ if (old_dismissed_ids.size() != new_dismissed_ids.size()) {
+ StoreDismissedIDsToPrefs(new_dismissed_ids);
+ }
+
+ FilterOutByGroupId(filtered_metadata_list);
+
+ std::sort(filtered_metadata_list.begin(), filtered_metadata_list.end(),
+ CompareByDistance);
+
+ std::vector<ContentSuggestion> suggestions;
+ for (const auto& page_metadata : filtered_metadata_list) {
+ if (static_cast<int>(suggestions.size()) == max_count) {
+ break;
+ }
+ suggestions.push_back(ConvertPhysicalWebPage(page_metadata));
+ }
+
+ return suggestions;
+}
+
+ContentSuggestion PhysicalWebPageSuggestionsProvider::ConvertPhysicalWebPage(
+ const physical_web::Metadata& page) const {
+ ContentSuggestion suggestion(provided_category_, GetPageId(page),
+ page.resolved_url);
+ DCHECK(base::IsStringUTF8(page.title));
+ suggestion.set_title(base::UTF8ToUTF16(page.title));
+ suggestion.set_publisher_name(base::UTF8ToUTF16(page.resolved_url.host()));
+ DCHECK(base::IsStringUTF8(page.description));
+ suggestion.set_snippet_text(base::UTF8ToUTF16(page.description));
+ return suggestion;
+}
+
+// PhysicalWebListener implementation.
+void PhysicalWebPageSuggestionsProvider::OnFound(const GURL& url) {
+ FetchPhysicalWebPages();
+}
+
+void PhysicalWebPageSuggestionsProvider::OnLost(const GURL& url) {
+ auto it = shown_resolved_urls_by_scanned_url_.find(url);
+ if (it == shown_resolved_urls_by_scanned_url_.end()) {
+ // The notification is propagated further in case the suggestion is shown on
+ // old NTPs (created before last |shown_resolved_urls_by_scanned_url_|
+ // update).
+
+ // TODO(vitaliii): Use |resolved_url| here when it is available. Currently
+ // there is no way to find out |resolved_url|, which corresponds to this
+ // |scanned_url| (the metadata has been already removed from the Physical
+ // Web list). We use |scanned_url| (it may be the same as |resolved_url|,
+ // otherwise nothing happens), however, we should use the latter once it is
+ // provided (e.g. as an argument).
+ InvalidateSuggestion(url.spec());
+ return;
+ }
+
+ // This is not a reference, because the multimap pair will be removed below.
+ const GURL lost_resolved_url = it->second;
+ shown_resolved_urls_by_scanned_url_.erase(it);
+ if (std::find_if(shown_resolved_urls_by_scanned_url_.begin(),
+ shown_resolved_urls_by_scanned_url_.end(),
+ [lost_resolved_url](const std::pair<GURL, GURL>& pair) {
+ return lost_resolved_url == pair.second;
+ }) == shown_resolved_urls_by_scanned_url_.end()) {
+ // There are no more beacons for this URL.
+ InvalidateSuggestion(lost_resolved_url.spec());
+ }
+}
+
+void PhysicalWebPageSuggestionsProvider::OnDistanceChanged(
+ const GURL& url,
+ double distance_estimate) {
+ FetchPhysicalWebPages();
+}
+
+void PhysicalWebPageSuggestionsProvider::InvalidateSuggestion(
+ const std::string& page_id) {
+ observer()->OnSuggestionInvalidated(
+ this, ContentSuggestion::ID(provided_category_, page_id));
+
+ // Remove |page_id| from dismissed suggestions, if present.
+ std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs();
+ auto it = dismissed_ids.find(page_id);
+ if (it != dismissed_ids.end()) {
+ dismissed_ids.erase(it);
+ StoreDismissedIDsToPrefs(dismissed_ids);
+ }
+}
+
+void PhysicalWebPageSuggestionsProvider::AppendToShownScannedUrls(
+ const std::vector<ContentSuggestion>& suggestions) {
+ std::unique_ptr<physical_web::MetadataList> page_metadata_list =
+ physical_web_data_source_->GetMetadataList();
+ for (const auto& page_metadata : *page_metadata_list) {
+ if (std::find_if(suggestions.begin(), suggestions.end(),
+ [page_metadata](const ContentSuggestion& suggestion) {
+ return suggestion.url() == page_metadata.resolved_url;
+ }) != suggestions.end()) {
+ shown_resolved_urls_by_scanned_url_.insert(std::make_pair(
+ page_metadata.scanned_url, page_metadata.resolved_url));
+ }
+ }
+}
+
+std::set<std::string>
+PhysicalWebPageSuggestionsProvider::ReadDismissedIDsFromPrefs() const {
+ return prefs::ReadDismissedIDsFromPrefs(
+ *pref_service_, prefs::kDismissedPhysicalWebPageSuggestions);
+}
+
+void PhysicalWebPageSuggestionsProvider::StoreDismissedIDsToPrefs(
+ const std::set<std::string>& dismissed_ids) {
+ prefs::StoreDismissedIDsToPrefs(pref_service_,
+ prefs::kDismissedPhysicalWebPageSuggestions,
+ dismissed_ids);
+}
+
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h b/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h
index 407536f4c69..befc8ce5e4a 100644
--- a/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h
+++ b/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h
@@ -5,44 +5,39 @@
#ifndef COMPONENTS_NTP_SNIPPETS_PHYSICAL_WEB_PAGES_PHYSICAL_WEB_PAGE_SUGGESTIONS_PROVIDER_H_
#define COMPONENTS_NTP_SNIPPETS_PHYSICAL_WEB_PAGES_PHYSICAL_WEB_PAGE_SUGGESTIONS_PROVIDER_H_
+#include <map>
#include <set>
-#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "components/ntp_snippets/category.h"
-#include "components/ntp_snippets/category_factory.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/physical_web/data_source/physical_web_listener.h"
-namespace gfx {
-class Image;
-} // namespace gfx
+class GURL;
+class PrefRegistrySimple;
+class PrefService;
-namespace ntp_snippets {
+namespace physical_web {
+class PhysicalWebDataSource;
+struct Metadata;
+} // namespace physical_web
-// TODO(vitaliii): remove when Physical Web C++ interface is provided.
-struct UrlInfo {
- UrlInfo();
- UrlInfo(const UrlInfo& other);
- ~UrlInfo();
- base::Time scan_time;
- GURL site_url;
- std::string title;
- std::string description;
-};
+namespace ntp_snippets {
// Provides content suggestions from the Physical Web Service.
-class PhysicalWebPageSuggestionsProvider : public ContentSuggestionsProvider {
+class PhysicalWebPageSuggestionsProvider
+ : public ContentSuggestionsProvider,
+ public physical_web::PhysicalWebListener {
public:
PhysicalWebPageSuggestionsProvider(
ContentSuggestionsProvider::Observer* observer,
- CategoryFactory* category_factory);
+ physical_web::PhysicalWebDataSource* physical_web_data_source,
+ PrefService* pref_service);
~PhysicalWebPageSuggestionsProvider() override;
- void OnDisplayableUrlsChanged(const std::vector<UrlInfo>& urls);
-
// ContentSuggestionsProvider implementation.
CategoryStatus GetCategoryStatus(Category category) override;
CategoryInfo GetCategoryInfo(Category category) override;
@@ -62,11 +57,54 @@ class PhysicalWebPageSuggestionsProvider : public ContentSuggestionsProvider {
const DismissedSuggestionsCallback& callback) override;
void ClearDismissedSuggestionsForDebugging(Category category) override;
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
private:
+ friend class PhysicalWebPageSuggestionsProviderTest;
+
+ // Updates the |category_status_| and notifies the |observer_|, if necessary.
void NotifyStatusChanged(CategoryStatus new_status);
+ // Manually requests all physical web pages and updates the suggestions.
+ void FetchPhysicalWebPages();
+
+ // Returns at most |max_count| ContentSuggestions with IDs not in
+ // |excluded_ids| and sorted by distance (the closest first). Dismissed
+ // suggestions are excluded automatically (no need to add them to
+ // |excluded_ids|) and pruned. The raw pages are obtained from Physical Web
+ // data source.
+ std::vector<ContentSuggestion> GetMostRecentPhysicalWebPagesWithFilter(
+ int max_count,
+ const std::set<std::string>& excluded_ids);
+
+ // Converts an Physical Web page to a ContentSuggestion.
+ ContentSuggestion ConvertPhysicalWebPage(
+ const physical_web::Metadata& page) const;
+
+ // PhysicalWebListener implementation.
+ void OnFound(const GURL& url) override;
+ void OnLost(const GURL& url) override;
+ void OnDistanceChanged(const GURL& url, double distance_estimate) override;
+
+ // Fires the |OnSuggestionInvalidated| event for the suggestion corresponding
+ // to the given |page_id| and deletes it from the dismissed IDs list, if
+ // necessary.
+ void InvalidateSuggestion(const std::string& page_id);
+
+ void AppendToShownScannedUrls(
+ const std::vector<ContentSuggestion>& suggestions);
+
+ // Reads dismissed IDs from Prefs.
+ std::set<std::string> ReadDismissedIDsFromPrefs() const;
+
+ // Writes |dismissed_ids| into Prefs.
+ void StoreDismissedIDsToPrefs(const std::set<std::string>& dismissed_ids);
+
+ std::multimap<GURL, GURL> shown_resolved_urls_by_scanned_url_;
CategoryStatus category_status_;
const Category provided_category_;
+ physical_web::PhysicalWebDataSource* physical_web_data_source_;
+ PrefService* pref_service_;
DISALLOW_COPY_AND_ASSIGN(PhysicalWebPageSuggestionsProvider);
};
diff --git a/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc b/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc
index b70d2c3d9cb..696cebb50a2 100644
--- a/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc
+++ b/chromium/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider_unittest.cc
@@ -4,60 +4,465 @@
#include "components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h"
+#include <memory>
#include <string>
#include <vector>
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
#include "components/ntp_snippets/category.h"
-#include "components/ntp_snippets/category_factory.h"
#include "components/ntp_snippets/content_suggestions_provider.h"
#include "components/ntp_snippets/mock_content_suggestions_provider_observer.h"
+#include "components/ntp_snippets/offline_pages/offline_pages_test_utils.h"
+#include "components/ntp_snippets/status.h"
+#include "components/physical_web/data_source/fake_physical_web_data_source.h"
+#include "components/prefs/testing_pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+using ntp_snippets::test::CaptureDismissedSuggestions;
+using physical_web::CreateDummyPhysicalWebPages;
+using physical_web::FakePhysicalWebDataSource;
+using testing::_;
+using testing::AnyNumber;
+using testing::AtMost;
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+using testing::Mock;
+using testing::StrictMock;
using testing::UnorderedElementsAre;
namespace ntp_snippets {
namespace {
-UrlInfo CreateUrlInfo(const std::string& site_url) {
- UrlInfo url_info;
- url_info.site_url = GURL(site_url);
- return url_info;
+MATCHER_P(HasUrl, url, "") {
+ *result_listener << "expected URL: " << url
+ << ", but has URL: " << arg.url().spec();
+ return url == arg.url().spec();
}
-std::vector<UrlInfo> CreateUrlInfos(const std::vector<std::string>& site_urls) {
- std::vector<UrlInfo> url_infos;
- for (const std::string& site_url : site_urls) {
- url_infos.emplace_back(CreateUrlInfo(site_url));
+void CompareFetchMoreResult(
+ const base::Closure& done_closure,
+ Status expected_status,
+ const std::vector<std::string>& expected_suggestion_urls,
+ Status actual_status,
+ std::vector<ContentSuggestion> actual_suggestions) {
+ EXPECT_EQ(expected_status.code, actual_status.code);
+ std::vector<std::string> actual_suggestion_urls;
+ for (const ContentSuggestion& suggestion : actual_suggestions) {
+ actual_suggestion_urls.push_back(suggestion.url().spec());
}
- return url_infos;
+ EXPECT_THAT(actual_suggestion_urls,
+ ElementsAreArray(expected_suggestion_urls));
+ done_closure.Run();
}
-MATCHER_P(HasUrl, url, "") {
- *result_listener << "expected URL: " << url
- << "has URL: " << arg.url().spec();
- return arg.url().spec() == url;
+} // namespace
+
+class PhysicalWebPageSuggestionsProviderTest : public testing::Test {
+ public:
+ PhysicalWebPageSuggestionsProviderTest()
+ : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()) {
+ PhysicalWebPageSuggestionsProvider::RegisterProfilePrefs(
+ pref_service_->registry());
+ }
+
+ void IgnoreOnCategoryStatusChangedToAvailable() {
+ EXPECT_CALL(observer_, OnCategoryStatusChanged(_, provided_category(),
+ CategoryStatus::AVAILABLE))
+ .Times(AnyNumber());
+ EXPECT_CALL(observer_,
+ OnCategoryStatusChanged(_, provided_category(),
+ CategoryStatus::AVAILABLE_LOADING))
+ .Times(AnyNumber());
+ }
+
+ void IgnoreOnSuggestionInvalidated() {
+ EXPECT_CALL(observer_, OnSuggestionInvalidated(_, _)).Times(AnyNumber());
+ }
+
+ void IgnoreOnNewSuggestions() {
+ EXPECT_CALL(observer_, OnNewSuggestions(_, provided_category(), _))
+ .Times(AnyNumber());
+ }
+
+ PhysicalWebPageSuggestionsProvider* CreateProvider() {
+ DCHECK(!provider_);
+ provider_ = base::MakeUnique<PhysicalWebPageSuggestionsProvider>(
+ &observer_, &physical_web_data_source_, pref_service_.get());
+ return provider_.get();
+ }
+
+ void DestroyProvider() { provider_.reset(); }
+
+ Category provided_category() {
+ return Category::FromKnownCategory(KnownCategories::PHYSICAL_WEB_PAGES);
+ }
+
+ ContentSuggestion::ID GetDummySuggestionId(int id) {
+ return ContentSuggestion::ID(
+ provided_category(),
+ "https://resolved_url.com/" + base::IntToString(id));
+ }
+
+ void FireUrlFound(const std::string& url) {
+ physical_web_data_source_.NotifyOnFound(GURL(url));
+ }
+
+ void FireUrlLost(const std::string& url) {
+ physical_web_data_source_.NotifyOnLost(GURL(url));
+ }
+
+ void FireUrlDistanceChanged(const GURL& url, double new_distance) {
+ physical_web_data_source_.NotifyOnDistanceChanged(url, new_distance);
+ }
+
+ ContentSuggestionsProvider* provider() {
+ DCHECK(provider_);
+ return provider_.get();
+ }
+
+ FakePhysicalWebDataSource* physical_web_data_source() {
+ return &physical_web_data_source_;
+ }
+ MockContentSuggestionsProviderObserver* observer() { return &observer_; }
+ TestingPrefServiceSimple* pref_service() { return pref_service_.get(); }
+
+ private:
+ FakePhysicalWebDataSource physical_web_data_source_;
+ StrictMock<MockContentSuggestionsProviderObserver> observer_;
+ std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+ // Added in order to test provider's |Fetch| method.
+ base::MessageLoop message_loop_;
+ // Last so that the dependencies are deleted after the provider.
+ std::unique_ptr<PhysicalWebPageSuggestionsProvider> provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(PhysicalWebPageSuggestionsProviderTest);
+};
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldSubmitSuggestionsOnStartup) {
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({1, 2, 3}));
+ EXPECT_CALL(*observer(), OnCategoryStatusChanged(_, provided_category(),
+ CategoryStatus::AVAILABLE));
+ EXPECT_CALL(*observer(),
+ OnNewSuggestions(
+ _, provided_category(),
+ UnorderedElementsAre(HasUrl("https://resolved_url.com/1"),
+ HasUrl("https://resolved_url.com/2"),
+ HasUrl("https://resolved_url.com/3"))));
+ CreateProvider();
}
-} // namespace
+TEST_F(PhysicalWebPageSuggestionsProviderTest, ShouldSortByDistance) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ IgnoreOnSuggestionInvalidated();
+ // |CreateDummyPhysicalWebPages| builds pages with distances 1, 2 and 3
+ // respectively.
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({3, 2, 1}));
+ EXPECT_CALL(
+ *observer(),
+ OnNewSuggestions(_, provided_category(),
+ ElementsAre(HasUrl("https://resolved_url.com/3"),
+ HasUrl("https://resolved_url.com/2"),
+ HasUrl("https://resolved_url.com/1"))));
+ CreateProvider();
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldConsiderPagesWithoutDistanceEstimateFurthest) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ IgnoreOnSuggestionInvalidated();
+ // |CreateDummyPhysicalWebPages| builds pages with distances 1, 2 and 3
+ // respectively.
+ std::unique_ptr<physical_web::MetadataList> pages =
+ CreateDummyPhysicalWebPages({3, 2, 1});
+ // Set the second page distance estimate to unknown.
+ (*pages)[1].distance_estimate = -1.0;
+ physical_web_data_source()->SetMetadataList(std::move(pages));
+ EXPECT_CALL(
+ *observer(),
+ OnNewSuggestions(_, provided_category(),
+ ElementsAre(HasUrl("https://resolved_url.com/3"),
+ HasUrl("https://resolved_url.com/1"),
+ HasUrl("https://resolved_url.com/2"))));
+ CreateProvider();
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldNotShowSuggestionsWithSameGroupId) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ IgnoreOnSuggestionInvalidated();
+ // |CreateDummyPhysicalWebPages| builds pages with distances 1, 2
+ // respectively.
+ std::unique_ptr<physical_web::MetadataList> pages =
+ CreateDummyPhysicalWebPages({2, 1});
+ for (auto& page : *pages) {
+ page.group_id = "some_group_id";
+ }
+
+ physical_web_data_source()->SetMetadataList(std::move(pages));
+ // The closest page should be reported.
+ EXPECT_CALL(
+ *observer(),
+ OnNewSuggestions(_, provided_category(),
+ ElementsAre(HasUrl("https://resolved_url.com/2"))));
+ CreateProvider();
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldShowSuggestionsWithEmptyGroupId) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ IgnoreOnSuggestionInvalidated();
+ std::unique_ptr<physical_web::MetadataList> pages =
+ CreateDummyPhysicalWebPages({1, 2});
+ for (auto& page : *pages) {
+ page.group_id = "";
+ }
-TEST(PhysicalWebPageSuggestionsProviderTest, ShouldCreateSuggestions) {
- MockContentSuggestionsProviderObserver observer;
- CategoryFactory category_factory;
- Category category =
- category_factory.FromKnownCategory(KnownCategories::PHYSICAL_WEB_PAGES);
- PhysicalWebPageSuggestionsProvider provider(&observer, &category_factory);
- const std::string first_url = "http://test1.com/";
- const std::string second_url = "http://test2.com/";
- const std::vector<UrlInfo> url_infos =
- CreateUrlInfos({first_url, second_url});
-
- EXPECT_CALL(observer,
+ physical_web_data_source()->SetMetadataList(std::move(pages));
+ EXPECT_CALL(*observer(),
OnNewSuggestions(
- &provider, category,
- UnorderedElementsAre(HasUrl(first_url), HasUrl(second_url))));
- provider.OnDisplayableUrlsChanged(url_infos);
+ _, provided_category(),
+ UnorderedElementsAre(HasUrl("https://resolved_url.com/1"),
+ HasUrl("https://resolved_url.com/2"))));
+ CreateProvider();
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ FetchMoreShouldFilterAndReturnSortedSuggestions) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ IgnoreOnSuggestionInvalidated();
+ std::vector<int> ids;
+ const int kNumSuggestionsInSection = 10;
+ const int kNumSuggestionsInSource = 3 * kNumSuggestionsInSection;
+ for (int i = 1; i <= kNumSuggestionsInSource; ++i) {
+ ids.push_back(i);
+ }
+ // |CreateDummyPhysicalWebPages| builds pages with distances 1, 2, 3, ... ,
+ // so we know the order of suggestions in the provider.
+ physical_web_data_source()->SetMetadataList(CreateDummyPhysicalWebPages(ids));
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _));
+ CreateProvider();
+
+ const std::string dummy_resolved_url = "https://resolved_url.com/";
+ std::set<std::string> known_ids;
+ for (int i = 1; i <= kNumSuggestionsInSection; ++i) {
+ known_ids.insert(dummy_resolved_url + base::IntToString(i));
+ }
+ known_ids.insert(dummy_resolved_url + base::IntToString(12));
+ known_ids.insert(dummy_resolved_url + base::IntToString(17));
+ std::vector<std::string> expected_suggestion_urls;
+ for (int i = 1; i <= kNumSuggestionsInSource; ++i) {
+ if (expected_suggestion_urls.size() == kNumSuggestionsInSection) {
+ break;
+ }
+ std::string url = dummy_resolved_url + base::IntToString(i);
+ if (!known_ids.count(url)) {
+ expected_suggestion_urls.push_back(url);
+ }
+ }
+
+ // Added to wait for |Fetch| callback to be called.
+ base::RunLoop run_loop;
+ provider()->Fetch(provided_category(), known_ids,
+ base::Bind(CompareFetchMoreResult, run_loop.QuitClosure(),
+ Status::Success(), expected_suggestion_urls));
+ // Wait for the callback to be called.
+ run_loop.Run();
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest, ShouldDismiss) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ IgnoreOnSuggestionInvalidated();
+ physical_web_data_source()->SetMetadataList(CreateDummyPhysicalWebPages({1}));
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _))
+ .Times(AtMost(1));
+ CreateProvider();
+
+ provider()->DismissSuggestion(GetDummySuggestionId(1));
+
+ std::vector<ContentSuggestion> dismissed_suggestions;
+ provider()->GetDismissedSuggestionsForDebugging(
+ provided_category(),
+ base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions));
+ EXPECT_THAT(dismissed_suggestions,
+ ElementsAre(HasUrl("https://resolved_url.com/1")));
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldNotShowDismissedSuggestions) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ IgnoreOnSuggestionInvalidated();
+ physical_web_data_source()->SetMetadataList(CreateDummyPhysicalWebPages({1}));
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _))
+ .Times(AtMost(1));
+ CreateProvider();
+ Mock::VerifyAndClearExpectations(observer());
+
+ provider()->DismissSuggestion(GetDummySuggestionId(1));
+
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({1, 2}));
+ EXPECT_CALL(
+ *observer(),
+ OnNewSuggestions(_, provided_category(),
+ ElementsAre(HasUrl("https://resolved_url.com/2"))));
+ FireUrlFound("https://resolved_url.com/2");
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldPruneDismissedSuggestionsWhenFetching) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ IgnoreOnSuggestionInvalidated();
+ IgnoreOnNewSuggestions();
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({1, 2}));
+ CreateProvider();
+
+ provider()->DismissSuggestion(GetDummySuggestionId(1));
+ provider()->DismissSuggestion(GetDummySuggestionId(2));
+
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({2, 3}));
+ FireUrlFound("https://resolved_url.com/3");
+
+ // The first page needs to be silently added back to the source, because
+ // |GetDismissedSuggestionsForDebugging| uses the data source to return
+ // suggestions and dismissed suggestions, which are not present there, cannot
+ // be returned.
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({1, 2, 3}));
+ std::vector<ContentSuggestion> dismissed_suggestions;
+ provider()->GetDismissedSuggestionsForDebugging(
+ provided_category(),
+ base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions));
+ EXPECT_THAT(dismissed_suggestions,
+ ElementsAre(HasUrl("https://resolved_url.com/2")));
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldPruneDismissedSuggestionsWhenUrlLost) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ IgnoreOnSuggestionInvalidated();
+ IgnoreOnNewSuggestions();
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({1, 2}));
+ CreateProvider();
+
+ provider()->DismissSuggestion(GetDummySuggestionId(1));
+ provider()->DismissSuggestion(GetDummySuggestionId(2));
+
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({2}));
+ FireUrlLost("https://resolved_url.com/1");
+
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({1, 2}));
+ std::vector<ContentSuggestion> dismissed_suggestions;
+ provider()->GetDismissedSuggestionsForDebugging(
+ provided_category(),
+ base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions));
+ EXPECT_THAT(dismissed_suggestions,
+ ElementsAre(HasUrl("https://resolved_url.com/2")));
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldStoreDismissedSuggestions) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ IgnoreOnSuggestionInvalidated();
+ IgnoreOnNewSuggestions();
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({1, 2}));
+ CreateProvider();
+ provider()->DismissSuggestion(GetDummySuggestionId(1));
+ DestroyProvider();
+
+ CreateProvider();
+ std::vector<ContentSuggestion> dismissed_suggestions;
+ provider()->GetDismissedSuggestionsForDebugging(
+ provided_category(),
+ base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions));
+ EXPECT_THAT(dismissed_suggestions,
+ ElementsAre(HasUrl("https://resolved_url.com/1")));
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldInvalidateSuggestionWhenItsOnlyBeaconIsLost) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ physical_web_data_source()->SetMetadataList(
+ CreateDummyPhysicalWebPages({1, 2}));
+
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _));
+ CreateProvider();
+
+ EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(1)));
+ FireUrlLost("https://scanned_url.com/1");
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldNotInvalidateSuggestionWhenBeaconWithDifferentScannedURLRemains) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ // Make 2 beacons point to the same URL, while having different |scanned_url|.
+ std::unique_ptr<physical_web::MetadataList> pages =
+ CreateDummyPhysicalWebPages({1, 2});
+ (*pages)[1].resolved_url = (*pages)[0].resolved_url;
+ physical_web_data_source()->SetMetadataList(std::move(pages));
+
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _));
+ CreateProvider();
+
+ // The first beacons is lost, but the second one still points to the same
+ // |resolved_url|, so the suggestion must not be invalidated.
+ EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, _)).Times(0);
+ FireUrlLost("https://scanned_url.com/1");
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldNotInvalidateSuggestionWhenBeaconWithSameScannedURLRemains) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ // Make 2 beacons point to the same URL, while having the same |scanned_url|.
+ std::unique_ptr<physical_web::MetadataList> pages =
+ CreateDummyPhysicalWebPages({1, 2});
+ (*pages)[1].scanned_url = (*pages)[0].scanned_url;
+ (*pages)[1].resolved_url = (*pages)[0].resolved_url;
+ physical_web_data_source()->SetMetadataList(std::move(pages));
+
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _));
+ CreateProvider();
+
+ // The first beacons is lost, but the second one still points to the same
+ // |resolved_url|, so the suggestion must not be invalidated.
+ EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, _)).Times(0);
+ FireUrlLost("https://scanned_url.com/1");
+}
+
+TEST_F(PhysicalWebPageSuggestionsProviderTest,
+ ShouldInvalidateSuggestionWhenAllBeaconsLost) {
+ IgnoreOnCategoryStatusChangedToAvailable();
+ // Make 3 beacons point to the same URL. Two of them have the same
+ // |scanned_url|.
+ std::unique_ptr<physical_web::MetadataList> pages =
+ CreateDummyPhysicalWebPages({1, 2, 3});
+ (*pages)[1].scanned_url = (*pages)[0].scanned_url;
+ (*pages)[1].resolved_url = (*pages)[0].resolved_url;
+ (*pages)[2].resolved_url = (*pages)[0].resolved_url;
+ physical_web_data_source()->SetMetadataList(std::move(pages));
+
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, provided_category(), _));
+ CreateProvider();
+
+ FireUrlLost("https://scanned_url.com/1");
+ FireUrlLost("https://scanned_url.com/1");
+ EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(1)));
+ FireUrlLost("https://scanned_url.com/3");
}
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/pref_names.cc b/chromium/components/ntp_snippets/pref_names.cc
index 10527da8cc5..b6e96c51510 100644
--- a/chromium/components/ntp_snippets/pref_names.cc
+++ b/chromium/components/ntp_snippets/pref_names.cc
@@ -11,10 +11,16 @@ const char kEnableSnippets[] = "ntp_snippets.enable";
const char kRemoteSuggestionCategories[] = "ntp_snippets.remote_categories";
-const char kSnippetBackgroundFetchingIntervalWifi[] =
+const char kSnippetLastFetchAttempt[] =
+ "ntp_snippets.last_fetch_attempt";
+
+const char kSnippetSoftFetchingIntervalOnUsageEvent[] =
+ "ntp_snippets.soft_fetching_interval_on_usage_event";
+
+const char kSnippetPersistentFetchingIntervalWifi[] =
"ntp_snippets.fetching_interval_wifi";
-const char kSnippetBackgroundFetchingIntervalFallback[] =
+const char kSnippetPersistentFetchingIntervalFallback[] =
"ntp_snippets.fetching_interval_fallback";
const char kSnippetFetcherRequestCount[] =
@@ -33,16 +39,19 @@ const char kSnippetThumbnailsRequestsDay[] =
const char kDismissedAssetDownloadSuggestions[] =
"ntp_suggestions.downloads.assets.dismissed_ids";
-const char kDismissedRecentOfflineTabSuggestions[] =
- "ntp_suggestions.offline_pages.recent_tabs.dismissed_ids";
-const char kDismissedOfflinePageDownloadSuggestions[] =
- "ntp_suggestions.downloads.offline_pages.dismissed_ids";
const char kDismissedForeignSessionsSuggestions[] =
"ntp_suggestions.foreign_sessions.dismissed_ids";
+const char kDismissedOfflinePageDownloadSuggestions[] =
+ "ntp_suggestions.downloads.offline_pages.dismissed_ids";
+const char kDismissedPhysicalWebPageSuggestions[] =
+ "ntp_suggestions.physical_web.dismissed_ids";
+const char kDismissedRecentOfflineTabSuggestions[] =
+ "ntp_suggestions.offline_pages.recent_tabs.dismissed_ids";
+
const char kDismissedCategories[] = "ntp_suggestions.dismissed_categories";
-const char kBookmarksFirstM54Start[] =
- "ntp_suggestions.bookmarks.first_M54_start";
+const char kLastSuccessfulBackgroundFetchTime[] =
+ "ntp_suggestions.remote.last_successful_background_fetch_time";
const char kUserClassifierAverageNTPOpenedPerHour[] =
"ntp_suggestions.user_classifier.average_ntp_opened_per_hour";
@@ -58,5 +67,10 @@ const char kUserClassifierLastTimeToShowSuggestions[] =
const char kUserClassifierLastTimeToUseSuggestions[] =
"ntp_suggestions.user_classifier.last_time_to_use_suggestions";
+const char kClickBasedCategoryRankerOrderWithClicks[] =
+ "ntp_suggestions.click_based_category_ranker.category_order_with_clicks";
+const char kClickBasedCategoryRankerLastDecayTime[] =
+ "ntp_suggestions.click_based_category_ranker.last_decay_time";
+
} // namespace prefs
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/pref_names.h b/chromium/components/ntp_snippets/pref_names.h
index 1a609cf9f61..7ff783d8a24 100644
--- a/chromium/components/ntp_snippets/pref_names.h
+++ b/chromium/components/ntp_snippets/pref_names.h
@@ -16,18 +16,26 @@ extern const char kEnableSnippets[];
// and title) are stored.
extern const char kRemoteSuggestionCategories[];
+// The pref name for the last time when a background fetch was attempted.
+extern const char kSnippetLastFetchAttempt[];
+// The pref name for the currently applied minimal interval between two
+// successive soft background fetches that react to user activity (such as
+// opening an NTP).
+extern const char kSnippetSoftFetchingIntervalOnUsageEvent[];
+
// The pref name for the currently-scheduled background fetching interval when
// there is WiFi connectivity.
-extern const char kSnippetBackgroundFetchingIntervalWifi[];
+extern const char kSnippetPersistentFetchingIntervalWifi[];
// The pref name for the currently-scheduled background fetching interval when
// there is no WiFi connectivity.
-extern const char kSnippetBackgroundFetchingIntervalFallback[];
+extern const char kSnippetPersistentFetchingIntervalFallback[];
-// The pref name for today's count of NTPSnippetsFetcher requests, so far.
+// The pref name for today's count of RemoteSuggestionsFetcher requests, so far.
extern const char kSnippetFetcherRequestCount[];
-// The pref name for today's count of NTPSnippetsFetcher interactive requests.
+// The pref name for today's count of RemoteSuggestionsFetcher interactive
+// requests.
extern const char kSnippetFetcherInteractiveRequestCount[];
-// The pref name for the current day for the counter of NTPSnippetsFetcher
+// The pref name for the current day for the counter of RemoteSuggestionsFetcher
// requests.
extern const char kSnippetFetcherRequestsDay[];
@@ -40,14 +48,16 @@ extern const char kSnippetThumbnailsInteractiveRequestCount[];
// thumbnails.
extern const char kSnippetThumbnailsRequestsDay[];
+// The pref name for the time of the last successful background fetch.
+extern const char kLastSuccessfulBackgroundFetchTime[];
+
extern const char kDismissedAssetDownloadSuggestions[];
-extern const char kDismissedRecentOfflineTabSuggestions[];
-extern const char kDismissedOfflinePageDownloadSuggestions[];
extern const char kDismissedForeignSessionsSuggestions[];
-extern const char kDismissedCategories[];
+extern const char kDismissedOfflinePageDownloadSuggestions[];
+extern const char kDismissedPhysicalWebPageSuggestions[];
+extern const char kDismissedRecentOfflineTabSuggestions[];
-// The pref name for the time when M54 was first started on the device.
-extern const char kBookmarksFirstM54Start[];
+extern const char kDismissedCategories[];
// The pref name for the discounted average number of browsing sessions per hour
// that involve opening a new NTP.
@@ -67,6 +77,11 @@ extern const char kUserClassifierLastTimeToShowSuggestions[];
// The pref name for the last time content suggestions were used by the user.
extern const char kUserClassifierLastTimeToUseSuggestions[];
+// The pref name for the current order of categories and their clicks.
+extern const char kClickBasedCategoryRankerOrderWithClicks[];
+// The pref name for the time when last click decay has happened.
+extern const char kClickBasedCategoryRankerLastDecayTime[];
+
} // namespace prefs
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/json_request.cc b/chromium/components/ntp_snippets/remote/json_request.cc
new file mode 100644
index 00000000000..9c24b7f8495
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/json_request.cc
@@ -0,0 +1,512 @@
+// 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/json_request.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/tick_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_info.h"
+#include "components/ntp_snippets/features.h"
+#include "components/ntp_snippets/remote/request_params.h"
+#include "components/ntp_snippets/user_classifier.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_manager_base.h"
+#include "components/variations/net/variations_http_headers.h"
+#include "components/variations/variations_associated_data.h"
+#include "grit/components_strings.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "third_party/icu/source/common/unicode/uloc.h"
+#include "third_party/icu/source/common/unicode/utypes.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using net::URLFetcher;
+using net::URLRequestContextGetter;
+using net::HttpRequestHeaders;
+using net::URLRequestStatus;
+using translate::LanguageModel;
+
+namespace ntp_snippets {
+
+namespace internal {
+
+namespace {
+
+// Variation parameter for disabling the retry.
+const char kBackground5xxRetriesName[] = "background_5xx_retries_count";
+
+const int kMaxExcludedIds = 100;
+
+// Variation parameter for sending LanguageModel info to the server.
+const char kSendTopLanguagesName[] = "send_top_languages";
+
+// Variation parameter for sending UserClassifier info to the server.
+const char kSendUserClassName[] = "send_user_class";
+
+int Get5xxRetryCount(bool interactive_request) {
+ if (interactive_request) {
+ return 2;
+ }
+ return std::max(0, variations::GetVariationParamByFeatureAsInt(
+ ntp_snippets::kArticleSuggestionsFeature,
+ kBackground5xxRetriesName, 0));
+}
+
+bool IsSendingTopLanguagesEnabled() {
+ return variations::GetVariationParamByFeatureAsBool(
+ ntp_snippets::kArticleSuggestionsFeature, kSendTopLanguagesName,
+ /*default_value=*/true);
+}
+
+bool IsSendingUserClassEnabled() {
+ return variations::GetVariationParamByFeatureAsBool(
+ ntp_snippets::kArticleSuggestionsFeature, kSendUserClassName,
+ /*default_value=*/false);
+}
+
+// Translate the BCP 47 |language_code| into a posix locale string.
+std::string PosixLocaleFromBCP47Language(const std::string& language_code) {
+ char locale[ULOC_FULLNAME_CAPACITY];
+ UErrorCode error = U_ZERO_ERROR;
+ // Translate the input to a posix locale.
+ uloc_forLanguageTag(language_code.c_str(), locale, ULOC_FULLNAME_CAPACITY,
+ nullptr, &error);
+ if (error != U_ZERO_ERROR) {
+ DLOG(WARNING) << "Error in translating language code to a locale string: "
+ << error;
+ return std::string();
+ }
+ return locale;
+}
+
+std::string ISO639FromPosixLocale(const std::string& locale) {
+ char language[ULOC_LANG_CAPACITY];
+ UErrorCode error = U_ZERO_ERROR;
+ uloc_getLanguage(locale.c_str(), language, ULOC_LANG_CAPACITY, &error);
+ if (error != U_ZERO_ERROR) {
+ DLOG(WARNING)
+ << "Error in translating locale string to a ISO639 language code: "
+ << error;
+ return std::string();
+ }
+ return language;
+}
+
+void AppendLanguageInfoToList(base::ListValue* list,
+ const LanguageModel::LanguageInfo& info) {
+ auto lang = base::MakeUnique<base::DictionaryValue>();
+ lang->SetString("language", info.language_code);
+ lang->SetDouble("frequency", info.frequency);
+ list->Append(std::move(lang));
+}
+
+std::string GetUserClassString(UserClassifier::UserClass user_class) {
+ switch (user_class) {
+ case UserClassifier::UserClass::RARE_NTP_USER:
+ return "RARE_NTP_USER";
+ case UserClassifier::UserClass::ACTIVE_NTP_USER:
+ return "ACTIVE_NTP_USER";
+ case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
+ return "ACTIVE_SUGGESTIONS_CONSUMER";
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+} // namespace
+
+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,
+ // TODO(dgn): merge has_more_action and has_reload_action when we remove
+ // the kFetchMoreFeature flag. See https://crbug.com/667752
+ /*has_more_action=*/base::FeatureList::IsEnabled(kFetchMoreFeature),
+ /*has_reload_action=*/true,
+ /*has_view_all_action=*/false,
+ /*show_if_empty=*/true,
+ l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_EMPTY));
+}
+
+CategoryInfo BuildRemoteCategoryInfo(const base::string16& title,
+ bool allow_fetching_more_results) {
+ return CategoryInfo(
+ title, ContentSuggestionsCardLayout::FULL_CARD,
+ // TODO(dgn): merge has_more_action and has_reload_action when we remove
+ // the kFetchMoreFeature flag. See https://crbug.com/667752
+ /*has_more_action=*/allow_fetching_more_results &&
+ base::FeatureList::IsEnabled(kFetchMoreFeature),
+ /*has_reload_action=*/allow_fetching_more_results,
+ /*has_view_all_action=*/false,
+ /*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));
+}
+
+JsonRequest::JsonRequest(
+ base::Optional<Category> exclusive_category,
+ base::TickClock* tick_clock, // Needed until destruction of the request.
+ const ParseJSONCallback& callback)
+ : exclusive_category_(exclusive_category),
+ tick_clock_(tick_clock),
+ parse_json_callback_(callback),
+ weak_ptr_factory_(this) {
+ creation_time_ = tick_clock_->NowTicks();
+}
+
+JsonRequest::~JsonRequest() {
+ LOG_IF(DFATAL, !request_completed_callback_.is_null())
+ << "The CompletionCallback was never called!";
+}
+
+void JsonRequest::Start(CompletedCallback callback) {
+ request_completed_callback_ = std::move(callback);
+ url_fetcher_->Start();
+}
+
+base::TimeDelta JsonRequest::GetFetchDuration() const {
+ return tick_clock_->NowTicks() - creation_time_;
+}
+
+std::string JsonRequest::GetResponseString() const {
+ std::string response;
+ url_fetcher_->GetResponseAsString(&response);
+ return response;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// URLFetcherDelegate overrides
+void JsonRequest::OnURLFetchComplete(const net::URLFetcher* source) {
+ DCHECK_EQ(url_fetcher_.get(), source);
+ const URLRequestStatus& status = url_fetcher_->GetStatus();
+ int response = url_fetcher_->GetResponseCode();
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "NewTabPage.Snippets.FetchHttpResponseOrErrorCode",
+ status.is_success() ? response : status.error());
+
+ 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 JsonRequest::ParseJsonResponse() {
+ std::string json_string;
+ bool stores_result_to_string =
+ url_fetcher_->GetResponseAsString(&json_string);
+ DCHECK(stores_result_to_string);
+
+ parse_json_callback_.Run(
+ json_string,
+ base::Bind(&JsonRequest::OnJsonParsed, weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&JsonRequest::OnJsonError, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void JsonRequest::OnJsonParsed(std::unique_ptr<base::Value> result) {
+ std::move(request_completed_callback_)
+ .Run(std::move(result), FetchResult::SUCCESS,
+ /*error_details=*/std::string());
+}
+
+void JsonRequest::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()));
+}
+
+JsonRequest::Builder::Builder()
+ : fetch_api_(CHROME_READER_API),
+ personalization_(Personalization::kBoth),
+ language_model_(nullptr) {}
+JsonRequest::Builder::Builder(JsonRequest::Builder&&) = default;
+JsonRequest::Builder::~Builder() = default;
+
+std::unique_ptr<JsonRequest> JsonRequest::Builder::Build() const {
+ DCHECK(!url_.is_empty());
+ DCHECK(url_request_context_getter_);
+ DCHECK(tick_clock_);
+ auto request = base::MakeUnique<JsonRequest>(
+ params_.exclusive_category, tick_clock_, parse_json_callback_);
+ std::string body = BuildBody();
+ std::string headers = BuildHeaders();
+ request->url_fetcher_ = BuildURLFetcher(request.get(), headers, body);
+
+ // Log the request for debugging network issues.
+ VLOG(1) << "Sending a NTP snippets request to " << url_ << ":\n"
+ << headers << "\n"
+ << body;
+
+ return request;
+}
+
+JsonRequest::Builder& JsonRequest::Builder::SetAuthentication(
+ const std::string& account_id,
+ const std::string& auth_header) {
+ obfuscated_gaia_id_ = account_id;
+ auth_header_ = auth_header;
+ return *this;
+}
+
+JsonRequest::Builder& JsonRequest::Builder::SetFetchAPI(FetchAPI fetch_api) {
+ fetch_api_ = fetch_api;
+ return *this;
+}
+
+JsonRequest::Builder& JsonRequest::Builder::SetLanguageModel(
+ const translate::LanguageModel* language_model) {
+ language_model_ = language_model;
+ return *this;
+}
+
+JsonRequest::Builder& JsonRequest::Builder::SetParams(
+ const RequestParams& params) {
+ params_ = params;
+ return *this;
+}
+
+JsonRequest::Builder& JsonRequest::Builder::SetParseJsonCallback(
+ ParseJSONCallback callback) {
+ parse_json_callback_ = callback;
+ return *this;
+}
+
+JsonRequest::Builder& JsonRequest::Builder::SetPersonalization(
+ Personalization personalization) {
+ personalization_ = personalization;
+ return *this;
+}
+
+JsonRequest::Builder& JsonRequest::Builder::SetTickClock(
+ base::TickClock* tick_clock) {
+ tick_clock_ = tick_clock;
+ return *this;
+}
+
+JsonRequest::Builder& JsonRequest::Builder::SetUrl(const GURL& url) {
+ url_ = url;
+ return *this;
+}
+
+JsonRequest::Builder& JsonRequest::Builder::SetUrlRequestContextGetter(
+ const scoped_refptr<net::URLRequestContextGetter>& context_getter) {
+ url_request_context_getter_ = context_getter;
+ return *this;
+}
+
+JsonRequest::Builder& JsonRequest::Builder::SetUserClassifier(
+ const UserClassifier& user_classifier) {
+ if (IsSendingUserClassEnabled()) {
+ user_class_ = GetUserClassString(user_classifier.GetUserClass());
+ }
+ return *this;
+}
+
+std::string JsonRequest::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 JsonRequest::Builder::BuildBody() const {
+ auto request = base::MakeUnique<base::DictionaryValue>();
+ std::string user_locale = PosixLocaleFromBCP47Language(params_.language_code);
+ switch (fetch_api_) {
+ case CHROME_READER_API: {
+ auto content_params = base::MakeUnique<base::DictionaryValue>();
+ content_params->SetBoolean("only_return_personalized_results",
+ ReturnOnlyPersonalizedResults());
+
+ auto content_restricts = base::MakeUnique<base::ListValue>();
+ for (const auto* metadata : {"TITLE", "SNIPPET", "THUMBNAIL"}) {
+ auto entry = base::MakeUnique<base::DictionaryValue>();
+ entry->SetString("type", "METADATA");
+ entry->SetString("value", metadata);
+ content_restricts->Append(std::move(entry));
+ }
+
+ auto local_scoring_params = base::MakeUnique<base::DictionaryValue>();
+ local_scoring_params->Set("content_params", std::move(content_params));
+ local_scoring_params->Set("content_restricts",
+ std::move(content_restricts));
+
+ auto global_scoring_params = base::MakeUnique<base::DictionaryValue>();
+ global_scoring_params->SetInteger("num_to_return",
+ params_.count_to_fetch);
+ global_scoring_params->SetInteger("sort_type", 1);
+
+ auto advanced = base::MakeUnique<base::DictionaryValue>();
+ advanced->Set("local_scoring_params", std::move(local_scoring_params));
+ advanced->Set("global_scoring_params", std::move(global_scoring_params));
+
+ request->SetString("response_detail_level", "STANDARD");
+ request->Set("advanced_options", std::move(advanced));
+ if (!obfuscated_gaia_id_.empty()) {
+ request->SetString("obfuscated_gaia_id", obfuscated_gaia_id_);
+ }
+ if (!user_locale.empty()) {
+ request->SetString("user_locale", user_locale);
+ }
+ break;
+ }
+
+ case CHROME_CONTENT_SUGGESTIONS_API: {
+ if (!user_locale.empty()) {
+ request->SetString("uiLanguage", user_locale);
+ }
+
+ request->SetString("priority", params_.interactive_request
+ ? "USER_ACTION"
+ : "BACKGROUND_PREFETCH");
+
+ auto excluded = base::MakeUnique<base::ListValue>();
+ for (const auto& id : params_.excluded_ids) {
+ excluded->AppendString(id);
+ if (excluded->GetSize() >= kMaxExcludedIds) {
+ break;
+ }
+ }
+ request->Set("excludedSuggestionIds", std::move(excluded));
+
+ if (!user_class_.empty()) {
+ request->SetString("userActivenessClass", user_class_);
+ }
+
+ translate::LanguageModel::LanguageInfo ui_language;
+ translate::LanguageModel::LanguageInfo other_top_language;
+ PrepareLanguages(&ui_language, &other_top_language);
+
+ if (ui_language.frequency == 0 && other_top_language.frequency == 0) {
+ break;
+ }
+
+ auto language_list = base::MakeUnique<base::ListValue>();
+ if (ui_language.frequency > 0) {
+ AppendLanguageInfoToList(language_list.get(), ui_language);
+ }
+ if (other_top_language.frequency > 0) {
+ AppendLanguageInfoToList(language_list.get(), other_top_language);
+ }
+ request->Set("topLanguages", std::move(language_list));
+
+ // TODO(sfiera): Support only_return_personalized_results.
+ // TODO(sfiera): Support count_to_fetch.
+ break;
+ }
+ }
+
+ 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> JsonRequest::Builder::BuildURLFetcher(
+ net::URLFetcherDelegate* delegate,
+ const std::string& headers,
+ const std::string& body) const {
+ std::unique_ptr<net::URLFetcher> url_fetcher =
+ net::URLFetcher::Create(url_, net::URLFetcher::POST, delegate);
+ 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);
+
+ url_fetcher->SetExtraRequestHeaders(headers);
+ url_fetcher->SetUploadData("application/json", body);
+
+ // Fetchers are sometimes cancelled because a network change was detected.
+ url_fetcher->SetAutomaticallyRetryOnNetworkChanges(3);
+ url_fetcher->SetMaxRetriesOn5xx(
+ Get5xxRetryCount(params_.interactive_request));
+ return url_fetcher;
+}
+
+void JsonRequest::Builder::PrepareLanguages(
+ translate::LanguageModel::LanguageInfo* ui_language,
+ translate::LanguageModel::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()) {
+ return;
+ }
+
+ // TODO(jkrcal): Is this back-and-forth converting necessary?
+ ui_language->language_code = ISO639FromPosixLocale(
+ PosixLocaleFromBCP47Language(params_.language_code));
+ ui_language->frequency =
+ language_model_->GetLanguageFrequency(ui_language->language_code);
+
+ std::vector<LanguageModel::LanguageInfo> top_languages =
+ language_model_->GetTopLanguages();
+ for (const LanguageModel::LanguageInfo& info : top_languages) {
+ if (info.language_code != ui_language->language_code) {
+ *other_top_language = info;
+
+ // Report to UMA how important the UI language is.
+ DCHECK_GT(other_top_language->frequency, 0)
+ << "GetTopLanguages() should not return languages with 0 frequency";
+ float ratio_ui_in_both_languages =
+ ui_language->frequency /
+ (ui_language->frequency + other_top_language->frequency);
+ UMA_HISTOGRAM_PERCENTAGE(
+ "NewTabPage.Languages.UILanguageRatioInTwoTopLanguages",
+ ratio_ui_in_both_languages * 100);
+ break;
+ }
+ }
+}
+
+} // namespace internal
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/json_request.h b/chromium/components/ntp_snippets/remote/json_request.h
new file mode 100644
index 00000000000..c7c73c7767e
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/json_request.h
@@ -0,0 +1,193 @@
+// 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_JSON_REQUEST_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_JSON_REQUEST_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.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"
+
+namespace base {
+class Value;
+class TickClock;
+} // namespace base
+
+class FetchAPI;
+class Personalization;
+
+namespace ntp_snippets {
+class UserClassifier;
+
+namespace internal {
+
+// Enumeration listing all possible outcomes for fetch attempts. Used for UMA
+// histograms, so do not change existing values. Insert new values at the end,
+// and update the histogram definition.
+enum class FetchResult {
+ SUCCESS,
+ DEPRECATED_EMPTY_HOSTS,
+ URL_REQUEST_STATUS_ERROR,
+ HTTP_ERROR,
+ JSON_PARSE_ERROR,
+ INVALID_SNIPPET_CONTENT_ERROR,
+ OAUTH_TOKEN_ERROR,
+ INTERACTIVE_QUOTA_ERROR,
+ NON_INTERACTIVE_QUOTA_ERROR,
+ RESULT_MAX
+};
+
+enum FetchAPI {
+ CHROME_READER_API,
+ CHROME_CONTENT_SUGGESTIONS_API,
+};
+
+// A single request to query remote suggestions. On success, the suggestions are
+// returned in parsed JSON form (base::Value).
+class JsonRequest : 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 JsonRequests.
+ class Builder {
+ public:
+ Builder();
+ Builder(Builder&&);
+ ~Builder();
+
+ // Builds a Request object that contains all data to fetch new snippets.
+ std::unique_ptr<JsonRequest> Build() const;
+
+ Builder& SetAuthentication(const std::string& account_id,
+ const std::string& auth_header);
+ Builder& SetCreationTime(base::TimeTicks creation_time);
+ Builder& SetFetchAPI(FetchAPI fetch_api);
+ // The language_model borrowed from the fetcher needs to stay alive until
+ // the request body is built.
+ Builder& SetLanguageModel(const translate::LanguageModel* language_model);
+ Builder& SetParams(const RequestParams& params);
+ Builder& SetParseJsonCallback(ParseJSONCallback callback);
+ Builder& SetPersonalization(Personalization personalization);
+ // The tick_clock borrowed from the fetcher will be injected into the
+ // request. It will be used at build time and after the fetch returned.
+ // It has to be alive until the request is destroyed.
+ Builder& SetTickClock(base::TickClock* tick_clock);
+ Builder& SetUrl(const GURL& url);
+ Builder& SetUrlRequestContextGetter(
+ const scoped_refptr<net::URLRequestContextGetter>& context_getter);
+ Builder& SetUserClassifier(const UserClassifier& user_classifier);
+
+ // These preview methods allow to inspect the Request without exposing it
+ // publicly.
+ // TODO(fhorschig): Remove these when moving the Builder to
+ // snippets::internal and trigger the request to intercept the request.
+ std::string PreviewRequestBodyForTesting() { return BuildBody(); }
+ std::string PreviewRequestHeadersForTesting() { return BuildHeaders(); }
+ Builder& SetUserClassForTesting(const std::string& user_class) {
+ user_class_ = user_class;
+ return *this;
+ }
+
+ 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;
+
+ bool ReturnOnlyPersonalizedResults() const {
+ return !obfuscated_gaia_id_.empty() &&
+ personalization_ == Personalization::kPersonal;
+ }
+
+ void PrepareLanguages(
+ translate::LanguageModel::LanguageInfo* ui_language,
+ translate::LanguageModel::LanguageInfo* other_top_language) const;
+
+ // Only required, if the request needs to be sent.
+ std::string auth_header_;
+ base::TickClock* tick_clock_;
+ FetchAPI fetch_api_;
+ RequestParams params_;
+ ParseJSONCallback parse_json_callback_;
+ Personalization personalization_;
+ GURL url_;
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+
+ // Optional properties.
+ std::string obfuscated_gaia_id_;
+ std::string user_class_;
+ const translate::LanguageModel* language_model_;
+
+ DISALLOW_COPY_AND_ASSIGN(Builder);
+ };
+
+ JsonRequest(base::Optional<Category> exclusive_category,
+ base::TickClock* tick_clock,
+ const ParseJSONCallback& callback);
+ JsonRequest(JsonRequest&&);
+ ~JsonRequest() override;
+
+ void Start(CompletedCallback callback);
+
+ const base::Optional<Category>& exclusive_category() const {
+ return exclusive_category_;
+ }
+
+ base::TimeDelta GetFetchDuration() const;
+ 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_;
+
+ // If set, only return results for this category.
+ base::Optional<Category> exclusive_category_;
+
+ // Use the TickClock from the Fetcher to measure the fetch time. It will be
+ // used on creation and after the fetch returned. It has to be alive until the
+ // request is destroyed.
+ base::TickClock* tick_clock_;
+ base::TimeTicks creation_time_;
+
+ // 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<JsonRequest> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(JsonRequest);
+};
+
+} // namespace internal
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_JSON_REQUEST_H_
diff --git a/chromium/components/ntp_snippets/remote/json_request_unittest.cc b/chromium/components/ntp_snippets/remote/json_request_unittest.cc
new file mode 100644
index 00000000000..909f7cc32c6
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/json_request_unittest.cc
@@ -0,0 +1,335 @@
+// 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/json_request.h"
+
+#include <set>
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "base/values.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/prefs/testing_pref_service.h"
+#include "components/variations/variations_params_manager.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "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::Not;
+using testing::NotNull;
+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 JsonRequestTest : public testing::Test {
+ public:
+ JsonRequestTest()
+ : params_manager_(
+ ntp_snippets::kStudyName,
+ {{"send_top_languages", "true"}, {"send_user_class", "true"}},
+ {ntp_snippets::kArticleSuggestionsFeature.name}),
+ pref_service_(base::MakeUnique<TestingPrefServiceSimple>()),
+ mock_task_runner_(new base::TestMockTimeTaskRunner()),
+ tick_clock_(mock_task_runner_->GetMockTickClock()),
+ request_context_getter_(
+ new net::TestURLRequestContextGetter(mock_task_runner_.get())) {
+ translate::LanguageModel::RegisterProfilePrefs(pref_service_->registry());
+ }
+
+ std::unique_ptr<translate::LanguageModel> MakeLanguageModel(
+ const std::set<std::string>& codes) {
+ std::unique_ptr<translate::LanguageModel> language_model =
+ base::MakeUnique<translate::LanguageModel>(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);
+ }
+ }
+ return language_model;
+ }
+
+ JsonRequest::Builder CreateMinimalBuilder() {
+ JsonRequest::Builder builder;
+ builder.SetUrl(GURL("http://valid-url.test"))
+ .SetTickClock(tick_clock_.get())
+ .SetUrlRequestContextGetter(request_context_getter_.get());
+ return builder;
+ }
+
+ private:
+ variations::testing::VariationParamsManager params_manager_;
+ std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+ scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
+ std::unique_ptr<base::TickClock> tick_clock_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+ net::TestURLFetcherFactory fetcher_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(JsonRequestTest);
+};
+
+TEST_F(JsonRequestTest, BuildRequestAuthenticated) {
+ JsonRequest::Builder builder = CreateMinimalBuilder();
+ RequestParams params;
+ params.excluded_ids = {"1234567890"};
+ params.count_to_fetch = 25;
+ params.interactive_request = false;
+ builder.SetParams(params)
+ .SetUrl(GURL("http://valid-url.test"))
+ .SetUrl(GURL("http://valid-url.test"))
+ .SetAuthentication("0BFUSGAIA", "headerstuff")
+ .SetPersonalization(Personalization::kPersonal)
+ .SetUserClassForTesting("ACTIVE_NTP_USER")
+ .SetFetchAPI(FetchAPI::CHROME_READER_API)
+ .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("{"
+ " \"response_detail_level\": \"STANDARD\","
+ " \"obfuscated_gaia_id\": \"0BFUSGAIA\","
+ " \"advanced_options\": {"
+ " \"local_scoring_params\": {"
+ " \"content_params\": {"
+ " \"only_return_personalized_results\": true"
+ " },"
+ " \"content_restricts\": ["
+ " {"
+ " \"type\": \"METADATA\","
+ " \"value\": \"TITLE\""
+ " },"
+ " {"
+ " \"type\": \"METADATA\","
+ " \"value\": \"SNIPPET\""
+ " },"
+ " {"
+ " \"type\": \"METADATA\","
+ " \"value\": \"THUMBNAIL\""
+ " }"
+ " ]"
+ " },"
+ " \"global_scoring_params\": {"
+ " \"num_to_return\": 25,"
+ " \"sort_type\": 1"
+ " }"
+ " }"
+ "}"));
+
+ builder.SetFetchAPI(FetchAPI::CHROME_CONTENT_SUGGESTIONS_API);
+
+ EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
+ EqualsJSON("{"
+ " \"priority\": \"BACKGROUND_PREFETCH\","
+ " \"excludedSuggestionIds\": ["
+ " \"1234567890\""
+ " ],"
+ " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
+ "}"));
+}
+
+TEST_F(JsonRequestTest, BuildRequestUnauthenticated) {
+ JsonRequest::Builder builder;
+ RequestParams params;
+ params.interactive_request = true;
+ params.count_to_fetch = 10;
+ builder.SetParams(params)
+ .SetUserClassForTesting("ACTIVE_NTP_USER")
+ .SetPersonalization(Personalization::kNonPersonal)
+ .SetFetchAPI(FetchAPI::CHROME_READER_API);
+
+ EXPECT_THAT(builder.PreviewRequestHeadersForTesting(),
+ StrEq("Content-Type: application/json; charset=UTF-8\r\n"
+ "\r\n"));
+ EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
+ EqualsJSON("{"
+ " \"response_detail_level\": \"STANDARD\","
+ " \"advanced_options\": {"
+ " \"local_scoring_params\": {"
+ " \"content_params\": {"
+ " \"only_return_personalized_results\": false"
+ " },"
+ " \"content_restricts\": ["
+ " {"
+ " \"type\": \"METADATA\","
+ " \"value\": \"TITLE\""
+ " },"
+ " {"
+ " \"type\": \"METADATA\","
+ " \"value\": \"SNIPPET\""
+ " },"
+ " {"
+ " \"type\": \"METADATA\","
+ " \"value\": \"THUMBNAIL\""
+ " }"
+ " ]"
+ " },"
+ " \"global_scoring_params\": {"
+ " \"num_to_return\": 10,"
+ " \"sort_type\": 1"
+ " }"
+ " }"
+ "}"));
+
+ builder.SetFetchAPI(FetchAPI::CHROME_CONTENT_SUGGESTIONS_API);
+ EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
+ EqualsJSON("{"
+ " \"priority\": \"USER_ACTION\","
+ " \"excludedSuggestionIds\": [],"
+ " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
+ "}"));
+}
+
+TEST_F(JsonRequestTest, BuildRequestExcludedIds) {
+ JsonRequest::Builder builder;
+ RequestParams params;
+ params.interactive_request = false;
+ for (int i = 0; i < 200; ++i) {
+ params.excluded_ids.insert(base::StringPrintf("%03d", i));
+ }
+ builder.SetParams(params)
+ .SetUserClassForTesting("ACTIVE_NTP_USER")
+ .SetPersonalization(Personalization::kNonPersonal)
+ .SetFetchAPI(FetchAPI::CHROME_CONTENT_SUGGESTIONS_API);
+
+ EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
+ EqualsJSON("{"
+ " \"priority\": \"BACKGROUND_PREFETCH\","
+ " \"excludedSuggestionIds\": ["
+ " \"000\", \"001\", \"002\", \"003\", \"004\","
+ " \"005\", \"006\", \"007\", \"008\", \"009\","
+ " \"010\", \"011\", \"012\", \"013\", \"014\","
+ " \"015\", \"016\", \"017\", \"018\", \"019\","
+ " \"020\", \"021\", \"022\", \"023\", \"024\","
+ " \"025\", \"026\", \"027\", \"028\", \"029\","
+ " \"030\", \"031\", \"032\", \"033\", \"034\","
+ " \"035\", \"036\", \"037\", \"038\", \"039\","
+ " \"040\", \"041\", \"042\", \"043\", \"044\","
+ " \"045\", \"046\", \"047\", \"048\", \"049\","
+ " \"050\", \"051\", \"052\", \"053\", \"054\","
+ " \"055\", \"056\", \"057\", \"058\", \"059\","
+ " \"060\", \"061\", \"062\", \"063\", \"064\","
+ " \"065\", \"066\", \"067\", \"068\", \"069\","
+ " \"070\", \"071\", \"072\", \"073\", \"074\","
+ " \"075\", \"076\", \"077\", \"078\", \"079\","
+ " \"080\", \"081\", \"082\", \"083\", \"084\","
+ " \"085\", \"086\", \"087\", \"088\", \"089\","
+ " \"090\", \"091\", \"092\", \"093\", \"094\","
+ " \"095\", \"096\", \"097\", \"098\", \"099\""
+ // Truncated to 100 entries. Currently, they happen to
+ // be those lexically first.
+ " ],"
+ " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
+ "}"));
+}
+
+TEST_F(JsonRequestTest, BuildRequestNoUserClass) {
+ JsonRequest::Builder builder;
+ RequestParams params;
+ params.interactive_request = false;
+ builder.SetPersonalization(Personalization::kNonPersonal)
+ .SetParams(params)
+ .SetFetchAPI(FetchAPI::CHROME_CONTENT_SUGGESTIONS_API);
+
+ EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
+ EqualsJSON("{"
+ " \"priority\": \"BACKGROUND_PREFETCH\","
+ " \"excludedSuggestionIds\": []"
+ "}"));
+}
+
+TEST_F(JsonRequestTest, BuildRequestWithTwoLanguages) {
+ JsonRequest::Builder builder;
+ std::unique_ptr<translate::LanguageModel> language_model =
+ MakeLanguageModel({"de", "en"});
+ RequestParams params;
+ params.interactive_request = true;
+ params.language_code = "en";
+ builder.SetParams(params)
+ .SetLanguageModel(language_model.get())
+ .SetPersonalization(Personalization::kNonPersonal)
+ .SetFetchAPI(FetchAPI::CHROME_CONTENT_SUGGESTIONS_API);
+
+ EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
+ EqualsJSON("{"
+ " \"priority\": \"USER_ACTION\","
+ " \"uiLanguage\": \"en\","
+ " \"excludedSuggestionIds\": [],"
+ " \"topLanguages\": ["
+ " {"
+ " \"language\" : \"en\","
+ " \"frequency\" : 0.5"
+ " },"
+ " {"
+ " \"language\" : \"de\","
+ " \"frequency\" : 0.5"
+ " }"
+ " ]"
+ "}"));
+}
+
+TEST_F(JsonRequestTest, BuildRequestWithUILanguageOnly) {
+ JsonRequest::Builder builder;
+ std::unique_ptr<translate::LanguageModel> language_model =
+ MakeLanguageModel({"en"});
+ RequestParams params;
+ params.interactive_request = true;
+ params.language_code = "en";
+ builder.SetParams(params)
+ .SetLanguageModel(language_model.get())
+ .SetPersonalization(Personalization::kNonPersonal)
+ .SetFetchAPI(FetchAPI::CHROME_CONTENT_SUGGESTIONS_API);
+
+ EXPECT_THAT(builder.PreviewRequestBodyForTesting(),
+ EqualsJSON("{"
+ " \"priority\": \"USER_ACTION\","
+ " \"uiLanguage\": \"en\","
+ " \"excludedSuggestionIds\": [],"
+ " \"topLanguages\": [{"
+ " \"language\" : \"en\","
+ " \"frequency\" : 1.0"
+ " }]"
+ "}"));
+}
+
+} // namespace internal
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippet.cc b/chromium/components/ntp_snippets/remote/ntp_snippet.cc
index 7b627a20c1f..6335e01bf39 100644
--- a/chromium/components/ntp_snippets/remote/ntp_snippet.cc
+++ b/chromium/components/ntp_snippets/remote/ntp_snippet.cc
@@ -4,15 +4,51 @@
#include "components/ntp_snippets/remote/ntp_snippet.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 {
+struct SnippetSource {
+ SnippetSource(const GURL& url,
+ const std::string& publisher_name,
+ const GURL& amp_url)
+ : url(url), publisher_name(publisher_name), amp_url(amp_url) {}
+ GURL url;
+ std::string publisher_name;
+ GURL amp_url;
+};
+
+const SnippetSource& FindBestSource(const std::vector<SnippetSource>& sources) {
+ // The same article can be hosted by multiple sources, e.g. nytimes.com,
+ // cnn.com, etc. We need to parse the list of sources for this article and
+ // find the best match. In order of preference:
+ // 1) A source that has URL, publisher name, AMP URL
+ // 2) A source that has URL, publisher name
+ // 3) A source that has URL and AMP URL, or URL only (since we won't show
+ // the snippet to users if the article does not have a publisher name, it
+ // doesn't matter whether the snippet has the AMP URL or not)
+ int best_source_index = 0;
+ for (size_t i = 0; i < sources.size(); ++i) {
+ const SnippetSource& source = sources[i];
+ if (!source.publisher_name.empty()) {
+ best_source_index = i;
+ if (!source.amp_url.is_empty()) {
+ // This is the best possible source, stop looking.
+ break;
+ }
+ }
+ }
+ return sources[best_source_index];
+}
+
// dict.Get() specialization for base::Time values
bool GetTimeValue(const base::DictionaryValue& dict,
const std::string& key,
@@ -45,12 +81,15 @@ static_assert(
kArticlesRemoteId,
"kArticlesRemoteId has a wrong value?!");
-NTPSnippet::NTPSnippet(const std::string& id, int remote_category_id)
- : ids_(1, id),
+const int kChromeReaderDefaultExpiryTimeMins = 3 * 24 * 60;
+
+NTPSnippet::NTPSnippet(const std::vector<std::string>& ids,
+ int remote_category_id)
+ : ids_(ids),
score_(0),
is_dismissed_(false),
remote_category_id_(remote_category_id),
- best_source_index_(0) {}
+ should_notify_(false) {}
NTPSnippet::~NTPSnippet() = default;
@@ -58,51 +97,36 @@ NTPSnippet::~NTPSnippet() = default;
std::unique_ptr<NTPSnippet> NTPSnippet::CreateFromChromeReaderDictionary(
const base::DictionaryValue& dict) {
const base::DictionaryValue* content = nullptr;
- if (!dict.GetDictionary("contentInfo", &content))
+ if (!dict.GetDictionary("contentInfo", &content)) {
return nullptr;
+ }
- // Need at least the id.
- std::string id;
- if (!content->GetString("url", &id) || id.empty())
+ // Need at least a primary id.
+ std::string primary_id;
+ if (!content->GetString("url", &primary_id) || primary_id.empty()) {
return nullptr;
-
- std::unique_ptr<NTPSnippet> snippet(new NTPSnippet(id, kArticlesRemoteId));
-
- std::string title;
- if (content->GetString("title", &title))
- snippet->set_title(title);
- std::string salient_image_url;
- if (content->GetString("thumbnailUrl", &salient_image_url))
- snippet->set_salient_image_url(GURL(salient_image_url));
- std::string snippet_str;
- if (content->GetString("snippet", &snippet_str))
- snippet->set_snippet(snippet_str);
- // The creation and expiry timestamps are uint64s which are stored as strings.
- std::string creation_timestamp_str;
- if (content->GetString("creationTimestampSec", &creation_timestamp_str))
- snippet->set_publish_date(TimeFromJsonString(creation_timestamp_str));
- std::string expiry_timestamp_str;
- if (content->GetString("expiryTimestampSec", &expiry_timestamp_str))
- snippet->set_expiry_date(TimeFromJsonString(expiry_timestamp_str));
+ }
const base::ListValue* corpus_infos_list = nullptr;
if (!content->GetList("sourceCorpusInfo", &corpus_infos_list)) {
- DLOG(WARNING) << "No sources found for article " << title;
+ DLOG(WARNING) << "No sources found for article " << primary_id;
return nullptr;
}
- std::vector<std::string> additional_ids;
+ std::vector<std::string> ids(1, primary_id);
+ std::vector<SnippetSource> sources;
for (const auto& value : *corpus_infos_list) {
const base::DictionaryValue* dict_value = nullptr;
if (!value->GetAsDictionary(&dict_value)) {
- DLOG(WARNING) << "Invalid source info for article " << id;
+ DLOG(WARNING) << "Invalid source info for article " << primary_id;
continue;
}
std::string corpus_id_str;
GURL corpus_id;
- if (dict_value->GetString("corpusId", &corpus_id_str))
+ if (dict_value->GetString("corpusId", &corpus_id_str)) {
corpus_id = GURL(corpus_id_str);
+ }
if (!corpus_id.is_valid()) {
// We must at least have a valid source URL.
@@ -128,26 +152,61 @@ std::unique_ptr<NTPSnippet> NTPSnippet::CreateFromChromeReaderDictionary(
DLOG_IF(WARNING, !amp_url.is_valid()) << "Invalid AMP url "
<< amp_url_str;
}
- SnippetSource source(corpus_id, site_title,
+ sources.emplace_back(corpus_id, site_title,
amp_url.is_valid() ? amp_url : GURL());
- snippet->add_source(source);
// We use the raw string so that we can compare it against other primary
// IDs. Parsing the ID as a URL might add a trailing slash (and we don't do
// this for the primary ID).
- additional_ids.push_back(corpus_id_str);
+ ids.push_back(corpus_id_str);
}
- snippet->AddIDs(additional_ids);
-
- if (snippet->sources_.empty()) {
- DLOG(WARNING) << "No sources found for article " << id;
+ if (sources.empty()) {
+ DLOG(WARNING) << "No sources found for article " << primary_id;
return nullptr;
}
- snippet->InitBestSource();
+ std::unique_ptr<NTPSnippet> snippet(new NTPSnippet(ids, kArticlesRemoteId));
+
+ std::string title;
+ if (content->GetString("title", &title)) {
+ snippet->title_ = title;
+ }
+ std::string salient_image_url;
+ if (content->GetString("thumbnailUrl", &salient_image_url)) {
+ snippet->salient_image_url_ = GURL(salient_image_url);
+ }
+ std::string snippet_str;
+ if (content->GetString("snippet", &snippet_str)) {
+ snippet->snippet_ = snippet_str;
+ }
+ // The creation and expiry timestamps are uint64s which are stored as strings.
+ std::string creation_timestamp_str;
+ if (content->GetString("creationTimestampSec", &creation_timestamp_str)) {
+ snippet->publish_date_ = TimeFromJsonString(creation_timestamp_str);
+ }
+ std::string expiry_timestamp_str;
+ if (content->GetString("expiryTimestampSec", &expiry_timestamp_str)) {
+ snippet->expiry_date_ = TimeFromJsonString(expiry_timestamp_str);
+ }
+
+ // If publish and/or expiry date are missing, fill in reasonable defaults.
+ if (snippet->publish_date_.is_null()) {
+ snippet->publish_date_ = base::Time::Now();
+ }
+ if (snippet->expiry_date_.is_null()) {
+ snippet->expiry_date_ =
+ snippet->publish_date() +
+ base::TimeDelta::FromMinutes(kChromeReaderDefaultExpiryTimeMins);
+ }
+
+ const SnippetSource& source = FindBestSource(sources);
+ snippet->url_ = source.url;
+ snippet->publisher_name_ = source.publisher_name;
+ snippet->amp_url_ = source.amp_url;
double score;
- if (dict.GetDouble("score", &score))
- snippet->set_score(score);
+ if (dict.GetDouble("score", &score)) {
+ snippet->score_ = score;
+ }
return snippet;
}
@@ -157,39 +216,51 @@ std::unique_ptr<NTPSnippet> NTPSnippet::CreateFromContentSuggestionsDictionary(
const base::DictionaryValue& dict,
int remote_category_id) {
const base::ListValue* ids;
- if (!dict.GetList("ids", &ids)) return nullptr;
+ if (!dict.GetList("ids", &ids)) {
+ return nullptr;
+ }
std::vector<std::string> parsed_ids;
for (const auto& value : *ids) {
std::string id;
- if (!value->GetAsString(&id)) return nullptr;
+ if (!value->GetAsString(&id)) {
+ return nullptr;
+ }
parsed_ids.push_back(id);
}
- if (parsed_ids.empty()) return nullptr;
- auto snippet =
- base::MakeUnique<NTPSnippet>(parsed_ids.front(), remote_category_id);
- parsed_ids.erase(parsed_ids.begin(), parsed_ids.begin() + 1);
- snippet->AddIDs(parsed_ids);
-
- snippet->sources_.emplace_back(GURL(), std::string(), GURL());
- auto* source = &snippet->sources_.back();
- snippet->best_source_index_ = 0;
+ if (parsed_ids.empty()) {
+ return nullptr;
+ }
+ auto snippet = MakeUnique(parsed_ids, remote_category_id);
if (!(dict.GetString("title", &snippet->title_) &&
dict.GetString("snippet", &snippet->snippet_) &&
GetTimeValue(dict, "creationTime", &snippet->publish_date_) &&
GetTimeValue(dict, "expirationTime", &snippet->expiry_date_) &&
GetURLValue(dict, "imageUrl", &snippet->salient_image_url_) &&
- dict.GetString("attribution", &source->publisher_name) &&
- GetURLValue(dict, "fullPageUrl", &source->url))) {
+ dict.GetString("attribution", &snippet->publisher_name_) &&
+ GetURLValue(dict, "fullPageUrl", &snippet->url_))) {
return nullptr;
}
- GetURLValue(dict, "ampUrl", &source->amp_url); // May fail; OK.
+ GetURLValue(dict, "ampUrl", &snippet->amp_url_); // May fail; OK.
// TODO(sfiera): also favicon URL.
double score;
- if (dict.GetDouble("score", &score))
- snippet->set_score(score);
+ if (dict.GetDouble("score", &score)) {
+ snippet->score_ = score;
+ }
+
+ const base::DictionaryValue* notification_info = nullptr;
+ if (dict.GetDictionary("notificationInfo", &notification_info)) {
+ if (notification_info->GetBoolean("shouldNotify",
+ &snippet->should_notify_) &&
+ snippet->should_notify_) {
+ if (!GetTimeValue(*notification_info, "deadline",
+ &snippet->notification_deadline_)) {
+ snippet->notification_deadline_ = base::Time::Max();
+ }
+ }
+ }
return snippet;
}
@@ -198,26 +269,26 @@ std::unique_ptr<NTPSnippet> NTPSnippet::CreateFromContentSuggestionsDictionary(
std::unique_ptr<NTPSnippet> NTPSnippet::CreateFromProto(
const SnippetProto& proto) {
// Need at least the id.
- if (proto.ids_size() == 0 || proto.ids(0).empty())
+ if (proto.ids_size() == 0 || proto.ids(0).empty()) {
return nullptr;
+ }
int remote_category_id = proto.has_remote_category_id()
? proto.remote_category_id()
: kArticlesRemoteId;
- auto snippet = base::MakeUnique<NTPSnippet>(proto.ids(0), remote_category_id);
- snippet->AddIDs(
- std::vector<std::string>(proto.ids().begin() + 1, proto.ids().end()));
+ std::vector<std::string> ids(proto.ids().begin(), proto.ids().end());
+ auto snippet = MakeUnique(ids, remote_category_id);
- snippet->set_title(proto.title());
- snippet->set_snippet(proto.snippet());
- snippet->set_salient_image_url(GURL(proto.salient_image_url()));
- snippet->set_publish_date(
- base::Time::FromInternalValue(proto.publish_date()));
- snippet->set_expiry_date(base::Time::FromInternalValue(proto.expiry_date()));
- snippet->set_score(proto.score());
- snippet->set_dismissed(proto.dismissed());
+ snippet->title_ = proto.title();
+ snippet->snippet_ = proto.snippet();
+ snippet->salient_image_url_ = GURL(proto.salient_image_url());
+ snippet->publish_date_ = base::Time::FromInternalValue(proto.publish_date());
+ snippet->expiry_date_ = base::Time::FromInternalValue(proto.expiry_date());
+ snippet->score_ = proto.score();
+ snippet->is_dismissed_ = proto.dismissed();
+ std::vector<SnippetSource> sources;
for (int i = 0; i < proto.sources_size(); ++i) {
const SnippetSourceProto& source_proto = proto.sources(i);
GURL url(source_proto.url());
@@ -233,16 +304,33 @@ std::unique_ptr<NTPSnippet> NTPSnippet::CreateFromProto(
<< source_proto.amp_url();
}
- snippet->add_source(
- SnippetSource(url, source_proto.publisher_name(), amp_url));
+ sources.emplace_back(url, source_proto.publisher_name(), amp_url);
}
- if (snippet->sources_.empty()) {
+ if (sources.empty()) {
DLOG(WARNING) << "No sources found for article " << snippet->id();
return nullptr;
}
+ const SnippetSource& source = FindBestSource(sources);
+ snippet->url_ = source.url;
+ snippet->publisher_name_ = source.publisher_name;
+ snippet->amp_url_ = source.amp_url;
- snippet->InitBestSource();
+ return snippet;
+}
+
+// static
+std::unique_ptr<NTPSnippet> NTPSnippet::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;
return snippet;
}
@@ -252,34 +340,56 @@ SnippetProto NTPSnippet::ToProto() const {
for (const std::string& id : ids_) {
result.add_ids(id);
}
- if (!title_.empty())
+ if (!title_.empty()) {
result.set_title(title_);
- if (!snippet_.empty())
+ }
+ if (!snippet_.empty()) {
result.set_snippet(snippet_);
- if (salient_image_url_.is_valid())
+ }
+ if (salient_image_url_.is_valid()) {
result.set_salient_image_url(salient_image_url_.spec());
- if (!publish_date_.is_null())
+ }
+ if (!publish_date_.is_null()) {
result.set_publish_date(publish_date_.ToInternalValue());
- if (!expiry_date_.is_null())
+ }
+ if (!expiry_date_.is_null()) {
result.set_expiry_date(expiry_date_.ToInternalValue());
+ }
result.set_score(score_);
result.set_dismissed(is_dismissed_);
result.set_remote_category_id(remote_category_id_);
- for (const SnippetSource& source : sources_) {
- SnippetSourceProto* source_proto = result.add_sources();
- source_proto->set_url(source.url.spec());
- if (!source.publisher_name.empty())
- source_proto->set_publisher_name(source.publisher_name);
- if (source.amp_url.is_valid())
- source_proto->set_amp_url(source.amp_url.spec());
+ SnippetSourceProto* source_proto = result.add_sources();
+ source_proto->set_url(url_.spec());
+ if (!publisher_name_.empty()) {
+ source_proto->set_publisher_name(publisher_name_);
+ }
+ if (amp_url_.is_valid()) {
+ source_proto->set_amp_url(amp_url_.spec());
}
return result;
}
-void NTPSnippet::AddIDs(const std::vector<std::string>& ids) {
- ids_.insert(ids_.end(), ids.begin(), ids.end());
+ContentSuggestion NTPSnippet::ToContentSuggestion(Category category) const {
+ GURL url = url_;
+ if (base::FeatureList::IsEnabled(kPreferAmpUrlsFeature) &&
+ !amp_url_.is_empty()) {
+ url = amp_url_;
+ }
+ ContentSuggestion suggestion(category, id(), url);
+ suggestion.set_title(base::UTF8ToUTF16(title_));
+ suggestion.set_snippet_text(base::UTF8ToUTF16(snippet_));
+ suggestion.set_publish_date(publish_date_);
+ suggestion.set_publisher_name(base::UTF8ToUTF16(publisher_name_));
+ suggestion.set_score(score_);
+ if (should_notify_) {
+ NotificationExtra extra;
+ extra.deadline = notification_deadline_;
+ suggestion.set_notification_extra(
+ base::MakeUnique<NotificationExtra>(extra));
+ }
+ return suggestion;
}
// static
@@ -288,6 +398,7 @@ base::Time NTPSnippet::TimeFromJsonString(const std::string& timestamp_str) {
if (!base::StringToInt64(timestamp_str, &timestamp)) {
// Even if there's an error in the conversion, some garbage data may still
// be written to the output var, so reset it.
+ DLOG(WARNING) << "Invalid json timestamp: " << timestamp_str;
timestamp = 0;
}
return base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(timestamp);
@@ -298,26 +409,10 @@ std::string NTPSnippet::TimeToJsonString(const base::Time& time) {
return base::Int64ToString((time - base::Time::UnixEpoch()).InSeconds());
}
-void NTPSnippet::InitBestSource() {
- // The same article can be hosted by multiple sources, e.g. nytimes.com,
- // cnn.com, etc. We need to parse the list of sources for this article and
- // find the best match. In order of preference:
- // 1 A source that has URL, publisher name, AMP URL
- // 2) A source that has URL, publisher name
- // 3) A source that has URL and AMP URL, or URL only (since we won't show
- // the snippet to users if the article does not have a publisher name, it
- // doesn't matter whether the snippet has the AMP URL or not)
- best_source_index_ = 0;
- for (size_t i = 0; i < sources_.size(); ++i) {
- const SnippetSource& source = sources_[i];
- if (!source.publisher_name.empty()) {
- best_source_index_ = i;
- if (!source.amp_url.is_empty()) {
- // This is the best possible source, stop looking.
- break;
- }
- }
- }
+// static
+std::unique_ptr<NTPSnippet> NTPSnippet::MakeUnique(
+ const std::vector<std::string>& ids, int remote_category_id) {
+ return base::WrapUnique(new NTPSnippet(ids, remote_category_id));
}
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippet.h b/chromium/components/ntp_snippets/remote/ntp_snippet.h
index 949a0459263..88c10a07782 100644
--- a/chromium/components/ntp_snippets/remote/ntp_snippet.h
+++ b/chromium/components/ntp_snippets/remote/ntp_snippet.h
@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "base/time/time.h"
+#include "components/ntp_snippets/content_suggestion.h"
#include "url/gurl.h"
namespace base {
@@ -20,30 +21,16 @@ class DictionaryValue;
namespace ntp_snippets {
+// Exposed for tests.
extern const int kArticlesRemoteId;
+extern const int kChromeReaderDefaultExpiryTimeMins;
class SnippetProto;
-struct SnippetSource {
- SnippetSource(const GURL& url,
- const std::string& publisher_name,
- const GURL& amp_url)
- : url(url), publisher_name(publisher_name), amp_url(amp_url) {}
- GURL url;
- std::string publisher_name;
- GURL amp_url;
-};
-
class NTPSnippet {
public:
using PtrVector = std::vector<std::unique_ptr<NTPSnippet>>;
- // Creates a new snippet with the given |id|.
- // Public for testing only - create snippets using the Create* methods below.
- // TODO(treib): Make this private and add a CreateSnippetForTest. The
- // constructor can then also take the vector of ids as the handling of unique
- // ids is then completely encapsulated inside the class.
- NTPSnippet(const std::string& id, int remote_category_id);
~NTPSnippet();
// Creates an NTPSnippet from a dictionary, as returned by Chrome Reader.
@@ -64,9 +51,20 @@ class NTPSnippet {
// protocol buffer doesn't correspond to a valid snippet.
static std::unique_ptr<NTPSnippet> CreateFromProto(const SnippetProto& proto);
+ // TODO(treib): Make tests use the public interface and remove this.
+ static std::unique_ptr<NTPSnippet> 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 snippet, for persisting.
SnippetProto ToProto() const;
+ // Coverts to general content suggestion form
+ ContentSuggestion ToContentSuggestion(Category category) const;
+
// Returns all ids of the snippet.
const std::vector<std::string>& GetAllIDs() const { return ids_; }
@@ -75,56 +73,44 @@ class NTPSnippet {
// Title of the snippet.
const std::string& title() const { return title_; }
- void set_title(const std::string& title) { title_ = title; }
+
+ // The main URL pointing to the content web page.
+ const GURL& url() const { return url_; }
+
+ // The name of the content's publisher.
+ const std::string& publisher_name() const { return publisher_name_; }
+
+ // Link to an AMP version of the content web page, if it exists.
+ const GURL& amp_url() const { return amp_url_; }
// Summary or relevant extract from the content.
const std::string& snippet() const { return snippet_; }
- void set_snippet(const std::string& snippet) { snippet_ = snippet; }
// Link to an image representative of the content. Do not fetch this image
// directly. If initialized by CreateFromChromeReaderDictionary() the relevant
// key is 'thumbnailUrl'
const GURL& salient_image_url() const { return salient_image_url_; }
- void set_salient_image_url(const GURL& salient_image_url) {
- salient_image_url_ = salient_image_url;
- }
// When the page pointed by this snippet was published. If initialized by
// CreateFromChromeReaderDictionary() the relevant key is
// 'creationTimestampSec'
const base::Time& publish_date() const { return publish_date_; }
- void set_publish_date(const base::Time& publish_date) {
- publish_date_ = publish_date;
- }
// After this expiration date this snippet should no longer be presented to
// the user.
const base::Time& expiry_date() const { return expiry_date_; }
- void set_expiry_date(const base::Time& expiry_date) {
- expiry_date_ = expiry_date;
- }
-
- // We should never construct an NTPSnippet object if we don't have any sources
- // so this should never fail
- const SnippetSource& best_source() const {
- return sources_[best_source_index_];
- }
-
- // Adds a source to the snippet.
- // TODO(tschumann): Remove this from the NTPSnippet interface
- // (NTPSnippetsDatabaseTest is currently using it and should be changed).
- void add_source(const SnippetSource& source) { sources_.push_back(source); }
// If this snippet has all the data we need to show a full card to the user
bool is_complete() const {
- return !id().empty() && !sources().empty() && !title().empty() &&
- !snippet().empty() && salient_image_url().is_valid() &&
- !publish_date().is_null() && !expiry_date().is_null() &&
- !best_source().publisher_name.empty();
+ return !id().empty() && !title().empty() && !snippet().empty() &&
+ salient_image_url().is_valid() && !publish_date().is_null() &&
+ !expiry_date().is_null() && !publisher_name().empty();
}
float score() const { return score_; }
- void set_score(float score) { score_ = score; }
+
+ bool should_notify() const { return should_notify_; }
+ base::Time notification_deadline() const { return notification_deadline_; }
bool is_dismissed() const { return is_dismissed_; }
void set_dismissed(bool dismissed) { is_dismissed_ = dismissed; }
@@ -138,13 +124,21 @@ class NTPSnippet {
static std::string TimeToJsonString(const base::Time& time);
private:
- void InitBestSource();
- void AddIDs(const std::vector<std::string>& ids);
- const std::vector<SnippetSource>& sources() const { return sources_; }
+ NTPSnippet(const std::vector<std::string>& ids, int remote_category_id);
+
+ // base::MakeUnique doesn't work if the ctor is private.
+ static std::unique_ptr<NTPSnippet> MakeUnique(
+ const std::vector<std::string>& ids, int remote_category_id);
// The first ID in the vector is the primary id.
std::vector<std::string> ids_;
std::string title_;
+ GURL url_;
+ std::string publisher_name_;
+
+ // TODO(mvanouwerkerk): Remove this field and its uses, just use |url_|.
+ GURL amp_url_;
+
GURL salient_image_url_;
std::string snippet_;
base::Time publish_date_;
@@ -153,9 +147,8 @@ class NTPSnippet {
bool is_dismissed_;
int remote_category_id_;
- size_t best_source_index_;
-
- std::vector<SnippetSource> sources_;
+ bool should_notify_;
+ base::Time notification_deadline_;
DISALLOW_COPY_AND_ASSIGN(NTPSnippet);
};
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippet_unittest.cc b/chromium/components/ntp_snippets/remote/ntp_snippet_unittest.cc
index ebd061016dc..b8d591ace6f 100644
--- a/chromium/components/ntp_snippets/remote/ntp_snippet_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/ntp_snippet_unittest.cc
@@ -7,6 +7,8 @@
#include <utility>
#include "base/json/json_reader.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/ntp_snippets/remote/proto/ntp_snippets.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -16,8 +18,9 @@ namespace ntp_snippets {
namespace {
using ::testing::ElementsAre;
+using ::testing::Eq;
using ::testing::IsNull;
-using ::testing::Not;
+using ::testing::NotNull;
std::unique_ptr<NTPSnippet> SnippetFromContentSuggestionJSON(
const std::string& json) {
@@ -43,10 +46,14 @@ TEST(NTPSnippetTest, FromChromeContentSuggestionsDictionary) {
" \"imageUrl\" : \"http://localhost/foobar.jpg\","
" \"ampUrl\" : \"http://localhost/amp\","
" \"faviconUrl\" : \"http://localhost/favicon.ico\", "
- " \"score\": 9001\n"
+ " \"score\": 9001,\n"
+ " \"notificationInfo\": {\n"
+ " \"shouldNotify\": true,"
+ " \"deadline\": \"2016-06-30T13:01:37.000Z\"\n"
+ " }\n"
"}";
auto snippet = SnippetFromContentSuggestionJSON(kJsonStr);
- ASSERT_THAT(snippet, testing::NotNull());
+ ASSERT_THAT(snippet, NotNull());
EXPECT_EQ(snippet->id(), "http://localhost/foobar");
EXPECT_EQ(snippet->title(), "Foo Barred from Baz");
@@ -58,9 +65,14 @@ TEST(NTPSnippetTest, FromChromeContentSuggestionsDictionary) {
EXPECT_FLOAT_EQ(unix_publish_date.InSecondsF(), 1467284497.000000f);
EXPECT_FLOAT_EQ(expiry_duration.InSecondsF(), 86400.000000f);
- EXPECT_EQ(snippet->best_source().publisher_name, "Foo News");
- EXPECT_EQ(snippet->best_source().url, GURL("http://localhost/foobar"));
- EXPECT_EQ(snippet->best_source().amp_url, GURL("http://localhost/amp"));
+ EXPECT_EQ(snippet->publisher_name(), "Foo News");
+ EXPECT_EQ(snippet->url(), GURL("http://localhost/foobar"));
+ EXPECT_EQ(snippet->amp_url(), GURL("http://localhost/amp"));
+
+ EXPECT_TRUE(snippet->should_notify());
+ auto notification_duration =
+ snippet->notification_deadline() - snippet->publish_date();
+ EXPECT_EQ(7200.0f, notification_duration.InSecondsF());
}
std::unique_ptr<NTPSnippet> SnippetFromChromeReaderDict(
@@ -71,16 +83,20 @@ std::unique_ptr<NTPSnippet> SnippetFromChromeReaderDict(
return NTPSnippet::CreateFromChromeReaderDictionary(*dict);
}
-std::unique_ptr<base::DictionaryValue> SnippetWithTwoSources() {
- const std::string kJsonStr =
+const char kChromeReaderCreationTimestamp[] = "1234567890";
+const char kChromeReaderExpiryTimestamp[] = "2345678901";
+
+// Old form, from chromereader-pa.googleapis.com. Two sources.
+std::unique_ptr<base::DictionaryValue> ChromeReaderSnippetWithTwoSources() {
+ const std::string kJsonStr = base::StringPrintf(
"{\n"
" \"contentInfo\": {\n"
" \"url\": \"http://url.com\",\n"
" \"title\": \"Source 1 Title\",\n"
" \"snippet\": \"Source 1 Snippet\",\n"
" \"thumbnailUrl\": \"http://url.com/thumbnail\",\n"
- " \"creationTimestampSec\": 1234567890,\n"
- " \"expiryTimestampSec\": 2345678901,\n"
+ " \"creationTimestampSec\": \"%s\",\n"
+ " \"expiryTimestampSec\": \"%s\",\n"
" \"sourceCorpusInfo\": [{\n"
" \"corpusId\": \"http://source1.com\",\n"
" \"publisherData\": {\n"
@@ -96,7 +112,8 @@ std::unique_ptr<base::DictionaryValue> SnippetWithTwoSources() {
" }]\n"
" },\n"
" \"score\": 5.0\n"
- "}\n";
+ "}\n",
+ kChromeReaderCreationTimestamp, kChromeReaderExpiryTimestamp);
auto json_value = base::JSONReader::Read(kJsonStr);
base::DictionaryValue* json_dict;
@@ -107,21 +124,21 @@ std::unique_ptr<base::DictionaryValue> SnippetWithTwoSources() {
}
TEST(NTPSnippetTest, TestMultipleSources) {
- auto snippet = SnippetFromChromeReaderDict(SnippetWithTwoSources());
- ASSERT_THAT(snippet, testing::NotNull());
+ auto snippet =
+ SnippetFromChromeReaderDict(ChromeReaderSnippetWithTwoSources());
+ ASSERT_THAT(snippet, NotNull());
// Expect the first source to be chosen.
- EXPECT_EQ(snippet->ToProto().sources_size(), 2);
EXPECT_EQ(snippet->id(), "http://url.com");
- EXPECT_EQ(snippet->best_source().url, GURL("http://source1.com"));
- EXPECT_EQ(snippet->best_source().publisher_name, std::string("Source 1"));
- EXPECT_EQ(snippet->best_source().amp_url, GURL("http://source1.amp.com"));
+ EXPECT_EQ(snippet->url(), GURL("http://source1.com"));
+ EXPECT_EQ(snippet->publisher_name(), std::string("Source 1"));
+ EXPECT_EQ(snippet->amp_url(), GURL("http://source1.amp.com"));
}
TEST(NTPSnippetTest, TestMultipleIncompleteSources1) {
// Set Source 2 to have no AMP url, and Source 1 to have no publisher name
// Source 2 should win since we favor publisher name over amp url
- auto dict = SnippetWithTwoSources();
+ auto dict = ChromeReaderSnippetWithTwoSources();
base::ListValue* sources;
ASSERT_TRUE(dict->GetList("contentInfo.sourceCorpusInfo", &sources));
base::DictionaryValue* source;
@@ -131,19 +148,18 @@ TEST(NTPSnippetTest, TestMultipleIncompleteSources1) {
source->Remove("ampUrl", nullptr);
auto snippet = SnippetFromChromeReaderDict(std::move(dict));
- ASSERT_THAT(snippet, testing::NotNull());
+ ASSERT_THAT(snippet, NotNull());
- EXPECT_EQ(snippet->ToProto().sources_size(), 2);
EXPECT_EQ(snippet->id(), "http://url.com");
- EXPECT_EQ(snippet->best_source().url, GURL("http://source2.com"));
- EXPECT_EQ(snippet->best_source().publisher_name, std::string("Source 2"));
- EXPECT_EQ(snippet->best_source().amp_url, GURL());
+ EXPECT_EQ(snippet->url(), GURL("http://source2.com"));
+ EXPECT_EQ(snippet->publisher_name(), std::string("Source 2"));
+ EXPECT_EQ(snippet->amp_url(), GURL());
}
TEST(NTPSnippetTest, TestMultipleIncompleteSources2) {
// Set Source 1 to have no AMP url, and Source 2 to have no publisher name
// Source 1 should win in this case since we prefer publisher name to AMP url
- auto dict = SnippetWithTwoSources();
+ auto dict = ChromeReaderSnippetWithTwoSources();
base::ListValue* sources;
ASSERT_TRUE(dict->GetList("contentInfo.sourceCorpusInfo", &sources));
base::DictionaryValue* source;
@@ -153,20 +169,19 @@ TEST(NTPSnippetTest, TestMultipleIncompleteSources2) {
source->Remove("publisherData.sourceName", nullptr);
auto snippet = SnippetFromChromeReaderDict(std::move(dict));
- ASSERT_THAT(snippet, testing::NotNull());
+ ASSERT_THAT(snippet, NotNull());
- EXPECT_EQ(snippet->ToProto().sources_size(), 2);
EXPECT_EQ(snippet->id(), "http://url.com");
- EXPECT_EQ(snippet->best_source().url, GURL("http://source1.com"));
- EXPECT_EQ(snippet->best_source().publisher_name, std::string("Source 1"));
- EXPECT_EQ(snippet->best_source().amp_url, GURL());
+ EXPECT_EQ(snippet->url(), GURL("http://source1.com"));
+ EXPECT_EQ(snippet->publisher_name(), std::string("Source 1"));
+ EXPECT_EQ(snippet->amp_url(), GURL());
}
TEST(NTPSnippetTest, TestMultipleIncompleteSources3) {
// Set source 1 to have no AMP url and no source, and source 2 to only have
// amp url. There should be no snippets since we only add sources we consider
// complete
- auto dict = SnippetWithTwoSources();
+ auto dict = ChromeReaderSnippetWithTwoSources();
base::ListValue* sources;
ASSERT_TRUE(dict->GetList("contentInfo.sourceCorpusInfo", &sources));
base::DictionaryValue* source;
@@ -177,20 +192,90 @@ TEST(NTPSnippetTest, TestMultipleIncompleteSources3) {
source->Remove("publisherData.sourceName", nullptr);
auto snippet = SnippetFromChromeReaderDict(std::move(dict));
- ASSERT_THAT(snippet, testing::NotNull());
+ ASSERT_THAT(snippet, NotNull());
ASSERT_FALSE(snippet->is_complete());
}
-std::unique_ptr<base::DictionaryValue> SnippetWithThreeSources() {
- const std::string kJsonStr =
+TEST(NTPSnippetTest, ShouldFillInCreation) {
+ auto dict = ChromeReaderSnippetWithTwoSources();
+ ASSERT_TRUE(dict->Remove("contentInfo.creationTimestampSec", nullptr));
+ auto snippet = SnippetFromChromeReaderDict(std::move(dict));
+ ASSERT_THAT(snippet, NotNull());
+
+ // Publish date should have been filled with "now" - just make sure it's not
+ // empty and not the test default value.
+ base::Time publish_date = snippet->publish_date();
+ EXPECT_FALSE(publish_date.is_null());
+ EXPECT_NE(publish_date,
+ NTPSnippet::TimeFromJsonString(kChromeReaderCreationTimestamp));
+ // Expiry date should have kept the test default value.
+ base::Time expiry_date = snippet->expiry_date();
+ EXPECT_FALSE(expiry_date.is_null());
+ EXPECT_EQ(expiry_date,
+ NTPSnippet::TimeFromJsonString(kChromeReaderExpiryTimestamp));
+}
+
+TEST(NTPSnippetTest, ShouldFillInExpiry) {
+ auto dict = ChromeReaderSnippetWithTwoSources();
+ ASSERT_TRUE(dict->Remove("contentInfo.expiryTimestampSec", nullptr));
+ auto snippet = SnippetFromChromeReaderDict(std::move(dict));
+ ASSERT_THAT(snippet, NotNull());
+
+ base::Time publish_date = snippet->publish_date();
+ ASSERT_FALSE(publish_date.is_null());
+ // Expiry date should have been filled with creation date + offset.
+ base::Time expiry_date = snippet->expiry_date();
+ EXPECT_FALSE(expiry_date.is_null());
+ EXPECT_EQ(publish_date + base::TimeDelta::FromMinutes(
+ kChromeReaderDefaultExpiryTimeMins),
+ expiry_date);
+}
+
+TEST(NTPSnippetTest, ShouldFillInCreationAndExpiry) {
+ auto dict = ChromeReaderSnippetWithTwoSources();
+ ASSERT_TRUE(dict->Remove("contentInfo.creationTimestampSec", nullptr));
+ ASSERT_TRUE(dict->Remove("contentInfo.expiryTimestampSec", nullptr));
+ auto snippet = SnippetFromChromeReaderDict(std::move(dict));
+ ASSERT_THAT(snippet, NotNull());
+
+ // Publish date should have been filled with "now" - just make sure it's not
+ // empty and not the test default value.
+ base::Time publish_date = snippet->publish_date();
+ EXPECT_FALSE(publish_date.is_null());
+ EXPECT_NE(publish_date,
+ NTPSnippet::TimeFromJsonString(kChromeReaderCreationTimestamp));
+ // Expiry date should have been filled with creation date + offset.
+ base::Time expiry_date = snippet->expiry_date();
+ EXPECT_FALSE(expiry_date.is_null());
+ EXPECT_EQ(publish_date + base::TimeDelta::FromMinutes(
+ kChromeReaderDefaultExpiryTimeMins),
+ expiry_date);
+}
+
+TEST(NTPSnippetTest, ShouldNotOverwriteExpiry) {
+ auto dict = ChromeReaderSnippetWithTwoSources();
+ ASSERT_TRUE(dict->Remove("contentInfo.creationTimestampSec", nullptr));
+ auto snippet = SnippetFromChromeReaderDict(std::move(dict));
+ ASSERT_THAT(snippet, NotNull());
+
+ // Expiry date should have kept the test default value.
+ base::Time expiry_date = snippet->expiry_date();
+ EXPECT_FALSE(expiry_date.is_null());
+ EXPECT_EQ(expiry_date,
+ NTPSnippet::TimeFromJsonString(kChromeReaderExpiryTimestamp));
+}
+
+// Old form, from chromereader-pa.googleapis.com. Three sources.
+std::unique_ptr<base::DictionaryValue> ChromeReaderSnippetWithThreeSources() {
+ const std::string kJsonStr = base::StringPrintf(
"{\n"
" \"contentInfo\": {\n"
" \"url\": \"http://url.com\",\n"
" \"title\": \"Source 1 Title\",\n"
" \"snippet\": \"Source 1 Snippet\",\n"
" \"thumbnailUrl\": \"http://url.com/thumbnail\",\n"
- " \"creationTimestampSec\": 1234567890,\n"
- " \"expiryTimestampSec\": 2345678901,\n"
+ " \"creationTimestampSec\": \"%s\",\n"
+ " \"expiryTimestampSec\": \"%s\",\n"
" \"sourceCorpusInfo\": [{\n"
" \"corpusId\": \"http://source1.com\",\n"
" \"publisherData\": {\n"
@@ -212,7 +297,8 @@ std::unique_ptr<base::DictionaryValue> SnippetWithThreeSources() {
" }]\n"
" },\n"
" \"score\": 5.0\n"
- "}\n";
+ "}\n",
+ kChromeReaderCreationTimestamp, kChromeReaderExpiryTimestamp);
auto json_value = base::JSONReader::Read(kJsonStr);
base::DictionaryValue* json_dict;
@@ -224,7 +310,7 @@ std::unique_ptr<base::DictionaryValue> SnippetWithThreeSources() {
TEST(NTPSnippetTest, TestMultipleCompleteSources1) {
// Test 2 complete sources, we should choose the first complete source
- auto dict = SnippetWithThreeSources();
+ auto dict = ChromeReaderSnippetWithThreeSources();
base::ListValue* sources;
ASSERT_TRUE(dict->GetList("contentInfo.sourceCorpusInfo", &sources));
base::DictionaryValue* source;
@@ -232,21 +318,20 @@ TEST(NTPSnippetTest, TestMultipleCompleteSources1) {
source->Remove("publisherData.sourceName", nullptr);
auto snippet = SnippetFromChromeReaderDict(std::move(dict));
- ASSERT_THAT(snippet, testing::NotNull());
+ ASSERT_THAT(snippet, NotNull());
- EXPECT_EQ(snippet->ToProto().sources_size(), 3);
EXPECT_EQ(snippet->id(), "http://url.com");
EXPECT_THAT(snippet->GetAllIDs(),
ElementsAre("http://url.com", "http://source1.com",
"http://source2.com", "http://source3.com"));
- EXPECT_EQ(snippet->best_source().url, GURL("http://source1.com"));
- EXPECT_EQ(snippet->best_source().publisher_name, std::string("Source 1"));
- EXPECT_EQ(snippet->best_source().amp_url, GURL("http://source1.amp.com"));
+ EXPECT_EQ(snippet->url(), GURL("http://source1.com"));
+ EXPECT_EQ(snippet->publisher_name(), std::string("Source 1"));
+ EXPECT_EQ(snippet->amp_url(), GURL("http://source1.amp.com"));
}
TEST(NTPSnippetTest, TestMultipleCompleteSources2) {
// Test 2 complete sources, we should choose the first complete source
- auto dict = SnippetWithThreeSources();
+ auto dict = ChromeReaderSnippetWithThreeSources();
base::ListValue* sources;
ASSERT_TRUE(dict->GetList("contentInfo.sourceCorpusInfo", &sources));
base::DictionaryValue* source;
@@ -254,26 +339,24 @@ TEST(NTPSnippetTest, TestMultipleCompleteSources2) {
source->Remove("publisherData.sourceName", nullptr);
auto snippet = SnippetFromChromeReaderDict(std::move(dict));
- ASSERT_THAT(snippet, testing::NotNull());
+ ASSERT_THAT(snippet, NotNull());
- EXPECT_EQ(snippet->ToProto().sources_size(), 3);
EXPECT_EQ(snippet->id(), "http://url.com");
- EXPECT_EQ(snippet->best_source().url, GURL("http://source2.com"));
- EXPECT_EQ(snippet->best_source().publisher_name, std::string("Source 2"));
- EXPECT_EQ(snippet->best_source().amp_url, GURL("http://source2.amp.com"));
+ EXPECT_EQ(snippet->url(), GURL("http://source2.com"));
+ EXPECT_EQ(snippet->publisher_name(), std::string("Source 2"));
+ EXPECT_EQ(snippet->amp_url(), GURL("http://source2.amp.com"));
}
TEST(NTPSnippetTest, TestMultipleCompleteSources3) {
// Test 3 complete sources, we should choose the first complete source
- auto dict = SnippetWithThreeSources();
+ auto dict = ChromeReaderSnippetWithThreeSources();
auto snippet = SnippetFromChromeReaderDict(std::move(dict));
- ASSERT_THAT(snippet, testing::NotNull());
+ ASSERT_THAT(snippet, NotNull());
- EXPECT_EQ(snippet->ToProto().sources_size(), 3);
EXPECT_EQ(snippet->id(), "http://url.com");
- EXPECT_EQ(snippet->best_source().url, GURL("http://source1.com"));
- EXPECT_EQ(snippet->best_source().publisher_name, std::string("Source 1"));
- EXPECT_EQ(snippet->best_source().amp_url, GURL("http://source1.amp.com"));
+ EXPECT_EQ(snippet->url(), GURL("http://source1.com"));
+ EXPECT_EQ(snippet->publisher_name(), std::string("Source 1"));
+ EXPECT_EQ(snippet->amp_url(), GURL("http://source1.amp.com"));
}
TEST(NTPSnippetTest, ShouldSupportMultipleIdsFromContentSuggestionsServer) {
@@ -291,7 +374,7 @@ TEST(NTPSnippetTest, ShouldSupportMultipleIdsFromContentSuggestionsServer) {
" \"faviconUrl\" : \"http://localhost/favicon.ico\" "
"}";
auto snippet = SnippetFromContentSuggestionJSON(kJsonStr);
- ASSERT_THAT(snippet, testing::NotNull());
+ ASSERT_THAT(snippet, NotNull());
EXPECT_EQ(snippet->id(), "http://localhost/foobar");
EXPECT_THAT(snippet->GetAllIDs(),
@@ -310,21 +393,19 @@ TEST(NTPSnippetTest, CreateFromProtoToProtoRoundtrip) {
proto.set_score(0.1f);
proto.set_dismissed(false);
proto.set_remote_category_id(1);
- auto source_1 = proto.add_sources();
- source_1->set_url("http://cool-suggestions.com/");
- source_1->set_publisher_name("Great Suggestions Inc.");
- auto amp_source = proto.add_sources();
- amp_source->set_url("http://foo/");
- amp_source->set_amp_url("http://cdn.ampproject.org/c/foo/");
+ auto source = proto.add_sources();
+ source->set_url("http://cool-suggestions.com/");
+ source->set_publisher_name("Great Suggestions Inc.");
+ source->set_amp_url("http://cdn.ampproject.org/c/foo/");
std::unique_ptr<NTPSnippet> snippet = NTPSnippet::CreateFromProto(proto);
- ASSERT_THAT(snippet, Not(IsNull()));
+ ASSERT_THAT(snippet, NotNull());
// The snippet database relies on the fact that the first id in the protocol
// buffer is considered the unique id.
EXPECT_EQ(snippet->id(), "foo");
// Unfortunately, we only have MessageLite protocol buffers in Chrome, so
// comparing via DebugString() or MessageDifferencer is not working.
- // So we either need to compare field-by-field (maintenenance heavy) or
+ // So we either need to compare field-by-field (maintenance heavy) or
// compare the binary version (unusable diagnostic). Deciding for the latter.
std::string proto_serialized, round_tripped_serialized;
proto.SerializeToString(&proto_serialized);
@@ -332,5 +413,114 @@ TEST(NTPSnippetTest, CreateFromProtoToProtoRoundtrip) {
EXPECT_EQ(proto_serialized, round_tripped_serialized);
}
+// New form, from chromecontentsuggestions-pa.googleapis.com.
+std::unique_ptr<base::DictionaryValue> ContentSuggestionSnippet() {
+ const std::string kJsonStr =
+ "{"
+ " \"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\", "
+ " \"score\": 9001\n"
+ "}";
+ auto json_value = base::JSONReader::Read(kJsonStr);
+ base::DictionaryValue* json_dict;
+ CHECK(json_value->GetAsDictionary(&json_dict));
+ return json_dict->CreateDeepCopy();
+}
+
+TEST(NTPSnippetTest, NotifcationInfoAllSpecified) {
+ auto json = ContentSuggestionSnippet();
+ json->SetBoolean("notificationInfo.shouldNotify", true);
+ json->SetString("notificationInfo.deadline", "2016-06-30T13:01:37.000Z");
+ auto snippet = NTPSnippet::CreateFromContentSuggestionsDictionary(*json, 0);
+ EXPECT_TRUE(snippet->should_notify());
+ EXPECT_EQ(7200.0f,
+ (snippet->notification_deadline() - snippet->publish_date())
+ .InSecondsF());
+}
+
+TEST(NTPSnippetTest, NotificationInfoDeadlineInvalid) {
+ auto json = ContentSuggestionSnippet();
+ json->SetBoolean("notificationInfo.shouldNotify", true);
+ json->SetInteger("notificationInfo.notificationDeadline", 0);
+ auto snippet = NTPSnippet::CreateFromContentSuggestionsDictionary(*json, 0);
+ EXPECT_TRUE(snippet->should_notify());
+ EXPECT_EQ(base::Time::Max(), snippet->notification_deadline());
+}
+
+TEST(NTPSnippetTest, NotificationInfoDeadlineAbsent) {
+ auto json = ContentSuggestionSnippet();
+ json->SetBoolean("notificationInfo.shouldNotify", true);
+ auto snippet = NTPSnippet::CreateFromContentSuggestionsDictionary(*json, 0);
+ EXPECT_TRUE(snippet->should_notify());
+ EXPECT_EQ(base::Time::Max(), snippet->notification_deadline());
+}
+
+TEST(NTPSnippetTest, NotificationInfoShouldNotifyInvalid) {
+ auto json = ContentSuggestionSnippet();
+ json->SetString("notificationInfo.shouldNotify", "non-bool");
+ auto snippet = NTPSnippet::CreateFromContentSuggestionsDictionary(*json, 0);
+ EXPECT_FALSE(snippet->should_notify());
+}
+
+TEST(NTPSnippetTest, NotificationInfoAbsent) {
+ auto json = ContentSuggestionSnippet();
+ auto snippet = NTPSnippet::CreateFromContentSuggestionsDictionary(*json, 0);
+ EXPECT_FALSE(snippet->should_notify());
+}
+
+TEST(NTPSnippetTest, ToContentSuggestion) {
+ auto json = ContentSuggestionSnippet();
+ auto snippet = NTPSnippet::CreateFromContentSuggestionsDictionary(*json, 0);
+ ASSERT_THAT(snippet, NotNull());
+ ContentSuggestion sugg = snippet->ToContentSuggestion(
+ Category::FromKnownCategory(KnownCategories::ARTICLES));
+
+ EXPECT_THAT(sugg.id().category(),
+ Eq(Category::FromKnownCategory(KnownCategories::ARTICLES)));
+ EXPECT_THAT(sugg.id().id_within_category(), Eq("http://localhost/foobar"));
+ EXPECT_THAT(sugg.url(), Eq(GURL("http://localhost/amp")));
+ EXPECT_THAT(sugg.title(), Eq(base::UTF8ToUTF16("Foo Barred from Baz")));
+ EXPECT_THAT(sugg.snippet_text(), Eq(base::UTF8ToUTF16("...")));
+ EXPECT_THAT(sugg.publish_date().ToJavaTime(), Eq(1467284497000));
+ EXPECT_THAT(sugg.publisher_name(), Eq(base::UTF8ToUTF16("Foo News")));
+ EXPECT_THAT(sugg.score(), Eq(9001));
+ EXPECT_THAT(sugg.download_suggestion_extra(), IsNull());
+ EXPECT_THAT(sugg.recent_tab_suggestion_extra(), IsNull());
+ EXPECT_THAT(sugg.notification_extra(), IsNull());
+}
+
+TEST(NTPSnippetTest, ToContentSuggestionWithNotificationInfo) {
+ auto json = ContentSuggestionSnippet();
+ json->SetBoolean("notificationInfo.shouldNotify", true);
+ json->SetString("notificationInfo.deadline", "2016-06-30T13:01:37.000Z");
+ auto snippet = NTPSnippet::CreateFromContentSuggestionsDictionary(*json, 0);
+ ASSERT_THAT(snippet, NotNull());
+ ContentSuggestion sugg = snippet->ToContentSuggestion(
+ Category::FromKnownCategory(KnownCategories::ARTICLES));
+
+ EXPECT_THAT(sugg.id().category(),
+ Eq(Category::FromKnownCategory(KnownCategories::ARTICLES)));
+ EXPECT_THAT(sugg.id().id_within_category(), Eq("http://localhost/foobar"));
+ EXPECT_THAT(sugg.url(), Eq(GURL("http://localhost/amp")));
+ EXPECT_THAT(sugg.title(), Eq(base::UTF8ToUTF16("Foo Barred from Baz")));
+ EXPECT_THAT(sugg.snippet_text(), Eq(base::UTF8ToUTF16("...")));
+ EXPECT_THAT(sugg.publish_date().ToJavaTime(), Eq(1467284497000));
+ EXPECT_THAT(sugg.publisher_name(), Eq(base::UTF8ToUTF16("Foo News")));
+ EXPECT_THAT(sugg.score(), Eq(9001));
+ EXPECT_THAT(sugg.download_suggestion_extra(), IsNull());
+ EXPECT_THAT(sugg.recent_tab_suggestion_extra(), IsNull());
+ ASSERT_THAT(sugg.notification_extra(), NotNull());
+ EXPECT_THAT(sugg.notification_extra()->deadline.ToJavaTime(),
+ Eq(1467291697000));
+}
+
} // namespace
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippets_fetcher.cc b/chromium/components/ntp_snippets/remote/ntp_snippets_fetcher.cc
deleted file mode 100644
index 355c8127546..00000000000
--- a/chromium/components/ntp_snippets/remote/ntp_snippets_fetcher.cc
+++ /dev/null
@@ -1,880 +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/ntp_snippets_fetcher.h"
-
-#include <cstdlib>
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/json/json_writer.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/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/default_tick_clock.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
-#include "components/ntp_snippets/category_factory.h"
-#include "components/ntp_snippets/features.h"
-#include "components/ntp_snippets/ntp_snippets_constants.h"
-#include "components/ntp_snippets/user_classifier.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_manager_base.h"
-#include "components/variations/net/variations_http_headers.h"
-#include "components/variations/variations_associated_data.h"
-#include "grit/components_strings.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_status_code.h"
-#include "net/url_request/url_fetcher.h"
-#include "third_party/icu/source/common/unicode/uloc.h"
-#include "third_party/icu/source/common/unicode/utypes.h"
-#include "ui/base/l10n/l10n_util.h"
-
-using net::URLFetcher;
-using net::URLRequestContextGetter;
-using net::HttpRequestHeaders;
-using net::URLRequestStatus;
-using translate::LanguageModel;
-
-namespace ntp_snippets {
-
-namespace {
-
-const char kChromeReaderApiScope[] =
- "https://www.googleapis.com/auth/webhistory";
-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 personalizing fetching of snippets.
-const char kPersonalizationName[] = "fetching_personalization";
-
-// Variation parameter for chrome-content-suggestions backend.
-const char kContentSuggestionsBackend[] = "content_suggestions_backend";
-
-// Constants for possible values of the "fetching_personalization" parameter.
-const char kPersonalizationPersonalString[] = "personal";
-const char kPersonalizationNonPersonalString[] = "non_personal";
-const char kPersonalizationBothString[] = "both"; // the default value
-
-const int kMaxExcludedIds = 100;
-
-// Variation parameter for sending LanguageModel info to the server.
-const char kSendTopLanguagesName[] = "send_top_languages";
-
-// Variation parameter for sending UserClassifier info to the server.
-const char kSendUserClassName[] = "send_user_class";
-
-const char kBooleanParameterEnabled[] = "true";
-const char kBooleanParameterDisabled[] = "false";
-
-const int kFetchTimeHistogramResolution = 5;
-
-std::string FetchResultToString(NTPSnippetsFetcher::FetchResult result) {
- switch (result) {
- case NTPSnippetsFetcher::FetchResult::SUCCESS:
- return "OK";
- case NTPSnippetsFetcher::FetchResult::DEPRECATED_EMPTY_HOSTS:
- return "Cannot fetch for empty hosts list.";
- case NTPSnippetsFetcher::FetchResult::URL_REQUEST_STATUS_ERROR:
- return "URLRequestStatus error";
- case NTPSnippetsFetcher::FetchResult::HTTP_ERROR:
- return "HTTP error";
- case NTPSnippetsFetcher::FetchResult::JSON_PARSE_ERROR:
- return "Received invalid JSON";
- case NTPSnippetsFetcher::FetchResult::INVALID_SNIPPET_CONTENT_ERROR:
- return "Invalid / empty list.";
- case NTPSnippetsFetcher::FetchResult::OAUTH_TOKEN_ERROR:
- return "Error in obtaining an OAuth2 access token.";
- case NTPSnippetsFetcher::FetchResult::INTERACTIVE_QUOTA_ERROR:
- return "Out of interactive quota.";
- case NTPSnippetsFetcher::FetchResult::NON_INTERACTIVE_QUOTA_ERROR:
- return "Out of non-interactive quota.";
- case NTPSnippetsFetcher::FetchResult::RESULT_MAX:
- break;
- }
- NOTREACHED();
- return "Unknown error";
-}
-
-bool IsFetchPreconditionFailed(NTPSnippetsFetcher::FetchResult result) {
- switch (result) {
- case NTPSnippetsFetcher::FetchResult::DEPRECATED_EMPTY_HOSTS:
- case NTPSnippetsFetcher::FetchResult::OAUTH_TOKEN_ERROR:
- case NTPSnippetsFetcher::FetchResult::INTERACTIVE_QUOTA_ERROR:
- case NTPSnippetsFetcher::FetchResult::NON_INTERACTIVE_QUOTA_ERROR:
- return true;
- case NTPSnippetsFetcher::FetchResult::SUCCESS:
- case NTPSnippetsFetcher::FetchResult::URL_REQUEST_STATUS_ERROR:
- case NTPSnippetsFetcher::FetchResult::HTTP_ERROR:
- case NTPSnippetsFetcher::FetchResult::JSON_PARSE_ERROR:
- case NTPSnippetsFetcher::FetchResult::INVALID_SNIPPET_CONTENT_ERROR:
- case NTPSnippetsFetcher::FetchResult::RESULT_MAX:
- return false;
- }
- NOTREACHED();
- return true;
-}
-
-std::string GetFetchEndpoint() {
- std::string endpoint = variations::GetVariationParamValue(
- ntp_snippets::kStudyName, kContentSuggestionsBackend);
- return endpoint.empty() ? kChromeReaderServer : endpoint;
-}
-
-bool IsBooleanParameterEnabled(const std::string& param_name,
- bool default_value) {
- std::string param_value = variations::GetVariationParamValueByFeature(
- ntp_snippets::kArticleSuggestionsFeature, param_name);
- if (param_value == kBooleanParameterEnabled)
- return true;
- if (param_value == kBooleanParameterDisabled)
- return false;
- if (!param_value.empty()) {
- LOG(WARNING) << "Invalid value \"" << param_value
- << "\" for variation parameter " << param_name;
- }
- return default_value;
-}
-
-bool IsSendingTopLanguagesEnabled() {
- return IsBooleanParameterEnabled(kSendTopLanguagesName, false);
-}
-
-bool IsSendingUserClassEnabled() {
- return IsBooleanParameterEnabled(kSendUserClassName, false);
-}
-
-bool UsesChromeContentSuggestionsAPI(const GURL& endpoint) {
- if (endpoint == kChromeReaderServer)
- return false;
-
- if (endpoint != kContentSuggestionsServer &&
- endpoint != kContentSuggestionsStagingServer &&
- endpoint != kContentSuggestionsAlphaServer) {
- LOG(WARNING) << "Unknown value for " << kContentSuggestionsBackend << ": "
- << "assuming chromecontentsuggestions-style API";
- }
- return true;
-}
-
-// Creates snippets from dictionary values in |list| and adds them to
-// |snippets|. Returns true on success, false if anything went wrong.
-// |remote_category_id| is only used if |content_suggestions_api| is true.
-bool AddSnippetsFromListValue(bool content_suggestions_api,
- int remote_category_id,
- const base::ListValue& list,
- NTPSnippet::PtrVector* snippets) {
- for (const auto& value : list) {
- const base::DictionaryValue* dict = nullptr;
- if (!value->GetAsDictionary(&dict)) {
- return false;
- }
-
- std::unique_ptr<NTPSnippet> snippet;
- if (content_suggestions_api) {
- snippet = NTPSnippet::CreateFromContentSuggestionsDictionary(
- *dict, remote_category_id);
- } else {
- snippet = NTPSnippet::CreateFromChromeReaderDictionary(*dict);
- }
- if (!snippet) {
- return false;
- }
-
- snippets->push_back(std::move(snippet));
- }
- return true;
-}
-
-// Translate the BCP 47 |language_code| into a posix locale string.
-std::string PosixLocaleFromBCP47Language(const std::string& language_code) {
- char locale[ULOC_FULLNAME_CAPACITY];
- UErrorCode error = U_ZERO_ERROR;
- // Translate the input to a posix locale.
- uloc_forLanguageTag(language_code.c_str(), locale, ULOC_FULLNAME_CAPACITY,
- nullptr, &error);
- if (error != U_ZERO_ERROR) {
- DLOG(WARNING) << "Error in translating language code to a locale string: "
- << error;
- return std::string();
- }
- return locale;
-}
-
-std::string ISO639FromPosixLocale(const std::string& locale) {
- char language[ULOC_LANG_CAPACITY];
- UErrorCode error = U_ZERO_ERROR;
- uloc_getLanguage(locale.c_str(), language, ULOC_LANG_CAPACITY, &error);
- if (error != U_ZERO_ERROR) {
- DLOG(WARNING)
- << "Error in translating locale string to a ISO639 language code: "
- << error;
- return std::string();
- }
- return language;
-}
-
-void AppendLanguageInfoToList(base::ListValue* list,
- const LanguageModel::LanguageInfo& info) {
- auto lang = base::MakeUnique<base::DictionaryValue>();
- lang->SetString("language", info.language_code);
- lang->SetDouble("frequency", info.frequency);
- list->Append(std::move(lang));
-}
-
-std::string GetUserClassString(UserClassifier::UserClass user_class) {
- switch (user_class) {
- case UserClassifier::UserClass::RARE_NTP_USER:
- return "RARE_NTP_USER";
- case UserClassifier::UserClass::ACTIVE_NTP_USER:
- return "ACTIVE_NTP_USER";
- case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
- return "ACTIVE_SUGGESTIONS_CONSUMER";
- }
- NOTREACHED();
- return std::string();
-}
-
-int GetMinuteOfTheDay(bool local_time, bool reduced_resolution) {
- base::Time now(base::Time::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;
-}
-
-} // namespace
-
-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,
- // TODO(dgn): merge has_more_action and has_reload_action when we remove
- // the kFetchMoreFeature flag. See https://crbug.com/667752
- /*has_more_action=*/base::FeatureList::IsEnabled(kFetchMoreFeature),
- /*has_reload_action=*/true,
- /*has_view_all_action=*/false,
- /*show_if_empty=*/true,
- l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_EMPTY));
-}
-
-CategoryInfo BuildRemoteCategoryInfo(const base::string16& title,
- bool allow_fetching_more_results) {
- return CategoryInfo(
- title, ContentSuggestionsCardLayout::FULL_CARD,
- // TODO(dgn): merge has_more_action and has_reload_action when we remove
- // the kFetchMoreFeature flag. See https://crbug.com/667752
- /*has_more_action=*/allow_fetching_more_results &&
- base::FeatureList::IsEnabled(kFetchMoreFeature),
- /*has_reload_action=*/allow_fetching_more_results,
- /*has_view_all_action=*/false,
- /*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));
-}
-
-NTPSnippetsFetcher::FetchedCategory::FetchedCategory(Category c,
- CategoryInfo&& info)
- : category(c), info(info) {}
-
-NTPSnippetsFetcher::FetchedCategory::FetchedCategory(FetchedCategory&&) =
- default;
-NTPSnippetsFetcher::FetchedCategory::~FetchedCategory() = default;
-NTPSnippetsFetcher::FetchedCategory& NTPSnippetsFetcher::FetchedCategory::
-operator=(FetchedCategory&&) = default;
-
-NTPSnippetsFetcher::Params::Params() = default;
-NTPSnippetsFetcher::Params::Params(const Params&) = default;
-NTPSnippetsFetcher::Params::~Params() = default;
-
-NTPSnippetsFetcher::NTPSnippetsFetcher(
- SigninManagerBase* signin_manager,
- OAuth2TokenService* token_service,
- scoped_refptr<URLRequestContextGetter> url_request_context_getter,
- PrefService* pref_service,
- CategoryFactory* category_factory,
- LanguageModel* language_model,
- const ParseJSONCallback& parse_json_callback,
- const std::string& api_key,
- const UserClassifier* user_classifier)
- : OAuth2TokenService::Consumer("ntp_snippets"),
- signin_manager_(signin_manager),
- token_service_(token_service),
- url_request_context_getter_(std::move(url_request_context_getter)),
- category_factory_(category_factory),
- language_model_(language_model),
- parse_json_callback_(parse_json_callback),
- fetch_url_(GetFetchEndpoint()),
- fetch_api_(UsesChromeContentSuggestionsAPI(fetch_url_)
- ? CHROME_CONTENT_SUGGESTIONS_API
- : CHROME_READER_API),
- api_key_(api_key),
- tick_clock_(new base::DefaultTickClock()),
- user_classifier_(user_classifier),
- request_throttler_rare_ntp_user_(
- pref_service,
- RequestThrottler::RequestType::
- CONTENT_SUGGESTION_FETCHER_RARE_NTP_USER),
- request_throttler_active_ntp_user_(
- pref_service,
- RequestThrottler::RequestType::
- CONTENT_SUGGESTION_FETCHER_ACTIVE_NTP_USER),
- request_throttler_active_suggestions_consumer_(
- pref_service,
- RequestThrottler::RequestType::
- CONTENT_SUGGESTION_FETCHER_ACTIVE_SUGGESTIONS_CONSUMER),
- weak_ptr_factory_(this) {
- // Parse the variation parameters and set the defaults if missing.
- std::string personalization = variations::GetVariationParamValue(
- ntp_snippets::kStudyName, kPersonalizationName);
- if (personalization == kPersonalizationNonPersonalString) {
- personalization_ = Personalization::kNonPersonal;
- } else if (personalization == kPersonalizationPersonalString) {
- personalization_ = Personalization::kPersonal;
- } else {
- personalization_ = Personalization::kBoth;
- LOG_IF(WARNING, !personalization.empty() &&
- personalization != kPersonalizationBothString)
- << "Unknown value for " << kPersonalizationName << ": "
- << personalization;
- }
-}
-
-NTPSnippetsFetcher::~NTPSnippetsFetcher() {
- if (waiting_for_refresh_token_)
- token_service_->RemoveObserver(this);
-}
-
-void NTPSnippetsFetcher::FetchSnippets(const Params& params,
- SnippetsAvailableCallback callback) {
- if (!DemandQuotaForRequest(params.interactive_request)) {
- FetchFinished(OptionalFetchedCategories(),
- params.interactive_request
- ? FetchResult::INTERACTIVE_QUOTA_ERROR
- : FetchResult::NON_INTERACTIVE_QUOTA_ERROR,
- /*extra_message=*/std::string());
- return;
- }
-
- if (!params.interactive_request) {
- UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeLocal",
- GetMinuteOfTheDay(/*local_time=*/true,
- /*reduced_resolution=*/true));
- UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeUTC",
- GetMinuteOfTheDay(/*local_time=*/false,
- /*reduced_resolution=*/true));
- }
-
- params_ = params;
- fetch_start_time_ = tick_clock_->NowTicks();
- snippets_available_callback_ = std::move(callback);
-
- bool use_authentication = UsesAuthentication();
- if (use_authentication && signin_manager_->IsAuthenticated()) {
- // Signed-in: get OAuth token --> fetch snippets.
- oauth_token_retried_ = false;
- StartTokenRequest();
- } else if (use_authentication && signin_manager_->AuthInProgress()) {
- // Currently signing in: wait for auth to finish (the refresh token) -->
- // get OAuth token --> fetch snippets.
- if (!waiting_for_refresh_token_) {
- // Wait until we get a refresh token.
- waiting_for_refresh_token_ = true;
- token_service_->AddObserver(this);
- }
- } else {
- // Not signed in: fetch snippets (without authentication).
- FetchSnippetsNonAuthenticated();
- }
-}
-
-NTPSnippetsFetcher::RequestBuilder::RequestBuilder() = default;
-
-NTPSnippetsFetcher::RequestBuilder::RequestBuilder(RequestBuilder&&) = default;
-
-NTPSnippetsFetcher::RequestBuilder::~RequestBuilder() = default;
-
-std::string NTPSnippetsFetcher::RequestBuilder::BuildRequest() {
- auto request = base::MakeUnique<base::DictionaryValue>();
- std::string user_locale = PosixLocaleFromBCP47Language(params.language_code);
- switch (fetch_api) {
- case CHROME_READER_API: {
- auto content_params = base::MakeUnique<base::DictionaryValue>();
- content_params->SetBoolean("only_return_personalized_results",
- only_return_personalized_results);
-
- auto content_restricts = base::MakeUnique<base::ListValue>();
- for (const auto* metadata : {"TITLE", "SNIPPET", "THUMBNAIL"}) {
- auto entry = base::MakeUnique<base::DictionaryValue>();
- entry->SetString("type", "METADATA");
- entry->SetString("value", metadata);
- content_restricts->Append(std::move(entry));
- }
-
- auto content_selectors = base::MakeUnique<base::ListValue>();
- for (const auto& host : params.hosts) {
- auto entry = base::MakeUnique<base::DictionaryValue>();
- entry->SetString("type", "HOST_RESTRICT");
- entry->SetString("value", host);
- content_selectors->Append(std::move(entry));
- }
-
- auto local_scoring_params = base::MakeUnique<base::DictionaryValue>();
- local_scoring_params->Set("content_params", std::move(content_params));
- local_scoring_params->Set("content_restricts",
- std::move(content_restricts));
- local_scoring_params->Set("content_selectors",
- std::move(content_selectors));
-
- auto global_scoring_params = base::MakeUnique<base::DictionaryValue>();
- global_scoring_params->SetInteger("num_to_return", params.count_to_fetch);
- global_scoring_params->SetInteger("sort_type", 1);
-
- auto advanced = base::MakeUnique<base::DictionaryValue>();
- advanced->Set("local_scoring_params", std::move(local_scoring_params));
- advanced->Set("global_scoring_params", std::move(global_scoring_params));
-
- request->SetString("response_detail_level", "STANDARD");
- request->Set("advanced_options", std::move(advanced));
- if (!obfuscated_gaia_id.empty()) {
- request->SetString("obfuscated_gaia_id", obfuscated_gaia_id);
- }
- if (!user_locale.empty()) {
- request->SetString("user_locale", user_locale);
- }
- break;
- }
-
- case CHROME_CONTENT_SUGGESTIONS_API: {
- if (!user_locale.empty()) {
- request->SetString("uiLanguage", user_locale);
- }
-
- auto regular_hosts = base::MakeUnique<base::ListValue>();
- for (const auto& host : params.hosts) {
- regular_hosts->AppendString(host);
- }
- request->Set("regularlyVisitedHostNames", std::move(regular_hosts));
- request->SetString("priority", params.interactive_request
- ? "USER_ACTION"
- : "BACKGROUND_PREFETCH");
-
- auto excluded = base::MakeUnique<base::ListValue>();
- for (const auto& id : params.excluded_ids) {
- excluded->AppendString(id);
- if (excluded->GetSize() >= kMaxExcludedIds)
- break;
- }
- request->Set("excludedSuggestionIds", std::move(excluded));
-
- if (!user_class.empty())
- request->SetString("userActivenessClass", user_class);
-
- if (ui_language.frequency == 0 && other_top_language.frequency == 0)
- break;
-
- auto language_list = base::MakeUnique<base::ListValue>();
- if (ui_language.frequency > 0)
- AppendLanguageInfoToList(language_list.get(), ui_language);
- if (other_top_language.frequency > 0)
- AppendLanguageInfoToList(language_list.get(), other_top_language);
- request->Set("topLanguages", std::move(language_list));
-
- // TODO(sfiera): Support only_return_personalized_results.
- // TODO(sfiera): Support count_to_fetch.
- break;
- }
- }
-
- std::string request_json;
- bool success = base::JSONWriter::WriteWithOptions(
- *request, base::JSONWriter::OPTIONS_PRETTY_PRINT, &request_json);
- DCHECK(success);
- return request_json;
-}
-
-void NTPSnippetsFetcher::FetchSnippetsImpl(const GURL& url,
- const std::string& auth_header,
- const std::string& request) {
- url_fetcher_ = URLFetcher::Create(url, URLFetcher::POST, this);
-
- 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);
-
- HttpRequestHeaders headers;
- if (!auth_header.empty())
- headers.SetHeader("Authorization", auth_header);
- headers.SetHeader("Content-Type", "application/json; charset=UTF-8");
- // Add X-Client-Data header with experiment IDs from field trials.
- // Note: It's fine to pass in |is_signed_in| false, which does not affect
- // transmission of experiment ids coming from the variations server.
- bool is_signed_in = false;
- variations::AppendVariationHeaders(url,
- false, // incognito
- false, // uma_enabled
- is_signed_in, &headers);
- url_fetcher_->SetExtraRequestHeaders(headers.ToString());
- url_fetcher_->SetUploadData("application/json", request);
- // Log the request for debugging network issues.
- VLOG(1) << "Sending a NTP snippets request to " << url << ":" << std::endl
- << headers.ToString() << std::endl
- << request;
- // Fetchers are sometimes cancelled because a network change was detected.
- url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3);
- // Try to make fetching the files bit more robust even with poor connection.
- url_fetcher_->SetMaxRetriesOn5xx(3);
- url_fetcher_->Start();
-}
-
-bool NTPSnippetsFetcher::UsesAuthentication() const {
- return (personalization_ == Personalization::kPersonal ||
- personalization_ == Personalization::kBoth);
-}
-
-NTPSnippetsFetcher::RequestBuilder NTPSnippetsFetcher::MakeRequestBuilder()
- const {
- RequestBuilder result;
- result.params = params_;
- result.fetch_api = fetch_api_;
-
- if (IsSendingUserClassEnabled())
- result.user_class = GetUserClassString(user_classifier_->GetUserClass());
-
- // 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())
- return result;
-
- // TODO(jkrcal): Is this back-and-forth converting necessary?
- result.ui_language.language_code = ISO639FromPosixLocale(
- PosixLocaleFromBCP47Language(result.params.language_code));
- result.ui_language.frequency =
- language_model_->GetLanguageFrequency(result.ui_language.language_code);
-
- std::vector<LanguageModel::LanguageInfo> top_languages =
- language_model_->GetTopLanguages();
- for (const LanguageModel::LanguageInfo& info : top_languages) {
- if (info.language_code != result.ui_language.language_code) {
- result.other_top_language = info;
-
- // Report to UMA how important the UI language is.
- DCHECK_GT(result.other_top_language.frequency, 0)
- << "GetTopLanguages() should not return languages with 0 frequency";
- float ratio_ui_in_both_languages =
- result.ui_language.frequency /
- (result.ui_language.frequency + result.other_top_language.frequency);
- UMA_HISTOGRAM_PERCENTAGE(
- "NewTabPage.Languages.UILanguageRatioInTwoTopLanguages",
- ratio_ui_in_both_languages * 100);
- break;
- }
- }
-
- return result;
-}
-
-void NTPSnippetsFetcher::FetchSnippetsNonAuthenticated() {
- // When not providing OAuth token, we need to pass the Google API key.
- GURL url(base::StringPrintf(kSnippetsServerNonAuthorizedFormat,
- fetch_url_.spec().c_str(), api_key_.c_str()));
- FetchSnippetsImpl(url, std::string(), MakeRequestBuilder().BuildRequest());
-}
-
-void NTPSnippetsFetcher::FetchSnippetsAuthenticated(
- const std::string& account_id,
- const std::string& oauth_access_token) {
- RequestBuilder builder = MakeRequestBuilder();
- builder.obfuscated_gaia_id = account_id;
- builder.only_return_personalized_results =
- personalization_ == Personalization::kPersonal;
- // TODO(jkrcal, treib): Add unit-tests for authenticated fetches.
- FetchSnippetsImpl(fetch_url_,
- base::StringPrintf(kAuthorizationRequestHeaderFormat,
- oauth_access_token.c_str()),
- builder.BuildRequest());
-}
-
-void NTPSnippetsFetcher::StartTokenRequest() {
- OAuth2TokenService::ScopeSet scopes;
- scopes.insert(fetch_api_ == CHROME_CONTENT_SUGGESTIONS_API
- ? kContentSuggestionsApiScope
- : kChromeReaderApiScope);
- oauth_request_ = token_service_->StartRequest(
- signin_manager_->GetAuthenticatedAccountId(), scopes, this);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// OAuth2TokenService::Consumer overrides
-void NTPSnippetsFetcher::OnGetTokenSuccess(
- const OAuth2TokenService::Request* request,
- const std::string& access_token,
- const base::Time& expiration_time) {
- // Delete the request after we leave this method.
- std::unique_ptr<OAuth2TokenService::Request> oauth_request(
- std::move(oauth_request_));
- DCHECK_EQ(oauth_request.get(), request)
- << "Got tokens from some previous request";
-
- FetchSnippetsAuthenticated(oauth_request->GetAccountId(), access_token);
-}
-
-void NTPSnippetsFetcher::OnGetTokenFailure(
- const OAuth2TokenService::Request* request,
- const GoogleServiceAuthError& error) {
- oauth_request_.reset();
-
- if (!oauth_token_retried_ &&
- error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED) {
- // The request (especially on startup) can get reset by loading the refresh
- // token - do it one more time.
- oauth_token_retried_ = true;
- StartTokenRequest();
- return;
- }
-
- DLOG(ERROR) << "Unable to get token: " << error.ToString();
- FetchFinished(
- OptionalFetchedCategories(), FetchResult::OAUTH_TOKEN_ERROR,
- /*extra_message=*/base::StringPrintf(" (%s)", error.ToString().c_str()));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// OAuth2TokenService::Observer overrides
-void NTPSnippetsFetcher::OnRefreshTokenAvailable(
- const std::string& account_id) {
- // Only react on tokens for the account the user has signed in with.
- if (account_id != signin_manager_->GetAuthenticatedAccountId())
- return;
-
- token_service_->RemoveObserver(this);
- waiting_for_refresh_token_ = false;
- oauth_token_retried_ = false;
- StartTokenRequest();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// URLFetcherDelegate overrides
-void NTPSnippetsFetcher::OnURLFetchComplete(const URLFetcher* source) {
- DCHECK_EQ(url_fetcher_.get(), source);
- std::unique_ptr<URLFetcher> deleter = std::move(url_fetcher_);
-
- const URLRequestStatus& status = source->GetStatus();
-
- UMA_HISTOGRAM_SPARSE_SLOWLY(
- "NewTabPage.Snippets.FetchHttpResponseOrErrorCode",
- status.is_success() ? source->GetResponseCode() : status.error());
-
- if (!status.is_success()) {
- FetchFinished(OptionalFetchedCategories(),
- FetchResult::URL_REQUEST_STATUS_ERROR,
- /*extra_message=*/base::StringPrintf(" %d", status.error()));
- } else if (source->GetResponseCode() != 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.
- FetchFinished(
- OptionalFetchedCategories(), FetchResult::HTTP_ERROR,
- /*extra_message=*/base::StringPrintf(" %d", source->GetResponseCode()));
- } else {
- bool stores_result_to_string =
- source->GetResponseAsString(&last_fetch_json_);
- DCHECK(stores_result_to_string);
-
- parse_json_callback_.Run(last_fetch_json_,
- base::Bind(&NTPSnippetsFetcher::OnJsonParsed,
- weak_ptr_factory_.GetWeakPtr()),
- base::Bind(&NTPSnippetsFetcher::OnJsonError,
- weak_ptr_factory_.GetWeakPtr()));
- }
-}
-
-bool NTPSnippetsFetcher::JsonToSnippets(const base::Value& parsed,
- FetchedCategoriesVector* categories) {
- const base::DictionaryValue* top_dict = nullptr;
- if (!parsed.GetAsDictionary(&top_dict)) {
- return false;
- }
-
- switch (fetch_api_) {
- case CHROME_READER_API: {
- const int kUnusedRemoteCategoryId = -1;
- categories->push_back(FetchedCategory(
- category_factory_->FromKnownCategory(KnownCategories::ARTICLES),
- BuildArticleCategoryInfo(base::nullopt)));
-
- const base::ListValue* recos = nullptr;
- return top_dict->GetList("recos", &recos) &&
- AddSnippetsFromListValue(/*content_suggestions_api=*/false,
- kUnusedRemoteCategoryId,
- *recos, &categories->back().snippets);
- }
-
- case CHROME_CONTENT_SUGGESTIONS_API: {
- 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;
- }
-
- NTPSnippet::PtrVector snippets;
- const base::ListValue* suggestions = nullptr;
- // Absence of a list of suggestions is treated as an empty list, which
- // is permissible.
- if (category_value->GetList("suggestions", &suggestions)) {
- if (!AddSnippetsFromListValue(
- /*content_suggestions_api=*/true, remote_category_id,
- *suggestions, &snippets)) {
- return false;
- }
- }
- Category category =
- category_factory_->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().snippets = std::move(snippets);
- }
- return true;
- }
- }
- NOTREACHED();
- return false;
-}
-
-void NTPSnippetsFetcher::OnJsonParsed(std::unique_ptr<base::Value> parsed) {
- FetchedCategoriesVector categories;
- if (JsonToSnippets(*parsed, &categories)) {
- FetchFinished(OptionalFetchedCategories(std::move(categories)),
- FetchResult::SUCCESS,
- /*extra_message=*/std::string());
- } else {
- LOG(WARNING) << "Received invalid snippets: " << last_fetch_json_;
- FetchFinished(OptionalFetchedCategories(),
- FetchResult::INVALID_SNIPPET_CONTENT_ERROR,
- /*extra_message=*/std::string());
- }
-}
-
-void NTPSnippetsFetcher::OnJsonError(const std::string& error) {
- LOG(WARNING) << "Received invalid JSON (" << error
- << "): " << last_fetch_json_;
- FetchFinished(
- OptionalFetchedCategories(), FetchResult::JSON_PARSE_ERROR,
- /*extra_message=*/base::StringPrintf(" (error %s)", error.c_str()));
-}
-
-// The response from the backend might include suggestions from multiple
-// categories. If only fetches for a single category were requested, this
-// function filters them out.
-void NTPSnippetsFetcher::FilterCategories(FetchedCategoriesVector* categories) {
- if (!params_.exclusive_category.has_value())
- return;
- Category exclusive = params_.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->emplace_back(std::move(category));
-}
-
-void NTPSnippetsFetcher::FetchFinished(
- OptionalFetchedCategories fetched_categories,
- FetchResult result,
- const std::string& extra_message) {
- DCHECK(result == FetchResult::SUCCESS || !fetched_categories);
- last_status_ = FetchResultToString(result) + extra_message;
-
- // Filter out unwanted categories if necessary.
- // TODO(fhorschig): As soon as the server supports filtering by
- // that instead of over-fetching and filtering here.
- if (fetched_categories.has_value())
- FilterCategories(&fetched_categories.value());
-
- // Don't record FetchTimes if the result indicates that a precondition
- // failed and we never actually sent a network request
- if (!IsFetchPreconditionFailed(result)) {
- UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime",
- tick_clock_->NowTicks() - fetch_start_time_);
- }
- UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.FetchResult",
- static_cast<int>(result),
- static_cast<int>(FetchResult::RESULT_MAX));
-
- DVLOG(1) << "Fetch finished: " << last_status_;
- if (!snippets_available_callback_.is_null())
- std::move(snippets_available_callback_).Run(std::move(fetched_categories));
-}
-
-bool NTPSnippetsFetcher::DemandQuotaForRequest(bool interactive_request) {
- switch (user_classifier_->GetUserClass()) {
- case UserClassifier::UserClass::RARE_NTP_USER:
- return request_throttler_rare_ntp_user_.DemandQuotaForRequest(
- interactive_request);
- case UserClassifier::UserClass::ACTIVE_NTP_USER:
- return request_throttler_active_ntp_user_.DemandQuotaForRequest(
- interactive_request);
- case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
- return request_throttler_active_suggestions_consumer_
- .DemandQuotaForRequest(interactive_request);
- }
- NOTREACHED();
- return false;
-}
-
-} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippets_scheduler.h b/chromium/components/ntp_snippets/remote/ntp_snippets_scheduler.h
deleted file mode 100644
index 7a4fc1ef5c9..00000000000
--- a/chromium/components/ntp_snippets/remote/ntp_snippets_scheduler.h
+++ /dev/null
@@ -1,36 +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_SNIPPETS_REMOTE_NTP_SNIPPETS_SCHEDULER_H_
-#define COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_SCHEDULER_H_
-
-#include "base/macros.h"
-#include "base/time/time.h"
-
-namespace ntp_snippets {
-
-// Interface to schedule the periodic fetching of snippets.
-class NTPSnippetsScheduler {
- public:
- // Schedule periodic fetching of snippets, with different periods depending on
- // network state. The concrete implementation should call
- // NTPSnippetsService::FetchSnippets once per period.
- // Any of the periods can be zero to indicate that the corresponding task
- // should not be scheduled.
- virtual bool Schedule(base::TimeDelta period_wifi,
- base::TimeDelta period_fallback) = 0;
-
- // Cancel any scheduled tasks.
- virtual bool Unschedule() = 0;
-
- protected:
- NTPSnippetsScheduler() = default;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(NTPSnippetsScheduler);
-};
-
-} // namespace ntp_snippets
-
-#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_SCHEDULER_H_
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippets_status_service.cc b/chromium/components/ntp_snippets/remote/ntp_snippets_status_service.cc
deleted file mode 100644
index 62514d270b1..00000000000
--- a/chromium/components/ntp_snippets/remote/ntp_snippets_status_service.cc
+++ /dev/null
@@ -1,119 +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/ntp_snippets_status_service.h"
-
-#include <string>
-
-#include "components/ntp_snippets/features.h"
-#include "components/ntp_snippets/pref_names.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-#include "components/variations/variations_associated_data.h"
-
-namespace ntp_snippets {
-
-namespace {
-
-const char kFetchingRequiresSignin[] = "fetching_requires_signin";
-const char kFetchingRequiresSigninEnabled[] = "true";
-const char kFetchingRequiresSigninDisabled[] = "false";
-
-} // namespace
-
-NTPSnippetsStatusService::NTPSnippetsStatusService(
- SigninManagerBase* signin_manager,
- PrefService* pref_service)
- : snippets_status_(SnippetsStatus::EXPLICITLY_DISABLED),
- require_signin_(false),
- signin_manager_(signin_manager),
- pref_service_(pref_service),
- signin_observer_(this) {
- std::string param_value_str = variations::GetVariationParamValueByFeature(
- kArticleSuggestionsFeature, kFetchingRequiresSignin);
- if (param_value_str == kFetchingRequiresSigninEnabled) {
- require_signin_ = true;
- } else if (!param_value_str.empty() &&
- param_value_str != kFetchingRequiresSigninDisabled) {
- DLOG(WARNING) << "Unknow value for the variations parameter "
- << kFetchingRequiresSignin << ": " << param_value_str;
- }
-}
-
-NTPSnippetsStatusService::~NTPSnippetsStatusService() = default;
-
-// static
-void NTPSnippetsStatusService::RegisterProfilePrefs(
- PrefRegistrySimple* registry) {
- registry->RegisterBooleanPref(prefs::kEnableSnippets, true);
-}
-
-void NTPSnippetsStatusService::Init(
- const SnippetsStatusChangeCallback& callback) {
- DCHECK(snippets_status_change_callback_.is_null());
-
- snippets_status_change_callback_ = callback;
-
- // Notify about the current state before registering the observer, to make
- // sure we don't get a double notification due to an undefined start state.
- SnippetsStatus old_snippets_status = snippets_status_;
- snippets_status_ = GetSnippetsStatusFromDeps();
- snippets_status_change_callback_.Run(old_snippets_status, snippets_status_);
-
- signin_observer_.Add(signin_manager_);
-
- pref_change_registrar_.Init(pref_service_);
- pref_change_registrar_.Add(
- prefs::kEnableSnippets,
- base::Bind(&NTPSnippetsStatusService::OnSnippetsEnabledChanged,
- base::Unretained(this)));
-}
-
-void NTPSnippetsStatusService::OnSnippetsEnabledChanged() {
- OnStateChanged(GetSnippetsStatusFromDeps());
-}
-
-void NTPSnippetsStatusService::OnStateChanged(
- SnippetsStatus new_snippets_status) {
- if (new_snippets_status == snippets_status_)
- return;
-
- snippets_status_change_callback_.Run(snippets_status_, new_snippets_status);
- snippets_status_ = new_snippets_status;
-}
-
-bool NTPSnippetsStatusService::IsSignedIn() const {
- return signin_manager_ && signin_manager_->IsAuthenticated();
-}
-
-void NTPSnippetsStatusService::GoogleSigninSucceeded(
- const std::string& account_id,
- const std::string& username,
- const std::string& password) {
- OnStateChanged(GetSnippetsStatusFromDeps());
-}
-
-void NTPSnippetsStatusService::GoogleSignedOut(const std::string& account_id,
- const std::string& username) {
- OnStateChanged(GetSnippetsStatusFromDeps());
-}
-
-SnippetsStatus NTPSnippetsStatusService::GetSnippetsStatusFromDeps() const {
- if (!pref_service_->GetBoolean(prefs::kEnableSnippets)) {
- DVLOG(1) << "[GetNewSnippetsStatus] Disabled via pref";
- return SnippetsStatus::EXPLICITLY_DISABLED;
- }
-
- if (require_signin_ && !IsSignedIn()) {
- DVLOG(1) << "[GetNewSnippetsStatus] Signed out and disabled due to this.";
- return SnippetsStatus::SIGNED_OUT_AND_DISABLED;
- }
-
- DVLOG(1) << "[GetNewSnippetsStatus] Enabled, signed "
- << (IsSignedIn() ? "in" : "out");
- return IsSignedIn() ? SnippetsStatus::ENABLED_AND_SIGNED_IN
- : SnippetsStatus::ENABLED_AND_SIGNED_OUT;
-}
-
-} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippets_status_service.h b/chromium/components/ntp_snippets/remote/ntp_snippets_status_service.h
deleted file mode 100644
index 2f8f47a7635..00000000000
--- a/chromium/components/ntp_snippets/remote/ntp_snippets_status_service.h
+++ /dev/null
@@ -1,86 +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_SNIPPETS_REMOTE_NTP_SNIPPETS_STATUS_SERVICE_H_
-#define COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_STATUS_SERVICE_H_
-
-#include "base/callback.h"
-#include "base/gtest_prod_util.h"
-#include "base/scoped_observer.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/signin/core/browser/signin_manager.h"
-
-class PrefRegistrySimple;
-class PrefService;
-
-namespace ntp_snippets {
-
-enum class SnippetsStatus : int {
- // Snippets are enabled and the user is signed in.
- ENABLED_AND_SIGNED_IN,
- // Snippets are enabled and the user is signed out (sign in is not required).
- ENABLED_AND_SIGNED_OUT,
- // Snippets have been disabled as part of the service configuration.
- EXPLICITLY_DISABLED,
- // The user is not signed in, and the service requires it to be enabled.
- SIGNED_OUT_AND_DISABLED,
-};
-
-// Aggregates data from preferences and signin to notify the snippet service of
-// relevant changes in their states.
-class NTPSnippetsStatusService : public SigninManagerBase::Observer {
- public:
- using SnippetsStatusChangeCallback =
- base::Callback<void(SnippetsStatus /*old_status*/,
- SnippetsStatus /*new_status*/)>;
-
- NTPSnippetsStatusService(SigninManagerBase* signin_manager,
- PrefService* pref_service);
-
- ~NTPSnippetsStatusService() override;
-
- static void RegisterProfilePrefs(PrefRegistrySimple* registry);
-
- // Starts listening for changes from the dependencies. |callback| will be
- // called when a significant change in state is detected.
- void Init(const SnippetsStatusChangeCallback& callback);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(NTPSnippetsStatusServiceTest, DisabledViaPref);
-
- // SigninManagerBase::Observer implementation
- void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username,
- const std::string& password) override;
- void GoogleSignedOut(const std::string& account_id,
- const std::string& username) override;
-
- // Callback for the PrefChangeRegistrar.
- void OnSnippetsEnabledChanged();
-
- void OnStateChanged(SnippetsStatus new_snippets_status);
-
- bool IsSignedIn() const;
-
- SnippetsStatus GetSnippetsStatusFromDeps() const;
-
- SnippetsStatus snippets_status_;
- SnippetsStatusChangeCallback snippets_status_change_callback_;
-
- bool require_signin_;
- SigninManagerBase* signin_manager_;
- PrefService* pref_service_;
-
- PrefChangeRegistrar pref_change_registrar_;
-
- // The observer for the SigninManager.
- ScopedObserver<SigninManagerBase, SigninManagerBase::Observer>
- signin_observer_;
-
- DISALLOW_COPY_AND_ASSIGN(NTPSnippetsStatusService);
-};
-
-} // namespace ntp_snippets
-
-#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_STATUS_SERVICE_H_
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippets_status_service_unittest.cc b/chromium/components/ntp_snippets/remote/ntp_snippets_status_service_unittest.cc
deleted file mode 100644
index 827f7de45ff..00000000000
--- a/chromium/components/ntp_snippets/remote/ntp_snippets_status_service_unittest.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/ntp_snippets/remote/ntp_snippets_status_service.h"
-
-#include <memory>
-
-#include "components/ntp_snippets/pref_names.h"
-#include "components/ntp_snippets/remote/ntp_snippets_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 "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ntp_snippets {
-
-class NTPSnippetsStatusServiceTest : public ::testing::Test {
- public:
- NTPSnippetsStatusServiceTest() {
- NTPSnippetsStatusService::RegisterProfilePrefs(
- utils_.pref_service()->registry());
- }
-
- std::unique_ptr<NTPSnippetsStatusService> MakeService() {
- return base::MakeUnique<NTPSnippetsStatusService>(
- utils_.fake_signin_manager(), utils_.pref_service());
- }
-
- protected:
- test::NTPSnippetsTestUtils utils_;
-};
-
-// TODO(jkrcal): Extend the ways to override variation parameters in unit-test
-// (bug 645447), and recover the SigninStateCompatibility test that sign-in is
-// required when the parameter is overriden.
-TEST_F(NTPSnippetsStatusServiceTest, DisabledViaPref) {
- auto service = MakeService();
-
- // The default test setup is signed out. The service is enabled.
- ASSERT_EQ(SnippetsStatus::ENABLED_AND_SIGNED_OUT,
- service->GetSnippetsStatusFromDeps());
-
- // Once the enabled pref is set to false, we should be disabled.
- utils_.pref_service()->SetBoolean(prefs::kEnableSnippets, false);
- EXPECT_EQ(SnippetsStatus::EXPLICITLY_DISABLED,
- service->GetSnippetsStatusFromDeps());
-
- // Signing-in shouldn't matter anymore.
- utils_.fake_signin_manager()->SignIn("foo@bar.com");
- EXPECT_EQ(SnippetsStatus::EXPLICITLY_DISABLED,
- service->GetSnippetsStatusFromDeps());
-}
-
-} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/persistent_scheduler.h b/chromium/components/ntp_snippets/remote/persistent_scheduler.h
new file mode 100644
index 00000000000..70a27e74ef0
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/persistent_scheduler.h
@@ -0,0 +1,47 @@
+// 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_PERSISTENT_SCHEDULER_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_PERSISTENT_SCHEDULER_H_
+
+#include "base/macros.h"
+#include "base/time/time.h"
+
+namespace ntp_snippets {
+
+// Interface to schedule persistent periodic fetches for remote suggestions, OS-
+// dependent. These persistent fetches must get triggered according to their
+// 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).
+class PersistentScheduler {
+ public:
+ // Schedule periodic fetching of remote suggestions, with different periods
+ // depending on network state. Any of the periods can be zero to indicate that
+ // the corresponding task should not be scheduled. Returns whether the
+ // scheduling was successful.
+ virtual bool Schedule(base::TimeDelta period_wifi,
+ base::TimeDelta period_fallback) = 0;
+
+ // Cancel any scheduled tasks. Equivalent to Schedule(0, 0). Returns whether
+ // the scheduling was successful.
+ virtual bool Unschedule() = 0;
+
+ protected:
+ PersistentScheduler() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PersistentScheduler);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_PERSISTENT_SCHEDULER_H_
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_database.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_database.cc
index 572d649081b..0bab98e1e7d 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_database.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_database.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/files/file_path.h"
+#include "base/memory/ptr_util.h"
#include "components/leveldb_proto/proto_database_impl.h"
#include "components/ntp_snippets/remote/proto/ntp_snippets.pb.h"
@@ -22,6 +23,11 @@ const char kImageDatabaseUMAClientName[] = "NTPSnippetImages";
const char kSnippetDatabaseFolder[] = "snippets";
const char kImageDatabaseFolder[] = "images";
+
+const size_t kSuggestionDatabaseReadCacheSizeBytes = 512 << 10;
+const size_t kImageDatabaseReadCacheSizeBytes = 2 << 20;
+
+const size_t kDatabaseWriteBufferSizeBytes = 512 << 10;
} // namespace
namespace ntp_snippets {
@@ -36,13 +42,18 @@ RemoteSuggestionsDatabase::RemoteSuggestionsDatabase(
image_database_initialized_(false),
weak_ptr_factory_(this) {
base::FilePath snippet_dir = database_dir.AppendASCII(kSnippetDatabaseFolder);
- database_->Init(kDatabaseUMAClientName, snippet_dir,
- base::Bind(&RemoteSuggestionsDatabase::OnDatabaseInited,
- weak_ptr_factory_.GetWeakPtr()));
+ database_->InitWithOptions(
+ kDatabaseUMAClientName,
+ leveldb_proto::Options(snippet_dir, kDatabaseWriteBufferSizeBytes,
+ kSuggestionDatabaseReadCacheSizeBytes),
+ base::Bind(&RemoteSuggestionsDatabase::OnDatabaseInited,
+ weak_ptr_factory_.GetWeakPtr()));
base::FilePath image_dir = database_dir.AppendASCII(kImageDatabaseFolder);
- image_database_->Init(
- kImageDatabaseUMAClientName, image_dir,
+ image_database_->InitWithOptions(
+ kImageDatabaseUMAClientName,
+ leveldb_proto::Options(image_dir, kDatabaseWriteBufferSizeBytes,
+ kImageDatabaseReadCacheSizeBytes),
base::Bind(&RemoteSuggestionsDatabase::OnImageDatabaseInited,
weak_ptr_factory_.GetWeakPtr()));
}
@@ -64,10 +75,11 @@ void RemoteSuggestionsDatabase::SetErrorCallback(
}
void RemoteSuggestionsDatabase::LoadSnippets(const SnippetsCallback& callback) {
- if (IsInitialized())
+ if (IsInitialized()) {
LoadSnippetsImpl(callback);
- else
+ } else {
pending_snippets_callbacks_.emplace_back(callback);
+ }
}
void RemoteSuggestionsDatabase::SaveSnippet(const NTPSnippet& snippet) {
@@ -109,10 +121,11 @@ void RemoteSuggestionsDatabase::DeleteSnippets(
void RemoteSuggestionsDatabase::LoadImage(
const std::string& snippet_id,
const SnippetImageCallback& callback) {
- if (IsInitialized())
+ if (IsInitialized()) {
LoadImageImpl(snippet_id, callback);
- else
+ } else {
pending_image_callbacks_.emplace_back(snippet_id, callback);
+ }
}
void RemoteSuggestionsDatabase::SaveImage(const std::string& snippet_id,
@@ -162,8 +175,9 @@ void RemoteSuggestionsDatabase::OnDatabaseInited(bool success) {
return;
}
database_initialized_ = true;
- if (IsInitialized())
+ if (IsInitialized()) {
ProcessPendingLoads();
+ }
}
void RemoteSuggestionsDatabase::OnDatabaseLoaded(
@@ -199,8 +213,9 @@ void RemoteSuggestionsDatabase::OnDatabaseLoaded(
// If any of the snippet protos couldn't be converted to actual snippets,
// clean them up now.
- if (!keys_to_remove->empty())
+ if (!keys_to_remove->empty()) {
DeleteSnippets(std::move(keys_to_remove));
+ }
}
void RemoteSuggestionsDatabase::OnDatabaseSaved(bool success) {
@@ -218,8 +233,9 @@ void RemoteSuggestionsDatabase::OnImageDatabaseInited(bool success) {
return;
}
image_database_initialized_ = true;
- if (IsInitialized())
+ if (IsInitialized()) {
ProcessPendingLoads();
+ }
}
void RemoteSuggestionsDatabase::OnImageDatabaseLoaded(
@@ -251,19 +267,22 @@ void RemoteSuggestionsDatabase::OnImageDatabaseSaved(bool success) {
void RemoteSuggestionsDatabase::OnDatabaseError() {
database_.reset();
image_database_.reset();
- if (!error_callback_.is_null())
+ if (!error_callback_.is_null()) {
error_callback_.Run();
+ }
}
void RemoteSuggestionsDatabase::ProcessPendingLoads() {
DCHECK(IsInitialized());
- for (const auto& callback : pending_snippets_callbacks_)
+ for (const auto& callback : pending_snippets_callbacks_) {
LoadSnippetsImpl(callback);
+ }
pending_snippets_callbacks_.clear();
- for (const auto& id_callback : pending_image_callbacks_)
+ for (const auto& id_callback : pending_image_callbacks_) {
LoadImageImpl(id_callback.first, id_callback.second);
+ }
pending_image_callbacks_.clear();
}
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 b3db54b9063..eee0b8bffb0 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_database_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_database_unittest.cc
@@ -27,43 +27,23 @@ using testing::_;
namespace ntp_snippets {
-namespace {
-
-std::vector<SnippetSource> ExtractSources(const NTPSnippet& snippet) {
- std::vector<SnippetSource> result;
- SnippetProto proto = snippet.ToProto();
- for (const auto& source : proto.sources()) {
- result.emplace_back(GURL(source.url()), source.publisher_name(),
- source.has_amp_url() ? GURL(source.amp_url()) : GURL());
- }
- return result;
-}
-
-} // namespace
-
-bool operator==(const SnippetSource& lhs, const SnippetSource& rhs) {
- return lhs.url == rhs.url && lhs.publisher_name == rhs.publisher_name &&
- lhs.amp_url == rhs.amp_url;
-}
-
bool operator==(const NTPSnippet& lhs, const NTPSnippet& rhs) {
return lhs.id() == rhs.id() && lhs.title() == rhs.title() &&
- lhs.snippet() == rhs.snippet() &&
+ lhs.url() == rhs.url() &&
+ lhs.publisher_name() == rhs.publisher_name() &&
+ lhs.amp_url() == rhs.amp_url() && lhs.snippet() == rhs.snippet() &&
lhs.salient_image_url() == rhs.salient_image_url() &&
lhs.publish_date() == rhs.publish_date() &&
- lhs.expiry_date() == rhs.expiry_date() &&
- ExtractSources(lhs) == ExtractSources(rhs) &&
- lhs.score() == rhs.score() && lhs.is_dismissed() == rhs.is_dismissed();
+ lhs.expiry_date() == rhs.expiry_date() && lhs.score() == rhs.score() &&
+ lhs.is_dismissed() == rhs.is_dismissed();
}
namespace {
std::unique_ptr<NTPSnippet> CreateTestSnippet() {
- auto snippet =
- base::MakeUnique<NTPSnippet>("http://localhost", kArticlesRemoteId);
- snippet->add_source(
- SnippetSource(GURL("http://localhost"), "Publisher", GURL("http://amp")));
- return snippet;
+ return NTPSnippet::CreateForTesting("http://localhost", kArticlesRemoteId,
+ GURL("http://localhost"), "Publisher",
+ GURL("http://amp"));
}
MATCHER_P(SnippetEq, snippet, "") {
@@ -233,8 +213,7 @@ TEST_F(RemoteSuggestionsDatabaseTest, Update) {
db()->SaveSnippet(*snippet);
// Change it.
- const std::string text("some text");
- snippet->set_snippet(text);
+ snippet->set_dismissed(true);
db()->SaveSnippet(*snippet);
// Make sure we get the updated version.
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
new file mode 100644
index 00000000000..fedd35aaa9d
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.cc
@@ -0,0 +1,631 @@
+// 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 <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_tick_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/signin_manager.h"
+#include "components/signin/core/browser/signin_manager_base.h"
+#include "components/variations/variations_associated_data.h"
+#include "grit/components_strings.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::FetchAPI;
+using internal::FetchResult;
+
+namespace {
+
+const char kChromeReaderApiScope[] =
+ "https://www.googleapis.com/auth/webhistory";
+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 personalizing fetching of snippets.
+const char kPersonalizationName[] = "fetching_personalization";
+
+// Variation parameter for chrome-content-suggestions backend.
+const char kContentSuggestionsBackend[] = "content_suggestions_backend";
+
+// Constants for possible values of the "fetching_personalization" parameter.
+const char kPersonalizationPersonalString[] = "personal";
+const char kPersonalizationNonPersonalString[] = "non_personal";
+const char kPersonalizationBothString[] = "both"; // the default value
+
+const int kFetchTimeHistogramResolution = 5;
+
+std::string FetchResultToString(FetchResult result) {
+ switch (result) {
+ case FetchResult::SUCCESS:
+ return "OK";
+ case FetchResult::DEPRECATED_EMPTY_HOSTS:
+ return "Cannot fetch for empty hosts list.";
+ 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::INTERACTIVE_QUOTA_ERROR:
+ return "Out of interactive quota.";
+ case FetchResult::NON_INTERACTIVE_QUOTA_ERROR:
+ return "Out of non-interactive quota.";
+ 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::DEPRECATED_EMPTY_HOSTS:
+ case FetchResult::OAUTH_TOKEN_ERROR:
+ 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::INTERACTIVE_QUOTA_ERROR:
+ case FetchResult::NON_INTERACTIVE_QUOTA_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());
+}
+
+std::string GetFetchEndpoint() {
+ std::string endpoint = variations::GetVariationParamValue(
+ ntp_snippets::kStudyName, kContentSuggestionsBackend);
+ return endpoint.empty() ? kContentSuggestionsServer : endpoint;
+}
+
+bool UsesChromeContentSuggestionsAPI(const GURL& endpoint) {
+ if (endpoint == kChromeReaderServer) {
+ return false;
+ }
+
+ if (endpoint != kContentSuggestionsServer &&
+ endpoint != kContentSuggestionsStagingServer &&
+ endpoint != kContentSuggestionsAlphaServer) {
+ LOG(WARNING) << "Unknown value for " << kContentSuggestionsBackend << ": "
+ << "assuming chromecontentsuggestions-style API";
+ }
+ return true;
+}
+
+// Creates snippets from dictionary values in |list| and adds them to
+// |snippets|. Returns true on success, false if anything went wrong.
+// |remote_category_id| is only used if |content_suggestions_api| is true.
+bool AddSnippetsFromListValue(bool content_suggestions_api,
+ int remote_category_id,
+ const base::ListValue& list,
+ NTPSnippet::PtrVector* snippets) {
+ for (const auto& value : list) {
+ const base::DictionaryValue* dict = nullptr;
+ if (!value->GetAsDictionary(&dict)) {
+ return false;
+ }
+
+ std::unique_ptr<NTPSnippet> snippet;
+ if (content_suggestions_api) {
+ snippet = NTPSnippet::CreateFromContentSuggestionsDictionary(
+ *dict, remote_category_id);
+ } else {
+ snippet = NTPSnippet::CreateFromChromeReaderDictionary(*dict);
+ }
+ if (!snippet) {
+ return false;
+ }
+
+ snippets->push_back(std::move(snippet));
+ }
+ return true;
+}
+
+int GetMinuteOfTheDay(bool local_time, bool reduced_resolution) {
+ base::Time now(base::Time::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
+
+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,
+ // TODO(dgn): merge has_more_action and has_reload_action when we remove
+ // the kFetchMoreFeature flag. See https://crbug.com/667752
+ /*has_more_action=*/base::FeatureList::IsEnabled(kFetchMoreFeature),
+ /*has_reload_action=*/true,
+ /*has_view_all_action=*/false,
+ /*show_if_empty=*/true,
+ l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_EMPTY));
+}
+
+CategoryInfo BuildRemoteCategoryInfo(const base::string16& title,
+ bool allow_fetching_more_results) {
+ return CategoryInfo(
+ title, ContentSuggestionsCardLayout::FULL_CARD,
+ // TODO(dgn): merge has_more_action and has_reload_action when we remove
+ // the kFetchMoreFeature flag. See https://crbug.com/667752
+ /*has_more_action=*/allow_fetching_more_results &&
+ base::FeatureList::IsEnabled(kFetchMoreFeature),
+ /*has_reload_action=*/allow_fetching_more_results,
+ /*has_view_all_action=*/false,
+ /*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 std::string& api_key,
+ const UserClassifier* user_classifier)
+ : OAuth2TokenService::Consumer("ntp_snippets"),
+ 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_(GetFetchEndpoint()),
+ fetch_api_(UsesChromeContentSuggestionsAPI(fetch_url_)
+ ? FetchAPI::CHROME_CONTENT_SUGGESTIONS_API
+ : FetchAPI::CHROME_READER_API),
+ api_key_(api_key),
+ tick_clock_(new base::DefaultTickClock()),
+ user_classifier_(user_classifier),
+ request_throttler_rare_ntp_user_(
+ pref_service,
+ RequestThrottler::RequestType::
+ CONTENT_SUGGESTION_FETCHER_RARE_NTP_USER),
+ request_throttler_active_ntp_user_(
+ pref_service,
+ RequestThrottler::RequestType::
+ CONTENT_SUGGESTION_FETCHER_ACTIVE_NTP_USER),
+ request_throttler_active_suggestions_consumer_(
+ pref_service,
+ RequestThrottler::RequestType::
+ CONTENT_SUGGESTION_FETCHER_ACTIVE_SUGGESTIONS_CONSUMER),
+ weak_ptr_factory_(this) {
+ std::string personalization = variations::GetVariationParamValue(
+ ntp_snippets::kStudyName, kPersonalizationName);
+ if (personalization == kPersonalizationNonPersonalString) {
+ personalization_ = Personalization::kNonPersonal;
+ } else if (personalization == kPersonalizationPersonalString) {
+ personalization_ = Personalization::kPersonal;
+ } else {
+ personalization_ = Personalization::kBoth;
+ LOG_IF(WARNING, !personalization.empty() &&
+ personalization != kPersonalizationBothString)
+ << "Unknown value for " << kPersonalizationName << ": "
+ << personalization;
+ }
+}
+
+RemoteSuggestionsFetcher::~RemoteSuggestionsFetcher() {
+ if (waiting_for_refresh_token_) {
+ token_service_->RemoveObserver(this);
+ }
+}
+
+void RemoteSuggestionsFetcher::FetchSnippets(
+ const RequestParams& params,
+ SnippetsAvailableCallback callback) {
+ if (!DemandQuotaForRequest(params.interactive_request)) {
+ FetchFinished(OptionalFetchedCategories(), std::move(callback),
+ params.interactive_request
+ ? FetchResult::INTERACTIVE_QUOTA_ERROR
+ : FetchResult::NON_INTERACTIVE_QUOTA_ERROR,
+ /*error_details=*/std::string());
+ return;
+ }
+
+ if (!params.interactive_request) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeLocal",
+ GetMinuteOfTheDay(/*local_time=*/true,
+ /*reduced_resolution=*/true));
+ UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeUTC",
+ GetMinuteOfTheDay(/*local_time=*/false,
+ /*reduced_resolution=*/true));
+ }
+
+ JsonRequest::Builder builder;
+ builder.SetFetchAPI(fetch_api_)
+ .SetFetchAPI(fetch_api_)
+ .SetLanguageModel(language_model_)
+ .SetParams(params)
+ .SetParseJsonCallback(parse_json_callback_)
+ .SetPersonalization(personalization_)
+ .SetTickClock(tick_clock_.get())
+ .SetUrlRequestContextGetter(url_request_context_getter_)
+ .SetUserClassifier(*user_classifier_);
+
+ if (NeedsAuthentication() && signin_manager_->IsAuthenticated()) {
+ // Signed-in: get OAuth token --> fetch snippets.
+ oauth_token_retried_ = false;
+ pending_requests_.emplace(std::move(builder), std::move(callback));
+ StartTokenRequest();
+ } else if (NeedsAuthentication() && signin_manager_->AuthInProgress()) {
+ // Currently signing in: wait for auth to finish (the refresh token) -->
+ // get OAuth token --> fetch snippets.
+ pending_requests_.emplace(std::move(builder), std::move(callback));
+ if (!waiting_for_refresh_token_) {
+ // Wait until we get a refresh token.
+ waiting_for_refresh_token_ = true;
+ token_service_->AddObserver(this);
+ }
+ } else {
+ // Not signed in: fetch snippets (without authentication).
+ FetchSnippetsNonAuthenticated(std::move(builder), std::move(callback));
+ }
+}
+
+void RemoteSuggestionsFetcher::FetchSnippetsNonAuthenticated(
+ JsonRequest::Builder builder,
+ SnippetsAvailableCallback callback) {
+ // 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& account_id,
+ const std::string& oauth_access_token) {
+ // TODO(jkrcal, treib): Add unit-tests for authenticated fetches.
+ builder.SetUrl(fetch_url_)
+ .SetAuthentication(account_id,
+ 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() {
+ OAuth2TokenService::ScopeSet scopes;
+ scopes.insert(fetch_api_ == FetchAPI::CHROME_CONTENT_SUGGESTIONS_API
+ ? kContentSuggestionsApiScope
+ : kChromeReaderApiScope);
+ oauth_request_ = token_service_->StartRequest(
+ signin_manager_->GetAuthenticatedAccountId(), scopes, this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// OAuth2TokenService::Consumer overrides
+void RemoteSuggestionsFetcher::OnGetTokenSuccess(
+ const OAuth2TokenService::Request* request,
+ const std::string& access_token,
+ const base::Time& expiration_time) {
+ // Delete the request after we leave this method.
+ std::unique_ptr<OAuth2TokenService::Request> oauth_request(
+ std::move(oauth_request_));
+ DCHECK_EQ(oauth_request.get(), request)
+ << "Got tokens from some previous request";
+
+ 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),
+ oauth_request->GetAccountId(), access_token);
+ }
+}
+
+void RemoteSuggestionsFetcher::OnGetTokenFailure(
+ const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) {
+ oauth_request_.reset();
+
+ if (!oauth_token_retried_ &&
+ error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED) {
+ // The request (especially on startup) can get reset by loading the refresh
+ // token - do it one more time.
+ oauth_token_retried_ = true;
+ StartTokenRequest();
+ return;
+ }
+
+ 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();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// OAuth2TokenService::Observer overrides
+void RemoteSuggestionsFetcher::OnRefreshTokenAvailable(
+ const std::string& account_id) {
+ // Only react on tokens for the account the user has signed in with.
+ if (account_id != signin_manager_->GetAuthenticatedAccountId()) {
+ return;
+ }
+
+ token_service_->RemoveObserver(this);
+ waiting_for_refresh_token_ = false;
+ oauth_token_retried_ = false;
+ StartTokenRequest();
+}
+
+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);
+ 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)) {
+ 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::DictionaryValue* top_dict = nullptr;
+ if (!parsed.GetAsDictionary(&top_dict)) {
+ return false;
+ }
+
+ switch (fetch_api_) {
+ case FetchAPI::CHROME_READER_API: {
+ const int kUnusedRemoteCategoryId = -1;
+ categories->push_back(FetchedCategory(
+ Category::FromKnownCategory(KnownCategories::ARTICLES),
+ BuildArticleCategoryInfo(base::nullopt)));
+
+ const base::ListValue* recos = nullptr;
+ return top_dict->GetList("recos", &recos) &&
+ AddSnippetsFromListValue(/*content_suggestions_api=*/false,
+ kUnusedRemoteCategoryId, *recos,
+ &categories->back().snippets);
+ }
+
+ case FetchAPI::CHROME_CONTENT_SUGGESTIONS_API: {
+ 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;
+ }
+
+ NTPSnippet::PtrVector snippets;
+ const base::ListValue* suggestions = nullptr;
+ // Absence of a list of suggestions is treated as an empty list, which
+ // is permissible.
+ if (category_value->GetList("suggestions", &suggestions)) {
+ if (!AddSnippetsFromListValue(
+ /*content_suggestions_api=*/true, remote_category_id,
+ *suggestions, &snippets)) {
+ 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().snippets = std::move(snippets);
+ }
+ return true;
+ }
+ }
+ NOTREACHED();
+ return false;
+}
+
+bool RemoteSuggestionsFetcher::DemandQuotaForRequest(bool interactive_request) {
+ switch (user_classifier_->GetUserClass()) {
+ case UserClassifier::UserClass::RARE_NTP_USER:
+ return request_throttler_rare_ntp_user_.DemandQuotaForRequest(
+ interactive_request);
+ case UserClassifier::UserClass::ACTIVE_NTP_USER:
+ return request_throttler_active_ntp_user_.DemandQuotaForRequest(
+ interactive_request);
+ case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
+ return request_throttler_active_suggestions_consumer_
+ .DemandQuotaForRequest(interactive_request);
+ }
+ NOTREACHED();
+ return false;
+}
+
+bool RemoteSuggestionsFetcher::NeedsAuthentication() const {
+ return (personalization_ == Personalization::kPersonal ||
+ personalization_ == Personalization::kBoth);
+}
+
+std::string RemoteSuggestionsFetcher::PersonalizationModeString() const {
+ switch (personalization_) {
+ case Personalization::kPersonal:
+ return "Only personalized";
+ break;
+ case Personalization::kBoth:
+ return "Both personalized and non-personalized";
+ break;
+ case Personalization::kNonPersonal:
+ return "Only non-personalized";
+ break;
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippets_fetcher.h b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.h
index 2a0d8eb4a42..79135e84d03 100644
--- a/chromium/components/ntp_snippets/remote/ntp_snippets_fetcher.h
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher.h
@@ -2,11 +2,11 @@
// 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_NTP_SNIPPETS_FETCHER_H_
-#define COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_FETCHER_H_
+#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_H_
#include <memory>
-#include <set>
+#include <queue>
#include <string>
#include <utility>
#include <vector>
@@ -15,21 +15,20 @@
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/time/tick_clock.h"
-#include "base/time/time.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/ntp_snippet.h"
+#include "components/ntp_snippets/remote/request_params.h"
#include "components/ntp_snippets/remote/request_throttler.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/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_context_getter.h"
class PrefService;
class SigninManagerBase;
namespace base {
-class ListValue;
class Value;
} // namespace base
@@ -38,7 +37,7 @@ namespace ntp_snippets {
class UserClassifier;
// TODO(tschumann): BuildArticleCategoryInfo() and BuildRemoteCategoryInfo()
-// don't really belong into this library. However, as the snippets fetcher is
+// 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
@@ -53,18 +52,13 @@ CategoryInfo BuildArticleCategoryInfo(
CategoryInfo BuildRemoteCategoryInfo(const base::string16& title,
bool allow_fetching_more_results);
-// Fetches snippet data for the NTP from the server.
-class NTPSnippetsFetcher : public OAuth2TokenService::Consumer,
- public OAuth2TokenService::Observer,
- public net::URLFetcherDelegate {
+// 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 OAuth2TokenService::Consumer,
+ public OAuth2TokenService::Observer {
public:
- // Callbacks for JSON parsing, needed because the parsing is platform-
- // dependent.
- using SuccessCallback = base::Callback<void(std::unique_ptr<base::Value>)>;
- using ErrorCallback = base::Callback<void(const std::string&)>;
- using ParseJSONCallback = base::Callback<
- void(const std::string&, const SuccessCallback&, const ErrorCallback&)>;
-
struct FetchedCategory {
Category category;
CategoryInfo info;
@@ -82,141 +76,74 @@ class NTPSnippetsFetcher : public OAuth2TokenService::Consumer,
// occur, |snippets| contains no value (no actual vector in base::Optional).
// Error details can be retrieved using last_status().
using SnippetsAvailableCallback =
- base::OnceCallback<void(OptionalFetchedCategories fetched_categories)>;
-
- // Enumeration listing all possible outcomes for fetch attempts. Used for UMA
- // histograms, so do not change existing values. Insert new values at the end,
- // and update the histogram definition.
- enum class FetchResult {
- SUCCESS,
- DEPRECATED_EMPTY_HOSTS,
- URL_REQUEST_STATUS_ERROR,
- HTTP_ERROR,
- JSON_PARSE_ERROR,
- INVALID_SNIPPET_CONTENT_ERROR,
- OAUTH_TOKEN_ERROR,
- INTERACTIVE_QUOTA_ERROR,
- NON_INTERACTIVE_QUOTA_ERROR,
- RESULT_MAX
- };
-
- // Enumeration listing all possible variants of dealing with personalization.
- enum class Personalization {
- kPersonal,
- kNonPersonal,
- kBoth
- };
-
- // Contains all the parameters for one fetch.
- struct Params {
- Params();
- Params(const Params&);
- ~Params();
-
- // If non-empty, restricts the result to the given set of hosts.
- std::set<std::string> hosts;
-
- // BCP 47 language code specifying the user's UI language.
- std::string language_code;
-
- // A set of suggestion IDs that should not be returned again.
- std::set<std::string> excluded_ids;
+ base::OnceCallback<void(Status status,
+ OptionalFetchedCategories fetched_categories)>;
- // Maximum number of snippets to fetch.
- int count_to_fetch = 0;
-
- // Whether this is an interactive request, i.e. triggered by an explicit
- // user action. Typically, non-interactive requests are subject to a daily
- // quota.
- bool interactive_request = false;
-
- // If set, only return results for this category.
- base::Optional<Category> exclusive_category;
- };
-
- NTPSnippetsFetcher(
+ RemoteSuggestionsFetcher(
SigninManagerBase* signin_manager,
OAuth2TokenService* token_service,
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
PrefService* pref_service,
- CategoryFactory* category_factory,
translate::LanguageModel* language_model,
const ParseJSONCallback& parse_json_callback,
const std::string& api_key,
const UserClassifier* user_classifier);
- ~NTPSnippetsFetcher() override;
+ ~RemoteSuggestionsFetcher() override;
// Initiates a fetch from the server. When done (successfully or not), calls
- // the subscriber of SetCallback().
+ // the callback.
//
- // If an ongoing fetch exists, it will be silently abandoned and a new one
- // started, without triggering an additional callback (i.e. the callback will
- // only be called once).
- void FetchSnippets(const Params& params, SnippetsAvailableCallback 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);
+
+ std::string PersonalizationModeString() const;
// Debug string representing the status/result of the last fetch attempt.
const std::string& last_status() const { return last_status_; }
// Returns the last JSON fetched from the server.
- const std::string& last_json() const {
- return last_fetch_json_;
- }
+ const std::string& last_json() const { return last_fetch_json_; }
- // Returns the personalization setting of the fetcher.
+ // Returns the personalization setting of the fetcher as used in tests.
+ // TODO(fhorschig): Reconsider these tests and remove this getter.
Personalization personalization() const { return personalization_; }
// Returns the URL endpoint used by the fetcher.
const GURL& fetch_url() const { return fetch_url_; }
- // Does the fetcher use authentication to get personalized results?
- bool UsesAuthentication() const;
-
// Overrides internal clock for testing purposes.
void SetTickClockForTesting(std::unique_ptr<base::TickClock> tick_clock) {
tick_clock_ = std::move(tick_clock);
}
private:
- FRIEND_TEST_ALL_PREFIXES(NTPSnippetsFetcherTest, BuildRequestAuthenticated);
- FRIEND_TEST_ALL_PREFIXES(NTPSnippetsFetcherTest, BuildRequestUnauthenticated);
- FRIEND_TEST_ALL_PREFIXES(NTPSnippetsFetcherTest, BuildRequestExcludedIds);
- FRIEND_TEST_ALL_PREFIXES(NTPSnippetsFetcherTest, BuildRequestNoUserClass);
- FRIEND_TEST_ALL_PREFIXES(NTPSnippetsFetcherTest,
+ 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(NTPSnippetsFetcherTest,
+ FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
BuildRequestWithUILanguageOnly);
- FRIEND_TEST_ALL_PREFIXES(NTPSnippetsFetcherTest,
+ FRIEND_TEST_ALL_PREFIXES(ChromeReaderSnippetsFetcherTest,
BuildRequestWithOtherLanguageOnly);
+ friend class ChromeReaderSnippetsFetcherTest;
- enum FetchAPI {
- CHROME_READER_API,
- CHROME_CONTENT_SUGGESTIONS_API,
- };
-
- struct RequestBuilder {
- Params params;
- FetchAPI fetch_api = CHROME_READER_API;
- std::string obfuscated_gaia_id;
- bool only_return_personalized_results = false;
- std::string user_class;
- translate::LanguageModel::LanguageInfo ui_language;
- translate::LanguageModel::LanguageInfo other_top_language;
-
- RequestBuilder();
- RequestBuilder(RequestBuilder&&);
- ~RequestBuilder();
-
- std::string BuildRequest();
- };
-
- RequestBuilder MakeRequestBuilder() const;
-
- void FetchSnippetsImpl(const GURL& url,
- const std::string& auth_header,
- const std::string& request);
- void FetchSnippetsNonAuthenticated();
- void FetchSnippetsAuthenticated(const std::string& account_id,
+ void FetchSnippetsNonAuthenticated(internal::JsonRequest::Builder builder,
+ SnippetsAvailableCallback callback);
+ void FetchSnippetsAuthenticated(internal::JsonRequest::Builder builder,
+ SnippetsAvailableCallback callback,
+ const std::string& account_id,
const std::string& oauth_access_token);
+ void StartRequest(internal::JsonRequest::Builder builder,
+ SnippetsAvailableCallback callback);
+
void StartTokenRequest();
// OAuth2TokenService::Consumer overrides:
@@ -229,20 +156,24 @@ class NTPSnippetsFetcher : public OAuth2TokenService::Consumer,
// OAuth2TokenService::Observer overrides:
void OnRefreshTokenAvailable(const std::string& account_id) override;
- // URLFetcherDelegate implementation.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ 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);
- void OnJsonParsed(std::unique_ptr<base::Value> parsed);
- void OnJsonError(const std::string& error);
- void FetchFinished(OptionalFetchedCategories fetched_categories,
- FetchResult result,
- const std::string& extra_message);
- void FilterCategories(FetchedCategoriesVector* categories);
bool DemandQuotaForRequest(bool interactive_request);
+ // Does the fetcher use authentication to get personalized results?
+ bool NeedsAuthentication() const;
+
// Authentication for signed-in users.
SigninManagerBase* signin_manager_;
OAuth2TokenService* token_service_;
@@ -255,8 +186,12 @@ class NTPSnippetsFetcher : public OAuth2TokenService::Consumer,
// Holds the URL request context.
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
- // Weak references, not owned.
- CategoryFactory* const category_factory_;
+ // 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_;
@@ -264,7 +199,7 @@ class NTPSnippetsFetcher : public OAuth2TokenService::Consumer,
// API endpoint for fetching snippets.
const GURL fetch_url_;
// Which API to use
- const FetchAPI fetch_api_;
+ const internal::FetchAPI fetch_api_;
// API key to use for non-authenticated requests.
const std::string api_key_;
@@ -283,27 +218,15 @@ class NTPSnippetsFetcher : public OAuth2TokenService::Consumer,
RequestThrottler request_throttler_active_ntp_user_;
RequestThrottler request_throttler_active_suggestions_consumer_;
- // The callback to notify when new snippets get fetched.
- SnippetsAvailableCallback snippets_available_callback_;
-
- // The parameters for the current request.
- Params params_;
-
- // The fetcher for downloading the snippets. Only non-null if a fetch is
- // currently ongoing.
- std::unique_ptr<net::URLFetcher> url_fetcher_;
-
- // When the current request was started, for logging purposes.
- base::TimeTicks fetch_start_time_;
-
// Info on the last finished fetch.
std::string last_status_;
std::string last_fetch_json_;
- base::WeakPtrFactory<NTPSnippetsFetcher> weak_ptr_factory_;
+ base::WeakPtrFactory<RemoteSuggestionsFetcher> weak_ptr_factory_;
- DISALLOW_COPY_AND_ASSIGN(NTPSnippetsFetcher);
+ DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsFetcher);
};
+
} // namespace ntp_snippets
-#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_FETCHER_H_
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_FETCHER_H_
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_unittest.cc
index 0e22d6c9cd1..ec046cf6005 100644
--- a/chromium/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_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/ntp_snippets/remote/ntp_snippets_fetcher.h"
+#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h"
+#include <deque>
#include <map>
+#include <set>
#include <utility>
+#include <vector>
#include "base/json/json_reader.h"
#include "base/memory/ptr_util.h"
@@ -15,9 +18,11 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/values.h"
-#include "components/ntp_snippets/category_factory.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/ntp_snippet.h"
+#include "components/ntp_snippets/remote/request_params.h"
#include "components/ntp_snippets/user_classifier.h"
#include "components/prefs/testing_pref_service.h"
#include "components/signin/core/browser/account_tracker_service.h"
@@ -25,7 +30,7 @@
#include "components/signin/core/browser/fake_signin_manager.h"
#include "components/signin/core/browser/test_signin_client.h"
#include "components/variations/entropy_provider.h"
-#include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_params_manager.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -45,6 +50,7 @@ using testing::Not;
using testing::NotNull;
using testing::Pointee;
using testing::PrintToString;
+using testing::Return;
using testing::StartsWith;
using testing::WithArg;
@@ -58,22 +64,34 @@ const char kTestChromeContentSuggestionsUrl[] =
// Artificial time delay for JSON parsing.
const int64_t kTestJsonParsingLatencyMs = 20;
-ACTION_P(MoveArgumentPointeeTo, ptr) {
- *ptr = std::move(*arg0);
+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(IsEmptyArticleList, "is an empty list of articles") {
- NTPSnippetsFetcher::OptionalFetchedCategories& fetched_categories = *arg;
+ RemoteSuggestionsFetcher::OptionalFetchedCategories& fetched_categories =
+ *arg;
return fetched_categories && fetched_categories->size() == 1 &&
fetched_categories->begin()->snippets.empty();
}
MATCHER_P(IsSingleArticle, url, "is a list with the single article %(url)s") {
- NTPSnippetsFetcher::OptionalFetchedCategories& fetched_categories = *arg;
+ RemoteSuggestionsFetcher::OptionalFetchedCategories& fetched_categories =
+ *arg;
if (!fetched_categories) {
*result_listener << "got empty categories.";
return false;
@@ -88,9 +106,9 @@ MATCHER_P(IsSingleArticle, url, "is a list with the single article %(url)s") {
<< category->snippets.size();
return false;
}
- if (category->snippets[0]->best_source().url.spec() != url) {
+ if (category->snippets[0]->url().spec() != url) {
*result_listener << "unexpected url, got: "
- << category->snippets[0]->best_source().url.spec();
+ << category->snippets[0]->url().spec();
return false;
}
return true;
@@ -120,47 +138,109 @@ 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);
-}
-
-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());
+ 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(
- NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) {
- Run(&fetched_categories);
+ 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) 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);
+ }
+
+ // 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);
}
- MOCK_METHOD1(
- Run,
- void(NTPSnippetsFetcher::OptionalFetchedCategories* fetched_categories));
+ 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,
+ int id,
+ const GURL& url,
+ net::URLFetcher::RequestType request_type,
net::URLFetcherDelegate* d) override {
return base::MakeUnique<net::FakeURLFetcher>(
url, d, /*response_data=*/std::string(), net::HTTP_NOT_FOUND,
@@ -168,93 +248,107 @@ class FailingFakeURLFetcherFactory : public net::URLFetcherFactory {
}
};
-void ParseJson(
- const std::string& json,
- const ntp_snippets::NTPSnippetsFetcher::SuccessCallback& success_callback,
- const ntp_snippets::NTPSnippetsFetcher::ErrorCallback& error_callback) {
+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)
+ if (value) {
success_callback.Run(std::move(value));
- else
+ } else {
error_callback.Run(json_reader.GetErrorMessage());
+ }
}
-void ParseJsonDelayed(
- const std::string& json,
- const ntp_snippets::NTPSnippetsFetcher::SuccessCallback& success_callback,
- const ntp_snippets::NTPSnippetsFetcher::ErrorCallback& error_callback) {
+void ParseJsonDelayed(const std::string& json,
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, base::Bind(&ParseJson, json, success_callback, error_callback),
+ FROM_HERE, base::Bind(&ParseJson, json, std::move(success_callback),
+ std::move(error_callback)),
base::TimeDelta::FromMilliseconds(kTestJsonParsingLatencyMs));
}
} // namespace
-class NTPSnippetsFetcherTest : public testing::Test {
+class RemoteSuggestionsFetcherTestBase : public testing::Test {
public:
- NTPSnippetsFetcherTest()
- : NTPSnippetsFetcherTest(GURL(kTestChromeReaderUrl),
- std::map<std::string, std::string>()) {}
-
- NTPSnippetsFetcherTest(const GURL& gurl,
- const std::map<std::string, std::string>& params)
- : params_manager_(ntp_snippets::kStudyName, params),
+ explicit RemoteSuggestionsFetcherTestBase(const GURL& gurl)
+ : default_variation_params_(
+ {{"send_top_languages", "true"}, {"send_user_class", "true"}}),
+ params_manager_(ntp_snippets::kStudyName,
+ default_variation_params_,
+ {ntp_snippets::kArticleSuggestionsFeature.name}),
mock_task_runner_(new base::TestMockTimeTaskRunner()),
mock_task_runner_handle_(mock_task_runner_),
- signin_client_(new TestSigninClient(nullptr)),
- account_tracker_(new AccountTrackerService()),
- fake_signin_manager_(new FakeSigninManagerBase(signin_client_.get(),
- account_tracker_.get())),
- fake_token_service_(new FakeProfileOAuth2TokenService()),
- pref_service_(new TestingPrefServiceSimple()),
+ signin_client_(base::MakeUnique<TestSigninClient>(nullptr)),
+ account_tracker_(base::MakeUnique<AccountTrackerService>()),
+ fake_signin_manager_(
+ base::MakeUnique<FakeSigninManagerBase>(signin_client_.get(),
+ account_tracker_.get())),
+ fake_token_service_(base::MakeUnique<FakeProfileOAuth2TokenService>()),
+ pref_service_(base::MakeUnique<TestingPrefServiceSimple>()),
test_url_(gurl) {
RequestThrottler::RegisterProfilePrefs(pref_service_->registry());
UserClassifier::RegisterProfilePrefs(pref_service_->registry());
user_classifier_ = base::MakeUnique<UserClassifier>(pref_service_.get());
+ // Increase initial time such that ticks are non-zero.
+ mock_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1234));
+ ResetSnippetsFetcher();
+ }
- snippets_fetcher_ = base::MakeUnique<NTPSnippetsFetcher>(
+ void ResetSnippetsFetcher() {
+ snippets_fetcher_ = base::MakeUnique<RemoteSuggestionsFetcher>(
fake_signin_manager_.get(), fake_token_service_.get(),
scoped_refptr<net::TestURLRequestContextGetter>(
new net::TestURLRequestContextGetter(mock_task_runner_.get())),
- pref_service_.get(), &category_factory_, nullptr,
- base::Bind(&ParseJsonDelayed), kAPIKey, user_classifier_.get());
+ pref_service_.get(), nullptr, base::Bind(&ParseJsonDelayed), kAPIKey,
+ user_classifier_.get());
snippets_fetcher_->SetTickClockForTesting(
mock_task_runner_->GetMockTickClock());
- // Increase initial time such that ticks are non-zero.
- mock_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1234));
}
- NTPSnippetsFetcher::SnippetsAvailableCallback ToSnippetsAvailableCallback(
- MockSnippetsAvailableCallback* callback) {
+ RemoteSuggestionsFetcher::SnippetsAvailableCallback
+ ToSnippetsAvailableCallback(MockSnippetsAvailableCallback* callback) {
return base::BindOnce(&MockSnippetsAvailableCallback::WrappedRun,
base::Unretained(callback));
}
- NTPSnippetsFetcher& snippets_fetcher() { return *snippets_fetcher_; }
+ RemoteSuggestionsFetcher& snippets_fetcher() { return *snippets_fetcher_; }
MockSnippetsAvailableCallback& mock_callback() { return mock_callback_; }
void FastForwardUntilNoTasksRemain() {
mock_task_runner_->FastForwardUntilNoTasksRemain();
}
base::HistogramTester& histogram_tester() { return histogram_tester_; }
- NTPSnippetsFetcher::Params test_params() {
- NTPSnippetsFetcher::Params result;
+ RequestParams test_params() {
+ RequestParams result;
result.count_to_fetch = 1;
result.interactive_request = true;
return result;
}
void InitFakeURLFetcherFactory() {
- if (fake_url_fetcher_factory_)
+ 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::kStudyName, params,
+ {ntp_snippets::kArticleSuggestionsFeature.name});
+ }
+
void SetFakeResponse(const std::string& response_data,
net::HttpStatusCode response_code,
net::URLRequestStatus::Status status) {
@@ -263,6 +357,11 @@ class NTPSnippetsFetcherTest : public testing::Test {
response_code, status);
}
+ TestingPrefServiceSimple* pref_service() const { return pref_service_.get(); }
+
+ protected:
+ std::map<std::string, std::string> default_variation_params_;
+
private:
variations::testing::VariationParamsManager params_manager_;
scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
@@ -274,265 +373,37 @@ class NTPSnippetsFetcherTest : public testing::Test {
std::unique_ptr<AccountTrackerService> account_tracker_;
std::unique_ptr<SigninManagerBase> fake_signin_manager_;
std::unique_ptr<OAuth2TokenService> fake_token_service_;
- std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher_;
+ std::unique_ptr<RemoteSuggestionsFetcher> snippets_fetcher_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
std::unique_ptr<UserClassifier> user_classifier_;
- CategoryFactory category_factory_;
MockSnippetsAvailableCallback mock_callback_;
const GURL test_url_;
base::HistogramTester histogram_tester_;
- DISALLOW_COPY_AND_ASSIGN(NTPSnippetsFetcherTest);
+ DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsFetcherTestBase);
};
-class NTPSnippetsContentSuggestionsFetcherTest : public NTPSnippetsFetcherTest {
+class ChromeReaderSnippetsFetcherTest
+ : public RemoteSuggestionsFetcherTestBase {
public:
- NTPSnippetsContentSuggestionsFetcherTest()
- : NTPSnippetsFetcherTest(
- GURL(kTestChromeContentSuggestionsUrl),
- {{"content_suggestions_backend", kContentSuggestionsServer}}) {}
+ ChromeReaderSnippetsFetcherTest()
+ : RemoteSuggestionsFetcherTestBase(GURL(kTestChromeReaderUrl)) {
+ default_variation_params_["content_suggestions_backend"] =
+ kChromeReaderServer;
+ SetVariationParam("content_suggestions_backend", kChromeReaderServer);
+ ResetSnippetsFetcher();
+ }
};
-TEST_F(NTPSnippetsFetcherTest, BuildRequestAuthenticated) {
- NTPSnippetsFetcher::RequestBuilder builder;
- builder.params.hosts = {"chromium.org"};
- builder.params.excluded_ids = {"1234567890"};
- builder.params.count_to_fetch = 25;
- builder.params.language_code = "en";
- builder.params.interactive_request = false;
- builder.obfuscated_gaia_id = "0BFUSGAIA";
- builder.only_return_personalized_results = true;
- builder.user_class = "ACTIVE_NTP_USER";
-
- builder.fetch_api = NTPSnippetsFetcher::CHROME_READER_API;
- EXPECT_THAT(builder.BuildRequest(),
- EqualsJSON("{"
- " \"response_detail_level\": \"STANDARD\","
- " \"obfuscated_gaia_id\": \"0BFUSGAIA\","
- " \"user_locale\": \"en\","
- " \"advanced_options\": {"
- " \"local_scoring_params\": {"
- " \"content_params\": {"
- " \"only_return_personalized_results\": true"
- " },"
- " \"content_restricts\": ["
- " {"
- " \"type\": \"METADATA\","
- " \"value\": \"TITLE\""
- " },"
- " {"
- " \"type\": \"METADATA\","
- " \"value\": \"SNIPPET\""
- " },"
- " {"
- " \"type\": \"METADATA\","
- " \"value\": \"THUMBNAIL\""
- " }"
- " ],"
- " \"content_selectors\": ["
- " {"
- " \"type\": \"HOST_RESTRICT\","
- " \"value\": \"chromium.org\""
- " }"
- " ]"
- " },"
- " \"global_scoring_params\": {"
- " \"num_to_return\": 25,"
- " \"sort_type\": 1"
- " }"
- " }"
- "}"));
-
- builder.fetch_api = NTPSnippetsFetcher::CHROME_CONTENT_SUGGESTIONS_API;
- EXPECT_THAT(builder.BuildRequest(),
- EqualsJSON("{"
- " \"uiLanguage\": \"en\","
- " \"priority\": \"BACKGROUND_PREFETCH\","
- " \"regularlyVisitedHostNames\": ["
- " \"chromium.org\""
- " ],"
- " \"excludedSuggestionIds\": ["
- " \"1234567890\""
- " ],"
- " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
- "}"));
-}
-
-TEST_F(NTPSnippetsFetcherTest, BuildRequestUnauthenticated) {
- NTPSnippetsFetcher::RequestBuilder builder;
- builder.params = test_params();
- builder.params.count_to_fetch = 10;
- builder.only_return_personalized_results = false;
- builder.user_class = "ACTIVE_NTP_USER";
-
- builder.fetch_api = NTPSnippetsFetcher::CHROME_READER_API;
- EXPECT_THAT(builder.BuildRequest(),
- EqualsJSON("{"
- " \"response_detail_level\": \"STANDARD\","
- " \"advanced_options\": {"
- " \"local_scoring_params\": {"
- " \"content_params\": {"
- " \"only_return_personalized_results\": false"
- " },"
- " \"content_restricts\": ["
- " {"
- " \"type\": \"METADATA\","
- " \"value\": \"TITLE\""
- " },"
- " {"
- " \"type\": \"METADATA\","
- " \"value\": \"SNIPPET\""
- " },"
- " {"
- " \"type\": \"METADATA\","
- " \"value\": \"THUMBNAIL\""
- " }"
- " ],"
- " \"content_selectors\": []"
- " },"
- " \"global_scoring_params\": {"
- " \"num_to_return\": 10,"
- " \"sort_type\": 1"
- " }"
- " }"
- "}"));
-
- builder.fetch_api = NTPSnippetsFetcher::CHROME_CONTENT_SUGGESTIONS_API;
- EXPECT_THAT(builder.BuildRequest(),
- EqualsJSON("{"
- " \"regularlyVisitedHostNames\": [],"
- " \"priority\": \"USER_ACTION\","
- " \"excludedSuggestionIds\": [],"
- " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
- "}"));
-}
-
-TEST_F(NTPSnippetsFetcherTest, BuildRequestExcludedIds) {
- NTPSnippetsFetcher::RequestBuilder builder;
- builder.params = test_params();
- builder.params.interactive_request = false;
- for (int i = 0; i < 200; ++i)
- builder.params.excluded_ids.insert(base::StringPrintf("%03d", i));
- builder.only_return_personalized_results = false;
- builder.user_class = "ACTIVE_NTP_USER";
-
- builder.fetch_api = NTPSnippetsFetcher::CHROME_CONTENT_SUGGESTIONS_API;
- EXPECT_THAT(builder.BuildRequest(),
- EqualsJSON("{"
- " \"regularlyVisitedHostNames\": [],"
- " \"priority\": \"BACKGROUND_PREFETCH\","
- " \"excludedSuggestionIds\": ["
- " \"000\", \"001\", \"002\", \"003\", \"004\","
- " \"005\", \"006\", \"007\", \"008\", \"009\","
- " \"010\", \"011\", \"012\", \"013\", \"014\","
- " \"015\", \"016\", \"017\", \"018\", \"019\","
- " \"020\", \"021\", \"022\", \"023\", \"024\","
- " \"025\", \"026\", \"027\", \"028\", \"029\","
- " \"030\", \"031\", \"032\", \"033\", \"034\","
- " \"035\", \"036\", \"037\", \"038\", \"039\","
- " \"040\", \"041\", \"042\", \"043\", \"044\","
- " \"045\", \"046\", \"047\", \"048\", \"049\","
- " \"050\", \"051\", \"052\", \"053\", \"054\","
- " \"055\", \"056\", \"057\", \"058\", \"059\","
- " \"060\", \"061\", \"062\", \"063\", \"064\","
- " \"065\", \"066\", \"067\", \"068\", \"069\","
- " \"070\", \"071\", \"072\", \"073\", \"074\","
- " \"075\", \"076\", \"077\", \"078\", \"079\","
- " \"080\", \"081\", \"082\", \"083\", \"084\","
- " \"085\", \"086\", \"087\", \"088\", \"089\","
- " \"090\", \"091\", \"092\", \"093\", \"094\","
- " \"095\", \"096\", \"097\", \"098\", \"099\""
- // Truncated to 100 entries. Currently, they happen to
- // be those lexically first.
- " ],"
- " \"userActivenessClass\": \"ACTIVE_NTP_USER\""
- "}"));
-}
-
-TEST_F(NTPSnippetsFetcherTest, BuildRequestNoUserClass) {
- NTPSnippetsFetcher::RequestBuilder builder;
- builder.params = test_params();
- builder.params.interactive_request = false;
- builder.only_return_personalized_results = false;
-
- builder.fetch_api = NTPSnippetsFetcher::CHROME_CONTENT_SUGGESTIONS_API;
- EXPECT_THAT(builder.BuildRequest(),
- EqualsJSON("{"
- " \"regularlyVisitedHostNames\": [],"
- " \"priority\": \"BACKGROUND_PREFETCH\","
- " \"excludedSuggestionIds\": []"
- "}"));
-}
-
-TEST_F(NTPSnippetsFetcherTest, BuildRequestWithTwoLanguages) {
- NTPSnippetsFetcher::RequestBuilder builder;
- builder.params = test_params();
- builder.only_return_personalized_results = false;
- builder.ui_language.language_code = "en";
- builder.ui_language.frequency = 0.5f;
- builder.other_top_language.language_code = "de";
- builder.other_top_language.frequency = 0.5f;
-
- builder.fetch_api = NTPSnippetsFetcher::CHROME_CONTENT_SUGGESTIONS_API;
- EXPECT_THAT(builder.BuildRequest(),
- EqualsJSON("{"
- " \"regularlyVisitedHostNames\": [],"
- " \"priority\": \"USER_ACTION\","
- " \"excludedSuggestionIds\": [],"
- " \"topLanguages\": ["
- " {"
- " \"language\" : \"en\","
- " \"frequency\" : 0.5"
- " },"
- " {"
- " \"language\" : \"de\","
- " \"frequency\" : 0.5"
- " }"
- " ]"
- "}"));
-}
-
-TEST_F(NTPSnippetsFetcherTest, BuildRequestWithUILanguageOnly) {
- NTPSnippetsFetcher::RequestBuilder builder;
- builder.params = test_params();
- builder.only_return_personalized_results = false;
- builder.ui_language.language_code = "en";
- builder.ui_language.frequency = 0.5f;
-
- builder.fetch_api = NTPSnippetsFetcher::CHROME_CONTENT_SUGGESTIONS_API;
- EXPECT_THAT(builder.BuildRequest(),
- EqualsJSON("{"
- " \"regularlyVisitedHostNames\": [],"
- " \"priority\": \"USER_ACTION\","
- " \"excludedSuggestionIds\": [],"
- " \"topLanguages\": [{"
- " \"language\" : \"en\","
- " \"frequency\" : 0.5"
- " }]"
- "}"));
-}
-
-TEST_F(NTPSnippetsFetcherTest, BuildRequestWithOtherLanguageOnly) {
- NTPSnippetsFetcher::RequestBuilder builder;
- builder.params = test_params();
- builder.only_return_personalized_results = false;
- builder.other_top_language.language_code = "de";
- builder.other_top_language.frequency = 0.5f;
-
- builder.fetch_api = NTPSnippetsFetcher::CHROME_CONTENT_SUGGESTIONS_API;
- EXPECT_THAT(builder.BuildRequest(),
- EqualsJSON("{"
- " \"regularlyVisitedHostNames\": [],"
- " \"priority\": \"USER_ACTION\","
- " \"excludedSuggestionIds\": [],"
- " \"topLanguages\": [{"
- " \"language\" : \"de\","
- " \"frequency\" : 0.5"
- " }]"
- "}"));
-}
+class NTPSnippetsContentSuggestionsFetcherTest
+ : public RemoteSuggestionsFetcherTestBase {
+ public:
+ NTPSnippetsContentSuggestionsFetcherTest()
+ : RemoteSuggestionsFetcherTestBase(
+ GURL(kTestChromeContentSuggestionsUrl)) {}
+};
-TEST_F(NTPSnippetsFetcherTest, ShouldNotFetchOnCreation) {
+TEST_F(ChromeReaderSnippetsFetcherTest, ShouldNotFetchOnCreation) {
// The lack of registered baked in responses would cause any fetch to fail.
FastForwardUntilNoTasksRemain();
EXPECT_THAT(histogram_tester().GetAllSamples(
@@ -543,7 +414,7 @@ TEST_F(NTPSnippetsFetcherTest, ShouldNotFetchOnCreation) {
EXPECT_THAT(snippets_fetcher().last_status(), IsEmpty());
}
-TEST_F(NTPSnippetsFetcherTest, ShouldFetchSuccessfully) {
+TEST_F(ChromeReaderSnippetsFetcherTest, ShouldFetchSuccessfully) {
const std::string kJsonStr =
"{\"recos\": [{"
" \"contentInfo\": {"
@@ -557,10 +428,10 @@ TEST_F(NTPSnippetsFetcherTest, ShouldFetchSuccessfully) {
"}]}";
SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(
- mock_callback(),
- Run(AllOf(IsSingleArticle("http://localhost/foobar"),
- FirstCategoryHasInfo(IsCategoryInfoForArticles()))));
+ EXPECT_CALL(mock_callback(),
+ Run(IsSuccess(),
+ AllOf(IsSingleArticle("http://localhost/foobar"),
+ FirstCategoryHasInfo(IsCategoryInfoForArticles()))));
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -595,7 +466,8 @@ TEST_F(NTPSnippetsContentSuggestionsFetcherTest, ShouldFetchSuccessfully) {
SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
EXPECT_CALL(mock_callback(),
- Run(AllOf(IsSingleArticle("http://localhost/foobar"),
+ Run(IsSuccess(),
+ AllOf(IsSingleArticle("http://localhost/foobar"),
FirstCategoryHasInfo(IsCategoryInfoForArticles()))));
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
@@ -618,7 +490,7 @@ TEST_F(NTPSnippetsContentSuggestionsFetcherTest, EmptyCategoryIsOK) {
"}]}";
SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(IsEmptyArticleList()));
+ EXPECT_CALL(mock_callback(), Run(IsSuccess(), IsEmptyArticleList()));
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -668,9 +540,9 @@ TEST_F(NTPSnippetsContentSuggestionsFetcherTest, ServerCategories) {
"}]}";
SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories;
- EXPECT_CALL(mock_callback(), Run(_))
- .WillOnce(MoveArgumentPointeeTo(&fetched_categories));
+ RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
+ EXPECT_CALL(mock_callback(), Run(IsSuccess(), _))
+ .WillOnce(MoveArgument1PointeeTo(&fetched_categories));
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -681,13 +553,11 @@ TEST_F(NTPSnippetsContentSuggestionsFetcherTest, ServerCategories) {
const auto& articles = category.snippets;
if (category.category.IsKnownCategory(KnownCategories::ARTICLES)) {
ASSERT_THAT(articles.size(), Eq(1u));
- EXPECT_THAT(articles[0]->best_source().url.spec(),
- Eq("http://localhost/foobar"));
+ EXPECT_THAT(articles[0]->url().spec(), Eq("http://localhost/foobar"));
EXPECT_THAT(category.info, IsCategoryInfoForArticles());
- } else if (category.category == CategoryFactory().FromRemoteCategory(2)) {
+ } else if (category.category == Category::FromRemoteCategory(2)) {
ASSERT_THAT(articles.size(), Eq(1u));
- EXPECT_THAT(articles[0]->best_source().url.spec(),
- Eq("http://localhost/foo2"));
+ EXPECT_THAT(articles[0]->url().spec(), Eq("http://localhost/foo2"));
EXPECT_THAT(category.info.has_more_action(), Eq(true));
EXPECT_THAT(category.info.has_reload_action(), Eq(true));
EXPECT_THAT(category.info.has_view_all_action(), Eq(false));
@@ -732,9 +602,9 @@ TEST_F(NTPSnippetsContentSuggestionsFetcherTest,
"}]}";
SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories;
- EXPECT_CALL(mock_callback(), Run(_))
- .WillOnce(MoveArgumentPointeeTo(&fetched_categories));
+ RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
+ EXPECT_CALL(mock_callback(), Run(IsSuccess(), _))
+ .WillOnce(MoveArgument1PointeeTo(&fetched_categories));
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -796,13 +666,14 @@ TEST_F(NTPSnippetsContentSuggestionsFetcherTest, ExclusiveCategoryOnly) {
"}]}";
SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories;
- EXPECT_CALL(mock_callback(), Run(_))
- .WillOnce(MoveArgumentPointeeTo(&fetched_categories));
+ 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));
- NTPSnippetsFetcher::Params params = test_params();
- params.exclusive_category = base::Optional<Category>(
- CategoryFactory().FromRemoteCategory(2));
snippets_fetcher().FetchSnippets(
params, ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -810,18 +681,36 @@ TEST_F(NTPSnippetsContentSuggestionsFetcherTest, ExclusiveCategoryOnly) {
ASSERT_TRUE(fetched_categories);
ASSERT_THAT(fetched_categories->size(), Eq(1u));
const auto& category = (*fetched_categories)[0];
- EXPECT_THAT(category.category.id(),
- Eq(CategoryFactory().FromRemoteCategory(2).id()));
+ EXPECT_THAT(category.category.id(), Eq(Category::FromRemoteCategory(2).id()));
ASSERT_THAT(category.snippets.size(), Eq(1u));
- EXPECT_THAT(category.snippets[0]->best_source().url.spec(),
- Eq("http://localhost/foo2"));
+ EXPECT_THAT(category.snippets[0]->url().spec(), Eq("http://localhost/foo2"));
}
-TEST_F(NTPSnippetsFetcherTest, ShouldFetchSuccessfullyEmptyList) {
+// TODO(fhorschig): Check for behavioral changes instead of state.
+TEST_F(ChromeReaderSnippetsFetcherTest, PersonalizesDependingOnVariations) {
+ // Default setting should be both personalization options.
+ EXPECT_THAT(snippets_fetcher().personalization(), Eq(Personalization::kBoth));
+
+ SetVariationParam("fetching_personalization", "personal");
+ ResetSnippetsFetcher();
+ EXPECT_THAT(snippets_fetcher().personalization(),
+ Eq(Personalization::kPersonal));
+
+ SetVariationParam("fetching_personalization", "non_personal");
+ ResetSnippetsFetcher();
+ EXPECT_THAT(snippets_fetcher().personalization(),
+ Eq(Personalization::kNonPersonal));
+
+ SetVariationParam("fetching_personalization", "both");
+ ResetSnippetsFetcher();
+ EXPECT_THAT(snippets_fetcher().personalization(), Eq(Personalization::kBoth));
+}
+
+TEST_F(ChromeReaderSnippetsFetcherTest, ShouldFetchSuccessfullyEmptyList) {
const std::string kJsonStr = "{\"recos\": []}";
SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(IsEmptyArticleList()));
+ EXPECT_CALL(mock_callback(), Run(IsSuccess(), IsEmptyArticleList()));
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -835,42 +724,55 @@ TEST_F(NTPSnippetsFetcherTest, ShouldFetchSuccessfullyEmptyList) {
ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
}
-TEST_F(NTPSnippetsFetcherTest, ShouldRestrictToHosts) {
- net::TestURLFetcherFactory test_url_fetcher_factory;
- NTPSnippetsFetcher::Params params = test_params();
- params.hosts = {"www.somehost1.com", "www.somehost2.com"};
- params.count_to_fetch = 17;
+TEST_F(ChromeReaderSnippetsFetcherTest, RetryOnInteractiveRequests) {
+ DelegateCallingTestURLFetcherFactory fetcher_factory;
+ RequestParams params = test_params();
+ params.interactive_request = true;
+
snippets_fetcher().FetchSnippets(
params, ToSnippetsAvailableCallback(&mock_callback()));
- net::TestURLFetcher* fetcher = test_url_fetcher_factory.GetFetcherByID(0);
+
+ net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher();
ASSERT_THAT(fetcher, NotNull());
- std::unique_ptr<base::Value> value =
- base::JSONReader::Read(fetcher->upload_data());
- ASSERT_TRUE(value) << " failed to parse JSON: "
- << PrintToString(fetcher->upload_data());
- const base::DictionaryValue* dict = nullptr;
- ASSERT_TRUE(value->GetAsDictionary(&dict));
- const base::DictionaryValue* local_scoring_params = nullptr;
- ASSERT_TRUE(dict->GetDictionary("advanced_options.local_scoring_params",
- &local_scoring_params));
- const base::ListValue* content_selectors = nullptr;
- ASSERT_TRUE(
- local_scoring_params->GetList("content_selectors", &content_selectors));
- ASSERT_THAT(content_selectors->GetSize(), Eq(static_cast<size_t>(2)));
- const base::DictionaryValue* content_selector = nullptr;
- ASSERT_TRUE(content_selectors->GetDictionary(0, &content_selector));
- std::string content_selector_value;
- EXPECT_TRUE(content_selector->GetString("value", &content_selector_value));
- EXPECT_THAT(content_selector_value, Eq("www.somehost1.com"));
- ASSERT_TRUE(content_selectors->GetDictionary(1, &content_selector));
- EXPECT_TRUE(content_selector->GetString("value", &content_selector_value));
- EXPECT_THAT(content_selector_value, Eq("www.somehost2.com"));
+ EXPECT_THAT(fetcher->GetMaxRetriesOn5xx(), Eq(2));
+}
+
+TEST_F(ChromeReaderSnippetsFetcherTest,
+ 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);
+
+ snippets_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(NTPSnippetsFetcherTest, ShouldReportUrlStatusError) {
+TEST_F(ChromeReaderSnippetsFetcherTest, ShouldReportUrlStatusError) {
SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND,
net::URLRequestStatus::FAILED);
- EXPECT_CALL(mock_callback(), Run(/*snippets=*/Not(HasValue()))).Times(1);
+ EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
+ /*snippets=*/Not(HasValue())))
+ .Times(1);
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -887,10 +789,12 @@ TEST_F(NTPSnippetsFetcherTest, ShouldReportUrlStatusError) {
Not(IsEmpty()));
}
-TEST_F(NTPSnippetsFetcherTest, ShouldReportHttpError) {
+TEST_F(ChromeReaderSnippetsFetcherTest, ShouldReportHttpError) {
SetFakeResponse(/*response_data=*/std::string(), net::HTTP_NOT_FOUND,
net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(/*snippets=*/Not(HasValue()))).Times(1);
+ EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
+ /*snippets=*/Not(HasValue())))
+ .Times(1);
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -905,11 +809,13 @@ TEST_F(NTPSnippetsFetcherTest, ShouldReportHttpError) {
Not(IsEmpty()));
}
-TEST_F(NTPSnippetsFetcherTest, ShouldReportJsonError) {
+TEST_F(ChromeReaderSnippetsFetcherTest, ShouldReportJsonError) {
const std::string kInvalidJsonStr = "{ \"recos\": []";
SetFakeResponse(/*response_data=*/kInvalidJsonStr, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(/*snippets=*/Not(HasValue()))).Times(1);
+ EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
+ /*snippets=*/Not(HasValue())))
+ .Times(1);
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -927,10 +833,12 @@ TEST_F(NTPSnippetsFetcherTest, ShouldReportJsonError) {
/*count=*/1)));
}
-TEST_F(NTPSnippetsFetcherTest, ShouldReportJsonErrorForEmptyResponse) {
+TEST_F(ChromeReaderSnippetsFetcherTest, ShouldReportJsonErrorForEmptyResponse) {
SetFakeResponse(/*response_data=*/std::string(), net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(/*snippets=*/Not(HasValue()))).Times(1);
+ EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
+ /*snippets=*/Not(HasValue())))
+ .Times(1);
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -943,12 +851,14 @@ TEST_F(NTPSnippetsFetcherTest, ShouldReportJsonErrorForEmptyResponse) {
ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
}
-TEST_F(NTPSnippetsFetcherTest, ShouldReportInvalidListError) {
+TEST_F(ChromeReaderSnippetsFetcherTest, ShouldReportInvalidListError) {
const std::string kJsonStr =
"{\"recos\": [{ \"contentInfo\": { \"foo\" : \"bar\" }}]}";
SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(/*snippets=*/Not(HasValue()))).Times(1);
+ EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
+ /*snippets=*/Not(HasValue())))
+ .Times(1);
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
@@ -965,40 +875,50 @@ TEST_F(NTPSnippetsFetcherTest, ShouldReportInvalidListError) {
// This test actually verifies that the test setup itself is sane, to prevent
// hard-to-reproduce test failures.
-TEST_F(NTPSnippetsFetcherTest, ShouldReportHttpErrorForMissingBakedResponse) {
+TEST_F(ChromeReaderSnippetsFetcherTest,
+ ShouldReportHttpErrorForMissingBakedResponse) {
InitFakeURLFetcherFactory();
- EXPECT_CALL(mock_callback(), Run(/*snippets=*/Not(HasValue()))).Times(1);
+ EXPECT_CALL(mock_callback(), Run(HasCode(StatusCode::TEMPORARY_ERROR),
+ /*snippets=*/Not(HasValue())))
+ .Times(1);
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
}
-TEST_F(NTPSnippetsFetcherTest, ShouldCancelOngoingFetch) {
+TEST_F(ChromeReaderSnippetsFetcherTest, ShouldProcessConcurrentFetches) {
const std::string kJsonStr = "{ \"recos\": [] }";
SetFakeResponse(/*response_data=*/kJsonStr, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- EXPECT_CALL(mock_callback(), Run(IsEmptyArticleList()));
+ EXPECT_CALL(mock_callback(), Run(IsSuccess(), IsEmptyArticleList())).Times(5);
+ snippets_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.
+ snippets_fetcher().FetchSnippets(
+ test_params(), ToSnippetsAvailableCallback(&mock_callback()));
+ snippets_fetcher().FetchSnippets(
+ test_params(), ToSnippetsAvailableCallback(&mock_callback()));
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
- // Second call to FetchSnippets() overrides/cancels the previous.
- // Callback is expected to be called once.
snippets_fetcher().FetchSnippets(
test_params(), ToSnippetsAvailableCallback(&mock_callback()));
FastForwardUntilNoTasksRemain();
EXPECT_THAT(
histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchResult"),
- ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/5)));
EXPECT_THAT(histogram_tester().GetAllSamples(
"NewTabPage.Snippets.FetchHttpResponseOrErrorCode"),
- ElementsAre(base::Bucket(/*min=*/200, /*count=*/1)));
+ ElementsAre(base::Bucket(/*min=*/200, /*count=*/5)));
EXPECT_THAT(histogram_tester().GetAllSamples("NewTabPage.Snippets.FetchTime"),
ElementsAre(base::Bucket(/*min=*/kTestJsonParsingLatencyMs,
- /*count=*/1)));
+ /*count=*/5)));
}
::std::ostream& operator<<(
::std::ostream& os,
- const NTPSnippetsFetcher::OptionalFetchedCategories& fetched_categories) {
+ 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.
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_provider.cc
index f1f084a7482..edfb5ea4aba 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider.cc
@@ -1,1314 +1,14 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// 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_provider.h"
-#include <algorithm>
-#include <iterator>
-#include <utility>
-
-#include "base/command_line.h"
-#include "base/feature_list.h"
-#include "base/location.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/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task_runner_util.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/image_fetcher/image_decoder.h"
-#include "components/image_fetcher/image_fetcher.h"
-#include "components/ntp_snippets/features.h"
-#include "components/ntp_snippets/pref_names.h"
-#include "components/ntp_snippets/remote/remote_suggestions_database.h"
-#include "components/ntp_snippets/switches.h"
-#include "components/ntp_snippets/user_classifier.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-#include "components/variations/variations_associated_data.h"
-#include "grit/components_strings.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/gfx/image/image.h"
-
namespace ntp_snippets {
-namespace {
-
-// Number of snippets requested to the server. Consider replacing sparse UMA
-// histograms with COUNTS() if this number increases beyond 50.
-const int kMaxSnippetCount = 10;
-
-// Number of archived snippets we keep around in memory.
-const int kMaxArchivedSnippetCount = 200;
-
-// Default values for fetching intervals, fallback and wifi.
-const double kDefaultFetchingIntervalRareNtpUser[] = {48.0, 24.0};
-const double kDefaultFetchingIntervalActiveNtpUser[] = {24.0, 6.0};
-const double kDefaultFetchingIntervalActiveSuggestionsConsumer[] = {24.0, 6.0};
-
-// Variation parameters than can override the default fetching intervals.
-const char* kFetchingIntervalParamNameRareNtpUser[] = {
- "fetching_interval_hours-fallback-rare_ntp_user",
- "fetching_interval_hours-wifi-rare_ntp_user"};
-const char* kFetchingIntervalParamNameActiveNtpUser[] = {
- "fetching_interval_hours-fallback-active_ntp_user",
- "fetching_interval_hours-wifi-active_ntp_user"};
-const char* kFetchingIntervalParamNameActiveSuggestionsConsumer[] = {
- "fetching_interval_hours-fallback-active_suggestions_consumer",
- "fetching_interval_hours-wifi-active_suggestions_consumer"};
-
-const int kDefaultExpiryTimeMins = 3 * 24 * 60;
-
-// Keys for storing CategoryContent info in prefs.
-const char kCategoryContentId[] = "id";
-const char kCategoryContentTitle[] = "title";
-const char kCategoryContentProvidedByServer[] = "provided_by_server";
-const char kCategoryContentAllowFetchingMore[] = "allow_fetching_more";
-
-// TODO(treib): Remove after M57.
-const char kDeprecatedSnippetHostsPref[] = "ntp_snippets.hosts";
-
-base::TimeDelta GetFetchingInterval(bool is_wifi,
- UserClassifier::UserClass user_class) {
- double value_hours = 0.0;
-
- const int index = is_wifi ? 1 : 0;
- const char* param_name = "";
- switch (user_class) {
- case UserClassifier::UserClass::RARE_NTP_USER:
- value_hours = kDefaultFetchingIntervalRareNtpUser[index];
- param_name = kFetchingIntervalParamNameRareNtpUser[index];
- break;
- case UserClassifier::UserClass::ACTIVE_NTP_USER:
- value_hours = kDefaultFetchingIntervalActiveNtpUser[index];
- param_name = kFetchingIntervalParamNameActiveNtpUser[index];
- break;
- case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
- value_hours = kDefaultFetchingIntervalActiveSuggestionsConsumer[index];
- param_name = kFetchingIntervalParamNameActiveSuggestionsConsumer[index];
- break;
- }
-
- // The default value can be overridden by a variation parameter.
- std::string param_value_str = variations::GetVariationParamValueByFeature(
- ntp_snippets::kArticleSuggestionsFeature, param_name);
- if (!param_value_str.empty()) {
- double param_value_hours = 0.0;
- if (base::StringToDouble(param_value_str, &param_value_hours))
- value_hours = param_value_hours;
- else
- LOG(WARNING) << "Invalid value for variation parameter " << param_name;
- }
-
- return base::TimeDelta::FromSecondsD(value_hours * 3600.0);
-}
-
-std::unique_ptr<std::vector<std::string>> GetSnippetIDVector(
- const NTPSnippet::PtrVector& snippets) {
- auto result = base::MakeUnique<std::vector<std::string>>();
- for (const auto& snippet : snippets) {
- result->push_back(snippet->id());
- }
- return result;
-}
-
-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))
- return true;
- }
- return false;
-}
-
-void EraseByPrimaryID(NTPSnippet::PtrVector* snippets,
- const std::vector<std::string>& ids) {
- std::set<std::string> ids_lookup(ids.begin(), ids.end());
- snippets->erase(
- std::remove_if(snippets->begin(), snippets->end(),
- [&ids_lookup](const std::unique_ptr<NTPSnippet>& snippet) {
- return base::ContainsValue(ids_lookup, snippet->id());
- }),
- snippets->end());
-}
-
-void EraseMatchingSnippets(NTPSnippet::PtrVector* snippets,
- const NTPSnippet::PtrVector& compare_against) {
- std::set<std::string> compare_against_ids;
- for (const std::unique_ptr<NTPSnippet>& snippet : compare_against) {
- const std::vector<std::string>& snippet_ids = snippet->GetAllIDs();
- compare_against_ids.insert(snippet_ids.begin(), snippet_ids.end());
- }
- snippets->erase(
- std::remove_if(
- snippets->begin(), snippets->end(),
- [&compare_against_ids](const std::unique_ptr<NTPSnippet>& snippet) {
- return HasIntersection(snippet->GetAllIDs(), compare_against_ids);
- }),
- snippets->end());
-}
-
-void RemoveNullPointers(NTPSnippet::PtrVector* snippets) {
- snippets->erase(
- std::remove_if(
- snippets->begin(), snippets->end(),
- [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }),
- snippets->end());
-}
-
-void AssignExpiryAndPublishDates(NTPSnippet::PtrVector* snippets) {
- for (std::unique_ptr<NTPSnippet>& snippet : *snippets) {
- if (snippet->publish_date().is_null())
- snippet->set_publish_date(base::Time::Now());
- if (snippet->expiry_date().is_null()) {
- snippet->set_expiry_date(
- snippet->publish_date() +
- base::TimeDelta::FromMinutes(kDefaultExpiryTimeMins));
- }
- }
-}
-
-void RemoveIncompleteSnippets(NTPSnippet::PtrVector* snippets) {
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kAddIncompleteSnippets)) {
- return;
- }
- int num_snippets = snippets->size();
- // Remove snippets that do not have all the info we need to display it to
- // the user.
- snippets->erase(
- std::remove_if(snippets->begin(), snippets->end(),
- [](const std::unique_ptr<NTPSnippet>& snippet) {
- return !snippet->is_complete();
- }),
- snippets->end());
- int num_snippets_removed = num_snippets - snippets->size();
- UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch",
- num_snippets_removed > 0);
- if (num_snippets_removed > 0) {
- UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets",
- num_snippets_removed);
- }
-}
-
-std::vector<ContentSuggestion> ConvertToContentSuggestions(
- Category category,
- const NTPSnippet::PtrVector& snippets) {
- std::vector<ContentSuggestion> result;
- for (const std::unique_ptr<NTPSnippet>& snippet : snippets) {
- // TODO(sfiera): if a snippet is not going to be displayed, move it
- // directly to content.dismissed on fetch. Otherwise, we might prune
- // other snippets to get down to kMaxSnippetCount, only to hide one of the
- // incomplete ones we kept.
- if (!snippet->is_complete())
- continue;
- ContentSuggestion suggestion(category, snippet->id(),
- snippet->best_source().url);
- suggestion.set_amp_url(snippet->best_source().amp_url);
- suggestion.set_title(base::UTF8ToUTF16(snippet->title()));
- suggestion.set_snippet_text(base::UTF8ToUTF16(snippet->snippet()));
- suggestion.set_publish_date(snippet->publish_date());
- suggestion.set_publisher_name(
- base::UTF8ToUTF16(snippet->best_source().publisher_name));
- suggestion.set_score(snippet->score());
- result.emplace_back(std::move(suggestion));
- }
- return result;
-}
-
-void CallWithEmptyResults(FetchDoneCallback callback, Status status) {
- if (callback.is_null())
- return;
- callback.Run(status, std::vector<ContentSuggestion>());
-}
-
-} // namespace
-
-RemoteSuggestionsProvider::RemoteSuggestionsProvider(
- Observer* observer,
- CategoryFactory* category_factory,
- PrefService* pref_service,
- const std::string& application_language_code,
- const UserClassifier* user_classifier,
- NTPSnippetsScheduler* scheduler,
- std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher,
- std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
- std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
- std::unique_ptr<RemoteSuggestionsDatabase> database,
- std::unique_ptr<NTPSnippetsStatusService> status_service)
- : ContentSuggestionsProvider(observer, category_factory),
- state_(State::NOT_INITED),
- pref_service_(pref_service),
- articles_category_(
- category_factory->FromKnownCategory(KnownCategories::ARTICLES)),
- application_language_code_(application_language_code),
- user_classifier_(user_classifier),
- scheduler_(scheduler),
- snippets_fetcher_(std::move(snippets_fetcher)),
- image_fetcher_(std::move(image_fetcher)),
- image_decoder_(std::move(image_decoder)),
- database_(std::move(database)),
- snippets_status_service_(std::move(status_service)),
- fetch_when_ready_(false),
- nuke_when_initialized_(false),
- thumbnail_requests_throttler_(
- pref_service,
- RequestThrottler::RequestType::CONTENT_SUGGESTION_THUMBNAIL) {
- pref_service_->ClearPref(kDeprecatedSnippetHostsPref);
-
- RestoreCategoriesFromPrefs();
- // The articles category always exists. Add it if we didn't get it from prefs.
- // TODO(treib): Rethink this.
- category_contents_.insert(
- std::make_pair(articles_category_,
- CategoryContent(BuildArticleCategoryInfo(base::nullopt))));
- // Tell the observer about all the categories.
- for (const auto& entry : category_contents_) {
- observer->OnCategoryStatusChanged(this, entry.first, entry.second.status);
- }
-
- if (database_->IsErrorState()) {
- EnterState(State::ERROR_OCCURRED);
- UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR);
- return;
- }
-
- database_->SetErrorCallback(base::Bind(
- &RemoteSuggestionsProvider::OnDatabaseError, base::Unretained(this)));
-
- // We transition to other states while finalizing the initialization, when the
- // database is done loading.
- database_load_start_ = base::TimeTicks::Now();
- database_->LoadSnippets(base::Bind(
- &RemoteSuggestionsProvider::OnDatabaseLoaded, base::Unretained(this)));
-}
+RemoteSuggestionsProvider::RemoteSuggestionsProvider(Observer* observer)
+ : ContentSuggestionsProvider(observer) {}
RemoteSuggestionsProvider::~RemoteSuggestionsProvider() = default;
-// static
-void RemoteSuggestionsProvider::RegisterProfilePrefs(
- PrefRegistrySimple* registry) {
- // TODO(treib): Remove after M57.
- registry->RegisterListPref(kDeprecatedSnippetHostsPref);
- registry->RegisterListPref(prefs::kRemoteSuggestionCategories);
- registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalWifi, 0);
- registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalFallback,
- 0);
-
- NTPSnippetsStatusService::RegisterProfilePrefs(registry);
-}
-
-void RemoteSuggestionsProvider::FetchSnippets(bool interactive_request) {
- if (ready())
- FetchSnippetsFromHosts(std::set<std::string>(), interactive_request);
- else
- fetch_when_ready_ = true;
-}
-
-void RemoteSuggestionsProvider::FetchSnippetsFromHosts(
- const std::set<std::string>& hosts,
- bool interactive_request) {
- // TODO(tschumann): FetchSnippets() and FetchSnippetsFromHost() implement the
- // fetch logic when called by the background fetcher or interactive "reload"
- // requests. Fetch() is right now only called for the fetch-more use case.
- // The names are confusing and we need to clean them up.
- if (!ready())
- return;
- MarkEmptyCategoriesAsLoading();
-
- NTPSnippetsFetcher::Params params =
- BuildFetchParams(/*exclude_archived_suggestions=*/true);
- params.hosts = hosts;
- params.interactive_request = interactive_request;
- snippets_fetcher_->FetchSnippets(
- params, base::BindOnce(&RemoteSuggestionsProvider::OnFetchFinished,
- base::Unretained(this)));
-}
-
-void RemoteSuggestionsProvider::Fetch(
- const Category& category,
- const std::set<std::string>& known_suggestion_ids,
- const FetchDoneCallback& callback) {
- if (!ready()) {
- CallWithEmptyResults(callback,
- Status(StatusCode::TEMPORARY_ERROR,
- "RemoteSuggestionsProvider is not ready!"));
- return;
- }
- NTPSnippetsFetcher::Params params =
- BuildFetchParams(/*exclude_archived_suggestions=*/false);
- params.excluded_ids.insert(known_suggestion_ids.begin(),
- known_suggestion_ids.end());
- params.interactive_request = true;
- params.exclusive_category = category;
-
- // TODO(tschumann): NTPSnippetsFetcher does not support concurrent requests
- // yet. If a background fetch happens while we fetch-more data, the callback
- // will never get called.
- snippets_fetcher_->FetchSnippets(
- params, base::BindOnce(&RemoteSuggestionsProvider::OnFetchMoreFinished,
- base::Unretained(this), callback));
-}
-
-// Builds default fetcher params.
-NTPSnippetsFetcher::Params RemoteSuggestionsProvider::BuildFetchParams(
- bool exclude_archived_suggestions) const {
- NTPSnippetsFetcher::Params result;
- result.language_code = application_language_code_;
- result.count_to_fetch = kMaxSnippetCount;
- for (const auto& map_entry : category_contents_) {
- const CategoryContent& content = map_entry.second;
- for (const auto& dismissed_snippet : content.dismissed) {
- result.excluded_ids.insert(dismissed_snippet->id());
- }
- if (exclude_archived_suggestions) {
- for (const auto& archived_snippet : content.archived) {
- result.excluded_ids.insert(archived_snippet->id());
- }
- }
- }
- return result;
-}
-
-void RemoteSuggestionsProvider::MarkEmptyCategoriesAsLoading() {
- for (const auto& item : category_contents_) {
- Category category = item.first;
- const CategoryContent& content = item.second;
- if (content.snippets.empty())
- UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING);
- }
-}
-
-void RemoteSuggestionsProvider::RescheduleFetching(bool force) {
- // The scheduler only exists on Android so far, it's null on other platforms.
- if (!scheduler_)
- return;
-
- if (ready()) {
- base::TimeDelta old_interval_wifi = base::TimeDelta::FromInternalValue(
- pref_service_->GetInt64(prefs::kSnippetBackgroundFetchingIntervalWifi));
- base::TimeDelta old_interval_fallback =
- base::TimeDelta::FromInternalValue(pref_service_->GetInt64(
- prefs::kSnippetBackgroundFetchingIntervalFallback));
- UserClassifier::UserClass user_class = user_classifier_->GetUserClass();
- base::TimeDelta interval_wifi =
- GetFetchingInterval(/*is_wifi=*/true, user_class);
- base::TimeDelta interval_fallback =
- GetFetchingInterval(/*is_wifi=*/false, user_class);
- if (force || interval_wifi != old_interval_wifi ||
- interval_fallback != old_interval_fallback) {
- scheduler_->Schedule(interval_wifi, interval_fallback);
- pref_service_->SetInt64(prefs::kSnippetBackgroundFetchingIntervalWifi,
- interval_wifi.ToInternalValue());
- pref_service_->SetInt64(prefs::kSnippetBackgroundFetchingIntervalFallback,
- interval_fallback.ToInternalValue());
- }
- } else {
- // If we're NOT_INITED, we don't know whether to schedule or unschedule.
- // If |force| is false, all is well: We'll reschedule on the next state
- // change anyway. If it's true, then unschedule here, to make sure that the
- // next reschedule actually happens.
- if (state_ != State::NOT_INITED || force) {
- scheduler_->Unschedule();
- pref_service_->ClearPref(prefs::kSnippetBackgroundFetchingIntervalWifi);
- pref_service_->ClearPref(
- prefs::kSnippetBackgroundFetchingIntervalFallback);
- }
- }
-}
-
-CategoryStatus RemoteSuggestionsProvider::GetCategoryStatus(Category category) {
- auto content_it = category_contents_.find(category);
- DCHECK(content_it != category_contents_.end());
- return content_it->second.status;
-}
-
-CategoryInfo RemoteSuggestionsProvider::GetCategoryInfo(Category category) {
- auto content_it = category_contents_.find(category);
- DCHECK(content_it != category_contents_.end());
- return content_it->second.info;
-}
-
-void RemoteSuggestionsProvider::DismissSuggestion(
- const ContentSuggestion::ID& suggestion_id) {
- if (!ready())
- return;
-
- auto content_it = category_contents_.find(suggestion_id.category());
- DCHECK(content_it != category_contents_.end());
- CategoryContent* content = &content_it->second;
- DismissSuggestionFromCategoryContent(content,
- suggestion_id.id_within_category());
-}
-
-void RemoteSuggestionsProvider::FetchSuggestionImage(
- const ContentSuggestion::ID& suggestion_id,
- const ImageFetchedCallback& callback) {
- database_->LoadImage(
- suggestion_id.id_within_category(),
- base::Bind(&RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase,
- base::Unretained(this), callback, suggestion_id));
-}
-
-void RemoteSuggestionsProvider::ClearHistory(
- base::Time begin,
- base::Time end,
- const base::Callback<bool(const GURL& url)>& filter) {
- // Both time range and the filter are ignored and all suggestions are removed,
- // because it is not known which history entries were used for the suggestions
- // personalization.
- if (!ready())
- nuke_when_initialized_ = true;
- else
- NukeAllSnippets();
-}
-
-void RemoteSuggestionsProvider::ClearCachedSuggestions(Category category) {
- if (!initialized())
- return;
-
- auto content_it = category_contents_.find(category);
- if (content_it == category_contents_.end()) {
- return;
- }
- CategoryContent* content = &content_it->second;
- if (content->snippets.empty())
- return;
-
- database_->DeleteSnippets(GetSnippetIDVector(content->snippets));
- database_->DeleteImages(GetSnippetIDVector(content->snippets));
- content->snippets.clear();
-
- if (IsCategoryStatusAvailable(content->status))
- NotifyNewSuggestions(category, *content);
-}
-
-void RemoteSuggestionsProvider::GetDismissedSuggestionsForDebugging(
- Category category,
- const DismissedSuggestionsCallback& callback) {
- auto content_it = category_contents_.find(category);
- DCHECK(content_it != category_contents_.end());
- callback.Run(
- ConvertToContentSuggestions(category, content_it->second.dismissed));
-}
-
-void RemoteSuggestionsProvider::ClearDismissedSuggestionsForDebugging(
- Category category) {
- auto content_it = category_contents_.find(category);
- DCHECK(content_it != category_contents_.end());
- CategoryContent* content = &content_it->second;
-
- if (!initialized())
- return;
-
- if (content->dismissed.empty())
- return;
-
- database_->DeleteSnippets(GetSnippetIDVector(content->dismissed));
- // The image got already deleted when the suggestion was dismissed.
-
- content->dismissed.clear();
-}
-
-// static
-int RemoteSuggestionsProvider::GetMaxSnippetCountForTesting() {
- return kMaxSnippetCount;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Private methods
-
-GURL RemoteSuggestionsProvider::FindSnippetImageUrl(
- const ContentSuggestion::ID& suggestion_id) const {
- DCHECK(base::ContainsKey(category_contents_, suggestion_id.category()));
-
- const CategoryContent& content =
- category_contents_.at(suggestion_id.category());
- const NTPSnippet* snippet =
- content.FindSnippet(suggestion_id.id_within_category());
- if (!snippet)
- return GURL();
- return snippet->salient_image_url();
-}
-
-// image_fetcher::ImageFetcherDelegate implementation.
-void RemoteSuggestionsProvider::OnImageDataFetched(
- const std::string& id_within_category,
- const std::string& image_data) {
- if (image_data.empty())
- return;
-
- // Only save the image if the corresponding snippet still exists.
- bool found = false;
- for (const auto& entry : category_contents_) {
- if (entry.second.FindSnippet(id_within_category)) {
- found = true;
- break;
- }
- }
- if (!found)
- return;
-
- // Only cache the data in the DB, the actual serving is done in the callback
- // provided to |image_fetcher_| (OnSnippetImageDecodedFromNetwork()).
- database_->SaveImage(id_within_category, image_data);
-}
-
-void RemoteSuggestionsProvider::OnDatabaseLoaded(
- NTPSnippet::PtrVector snippets) {
- if (state_ == State::ERROR_OCCURRED)
- return;
- DCHECK(state_ == State::NOT_INITED);
- DCHECK(base::ContainsKey(category_contents_, articles_category_));
-
- base::TimeDelta database_load_time =
- base::TimeTicks::Now() - database_load_start_;
- UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Snippets.DatabaseLoadTime",
- database_load_time);
-
- NTPSnippet::PtrVector to_delete;
- for (std::unique_ptr<NTPSnippet>& snippet : snippets) {
- Category snippet_category =
- category_factory()->FromRemoteCategory(snippet->remote_category_id());
- auto content_it = category_contents_.find(snippet_category);
- // We should already know about the category.
- if (content_it == category_contents_.end()) {
- DLOG(WARNING) << "Loaded a suggestion for unknown category "
- << snippet_category << " from the DB; deleting";
- to_delete.emplace_back(std::move(snippet));
- continue;
- }
- CategoryContent* content = &content_it->second;
- if (snippet->is_dismissed())
- content->dismissed.emplace_back(std::move(snippet));
- else
- content->snippets.emplace_back(std::move(snippet));
- }
- if (!to_delete.empty()) {
- database_->DeleteSnippets(GetSnippetIDVector(to_delete));
- database_->DeleteImages(GetSnippetIDVector(to_delete));
- }
-
- // Sort the suggestions in each category.
- // TODO(treib): Persist the actual order in the DB somehow? crbug.com/654409
- for (auto& entry : category_contents_) {
- CategoryContent* content = &entry.second;
- std::sort(content->snippets.begin(), content->snippets.end(),
- [](const std::unique_ptr<NTPSnippet>& lhs,
- const std::unique_ptr<NTPSnippet>& rhs) {
- return lhs->score() > rhs->score();
- });
- }
-
- // TODO(tschumann): If I move ClearExpiredDismissedSnippets() to the beginning
- // of the function, it essentially does nothing but tests are still green. Fix
- // this!
- ClearExpiredDismissedSnippets();
- ClearOrphanedImages();
- FinishInitialization();
-}
-
-void RemoteSuggestionsProvider::OnDatabaseError() {
- EnterState(State::ERROR_OCCURRED);
- UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR);
-}
-
-void RemoteSuggestionsProvider::OnFetchMoreFinished(
- FetchDoneCallback fetching_callback,
- NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) {
- if (!fetched_categories) {
- // TODO(fhorschig): Disambiguate the kind of error that led here.
- CallWithEmptyResults(fetching_callback,
- Status(StatusCode::PERMANENT_ERROR,
- "The NTPSnippetsFetcher did not "
- "complete the fetching successfully."));
- return;
- }
- if (fetched_categories->size() != 1u) {
- LOG(DFATAL) << "Requested one exclusive category but received "
- << fetched_categories->size() << " categories.";
- CallWithEmptyResults(fetching_callback,
- Status(StatusCode::PERMANENT_ERROR,
- "RemoteSuggestionsProvider received more "
- "categories than requested."));
- return;
- }
- auto& fetched_category = (*fetched_categories)[0];
- Category category = fetched_category.category;
- CategoryContent* existing_content =
- UpdateCategoryInfo(category, fetched_category.info);
- SanitizeReceivedSnippets(existing_content->dismissed,
- &fetched_category.snippets);
- // We compute the result now before modifying |fetched_category.snippets|.
- // However, we wait with notifying the caller until the end of the method when
- // all state is updated.
- std::vector<ContentSuggestion> result =
- ConvertToContentSuggestions(category, fetched_category.snippets);
-
- // Fill up the newly fetched snippets with existing ones, store them, and
- // notify observers about new data.
- while (fetched_category.snippets.size() <
- static_cast<size_t>(kMaxSnippetCount) &&
- !existing_content->snippets.empty()) {
- fetched_category.snippets.emplace(
- fetched_category.snippets.begin(),
- std::move(existing_content->snippets.back()));
- existing_content->snippets.pop_back();
- }
- std::vector<std::string> to_dismiss =
- *GetSnippetIDVector(existing_content->snippets);
- for (const auto& id : to_dismiss) {
- DismissSuggestionFromCategoryContent(existing_content, id);
- }
- DCHECK(existing_content->snippets.empty());
-
- IntegrateSnippets(existing_content, std::move(fetched_category.snippets));
-
- // 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
- // once the snippets fetcher supports concurrent requests. We can then see if
- // Nuke should also cancel outstanding requests or we want to check the
- // status.
- UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
- // Notify callers and observers.
- fetching_callback.Run(Status(StatusCode::SUCCESS), std::move(result));
- NotifyNewSuggestions(category, *existing_content);
-}
-
-void RemoteSuggestionsProvider::OnFetchFinished(
- NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories) {
- if (!ready()) {
- // TODO(tschumann): What happens if this was a user-triggered, interactive
- // request? Is the UI waiting indefinitely now?
- return;
- }
-
- // Mark all categories as not provided by the server in the latest fetch. The
- // ones we got will be marked again below.
- for (auto& item : category_contents_) {
- CategoryContent* content = &item.second;
- content->included_in_last_server_response = false;
- }
-
- // Clear up expired dismissed snippets before we use them to filter new ones.
- ClearExpiredDismissedSnippets();
-
- // If snippets were fetched successfully, update our |category_contents_| from
- // each category provided by the server.
- if (fetched_categories) {
- // TODO(treib): Reorder |category_contents_| to match the order we received
- // from the server. crbug.com/653816
- for (NTPSnippetsFetcher::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_) {
- UMA_HISTOGRAM_SPARSE_SLOWLY(
- "NewTabPage.Snippets.NumArticlesFetched",
- std::min(fetched_category.snippets.size(),
- static_cast<size_t>(kMaxSnippetCount + 1)));
- }
- CategoryContent* content =
- UpdateCategoryInfo(fetched_category.category, fetched_category.info);
- content->included_in_last_server_response = true;
- SanitizeReceivedSnippets(content->dismissed, &fetched_category.snippets);
- IntegrateSnippets(content, std::move(fetched_category.snippets));
- }
- }
-
- // TODO(tschumann): The snippets fetcher needs to signal errors so that we
- // know why we received no data. If an error occured, none of the following
- // should take place.
-
- // We might have gotten new categories (or updated the titles of existing
- // ones), so update the pref.
- StoreCategoriesToPrefs();
-
- for (const auto& item : category_contents_) {
- Category category = item.first;
- UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
- // TODO(sfiera): notify only when a category changed above.
- NotifyNewSuggestions(category, item.second);
- }
-
- // TODO(sfiera): equivalent metrics for non-articles.
- auto content_it = category_contents_.find(articles_category_);
- DCHECK(content_it != category_contents_.end());
- const CategoryContent& content = content_it->second;
- UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles",
- content.snippets.size());
- if (content.snippets.empty() && !content.dismissed.empty()) {
- UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded",
- content.dismissed.size());
- }
-
- // Reschedule after a successful fetch. This resets all currently scheduled
- // fetches, to make sure the fallback interval triggers only if no wifi fetch
- // succeeded, and also that we don't do a background fetch immediately after
- // a user-initiated one.
- if (fetched_categories)
- RescheduleFetching(true);
-}
-
-void RemoteSuggestionsProvider::ArchiveSnippets(
- CategoryContent* content,
- NTPSnippet::PtrVector* to_archive) {
- // Archive previous snippets - move them at the beginning of the list.
- content->archived.insert(content->archived.begin(),
- std::make_move_iterator(to_archive->begin()),
- std::make_move_iterator(to_archive->end()));
- to_archive->clear();
-
- // If there are more archived snippets than we want to keep, delete the
- // oldest ones by their fetch time (which are always in the back).
- if (content->archived.size() > kMaxArchivedSnippetCount) {
- NTPSnippet::PtrVector to_delete(
- std::make_move_iterator(content->archived.begin() +
- kMaxArchivedSnippetCount),
- std::make_move_iterator(content->archived.end()));
- content->archived.resize(kMaxArchivedSnippetCount);
- database_->DeleteImages(GetSnippetIDVector(to_delete));
- }
-}
-
-void RemoteSuggestionsProvider::SanitizeReceivedSnippets(
- const NTPSnippet::PtrVector& dismissed,
- NTPSnippet::PtrVector* snippets) {
- DCHECK(ready());
- EraseMatchingSnippets(snippets, dismissed);
- AssignExpiryAndPublishDates(snippets);
- RemoveIncompleteSnippets(snippets);
-}
-
-void RemoteSuggestionsProvider::IntegrateSnippets(
- CategoryContent* content,
- NTPSnippet::PtrVector new_snippets) {
- DCHECK(ready());
-
- // Do not touch the current set of snippets if the newly fetched one is empty.
- // TODO(tschumann): This should go. If we get empty results we should update
- // accordingly and remove the old one (only of course if this was not received
- // through a fetch-more).
- if (new_snippets.empty())
- return;
-
- // It's entirely possible that the newly fetched snippets contain articles
- // that have been present before.
- // We need to make sure to only delete and archive snippets that don't
- // appear with the same ID in the new suggestions (it's fine for additional
- // IDs though).
- EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets));
- // Do not delete the thumbnail images as they are still handy on open NTPs.
- database_->DeleteSnippets(GetSnippetIDVector(content->snippets));
- // Note, that ArchiveSnippets will clear |content->snippets|.
- ArchiveSnippets(content, &content->snippets);
-
- database_->SaveSnippets(new_snippets);
-
- content->snippets = std::move(new_snippets);
-}
-
-void RemoteSuggestionsProvider::DismissSuggestionFromCategoryContent(
- CategoryContent* content,
- const std::string& id_within_category) {
- auto it = std::find_if(
- content->snippets.begin(), content->snippets.end(),
- [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) {
- return snippet->id() == id_within_category;
- });
- if (it == content->snippets.end())
- return;
-
- (*it)->set_dismissed(true);
-
- database_->SaveSnippet(**it);
-
- content->dismissed.push_back(std::move(*it));
- content->snippets.erase(it);
-}
-
-void RemoteSuggestionsProvider::ClearExpiredDismissedSnippets() {
- std::vector<Category> categories_to_erase;
-
- const base::Time now = base::Time::Now();
-
- for (auto& item : category_contents_) {
- Category category = item.first;
- CategoryContent* content = &item.second;
-
- NTPSnippet::PtrVector to_delete;
- // Move expired dismissed snippets over into |to_delete|.
- for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) {
- if (snippet->expiry_date() <= now)
- to_delete.emplace_back(std::move(snippet));
- }
- RemoveNullPointers(&content->dismissed);
-
- // Delete the images.
- database_->DeleteImages(GetSnippetIDVector(to_delete));
- // Delete the removed article suggestions from the DB.
- database_->DeleteSnippets(GetSnippetIDVector(to_delete));
-
- if (content->snippets.empty() && content->dismissed.empty() &&
- category != articles_category_ &&
- !content->included_in_last_server_response) {
- categories_to_erase.push_back(category);
- }
- }
-
- for (Category category : categories_to_erase) {
- UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED);
- category_contents_.erase(category);
- }
-
- StoreCategoriesToPrefs();
-}
-
-void RemoteSuggestionsProvider::ClearOrphanedImages() {
- auto alive_snippets = base::MakeUnique<std::set<std::string>>();
- for (const auto& entry : category_contents_) {
- const CategoryContent& content = entry.second;
- for (const auto& snippet_ptr : content.snippets) {
- alive_snippets->insert(snippet_ptr->id());
- }
- for (const auto& snippet_ptr : content.dismissed) {
- alive_snippets->insert(snippet_ptr->id());
- }
- }
- database_->GarbageCollectImages(std::move(alive_snippets));
-}
-
-void RemoteSuggestionsProvider::NukeAllSnippets() {
- std::vector<Category> categories_to_erase;
-
- // Empty the ARTICLES category and remove all others, since they may or may
- // not be personalized.
- for (const auto& item : category_contents_) {
- Category category = item.first;
-
- ClearCachedSuggestions(category);
- ClearDismissedSuggestionsForDebugging(category);
-
- UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED);
-
- // Remove the category entirely; it may or may not reappear.
- if (category != articles_category_)
- categories_to_erase.push_back(category);
- }
-
- for (Category category : categories_to_erase) {
- category_contents_.erase(category);
- }
-
- StoreCategoriesToPrefs();
-}
-
-void RemoteSuggestionsProvider::OnSnippetImageFetchedFromDatabase(
- const ImageFetchedCallback& callback,
- const ContentSuggestion::ID& suggestion_id,
- std::string data) {
- // |image_decoder_| is null in tests.
- if (image_decoder_ && !data.empty()) {
- image_decoder_->DecodeImage(
- data, base::Bind(
- &RemoteSuggestionsProvider::OnSnippetImageDecodedFromDatabase,
- base::Unretained(this), callback, suggestion_id));
- return;
- }
-
- // Fetching from the DB failed; start a network fetch.
- FetchSnippetImageFromNetwork(suggestion_id, callback);
-}
-
-void RemoteSuggestionsProvider::OnSnippetImageDecodedFromDatabase(
- const ImageFetchedCallback& callback,
- const ContentSuggestion::ID& suggestion_id,
- 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());
-
- FetchSnippetImageFromNetwork(suggestion_id, callback);
-}
-
-void RemoteSuggestionsProvider::FetchSnippetImageFromNetwork(
- const ContentSuggestion::ID& suggestion_id,
- const ImageFetchedCallback& callback) {
- if (!base::ContainsKey(category_contents_, suggestion_id.category())) {
- OnSnippetImageDecodedFromNetwork(
- callback, suggestion_id.id_within_category(), gfx::Image());
- return;
- }
-
- GURL image_url = FindSnippetImageUrl(suggestion_id);
-
- if (image_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.
- OnSnippetImageDecodedFromNetwork(
- callback, suggestion_id.id_within_category(), gfx::Image());
- return;
- }
-
- image_fetcher_->StartOrQueueNetworkRequest(
- suggestion_id.id_within_category(), image_url,
- base::Bind(&RemoteSuggestionsProvider::OnSnippetImageDecodedFromNetwork,
- base::Unretained(this), callback));
-}
-
-void RemoteSuggestionsProvider::OnSnippetImageDecodedFromNetwork(
- const ImageFetchedCallback& callback,
- const std::string& id_within_category,
- const gfx::Image& image) {
- callback.Run(image);
-}
-
-void RemoteSuggestionsProvider::EnterStateReady() {
- if (nuke_when_initialized_) {
- NukeAllSnippets();
- nuke_when_initialized_ = false;
- }
-
- auto article_category_it = category_contents_.find(articles_category_);
- DCHECK(article_category_it != category_contents_.end());
- if (article_category_it->second.snippets.empty() || fetch_when_ready_) {
- // TODO(jkrcal): Fetching snippets automatically upon creation of this
- // lazily created service can cause troubles, e.g. in unit tests where
- // network I/O is not allowed.
- // Either add a DCHECK here that we actually are allowed to do network I/O
- // or change the logic so that some explicit call is always needed for the
- // network request.
- FetchSnippets(/*interactive_request=*/false);
- fetch_when_ready_ = false;
- }
-
- for (const auto& item : category_contents_) {
- Category category = item.first;
- const CategoryContent& content = item.second;
- // FetchSnippets has set the status to |AVAILABLE_LOADING| if relevant,
- // otherwise we transition to |AVAILABLE| here.
- if (content.status != CategoryStatus::AVAILABLE_LOADING)
- UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
- }
-}
-
-void RemoteSuggestionsProvider::EnterStateDisabled() {
- NukeAllSnippets();
-}
-
-void RemoteSuggestionsProvider::EnterStateError() {
- snippets_status_service_.reset();
-}
-
-void RemoteSuggestionsProvider::FinishInitialization() {
- if (nuke_when_initialized_) {
- // We nuke here in addition to EnterStateReady, so that it happens even if
- // we enter the DISABLED state below.
- NukeAllSnippets();
- nuke_when_initialized_ = false;
- }
-
- // |image_fetcher_| can be null in tests.
- if (image_fetcher_) {
- image_fetcher_->SetImageFetcherDelegate(this);
- image_fetcher_->SetDataUseServiceName(
- data_use_measurement::DataUseUserData::NTP_SNIPPETS);
- }
-
- // Note: Initializing the status service will run the callback right away with
- // the current state.
- snippets_status_service_->Init(
- base::Bind(&RemoteSuggestionsProvider::OnSnippetsStatusChanged,
- base::Unretained(this)));
-
- // Always notify here even if we got nothing from the database, because we
- // don't know how long the fetch will take or if it will even complete.
- for (const auto& item : category_contents_) {
- Category category = item.first;
- const CategoryContent& content = item.second;
- // Note: We might be in a non-available status here, e.g. DISABLED due to
- // enterprise policy.
- if (IsCategoryStatusAvailable(content.status))
- NotifyNewSuggestions(category, content);
- }
-}
-
-void RemoteSuggestionsProvider::OnSnippetsStatusChanged(
- SnippetsStatus old_snippets_status,
- SnippetsStatus new_snippets_status) {
- switch (new_snippets_status) {
- case SnippetsStatus::ENABLED_AND_SIGNED_IN:
- if (old_snippets_status == SnippetsStatus::ENABLED_AND_SIGNED_OUT) {
- DCHECK(state_ == State::READY);
- // Clear nonpersonalized suggestions.
- NukeAllSnippets();
- // Fetch personalized ones.
- FetchSnippets(/*interactive_request=*/true);
- } else {
- // Do not change the status. That will be done in EnterStateReady().
- EnterState(State::READY);
- }
- break;
-
- case SnippetsStatus::ENABLED_AND_SIGNED_OUT:
- if (old_snippets_status == SnippetsStatus::ENABLED_AND_SIGNED_IN) {
- DCHECK(state_ == State::READY);
- // Clear personalized suggestions.
- NukeAllSnippets();
- // Fetch nonpersonalized ones.
- FetchSnippets(/*interactive_request=*/true);
- } else {
- // Do not change the status. That will be done in EnterStateReady().
- EnterState(State::READY);
- }
- break;
-
- case SnippetsStatus::EXPLICITLY_DISABLED:
- EnterState(State::DISABLED);
- UpdateAllCategoryStatus(CategoryStatus::CATEGORY_EXPLICITLY_DISABLED);
- break;
-
- case SnippetsStatus::SIGNED_OUT_AND_DISABLED:
- EnterState(State::DISABLED);
- UpdateAllCategoryStatus(CategoryStatus::SIGNED_OUT);
- break;
- }
-}
-
-void RemoteSuggestionsProvider::EnterState(State state) {
- if (state == state_)
- return;
-
- UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.EnteredState",
- static_cast<int>(state),
- static_cast<int>(State::COUNT));
-
- switch (state) {
- case State::NOT_INITED:
- // Initial state, it should not be possible to get back there.
- NOTREACHED();
- break;
-
- case State::READY:
- DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED);
-
- DVLOG(1) << "Entering state: READY";
- state_ = State::READY;
- EnterStateReady();
- break;
-
- case State::DISABLED:
- DCHECK(state_ == State::NOT_INITED || state_ == State::READY);
-
- DVLOG(1) << "Entering state: DISABLED";
- state_ = State::DISABLED;
- EnterStateDisabled();
- break;
-
- case State::ERROR_OCCURRED:
- DVLOG(1) << "Entering state: ERROR_OCCURRED";
- state_ = State::ERROR_OCCURRED;
- EnterStateError();
- break;
-
- case State::COUNT:
- NOTREACHED();
- break;
- }
-
- // Schedule or un-schedule background fetching after each state change.
- RescheduleFetching(false);
-}
-
-void RemoteSuggestionsProvider::NotifyNewSuggestions(
- Category category,
- const CategoryContent& content) {
- DCHECK(IsCategoryStatusAvailable(content.status));
-
- std::vector<ContentSuggestion> result =
- ConvertToContentSuggestions(category, content.snippets);
-
- DVLOG(1) << "NotifyNewSuggestions(): " << result.size()
- << " items in category " << category;
- observer()->OnNewSuggestions(this, category, std::move(result));
-}
-
-void RemoteSuggestionsProvider::UpdateCategoryStatus(Category category,
- CategoryStatus status) {
- auto content_it = category_contents_.find(category);
- DCHECK(content_it != category_contents_.end());
- CategoryContent& content = content_it->second;
-
- if (status == content.status)
- return;
-
- DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": "
- << static_cast<int>(content.status) << " -> "
- << static_cast<int>(status);
- content.status = status;
- observer()->OnCategoryStatusChanged(this, category, content.status);
-}
-
-void RemoteSuggestionsProvider::UpdateAllCategoryStatus(CategoryStatus status) {
- for (const auto& category : category_contents_) {
- UpdateCategoryStatus(category.first, status);
- }
-}
-
-namespace {
-
-template <typename T>
-typename T::const_iterator FindSnippetInContainer(
- const T& container,
- const std::string& id_within_category) {
- return std::find_if(
- container.begin(), container.end(),
- [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) {
- return snippet->id() == id_within_category;
- });
-}
-
-} // namespace
-
-const NTPSnippet* RemoteSuggestionsProvider::CategoryContent::FindSnippet(
- const std::string& id_within_category) const {
- // Search for the snippet in current and archived snippets.
- auto it = FindSnippetInContainer(snippets, id_within_category);
- if (it != snippets.end()) {
- return it->get();
- }
- auto archived_it = FindSnippetInContainer(archived, id_within_category);
- if (archived_it != archived.end()) {
- return archived_it->get();
- }
- auto dismissed_it = FindSnippetInContainer(dismissed, id_within_category);
- if (dismissed_it != dismissed.end()) {
- return dismissed_it->get();
- }
- return nullptr;
-}
-
-RemoteSuggestionsProvider::CategoryContent*
-RemoteSuggestionsProvider::UpdateCategoryInfo(Category category,
- const CategoryInfo& info) {
- auto content_it = category_contents_.find(category);
- if (content_it == category_contents_.end()) {
- content_it = category_contents_
- .insert(std::make_pair(category, CategoryContent(info)))
- .first;
- } else {
- content_it->second.info = info;
- }
- return &content_it->second;
-}
-
-void RemoteSuggestionsProvider::RestoreCategoriesFromPrefs() {
- // This must only be called at startup, before there are any categories.
- DCHECK(category_contents_.empty());
-
- const base::ListValue* list =
- pref_service_->GetList(prefs::kRemoteSuggestionCategories);
- for (const std::unique_ptr<base::Value>& entry : *list) {
- const base::DictionaryValue* dict = nullptr;
- if (!entry->GetAsDictionary(&dict)) {
- DLOG(WARNING) << "Invalid category pref value: " << *entry;
- continue;
- }
- int id = 0;
- if (!dict->GetInteger(kCategoryContentId, &id)) {
- DLOG(WARNING) << "Invalid category pref value, missing '"
- << kCategoryContentId << "': " << *entry;
- continue;
- }
- base::string16 title;
- if (!dict->GetString(kCategoryContentTitle, &title)) {
- DLOG(WARNING) << "Invalid category pref value, missing '"
- << kCategoryContentTitle << "': " << *entry;
- continue;
- }
- bool included_in_last_server_response = false;
- if (!dict->GetBoolean(kCategoryContentProvidedByServer,
- &included_in_last_server_response)) {
- DLOG(WARNING) << "Invalid category pref value, missing '"
- << kCategoryContentProvidedByServer << "': " << *entry;
- continue;
- }
- bool allow_fetching_more_results = false;
- // This wasn't always around, so it's okay if it's missing.
- dict->GetBoolean(kCategoryContentAllowFetchingMore,
- &allow_fetching_more_results);
-
- Category category = category_factory()->FromIDValue(id);
- // TODO(tschumann): The following has a bad smell that category
- // serialization / deserialization should not be done inside this
- // class. We should move that into a central place that also knows how to
- // parse data we received from remote backends.
- CategoryInfo info =
- category == articles_category_
- ? BuildArticleCategoryInfo(title)
- : BuildRemoteCategoryInfo(title, allow_fetching_more_results);
- CategoryContent* content = UpdateCategoryInfo(category, info);
- content->included_in_last_server_response =
- included_in_last_server_response;
- }
-}
-
-void RemoteSuggestionsProvider::StoreCategoriesToPrefs() {
- // Collect all the CategoryContents.
- std::vector<std::pair<Category, const CategoryContent*>> to_store;
- for (const auto& entry : category_contents_)
- to_store.emplace_back(entry.first, &entry.second);
- // Sort them into the proper category order.
- std::sort(to_store.begin(), to_store.end(),
- [this](const std::pair<Category, const CategoryContent*>& left,
- const std::pair<Category, const CategoryContent*>& right) {
- return category_factory()->CompareCategories(left.first,
- right.first);
- });
- // Convert the relevant info into a base::ListValue for storage.
- base::ListValue list;
- for (const auto& entry : to_store) {
- const Category& category = entry.first;
- const CategoryContent& content = *entry.second;
- auto dict = base::MakeUnique<base::DictionaryValue>();
- dict->SetInteger(kCategoryContentId, category.id());
- // TODO(tschumann): Persist other properties of the CategoryInfo.
- dict->SetString(kCategoryContentTitle, content.info.title());
- dict->SetBoolean(kCategoryContentProvidedByServer,
- content.included_in_last_server_response);
- dict->SetBoolean(kCategoryContentAllowFetchingMore,
- content.info.has_more_action());
- list.Append(std::move(dict));
- }
- // Finally, store the result in the pref service.
- pref_service_->Set(prefs::kRemoteSuggestionCategories, list);
-}
-
-RemoteSuggestionsProvider::CategoryContent::CategoryContent(
- const CategoryInfo& info)
- : info(info) {}
-
-RemoteSuggestionsProvider::CategoryContent::CategoryContent(CategoryContent&&) =
- default;
-
-RemoteSuggestionsProvider::CategoryContent::~CategoryContent() = default;
-
-RemoteSuggestionsProvider::CategoryContent&
-RemoteSuggestionsProvider::CategoryContent::operator=(CategoryContent&&) =
- default;
-
} // 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 42f05c526aa..8e07099aea0 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider.h
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider.h
@@ -5,399 +5,56 @@
#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_H_
#define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_H_
-#include <cstddef>
-#include <deque>
-#include <map>
#include <memory>
-#include <set>
-#include <string>
-#include <vector>
#include "base/callback_forward.h"
-#include "base/gtest_prod_util.h"
#include "base/macros.h"
-#include "base/time/time.h"
-#include "components/image_fetcher/image_fetcher_delegate.h"
-#include "components/ntp_snippets/category.h"
-#include "components/ntp_snippets/category_factory.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/ntp_snippet.h"
-#include "components/ntp_snippets/remote/ntp_snippets_fetcher.h"
-#include "components/ntp_snippets/remote/ntp_snippets_scheduler.h"
-#include "components/ntp_snippets/remote/ntp_snippets_status_service.h"
-#include "components/ntp_snippets/remote/request_throttler.h"
-
-class PrefRegistrySimple;
-class PrefService;
-
-namespace gfx {
-class Image;
-} // namespace gfx
-
-namespace image_fetcher {
-class ImageDecoder;
-class ImageFetcher;
-} // namespace image_fetcher
namespace ntp_snippets {
-class RemoteSuggestionsDatabase;
-class UserClassifier;
+class RemoteSuggestionsFetcher;
// 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
-// unsafe to derive from it.
-// TODO(treib): Introduce two-phase initialization and make the class not final?
-// TODO(jkrcal): this class grows really, really large. The fact that
-// NTPSnippetService also implements ImageFetcherDelegate adds unnecessary
-// complexity (and after all the Service is conceptually not an
-// ImagerFetcherDeletage ;-)). Instead, the cleaner solution would be to define
-// a CachedImageFetcher class that handles the caching aspects and looks like an
-// image fetcher to the NTPSnippetService.
-class RemoteSuggestionsProvider final
- : public ContentSuggestionsProvider,
- public image_fetcher::ImageFetcherDelegate {
+class RemoteSuggestionsProvider : public ContentSuggestionsProvider {
public:
- // |application_language_code| should be a ISO 639-1 compliant string, e.g.
- // 'en' or 'en-US'. Note that this code should only specify the language, not
- // the locale, so 'en_US' (English language with US locale) and 'en-GB_US'
- // (British English person in the US) are not language codes.
- RemoteSuggestionsProvider(
- Observer* observer,
- CategoryFactory* category_factory,
- PrefService* pref_service,
- const std::string& application_language_code,
- const UserClassifier* user_classifier,
- NTPSnippetsScheduler* scheduler,
- std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher,
- std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
- std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
- std::unique_ptr<RemoteSuggestionsDatabase> database,
- std::unique_ptr<NTPSnippetsStatusService> status_service);
+ // TODO(jkrcal): Would be nice to get rid of this another level of statuses.
+ // Maybe possible while refactoring the RemoteSuggestionsStatusService? (and
+ // letting it notify both the SchedulingRemoteSuggestionsProvider and
+ // RemoteSuggestionsProviderImpl or just the scheduling one).
+ enum class ProviderStatus { ACTIVE, INACTIVE };
+ using ProviderStatusCallback =
+ base::RepeatingCallback<void(ProviderStatus status)>;
+
+ // Callback to notify with the result of a fetch.
+ // TODO(jkrcal): Change to OnceCallback? A OnceCallback does only have a
+ // move-constructor which seems problematic for google mock.
+ using FetchStatusCallback = base::Callback<void(Status status_code)>;
~RemoteSuggestionsProvider() override;
- static void RegisterProfilePrefs(PrefRegistrySimple* registry);
-
- // Returns whether the service is ready. While this is false, the list of
- // snippets will be empty, and all modifications to it (fetch, dismiss, etc)
- // will be ignored.
- bool ready() const { return state_ == State::READY; }
-
- // Returns whether the service is initialized. While this is false, some
- // calls may trigger DCHECKs.
- bool initialized() const { return ready() || state_ == State::DISABLED; }
-
- // Fetches snippets from the server and replaces old snippets by the new ones.
- // Requests can be marked more important by setting |interactive_request| to
- // true (such request might circumvent the daily quota for requests, etc.)
- // Useful for requests triggered by the user.
- void FetchSnippets(bool interactive_request);
-
- // Fetches snippets from the server for specified hosts and adds them to the
- // current ones. Only called from chrome://snippets-internals, DO NOT USE
- // otherwise! Ignored while ready() is false.
- void FetchSnippetsFromHosts(const std::set<std::string>& hosts,
- bool interactive_request);
-
- const NTPSnippetsFetcher* snippets_fetcher() const {
- return snippets_fetcher_.get();
- }
-
- // (Re)schedules the periodic fetching of snippets. If |force| is true, the
- // tasks will be re-scheduled even if they already exist and have the correct
- // periods.
- void RescheduleFetching(bool force);
-
- // 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;
-
- // Returns the maximum number of snippets that will be shown at once.
- static int GetMaxSnippetCountForTesting();
-
- // Available snippets, only for unit tests.
- // TODO(treib): Get rid of this. Tests should use a fake observer instead.
- const NTPSnippet::PtrVector& GetSnippetsForTesting(Category category) const {
- return category_contents_.find(category)->second.snippets;
- }
-
- // Dismissed snippets, only for unit tests.
- const NTPSnippet::PtrVector& GetDismissedSnippetsForTesting(
- Category category) const {
- return category_contents_.find(category)->second.dismissed;
- }
-
- private:
- friend class RemoteSuggestionsProviderTest;
-
- FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest,
- DontNotifyIfNotAvailable);
- FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest,
- RemoveExpiredDismissedContent);
- FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest,
- RescheduleOnStateChange);
- FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest, StatusChanges);
- FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest,
- SuggestionsFetchedOnSignInAndSignOut);
-
- // Possible state transitions:
- // NOT_INITED --------+
- // / \ |
- // v v |
- // READY <--> DISABLED |
- // \ / |
- // v v |
- // ERROR_OCCURRED <-----+
- enum class State {
- // The service has just been created. Can change to states:
- // - DISABLED: After the database is done loading,
- // GetStateForDependenciesStatus can identify the next state to
- // be DISABLED.
- // - READY: if GetStateForDependenciesStatus returns it, after the database
- // is done loading.
- // - ERROR_OCCURRED: when an unrecoverable error occurred.
- NOT_INITED,
-
- // The service registered observers, timers, etc. and is ready to answer to
- // queries, fetch snippets... Can change to states:
- // - DISABLED: when the global Chrome state changes, for example after
- // |OnStateChanged| is called and sync is disabled.
- // - ERROR_OCCURRED: when an unrecoverable error occurred.
- READY,
-
- // The service is disabled and unregistered the related resources.
- // Can change to states:
- // - READY: when the global Chrome state changes, for example after
- // |OnStateChanged| is called and sync is enabled.
- // - ERROR_OCCURRED: when an unrecoverable error occurred.
- DISABLED,
-
- // The service or one of its dependencies encountered an unrecoverable error
- // and the service can't be used anymore.
- ERROR_OCCURRED,
-
- COUNT
- };
-
- struct CategoryContent {
- // The current status of the category.
- CategoryStatus status = CategoryStatus::INITIALIZING;
-
- // The additional information about a category.
- CategoryInfo info;
-
- // True iff the server returned results in this category in the last fetch.
- // We never remove categories that the server still provides, but if the
- // server stops providing a category, we won't yet report it as NOT_PROVIDED
- // while we still have non-expired snippets in it.
- bool included_in_last_server_response = true;
-
- // All currently active suggestions (excl. the dismissed ones).
- NTPSnippet::PtrVector snippets;
-
- // All previous suggestions that we keep around in memory because they can
- // be on some open NTP. We do not persist this list so that on a new start
- // of Chrome, this is empty.
- // |archived| is a FIFO buffer with a maximum length.
- std::deque<std::unique_ptr<NTPSnippet>> archived;
-
- // Suggestions that the user dismissed. We keep these around until they
- // expire so we won't re-add them to |snippets| on the next fetch.
- NTPSnippet::PtrVector dismissed;
-
- // Returns a non-dismissed snippet with the given |id_within_category|, or
- // null if none exist.
- const NTPSnippet* FindSnippet(const std::string& id_within_category) const;
-
- explicit CategoryContent(const CategoryInfo& info);
- CategoryContent(CategoryContent&&);
- ~CategoryContent();
- CategoryContent& operator=(CategoryContent&&);
- };
-
- // Returns the URL of the image of a snippet if it is among the current or
- // among the archived snippets in the matching category. Returns an empty URL
- // otherwise.
- GURL FindSnippetImageUrl(const ContentSuggestion::ID& suggestion_id) const;
-
- // image_fetcher::ImageFetcherDelegate implementation.
- void OnImageDataFetched(const std::string& id_within_category,
- const std::string& image_data) override;
-
- // Callbacks for the RemoteSuggestionsDatabase.
- void OnDatabaseLoaded(NTPSnippet::PtrVector snippets);
- void OnDatabaseError();
-
- // Callback for fetch-more requests with the NTPSnippetsFetcher.
- void OnFetchMoreFinished(
- FetchDoneCallback fetching_callback,
- NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories);
-
- // Callback for regular fetch requests with the NTPSnippetsFetcher.
- void OnFetchFinished(
- NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories);
-
- // Moves all snippets from |to_archive| into the archive of the |content|.
- // Clears |to_archive|. As the archive is a FIFO buffer of limited size, this
- // function will also delete images from the database in case the associated
- // snippet gets evicted from the archive.
- void ArchiveSnippets(CategoryContent* content,
- NTPSnippet::PtrVector* to_archive);
-
- // Sanitizes newly fetched snippets -- e.g. adding missing dates and filtering
- // out incomplete results or dismissed snippets (indicated by |dismissed|).
- void SanitizeReceivedSnippets(const NTPSnippet::PtrVector& dismissed,
- NTPSnippet::PtrVector* snippets);
-
- // Adds newly available suggestions to |content|.
- void IntegrateSnippets(CategoryContent* content,
- NTPSnippet::PtrVector new_snippets);
-
- // Dismisses a snippet within a given category content.
- // Note that this modifies the snippet datastructures of |content|
- // invalidating iterators.
- void DismissSuggestionFromCategoryContent(
- CategoryContent* content,
- const std::string& id_within_category);
-
- // Removes expired dismissed snippets from the service and the database.
- void ClearExpiredDismissedSnippets();
-
- // Removes images from the DB that are not referenced from any known snippet.
- // Needs to iterate the whole snippet database -- so do it often enough to
- // keep it small but not too often as it still iterates over the file system.
- void ClearOrphanedImages();
-
- // Clears all stored snippets and updates the observer.
- void NukeAllSnippets();
-
- // Completes the initialization phase of the service, registering the last
- // observers. This is done after construction, once the database is loaded.
- void FinishInitialization();
-
- void OnSnippetImageFetchedFromDatabase(
- const ImageFetchedCallback& callback,
- const ContentSuggestion::ID& suggestion_id,
- std::string data);
-
- void OnSnippetImageDecodedFromDatabase(
- const ImageFetchedCallback& callback,
- const ContentSuggestion::ID& suggestion_id,
- const gfx::Image& image);
-
- void FetchSnippetImageFromNetwork(const ContentSuggestion::ID& suggestion_id,
- const ImageFetchedCallback& callback);
-
- void OnSnippetImageDecodedFromNetwork(const ImageFetchedCallback& callback,
- const std::string& id_within_category,
- const gfx::Image& image);
-
- // Triggers a state transition depending on the provided snippets status. This
- // method is called when a change is detected by |snippets_status_service_|.
- void OnSnippetsStatusChanged(SnippetsStatus old_snippets_status,
- SnippetsStatus new_snippets_status);
-
- // Verifies state transitions (see |State|'s documentation) and applies them.
- // Also updates the provider status. Does nothing except updating the provider
- // status if called with the current state.
- void EnterState(State state);
-
- // Enables the service. Do not call directly, use |EnterState| instead.
- void EnterStateReady();
-
- // Disables the service. Do not call directly, use |EnterState| instead.
- void EnterStateDisabled();
-
- // Disables the service permanently because an unrecoverable error occurred.
- // Do not call directly, use |EnterState| instead.
- void EnterStateError();
-
- // Converts the cached snippets in the given |category| to content suggestions
- // and notifies the observer.
- void NotifyNewSuggestions(Category category, const CategoryContent& content);
-
- // Updates the internal status for |category| to |category_status_| and
- // notifies the content suggestions observer if it changed.
- void UpdateCategoryStatus(Category category, CategoryStatus status);
- // Calls UpdateCategoryStatus() for all provided categories.
- void UpdateAllCategoryStatus(CategoryStatus status);
-
- // Updates the category info for |category|. If a corresponding
- // CategoryContent object does not exist, it will be created.
- // Returns the existing or newly created object.
- CategoryContent* UpdateCategoryInfo(Category category,
- const CategoryInfo& info);
-
- void RestoreCategoriesFromPrefs();
- void StoreCategoriesToPrefs();
-
- NTPSnippetsFetcher::Params BuildFetchParams(
- bool exclude_archived_suggestions) const;
-
- void MarkEmptyCategoriesAsLoading();
-
- State state_;
-
- PrefService* pref_service_;
-
- const Category articles_category_;
-
- std::map<Category, CategoryContent, Category::CompareByID> category_contents_;
-
- // The ISO 639-1 code of the language used by the application.
- const std::string application_language_code_;
-
- // Classifier that tells us how active the user is. Not owned.
- const UserClassifier* user_classifier_;
-
- // Scheduler for fetching snippets. Not owned.
- NTPSnippetsScheduler* scheduler_;
-
- // The snippets fetcher.
- std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher_;
-
- std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher_;
- std::unique_ptr<image_fetcher::ImageDecoder> image_decoder_;
-
- // The database for persisting snippets.
- std::unique_ptr<RemoteSuggestionsDatabase> database_;
- base::TimeTicks database_load_start_;
-
- // The service that provides events and data about the signin and sync state.
- std::unique_ptr<NTPSnippetsStatusService> snippets_status_service_;
-
- // Set to true if FetchSnippets is called while the service isn't ready.
- // The fetch will be executed once the service enters the READY state.
- bool fetch_when_ready_;
-
- // Set to true if NukeAllSnippets is called while the service isn't ready.
- // The nuke will be executed once the service finishes initialization or
- // enters the READY state.
- bool nuke_when_initialized_;
-
- // Request throttler for limiting requests to thumbnail images.
- RequestThrottler thumbnail_requests_throttler_;
-
- DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProvider);
+ // Set a callback to be notified whenever the status of the provider changes.
+ // The initial change is also notified (switching from an initial undecided
+ // status). If the callback is set after the first change, it is called back
+ // immediately.
+ virtual void SetProviderStatusCallback(
+ std::unique_ptr<ProviderStatusCallback> callback) = 0;
+
+ // Fetches snippets from the server for all remote categories and replaces old
+ // snippets by the new ones. The request to the server is performed as an
+ // background request. Background requests are used for actions not triggered
+ // by the user and have lower priority on the server. After the fetch
+ // finished, the provided |callback| will be triggered with the status of the
+ // fetch (unless not nullptr).
+ virtual void RefetchInTheBackground(
+ std::unique_ptr<FetchStatusCallback> callback) = 0;
+
+ virtual const RemoteSuggestionsFetcher* suggestions_fetcher_for_debugging()
+ const = 0;
+
+ protected:
+ RemoteSuggestionsProvider(Observer* observer);
};
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
new file mode 100644
index 00000000000..4aec9ba91ec
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -0,0 +1,1284 @@
+// 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/ntp_snippets/remote/remote_suggestions_provider_impl.h"
+
+#include <algorithm>
+#include <iterator>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/feature_list.h"
+#include "base/location.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/stl_util.h"
+#include "base/strings/string_number_conversions.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/data_use_measurement/core/data_use_user_data.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/image_fetcher/image_decoder.h"
+#include "components/image_fetcher/image_fetcher.h"
+#include "components/ntp_snippets/category_rankers/category_ranker.h"
+#include "components/ntp_snippets/pref_names.h"
+#include "components/ntp_snippets/remote/remote_suggestions_database.h"
+#include "components/ntp_snippets/switches.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/variations/variations_associated_data.h"
+#include "grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/image/image.h"
+
+namespace ntp_snippets {
+
+namespace {
+
+// Number of snippets requested to the server. Consider replacing sparse UMA
+// histograms with COUNTS() if this number increases beyond 50.
+const int kMaxSnippetCount = 10;
+
+// Number of archived snippets we keep around in memory.
+const int kMaxArchivedSnippetCount = 200;
+
+// Keys for storing CategoryContent info in prefs.
+const char kCategoryContentId[] = "id";
+const char kCategoryContentTitle[] = "title";
+const char kCategoryContentProvidedByServer[] = "provided_by_server";
+const char kCategoryContentAllowFetchingMore[] = "allow_fetching_more";
+
+// TODO(treib): Remove after M57.
+const char kDeprecatedSnippetHostsPref[] = "ntp_snippets.hosts";
+
+template<typename SnippetPtrContainer>
+std::unique_ptr<std::vector<std::string>> GetSnippetIDVector(
+ const SnippetPtrContainer& snippets) {
+ auto result = base::MakeUnique<std::vector<std::string>>();
+ for (const auto& snippet : snippets) {
+ result->push_back(snippet->id());
+ }
+ return result;
+}
+
+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)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void EraseByPrimaryID(NTPSnippet::PtrVector* snippets,
+ const std::vector<std::string>& ids) {
+ std::set<std::string> ids_lookup(ids.begin(), ids.end());
+ snippets->erase(
+ std::remove_if(snippets->begin(), snippets->end(),
+ [&ids_lookup](const std::unique_ptr<NTPSnippet>& snippet) {
+ return base::ContainsValue(ids_lookup, snippet->id());
+ }),
+ snippets->end());
+}
+
+void EraseMatchingSnippets(NTPSnippet::PtrVector* snippets,
+ const NTPSnippet::PtrVector& compare_against) {
+ std::set<std::string> compare_against_ids;
+ for (const std::unique_ptr<NTPSnippet>& snippet : compare_against) {
+ const std::vector<std::string>& snippet_ids = snippet->GetAllIDs();
+ compare_against_ids.insert(snippet_ids.begin(), snippet_ids.end());
+ }
+ snippets->erase(
+ std::remove_if(
+ snippets->begin(), snippets->end(),
+ [&compare_against_ids](const std::unique_ptr<NTPSnippet>& snippet) {
+ return HasIntersection(snippet->GetAllIDs(), compare_against_ids);
+ }),
+ snippets->end());
+}
+
+void RemoveNullPointers(NTPSnippet::PtrVector* snippets) {
+ snippets->erase(
+ std::remove_if(
+ snippets->begin(), snippets->end(),
+ [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }),
+ snippets->end());
+}
+
+void RemoveIncompleteSnippets(NTPSnippet::PtrVector* snippets) {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAddIncompleteSnippets)) {
+ return;
+ }
+ int num_snippets = snippets->size();
+ // Remove snippets that do not have all the info we need to display it to
+ // the user.
+ snippets->erase(
+ std::remove_if(snippets->begin(), snippets->end(),
+ [](const std::unique_ptr<NTPSnippet>& snippet) {
+ return !snippet->is_complete();
+ }),
+ snippets->end());
+ int num_snippets_removed = num_snippets - snippets->size();
+ UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch",
+ num_snippets_removed > 0);
+ if (num_snippets_removed > 0) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets",
+ num_snippets_removed);
+ }
+}
+
+std::vector<ContentSuggestion> ConvertToContentSuggestions(
+ Category category,
+ const NTPSnippet::PtrVector& snippets) {
+ std::vector<ContentSuggestion> result;
+ for (const std::unique_ptr<NTPSnippet>& snippet : snippets) {
+ // TODO(sfiera): if a snippet is not going to be displayed, move it
+ // directly to content.dismissed on fetch. Otherwise, we might prune
+ // other snippets to get down to kMaxSnippetCount, only to hide one of the
+ // incomplete ones we kept.
+ if (!snippet->is_complete()) {
+ continue;
+ }
+ result.emplace_back(snippet->ToContentSuggestion(category));
+ }
+ return result;
+}
+
+void CallWithEmptyResults(const FetchDoneCallback& callback,
+ const Status& status) {
+ if (callback.is_null()) {
+ return;
+ }
+ callback.Run(status, std::vector<ContentSuggestion>());
+}
+
+} // namespace
+
+CachedImageFetcher::CachedImageFetcher(
+ std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
+ std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
+ PrefService* pref_service,
+ RemoteSuggestionsDatabase* database)
+ : image_fetcher_(std::move(image_fetcher)),
+ image_decoder_(std::move(image_decoder)),
+ 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);
+ }
+}
+
+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::OnSnippetImageFetchedFromDatabase,
+ 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 OnSnippetImageDecodedFromDatabase().
+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) {
+ callback.Run(image);
+}
+
+void CachedImageFetcher::OnSnippetImageFetchedFromDatabase(
+ const ImageFetchedCallback& callback,
+ const ContentSuggestion::ID& suggestion_id,
+ const GURL& url,
+ std::string data) { // SnippetImageCallback requires nonconst reference.
+ // |image_decoder_| is null in tests.
+ if (image_decoder_ && !data.empty()) {
+ image_decoder_->DecodeImage(
+ data, base::Bind(&CachedImageFetcher::OnSnippetImageDecodedFromDatabase,
+ base::Unretained(this), callback, suggestion_id, url));
+ return;
+ }
+ // Fetching from the DB failed; start a network fetch.
+ FetchSnippetImageFromNetwork(suggestion_id, url, callback);
+}
+
+void CachedImageFetcher::OnSnippetImageDecodedFromDatabase(
+ 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());
+ FetchSnippetImageFromNetwork(suggestion_id, url, callback);
+}
+
+void CachedImageFetcher::FetchSnippetImageFromNetwork(
+ 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;
+ }
+
+ image_fetcher_->StartOrQueueNetworkRequest(
+ suggestion_id.id_within_category(), url,
+ base::Bind(&CachedImageFetcher::OnImageDecodingDone,
+ base::Unretained(this), callback));
+}
+
+RemoteSuggestionsProviderImpl::RemoteSuggestionsProviderImpl(
+ Observer* observer,
+ PrefService* pref_service,
+ const std::string& application_language_code,
+ CategoryRanker* category_ranker,
+ std::unique_ptr<RemoteSuggestionsFetcher> suggestions_fetcher,
+ std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
+ std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
+ std::unique_ptr<RemoteSuggestionsDatabase> database,
+ std::unique_ptr<RemoteSuggestionsStatusService> status_service)
+ : RemoteSuggestionsProvider(observer),
+ state_(State::NOT_INITED),
+ pref_service_(pref_service),
+ articles_category_(
+ Category::FromKnownCategory(KnownCategories::ARTICLES)),
+ application_language_code_(application_language_code),
+ category_ranker_(category_ranker),
+ suggestions_fetcher_(std::move(suggestions_fetcher)),
+ database_(std::move(database)),
+ image_fetcher_(std::move(image_fetcher),
+ std::move(image_decoder),
+ pref_service,
+ database_.get()),
+ status_service_(std::move(status_service)),
+ fetch_when_ready_(false),
+ fetch_when_ready_interactive_(false),
+ fetch_when_ready_callback_(nullptr),
+ provider_status_callback_(nullptr),
+ nuke_when_initialized_(false),
+ clock_(base::MakeUnique<base::DefaultClock>()) {
+ pref_service_->ClearPref(kDeprecatedSnippetHostsPref);
+
+ RestoreCategoriesFromPrefs();
+ // The articles category always exists. Add it if we didn't get it from prefs.
+ // TODO(treib): Rethink this.
+ category_contents_.insert(
+ std::make_pair(articles_category_,
+ CategoryContent(BuildArticleCategoryInfo(base::nullopt))));
+ // Tell the observer about all the categories.
+ for (const auto& entry : category_contents_) {
+ observer->OnCategoryStatusChanged(this, entry.first, entry.second.status);
+ }
+
+ if (database_->IsErrorState()) {
+ EnterState(State::ERROR_OCCURRED);
+ UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR);
+ return;
+ }
+
+ database_->SetErrorCallback(base::Bind(
+ &RemoteSuggestionsProviderImpl::OnDatabaseError, base::Unretained(this)));
+
+ // We transition to other states while finalizing the initialization, when the
+ // database is done loading.
+ database_load_start_ = base::TimeTicks::Now();
+ database_->LoadSnippets(
+ base::Bind(&RemoteSuggestionsProviderImpl::OnDatabaseLoaded,
+ base::Unretained(this)));
+}
+
+RemoteSuggestionsProviderImpl::~RemoteSuggestionsProviderImpl() = default;
+
+// static
+void RemoteSuggestionsProviderImpl::RegisterProfilePrefs(
+ PrefRegistrySimple* registry) {
+ // TODO(treib): Remove after M57.
+ registry->RegisterListPref(kDeprecatedSnippetHostsPref);
+ registry->RegisterListPref(prefs::kRemoteSuggestionCategories);
+ registry->RegisterInt64Pref(prefs::kLastSuccessfulBackgroundFetchTime, 0);
+
+ RemoteSuggestionsStatusService::RegisterProfilePrefs(registry);
+}
+
+void RemoteSuggestionsProviderImpl::SetProviderStatusCallback(
+ std::unique_ptr<ProviderStatusCallback> callback) {
+ provider_status_callback_ = std::move(callback);
+ // Call the observer right away if we've reached any final state.
+ NotifyStateChanged();
+}
+
+void RemoteSuggestionsProviderImpl::ReloadSuggestions() {
+ FetchSnippets(/*interactive_request=*/true,
+ /*callback=*/nullptr);
+}
+
+void RemoteSuggestionsProviderImpl::RefetchInTheBackground(
+ std::unique_ptr<FetchStatusCallback> callback) {
+ FetchSnippets(/*interactive_request=*/false, std::move(callback));
+}
+
+const RemoteSuggestionsFetcher*
+RemoteSuggestionsProviderImpl::suggestions_fetcher_for_debugging() const {
+ return suggestions_fetcher_.get();
+}
+
+void RemoteSuggestionsProviderImpl::FetchSnippets(
+ bool interactive_request,
+ std::unique_ptr<FetchStatusCallback> callback) {
+ if (!ready()) {
+ fetch_when_ready_ = true;
+ fetch_when_ready_interactive_ = interactive_request;
+ fetch_when_ready_callback_ = std::move(callback);
+ return;
+ }
+
+ MarkEmptyCategoriesAsLoading();
+
+ RequestParams params = BuildFetchParams();
+ params.interactive_request = interactive_request;
+ suggestions_fetcher_->FetchSnippets(
+ params, base::BindOnce(&RemoteSuggestionsProviderImpl::OnFetchFinished,
+ base::Unretained(this), std::move(callback),
+ interactive_request));
+}
+
+void RemoteSuggestionsProviderImpl::Fetch(
+ const Category& category,
+ const std::set<std::string>& known_suggestion_ids,
+ const FetchDoneCallback& callback) {
+ if (!ready()) {
+ CallWithEmptyResults(callback,
+ Status(StatusCode::TEMPORARY_ERROR,
+ "RemoteSuggestionsProvider is not ready!"));
+ return;
+ }
+ RequestParams params = BuildFetchParams();
+ params.excluded_ids.insert(known_suggestion_ids.begin(),
+ known_suggestion_ids.end());
+ params.interactive_request = true;
+ params.exclusive_category = category;
+
+ suggestions_fetcher_->FetchSnippets(
+ params,
+ base::BindOnce(&RemoteSuggestionsProviderImpl::OnFetchMoreFinished,
+ base::Unretained(this), callback));
+}
+
+// Builds default fetcher params.
+RequestParams RemoteSuggestionsProviderImpl::BuildFetchParams() const {
+ RequestParams result;
+ result.language_code = application_language_code_;
+ result.count_to_fetch = kMaxSnippetCount;
+ for (const auto& map_entry : category_contents_) {
+ const CategoryContent& content = map_entry.second;
+ for (const auto& dismissed_snippet : content.dismissed) {
+ result.excluded_ids.insert(dismissed_snippet->id());
+ }
+ }
+ return result;
+}
+
+void RemoteSuggestionsProviderImpl::MarkEmptyCategoriesAsLoading() {
+ for (const auto& item : category_contents_) {
+ Category category = item.first;
+ const CategoryContent& content = item.second;
+ if (content.snippets.empty()) {
+ UpdateCategoryStatus(category, CategoryStatus::AVAILABLE_LOADING);
+ }
+ }
+}
+
+CategoryStatus RemoteSuggestionsProviderImpl::GetCategoryStatus(
+ Category category) {
+ auto content_it = category_contents_.find(category);
+ DCHECK(content_it != category_contents_.end());
+ return content_it->second.status;
+}
+
+CategoryInfo RemoteSuggestionsProviderImpl::GetCategoryInfo(Category category) {
+ auto content_it = category_contents_.find(category);
+ DCHECK(content_it != category_contents_.end());
+ return content_it->second.info;
+}
+
+void RemoteSuggestionsProviderImpl::DismissSuggestion(
+ const ContentSuggestion::ID& suggestion_id) {
+ if (!ready()) {
+ return;
+ }
+
+ auto content_it = category_contents_.find(suggestion_id.category());
+ DCHECK(content_it != category_contents_.end());
+ CategoryContent* content = &content_it->second;
+ DismissSuggestionFromCategoryContent(content,
+ suggestion_id.id_within_category());
+}
+
+void RemoteSuggestionsProviderImpl::ClearHistory(
+ base::Time begin,
+ base::Time end,
+ const base::Callback<bool(const GURL& url)>& filter) {
+ // Both time range and the filter are ignored and all suggestions are removed,
+ // because it is not known which history entries were used for the suggestions
+ // personalization.
+ if (!ready()) {
+ // No need to refresh the UI afterwards as we didn't provide any data to the
+ // UI so far.
+ nuke_when_initialized_ = true;
+ } else {
+ NukeAllSnippets();
+ }
+}
+
+void RemoteSuggestionsProviderImpl::ClearCachedSuggestions(Category category) {
+ if (!initialized()) {
+ return;
+ }
+
+ auto content_it = category_contents_.find(category);
+ if (content_it == category_contents_.end()) {
+ return;
+ }
+ CategoryContent* content = &content_it->second;
+ // TODO(tschumann): We do the unnecessary checks for .empty() in many places
+ // before calling database methods. Change the RemoteSuggestionsDatabase to
+ // return early for those and remove the many if statements in this file.
+ if (!content->snippets.empty()) {
+ database_->DeleteSnippets(GetSnippetIDVector(content->snippets));
+ database_->DeleteImages(GetSnippetIDVector(content->snippets));
+ content->snippets.clear();
+ }
+ if (!content->archived.empty()) {
+ database_->DeleteSnippets(GetSnippetIDVector(content->archived));
+ database_->DeleteImages(GetSnippetIDVector(content->archived));
+ content->archived.clear();
+ }
+}
+
+void RemoteSuggestionsProviderImpl::OnSignInStateChanged() {
+ // Make sure the status service is registered and we already initialised its
+ // start state.
+ if (!initialized()) {
+ return;
+ }
+
+ status_service_->OnSignInStateChanged();
+}
+
+void RemoteSuggestionsProviderImpl::GetDismissedSuggestionsForDebugging(
+ Category category,
+ const DismissedSuggestionsCallback& callback) {
+ auto content_it = category_contents_.find(category);
+ DCHECK(content_it != category_contents_.end());
+ callback.Run(
+ ConvertToContentSuggestions(category, content_it->second.dismissed));
+}
+
+void RemoteSuggestionsProviderImpl::ClearDismissedSuggestionsForDebugging(
+ Category category) {
+ auto content_it = category_contents_.find(category);
+ DCHECK(content_it != category_contents_.end());
+ CategoryContent* content = &content_it->second;
+
+ if (!initialized()) {
+ return;
+ }
+
+ if (content->dismissed.empty()) {
+ return;
+ }
+
+ database_->DeleteSnippets(GetSnippetIDVector(content->dismissed));
+ // The image got already deleted when the suggestion was dismissed.
+
+ content->dismissed.clear();
+}
+
+// static
+int RemoteSuggestionsProviderImpl::GetMaxSnippetCountForTesting() {
+ return kMaxSnippetCount;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private methods
+
+GURL RemoteSuggestionsProviderImpl::FindSnippetImageUrl(
+ const ContentSuggestion::ID& suggestion_id) const {
+ DCHECK(base::ContainsKey(category_contents_, suggestion_id.category()));
+
+ const CategoryContent& content =
+ category_contents_.at(suggestion_id.category());
+ const NTPSnippet* snippet =
+ content.FindSnippet(suggestion_id.id_within_category());
+ if (!snippet) {
+ return GURL();
+ }
+ return snippet->salient_image_url();
+}
+
+void RemoteSuggestionsProviderImpl::OnDatabaseLoaded(
+ NTPSnippet::PtrVector snippets) {
+ if (state_ == State::ERROR_OCCURRED) {
+ return;
+ }
+ DCHECK(state_ == State::NOT_INITED);
+ DCHECK(base::ContainsKey(category_contents_, articles_category_));
+
+ base::TimeDelta database_load_time =
+ base::TimeTicks::Now() - database_load_start_;
+ UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.Snippets.DatabaseLoadTime",
+ database_load_time);
+
+ NTPSnippet::PtrVector to_delete;
+ for (std::unique_ptr<NTPSnippet>& snippet : snippets) {
+ Category snippet_category =
+ Category::FromRemoteCategory(snippet->remote_category_id());
+ auto content_it = category_contents_.find(snippet_category);
+ // We should already know about the category.
+ if (content_it == category_contents_.end()) {
+ DLOG(WARNING) << "Loaded a suggestion for unknown category "
+ << snippet_category << " from the DB; deleting";
+ to_delete.emplace_back(std::move(snippet));
+ continue;
+ }
+ CategoryContent* content = &content_it->second;
+ if (snippet->is_dismissed()) {
+ content->dismissed.emplace_back(std::move(snippet));
+ } else {
+ content->snippets.emplace_back(std::move(snippet));
+ }
+ }
+ if (!to_delete.empty()) {
+ database_->DeleteSnippets(GetSnippetIDVector(to_delete));
+ database_->DeleteImages(GetSnippetIDVector(to_delete));
+ }
+
+ // Sort the suggestions in each category.
+ // TODO(treib): Persist the actual order in the DB somehow? crbug.com/654409
+ for (auto& entry : category_contents_) {
+ CategoryContent* content = &entry.second;
+ std::sort(content->snippets.begin(), content->snippets.end(),
+ [](const std::unique_ptr<NTPSnippet>& lhs,
+ const std::unique_ptr<NTPSnippet>& rhs) {
+ return lhs->score() > rhs->score();
+ });
+ }
+
+ // TODO(tschumann): If I move ClearExpiredDismissedSnippets() to the beginning
+ // of the function, it essentially does nothing but tests are still green. Fix
+ // this!
+ ClearExpiredDismissedSnippets();
+ ClearOrphanedImages();
+ FinishInitialization();
+}
+
+void RemoteSuggestionsProviderImpl::OnDatabaseError() {
+ EnterState(State::ERROR_OCCURRED);
+ UpdateAllCategoryStatus(CategoryStatus::LOADING_ERROR);
+}
+
+void RemoteSuggestionsProviderImpl::OnFetchMoreFinished(
+ const FetchDoneCallback& fetching_callback,
+ Status status,
+ RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories) {
+ if (!fetched_categories) {
+ DCHECK(!status.IsSuccess());
+ CallWithEmptyResults(fetching_callback, status);
+ return;
+ }
+ if (fetched_categories->size() != 1u) {
+ LOG(DFATAL) << "Requested one exclusive category but received "
+ << fetched_categories->size() << " categories.";
+ CallWithEmptyResults(fetching_callback,
+ Status(StatusCode::PERMANENT_ERROR,
+ "RemoteSuggestionsProvider received more "
+ "categories than requested."));
+ return;
+ }
+ auto& fetched_category = (*fetched_categories)[0];
+ Category category = fetched_category.category;
+ CategoryContent* existing_content =
+ UpdateCategoryInfo(category, fetched_category.info);
+ SanitizeReceivedSnippets(existing_content->dismissed,
+ &fetched_category.snippets);
+ // We compute the result now before modifying |fetched_category.snippets|.
+ // However, we wait with notifying the caller until the end of the method when
+ // all state is updated.
+ std::vector<ContentSuggestion> result =
+ ConvertToContentSuggestions(category, fetched_category.snippets);
+
+ // Fill up the newly fetched snippets with existing ones, store them, and
+ // notify observers about new data.
+ while (fetched_category.snippets.size() <
+ static_cast<size_t>(kMaxSnippetCount) &&
+ !existing_content->snippets.empty()) {
+ fetched_category.snippets.emplace(
+ fetched_category.snippets.begin(),
+ std::move(existing_content->snippets.back()));
+ existing_content->snippets.pop_back();
+ }
+ std::vector<std::string> to_dismiss =
+ *GetSnippetIDVector(existing_content->snippets);
+ for (const auto& id : to_dismiss) {
+ DismissSuggestionFromCategoryContent(existing_content, id);
+ }
+ DCHECK(existing_content->snippets.empty());
+
+ IntegrateSnippets(existing_content, std::move(fetched_category.snippets));
+
+ // 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
+ // once the snippets fetcher supports concurrent requests. We can then see if
+ // Nuke should also cancel outstanding requests or we want to check the
+ // status.
+ UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
+ // Notify callers and observers.
+ fetching_callback.Run(Status::Success(), std::move(result));
+ NotifyNewSuggestions(category, *existing_content);
+}
+
+void RemoteSuggestionsProviderImpl::OnFetchFinished(
+ std::unique_ptr<FetchStatusCallback> callback,
+ bool interactive_request,
+ Status status,
+ RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories) {
+ if (!ready()) {
+ // TODO(tschumann): What happens if this was a user-triggered, interactive
+ // request? Is the UI waiting indefinitely now?
+ return;
+ }
+
+ // Record the fetch time of a successfull background fetch.
+ if (!interactive_request && status.IsSuccess()) {
+ pref_service_->SetInt64(prefs::kLastSuccessfulBackgroundFetchTime,
+ clock_->Now().ToInternalValue());
+ }
+
+ // Mark all categories as not provided by the server in the latest fetch. The
+ // ones we got will be marked again below.
+ for (auto& item : category_contents_) {
+ CategoryContent* content = &item.second;
+ content->included_in_last_server_response = false;
+ }
+
+ // Clear up expired dismissed snippets before we use them to filter new ones.
+ ClearExpiredDismissedSnippets();
+
+ // If snippets were fetched successfully, update our |category_contents_| from
+ // each category provided by the server.
+ if (fetched_categories) {
+ // TODO(treib): Reorder |category_contents_| to match the order we received
+ // from the server. crbug.com/653816
+ for (RemoteSuggestionsFetcher::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_) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "NewTabPage.Snippets.NumArticlesFetched",
+ std::min(fetched_category.snippets.size(),
+ static_cast<size_t>(kMaxSnippetCount + 1)));
+ }
+ category_ranker_->AppendCategoryIfNecessary(fetched_category.category);
+ CategoryContent* content =
+ UpdateCategoryInfo(fetched_category.category, fetched_category.info);
+ content->included_in_last_server_response = true;
+ SanitizeReceivedSnippets(content->dismissed, &fetched_category.snippets);
+ IntegrateSnippets(content, std::move(fetched_category.snippets));
+ }
+ }
+
+ // TODO(tschumann): The snippets fetcher needs to signal errors so that we
+ // know why we received no data. If an error occured, none of the following
+ // should take place.
+
+ // We might have gotten new categories (or updated the titles of existing
+ // ones), so update the pref.
+ StoreCategoriesToPrefs();
+
+ for (const auto& item : category_contents_) {
+ Category category = item.first;
+ UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
+ // TODO(sfiera): notify only when a category changed above.
+ NotifyNewSuggestions(category, item.second);
+ }
+
+ // TODO(sfiera): equivalent metrics for non-articles.
+ auto content_it = category_contents_.find(articles_category_);
+ DCHECK(content_it != category_contents_.end());
+ const CategoryContent& content = content_it->second;
+ UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles",
+ content.snippets.size());
+ if (content.snippets.empty() && !content.dismissed.empty()) {
+ UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded",
+ content.dismissed.size());
+ }
+
+ if (callback) {
+ callback->Run(status);
+ }
+}
+
+void RemoteSuggestionsProviderImpl::ArchiveSnippets(
+ CategoryContent* content,
+ NTPSnippet::PtrVector* to_archive) {
+ // Archive previous snippets - move them at the beginning of the list.
+ content->archived.insert(content->archived.begin(),
+ std::make_move_iterator(to_archive->begin()),
+ std::make_move_iterator(to_archive->end()));
+ to_archive->clear();
+
+ // If there are more archived snippets than we want to keep, delete the
+ // oldest ones by their fetch time (which are always in the back).
+ if (content->archived.size() > kMaxArchivedSnippetCount) {
+ NTPSnippet::PtrVector to_delete(
+ std::make_move_iterator(content->archived.begin() +
+ kMaxArchivedSnippetCount),
+ std::make_move_iterator(content->archived.end()));
+ content->archived.resize(kMaxArchivedSnippetCount);
+ database_->DeleteImages(GetSnippetIDVector(to_delete));
+ }
+}
+
+void RemoteSuggestionsProviderImpl::SanitizeReceivedSnippets(
+ const NTPSnippet::PtrVector& dismissed,
+ NTPSnippet::PtrVector* snippets) {
+ DCHECK(ready());
+ EraseMatchingSnippets(snippets, dismissed);
+ RemoveIncompleteSnippets(snippets);
+}
+
+void RemoteSuggestionsProviderImpl::IntegrateSnippets(
+ CategoryContent* content,
+ NTPSnippet::PtrVector new_snippets) {
+ DCHECK(ready());
+
+ // Do not touch the current set of snippets if the newly fetched one is empty.
+ // TODO(tschumann): This should go. If we get empty results we should update
+ // accordingly and remove the old one (only of course if this was not received
+ // through a fetch-more).
+ if (new_snippets.empty()) {
+ return;
+ }
+
+ // It's entirely possible that the newly fetched snippets contain articles
+ // that have been present before.
+ // We need to make sure to only delete and archive snippets that don't
+ // appear with the same ID in the new suggestions (it's fine for additional
+ // IDs though).
+ EraseByPrimaryID(&content->snippets, *GetSnippetIDVector(new_snippets));
+ // Do not delete the thumbnail images as they are still handy on open NTPs.
+ database_->DeleteSnippets(GetSnippetIDVector(content->snippets));
+ // Note, that ArchiveSnippets will clear |content->snippets|.
+ ArchiveSnippets(content, &content->snippets);
+
+ database_->SaveSnippets(new_snippets);
+
+ content->snippets = std::move(new_snippets);
+}
+
+void RemoteSuggestionsProviderImpl::DismissSuggestionFromCategoryContent(
+ CategoryContent* content,
+ const std::string& id_within_category) {
+ auto it = std::find_if(
+ content->snippets.begin(), content->snippets.end(),
+ [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) {
+ return snippet->id() == id_within_category;
+ });
+ if (it == content->snippets.end()) {
+ return;
+ }
+
+ (*it)->set_dismissed(true);
+
+ database_->SaveSnippet(**it);
+
+ content->dismissed.push_back(std::move(*it));
+ content->snippets.erase(it);
+}
+
+void RemoteSuggestionsProviderImpl::ClearExpiredDismissedSnippets() {
+ std::vector<Category> categories_to_erase;
+
+ const base::Time now = base::Time::Now();
+
+ for (auto& item : category_contents_) {
+ Category category = item.first;
+ CategoryContent* content = &item.second;
+
+ NTPSnippet::PtrVector to_delete;
+ // Move expired dismissed snippets over into |to_delete|.
+ for (std::unique_ptr<NTPSnippet>& snippet : content->dismissed) {
+ if (snippet->expiry_date() <= now) {
+ to_delete.emplace_back(std::move(snippet));
+ }
+ }
+ RemoveNullPointers(&content->dismissed);
+
+ // Delete the images.
+ database_->DeleteImages(GetSnippetIDVector(to_delete));
+ // Delete the removed article suggestions from the DB.
+ database_->DeleteSnippets(GetSnippetIDVector(to_delete));
+
+ if (content->snippets.empty() && content->dismissed.empty() &&
+ category != articles_category_ &&
+ !content->included_in_last_server_response) {
+ categories_to_erase.push_back(category);
+ }
+ }
+
+ for (Category category : categories_to_erase) {
+ UpdateCategoryStatus(category, CategoryStatus::NOT_PROVIDED);
+ category_contents_.erase(category);
+ }
+
+ StoreCategoriesToPrefs();
+}
+
+void RemoteSuggestionsProviderImpl::ClearOrphanedImages() {
+ auto alive_snippets = base::MakeUnique<std::set<std::string>>();
+ for (const auto& entry : category_contents_) {
+ const CategoryContent& content = entry.second;
+ for (const auto& snippet_ptr : content.snippets) {
+ alive_snippets->insert(snippet_ptr->id());
+ }
+ for (const auto& snippet_ptr : content.dismissed) {
+ alive_snippets->insert(snippet_ptr->id());
+ }
+ }
+ database_->GarbageCollectImages(std::move(alive_snippets));
+}
+
+void RemoteSuggestionsProviderImpl::NukeAllSnippets() {
+ for (const auto& item : category_contents_) {
+ Category category = item.first;
+ const CategoryContent& content = item.second;
+
+ ClearCachedSuggestions(category);
+ // Update listeners about the new (empty) state.
+ if (IsCategoryStatusAvailable(content.status)) {
+ NotifyNewSuggestions(category, content);
+ }
+ // TODO(tschumann): We should not call debug code from production code.
+ ClearDismissedSuggestionsForDebugging(category);
+ }
+
+ StoreCategoriesToPrefs();
+}
+
+void RemoteSuggestionsProviderImpl::FetchSuggestionImage(
+ const ContentSuggestion::ID& suggestion_id,
+ const ImageFetchedCallback& callback) {
+ if (!base::ContainsKey(category_contents_, suggestion_id.category())) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, gfx::Image()));
+ return;
+ }
+ GURL image_url = FindSnippetImageUrl(suggestion_id);
+ if (image_url.is_empty()) {
+ // As we don't know the corresponding snippet anymore, we don't expect to
+ // find it in the database (and also can't fetch it remotely). Cut the
+ // lookup short and return directly.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, gfx::Image()));
+ return;
+ }
+ image_fetcher_.FetchSuggestionImage(suggestion_id, image_url, callback);
+}
+
+void RemoteSuggestionsProviderImpl::EnterStateReady() {
+ if (nuke_when_initialized_) {
+ NukeAllSnippets();
+ nuke_when_initialized_ = false;
+ }
+
+ auto article_category_it = category_contents_.find(articles_category_);
+ DCHECK(article_category_it != category_contents_.end());
+ if (article_category_it->second.snippets.empty() || fetch_when_ready_) {
+ // TODO(jkrcal): Fetching snippets automatically upon creation of this
+ // lazily created service can cause troubles, e.g. in unit tests where
+ // network I/O is not allowed.
+ // Either add a DCHECK here that we actually are allowed to do network I/O
+ // or change the logic so that some explicit call is always needed for the
+ // network request.
+ FetchSnippets(fetch_when_ready_interactive_,
+ std::move(fetch_when_ready_callback_));
+ fetch_when_ready_ = false;
+ }
+
+ for (const auto& item : category_contents_) {
+ Category category = item.first;
+ const CategoryContent& content = item.second;
+ // FetchSnippets has set the status to |AVAILABLE_LOADING| if relevant,
+ // otherwise we transition to |AVAILABLE| here.
+ if (content.status != CategoryStatus::AVAILABLE_LOADING) {
+ UpdateCategoryStatus(category, CategoryStatus::AVAILABLE);
+ }
+ }
+}
+
+void RemoteSuggestionsProviderImpl::EnterStateDisabled() {
+ NukeAllSnippets();
+}
+
+void RemoteSuggestionsProviderImpl::EnterStateError() {
+ status_service_.reset();
+}
+
+void RemoteSuggestionsProviderImpl::FinishInitialization() {
+ if (nuke_when_initialized_) {
+ // We nuke here in addition to EnterStateReady, so that it happens even if
+ // we enter the DISABLED state below.
+ NukeAllSnippets();
+ nuke_when_initialized_ = false;
+ }
+
+ // Note: Initializing the status service will run the callback right away with
+ // the current state.
+ status_service_->Init(base::Bind(
+ &RemoteSuggestionsProviderImpl::OnStatusChanged, base::Unretained(this)));
+
+ // Always notify here even if we got nothing from the database, because we
+ // don't know how long the fetch will take or if it will even complete.
+ for (const auto& item : category_contents_) {
+ Category category = item.first;
+ const CategoryContent& content = item.second;
+ // Note: We might be in a non-available status here, e.g. DISABLED due to
+ // enterprise policy.
+ if (IsCategoryStatusAvailable(content.status)) {
+ NotifyNewSuggestions(category, content);
+ }
+ }
+}
+
+void RemoteSuggestionsProviderImpl::OnStatusChanged(
+ RemoteSuggestionsStatus old_status,
+ RemoteSuggestionsStatus new_status) {
+ switch (new_status) {
+ case RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN:
+ if (old_status == RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT) {
+ DCHECK(state_ == State::READY);
+ // Clear nonpersonalized suggestions.
+ NukeAllSnippets();
+ // Fetch personalized ones.
+ // TODO(jkrcal): Loop in SchedulingRemoteSuggestionsProvider somehow.
+ FetchSnippets(/*interactive_request=*/true,
+ /*callback=*/nullptr);
+ } else {
+ // Do not change the status. That will be done in EnterStateReady().
+ EnterState(State::READY);
+ }
+ break;
+
+ case RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT:
+ if (old_status == RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN) {
+ DCHECK(state_ == State::READY);
+ // Clear personalized suggestions.
+ NukeAllSnippets();
+ // Fetch nonpersonalized ones.
+ // TODO(jkrcal): Loop in SchedulingRemoteSuggestionsProvider somehow.
+ FetchSnippets(/*interactive_request=*/true,
+ /*callback=*/nullptr);
+ } else {
+ // Do not change the status. That will be done in EnterStateReady().
+ EnterState(State::READY);
+ }
+ break;
+
+ case RemoteSuggestionsStatus::EXPLICITLY_DISABLED:
+ EnterState(State::DISABLED);
+ UpdateAllCategoryStatus(CategoryStatus::CATEGORY_EXPLICITLY_DISABLED);
+ break;
+
+ case RemoteSuggestionsStatus::SIGNED_OUT_AND_DISABLED:
+ EnterState(State::DISABLED);
+ UpdateAllCategoryStatus(CategoryStatus::SIGNED_OUT);
+ break;
+ }
+}
+
+void RemoteSuggestionsProviderImpl::EnterState(State state) {
+ if (state == state_) {
+ return;
+ }
+
+ UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.EnteredState",
+ static_cast<int>(state),
+ static_cast<int>(State::COUNT));
+
+ switch (state) {
+ case State::NOT_INITED:
+ // Initial state, it should not be possible to get back there.
+ NOTREACHED();
+ break;
+
+ case State::READY:
+ DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED);
+
+ DVLOG(1) << "Entering state: READY";
+ state_ = State::READY;
+ EnterStateReady();
+ break;
+
+ case State::DISABLED:
+ DCHECK(state_ == State::NOT_INITED || state_ == State::READY);
+
+ DVLOG(1) << "Entering state: DISABLED";
+ state_ = State::DISABLED;
+ EnterStateDisabled();
+ break;
+
+ case State::ERROR_OCCURRED:
+ DVLOG(1) << "Entering state: ERROR_OCCURRED";
+ state_ = State::ERROR_OCCURRED;
+ EnterStateError();
+ break;
+
+ case State::COUNT:
+ NOTREACHED();
+ break;
+ }
+
+ NotifyStateChanged();
+}
+
+void RemoteSuggestionsProviderImpl::NotifyStateChanged() {
+ if (!provider_status_callback_) {
+ return;
+ }
+
+ switch (state_) {
+ case State::NOT_INITED:
+ // Initial state, not sure yet whether active or not.
+ break;
+ case State::READY:
+ provider_status_callback_->Run(ProviderStatus::ACTIVE);
+ break;
+ case State::DISABLED:
+ provider_status_callback_->Run(ProviderStatus::INACTIVE);
+ break;
+ case State::ERROR_OCCURRED:
+ provider_status_callback_->Run(ProviderStatus::INACTIVE);
+ break;
+ case State::COUNT:
+ NOTREACHED();
+ break;
+ }
+}
+
+void RemoteSuggestionsProviderImpl::NotifyNewSuggestions(
+ Category category,
+ const CategoryContent& content) {
+ DCHECK(IsCategoryStatusAvailable(content.status));
+
+ std::vector<ContentSuggestion> result =
+ ConvertToContentSuggestions(category, content.snippets);
+
+ DVLOG(1) << "NotifyNewSuggestions(): " << result.size()
+ << " items in category " << category;
+ observer()->OnNewSuggestions(this, category, std::move(result));
+}
+
+void RemoteSuggestionsProviderImpl::UpdateCategoryStatus(
+ Category category,
+ CategoryStatus status) {
+ auto content_it = category_contents_.find(category);
+ DCHECK(content_it != category_contents_.end());
+ CategoryContent& content = content_it->second;
+
+ if (status == content.status) {
+ return;
+ }
+
+ DVLOG(1) << "UpdateCategoryStatus(): " << category.id() << ": "
+ << static_cast<int>(content.status) << " -> "
+ << static_cast<int>(status);
+ content.status = status;
+ observer()->OnCategoryStatusChanged(this, category, content.status);
+}
+
+void RemoteSuggestionsProviderImpl::UpdateAllCategoryStatus(
+ CategoryStatus status) {
+ for (const auto& category : category_contents_) {
+ UpdateCategoryStatus(category.first, status);
+ }
+}
+
+namespace {
+
+template <typename T>
+typename T::const_iterator FindSnippetInContainer(
+ const T& container,
+ const std::string& id_within_category) {
+ return std::find_if(
+ container.begin(), container.end(),
+ [&id_within_category](const std::unique_ptr<NTPSnippet>& snippet) {
+ return snippet->id() == id_within_category;
+ });
+}
+
+} // namespace
+
+const NTPSnippet* RemoteSuggestionsProviderImpl::CategoryContent::FindSnippet(
+ const std::string& id_within_category) const {
+ // Search for the snippet in current and archived snippets.
+ auto it = FindSnippetInContainer(snippets, id_within_category);
+ if (it != snippets.end()) {
+ return it->get();
+ }
+ auto archived_it = FindSnippetInContainer(archived, id_within_category);
+ if (archived_it != archived.end()) {
+ return archived_it->get();
+ }
+ auto dismissed_it = FindSnippetInContainer(dismissed, id_within_category);
+ if (dismissed_it != dismissed.end()) {
+ return dismissed_it->get();
+ }
+ return nullptr;
+}
+
+RemoteSuggestionsProviderImpl::CategoryContent*
+RemoteSuggestionsProviderImpl::UpdateCategoryInfo(Category category,
+ const CategoryInfo& info) {
+ auto content_it = category_contents_.find(category);
+ if (content_it == category_contents_.end()) {
+ content_it = category_contents_
+ .insert(std::make_pair(category, CategoryContent(info)))
+ .first;
+ } else {
+ content_it->second.info = info;
+ }
+ return &content_it->second;
+}
+
+void RemoteSuggestionsProviderImpl::RestoreCategoriesFromPrefs() {
+ // This must only be called at startup, before there are any categories.
+ DCHECK(category_contents_.empty());
+
+ const base::ListValue* list =
+ pref_service_->GetList(prefs::kRemoteSuggestionCategories);
+ for (const std::unique_ptr<base::Value>& entry : *list) {
+ const base::DictionaryValue* dict = nullptr;
+ if (!entry->GetAsDictionary(&dict)) {
+ DLOG(WARNING) << "Invalid category pref value: " << *entry;
+ continue;
+ }
+ int id = 0;
+ if (!dict->GetInteger(kCategoryContentId, &id)) {
+ DLOG(WARNING) << "Invalid category pref value, missing '"
+ << kCategoryContentId << "': " << *entry;
+ continue;
+ }
+ base::string16 title;
+ if (!dict->GetString(kCategoryContentTitle, &title)) {
+ DLOG(WARNING) << "Invalid category pref value, missing '"
+ << kCategoryContentTitle << "': " << *entry;
+ continue;
+ }
+ bool included_in_last_server_response = false;
+ if (!dict->GetBoolean(kCategoryContentProvidedByServer,
+ &included_in_last_server_response)) {
+ DLOG(WARNING) << "Invalid category pref value, missing '"
+ << kCategoryContentProvidedByServer << "': " << *entry;
+ continue;
+ }
+ bool allow_fetching_more_results = false;
+ // This wasn't always around, so it's okay if it's missing.
+ dict->GetBoolean(kCategoryContentAllowFetchingMore,
+ &allow_fetching_more_results);
+
+ Category category = Category::FromIDValue(id);
+ // The ranker may not persist the order of remote categories.
+ category_ranker_->AppendCategoryIfNecessary(category);
+ // TODO(tschumann): The following has a bad smell that category
+ // serialization / deserialization should not be done inside this
+ // class. We should move that into a central place that also knows how to
+ // parse data we received from remote backends.
+ CategoryInfo info =
+ category == articles_category_
+ ? BuildArticleCategoryInfo(title)
+ : BuildRemoteCategoryInfo(title, allow_fetching_more_results);
+ CategoryContent* content = UpdateCategoryInfo(category, info);
+ content->included_in_last_server_response =
+ included_in_last_server_response;
+ }
+}
+
+void RemoteSuggestionsProviderImpl::StoreCategoriesToPrefs() {
+ // Collect all the CategoryContents.
+ std::vector<std::pair<Category, const CategoryContent*>> to_store;
+ for (const auto& entry : category_contents_) {
+ to_store.emplace_back(entry.first, &entry.second);
+ }
+ // The ranker may not persist the order, thus, it is stored by the provider.
+ std::sort(to_store.begin(), to_store.end(),
+ [this](const std::pair<Category, const CategoryContent*>& left,
+ const std::pair<Category, const CategoryContent*>& right) {
+ return category_ranker_->Compare(left.first, right.first);
+ });
+ // Convert the relevant info into a base::ListValue for storage.
+ base::ListValue list;
+ for (const auto& entry : to_store) {
+ const Category& category = entry.first;
+ const CategoryContent& content = *entry.second;
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+ dict->SetInteger(kCategoryContentId, category.id());
+ // TODO(tschumann): Persist other properties of the CategoryInfo.
+ dict->SetString(kCategoryContentTitle, content.info.title());
+ dict->SetBoolean(kCategoryContentProvidedByServer,
+ content.included_in_last_server_response);
+ dict->SetBoolean(kCategoryContentAllowFetchingMore,
+ content.info.has_more_action());
+ list.Append(std::move(dict));
+ }
+ // Finally, store the result in the pref service.
+ pref_service_->Set(prefs::kRemoteSuggestionCategories, list);
+}
+
+RemoteSuggestionsProviderImpl::CategoryContent::CategoryContent(
+ const CategoryInfo& info)
+ : info(info) {}
+
+RemoteSuggestionsProviderImpl::CategoryContent::CategoryContent(
+ CategoryContent&&) = default;
+
+RemoteSuggestionsProviderImpl::CategoryContent::~CategoryContent() = default;
+
+RemoteSuggestionsProviderImpl::CategoryContent&
+RemoteSuggestionsProviderImpl::CategoryContent::operator=(CategoryContent&&) =
+ default;
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h
new file mode 100644
index 00000000000..53a75646a1a
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl.h
@@ -0,0 +1,455 @@
+// 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_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_IMPL_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_IMPL_H_
+
+#include <cstddef>
+#include <deque>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "components/image_fetcher/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/ntp_snippet.h"
+#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h"
+#include "components/ntp_snippets/remote/remote_suggestions_provider.h"
+#include "components/ntp_snippets/remote/remote_suggestions_status_service.h"
+#include "components/ntp_snippets/remote/request_params.h"
+#include "components/ntp_snippets/remote/request_throttler.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace gfx {
+class Image;
+} // namespace gfx
+
+namespace image_fetcher {
+class ImageDecoder;
+class ImageFetcher;
+} // namespace image_fetcher
+
+namespace ntp_snippets {
+
+class CategoryRanker;
+class RemoteSuggestionsDatabase;
+
+// 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,
+ std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
+ 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);
+ void OnSnippetImageFetchedFromDatabase(
+ const ImageFetchedCallback& callback,
+ const ContentSuggestion::ID& suggestion_id,
+ const GURL& image_url,
+ // SnippetImageCallback requires nonconst reference
+ std::string data);
+ void OnSnippetImageDecodedFromDatabase(
+ const ImageFetchedCallback& callback,
+ const ContentSuggestion::ID& suggestion_id,
+ const GURL& url,
+ const gfx::Image& image);
+ void FetchSnippetImageFromNetwork(const ContentSuggestion::ID& suggestion_id,
+ const GURL& url,
+ const ImageFetchedCallback& callback);
+
+ std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher_;
+ std::unique_ptr<image_fetcher::ImageDecoder> image_decoder_;
+ 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
+// unsafe to derive from it.
+// TODO(treib): Introduce two-phase initialization and make the class not final?
+class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider {
+ public:
+ // |application_language_code| should be a ISO 639-1 compliant string, e.g.
+ // 'en' or 'en-US'. Note that this code should only specify the language, not
+ // the locale, so 'en_US' (English language with US locale) and 'en-GB_US'
+ // (British English person in the US) are not language codes.
+ RemoteSuggestionsProviderImpl(
+ Observer* observer,
+ PrefService* pref_service,
+ const std::string& application_language_code,
+ CategoryRanker* category_ranker,
+ std::unique_ptr<RemoteSuggestionsFetcher> suggestions_fetcher,
+ std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
+ std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
+ std::unique_ptr<RemoteSuggestionsDatabase> database,
+ std::unique_ptr<RemoteSuggestionsStatusService> status_service);
+
+ ~RemoteSuggestionsProviderImpl() override;
+
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ // Returns whether the service is ready. While this is false, the list of
+ // snippets will be empty, and all modifications to it (fetch, dismiss, etc)
+ // will be ignored.
+ bool ready() const { return state_ == State::READY; }
+
+ // Returns whether the service is successfully initialized. While this is
+ // false, some calls may trigger DCHECKs.
+ bool initialized() const { return ready() || state_ == State::DISABLED; }
+
+ // RemoteSuggestionsProvider implementation.
+ void SetProviderStatusCallback(
+ std::unique_ptr<ProviderStatusCallback> callback) override;
+ void RefetchInTheBackground(
+ std::unique_ptr<FetchStatusCallback> callback) override;
+
+ // TODO(fhorschig): Remove this getter when there is an interface for the
+ // fetcher that allows better mocks.
+ const RemoteSuggestionsFetcher* suggestions_fetcher_for_debugging()
+ const 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 ReloadSuggestions() override;
+ void ClearHistory(
+ base::Time begin,
+ base::Time end,
+ const base::Callback<bool(const GURL& url)>& filter) override;
+ void ClearCachedSuggestions(Category category) override;
+ void OnSignInStateChanged() override;
+ void GetDismissedSuggestionsForDebugging(
+ Category category,
+ const DismissedSuggestionsCallback& callback) override;
+ void ClearDismissedSuggestionsForDebugging(Category category) override;
+
+ // Returns the maximum number of snippets that will be shown at once.
+ static int GetMaxSnippetCountForTesting();
+
+ // Available snippets, only for unit tests.
+ // TODO(treib): Get rid of this. Tests should use a fake observer instead.
+ const NTPSnippet::PtrVector& GetSnippetsForTesting(Category category) const {
+ return category_contents_.find(category)->second.snippets;
+ }
+
+ // Dismissed snippets, only for unit tests.
+ const NTPSnippet::PtrVector& GetDismissedSnippetsForTesting(
+ Category category) const {
+ return category_contents_.find(category)->second.dismissed;
+ }
+
+ // Overrides internal clock for testing purposes.
+ void SetClockForTesting(std::unique_ptr<base::Clock> clock) {
+ clock_ = std::move(clock);
+ }
+
+ // TODO(tschumann): remove this method as soon as we inject the fetcher into
+ // the constructor.
+ CachedImageFetcher& GetImageFetcherForTesting() { return image_fetcher_; }
+
+ private:
+ friend class RemoteSuggestionsProviderImplTest;
+
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
+ CallsProviderStatusCallbackWhenReady);
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
+ CallsProviderStatusCallbackOnError);
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
+ CallsProviderStatusCallbackWhenDisabled);
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
+ ShouldNotCrashWhenCallingEmptyCallback);
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
+ DontNotifyIfNotAvailable);
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
+ RemoveExpiredDismissedContent);
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest, StatusChanges);
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
+ SuggestionsFetchedOnSignInAndSignOut);
+
+ // Possible state transitions:
+ // NOT_INITED --------+
+ // / \ |
+ // v v |
+ // READY <--> DISABLED |
+ // \ / |
+ // v v |
+ // ERROR_OCCURRED <-----+
+ enum class State {
+ // The service has just been created. Can change to states:
+ // - DISABLED: After the database is done loading,
+ // GetStateForDependenciesStatus can identify the next state to
+ // be DISABLED.
+ // - READY: if GetStateForDependenciesStatus returns it, after the database
+ // is done loading.
+ // - ERROR_OCCURRED: when an unrecoverable error occurred.
+ NOT_INITED,
+
+ // The service registered observers, timers, etc. and is ready to answer to
+ // queries, fetch snippets... Can change to states:
+ // - DISABLED: when the global Chrome state changes, for example after
+ // |OnStateChanged| is called and sync is disabled.
+ // - ERROR_OCCURRED: when an unrecoverable error occurred.
+ READY,
+
+ // The service is disabled and unregistered the related resources.
+ // Can change to states:
+ // - READY: when the global Chrome state changes, for example after
+ // |OnStateChanged| is called and sync is enabled.
+ // - ERROR_OCCURRED: when an unrecoverable error occurred.
+ DISABLED,
+
+ // The service or one of its dependencies encountered an unrecoverable error
+ // and the service can't be used anymore.
+ ERROR_OCCURRED,
+
+ COUNT
+ };
+
+ struct CategoryContent {
+ // The current status of the category.
+ CategoryStatus status = CategoryStatus::INITIALIZING;
+
+ // The additional information about a category.
+ CategoryInfo info;
+
+ // True iff the server returned results in this category in the last fetch.
+ // We never remove categories that the server still provides, but if the
+ // server stops providing a category, we won't yet report it as NOT_PROVIDED
+ // while we still have non-expired snippets in it.
+ bool included_in_last_server_response = true;
+
+ // All currently active suggestions (excl. the dismissed ones).
+ NTPSnippet::PtrVector snippets;
+
+ // All previous suggestions that we keep around in memory because they can
+ // be on some open NTP. We do not persist this list so that on a new start
+ // of Chrome, this is empty.
+ // |archived| is a FIFO buffer with a maximum length.
+ std::deque<std::unique_ptr<NTPSnippet>> archived;
+
+ // Suggestions that the user dismissed. We keep these around until they
+ // expire so we won't re-add them to |snippets| on the next fetch.
+ NTPSnippet::PtrVector dismissed;
+
+ // Returns a non-dismissed snippet with the given |id_within_category|, or
+ // null if none exist.
+ const NTPSnippet* FindSnippet(const std::string& id_within_category) const;
+
+ explicit CategoryContent(const CategoryInfo& info);
+ CategoryContent(CategoryContent&&);
+ ~CategoryContent();
+ CategoryContent& operator=(CategoryContent&&);
+ };
+
+ // Fetches snippets from the server and replaces old snippets by the new ones.
+ // Requests can be marked more important by setting |interactive_request| to
+ // true (such request might circumvent the daily quota for requests, etc.)
+ // Useful for requests triggered by the user. After the fetch finished, the
+ // provided |callback| will be triggered with the status of the fetch.
+ void FetchSnippets(bool interactive_request,
+ std::unique_ptr<FetchStatusCallback> callback);
+
+ // Returns the URL of the image of a snippet if it is among the current or
+ // among the archived snippets in the matching category. Returns an empty URL
+ // otherwise.
+ GURL FindSnippetImageUrl(const ContentSuggestion::ID& suggestion_id) const;
+
+ // Callbacks for the RemoteSuggestionsDatabase.
+ void OnDatabaseLoaded(NTPSnippet::PtrVector snippets);
+ void OnDatabaseError();
+
+ // Callback for fetch-more requests with the RemoteSuggestionsFetcher.
+ void OnFetchMoreFinished(
+ const FetchDoneCallback& fetching_callback,
+ Status status,
+ RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories);
+
+ // Callback for regular fetch requests with the RemoteSuggestionsFetcher.
+ void OnFetchFinished(
+ std::unique_ptr<FetchStatusCallback> callback,
+ bool interactive_request,
+ Status status,
+ RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories);
+
+ // Moves all snippets from |to_archive| into the archive of the |content|.
+ // Clears |to_archive|. As the archive is a FIFO buffer of limited size, this
+ // function will also delete images from the database in case the associated
+ // snippet gets evicted from the archive.
+ void ArchiveSnippets(CategoryContent* content,
+ NTPSnippet::PtrVector* to_archive);
+
+ // Sanitizes newly fetched snippets -- e.g. adding missing dates and filtering
+ // out incomplete results or dismissed snippets (indicated by |dismissed|).
+ void SanitizeReceivedSnippets(const NTPSnippet::PtrVector& dismissed,
+ NTPSnippet::PtrVector* snippets);
+
+ // Adds newly available suggestions to |content|.
+ void IntegrateSnippets(CategoryContent* content,
+ NTPSnippet::PtrVector new_snippets);
+
+ // Dismisses a snippet within a given category content.
+ // Note that this modifies the snippet datastructures of |content|
+ // invalidating iterators.
+ void DismissSuggestionFromCategoryContent(
+ CategoryContent* content,
+ const std::string& id_within_category);
+
+ // Removes expired dismissed snippets from the service and the database.
+ void ClearExpiredDismissedSnippets();
+
+ // Removes images from the DB that are not referenced from any known snippet.
+ // Needs to iterate the whole snippet database -- so do it often enough to
+ // keep it small but not too often as it still iterates over the file system.
+ void ClearOrphanedImages();
+
+ // Clears all stored snippets and updates the observer.
+ void NukeAllSnippets();
+
+ // Completes the initialization phase of the service, registering the last
+ // observers. This is done after construction, once the database is loaded.
+ void FinishInitialization();
+
+ // Triggers a state transition depending on the provided status. This method
+ // is called when a change is detected by |status_service_|.
+ void OnStatusChanged(RemoteSuggestionsStatus old_status,
+ RemoteSuggestionsStatus new_status);
+
+ // Verifies state transitions (see |State|'s documentation) and applies them.
+ // Also updates the provider status. Does nothing except updating the provider
+ // status if called with the current state.
+ void EnterState(State state);
+
+ // Notifies the state change to ProviderStatusCallback specified by
+ // SetProviderStatusCallback().
+ void NotifyStateChanged();
+
+ // Enables the service. Do not call directly, use |EnterState| instead.
+ void EnterStateReady();
+
+ // Disables the service. Do not call directly, use |EnterState| instead.
+ void EnterStateDisabled();
+
+ // Disables the service permanently because an unrecoverable error occurred.
+ // Do not call directly, use |EnterState| instead.
+ void EnterStateError();
+
+ // Converts the cached snippets in the given |category| to content suggestions
+ // and notifies the observer.
+ void NotifyNewSuggestions(Category category, const CategoryContent& content);
+
+ // Updates the internal status for |category| to |category_status_| and
+ // notifies the content suggestions observer if it changed.
+ void UpdateCategoryStatus(Category category, CategoryStatus status);
+ // Calls UpdateCategoryStatus() for all provided categories.
+ void UpdateAllCategoryStatus(CategoryStatus status);
+
+ // Updates the category info for |category|. If a corresponding
+ // CategoryContent object does not exist, it will be created.
+ // Returns the existing or newly created object.
+ CategoryContent* UpdateCategoryInfo(Category category,
+ const CategoryInfo& info);
+
+ void RestoreCategoriesFromPrefs();
+ void StoreCategoriesToPrefs();
+
+ RequestParams BuildFetchParams() const;
+
+ void MarkEmptyCategoriesAsLoading();
+
+ State state_;
+
+ PrefService* pref_service_;
+
+ const Category articles_category_;
+
+ std::map<Category, CategoryContent, Category::CompareByID> category_contents_;
+
+ // The ISO 639-1 code of the language used by the application.
+ const std::string application_language_code_;
+
+ // Ranker that orders the categories. Not owned.
+ CategoryRanker* category_ranker_;
+
+ // The suggestions fetcher.
+ std::unique_ptr<RemoteSuggestionsFetcher> suggestions_fetcher_;
+
+ // The database for persisting suggestions.
+ std::unique_ptr<RemoteSuggestionsDatabase> database_;
+ base::TimeTicks database_load_start_;
+
+ // The image fetcher.
+ CachedImageFetcher image_fetcher_;
+
+ // The service that provides events and data about the signin and sync state.
+ std::unique_ptr<RemoteSuggestionsStatusService> status_service_;
+
+ // Set to true if FetchSnippets is called while the service isn't ready.
+ // The fetch will be executed once the service enters the READY state.
+ // TODO(jkrcal): create a struct and have here just one base::Optional<>?
+ bool fetch_when_ready_;
+
+ // The parameters for the fetch to perform later.
+ bool fetch_when_ready_interactive_;
+ std::unique_ptr<FetchStatusCallback> fetch_when_ready_callback_;
+
+ std::unique_ptr<ProviderStatusCallback> provider_status_callback_;
+
+ // Set to true if NukeAllSnippets is called while the service isn't ready.
+ // The nuke will be executed once the service finishes initialization or
+ // enters the READY state.
+ bool nuke_when_initialized_;
+
+ // A clock for getting the time. This allows to inject a clock in tests.
+ std::unique_ptr<base::Clock> clock_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProviderImpl);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_IMPL_H_
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
index a7021243716..9dc9b61bc83 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/ntp_snippets/remote/remote_suggestions_provider.h"
+#include "components/ntp_snippets/remote/remote_suggestions_provider_impl.h"
#include <memory>
#include <utility>
@@ -20,25 +20,30 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/image_fetcher/image_decoder.h"
#include "components/image_fetcher/image_fetcher.h"
#include "components/image_fetcher/image_fetcher_delegate.h"
-#include "components/ntp_snippets/category_factory.h"
+#include "components/ntp_snippets/category.h"
#include "components/ntp_snippets/category_info.h"
+#include "components/ntp_snippets/category_rankers/category_ranker.h"
+#include "components/ntp_snippets/category_rankers/constant_category_ranker.h"
+#include "components/ntp_snippets/category_rankers/mock_category_ranker.h"
+#include "components/ntp_snippets/fake_content_suggestions_provider_observer.h"
#include "components/ntp_snippets/ntp_snippets_constants.h"
#include "components/ntp_snippets/pref_names.h"
#include "components/ntp_snippets/remote/ntp_snippet.h"
-#include "components/ntp_snippets/remote/ntp_snippets_fetcher.h"
-#include "components/ntp_snippets/remote/ntp_snippets_scheduler.h"
-#include "components/ntp_snippets/remote/ntp_snippets_test_utils.h"
+#include "components/ntp_snippets/remote/persistent_scheduler.h"
#include "components/ntp_snippets/remote/remote_suggestions_database.h"
+#include "components/ntp_snippets/remote/remote_suggestions_fetcher.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/variations_associated_data.h"
+#include "components/variations/variations_params_manager.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -48,6 +53,7 @@
using image_fetcher::ImageFetcher;
using image_fetcher::ImageFetcherDelegate;
+using testing::_;
using testing::ElementsAre;
using testing::Eq;
using testing::InSequence;
@@ -60,8 +66,8 @@ using testing::Not;
using testing::SaveArg;
using testing::SizeIs;
using testing::StartsWith;
+using testing::StrictMock;
using testing::WithArgs;
-using testing::_;
namespace ntp_snippets {
@@ -80,7 +86,7 @@ MATCHER_P(IsCategory, id, "") {
}
MATCHER_P(HasCode, code, "") {
- return arg.status == code;
+ return arg.code == code;
}
const base::Time::Exploded kDefaultCreationTime = {2015, 11, 4, 25, 13, 46, 45};
@@ -115,44 +121,66 @@ base::Time GetDefaultExpirationTime() {
return base::Time::Now() + base::TimeDelta::FromHours(1);
}
-std::string GetTestJson(const std::vector<std::string>& snippets,
- const std::string& category_title) {
+std::string GetCategoryJson(const std::vector<std::string>& snippets,
+ int remote_category_id,
+ const std::string& category_title) {
return base::StringPrintf(
- "{\n"
- " \"categories\": [{\n"
- " \"id\": 1,\n"
+ " {\n"
+ " \"id\": %d,\n"
" \"localizedTitle\": \"%s\",\n"
" \"suggestions\": [%s]\n"
- " }]\n"
- "}\n",
- category_title.c_str(), base::JoinString(snippets, ", ").c_str());
+ " }\n",
+ remote_category_id, category_title.c_str(),
+ base::JoinString(snippets, ", ").c_str());
}
-std::string GetTestJson(const std::vector<std::string>& snippets) {
- return GetTestJson(snippets, kTestJsonDefaultCategoryTitle);
+class MultiCategoryJsonBuilder {
+ public:
+ MultiCategoryJsonBuilder() {}
+
+ MultiCategoryJsonBuilder& AddCategoryWithCustomTitle(
+ const std::vector<std::string>& snippets,
+ int remote_category_id,
+ const std::string& category_title) {
+ category_json_.push_back(
+ GetCategoryJson(snippets, remote_category_id, category_title));
+ return *this;
+ }
+
+ MultiCategoryJsonBuilder& AddCategory(
+ const std::vector<std::string>& snippets,
+ int remote_category_id) {
+ return AddCategoryWithCustomTitle(
+ snippets, remote_category_id,
+ "Title" + base::IntToString(remote_category_id));
+ }
+
+ std::string Build() {
+ return base::StringPrintf(
+ "{\n"
+ " \"categories\": [\n"
+ "%s\n"
+ " ]\n"
+ "}\n",
+ base::JoinString(category_json_, " ,\n").c_str());
+ }
+
+ private:
+ std::vector<std::string> category_json_;
+};
+
+// 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>& snippets,
+ const std::string& category_title) {
+ return MultiCategoryJsonBuilder()
+ .AddCategoryWithCustomTitle(snippets, /*remote_category_id=*/1,
+ category_title)
+ .Build();
}
-// TODO(tschumann): Remove the default parameter other_id. It makes the tests
-// less explicit and hard to read. Also get rid of the convenience
-// other_category() and unknown_category() helpers -- tests can just define
-// their own.
-std::string GetMultiCategoryJson(const std::vector<std::string>& articles,
- const std::vector<std::string>& others,
- int other_id = 2) {
- return base::StringPrintf(
- "{\n"
- " \"categories\": [{\n"
- " \"id\": 1,\n"
- " \"localizedTitle\": \"Articles for You\",\n"
- " \"suggestions\": [%s]\n"
- " }, {\n"
- " \"id\": %i,\n"
- " \"localizedTitle\": \"Other Things\",\n"
- " \"suggestions\": [%s]\n"
- " }]\n"
- "}\n",
- base::JoinString(articles, ", ").c_str(), other_id,
- base::JoinString(others, ", ").c_str());
+std::string GetTestJson(const std::vector<std::string>& snippets) {
+ return GetTestJson(snippets, kTestJsonDefaultCategoryTitle);
}
std::string FormatTime(const base::Time& t) {
@@ -263,7 +291,7 @@ void ServeOneByOneImage(
notify->OnImageDataFetched(id, "1-by-1-image-data");
}
-gfx::Image FetchImage(RemoteSuggestionsProvider* service,
+gfx::Image FetchImage(RemoteSuggestionsProviderImpl* service,
const ContentSuggestion::ID& suggestion_id) {
gfx::Image result;
base::RunLoop run_loop;
@@ -279,10 +307,9 @@ gfx::Image FetchImage(RemoteSuggestionsProvider* service,
return result;
}
-void ParseJson(
- const std::string& json,
- const ntp_snippets::NTPSnippetsFetcher::SuccessCallback& success_callback,
- const ntp_snippets::NTPSnippetsFetcher::ErrorCallback& error_callback) {
+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) {
@@ -306,14 +333,6 @@ class FailingFakeURLFetcherFactory : public net::URLFetcherFactory {
}
};
-class MockScheduler : public NTPSnippetsScheduler {
- public:
- MOCK_METHOD2(Schedule,
- bool(base::TimeDelta period_wifi,
- base::TimeDelta period_fallback));
- MOCK_METHOD0(Unschedule, bool());
-};
-
class MockImageFetcher : public ImageFetcher {
public:
MOCK_METHOD1(SetImageFetcherDelegate, void(ImageFetcherDelegate*));
@@ -325,53 +344,6 @@ class MockImageFetcher : public ImageFetcher {
base::Callback<void(const std::string&, const gfx::Image&)>));
};
-class FakeContentSuggestionsProviderObserver
- : public ContentSuggestionsProvider::Observer {
- public:
- FakeContentSuggestionsProviderObserver() = default;
-
- void OnNewSuggestions(ContentSuggestionsProvider* provider,
- Category category,
- std::vector<ContentSuggestion> suggestions) override {
- suggestions_[category] = std::move(suggestions);
- }
-
- void OnCategoryStatusChanged(ContentSuggestionsProvider* provider,
- Category category,
- CategoryStatus new_status) override {
- statuses_[category] = new_status;
- }
-
- void OnSuggestionInvalidated(
- ContentSuggestionsProvider* provider,
- const ContentSuggestion::ID& suggestion_id) override {}
-
- const std::map<Category, CategoryStatus, Category::CompareByID>& statuses()
- const {
- return statuses_;
- }
-
- CategoryStatus StatusForCategory(Category category) const {
- auto it = statuses_.find(category);
- if (it == statuses_.end()) {
- return CategoryStatus::NOT_PROVIDED;
- }
- return it->second;
- }
-
- const std::vector<ContentSuggestion>& SuggestionsForCategory(
- Category category) {
- return suggestions_[category];
- }
-
- private:
- std::map<Category, CategoryStatus, Category::CompareByID> statuses_;
- std::map<Category, std::vector<ContentSuggestion>, Category::CompareByID>
- suggestions_;
-
- DISALLOW_COPY_AND_ASSIGN(FakeContentSuggestionsProviderObserver);
-};
-
class FakeImageDecoder : public image_fetcher::ImageDecoder {
public:
FakeImageDecoder() {}
@@ -390,9 +362,9 @@ class FakeImageDecoder : public image_fetcher::ImageDecoder {
} // namespace
-class RemoteSuggestionsProviderTest : public ::testing::Test {
+class RemoteSuggestionsProviderImplTest : public ::testing::Test {
public:
- RemoteSuggestionsProviderTest()
+ RemoteSuggestionsProviderImplTest()
: params_manager_(ntp_snippets::kStudyName,
{{"content_suggestions_backend",
kTestContentSuggestionsServerEndpoint},
@@ -400,31 +372,36 @@ class RemoteSuggestionsProviderTest : public ::testing::Test {
fake_url_fetcher_factory_(
/*default_factory=*/&failing_url_fetcher_factory_),
test_url_(kTestContentSuggestionsServerWithAPIKey),
+ category_ranker_(base::MakeUnique<ConstantCategoryRanker>()),
user_classifier_(/*pref_service=*/nullptr),
+ suggestions_fetcher_(nullptr),
image_fetcher_(nullptr),
- image_decoder_(nullptr) {
- RemoteSuggestionsProvider::RegisterProfilePrefs(
+ image_decoder_(nullptr),
+ database_(nullptr) {
+ RemoteSuggestionsProviderImpl::RegisterProfilePrefs(
utils_.pref_service()->registry());
RequestThrottler::RegisterProfilePrefs(utils_.pref_service()->registry());
EXPECT_TRUE(database_dir_.CreateUniqueTempDir());
}
- ~RemoteSuggestionsProviderTest() override {
+ ~RemoteSuggestionsProviderImplTest() override {
// We need to run the message loop after deleting the database, because
// ProtoDatabaseImpl deletes the actual LevelDB asynchronously on the task
// runner. Without this, we'd get reports of memory leaks.
base::RunLoop().RunUntilIdle();
}
- std::unique_ptr<RemoteSuggestionsProvider> MakeSnippetsService(
+ // TODO(vitaliii): Rewrite this function to initialize a test class member
+ // instead of creating a new service.
+ std::unique_ptr<RemoteSuggestionsProviderImpl> MakeSnippetsService(
bool set_empty_response = true) {
auto service = MakeSnippetsServiceWithoutInitialization();
WaitForSnippetsServiceInitialization(service.get(), set_empty_response);
return service;
}
- std::unique_ptr<RemoteSuggestionsProvider>
+ std::unique_ptr<RemoteSuggestionsProviderImpl>
MakeSnippetsServiceWithoutInitialization() {
scoped_refptr<base::SingleThreadTaskRunner> task_runner(
base::ThreadTaskRunnerHandle::Get());
@@ -432,12 +409,11 @@ class RemoteSuggestionsProviderTest : public ::testing::Test {
new net::TestURLRequestContextGetter(task_runner.get());
utils_.ResetSigninManager();
- std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher =
- base::MakeUnique<NTPSnippetsFetcher>(
- utils_.fake_signin_manager(), fake_token_service_.get(),
- std::move(request_context_getter), utils_.pref_service(),
- &category_factory_, nullptr, base::Bind(&ParseJson), kAPIKey,
- &user_classifier_);
+ auto suggestions_fetcher = base::MakeUnique<RemoteSuggestionsFetcher>(
+ utils_.fake_signin_manager(), fake_token_service_.get(),
+ std::move(request_context_getter), utils_.pref_service(), nullptr,
+ base::Bind(&ParseJson), kAPIKey, &user_classifier_);
+ suggestions_fetcher_ = suggestions_fetcher.get();
utils_.fake_signin_manager()->SignIn("foo@bar.com");
@@ -449,35 +425,45 @@ class RemoteSuggestionsProviderTest : public ::testing::Test {
image_decoder_ = image_decoder.get();
EXPECT_FALSE(observer_);
observer_ = base::MakeUnique<FakeContentSuggestionsProviderObserver>();
- return base::MakeUnique<RemoteSuggestionsProvider>(
- observer_.get(), &category_factory_, utils_.pref_service(), "fr",
- &user_classifier_, &scheduler_, std::move(snippets_fetcher),
- std::move(image_fetcher), std::move(image_decoder),
- base::MakeUnique<RemoteSuggestionsDatabase>(database_dir_.GetPath(),
- task_runner),
- base::MakeUnique<NTPSnippetsStatusService>(utils_.fake_signin_manager(),
- utils_.pref_service()));
+ auto database = base::MakeUnique<RemoteSuggestionsDatabase>(
+ database_dir_.GetPath(), task_runner);
+ database_ = database.get();
+ return base::MakeUnique<RemoteSuggestionsProviderImpl>(
+ observer_.get(), utils_.pref_service(), "fr", category_ranker_.get(),
+ std::move(suggestions_fetcher), std::move(image_fetcher),
+ std::move(image_decoder), std::move(database),
+ base::MakeUnique<RemoteSuggestionsStatusService>(
+ utils_.fake_signin_manager(), utils_.pref_service()));
}
- void WaitForSnippetsServiceInitialization(RemoteSuggestionsProvider* service,
- bool set_empty_response) {
- EXPECT_EQ(RemoteSuggestionsProvider::State::NOT_INITED, service->state_);
+ void WaitForSnippetsServiceInitialization(
+ RemoteSuggestionsProviderImpl* service,
+ bool set_empty_response) {
+ 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)
+ if (set_empty_response) {
SetUpFetchResponse(GetTestJson(std::vector<std::string>()));
+ }
// TODO(treib): Find a better way to wait for initialization to finish.
base::RunLoop().RunUntilIdle();
- EXPECT_NE(RemoteSuggestionsProvider::State::NOT_INITED, service->state_);
+ EXPECT_NE(RemoteSuggestionsProviderImpl::State::NOT_INITED,
+ service->state_);
}
void ResetSnippetsService(
- std::unique_ptr<RemoteSuggestionsProvider>* service) {
+ std::unique_ptr<RemoteSuggestionsProviderImpl>* service,
+ bool set_empty_response) {
service->reset();
observer_.reset();
- *service = MakeSnippetsService();
+ *service = MakeSnippetsService(set_empty_response);
+ }
+
+ void SetCategoryRanker(std::unique_ptr<CategoryRanker> category_ranker) {
+ category_ranker_ = std::move(category_ranker);
}
ContentSuggestion::ID MakeArticleID(const std::string& id_within_category) {
@@ -485,28 +471,33 @@ class RemoteSuggestionsProviderTest : public ::testing::Test {
}
Category articles_category() {
- return category_factory_.FromKnownCategory(KnownCategories::ARTICLES);
+ return Category::FromKnownCategory(KnownCategories::ARTICLES);
}
ContentSuggestion::ID MakeOtherID(const std::string& id_within_category) {
return ContentSuggestion::ID(other_category(), id_within_category);
}
- Category other_category() { return category_factory_.FromRemoteCategory(2); }
+ // TODO(tschumann): Get rid of the convenience other_category() and
+ // unknown_category() helpers -- tests can just define their own.
+ Category other_category() { return Category::FromRemoteCategory(2); }
Category unknown_category() {
- return category_factory_.FromRemoteCategory(kUnknownRemoteCategoryId);
+ return Category::FromRemoteCategory(kUnknownRemoteCategoryId);
}
protected:
const GURL& test_url() { return test_url_; }
FakeContentSuggestionsProviderObserver& observer() { return *observer_; }
- MockScheduler& mock_scheduler() { return scheduler_; }
+ RemoteSuggestionsFetcher* suggestions_fetcher() {
+ return suggestions_fetcher_;
+ }
// TODO(tschumann): Make this a strict-mock. We want to avoid unneccesary
// network requests.
NiceMock<MockImageFetcher>* image_fetcher() { return image_fetcher_; }
FakeImageDecoder* image_decoder() { return image_decoder_; }
PrefService* pref_service() { return utils_.pref_service(); }
+ RemoteSuggestionsDatabase* database() { return database_; }
// Provide the json to be returned by the fake fetcher.
void SetUpFetchResponse(const std::string& json) {
@@ -514,14 +505,22 @@ class RemoteSuggestionsProviderTest : public ::testing::Test {
net::URLRequestStatus::SUCCESS);
}
- void LoadFromJSONString(RemoteSuggestionsProvider* service,
+ // 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->FetchSnippets(true);
+ service->FetchSnippets(/*interactive_request=*/true,
+ /*callback=*/nullptr);
base::RunLoop().RunUntilIdle();
}
- void LoadMoreFromJSONString(RemoteSuggestionsProvider* service,
+ void LoadMoreFromJSONString(RemoteSuggestionsProviderImpl* service,
const Category& category,
const std::string& json,
const std::set<std::string>& known_ids,
@@ -533,143 +532,27 @@ class RemoteSuggestionsProviderTest : public ::testing::Test {
private:
variations::testing::VariationParamsManager params_manager_;
- test::NTPSnippetsTestUtils utils_;
+ 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<OAuth2TokenService> fake_token_service_;
+ std::unique_ptr<CategoryRanker> category_ranker_;
UserClassifier user_classifier_;
- NiceMock<MockScheduler> scheduler_;
std::unique_ptr<FakeContentSuggestionsProviderObserver> observer_;
- CategoryFactory category_factory_;
+ RemoteSuggestionsFetcher* suggestions_fetcher_;
NiceMock<MockImageFetcher>* image_fetcher_;
FakeImageDecoder* image_decoder_;
base::ScopedTempDir database_dir_;
+ RemoteSuggestionsDatabase* database_;
- DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProviderTest);
+ DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProviderImplTest);
};
-TEST_F(RemoteSuggestionsProviderTest, ScheduleOnStart) {
- // We should get two |Schedule| calls: The first when initialization
- // completes, the second one after the automatic (since the service doesn't
- // have any data yet) fetch finishes.
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(2);
- EXPECT_CALL(mock_scheduler(), Unschedule()).Times(0);
- auto service = MakeSnippetsService();
-
- // When we have no snippets are all, loading the service initiates a fetch.
- EXPECT_EQ("OK", service->snippets_fetcher()->last_status());
-}
-
-TEST_F(RemoteSuggestionsProviderTest, DontRescheduleOnStart) {
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(2);
- EXPECT_CALL(mock_scheduler(), Unschedule()).Times(0);
- SetUpFetchResponse(GetTestJson({GetSnippet()}));
- auto service = MakeSnippetsService(/*set_empty_response=*/false);
-
- // When recreating the service, we should not get any |Schedule| calls:
- // The tasks are already scheduled with the correct intervals, so nothing on
- // initialization, and the service has data from the DB, so no automatic fetch
- // should happen.
- Mock::VerifyAndClearExpectations(&mock_scheduler());
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(0);
- EXPECT_CALL(mock_scheduler(), Unschedule()).Times(0);
- ResetSnippetsService(&service);
-}
-
-TEST_F(RemoteSuggestionsProviderTest, RescheduleAfterSuccessfulFetch) {
- // We should get two |Schedule| calls: The first when initialization
- // completes, the second one after the automatic (since the service doesn't
- // have any data yet) fetch finishes.
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(2);
- auto service = MakeSnippetsService();
-
- // A successful fetch should trigger another |Schedule|.
- EXPECT_CALL(mock_scheduler(), Schedule(_, _));
- LoadFromJSONString(service.get(), GetTestJson({GetSnippet()}));
-}
-
-TEST_F(RemoteSuggestionsProviderTest, DontRescheduleAfterFailedFetch) {
- // We should get two |Schedule| calls: The first when initialization
- // completes, the second one after the automatic (since the service doesn't
- // have any data yet) fetch finishes.
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(2);
- auto service = MakeSnippetsService();
-
- // A failed fetch should NOT trigger another |Schedule|.
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(0);
- LoadFromJSONString(service.get(), GetTestJson({GetInvalidSnippet()}));
-}
-
-TEST_F(RemoteSuggestionsProviderTest, IgnoreRescheduleBeforeInit) {
- // We should get two |Schedule| calls: The first when initialization
- // completes, the second one after the automatic (since the service doesn't
- // have any data yet) fetch finishes.
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(2);
- // The |RescheduleFetching| call shouldn't do anything (in particular not
- // result in an |Unschedule|), since the service isn't initialized yet.
- EXPECT_CALL(mock_scheduler(), Unschedule()).Times(0);
- auto service = MakeSnippetsServiceWithoutInitialization();
- service->RescheduleFetching(false);
- WaitForSnippetsServiceInitialization(service.get(),
- /*set_empty_response=*/true);
-}
-
-TEST_F(RemoteSuggestionsProviderTest, HandleForcedRescheduleBeforeInit) {
- {
- InSequence s;
- // The |RescheduleFetching| call with force=true should result in an
- // |Unschedule|, since the service isn't initialized yet.
- EXPECT_CALL(mock_scheduler(), Unschedule()).Times(1);
- // We should get two |Schedule| calls: The first when initialization
- // completes, the second one after the automatic (since the service doesn't
- // have any data yet) fetch finishes.
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(2);
- }
- auto service = MakeSnippetsServiceWithoutInitialization();
- service->RescheduleFetching(true);
- WaitForSnippetsServiceInitialization(service.get(),
- /*set_empty_response=*/true);
-}
-
-TEST_F(RemoteSuggestionsProviderTest, RescheduleOnStateChange) {
- {
- InSequence s;
- // Initial startup.
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(2);
- // Service gets disabled.
- EXPECT_CALL(mock_scheduler(), Unschedule());
- // Service gets enabled again.
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(2);
- }
- auto service = MakeSnippetsService();
- ASSERT_TRUE(service->ready());
-
- service->OnSnippetsStatusChanged(SnippetsStatus::ENABLED_AND_SIGNED_IN,
- SnippetsStatus::EXPLICITLY_DISABLED);
- ASSERT_FALSE(service->ready());
- base::RunLoop().RunUntilIdle();
-
- service->OnSnippetsStatusChanged(SnippetsStatus::EXPLICITLY_DISABLED,
- SnippetsStatus::ENABLED_AND_SIGNED_OUT);
- ASSERT_TRUE(service->ready());
- base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(RemoteSuggestionsProviderTest, DontUnscheduleOnShutdown) {
- EXPECT_CALL(mock_scheduler(), Schedule(_, _)).Times(2);
- EXPECT_CALL(mock_scheduler(), Unschedule()).Times(0);
-
- auto service = MakeSnippetsService();
-
- service.reset();
- base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(RemoteSuggestionsProviderTest, Full) {
+TEST_F(RemoteSuggestionsProviderImplTest, Full) {
std::string json_str(GetTestJson({GetSnippet()}));
auto service = MakeSnippetsService();
@@ -689,10 +572,9 @@ TEST_F(RemoteSuggestionsProviderTest, Full) {
EXPECT_EQ(GetDefaultCreationTime(), suggestion.publish_date());
EXPECT_EQ(kSnippetPublisherName,
base::UTF16ToUTF8(suggestion.publisher_name()));
- EXPECT_EQ(GURL(kSnippetAmpUrl), suggestion.amp_url());
}
-TEST_F(RemoteSuggestionsProviderTest, CategoryTitle) {
+TEST_F(RemoteSuggestionsProviderImplTest, CategoryTitle) {
const base::string16 test_default_title =
base::UTF8ToUTF16(kTestJsonDefaultCategoryTitle);
@@ -727,12 +609,13 @@ TEST_F(RemoteSuggestionsProviderTest, CategoryTitle) {
EXPECT_THAT(info_before.show_if_empty(), Eq(true));
}
-TEST_F(RemoteSuggestionsProviderTest, MultipleCategories) {
- std::string json_str(
- GetMultiCategoryJson({GetSnippetN(0)}, {GetSnippetN(1)}));
-
+TEST_F(RemoteSuggestionsProviderImplTest, MultipleCategories) {
auto service = MakeSnippetsService();
-
+ std::string json_str =
+ MultiCategoryJsonBuilder()
+ .AddCategory({GetSnippetN(0)}, /*remote_category_id=*/1)
+ .AddCategory({GetSnippetN(1)}, /*remote_category_id=*/2)
+ .Build();
LoadFromJSONString(service.get(), json_str);
ASSERT_THAT(observer().statuses(),
@@ -758,7 +641,6 @@ TEST_F(RemoteSuggestionsProviderTest, MultipleCategories) {
EXPECT_EQ(GetDefaultCreationTime(), suggestion.publish_date());
EXPECT_EQ(kSnippetPublisherName,
base::UTF16ToUTF8(suggestion.publisher_name()));
- EXPECT_EQ(GURL(kSnippetAmpUrl), suggestion.amp_url());
}
{
@@ -770,11 +652,10 @@ TEST_F(RemoteSuggestionsProviderTest, MultipleCategories) {
EXPECT_EQ(GetDefaultCreationTime(), suggestion.publish_date());
EXPECT_EQ(kSnippetPublisherName,
base::UTF16ToUTF8(suggestion.publisher_name()));
- EXPECT_EQ(GURL(kSnippetAmpUrl), suggestion.amp_url());
}
}
-TEST_F(RemoteSuggestionsProviderTest, ArticleCategoryInfo) {
+TEST_F(RemoteSuggestionsProviderImplTest, ArticleCategoryInfo) {
auto service = MakeSnippetsService();
CategoryInfo article_info = service->GetCategoryInfo(articles_category());
EXPECT_THAT(article_info.has_more_action(), Eq(true));
@@ -783,14 +664,17 @@ TEST_F(RemoteSuggestionsProviderTest, ArticleCategoryInfo) {
EXPECT_THAT(article_info.show_if_empty(), Eq(true));
}
-TEST_F(RemoteSuggestionsProviderTest, ExperimentalCategoryInfo) {
+TEST_F(RemoteSuggestionsProviderImplTest, ExperimentalCategoryInfo) {
auto service = MakeSnippetsService();
-
+ std::string json_str =
+ MultiCategoryJsonBuilder()
+ .AddCategory({GetSnippetN(0)}, /*remote_category_id=*/1)
+ .AddCategory({GetSnippetN(1)}, kUnknownRemoteCategoryId)
+ .Build();
// Load data with multiple categories so that a new experimental category gets
// registered.
- LoadFromJSONString(service.get(),
- GetMultiCategoryJson({GetSnippetN(0)}, {GetSnippetN(1)},
- kUnknownRemoteCategoryId));
+ LoadFromJSONString(service.get(), json_str);
+
CategoryInfo info = service->GetCategoryInfo(unknown_category());
EXPECT_THAT(info.has_more_action(), Eq(false));
EXPECT_THAT(info.has_reload_action(), Eq(false));
@@ -798,12 +682,42 @@ TEST_F(RemoteSuggestionsProviderTest, ExperimentalCategoryInfo) {
EXPECT_THAT(info.show_if_empty(), Eq(false));
}
-TEST_F(RemoteSuggestionsProviderTest, PersistCategoryInfos) {
- auto service = MakeSnippetsService();
+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({GetSnippetN(0)}, /*remote_category_id=*/11)
+ .AddCategory({GetSnippetN(1)}, /*remote_category_id=*/13)
+ .AddCategory({GetSnippetN(2)}, /*remote_category_id=*/12)
+ .Build();
+ SetUpFetchResponse(json_str);
+ {
+ // The order of categories is determined by the order in which they are
+ // added. Thus, the latter is tested here.
+ InSequence s;
+ EXPECT_CALL(*raw_mock_ranker,
+ AppendCategoryIfNecessary(Category::FromRemoteCategory(11)));
+ EXPECT_CALL(*raw_mock_ranker,
+ AppendCategoryIfNecessary(Category::FromRemoteCategory(13)));
+ EXPECT_CALL(*raw_mock_ranker,
+ AppendCategoryIfNecessary(Category::FromRemoteCategory(12)));
+ }
+ auto service = MakeSnippetsService(/*set_empty_response=*/false);
+}
- LoadFromJSONString(service.get(),
- GetMultiCategoryJson({GetSnippetN(0)}, {GetSnippetN(1)},
- kUnknownRemoteCategoryId));
+TEST_F(RemoteSuggestionsProviderImplTest, PersistCategoryInfos) {
+ auto service = MakeSnippetsService();
+ // TODO(vitaliii): Use |articles_category()| instead of constant ID below.
+ std::string json_str =
+ MultiCategoryJsonBuilder()
+ .AddCategoryWithCustomTitle(
+ {GetSnippetN(0)}, /*remote_category_id=*/1, "Articles for You")
+ .AddCategoryWithCustomTitle({GetSnippetN(1)},
+ kUnknownRemoteCategoryId, "Other Things")
+ .Build();
+ LoadFromJSONString(service.get(), json_str);
ASSERT_EQ(observer().StatusForCategory(articles_category()),
CategoryStatus::AVAILABLE);
@@ -816,7 +730,7 @@ TEST_F(RemoteSuggestionsProviderTest, PersistCategoryInfos) {
service->GetCategoryInfo(unknown_category());
// Recreate the service to simulate a Chrome restart.
- ResetSnippetsService(&service);
+ ResetSnippetsService(&service, /*set_empty_response=*/true);
// The categories should have been restored.
ASSERT_NE(observer().StatusForCategory(articles_category()),
@@ -838,18 +752,57 @@ TEST_F(RemoteSuggestionsProviderTest, PersistCategoryInfos) {
EXPECT_EQ(info_unknown_before.title(), info_unknown_after.title());
}
-TEST_F(RemoteSuggestionsProviderTest, PersistSuggestions) {
- auto service = MakeSnippetsService();
+TEST_F(RemoteSuggestionsProviderImplTest, PersistRemoteCategoryOrder) {
+ // We create a service with a normal ranker to store the order.
+ std::string json_str =
+ MultiCategoryJsonBuilder()
+ .AddCategory({GetSnippetN(0)}, /*remote_category_id=*/11)
+ .AddCategory({GetSnippetN(1)}, /*remote_category_id=*/13)
+ .AddCategory({GetSnippetN(2)}, /*remote_category_id=*/12)
+ .Build();
+ SetUpFetchResponse(json_str);
+ auto service = MakeSnippetsService(/*set_empty_response=*/false);
- LoadFromJSONString(service.get(),
- GetMultiCategoryJson({GetSnippetN(0)}, {GetSnippetN(1)}));
+ // 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("");
+ auto mock_ranker = base::MakeUnique<MockCategoryRanker>();
+ MockCategoryRanker* raw_mock_ranker = mock_ranker.get();
+ SetCategoryRanker(std::move(mock_ranker));
+ {
+ // The order of categories is determined by the order in which they are
+ // added. Thus, the latter is tested here.
+ InSequence s;
+ // Article category always exists and, therefore, it is stored in prefs too.
+ EXPECT_CALL(*raw_mock_ranker,
+ AppendCategoryIfNecessary(articles_category()));
+
+ EXPECT_CALL(*raw_mock_ranker,
+ AppendCategoryIfNecessary(Category::FromRemoteCategory(11)));
+ EXPECT_CALL(*raw_mock_ranker,
+ AppendCategoryIfNecessary(Category::FromRemoteCategory(13)));
+ EXPECT_CALL(*raw_mock_ranker,
+ AppendCategoryIfNecessary(Category::FromRemoteCategory(12)));
+ }
+ ResetSnippetsService(&service, /*set_empty_response=*/false);
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest, PersistSuggestions) {
+ auto service = MakeSnippetsService();
+ std::string json_str =
+ MultiCategoryJsonBuilder()
+ .AddCategory({GetSnippetN(0)}, /*remote_category_id=*/1)
+ .AddCategory({GetSnippetN(2)}, /*remote_category_id=*/2)
+ .Build();
+ LoadFromJSONString(service.get(), json_str);
ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
SizeIs(1));
ASSERT_THAT(observer().SuggestionsForCategory(other_category()), SizeIs(1));
// Recreate the service to simulate a Chrome restart.
- ResetSnippetsService(&service);
+ ResetSnippetsService(&service, /*set_empty_response=*/true);
// The suggestions in both categories should have been restored.
EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
@@ -857,11 +810,17 @@ TEST_F(RemoteSuggestionsProviderTest, PersistSuggestions) {
EXPECT_THAT(observer().SuggestionsForCategory(other_category()), SizeIs(1));
}
-TEST_F(RemoteSuggestionsProviderTest, DontNotifyIfNotAvailable) {
+TEST_F(RemoteSuggestionsProviderImplTest, DontNotifyIfNotAvailable) {
// Get some suggestions into the database.
auto service = MakeSnippetsService();
- LoadFromJSONString(service.get(),
- GetMultiCategoryJson({GetSnippetN(0)}, {GetSnippetN(1)}));
+ std::string json_str =
+ MultiCategoryJsonBuilder()
+ .AddCategory({GetSnippetN(0)},
+ /*remote_category_id=*/1)
+ .AddCategory({GetSnippetN(1)}, /*remote_category_id=*/2)
+ .Build();
+ LoadFromJSONString(service.get(), json_str);
+
ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
SizeIs(1));
ASSERT_THAT(observer().SuggestionsForCategory(other_category()), SizeIs(1));
@@ -872,9 +831,10 @@ TEST_F(RemoteSuggestionsProviderTest, DontNotifyIfNotAvailable) {
pref_service()->SetBoolean(prefs::kEnableSnippets, false);
// Recreate the service to simulate a Chrome start.
- ResetSnippetsService(&service);
+ ResetSnippetsService(&service, /*set_empty_response=*/true);
- ASSERT_THAT(RemoteSuggestionsProvider::State::DISABLED, Eq(service->state_));
+ ASSERT_THAT(RemoteSuggestionsProviderImpl::State::DISABLED,
+ Eq(service->state_));
// Now the observer should not have received any suggestions.
EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
@@ -882,7 +842,7 @@ TEST_F(RemoteSuggestionsProviderTest, DontNotifyIfNotAvailable) {
EXPECT_THAT(observer().SuggestionsForCategory(other_category()), IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, Clear) {
+TEST_F(RemoteSuggestionsProviderImplTest, Clear) {
auto service = MakeSnippetsService();
std::string json_str(GetTestJson({GetSnippet()}));
@@ -894,7 +854,7 @@ TEST_F(RemoteSuggestionsProviderTest, Clear) {
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, ReplaceSnippets) {
+TEST_F(RemoteSuggestionsProviderImplTest, ReplaceSnippets) {
auto service = MakeSnippetsService();
std::string first("http://first");
@@ -909,7 +869,7 @@ TEST_F(RemoteSuggestionsProviderTest, ReplaceSnippets) {
ElementsAre(IdEq(second)));
}
-TEST_F(RemoteSuggestionsProviderTest, LoadsAdditionalSnippets) {
+TEST_F(RemoteSuggestionsProviderImplTest, LoadsAdditionalSnippets) {
auto service = MakeSnippetsService();
LoadFromJSONString(service.get(),
@@ -928,7 +888,8 @@ TEST_F(RemoteSuggestionsProviderTest, LoadsAdditionalSnippets) {
expect_only_second_suggestion_received);
// Verify we can resolve the image of the new snippets.
- ServeImageCallback cb = base::Bind(&ServeOneByOneImage, service.get());
+ ServeImageCallback cb =
+ base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _))
.Times(2)
.WillRepeatedly(WithArgs<0, 2>(Invoke(&cb, &ServeImageCallback::Run)));
@@ -958,7 +919,8 @@ TEST_F(RemoteSuggestionsProviderTest, LoadsAdditionalSnippets) {
// 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 snippets.
-TEST_F(RemoteSuggestionsProviderTest, TestMergingFetchedMoreSnippetsFillup) {
+TEST_F(RemoteSuggestionsProviderImplTest,
+ TestMergingFetchedMoreSnippetsFillup) {
auto service = MakeSnippetsService(/*set_empty_response=*/false);
LoadFromJSONString(
service.get(),
@@ -1011,7 +973,47 @@ TEST_F(RemoteSuggestionsProviderTest, TestMergingFetchedMoreSnippetsFillup) {
ElementsAre(IdEq("http://id-1"), IdEq("http://id-2")));
}
-TEST_F(RemoteSuggestionsProviderTest,
+TEST_F(RemoteSuggestionsProviderImplTest,
+ ClearHistoryShouldDeleteArchivedSnippets) {
+ auto service = MakeSnippetsService(/*set_empty_response=*/false);
+ // 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({GetSnippetWithUrl("http://id-1"),
+ GetSnippetWithUrl("http://id-2")}));
+ LoadFromJSONString(service.get(),
+ GetTestJson({GetSnippetWithUrl("http://new-id-1"),
+ GetSnippetWithUrl("http://new-id-2")}));
+ // 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());
+ EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _))
+ .Times(2)
+ .WillRepeatedly(WithArgs<0, 2>(Invoke(&cb, &ServeImageCallback::Run)));
+ image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
+ gfx::Image image = FetchImage(service.get(), MakeArticleID("http://id-1"));
+ ASSERT_FALSE(image.IsEmpty());
+ ASSERT_EQ(1, image.Width());
+ image = FetchImage(service.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)>());
+
+ // Make sure images of both batches are gone.
+ // Verify we cannot resolve the image of the new snippets.
+ image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
+ EXPECT_TRUE(
+ FetchImage(service.get(), MakeArticleID("http://id-1")).IsEmpty());
+ EXPECT_TRUE(
+ FetchImage(service.get(), MakeArticleID("http://new-id-1")).IsEmpty());
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
TestMergingFetchedMoreSnippetsReplaceAll) {
auto service = MakeSnippetsService(/*set_empty_response=*/false);
LoadFromJSONString(
@@ -1035,17 +1037,17 @@ TEST_F(RemoteSuggestionsProviderTest,
auto expect_receiving_ten_new_snippets =
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")));
+ 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")));
});
LoadMoreFromJSONString(
service.get(), articles_category(),
@@ -1083,10 +1085,10 @@ TEST_F(RemoteSuggestionsProviderTest,
IdEq("http://id-10")));
}
-// TODO(tschumann): We don't have test making sure the NTPSnippetsFetcher
+// TODO(tschumann): We don't have test making sure the RemoteSuggestionsFetcher
// actually gets the proper parameters. Add tests with an injected
-// NTPSnippetsFetcher to verify the parameters, including proper handling of
-// dismissed and known_ids.
+// RemoteSuggestionsFetcher to verify the parameters, including proper handling
+// of dismissed and known_ids.
namespace {
@@ -1100,61 +1102,112 @@ void SuggestionsLoaded(
} // namespace
-TEST_F(RemoteSuggestionsProviderTest, ReturnFetchRequestEmptyBeforeInit) {
+TEST_F(RemoteSuggestionsProviderImplTest, ReturnFetchRequestEmptyBeforeInit) {
auto service = MakeSnippetsServiceWithoutInitialization();
MockFunction<void(Status, const std::vector<ContentSuggestion>&)> loaded;
- EXPECT_CALL(loaded, Call(HasCode(StatusCode::TEMPORARY_ERROR), SizeIs(0)));
+ EXPECT_CALL(loaded, Call(HasCode(StatusCode::TEMPORARY_ERROR), IsEmpty()));
service->Fetch(articles_category(), std::set<std::string>(),
base::Bind(&SuggestionsLoaded, &loaded));
base::RunLoop().RunUntilIdle();
}
-TEST_F(RemoteSuggestionsProviderTest, LoadInvalidJson) {
+TEST_F(RemoteSuggestionsProviderImplTest, ReturnTemporaryErrorForInvalidJson) {
+ auto service = MakeSnippetsService();
+
+ 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,
+ ReturnTemporaryErrorForInvalidSnippet) {
+ auto service = MakeSnippetsService();
+
+ MockFunction<void(Status, const std::vector<ContentSuggestion>&)> loaded;
+ EXPECT_CALL(loaded, Call(HasCode(StatusCode::TEMPORARY_ERROR), IsEmpty()));
+ LoadMoreFromJSONString(service.get(), articles_category(),
+ GetTestJson({GetIncompleteSnippet()}),
+ /*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 SnippetsService will fail by default with unsuccessful request.
+ auto service = MakeSnippetsService(/*set_empty_response=*/false);
+
+ 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 = MakeSnippetsService();
+ 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 = MakeSnippetsService();
LoadFromJSONString(service.get(), GetTestJson({GetInvalidSnippet()}));
- EXPECT_THAT(service->snippets_fetcher()->last_status(),
+ EXPECT_THAT(suggestions_fetcher()->last_status(),
StartsWith("Received invalid JSON"));
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, LoadInvalidJsonWithExistingSnippets) {
+TEST_F(RemoteSuggestionsProviderImplTest, LoadInvalidJsonWithExistingSnippets) {
auto service = MakeSnippetsService();
LoadFromJSONString(service.get(), GetTestJson({GetSnippet()}));
ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1));
- ASSERT_EQ("OK", service->snippets_fetcher()->last_status());
+ ASSERT_EQ("OK", suggestions_fetcher()->last_status());
LoadFromJSONString(service.get(), GetTestJson({GetInvalidSnippet()}));
- EXPECT_THAT(service->snippets_fetcher()->last_status(),
+ EXPECT_THAT(suggestions_fetcher()->last_status(),
StartsWith("Received invalid JSON"));
// This should not have changed the existing snippets.
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1));
}
-TEST_F(RemoteSuggestionsProviderTest, LoadIncompleteJson) {
+TEST_F(RemoteSuggestionsProviderImplTest, LoadIncompleteJson) {
auto service = MakeSnippetsService();
LoadFromJSONString(service.get(), GetTestJson({GetIncompleteSnippet()}));
- EXPECT_EQ("Invalid / empty list.",
- service->snippets_fetcher()->last_status());
+ EXPECT_EQ("Invalid / empty list.", suggestions_fetcher()->last_status());
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, LoadIncompleteJsonWithExistingSnippets) {
+TEST_F(RemoteSuggestionsProviderImplTest,
+ LoadIncompleteJsonWithExistingSnippets) {
auto service = MakeSnippetsService();
LoadFromJSONString(service.get(), GetTestJson({GetSnippet()}));
ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1));
LoadFromJSONString(service.get(), GetTestJson({GetIncompleteSnippet()}));
- EXPECT_EQ("Invalid / empty list.",
- service->snippets_fetcher()->last_status());
+ EXPECT_EQ("Invalid / empty list.", suggestions_fetcher()->last_status());
// This should not have changed the existing snippets.
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1));
}
-TEST_F(RemoteSuggestionsProviderTest, Dismiss) {
+TEST_F(RemoteSuggestionsProviderImplTest, Dismiss) {
auto service = MakeSnippetsService();
std::string json_str(
@@ -1164,7 +1217,8 @@ TEST_F(RemoteSuggestionsProviderTest, Dismiss) {
ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1));
// Load the image to store it in the database.
- ServeImageCallback cb = base::Bind(&ServeOneByOneImage, service.get());
+ ServeImageCallback cb =
+ base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _))
.WillOnce(WithArgs<0, 2>(Invoke(&cb, &ServeImageCallback::Run)));
image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
@@ -1193,7 +1247,7 @@ TEST_F(RemoteSuggestionsProviderTest, Dismiss) {
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
// The snippet should stay dismissed even after re-creating the service.
- ResetSnippetsService(&service);
+ ResetSnippetsService(&service, /*set_empty_response=*/true);
LoadFromJSONString(service.get(), json_str);
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
@@ -1204,7 +1258,7 @@ TEST_F(RemoteSuggestionsProviderTest, Dismiss) {
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1));
}
-TEST_F(RemoteSuggestionsProviderTest, GetDismissed) {
+TEST_F(RemoteSuggestionsProviderImplTest, GetDismissed) {
auto service = MakeSnippetsService();
LoadFromJSONString(service.get(), GetTestJson({GetSnippet()}));
@@ -1214,8 +1268,8 @@ TEST_F(RemoteSuggestionsProviderTest, GetDismissed) {
service->GetDismissedSuggestionsForDebugging(
articles_category(),
base::Bind(
- [](RemoteSuggestionsProvider* service,
- RemoteSuggestionsProviderTest* test,
+ [](RemoteSuggestionsProviderImpl* service,
+ RemoteSuggestionsProviderImplTest* test,
std::vector<ContentSuggestion> dismissed_suggestions) {
EXPECT_EQ(1u, dismissed_suggestions.size());
for (auto& suggestion : dismissed_suggestions) {
@@ -1230,8 +1284,8 @@ TEST_F(RemoteSuggestionsProviderTest, GetDismissed) {
service->GetDismissedSuggestionsForDebugging(
articles_category(),
base::Bind(
- [](RemoteSuggestionsProvider* service,
- RemoteSuggestionsProviderTest* test,
+ [](RemoteSuggestionsProviderImpl* service,
+ RemoteSuggestionsProviderImplTest* test,
std::vector<ContentSuggestion> dismissed_suggestions) {
EXPECT_EQ(0u, dismissed_suggestions.size());
},
@@ -1239,7 +1293,7 @@ TEST_F(RemoteSuggestionsProviderTest, GetDismissed) {
base::RunLoop().RunUntilIdle();
}
-TEST_F(RemoteSuggestionsProviderTest, CreationTimestampParseFail) {
+TEST_F(RemoteSuggestionsProviderImplTest, CreationTimestampParseFail) {
auto service = MakeSnippetsService();
std::string json =
@@ -1252,7 +1306,7 @@ TEST_F(RemoteSuggestionsProviderTest, CreationTimestampParseFail) {
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, RemoveExpiredDismissedContent) {
+TEST_F(RemoteSuggestionsProviderImplTest, RemoveExpiredDismissedContent) {
auto service = MakeSnippetsService();
std::string json_str1(GetTestJson({GetExpiredSnippet()}));
@@ -1261,7 +1315,8 @@ TEST_F(RemoteSuggestionsProviderTest, RemoveExpiredDismissedContent) {
// 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.get());
+ ServeImageCallback cb =
+ base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _))
.WillOnce(WithArgs<0, 2>(Invoke(&cb, &ServeImageCallback::Run)));
image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
@@ -1284,7 +1339,7 @@ TEST_F(RemoteSuggestionsProviderTest, RemoveExpiredDismissedContent) {
EXPECT_TRUE(FetchImage(service.get(), MakeArticleID(kSnippetUrl)).IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, ExpiredContentNotRemoved) {
+TEST_F(RemoteSuggestionsProviderImplTest, ExpiredContentNotRemoved) {
auto service = MakeSnippetsService();
std::string json_str(GetTestJson({GetExpiredSnippet()}));
@@ -1293,7 +1348,7 @@ TEST_F(RemoteSuggestionsProviderTest, ExpiredContentNotRemoved) {
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1));
}
-TEST_F(RemoteSuggestionsProviderTest, TestSingleSource) {
+TEST_F(RemoteSuggestionsProviderImplTest, TestSingleSource) {
auto service = MakeSnippetsService();
std::string json_str(GetTestJson({GetSnippetWithSources(
@@ -1304,12 +1359,12 @@ TEST_F(RemoteSuggestionsProviderTest, TestSingleSource) {
const NTPSnippet& snippet =
*service->GetSnippetsForTesting(articles_category()).front();
EXPECT_EQ(snippet.id(), kSnippetUrl);
- EXPECT_EQ(snippet.best_source().url, GURL("http://source1.com"));
- EXPECT_EQ(snippet.best_source().publisher_name, std::string("Source 1"));
- EXPECT_EQ(snippet.best_source().amp_url, GURL("http://source1.amp.com"));
+ EXPECT_EQ(snippet.url(), GURL("http://source1.com"));
+ EXPECT_EQ(snippet.publisher_name(), std::string("Source 1"));
+ EXPECT_EQ(snippet.amp_url(), GURL("http://source1.amp.com"));
}
-TEST_F(RemoteSuggestionsProviderTest, TestSingleSourceWithMalformedUrl) {
+TEST_F(RemoteSuggestionsProviderImplTest, TestSingleSourceWithMalformedUrl) {
auto service = MakeSnippetsService();
std::string json_str(GetTestJson({GetSnippetWithSources(
@@ -1319,7 +1374,7 @@ TEST_F(RemoteSuggestionsProviderTest, TestSingleSourceWithMalformedUrl) {
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, TestSingleSourceWithMissingData) {
+TEST_F(RemoteSuggestionsProviderImplTest, TestSingleSourceWithMissingData) {
auto service = MakeSnippetsService();
std::string json_str(
@@ -1329,7 +1384,7 @@ TEST_F(RemoteSuggestionsProviderTest, TestSingleSourceWithMissingData) {
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, LogNumArticlesHistogram) {
+TEST_F(RemoteSuggestionsProviderImplTest, LogNumArticlesHistogram) {
auto service = MakeSnippetsService();
base::HistogramTester tester;
@@ -1388,7 +1443,7 @@ TEST_F(RemoteSuggestionsProviderTest, LogNumArticlesHistogram) {
// There is only a single, dismissed snippet in the database, so recreating
// the service will require us to re-fetch.
tester.ExpectTotalCount("NewTabPage.Snippets.NumArticlesFetched", 4);
- ResetSnippetsService(&service);
+ ResetSnippetsService(&service, /*set_empty_response=*/true);
EXPECT_EQ(observer().StatusForCategory(articles_category()),
CategoryStatus::AVAILABLE);
tester.ExpectTotalCount("NewTabPage.Snippets.NumArticlesFetched", 5);
@@ -1402,11 +1457,11 @@ TEST_F(RemoteSuggestionsProviderTest, LogNumArticlesHistogram) {
service.get(),
GetTestJson({GetSnippetWithUrl("http://not-dismissed.com")}));
tester.ExpectTotalCount("NewTabPage.Snippets.NumArticlesFetched", 6);
- ResetSnippetsService(&service);
+ ResetSnippetsService(&service, /*set_empty_response=*/true);
tester.ExpectTotalCount("NewTabPage.Snippets.NumArticlesFetched", 6);
}
-TEST_F(RemoteSuggestionsProviderTest, DismissShouldRespectAllKnownUrls) {
+TEST_F(RemoteSuggestionsProviderImplTest, DismissShouldRespectAllKnownUrls) {
auto service = MakeSnippetsService();
const base::Time creation = GetDefaultCreationTime();
@@ -1437,43 +1492,45 @@ TEST_F(RemoteSuggestionsProviderTest, DismissShouldRespectAllKnownUrls) {
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, StatusChanges) {
+TEST_F(RemoteSuggestionsProviderImplTest, StatusChanges) {
auto service = MakeSnippetsService();
// Simulate user signed out
SetUpFetchResponse(GetTestJson({GetSnippet()}));
- service->OnSnippetsStatusChanged(SnippetsStatus::ENABLED_AND_SIGNED_IN,
- SnippetsStatus::SIGNED_OUT_AND_DISABLED);
+ service->OnStatusChanged(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN,
+ RemoteSuggestionsStatus::SIGNED_OUT_AND_DISABLED);
base::RunLoop().RunUntilIdle();
EXPECT_THAT(observer().StatusForCategory(articles_category()),
Eq(CategoryStatus::SIGNED_OUT));
- EXPECT_THAT(RemoteSuggestionsProvider::State::DISABLED, Eq(service->state_));
+ EXPECT_THAT(RemoteSuggestionsProviderImpl::State::DISABLED,
+ Eq(service->state_));
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()),
IsEmpty()); // No fetch should be made.
// Simulate user sign in. The service should be ready again and load snippets.
SetUpFetchResponse(GetTestJson({GetSnippet()}));
- service->OnSnippetsStatusChanged(SnippetsStatus::SIGNED_OUT_AND_DISABLED,
- SnippetsStatus::ENABLED_AND_SIGNED_IN);
+ service->OnStatusChanged(RemoteSuggestionsStatus::SIGNED_OUT_AND_DISABLED,
+ RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN);
EXPECT_THAT(observer().StatusForCategory(articles_category()),
Eq(CategoryStatus::AVAILABLE_LOADING));
base::RunLoop().RunUntilIdle();
EXPECT_THAT(observer().StatusForCategory(articles_category()),
Eq(CategoryStatus::AVAILABLE));
- EXPECT_THAT(RemoteSuggestionsProvider::State::READY, Eq(service->state_));
+ EXPECT_THAT(RemoteSuggestionsProviderImpl::State::READY, Eq(service->state_));
EXPECT_FALSE(service->GetSnippetsForTesting(articles_category()).empty());
}
-TEST_F(RemoteSuggestionsProviderTest, ImageReturnedWithTheSameId) {
+TEST_F(RemoteSuggestionsProviderImplTest, ImageReturnedWithTheSameId) {
auto service = MakeSnippetsService();
LoadFromJSONString(service.get(), GetTestJson({GetSnippet()}));
gfx::Image image;
MockFunction<void(const gfx::Image&)> image_fetched;
- ServeImageCallback cb = base::Bind(&ServeOneByOneImage, service.get());
+ ServeImageCallback cb =
+ base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
{
InSequence s;
EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _))
@@ -1490,7 +1547,7 @@ TEST_F(RemoteSuggestionsProviderTest, ImageReturnedWithTheSameId) {
EXPECT_EQ(1, image.Width());
}
-TEST_F(RemoteSuggestionsProviderTest, EmptyImageReturnedForNonExistentId) {
+TEST_F(RemoteSuggestionsProviderImplTest, EmptyImageReturnedForNonExistentId) {
auto service = MakeSnippetsService();
// Create a non-empty image so that we can test the image gets updated.
@@ -1507,7 +1564,33 @@ TEST_F(RemoteSuggestionsProviderTest, EmptyImageReturnedForNonExistentId) {
EXPECT_TRUE(image.IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, ClearHistoryRemovesAllSuggestions) {
+TEST_F(RemoteSuggestionsProviderImplTest,
+ FetchingUnknownImageIdShouldNotHitDatabase) {
+ // 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 = MakeSnippetsService();
+
+ ContentSuggestion::ID unknown_id = MakeArticleID(kSnippetUrl2);
+ database()->SaveImage(unknown_id.id_within_category(), "some image blob");
+ // Set up the image decoder to always return the 1x1 test image.
+ image_decoder()->SetDecodedImage(gfx::test::CreateImage(1, 1));
+
+ // Create a non-empty image so that we can test the image gets updated.
+ gfx::Image image = gfx::test::CreateImage(2, 2);
+ MockFunction<void(const gfx::Image&)> image_fetched;
+ EXPECT_CALL(image_fetched, Call(_)).WillOnce(SaveArg<0>(&image));
+
+ service->FetchSuggestionImage(
+ MakeArticleID(kSnippetUrl2),
+ base::Bind(&MockFunction<void(const gfx::Image&)>::Call,
+ base::Unretained(&image_fetched)));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(image.IsEmpty()) << "got image with width: " << image.Width();
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest, ClearHistoryRemovesAllSuggestions) {
auto service = MakeSnippetsService();
std::string first_snippet = GetSnippetWithUrl("http://url1.com");
@@ -1517,7 +1600,8 @@ TEST_F(RemoteSuggestionsProviderTest, ClearHistoryRemovesAllSuggestions) {
ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(2));
service->DismissSuggestion(MakeArticleID("http://url1.com"));
- ASSERT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1));
+ ASSERT_THAT(observer().SuggestionsForCategory(articles_category()),
+ Not(IsEmpty()));
ASSERT_THAT(service->GetDismissedSnippetsForTesting(articles_category()),
SizeIs(1));
@@ -1526,36 +1610,55 @@ TEST_F(RemoteSuggestionsProviderTest, ClearHistoryRemovesAllSuggestions) {
base::Callback<bool(const GURL& url)> filter;
service->ClearHistory(begin, end, filter);
- EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
+ // Verify that the observer received the update with the empty data as well.
+ EXPECT_THAT(observer().SuggestionsForCategory(articles_category()),
+ IsEmpty());
EXPECT_THAT(service->GetDismissedSnippetsForTesting(articles_category()),
IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest, SuggestionsFetchedOnSignInAndSignOut) {
+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 = MakeSnippetsService();
+
+ ASSERT_THAT(observer().StatusForCategory(articles_category()),
+ Eq(CategoryStatus::AVAILABLE));
+ service->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,
+ SuggestionsFetchedOnSignInAndSignOut) {
auto service = MakeSnippetsService();
- EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(0));
+ EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), IsEmpty());
// |MakeSnippetsService()| creates a service where user is signed in already,
// so we start by signing out.
SetUpFetchResponse(GetTestJson({GetSnippetN(1)}));
- service->OnSnippetsStatusChanged(SnippetsStatus::ENABLED_AND_SIGNED_IN,
- SnippetsStatus::ENABLED_AND_SIGNED_OUT);
+ service->OnStatusChanged(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN,
+ RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT);
base::RunLoop().RunUntilIdle();
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(1));
// Sign in to check a transition from signed out to signed in.
SetUpFetchResponse(GetTestJson({GetSnippetN(1), GetSnippetN(2)}));
- service->OnSnippetsStatusChanged(SnippetsStatus::ENABLED_AND_SIGNED_OUT,
- SnippetsStatus::ENABLED_AND_SIGNED_IN);
+ service->OnStatusChanged(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT,
+ RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN);
base::RunLoop().RunUntilIdle();
EXPECT_THAT(service->GetSnippetsForTesting(articles_category()), SizeIs(2));
}
-TEST_F(RemoteSuggestionsProviderTest, ShouldClearOrphanedImagesOnRestart) {
+TEST_F(RemoteSuggestionsProviderImplTest, ShouldClearOrphanedImagesOnRestart) {
auto service = MakeSnippetsService();
LoadFromJSONString(service.get(), GetTestJson({GetSnippet()}));
- ServeImageCallback cb = base::Bind(&ServeOneByOneImage, service.get());
+ ServeImageCallback cb =
+ base::Bind(&ServeOneByOneImage, &service->GetImageFetcherForTesting());
EXPECT_CALL(*image_fetcher(), StartOrQueueNetworkRequest(_, _, _))
.WillOnce(WithArgs<0, 2>(Invoke(&cb, &ServeImageCallback::Run)));
@@ -1571,12 +1674,12 @@ TEST_F(RemoteSuggestionsProviderTest, ShouldClearOrphanedImagesOnRestart) {
"http://something.com/pletely/unrelated")}));
// The image should still be available until a restart happens.
EXPECT_FALSE(FetchImage(service.get(), MakeArticleID(kSnippetUrl)).IsEmpty());
- ResetSnippetsService(&service);
+ ResetSnippetsService(&service, /*set_empty_response=*/true);
// After the restart, the image should be garbage collected.
EXPECT_TRUE(FetchImage(service.get(), MakeArticleID(kSnippetUrl)).IsEmpty());
}
-TEST_F(RemoteSuggestionsProviderTest,
+TEST_F(RemoteSuggestionsProviderImplTest,
ShouldHandleMoreThanMaxSnippetsInResponse) {
auto service = MakeSnippetsService();
@@ -1592,4 +1695,121 @@ TEST_F(RemoteSuggestionsProviderTest,
SizeIs(service->GetMaxSnippetCountForTesting() + 1));
}
+TEST_F(RemoteSuggestionsProviderImplTest,
+ StoreLastSuccessfullBackgroundFetchTime) {
+ // On initialization of the RemoteSuggestionsProviderImpl a background fetch
+ // is triggered since the snippets DB is empty. Therefore the service must not
+ // be initialized until the test clock is set.
+ auto service = MakeSnippetsServiceWithoutInitialization();
+
+ 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));
+
+ // Test that the preference is correctly initialized with the default value 0.
+ EXPECT_EQ(
+ 0, pref_service()->GetInt64(prefs::kLastSuccessfulBackgroundFetchTime));
+
+ WaitForSnippetsServiceInitialization(service.get(),
+ /*set_empty_response=*/true);
+ 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));
+
+ service->RefetchInTheBackground(/*callback=*/nullptr);
+ base::RunLoop().RunUntilIdle();
+ // TODO(jkrcal): Move together with the pref storage into the scheduler.
+ EXPECT_EQ(
+ simple_test_clock_ptr->Now().ToInternalValue(),
+ pref_service()->GetInt64(prefs::kLastSuccessfulBackgroundFetchTime));
+ // TODO(markusheintz): Add a test that simulates a browser restart once the
+ // scheduler refactoring is done (crbug.com/672434).
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest, CallsProviderStatusCallbackIfReady) {
+ // Initiate the service so that it is already READY.
+ auto service = MakeSnippetsService();
+
+ StrictMock<MockFunction<void(RemoteSuggestionsProvider::ProviderStatus)>>
+ status_callback;
+ // The callback should be called on registering.
+ EXPECT_CALL(status_callback,
+ Call(RemoteSuggestionsProvider::ProviderStatus::ACTIVE));
+ service->SetProviderStatusCallback(
+ base::MakeUnique<RemoteSuggestionsProvider::ProviderStatusCallback>(
+ base::Bind(&MockFunction<void(
+ RemoteSuggestionsProvider::ProviderStatus)>::Call,
+ base::Unretained(&status_callback))));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ DoesNotCallProviderStatusCallbackIfNotInited) {
+ auto service = MakeSnippetsServiceWithoutInitialization();
+
+ StrictMock<MockFunction<void(RemoteSuggestionsProvider::ProviderStatus)>>
+ status_callback;
+ // The provider is not initialized yet, no callback should be called on
+ // registering.
+ service->SetProviderStatusCallback(
+ base::MakeUnique<RemoteSuggestionsProvider::ProviderStatusCallback>(
+ base::Bind(&MockFunction<void(
+ RemoteSuggestionsProvider::ProviderStatus)>::Call,
+ base::Unretained(&status_callback))));
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ CallsProviderStatusCallbackWhenReady) {
+ auto service = MakeSnippetsServiceWithoutInitialization();
+ StrictMock<MockFunction<void(RemoteSuggestionsProvider::ProviderStatus)>>
+ status_callback;
+ service->SetProviderStatusCallback(
+ base::MakeUnique<RemoteSuggestionsProvider::ProviderStatusCallback>(
+ base::Bind(&MockFunction<void(
+ RemoteSuggestionsProvider::ProviderStatus)>::Call,
+ base::Unretained(&status_callback))));
+
+ // Should be called when becoming ready.
+ EXPECT_CALL(status_callback,
+ Call(RemoteSuggestionsProvider::ProviderStatus::ACTIVE));
+ WaitForSnippetsServiceInitialization(service.get(),
+ /*set_empty_response=*/true);
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest, CallsProviderStatusCallbackOnError) {
+ auto service = MakeSnippetsServiceWithoutInitialization();
+ StrictMock<MockFunction<void(RemoteSuggestionsProvider::ProviderStatus)>>
+ status_callback;
+ service->SetProviderStatusCallback(
+ base::MakeUnique<RemoteSuggestionsProvider::ProviderStatusCallback>(
+ base::Bind(&MockFunction<void(
+ RemoteSuggestionsProvider::ProviderStatus)>::Call,
+ base::Unretained(&status_callback))));
+
+ // Should be called on error.
+ EXPECT_CALL(status_callback,
+ Call(RemoteSuggestionsProvider::ProviderStatus::INACTIVE));
+ service->EnterState(RemoteSuggestionsProviderImpl::State::ERROR_OCCURRED);
+}
+
+TEST_F(RemoteSuggestionsProviderImplTest,
+ CallsProviderStatusCallbackWhenDisabled) {
+ auto service = MakeSnippetsServiceWithoutInitialization();
+ StrictMock<MockFunction<void(RemoteSuggestionsProvider::ProviderStatus)>>
+ status_callback;
+ service->SetProviderStatusCallback(
+ base::MakeUnique<RemoteSuggestionsProvider::ProviderStatusCallback>(
+ base::Bind(&MockFunction<void(
+ RemoteSuggestionsProvider::ProviderStatus)>::Call,
+ base::Unretained(&status_callback))));
+
+ // Should be called when becoming disabled.
+ EXPECT_CALL(status_callback,
+ Call(RemoteSuggestionsProvider::ProviderStatus::INACTIVE));
+ service->EnterState(RemoteSuggestionsProviderImpl::State::DISABLED);
+}
+
} // 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
new file mode 100644
index 00000000000..7621161a824
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler.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_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_SCHEDULER_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_SCHEDULER_H_
+
+#include "base/macros.h"
+
+namespace ntp_snippets {
+
+// Interface for informing the scheduler.
+class RemoteSuggestionsScheduler {
+ public:
+ // External triggers to consider fetching content suggestions.
+
+ // Called whenever chrome is started warm or the user switches to Chrome.
+ virtual void OnBrowserForegrounded() = 0;
+
+ // Called whenever chrome is cold started.
+ // 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;
+
+ // Fetch content suggestions.
+ virtual void OnPersistentSchedulerWakeUp() = 0;
+
+ // Force rescheduling of fetching.
+ virtual void RescheduleFetching() = 0;
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_SCHEDULER_H_
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_status_service.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_status_service.cc
new file mode 100644
index 00000000000..315cf13b691
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_status_service.cc
@@ -0,0 +1,113 @@
+// 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_status_service.h"
+
+#include <string>
+
+#include "components/ntp_snippets/features.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/signin_manager.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace ntp_snippets {
+
+namespace {
+
+const char kFetchingRequiresSignin[] = "fetching_requires_signin";
+const char kFetchingRequiresSigninEnabled[] = "true";
+const char kFetchingRequiresSigninDisabled[] = "false";
+
+} // namespace
+
+RemoteSuggestionsStatusService::RemoteSuggestionsStatusService(
+ SigninManagerBase* signin_manager,
+ PrefService* pref_service)
+ : status_(RemoteSuggestionsStatus::EXPLICITLY_DISABLED),
+ require_signin_(false),
+ signin_manager_(signin_manager),
+ pref_service_(pref_service) {
+ std::string param_value_str = variations::GetVariationParamValueByFeature(
+ kArticleSuggestionsFeature, kFetchingRequiresSignin);
+ if (param_value_str == kFetchingRequiresSigninEnabled) {
+ require_signin_ = true;
+ } else if (!param_value_str.empty() &&
+ param_value_str != kFetchingRequiresSigninDisabled) {
+ DLOG(WARNING) << "Unknow value for the variations parameter "
+ << kFetchingRequiresSignin << ": " << param_value_str;
+ }
+}
+
+RemoteSuggestionsStatusService::~RemoteSuggestionsStatusService() = default;
+
+// static
+void RemoteSuggestionsStatusService::RegisterProfilePrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(prefs::kEnableSnippets, true);
+}
+
+void RemoteSuggestionsStatusService::Init(
+ const StatusChangeCallback& callback) {
+ DCHECK(status_change_callback_.is_null());
+
+ status_change_callback_ = callback;
+
+ // Notify about the current state before registering the observer, to make
+ // sure we don't get a double notification due to an undefined start state.
+ RemoteSuggestionsStatus old_status = status_;
+ status_ = GetStatusFromDeps();
+ status_change_callback_.Run(old_status, status_);
+
+ pref_change_registrar_.Init(pref_service_);
+ pref_change_registrar_.Add(
+ prefs::kEnableSnippets,
+ base::Bind(&RemoteSuggestionsStatusService::OnSnippetsEnabledChanged,
+ base::Unretained(this)));
+}
+
+void RemoteSuggestionsStatusService::OnSnippetsEnabledChanged() {
+ OnStateChanged(GetStatusFromDeps());
+}
+
+void RemoteSuggestionsStatusService::OnStateChanged(
+ RemoteSuggestionsStatus new_status) {
+ if (new_status == status_) {
+ return;
+ }
+
+ status_change_callback_.Run(status_, new_status);
+ status_ = new_status;
+}
+
+bool RemoteSuggestionsStatusService::IsSignedIn() const {
+ // TODO(dgn): remove the SigninManager dependency. It should be possible to
+ // replace it by passing the new state via OnSignInStateChanged().
+ return signin_manager_ && signin_manager_->IsAuthenticated();
+}
+
+void RemoteSuggestionsStatusService::OnSignInStateChanged() {
+ OnStateChanged(GetStatusFromDeps());
+}
+
+RemoteSuggestionsStatus RemoteSuggestionsStatusService::GetStatusFromDeps()
+ const {
+ if (!pref_service_->GetBoolean(prefs::kEnableSnippets)) {
+ DVLOG(1) << "[GetStatusFromDeps] Disabled via pref";
+ return RemoteSuggestionsStatus::EXPLICITLY_DISABLED;
+ }
+
+ if (require_signin_ && !IsSignedIn()) {
+ DVLOG(1) << "[GetStatusFromDeps] Signed out and disabled due to this.";
+ return RemoteSuggestionsStatus::SIGNED_OUT_AND_DISABLED;
+ }
+
+ DVLOG(1) << "[GetStatusFromDeps] Enabled, signed "
+ << (IsSignedIn() ? "in" : "out");
+ return IsSignedIn() ? RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN
+ : RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT;
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_status_service.h b/chromium/components/ntp_snippets/remote/remote_suggestions_status_service.h
new file mode 100644
index 00000000000..e0542bb45e2
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_status_service.h
@@ -0,0 +1,85 @@
+// 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_REMOTE_SUGGESTIONS_STATUS_SERVICE_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_STATUS_SERVICE_H_
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/scoped_observer.h"
+#include "components/prefs/pref_change_registrar.h"
+
+class PrefRegistrySimple;
+class PrefService;
+class SigninManagerBase;
+
+namespace ntp_snippets {
+
+enum class RemoteSuggestionsStatus : int {
+ // Suggestions are enabled and the user is signed in.
+ ENABLED_AND_SIGNED_IN,
+ // Suggestions are enabled; the user is signed out (sign-in is not required).
+ ENABLED_AND_SIGNED_OUT,
+ // Suggestions have been disabled as part of the service configuration.
+ EXPLICITLY_DISABLED,
+ // The user is not signed in, but sign-in is required.
+ SIGNED_OUT_AND_DISABLED,
+};
+
+// Aggregates data from preferences and signin to notify the provider of
+// relevant changes in their states.
+class RemoteSuggestionsStatusService {
+ public:
+ using StatusChangeCallback =
+ base::Callback<void(RemoteSuggestionsStatus old_status,
+ RemoteSuggestionsStatus new_status)>;
+
+ RemoteSuggestionsStatusService(SigninManagerBase* signin_manager,
+ PrefService* pref_service);
+
+ virtual ~RemoteSuggestionsStatusService();
+
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ // Starts listening for changes from the dependencies. |callback| will be
+ // called when a significant change in state is detected.
+ void Init(const StatusChangeCallback& callback);
+
+ // To be called when the signin state changed. Will compute the new
+ // state considering the initialisation configuration and the preferences,
+ // and notify via the registered callback if appropriate.
+ void OnSignInStateChanged();
+
+ private:
+ // TODO(jkrcal): Rewrite the tests using the public API - observing status
+ // changes instead of calling private GetStatusFromDeps() directly.
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsStatusServiceTest,
+ SigninNeededIfSpecifiedByParam);
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsStatusServiceTest, NoSigninNeeded);
+ FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsStatusServiceTest, DisabledViaPref);
+
+ // Callback for the PrefChangeRegistrar.
+ void OnSnippetsEnabledChanged();
+
+ void OnStateChanged(RemoteSuggestionsStatus new_status);
+
+ bool IsSignedIn() const;
+
+ RemoteSuggestionsStatus GetStatusFromDeps() const;
+
+ RemoteSuggestionsStatus status_;
+ StatusChangeCallback status_change_callback_;
+
+ bool require_signin_;
+ SigninManagerBase* signin_manager_;
+ PrefService* pref_service_;
+
+ PrefChangeRegistrar pref_change_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsStatusService);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_STATUS_SERVICE_H_
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
new file mode 100644
index 00000000000..c24650af8cf
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_status_service_unittest.cc
@@ -0,0 +1,92 @@
+// 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_status_service.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.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/variations/variations_params_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp_snippets {
+
+class RemoteSuggestionsStatusServiceTest : public ::testing::Test {
+ public:
+ RemoteSuggestionsStatusServiceTest() {
+ RemoteSuggestionsStatusService::RegisterProfilePrefs(
+ utils_.pref_service()->registry());
+ }
+
+ std::unique_ptr<RemoteSuggestionsStatusService> MakeService() {
+ return base::MakeUnique<RemoteSuggestionsStatusService>(
+ utils_.fake_signin_manager(), utils_.pref_service());
+ }
+
+ protected:
+ test::RemoteSuggestionsTestUtils utils_;
+ variations::testing::VariationParamsManager params_manager_;
+};
+
+TEST_F(RemoteSuggestionsStatusServiceTest, SigninNeededIfSpecifiedByParam) {
+ // Specify by the parameter that signin is required.
+ params_manager_.SetVariationParamsWithFeatureAssociations(
+ ntp_snippets::kStudyName, {{"fetching_requires_signin", "true"}},
+ {ntp_snippets::kArticleSuggestionsFeature.name});
+
+ auto service = MakeService();
+
+ // The default test setup is signed out.
+ EXPECT_EQ(RemoteSuggestionsStatus::SIGNED_OUT_AND_DISABLED,
+ service->GetStatusFromDeps());
+
+ // Once signed in, we should be in a compatible state.
+ utils_.fake_signin_manager()->SignIn("foo@bar.com");
+ EXPECT_EQ(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN,
+ service->GetStatusFromDeps());
+}
+
+TEST_F(RemoteSuggestionsStatusServiceTest, NoSigninNeeded) {
+ auto service = MakeService();
+
+ // By default, no signin is required.
+ EXPECT_EQ(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT,
+ service->GetStatusFromDeps());
+
+ // One can still sign in.
+ utils_.fake_signin_manager()->SignIn("foo@bar.com");
+ EXPECT_EQ(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_IN,
+ service->GetStatusFromDeps());
+}
+
+TEST_F(RemoteSuggestionsStatusServiceTest, DisabledViaPref) {
+ auto service = MakeService();
+
+ // The default test setup is signed out. The service is enabled.
+ ASSERT_EQ(RemoteSuggestionsStatus::ENABLED_AND_SIGNED_OUT,
+ service->GetStatusFromDeps());
+
+ // Once the enabled pref is set to false, we should be disabled.
+ utils_.pref_service()->SetBoolean(prefs::kEnableSnippets, false);
+ EXPECT_EQ(RemoteSuggestionsStatus::EXPLICITLY_DISABLED,
+ service->GetStatusFromDeps());
+
+ // The other dependencies shouldn't matter anymore.
+ utils_.fake_signin_manager()->SignIn("foo@bar.com");
+ EXPECT_EQ(RemoteSuggestionsStatus::EXPLICITLY_DISABLED,
+ service->GetStatusFromDeps());
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/request_params.cc b/chromium/components/ntp_snippets/remote/request_params.cc
new file mode 100644
index 00000000000..b661a90e810
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/request_params.cc
@@ -0,0 +1,13 @@
+// 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/request_params.h"
+
+namespace ntp_snippets {
+
+RequestParams::RequestParams() = default;
+RequestParams::RequestParams(const RequestParams&) = default;
+RequestParams::~RequestParams() = default;
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/request_params.h b/chromium/components/ntp_snippets/remote/request_params.h
new file mode 100644
index 00000000000..02289dd3143
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/request_params.h
@@ -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.
+
+#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_REQUEST_PARAMS_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_REQUEST_PARAMS_H_
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/callback.h"
+#include "base/optional.h"
+#include "base/values.h"
+#include "components/ntp_snippets/category.h"
+
+namespace ntp_snippets {
+
+// Enumeration listing all possible variants of dealing with personalization.
+enum class Personalization { kPersonal, kNonPersonal, kBoth };
+
+// Contains all parameters for fetching remote suggestions.
+struct RequestParams {
+ RequestParams();
+ RequestParams(const RequestParams&);
+ ~RequestParams();
+
+ // BCP 47 language code specifying the user's UI language.
+ std::string language_code;
+
+ // A set of suggestion IDs that should not be returned again.
+ std::set<std::string> excluded_ids;
+
+ // Maximum number of snippets to fetch.
+ int count_to_fetch = 0;
+
+ // Whether this is an interactive request, i.e. triggered by an explicit
+ // user action. Typically, non-interactive requests are subject to a daily
+ // quota.
+ bool interactive_request = false;
+
+ // If set, only return results for this category.
+ base::Optional<Category> exclusive_category;
+};
+
+// 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)>;
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_REQUEST_PARAMS_H_
diff --git a/chromium/components/ntp_snippets/remote/request_throttler.cc b/chromium/components/ntp_snippets/remote/request_throttler.cc
index f1b06bd428f..ae78103892b 100644
--- a/chromium/components/ntp_snippets/remote/request_throttler.cc
+++ b/chromium/components/ntp_snippets/remote/request_throttler.cc
@@ -128,8 +128,9 @@ void RequestThrottler::RegisterProfilePrefs(PrefRegistrySimple* registry) {
keys_to_register.insert(info.interactive_count_pref);
}
- for (const std::string& key : keys_to_register)
+ for (const std::string& key : keys_to_register) {
registry->RegisterIntegerPref(key, 0);
+ }
}
bool RequestThrottler::DemandQuotaForRequest(bool interactive_request) {
diff --git a/chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc b/chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc
new file mode 100644
index 00000000000..0b79eac771a
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc
@@ -0,0 +1,517 @@
+// 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/scheduling_remote_suggestions_provider.h"
+
+#include <random>
+#include <string>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_split.h"
+#include "base/time/clock.h"
+#include "components/ntp_snippets/features.h"
+#include "components/ntp_snippets/pref_names.h"
+#include "components/ntp_snippets/remote/persistent_scheduler.h"
+#include "components/ntp_snippets/status.h"
+#include "components/ntp_snippets/user_classifier.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace ntp_snippets {
+
+namespace {
+
+enum class FetchingInterval {
+ PERSISTENT_FALLBACK,
+ PERSISTENT_WIFI,
+ SOFT_ON_USAGE_EVENT,
+ COUNT
+};
+
+// Default values for fetching intervals, fallback and wifi.
+const double kDefaultFetchingIntervalRareNtpUser[] = {48.0, 24.0, 12.0};
+const double kDefaultFetchingIntervalActiveNtpUser[] = {24.0, 6.0, 2.0};
+const double kDefaultFetchingIntervalActiveSuggestionsConsumer[] = {24.0, 6.0,
+ 2.0};
+
+// Variation parameters than can the default fetching intervals.
+const char* kFetchingIntervalParamNameRareNtpUser[] = {
+ "fetching_interval_hours-fallback-rare_ntp_user",
+ "fetching_interval_hours-wifi-rare_ntp_user",
+ "soft_fetching_interval_hours-active-rare_ntp_user"};
+const char* kFetchingIntervalParamNameActiveNtpUser[] = {
+ "fetching_interval_hours-fallback-active_ntp_user",
+ "fetching_interval_hours-wifi-active_ntp_user",
+ "soft_fetching_interval_hours-active-active_ntp_user"};
+const char* kFetchingIntervalParamNameActiveSuggestionsConsumer[] = {
+ "fetching_interval_hours-fallback-active_suggestions_consumer",
+ "fetching_interval_hours-wifi-active_suggestions_consumer",
+ "soft_fetching_interval_hours-active-active_suggestions_consumer"};
+
+static_assert(
+ static_cast<unsigned int>(FetchingInterval::COUNT) ==
+ arraysize(kDefaultFetchingIntervalRareNtpUser) &&
+ static_cast<unsigned int>(FetchingInterval::COUNT) ==
+ arraysize(kDefaultFetchingIntervalActiveNtpUser) &&
+ static_cast<unsigned int>(FetchingInterval::COUNT) ==
+ arraysize(kDefaultFetchingIntervalActiveSuggestionsConsumer) &&
+ static_cast<unsigned int>(FetchingInterval::COUNT) ==
+ arraysize(kFetchingIntervalParamNameRareNtpUser) &&
+ static_cast<unsigned int>(FetchingInterval::COUNT) ==
+ arraysize(kFetchingIntervalParamNameActiveNtpUser) &&
+ static_cast<unsigned int>(FetchingInterval::COUNT) ==
+ arraysize(kFetchingIntervalParamNameActiveSuggestionsConsumer),
+ "Fill in all the info for fetching intervals.");
+
+const char* kTriggerTypeNames[] = {"persistent_scheduler_wake_up", "ntp_opened",
+ "browser_foregrounded",
+ "browser_cold_start"};
+
+const char* kTriggerTypesParamName = "scheduler_trigger_types";
+const char* kTriggerTypesParamValueForEmptyList = "-";
+
+base::TimeDelta GetDesiredFetchingInterval(
+ FetchingInterval interval,
+ UserClassifier::UserClass user_class) {
+ double default_value_hours = 0.0;
+
+ DCHECK(interval != FetchingInterval::COUNT);
+ const unsigned int index = static_cast<unsigned int>(interval);
+ DCHECK(index < arraysize(kDefaultFetchingIntervalRareNtpUser));
+
+ const char* param_name = nullptr;
+ switch (user_class) {
+ case UserClassifier::UserClass::RARE_NTP_USER:
+ default_value_hours = kDefaultFetchingIntervalRareNtpUser[index];
+ param_name = kFetchingIntervalParamNameRareNtpUser[index];
+ break;
+ case UserClassifier::UserClass::ACTIVE_NTP_USER:
+ default_value_hours = kDefaultFetchingIntervalActiveNtpUser[index];
+ param_name = kFetchingIntervalParamNameActiveNtpUser[index];
+ break;
+ case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
+ default_value_hours =
+ kDefaultFetchingIntervalActiveSuggestionsConsumer[index];
+ param_name = kFetchingIntervalParamNameActiveSuggestionsConsumer[index];
+ break;
+ }
+
+ double value_hours = variations::GetVariationParamByFeatureAsDouble(
+ ntp_snippets::kArticleSuggestionsFeature, param_name,
+ default_value_hours);
+
+ return base::TimeDelta::FromSecondsD(value_hours * 3600.0);
+}
+
+} // namespace
+
+// static
+SchedulingRemoteSuggestionsProvider::FetchingSchedule
+SchedulingRemoteSuggestionsProvider::FetchingSchedule::Empty() {
+ return FetchingSchedule{base::TimeDelta(), base::TimeDelta(),
+ base::TimeDelta()};
+}
+
+bool SchedulingRemoteSuggestionsProvider::FetchingSchedule::operator==(
+ const FetchingSchedule& other) const {
+ return interval_persistent_wifi == other.interval_persistent_wifi &&
+ interval_persistent_fallback == other.interval_persistent_fallback &&
+ interval_soft_on_usage_event == other.interval_soft_on_usage_event;
+}
+
+bool SchedulingRemoteSuggestionsProvider::FetchingSchedule::operator!=(
+ const FetchingSchedule& other) const {
+ return !operator==(other);
+}
+
+bool SchedulingRemoteSuggestionsProvider::FetchingSchedule::is_empty() const {
+ return interval_persistent_wifi.is_zero() &&
+ interval_persistent_fallback.is_zero() &&
+ interval_soft_on_usage_event.is_zero();
+}
+
+// These values are written to logs. New enum values can be added, but existing
+// enums must never be renumbered or deleted and reused. When adding new
+// entries, also update the array |kTriggerTypeNames| above.
+enum class SchedulingRemoteSuggestionsProvider::TriggerType {
+ PERSISTENT_SCHEDULER_WAKE_UP = 0,
+ NTP_OPENED = 1,
+ BROWSER_FOREGROUNDED = 2,
+ BROWSER_COLD_START = 3,
+ COUNT
+};
+
+SchedulingRemoteSuggestionsProvider::SchedulingRemoteSuggestionsProvider(
+ Observer* observer,
+ std::unique_ptr<RemoteSuggestionsProvider> provider,
+ PersistentScheduler* persistent_scheduler,
+ const UserClassifier* user_classifier,
+ PrefService* pref_service,
+ std::unique_ptr<base::Clock> clock)
+ : RemoteSuggestionsProvider(observer),
+ RemoteSuggestionsScheduler(),
+ provider_(std::move(provider)),
+ persistent_scheduler_(persistent_scheduler),
+ background_fetch_in_progress_(false),
+ user_classifier_(user_classifier),
+ pref_service_(pref_service),
+ clock_(std::move(clock)),
+ enabled_triggers_(GetEnabledTriggerTypes()) {
+ DCHECK(user_classifier);
+ DCHECK(pref_service);
+
+ LoadLastFetchingSchedule();
+
+ provider_->SetProviderStatusCallback(
+ base::MakeUnique<RemoteSuggestionsProvider::ProviderStatusCallback>(
+ base::BindRepeating(
+ &SchedulingRemoteSuggestionsProvider::OnProviderStatusChanged,
+ base::Unretained(this))));
+}
+
+SchedulingRemoteSuggestionsProvider::~SchedulingRemoteSuggestionsProvider() =
+ default;
+
+// static
+void SchedulingRemoteSuggestionsProvider::RegisterProfilePrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalWifi, 0);
+ registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalFallback,
+ 0);
+ registry->RegisterInt64Pref(prefs::kSnippetSoftFetchingIntervalOnUsageEvent,
+ 0);
+ registry->RegisterInt64Pref(prefs::kSnippetLastFetchAttempt, 0);
+}
+
+void SchedulingRemoteSuggestionsProvider::RescheduleFetching() {
+ // Force the reschedule by stopping and starting it again.
+ StopScheduling();
+ StartScheduling();
+}
+
+void SchedulingRemoteSuggestionsProvider::OnPersistentSchedulerWakeUp() {
+ RefetchInTheBackgroundIfEnabled(TriggerType::PERSISTENT_SCHEDULER_WAKE_UP);
+}
+
+void SchedulingRemoteSuggestionsProvider::OnBrowserForegrounded() {
+ // TODO(jkrcal): Consider that this is called whenever we open or return to an
+ // Activity. Therefore, keep work light for fast start up calls.
+ if (!ShouldRefetchInTheBackgroundNow()) {
+ return;
+ }
+
+ RefetchInTheBackgroundIfEnabled(TriggerType::BROWSER_FOREGROUNDED);
+}
+
+void SchedulingRemoteSuggestionsProvider::OnBrowserColdStart() {
+ // TODO(fhorschig|jkrcal): Consider that work here must be kept light for fast
+ // cold start ups.
+ if (!ShouldRefetchInTheBackgroundNow()) {
+ return;
+ }
+
+ RefetchInTheBackgroundIfEnabled(TriggerType::BROWSER_COLD_START);
+}
+
+void SchedulingRemoteSuggestionsProvider::OnNTPOpened() {
+ if (!ShouldRefetchInTheBackgroundNow()) {
+ return;
+ }
+
+ RefetchInTheBackgroundIfEnabled(TriggerType::NTP_OPENED);
+}
+
+void SchedulingRemoteSuggestionsProvider::SetProviderStatusCallback(
+ std::unique_ptr<ProviderStatusCallback> callback) {
+ provider_->SetProviderStatusCallback(std::move(callback));
+}
+
+void SchedulingRemoteSuggestionsProvider::RefetchInTheBackground(
+ std::unique_ptr<FetchStatusCallback> callback) {
+ if (background_fetch_in_progress_) {
+ if (callback) {
+ callback->Run(
+ Status(StatusCode::TEMPORARY_ERROR, "Background fetch in progress"));
+ }
+ return;
+ }
+
+ background_fetch_in_progress_ = true;
+ RemoteSuggestionsProvider::FetchStatusCallback wrapper_callback = base::Bind(
+ &SchedulingRemoteSuggestionsProvider::RefetchInTheBackgroundFinished,
+ base::Unretained(this), base::Passed(&callback));
+ provider_->RefetchInTheBackground(
+ base::MakeUnique<RemoteSuggestionsProvider::FetchStatusCallback>(
+ std::move(wrapper_callback)));
+}
+
+const RemoteSuggestionsFetcher*
+SchedulingRemoteSuggestionsProvider::suggestions_fetcher_for_debugging() const {
+ return provider_->suggestions_fetcher_for_debugging();
+}
+
+CategoryStatus SchedulingRemoteSuggestionsProvider::GetCategoryStatus(
+ Category category) {
+ return provider_->GetCategoryStatus(category);
+}
+
+CategoryInfo SchedulingRemoteSuggestionsProvider::GetCategoryInfo(
+ Category category) {
+ return provider_->GetCategoryInfo(category);
+}
+
+void SchedulingRemoteSuggestionsProvider::DismissSuggestion(
+ const ContentSuggestion::ID& suggestion_id) {
+ provider_->DismissSuggestion(suggestion_id);
+}
+
+void SchedulingRemoteSuggestionsProvider::FetchSuggestionImage(
+ const ContentSuggestion::ID& suggestion_id,
+ const ImageFetchedCallback& callback) {
+ provider_->FetchSuggestionImage(suggestion_id, callback);
+}
+
+void SchedulingRemoteSuggestionsProvider::Fetch(
+ const Category& category,
+ const std::set<std::string>& known_suggestion_ids,
+ const FetchDoneCallback& callback) {
+ provider_->Fetch(
+ category, known_suggestion_ids,
+ base::Bind(&SchedulingRemoteSuggestionsProvider::FetchFinished,
+ base::Unretained(this), callback));
+}
+
+void SchedulingRemoteSuggestionsProvider::ReloadSuggestions() {
+ provider_->ReloadSuggestions();
+}
+
+void SchedulingRemoteSuggestionsProvider::ClearHistory(
+ base::Time begin,
+ base::Time end,
+ const base::Callback<bool(const GURL& url)>& filter) {
+ provider_->ClearHistory(begin, end, filter);
+}
+
+void SchedulingRemoteSuggestionsProvider::ClearCachedSuggestions(
+ Category category) {
+ provider_->ClearCachedSuggestions(category);
+}
+
+void SchedulingRemoteSuggestionsProvider::OnSignInStateChanged() {
+ provider_->OnSignInStateChanged();
+}
+
+void SchedulingRemoteSuggestionsProvider::GetDismissedSuggestionsForDebugging(
+ Category category,
+ const DismissedSuggestionsCallback& callback) {
+ provider_->GetDismissedSuggestionsForDebugging(category, callback);
+}
+
+void SchedulingRemoteSuggestionsProvider::ClearDismissedSuggestionsForDebugging(
+ Category category) {
+ provider_->ClearDismissedSuggestionsForDebugging(category);
+}
+
+void SchedulingRemoteSuggestionsProvider::OnProviderStatusChanged(
+ RemoteSuggestionsProvider::ProviderStatus status) {
+ switch (status) {
+ case RemoteSuggestionsProvider::ProviderStatus::ACTIVE:
+ StartScheduling();
+ return;
+ case RemoteSuggestionsProvider::ProviderStatus::INACTIVE:
+ StopScheduling();
+ return;
+ }
+ NOTREACHED();
+}
+
+void SchedulingRemoteSuggestionsProvider::StartScheduling() {
+ FetchingSchedule new_schedule = GetDesiredFetchingSchedule();
+
+ if (schedule_ == new_schedule) {
+ // Do not schedule if nothing has changed;
+ return;
+ }
+
+ schedule_ = new_schedule;
+ StoreFetchingSchedule();
+ ApplyPersistentFetchingSchedule();
+}
+
+void SchedulingRemoteSuggestionsProvider::StopScheduling() {
+ if (schedule_.is_empty()) {
+ // Do not unschedule if already switched off.
+ return;
+ }
+
+ schedule_ = FetchingSchedule::Empty();
+ StoreFetchingSchedule();
+ ApplyPersistentFetchingSchedule();
+}
+
+void SchedulingRemoteSuggestionsProvider::ApplyPersistentFetchingSchedule() {
+ // The scheduler only exists on Android so far, it's null on other platforms.
+ if (persistent_scheduler_) {
+ if (schedule_.is_empty()) {
+ persistent_scheduler_->Unschedule();
+ } else {
+ persistent_scheduler_->Schedule(schedule_.interval_persistent_wifi,
+ schedule_.interval_persistent_fallback);
+ }
+ }
+}
+
+SchedulingRemoteSuggestionsProvider::FetchingSchedule
+SchedulingRemoteSuggestionsProvider::GetDesiredFetchingSchedule() const {
+ UserClassifier::UserClass user_class = user_classifier_->GetUserClass();
+
+ FetchingSchedule schedule;
+ schedule.interval_persistent_wifi =
+ GetDesiredFetchingInterval(FetchingInterval::PERSISTENT_WIFI, user_class);
+ schedule.interval_persistent_fallback = GetDesiredFetchingInterval(
+ FetchingInterval::PERSISTENT_FALLBACK, user_class);
+ schedule.interval_soft_on_usage_event = GetDesiredFetchingInterval(
+ FetchingInterval::SOFT_ON_USAGE_EVENT, user_class);
+
+ return schedule;
+}
+
+void SchedulingRemoteSuggestionsProvider::LoadLastFetchingSchedule() {
+ schedule_.interval_persistent_wifi = base::TimeDelta::FromInternalValue(
+ pref_service_->GetInt64(prefs::kSnippetPersistentFetchingIntervalWifi));
+ schedule_.interval_persistent_fallback =
+ base::TimeDelta::FromInternalValue(pref_service_->GetInt64(
+ prefs::kSnippetPersistentFetchingIntervalFallback));
+ schedule_.interval_soft_on_usage_event = base::TimeDelta::FromInternalValue(
+ pref_service_->GetInt64(prefs::kSnippetSoftFetchingIntervalOnUsageEvent));
+}
+
+void SchedulingRemoteSuggestionsProvider::StoreFetchingSchedule() {
+ pref_service_->SetInt64(prefs::kSnippetPersistentFetchingIntervalWifi,
+ schedule_.interval_persistent_wifi.ToInternalValue());
+ pref_service_->SetInt64(
+ prefs::kSnippetPersistentFetchingIntervalFallback,
+ schedule_.interval_persistent_fallback.ToInternalValue());
+ pref_service_->SetInt64(
+ prefs::kSnippetSoftFetchingIntervalOnUsageEvent,
+ schedule_.interval_soft_on_usage_event.ToInternalValue());
+}
+
+void SchedulingRemoteSuggestionsProvider::RefetchInTheBackgroundIfEnabled(
+ SchedulingRemoteSuggestionsProvider::TriggerType trigger) {
+ if (BackgroundFetchesDisabled(trigger)) {
+ return;
+ }
+
+ UMA_HISTOGRAM_ENUMERATION(
+ "NewTabPage.ContentSuggestions.BackgroundFetchTrigger",
+ static_cast<int>(trigger), static_cast<int>(TriggerType::COUNT));
+
+ RefetchInTheBackground(/*callback=*/nullptr);
+}
+
+bool SchedulingRemoteSuggestionsProvider::ShouldRefetchInTheBackgroundNow() {
+ base::Time first_allowed_fetch_time =
+ base::Time::FromInternalValue(
+ pref_service_->GetInt64(prefs::kSnippetLastFetchAttempt)) +
+ schedule_.interval_soft_on_usage_event;
+ return first_allowed_fetch_time <= clock_->Now();
+}
+
+bool SchedulingRemoteSuggestionsProvider::BackgroundFetchesDisabled(
+ SchedulingRemoteSuggestionsProvider::TriggerType trigger) const {
+ if (schedule_.is_empty()) {
+ return true; // Background fetches are disabled in general.
+ }
+
+ if (enabled_triggers_.count(trigger) == 0) {
+ return true; // Background fetches for |trigger| are not enabled.
+ }
+ return false;
+}
+
+void SchedulingRemoteSuggestionsProvider::FetchFinished(
+ const FetchDoneCallback& callback,
+ Status fetch_status,
+ std::vector<ContentSuggestion> suggestions) {
+ OnFetchCompleted(fetch_status);
+ if (callback) {
+ callback.Run(fetch_status, std::move(suggestions));
+ }
+}
+
+void SchedulingRemoteSuggestionsProvider::RefetchInTheBackgroundFinished(
+ std::unique_ptr<FetchStatusCallback> callback,
+ Status fetch_status) {
+ background_fetch_in_progress_ = false;
+ OnFetchCompleted(fetch_status);
+ if (callback) {
+ callback->Run(fetch_status);
+ }
+}
+
+void SchedulingRemoteSuggestionsProvider::OnFetchCompleted(
+ Status fetch_status) {
+ pref_service_->SetInt64(prefs::kSnippetLastFetchAttempt,
+ clock_->Now().ToInternalValue());
+
+ // Reschedule after a fetch. The persistent schedule is applied only after a
+ // successful fetch. After a failed fetch, we want to keep the previous
+ // persistent schedule intact so that we eventually get a persistent
+ // fallback fetch (if the wifi persistent fetches keep failing).
+ if (fetch_status.code != StatusCode::SUCCESS) {
+ return;
+ }
+ ApplyPersistentFetchingSchedule();
+}
+
+std::set<SchedulingRemoteSuggestionsProvider::TriggerType>
+SchedulingRemoteSuggestionsProvider::GetEnabledTriggerTypes() {
+ static_assert(static_cast<unsigned int>(TriggerType::COUNT) ==
+ arraysize(kTriggerTypeNames),
+ "Fill in names for trigger types.");
+
+ std::string param_value = variations::GetVariationParamValueByFeature(
+ ntp_snippets::kArticleSuggestionsFeature, kTriggerTypesParamName);
+ if (param_value == kTriggerTypesParamValueForEmptyList) {
+ return std::set<TriggerType>();
+ }
+
+ std::vector<std::string> tokens = base::SplitString(
+ param_value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (tokens.empty()) {
+ return GetDefaultEnabledTriggerTypes();
+ }
+
+ std::set<TriggerType> enabled_types;
+ for (const auto& token : tokens) {
+ auto it = std::find(std::begin(kTriggerTypeNames),
+ std::end(kTriggerTypeNames), token);
+ if (it == std::end(kTriggerTypeNames)) {
+ DLOG(WARNING) << "Failed to parse variation param "
+ << kTriggerTypesParamName << " with string value "
+ << param_value
+ << " into a comma-separated list of keywords. "
+ << "Unknown token " << token
+ << " found. Falling back to default value.";
+ return GetDefaultEnabledTriggerTypes();
+ }
+
+ // Add the enabled type represented by |token| into the result set.
+ enabled_types.insert(
+ static_cast<TriggerType>(it - std::begin(kTriggerTypeNames)));
+ }
+ return enabled_types;
+}
+
+std::set<SchedulingRemoteSuggestionsProvider::TriggerType>
+SchedulingRemoteSuggestionsProvider::GetDefaultEnabledTriggerTypes() {
+ return {TriggerType::PERSISTENT_SCHEDULER_WAKE_UP, TriggerType::NTP_OPENED,
+ TriggerType::BROWSER_FOREGROUNDED};
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h b/chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h
new file mode 100644
index 00000000000..7a42b470380
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h
@@ -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.
+
+#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_SCHEDULING_REMOTE_SUGGESTIONS_PROVIDER_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_SCHEDULING_REMOTE_SUGGESTIONS_PROVIDER_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "components/ntp_snippets/content_suggestions_provider.h"
+#include "components/ntp_snippets/remote/persistent_scheduler.h"
+#include "components/ntp_snippets/remote/remote_suggestions_provider.h"
+#include "components/ntp_snippets/remote/remote_suggestions_scheduler.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace base {
+class Clock;
+}
+
+namespace ntp_snippets {
+
+struct Status;
+class UserClassifier;
+
+// A wrapper around RemoteSuggestionsProvider that introduces periodic fetching.
+//
+// The class initiates fetches on its own in these situations:
+// - initial fetch when the provider is constructed and we have no suggestions;
+// - regular fetches according to its schedule.
+// TODO(jkrcal): After soft fetch on Chrome startup is introduced, remove
+// the initial fetch completely.
+//
+// The class also needs to understand when last fetch trials and successful
+// fetches happen and thus it intercepts following interactive fetch requests:
+// - Fetch() - after "More" button of a remote section is pressed in the UI;
+// TODO(jkrcal): Clarify what Fetch() should do for this provider and maybe stop
+// intercepting it.
+// TODO(jkrcal): Intercept also ReloadSuggestions() call (after the user swipes
+// away everything incl. all empty sections and presses "More"); Not done in the
+// first shot because it implements a public interface function without any
+// callback.
+// This class is final because it does things in its constructor which make it
+// unsafe to derive from it.
+// TODO(jkrcal): Introduce two-phase initialization and make the class not
+// final? (see the same comment for RemoteSuggestionsProvider)
+class SchedulingRemoteSuggestionsProvider final
+ : public RemoteSuggestionsProvider,
+ public RemoteSuggestionsScheduler {
+ public:
+ SchedulingRemoteSuggestionsProvider(
+ Observer* observer,
+ std::unique_ptr<RemoteSuggestionsProvider> provider,
+ PersistentScheduler* persistent_scheduler,
+ const UserClassifier* user_classifier,
+ PrefService* pref_service,
+ std::unique_ptr<base::Clock> clock);
+
+ ~SchedulingRemoteSuggestionsProvider() override;
+
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ // RemoteSuggestionsScheduler implementation.
+ void RescheduleFetching() override;
+ void OnPersistentSchedulerWakeUp() override;
+ void OnBrowserForegrounded() override;
+ void OnBrowserColdStart() override;
+ void OnNTPOpened() override;
+
+ // RemoteSuggestionsProvider implementation.
+ void SetProviderStatusCallback(
+ std::unique_ptr<ProviderStatusCallback> callback) override;
+ void RefetchInTheBackground(
+ std::unique_ptr<FetchStatusCallback> callback) override;
+ const RemoteSuggestionsFetcher* suggestions_fetcher_for_debugging()
+ const 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 ReloadSuggestions() override;
+ void ClearHistory(
+ base::Time begin,
+ base::Time end,
+ const base::Callback<bool(const GURL& url)>& filter) override;
+ void ClearCachedSuggestions(Category category) override;
+ void OnSignInStateChanged() override;
+ void GetDismissedSuggestionsForDebugging(
+ Category category,
+ const DismissedSuggestionsCallback& callback) override;
+ void ClearDismissedSuggestionsForDebugging(Category category) override;
+
+ private:
+ // Abstract description of the fetching schedule.
+ struct FetchingSchedule {
+ static FetchingSchedule Empty();
+ bool operator==(const FetchingSchedule& other) const;
+ bool operator!=(const FetchingSchedule& other) const;
+ bool is_empty() const;
+
+ base::TimeDelta interval_persistent_wifi;
+ base::TimeDelta interval_persistent_fallback;
+ base::TimeDelta interval_soft_on_usage_event;
+ };
+
+ enum class TriggerType;
+
+ // Callback that is notified whenever the status of |provider_| changes.
+ void OnProviderStatusChanged(
+ RemoteSuggestionsProvider::ProviderStatus status);
+
+ // After the call, updates will be scheduled in the future. Idempotent, can be
+ // run any time later without impacting the current schedule.
+ // If you want to enforce rescheduling, call Unschedule() and then Schedule().
+ void StartScheduling();
+
+ // After the call, no updates will happen before another call to Schedule().
+ // Idempotent, can be run any time later without impacting the current
+ // schedule.
+ void StopScheduling();
+
+ // Trigger a background refetch for the given |trigger| if enabled.
+ void RefetchInTheBackgroundIfEnabled(TriggerType trigger);
+
+ // Checks whether it is time to perform a soft background fetch, according to
+ // |schedule|.
+ bool ShouldRefetchInTheBackgroundNow();
+
+ // Returns whether background fetching (for the given |trigger|) is disabled.
+ bool BackgroundFetchesDisabled(TriggerType trigger) const;
+
+ // Callback after Fetch is completed.
+ void FetchFinished(const FetchDoneCallback& callback,
+ Status fetch_status,
+ std::vector<ContentSuggestion> suggestions);
+
+ // Callback after RefetchInTheBackground is completed.
+ void RefetchInTheBackgroundFinished(
+ std::unique_ptr<FetchStatusCallback> callback,
+ Status fetch_status);
+
+ // Common function to call after a fetch of any type is finished.
+ void OnFetchCompleted(Status fetch_status);
+
+ FetchingSchedule GetDesiredFetchingSchedule() const;
+
+ // Load and store |schedule_|.
+ void LoadLastFetchingSchedule();
+ void StoreFetchingSchedule();
+
+ // Applies the persistent schedule given by |schedule_|.
+ void ApplyPersistentFetchingSchedule();
+
+ // Gets enabled trigger types from the variation parameter.
+ std::set<TriggerType> GetEnabledTriggerTypes();
+
+ // Gets trigger types enabled by default.
+ std::set<TriggerType> GetDefaultEnabledTriggerTypes();
+
+ // Interface for doing all the actual work (apart from scheduling).
+ std::unique_ptr<RemoteSuggestionsProvider> provider_;
+
+ // Interface for scheduling hard fetches, OS dependent. Not owned, may be
+ // null.
+ PersistentScheduler* persistent_scheduler_;
+
+ FetchingSchedule schedule_;
+ bool background_fetch_in_progress_;
+
+ // Used to adapt the schedule based on usage activity of the user. Not owned.
+ const UserClassifier* user_classifier_;
+
+ PrefService* pref_service_;
+ std::unique_ptr<base::Clock> clock_;
+ std::set<SchedulingRemoteSuggestionsProvider::TriggerType> enabled_triggers_;
+
+ DISALLOW_COPY_AND_ASSIGN(SchedulingRemoteSuggestionsProvider);
+};
+
+} // namespace ntp_snippets
+
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_SCHEDULING_REMOTE_SUGGESTIONS_PROVIDER_H_
diff --git a/chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc b/chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc
new file mode 100644
index 00000000000..950923fe11b
--- /dev/null
+++ b/chromium/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc
@@ -0,0 +1,589 @@
+// 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/scheduling_remote_suggestions_provider.h"
+
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/command_line.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/test/simple_test_clock.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
+#include "base/time/time.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/persistent_scheduler.h"
+#include "components/ntp_snippets/remote/remote_suggestions_provider.h"
+#include "components/ntp_snippets/remote/test_utils.h"
+#include "components/ntp_snippets/status.h"
+#include "components/ntp_snippets/user_classifier.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/variations/variations_params_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+using testing::Eq;
+using testing::InSequence;
+using testing::Invoke;
+using testing::IsEmpty;
+using testing::Mock;
+using testing::MockFunction;
+using testing::Not;
+using testing::Return;
+using testing::SaveArg;
+using testing::SaveArgPointee;
+using testing::SizeIs;
+using testing::StartsWith;
+using testing::StrictMock;
+using testing::WithArgs;
+using testing::_;
+
+namespace ntp_snippets {
+
+class RemoteSuggestionsFetcher;
+
+namespace {
+
+class MockPersistentScheduler : public PersistentScheduler {
+ public:
+ MOCK_METHOD2(Schedule,
+ bool(base::TimeDelta period_wifi,
+ base::TimeDelta period_fallback));
+ MOCK_METHOD0(Unschedule, bool());
+};
+
+// TODO(jkrcal): Move into its own library to reuse in other unit-tests?
+class MockRemoteSuggestionsProvider : public RemoteSuggestionsProvider {
+ public:
+ MockRemoteSuggestionsProvider(Observer* observer)
+ : RemoteSuggestionsProvider(observer) {}
+
+ // Move-only params are not supported by GMock. We want to mock out
+ // RefetchInTheBackground() which takes a unique_ptr<>. Instead, we add a new
+ // mock function which takes a copy of the callback and override the
+ // RemoteSuggestionsProvider's method to forward the call into the new mock
+ // function.
+ void SetProviderStatusCallback(
+ std::unique_ptr<RemoteSuggestionsProvider::ProviderStatusCallback>
+ callback) override {
+ SetProviderStatusCallback(*callback);
+ }
+ MOCK_METHOD1(SetProviderStatusCallback,
+ void(RemoteSuggestionsProvider::ProviderStatusCallback));
+
+ // Move-only params are not supported by GMock (same work-around as above).
+ void RefetchInTheBackground(
+ std::unique_ptr<RemoteSuggestionsProvider::FetchStatusCallback> callback)
+ override {
+ RefetchInTheBackground(*callback);
+ }
+ MOCK_METHOD1(RefetchInTheBackground,
+ void(RemoteSuggestionsProvider::FetchStatusCallback));
+
+ MOCK_CONST_METHOD0(suggestions_fetcher_for_debugging,
+ const RemoteSuggestionsFetcher*());
+
+ MOCK_METHOD1(GetCategoryStatus, CategoryStatus(Category));
+ MOCK_METHOD1(GetCategoryInfo, CategoryInfo(Category));
+ MOCK_METHOD3(ClearHistory,
+ void(base::Time begin,
+ base::Time end,
+ const base::Callback<bool(const GURL& url)>& filter));
+ MOCK_METHOD3(Fetch,
+ void(const Category&,
+ const std::set<std::string>&,
+ const FetchDoneCallback&));
+ MOCK_METHOD1(ClearCachedSuggestions, void(Category));
+ MOCK_METHOD1(ClearDismissedSuggestionsForDebugging, void(Category));
+ MOCK_METHOD1(DismissSuggestion, void(const ContentSuggestion::ID&));
+ MOCK_METHOD2(FetchSuggestionImage,
+ void(const ContentSuggestion::ID&, const ImageFetchedCallback&));
+ MOCK_METHOD2(GetDismissedSuggestionsForDebugging,
+ void(Category, const DismissedSuggestionsCallback&));
+ MOCK_METHOD0(OnSignInStateChanged, void());
+};
+
+} // namespace
+
+class SchedulingRemoteSuggestionsProviderTest
+ : public ::testing::Test {
+ public:
+ SchedulingRemoteSuggestionsProviderTest()
+ : // For the test we enabled all trigger types.
+ default_variation_params_{{"scheduler_trigger_types",
+ "persistent_scheduler_wake_up,ntp_opened,"
+ "browser_foregrounded,browser_cold_start"}},
+ params_manager_(ntp_snippets::kStudyName,
+ default_variation_params_,
+ {kArticleSuggestionsFeature.name}),
+ underlying_provider_(nullptr),
+ scheduling_provider_(nullptr),
+ user_classifier_(/*pref_service=*/nullptr) {
+ SchedulingRemoteSuggestionsProvider::RegisterProfilePrefs(
+ utils_.pref_service()->registry());
+ ResetProvider();
+ }
+
+ void ResetProvider() {
+ auto underlying_provider =
+ base::MakeUnique<StrictMock<MockRemoteSuggestionsProvider>>(
+ /*observer=*/nullptr);
+ underlying_provider_ = underlying_provider.get();
+
+ // SchedulingRemoteSuggestionsProvider calls SetProviderStatusCallback(_) to
+ // stay in the loop of status changes.
+ EXPECT_CALL(*underlying_provider_, SetProviderStatusCallback(_))
+ .WillOnce(SaveArg<0>(&provider_status_callback_));
+
+ auto test_clock = base::MakeUnique<base::SimpleTestClock>();
+ test_clock_ = test_clock.get();
+ test_clock_->SetNow(base::Time::Now());
+
+ scheduling_provider_ =
+ base::MakeUnique<SchedulingRemoteSuggestionsProvider>(
+ /*observer=*/nullptr, std::move(underlying_provider),
+ &persistent_scheduler_, &user_classifier_, utils_.pref_service(),
+ std::move(test_clock));
+ }
+
+ void SetVariationParameter(const std::string& param_name,
+ const std::string& param_value) {
+ std::map<std::string, std::string> params = default_variation_params_;
+ params[param_name] = param_value;
+
+ params_manager_.ClearAllVariationParams();
+ params_manager_.SetVariationParamsWithFeatureAssociations(
+ ntp_snippets::kStudyName, params,
+ {ntp_snippets::kArticleSuggestionsFeature.name});
+ }
+
+ protected:
+ std::map<std::string, std::string> default_variation_params_;
+ variations::testing::VariationParamsManager params_manager_;
+ StrictMock<MockPersistentScheduler> persistent_scheduler_;
+ StrictMock<MockRemoteSuggestionsProvider>* underlying_provider_;
+ std::unique_ptr<SchedulingRemoteSuggestionsProvider> scheduling_provider_;
+ RemoteSuggestionsProvider::ProviderStatusCallback provider_status_callback_;
+ base::SimpleTestClock* test_clock_;
+
+ void ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus new_status) {
+ provider_status_callback_.Run(new_status);
+ }
+
+ private:
+ test::RemoteSuggestionsTestUtils utils_;
+ UserClassifier user_classifier_;
+
+ DISALLOW_COPY_AND_ASSIGN(SchedulingRemoteSuggestionsProviderTest);
+};
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldIgnoreSignalsWhenNotEnabled) {
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+ scheduling_provider_->OnNTPOpened();
+ scheduling_provider_->OnBrowserForegrounded();
+ scheduling_provider_->OnBrowserColdStart();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldIgnoreSignalsWhenDisabledByParam) {
+ // First set an empty list of allowed trigger types.
+ SetVariationParameter("scheduler_trigger_types", "-");
+ ResetProvider();
+
+ // Then enable the scheduler.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+ scheduling_provider_->OnNTPOpened();
+ scheduling_provider_->OnBrowserForegrounded();
+ scheduling_provider_->OnBrowserColdStart();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldHandleEmptyParamForTriggerTypes) {
+ // First set an empty param for allowed trigger types -> should result in the
+ // default list.
+ SetVariationParameter("scheduler_trigger_types", "");
+ ResetProvider();
+
+ // Then enable the scheduler.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ // For instance, persistent scheduler wake up should be enabled by default.
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_));
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldHandleIncorrentParamForTriggerTypes) {
+ // First set an invalid list of allowed trigger types.
+ SetVariationParameter("scheduler_trigger_types", "ntp_opened,foo;");
+ ResetProvider();
+
+ // Then enable the scheduler.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ // For instance, persistent scheduler wake up should be enabled by default.
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_));
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldFetchOnPersistentSchedulerWakeUp) {
+ // First set only this type to be allowed.
+ SetVariationParameter("scheduler_trigger_types",
+ "persistent_scheduler_wake_up");
+ ResetProvider();
+
+ // Then enable the scheduler.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_));
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldFetchOnPersistentSchedulerWakeUpRepeated) {
+ RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+ {
+ InSequence s;
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_))
+ .WillOnce(SaveArg<0>(&signal_fetch_done));
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_));
+ }
+ // First enable the scheduler -- calling Schedule() for the first time.
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+ // Make the first persistent fetch successful -- calling Schedule() again.
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+ signal_fetch_done.Run(Status::Success());
+ // Make the second fetch.
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldNotTriggerBackgroundFetchIfAlreadyInProgess) {
+ {
+ InSequence s;
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_));
+ // RefetchInTheBackground is not called after the second trigger.
+ }
+ // First enable the scheduler -- calling Schedule() for the first time.
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+ // Make the first persistent fetch never finish.
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+ // Make the second fetch.
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldFetchOnNTPOpenedForTheFirstTime) {
+ // First set only this type to be allowed.
+ SetVariationParameter("scheduler_trigger_types", "ntp_opened");
+ ResetProvider();
+
+ // Then enable the scheduler.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_));
+ scheduling_provider_->OnNTPOpened();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldFetchOnBrowserForegroundedForTheFirstTime) {
+ // First set only this type to be allowed.
+ SetVariationParameter("scheduler_trigger_types", "browser_foregrounded");
+ ResetProvider();
+
+ // Then enable the scheduler.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_));
+ scheduling_provider_->OnBrowserForegrounded();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldFetchOnBrowserColdStartForTheFirstTime) {
+ // First set only this type to be allowed.
+ SetVariationParameter("scheduler_trigger_types", "browser_cold_start");
+ ResetProvider();
+
+ // Then enable the scheduler.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_));
+ scheduling_provider_->OnBrowserColdStart();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldNotFetchOnNTPOpenedAfterSuccessfulSoftFetch) {
+ // First enable the scheduler; the second Schedule is called after the
+ // successful fetch.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _)).Times(2);
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ // Make the first soft fetch successful.
+ RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_))
+ .WillOnce(SaveArg<0>(&signal_fetch_done));
+ scheduling_provider_->OnNTPOpened();
+ signal_fetch_done.Run(Status::Success());
+ // The second call is ignored if it happens right after the first one.
+ scheduling_provider_->OnNTPOpened();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldNotFetchOnNTPOpenedAfterSuccessfulPersistentFetch) {
+ // First enable the scheduler; the second Schedule is called after the
+ // successful fetch.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _)).Times(2);
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ // Make the first persistent fetch successful.
+ RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_))
+ .WillOnce(SaveArg<0>(&signal_fetch_done));
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+ signal_fetch_done.Run(Status::Success());
+ // The second call is ignored if it happens right after the first one.
+ scheduling_provider_->OnNTPOpened();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldNotFetchOnNTPOpenedAfterFailedSoftFetch) {
+ // First enable the scheduler.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ // Make the first soft fetch failed.
+ RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_))
+ .WillOnce(SaveArg<0>(&signal_fetch_done));
+ scheduling_provider_->OnNTPOpened();
+ signal_fetch_done.Run(Status(StatusCode::PERMANENT_ERROR, ""));
+
+ // The second call is ignored if it happens right after the first one.
+ scheduling_provider_->OnNTPOpened();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldNotFetchOnNTPOpenedAfterFailedPersistentFetch) {
+ // First enable the scheduler.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ // Make the first persistent fetch failed.
+ RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_))
+ .WillOnce(SaveArg<0>(&signal_fetch_done));
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+ signal_fetch_done.Run(Status(StatusCode::PERMANENT_ERROR, ""));
+
+ // The second call is ignored if it happens right after the first one.
+ scheduling_provider_->OnNTPOpened();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldFetchAgainOnNTPOpenedLaterAgain) {
+ RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+ {
+ InSequence s;
+ // Initial scheduling after being enabled.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ // The first call to NTPOpened results in a fetch.
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_))
+ .WillOnce(SaveArg<0>(&signal_fetch_done));
+ // Rescheduling after a succesful fetch.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ // The second call to NTPOpened 2hrs later again results in a fetch.
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_));
+ }
+
+ // First enable the scheduler.
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+ // Make the first soft fetch successful.
+ scheduling_provider_->OnNTPOpened();
+ signal_fetch_done.Run(Status::Success());
+ // Open NTP again after 2hrs.
+ test_clock_->Advance(base::TimeDelta::FromHours(2));
+ scheduling_provider_->OnNTPOpened();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldRescheduleOnRescheduleFetching) {
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ scheduling_provider_->RescheduleFetching();
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest, ShouldScheduleOnActivation) {
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldUnscheduleOnLaterInactivation) {
+ {
+ InSequence s;
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ EXPECT_CALL(persistent_scheduler_, Unschedule());
+ }
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::INACTIVE);
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldScheduleOnLaterActivation) {
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ // There is no schedule yet, so inactivation does not trigger unschedule.
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::INACTIVE);
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldRescheduleAfterSuccessfulFetch) {
+ // First reschedule on becoming active.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _)).Times(2);
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_))
+ .WillOnce(SaveArg<0>(&signal_fetch_done));
+
+ // Trigger a fetch.
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+ // Second reschedule after a successful fetch.
+ signal_fetch_done.Run(Status::Success());
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ShouldNotRescheduleAfterFailedFetch) {
+ // Only reschedule on becoming active.
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+ EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_))
+ .WillOnce(SaveArg<0>(&signal_fetch_done));
+
+ // Trigger a fetch.
+ scheduling_provider_->OnPersistentSchedulerWakeUp();
+ // No furter reschedule after a failure.
+ signal_fetch_done.Run(Status(StatusCode::PERMANENT_ERROR, ""));
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest, ShouldScheduleOnlyOnce) {
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+ // No further call to Schedule on a second status callback.
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest, ShouldUnscheduleOnlyOnce) {
+ {
+ InSequence s;
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
+ EXPECT_CALL(persistent_scheduler_, Unschedule());
+ }
+ // First schedule so that later we really unschedule.
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::INACTIVE);
+ // No further call to Unschedule on second status callback.
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::INACTIVE);
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ReschedulesWhenWifiParamChanges) {
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _)).Times(2);
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
+ // null. Change the wifi interval for this class.
+ SetVariationParameter("fetching_interval_hours-wifi-active_ntp_user", "1.5");
+
+ // Schedule() should get called for the second time after params have changed.
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ReschedulesWhenFallbackParamChanges) {
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _)).Times(2);
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
+ // null. Change the fallback interval for this class.
+ SetVariationParameter("fetching_interval_hours-fallback-active_ntp_user",
+ "1.5");
+
+ // Schedule() should get called for the second time after params have changed.
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+}
+
+TEST_F(SchedulingRemoteSuggestionsProviderTest,
+ ReschedulesWhenOnUsageEventParamChanges) {
+ EXPECT_CALL(persistent_scheduler_, Schedule(_, _)).Times(2);
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+
+ // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
+ // null. Change the on usage interval for this class.
+ SetVariationParameter("soft_fetching_interval_hours-active-active_ntp_user",
+ "1.5");
+
+ // Schedule() should get called for the second time after params have changed.
+ ChangeStatusOfUnderlyingProvider(
+ RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
+}
+
+} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippets_test_utils.cc b/chromium/components/ntp_snippets/remote/test_utils.cc
index cdcb4501b14..eb63bc40431 100644
--- a/chromium/components/ntp_snippets/remote/ntp_snippets_test_utils.cc
+++ b/chromium/components/ntp_snippets/remote/test_utils.cc
@@ -2,10 +2,11 @@
// 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/ntp_snippets_test_utils.h"
+#include "components/ntp_snippets/remote/test_utils.h"
#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"
@@ -46,25 +47,25 @@ syncer::ModelTypeSet FakeSyncService::GetActiveDataTypes() const {
return active_data_types_;
}
-NTPSnippetsTestUtils::NTPSnippetsTestUtils()
- : pref_service_(new TestingPrefServiceSimple()) {
+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());
- signin_client_.reset(new TestSigninClient(pref_service_.get()));
- account_tracker_.reset(new AccountTrackerService());
- fake_sync_service_.reset(new FakeSyncService());
+ signin_client_ = base::MakeUnique<TestSigninClient>(pref_service_.get());
+ account_tracker_ = base::MakeUnique<AccountTrackerService>();
+ fake_sync_service_ = base::MakeUnique<FakeSyncService>();
ResetSigninManager();
}
-NTPSnippetsTestUtils::~NTPSnippetsTestUtils() = default;
+RemoteSuggestionsTestUtils::~RemoteSuggestionsTestUtils() = default;
-void NTPSnippetsTestUtils::ResetSigninManager() {
- fake_signin_manager_.reset(
- new FakeSigninManagerBase(signin_client_.get(), account_tracker_.get()));
+void RemoteSuggestionsTestUtils::ResetSigninManager() {
+ fake_signin_manager_ = base::MakeUnique<FakeSigninManagerBase>(
+ signin_client_.get(), account_tracker_.get());
}
} // namespace test
diff --git a/chromium/components/ntp_snippets/remote/ntp_snippets_test_utils.h b/chromium/components/ntp_snippets/remote/test_utils.h
index 2a61b3d037e..d7cdfa1ba50 100644
--- a/chromium/components/ntp_snippets/remote/ntp_snippets_test_utils.h
+++ b/chromium/components/ntp_snippets/remote/test_utils.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_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_TEST_UTILS_H_
-#define COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_TEST_UTILS_H_
+#ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_TEST_UTILS_H_
+#define COMPONENTS_NTP_SNIPPETS_REMOTE_TEST_UTILS_H_
#include <memory>
@@ -12,7 +12,6 @@
class AccountTrackerService;
class FakeSigninManagerBase;
-class MockSyncService;
class TestingPrefServiceSimple;
class TestSigninClient;
@@ -37,12 +36,12 @@ class FakeSyncService : public syncer::FakeSyncService {
syncer::ModelTypeSet active_data_types_;
};
-// Common utilities for snippet tests, handles initializing fakes for sync and
-// signin.
-class NTPSnippetsTestUtils {
+// Common utilities for remote suggestion tests, handles initializing fakes for
+// sync and signin.
+class RemoteSuggestionsTestUtils {
public:
- NTPSnippetsTestUtils();
- ~NTPSnippetsTestUtils();
+ RemoteSuggestionsTestUtils();
+ ~RemoteSuggestionsTestUtils();
void ResetSigninManager();
@@ -63,4 +62,4 @@ class NTPSnippetsTestUtils {
} // namespace test
} // namespace ntp_snippets
-#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_NTP_SNIPPETS_TEST_UTILS_H_
+#endif // COMPONENTS_NTP_SNIPPETS_REMOTE_TEST_UTILS_H_
diff --git a/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.cc b/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.cc
index d05da3759e6..0d05469f138 100644
--- a/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.cc
+++ b/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.cc
@@ -11,8 +11,9 @@
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
-#include "components/ntp_snippets/category_factory.h"
+#include "components/ntp_snippets/category.h"
#include "components/ntp_snippets/category_info.h"
#include "components/ntp_snippets/content_suggestion.h"
#include "components/ntp_snippets/features.h"
@@ -22,6 +23,7 @@
#include "components/prefs/pref_service.h"
#include "components/sessions/core/session_types.h"
#include "components/sync_sessions/synced_session.h"
+#include "components/variations/variations_associated_data.h"
#include "grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image.h"
@@ -49,18 +51,19 @@ const char* kMaxForeignTabAgeInMinutesParamName =
"max_foreign_tabs_age_in_minutes";
int GetMaxForeignTabsTotal() {
- return GetParamAsInt(ntp_snippets::kForeignSessionsSuggestionsFeature,
- kMaxForeignTabsTotalParamName, kMaxForeignTabsTotal);
+ return variations::GetVariationParamByFeatureAsInt(
+ ntp_snippets::kForeignSessionsSuggestionsFeature,
+ kMaxForeignTabsTotalParamName, kMaxForeignTabsTotal);
}
int GetMaxForeignTabsPerDevice() {
- return GetParamAsInt(ntp_snippets::kForeignSessionsSuggestionsFeature,
- kMaxForeignTabsPerDeviceParamName,
- kMaxForeignTabsPerDevice);
+ return variations::GetVariationParamByFeatureAsInt(
+ ntp_snippets::kForeignSessionsSuggestionsFeature,
+ kMaxForeignTabsPerDeviceParamName, kMaxForeignTabsPerDevice);
}
TimeDelta GetMaxForeignTabAge() {
- return TimeDelta::FromMinutes(GetParamAsInt(
+ return TimeDelta::FromMinutes(variations::GetVariationParamByFeatureAsInt(
ntp_snippets::kForeignSessionsSuggestionsFeature,
kMaxForeignTabAgeInMinutesParamName, kMaxForeignTabAgeInMinutes));
}
@@ -159,13 +162,12 @@ struct ForeignSessionsSuggestionsProvider::SessionData {
ForeignSessionsSuggestionsProvider::ForeignSessionsSuggestionsProvider(
ContentSuggestionsProvider::Observer* observer,
- CategoryFactory* category_factory,
std::unique_ptr<ForeignSessionsProvider> foreign_sessions_provider,
PrefService* pref_service)
- : ContentSuggestionsProvider(observer, category_factory),
+ : ContentSuggestionsProvider(observer),
category_status_(CategoryStatus::INITIALIZING),
provided_category_(
- category_factory->FromKnownCategory(KnownCategories::FOREIGN_TABS)),
+ Category::FromKnownCategory(KnownCategories::FOREIGN_TABS)),
foreign_sessions_provider_(std::move(foreign_sessions_provider)),
pref_service_(pref_service) {
foreign_sessions_provider_->SubscribeForForeignTabChange(
@@ -369,8 +371,9 @@ ForeignSessionsSuggestionsProvider::GetSuggestionCandidates(
std::unique_ptr<sessions::SessionWindow>>& key_value :
session->windows) {
for (const std::unique_ptr<SessionTab>& tab : key_value.second->tabs) {
- if (tab->navigations.empty())
+ if (tab->navigations.empty()) {
continue;
+ }
const SerializedNavigationEntry& navigation = tab->navigations.back();
const std::string id = navigation.virtual_url().spec();
diff --git a/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h b/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h
index ec50eed576d..0fa52849ac6 100644
--- a/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h
+++ b/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h
@@ -40,7 +40,6 @@ class ForeignSessionsSuggestionsProvider : public ContentSuggestionsProvider {
public:
ForeignSessionsSuggestionsProvider(
ContentSuggestionsProvider::Observer* observer,
- CategoryFactory* category_factory,
std::unique_ptr<ForeignSessionsProvider> foreign_sessions_provider,
PrefService* pref_service);
~ForeignSessionsSuggestionsProvider() override;
diff --git a/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc b/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc
index a65e5865fc0..e966048a538 100644
--- a/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc
+++ b/chromium/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc
@@ -11,7 +11,6 @@
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "components/ntp_snippets/category.h"
-#include "components/ntp_snippets/category_factory.h"
#include "components/ntp_snippets/content_suggestions_provider.h"
#include "components/ntp_snippets/mock_content_suggestions_provider_observer.h"
#include "components/prefs/testing_pref_service.h"
@@ -51,8 +50,9 @@ const char kUrl11[] = "http://www.fake11.com/";
const char kTitle[] = "title is ignored";
SessionWindow* GetOrCreateWindow(SyncedSession* session, int window_id) {
- if (session->windows.find(window_id) == session->windows.end())
+ if (session->windows.find(window_id) == session->windows.end()) {
session->windows[window_id] = base::MakeUnique<SessionWindow>();
+ }
return session->windows[window_id].get();
}
@@ -116,8 +116,7 @@ class ForeignSessionsSuggestionsProviderTest : public Test {
_, category(), CategoryStatus::AVAILABLE));
provider_ = base::MakeUnique<ForeignSessionsSuggestionsProvider>(
- &observer_, &category_factory_,
- std::move(fake_foreign_sessions_provider), &pref_service_);
+ &observer_, std::move(fake_foreign_sessions_provider), &pref_service_);
}
protected:
@@ -156,7 +155,7 @@ class ForeignSessionsSuggestionsProviderTest : public Test {
}
Category category() {
- return category_factory_.FromKnownCategory(KnownCategories::FOREIGN_TABS);
+ return Category::FromKnownCategory(KnownCategories::FOREIGN_TABS);
}
MockContentSuggestionsProviderObserver* observer() { return &observer_; }
@@ -164,7 +163,6 @@ class ForeignSessionsSuggestionsProviderTest : public Test {
private:
FakeForeignSessionsProvider* fake_foreign_sessions_provider_;
MockContentSuggestionsProviderObserver observer_;
- CategoryFactory category_factory_;
TestingPrefServiceSimple pref_service_;
std::unique_ptr<ForeignSessionsSuggestionsProvider> provider_;
std::map<int, std::unique_ptr<SyncedSession>> sessions_map_;
diff --git a/chromium/components/ntp_snippets/status.cc b/chromium/components/ntp_snippets/status.cc
index 756dc1c1da5..519a8e847bb 100644
--- a/chromium/components/ntp_snippets/status.cc
+++ b/chromium/components/ntp_snippets/status.cc
@@ -6,10 +6,12 @@
namespace ntp_snippets {
-Status::Status(StatusCode status, const std::string& message)
- : status(status), message(message) {}
+Status::Status(StatusCode status_code, const std::string& message)
+ : code(status_code), message(message) {}
-Status::Status(StatusCode status) : Status(status, std::string()) {}
+Status Status::Success() {
+ return Status(StatusCode::SUCCESS, std::string());
+}
Status::~Status() = default;
diff --git a/chromium/components/ntp_snippets/status.h b/chromium/components/ntp_snippets/status.h
index ef5ae940025..83710451c7e 100644
--- a/chromium/components/ntp_snippets/status.h
+++ b/chromium/components/ntp_snippets/status.h
@@ -19,13 +19,15 @@ enum class StatusCode {
// This struct provides the status code of a request and an optional message
// describing the status (esp. failures) in detail.
struct Status {
- Status(StatusCode status, const std::string& message);
- // TODO(tschumann): Change this into a a Success() factory method. Error
- // states should always have a message.
- explicit Status(StatusCode status);
+ Status(StatusCode status_code, const std::string& message);
~Status();
- StatusCode status;
+ // Errors always need a message but a success does not.
+ static Status Success();
+
+ bool IsSuccess() const { return code == StatusCode::SUCCESS; }
+
+ StatusCode code;
// The message is not meant to be displayed to the user.
std::string message;
diff --git a/chromium/components/ntp_snippets/user_classifier.cc b/chromium/components/ntp_snippets/user_classifier.cc
index d25cb2256a1..35b538936ff 100644
--- a/chromium/components/ntp_snippets/user_classifier.cc
+++ b/chromium/components/ntp_snippets/user_classifier.cc
@@ -91,22 +91,11 @@ static_assert(arraysize(kMetrics) ==
static_cast<int>(UserClassifier::Metric::COUNT),
"Fill in info for all metrics.");
-double GetParamValue(const char* param_name, double default_value) {
- std::string param_value_str = variations::GetVariationParamValueByFeature(
- kArticleSuggestionsFeature, param_name);
- double param_value = 0;
- if (!base::StringToDouble(param_value_str, &param_value)) {
- LOG_IF(WARNING, !param_value_str.empty())
- << "Invalid variation parameter for " << param_name;
- return default_value;
- }
- return param_value;
-}
-
// Computes the discount rate.
double GetDiscountRatePerHour() {
- double discount_rate_per_day =
- GetParamValue(kDiscountRatePerDayParam, kDiscountRatePerDay);
+ double discount_rate_per_day = variations::GetVariationParamByFeatureAsDouble(
+ kArticleSuggestionsFeature, kDiscountRatePerDayParam,
+ kDiscountRatePerDay);
// Check for illegal values.
if (discount_rate_per_day <= 0 || discount_rate_per_day >= 1) {
DLOG(WARNING) << "Illegal value " << discount_rate_per_day
@@ -121,17 +110,20 @@ double GetDiscountRatePerHour() {
}
double GetInitialHoursBetweenEvents(UserClassifier::Metric metric) {
- return GetParamValue(
+ return variations::GetVariationParamByFeatureAsDouble(
+ kArticleSuggestionsFeature,
kInitialHoursBetweenEventsParams[static_cast<int>(metric)],
kInitialHoursBetweenEvents[static_cast<int>(metric)]);
}
double GetMinHours() {
- return GetParamValue(kMinHoursParam, kMinHours);
+ return variations::GetVariationParamByFeatureAsDouble(
+ kArticleSuggestionsFeature, kMinHoursParam, kMinHours);
}
double GetMaxHours() {
- return GetParamValue(kMaxHoursParam, kMaxHours);
+ return variations::GetVariationParamByFeatureAsDouble(
+ kArticleSuggestionsFeature, kMaxHoursParam, kMaxHours);
}
// Returns the new value of the metric using its |old_value|, assuming
@@ -153,8 +145,9 @@ double GetEstimateHoursBetweenEvents(double metric_value,
// The computation below is well-defined only for |metric_value| > 1 (log of
// negative value or division by zero). When |metric_value| -> 1, the estimate
// below -> infinity, so max_hours is a natural result, here.
- if (metric_value <= 1)
+ if (metric_value <= 1) {
return max_hours;
+ }
// This is the estimate with the assumption that last event happened right
// now and the system is in the steady-state. Solve estimate_hours in the
@@ -194,14 +187,19 @@ UserClassifier::UserClassifier(PrefService* pref_service)
min_hours_(GetMinHours()),
max_hours_(GetMaxHours()),
active_consumer_scrolls_at_least_once_per_hours_(
- GetParamValue(kActiveConsumerScrollsAtLeastOncePerHoursParam,
- kActiveConsumerScrollsAtLeastOncePerHours)),
+ variations::GetVariationParamByFeatureAsDouble(
+ kArticleSuggestionsFeature,
+ kActiveConsumerScrollsAtLeastOncePerHoursParam,
+ kActiveConsumerScrollsAtLeastOncePerHours)),
rare_user_opens_ntp_at_most_once_per_hours_(
- GetParamValue(kRareUserOpensNTPAtMostOncePerHoursParam,
- kRareUserOpensNTPAtMostOncePerHours)) {
+ variations::GetVariationParamByFeatureAsDouble(
+ kArticleSuggestionsFeature,
+ kRareUserOpensNTPAtMostOncePerHoursParam,
+ kRareUserOpensNTPAtMostOncePerHours)) {
// The pref_service_ can be null in tests.
- if (!pref_service_)
+ if (!pref_service_) {
return;
+ }
// TODO(jkrcal): Store the current discount rate per hour into prefs. If it
// differs from the previous value, rescale the metric values so that the
@@ -209,8 +207,9 @@ UserClassifier::UserClassifier(PrefService* pref_service)
// Initialize the prefs storing the last time: the counter has just started!
for (const Metric metric : kMetrics) {
- if (!HasLastTime(metric))
+ if (!HasLastTime(metric)) {
SetLastTimeToNow(metric);
+ }
}
}
@@ -268,8 +267,9 @@ double UserClassifier::GetEstimatedAvgTime(Metric metric) const {
UserClassifier::UserClass UserClassifier::GetUserClass() const {
// The pref_service_ can be null in tests.
- if (!pref_service_)
+ if (!pref_service_) {
return UserClass::ACTIVE_NTP_USER;
+ }
if (GetEstimatedAvgTime(Metric::NTP_OPENED) >=
rare_user_opens_ntp_at_most_once_per_hours_) {
@@ -299,8 +299,9 @@ std::string UserClassifier::GetUserClassDescriptionForDebugging() const {
void UserClassifier::ClearClassificationForDebugging() {
// The pref_service_ can be null in tests.
- if (!pref_service_)
+ if (!pref_service_) {
return;
+ }
for (const Metric& metric : kMetrics) {
ClearMetricValue(metric);
@@ -310,14 +311,16 @@ void UserClassifier::ClearClassificationForDebugging() {
double UserClassifier::UpdateMetricOnEvent(Metric metric) {
// The pref_service_ can be null in tests.
- if (!pref_service_)
+ if (!pref_service_) {
return 0;
+ }
double hours_since_last_time =
std::min(max_hours_, GetHoursSinceLastTime(metric));
// Ignore events within the same "browsing session".
- if (hours_since_last_time < min_hours_)
+ if (hours_since_last_time < min_hours_) {
return GetUpToDateMetricValue(metric);
+ }
SetLastTimeToNow(metric);
@@ -332,8 +335,9 @@ double UserClassifier::UpdateMetricOnEvent(Metric metric) {
double UserClassifier::GetUpToDateMetricValue(Metric metric) const {
// The pref_service_ can be null in tests.
- if (!pref_service_)
+ if (!pref_service_) {
return 0;
+ }
double hours_since_last_time =
std::min(max_hours_, GetHoursSinceLastTime(metric));
@@ -344,8 +348,9 @@ double UserClassifier::GetUpToDateMetricValue(Metric metric) const {
}
double UserClassifier::GetHoursSinceLastTime(Metric metric) const {
- if (!HasLastTime(metric))
+ if (!HasLastTime(metric)) {
return 0;
+ }
base::TimeDelta since_last_time =
base::Time::Now() - base::Time::FromInternalValue(pref_service_->GetInt64(
diff --git a/chromium/components/ntp_snippets_strings.grdp b/chromium/components/ntp_snippets_strings.grdp
index 2e4d85bebce..e9e2ef5f1e5 100644
--- a/chromium/components/ntp_snippets_strings.grdp
+++ b/chromium/components/ntp_snippets_strings.grdp
@@ -5,8 +5,8 @@
<message name="IDS_SNIPPETS_DISABLED_SIGNED_OUT_INSTRUCTIONS" desc="Body of the card explaining the status of the content suggestions on the New Tab Page, when the user is signed out." formatter_data="android_java">
To get personalized content suggested by Google, sign in to Chrome.
</message>
- <message name="IDS_SNIPPETS_DISABLED_GENERIC_PROMPT" desc="Title of the card explaining what action the user can take to get content suggestions on the New Tab Page." formatter_data="android_java">
- Get suggested content
+ <message name="IDS_SNIPPETS_DISABLED_GENERIC_PROMPT" desc="Title of the card explaining what action the user can take to get personalized content suggestions on the New Tab Page." formatter_data="android_java">
+ Get personalized content
</message>
<message name="IDS_NTP_STATUS_CARD_TITLE_NO_SUGGESTIONS" desc="On the New Tab Page, title of the status card explaining that there is no new content available in the section." formatter_data="android_java">
That’s all for now
@@ -21,10 +21,6 @@
Your suggested articles appear here
</message>
- <message name="IDS_NTP_RECENT_TAB_SUGGESTIONS_SECTION_HEADER" desc="Header of the recent tabs section, which is a list of pages that the user recently viewed, displayed as cards on the New Tab Page.">
- Recent tabs
- </message>
-
<message name="IDS_NTP_BOOKMARK_SUGGESTIONS_SECTION_HEADER" desc="Header of the bookmarks section. The bookmarks section shows a list of recently *visited* bookmarks, irrespective of their creation date. It is *not* a list of recently *created* bookmarks">
Recent bookmarks
</message>
@@ -34,15 +30,28 @@
</message>
<message name="IDS_NTP_FOREIGN_SESSIONS_SUGGESTIONS_SECTION_HEADER" desc="Header of the foreign sessions, which is a list of the user's most recently visited tabs on other devices displayed as cards on the New Tab Page.">
- Recent tabs
+ Tabs from other devices
</message>
<message name="IDS_NTP_FOREIGN_SESSIONS_SUGGESTIONS_SECTION_EMPTY" desc="On the New Tab Page, text of the card explaining to the user that they can expect to see tabs from other devices in this area in the future.">
Your recent tabs from other devices appear here
</message>
- <message name="IDS_NTP_SUGGESTIONS_SECTION_EMPTY" desc="On the New Tab Page, text of card explaining to the user that they can expect to see some sort of content suggestions in this area in the future.">
- Your suggestions appear here
+ <!-- When you change this string, please change IDS_NTP_PHYSICAL_WEB_PAGE_SUGGESTIONS_SECTION_EMPTY description accordingly. -->
+ <message name="IDS_NTP_PHYSICAL_WEB_PAGE_SUGGESTIONS_SECTION_HEADER" desc="Header of the physical web section. The physical web section shows a list of physical web pages available near the user. A physical web page is a URL transmitted over bluetooth by a low power device (a beacon). The user must be at most 100 meters (generally even closer) away from the beacon to receive the URL. This allows the URL to be context based (e.g. one is at a train station and the schedule is available as a physical web page).">
+ Nearby
+ </message>
+
+ <message name="IDS_NTP_PHYSICAL_WEB_PAGE_SUGGESTIONS_SECTION_EMPTY" desc="On the New Tab Page, text of the card explaining to the user that they can expect to see physical web page suggestions in this area in the future, but currently this area is empty. Connection to the physical web section header (which is currently 'Nearby') must be clear, but the words (i.e. translation of 'nearby') may differ. The physical web section shows a list of physical web pages available near the user. A physical web page is a URL transmitted over bluetooth by a low power device (a beacon). The user must be at most 100 meters (generally even closer) away from the beacon to receive the URL. This allows the URL to be context based (e.g. one is at a train station and the schedule is available as a physical web page).">
+ Your nearby suggestions appear here
+ </message>
+
+ <message name="IDS_NTP_RECENT_TAB_SUGGESTIONS_SECTION_HEADER" desc="Header of the recent tabs section, which is a list of recent open tabs, which are currently opened and were automatically saved for offline consumption. This list is displayed as cards on the New Tab Page. The saved version is opened only if there are problems with the data connection (e.g. slow or absent).">
+ Open tabs
+ </message>
+
+ <message name="IDS_NTP_RECENT_TAB_SUGGESTIONS_SECTION_EMPTY" desc="On the New Tab Page, text of the card explaining to the user that they can expect to see recent open tabs in this area in the future, but currently this area is empty. Recent open tabs - the tabs which are currently opened and were automatically saved for offline consumption.">
+ Your open tabs appear here
</message>
</grit-part>
diff --git a/chromium/components/ntp_tiles/BUILD.gn b/chromium/components/ntp_tiles/BUILD.gn
index c50d5c4fb78..1e1a20be3ce 100644
--- a/chromium/components/ntp_tiles/BUILD.gn
+++ b/chromium/components/ntp_tiles/BUILD.gn
@@ -14,22 +14,29 @@ static_library("ntp_tiles") {
"country_code_ios.mm",
"field_trial.cc",
"field_trial.h",
- "icon_cacher.cc",
"icon_cacher.h",
+ "icon_cacher_impl.cc",
+ "icon_cacher_impl.h",
"metrics.cc",
"metrics.h",
"most_visited_sites.cc",
"most_visited_sites.h",
"ntp_tile.cc",
"ntp_tile.h",
- "popular_sites.cc",
+ "ntp_tile_source.h",
"popular_sites.h",
+ "popular_sites_impl.cc",
+ "popular_sites_impl.h",
"pref_names.cc",
"pref_names.h",
"switches.cc",
"switches.h",
+ "webui/ntp_tiles_internals_message_handler.cc",
+ "webui/ntp_tiles_internals_message_handler.h",
+ "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 = [
@@ -45,6 +52,7 @@ static_library("ntp_tiles") {
"//components/image_fetcher",
"//components/pref_registry",
"//components/prefs",
+ "//components/rappor/public",
"//components/search_engines",
"//components/url_formatter",
"//components/variations",
@@ -56,21 +64,41 @@ static_library("ntp_tiles") {
}
}
+# If you want to use this, let us (ntp-dev@chromium.org) know. In that case, it
+# should be moved to a more common location as it has 2+ callers already.
+# Note that you probably shouldn't be using it outside of ios or tests.
+source_set("json_unsafe_parser") {
+ testonly = !is_ios
+
+ sources = [
+ "json_unsafe_parser.cc",
+ "json_unsafe_parser.h",
+ ]
+
+ public_deps = [
+ "//base",
+ ]
+}
+
source_set("unit_tests") {
testonly = true
sources = [
- "icon_cacher_unittest.cc",
+ "icon_cacher_impl_unittest.cc",
+ "metrics_unittest.cc",
"most_visited_sites_unittest.cc",
- "popular_sites_unittest.cc",
+ "popular_sites_impl_unittest.cc",
]
deps = [
+ ":json_unsafe_parser",
":ntp_tiles",
"//base/test:test_support",
"//components/favicon/core",
"//components/favicon_base",
"//components/image_fetcher",
- "//components/pref_registry:test_support",
+ "//components/pref_registry:pref_registry",
+ "//components/rappor:test_support",
+ "//components/sync_preferences:test_support",
"//net:test_support",
"//testing/gmock",
"//testing/gtest",
@@ -82,7 +110,7 @@ if (is_android) {
java_cpp_enum("ntp_tiles_enums_java") {
sources = [
"metrics.h",
- "ntp_tile.h",
+ "ntp_tile_source.h",
]
}
}
diff --git a/chromium/components/ntp_tiles/DEPS b/chromium/components/ntp_tiles/DEPS
index 77e6495866b..c5cd18dfa06 100644
--- a/chromium/components/ntp_tiles/DEPS
+++ b/chromium/components/ntp_tiles/DEPS
@@ -7,8 +7,10 @@ include_rules = [
"+components/history/core/browser",
"+components/pref_registry",
"+components/prefs",
+ "+components/rappor",
"+components/search_engines",
"+components/suggestions",
+ "+components/sync_preferences",
"+components/url_formatter",
"+components/variations",
"+jni",
diff --git a/chromium/components/ntp_tiles/field_trial.cc b/chromium/components/ntp_tiles/field_trial.cc
index 39f4b7252ea..e3f5f6d7c7a 100644
--- a/chromium/components/ntp_tiles/field_trial.cc
+++ b/chromium/components/ntp_tiles/field_trial.cc
@@ -14,6 +14,7 @@
#include "components/ntp_tiles/constants.h"
#include "components/ntp_tiles/field_trial.h"
#include "components/ntp_tiles/switches.h"
+#include "components/variations/variations_associated_data.h"
#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
@@ -42,17 +43,20 @@ void SetUpFirstLaunchFieldTrial(bool is_stable_channel) {
if (base::FieldTrialList::TrialExists(kPopularSitesFieldTrialName))
return;
+ // The experiment is only for stable channel, the other channels will simply
+ // get the default behavior.
+ if (!is_stable_channel)
+ return;
+
// Stable channels will run with 10% probability.
- // Non-stable channels will run with 50% probability.
const base::FieldTrial::Probability kTotalProbability = 100;
- const base::FieldTrial::Probability kEnabledAndControlProbability =
- is_stable_channel ? 10 : 50;
+ const base::FieldTrial::Probability kEnabledAndControlProbability = 10;
- // Experiment enabled until March 15, 2017. By default, disabled.
+ // Experiment enabled until April 26, 2017.
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
kPopularSitesFieldTrialName, kTotalProbability,
- kPopularSiteDefaultGroup, 2017, 3, 15, // Mar 15, 2017
+ kPopularSiteDefaultGroup, 2017, 4, 26, // Apr 26, 2017
base::FieldTrial::ONE_TIME_RANDOMIZED, nullptr));
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
@@ -65,7 +69,13 @@ void SetUpFirstLaunchFieldTrial(bool is_stable_channel) {
kTotalProbability);
} else {
trial->AppendGroup(kPopularSiteControlGroup, kEnabledAndControlProbability);
+ AssociateGoogleVariationID(variations::GOOGLE_WEB_PROPERTIES,
+ kPopularSitesFieldTrialName,
+ kPopularSiteControlGroup, 3312959);
trial->AppendGroup(kPopularSiteEnabledGroup, kEnabledAndControlProbability);
+ AssociateGoogleVariationID(variations::GOOGLE_WEB_PROPERTIES,
+ kPopularSitesFieldTrialName,
+ kPopularSiteEnabledGroup, 3312958);
}
}
@@ -89,6 +99,13 @@ bool ShouldShowPopularSites() {
}
#endif
+#if defined(OS_IOS)
+ // On iOS, if not enrolled in the experiment, the default is to enable the
+ // feature.
+ if (group_name.empty() || (group_name == kPopularSiteDefaultGroup))
+ return true;
+#endif
+
return base::StartsWith(group_name, "Enabled",
base::CompareCase::INSENSITIVE_ASCII);
}
diff --git a/chromium/components/ntp_tiles/icon_cacher.h b/chromium/components/ntp_tiles/icon_cacher.h
index 597faeeac82..22beeb6f48c 100644
--- a/chromium/components/ntp_tiles/icon_cacher.h
+++ b/chromium/components/ntp_tiles/icon_cacher.h
@@ -1,68 +1,29 @@
-// Copyright 2016 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.
#ifndef COMPONENTS_NTP_TILES_ICON_CACHER_H_
#define COMPONENTS_NTP_TILES_ICON_CACHER_H_
-#include <memory>
-
#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "base/task/cancelable_task_tracker.h"
#include "components/ntp_tiles/popular_sites.h"
-namespace favicon {
-class FaviconService;
-} // namespace favicon
-
-namespace favicon_base {
-struct FaviconImageResult;
-} // namespace favicon_base
-
-namespace gfx {
-class Image;
-} // namespace gfx
-
-namespace image_fetcher {
-class ImageFetcher;
-} // namespace image_fetcher
-
namespace ntp_tiles {
// Ensures that a Popular Sites icon is cached, downloading and saving it if
// not.
//
// Does not provide any way to get a fetched favicon; use the FaviconService for
-// that. All this class does is guarantee that FaviconService will be able to
+// that. All this interface guarantees is that FaviconService will be able to
// get you an icon (if it exists).
class IconCacher {
public:
- IconCacher(favicon::FaviconService* favicon_service,
- std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher);
- ~IconCacher();
+ virtual ~IconCacher() = default;
// Fetches the icon if necessary, then invokes |done| with true if it was
// newly fetched (false if it was already cached or could not be fetched).
- void StartFetch(PopularSites::Site site,
- const base::Callback<void(bool)>& done);
-
- private:
- void OnGetFaviconImageForPageURLFinished(
- PopularSites::Site site,
- const base::Callback<void(bool)>& done,
- const favicon_base::FaviconImageResult& result);
-
- void OnFaviconDownloaded(PopularSites::Site site,
- const base::Callback<void(bool)>& done,
- const std::string& id,
- const gfx::Image& fetched_image);
-
- base::CancelableTaskTracker tracker_;
- favicon::FaviconService* const favicon_service_;
- std::unique_ptr<image_fetcher::ImageFetcher> const image_fetcher_;
-
- DISALLOW_COPY_AND_ASSIGN(IconCacher);
+ virtual void StartFetch(PopularSites::Site site,
+ const base::Callback<void(bool)>& done) = 0;
};
} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/icon_cacher.cc b/chromium/components/ntp_tiles/icon_cacher_impl.cc
index 687e1cbc3c4..cb957ea010e 100644
--- a/chromium/components/ntp_tiles/icon_cacher.cc
+++ b/chromium/components/ntp_tiles/icon_cacher_impl.cc
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/ntp_tiles/icon_cacher.h"
+#include "components/ntp_tiles/icon_cacher_impl.h"
+
+#include <utility>
#include "components/favicon/core/favicon_service.h"
#include "components/favicon/core/favicon_util.h"
@@ -28,7 +30,7 @@ const GURL& IconURL(const PopularSites::Site& site) {
} // namespace
-IconCacher::IconCacher(
+IconCacherImpl::IconCacherImpl(
favicon::FaviconService* favicon_service,
std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher)
: favicon_service_(favicon_service),
@@ -37,18 +39,18 @@ IconCacher::IconCacher(
data_use_measurement::DataUseUserData::NTP_TILES);
}
-IconCacher::~IconCacher() = default;
+IconCacherImpl::~IconCacherImpl() = default;
-void IconCacher::StartFetch(PopularSites::Site site,
- const base::Callback<void(bool)>& done) {
+void IconCacherImpl::StartFetch(PopularSites::Site site,
+ const base::Callback<void(bool)>& done) {
favicon::GetFaviconImageForPageURL(
favicon_service_, site.url, IconType(site),
- base::Bind(&IconCacher::OnGetFaviconImageForPageURLFinished,
+ base::Bind(&IconCacherImpl::OnGetFaviconImageForPageURLFinished,
base::Unretained(this), std::move(site), done),
&tracker_);
}
-void IconCacher::OnGetFaviconImageForPageURLFinished(
+void IconCacherImpl::OnGetFaviconImageForPageURLFinished(
PopularSites::Site site,
const base::Callback<void(bool)>& done,
const favicon_base::FaviconImageResult& result) {
@@ -59,14 +61,14 @@ void IconCacher::OnGetFaviconImageForPageURLFinished(
image_fetcher_->StartOrQueueNetworkRequest(
std::string(), IconURL(site),
- base::Bind(&IconCacher::OnFaviconDownloaded, base::Unretained(this), site,
- done));
+ base::Bind(&IconCacherImpl::OnFaviconDownloaded, base::Unretained(this),
+ site, done));
}
-void IconCacher::OnFaviconDownloaded(PopularSites::Site site,
- const base::Callback<void(bool)>& done,
- const std::string& id,
- const gfx::Image& fetched_image) {
+void IconCacherImpl::OnFaviconDownloaded(PopularSites::Site site,
+ const base::Callback<void(bool)>& done,
+ const std::string& id,
+ const gfx::Image& fetched_image) {
if (fetched_image.IsEmpty()) {
done.Run(false);
return;
diff --git a/chromium/components/ntp_tiles/icon_cacher_impl.h b/chromium/components/ntp_tiles/icon_cacher_impl.h
new file mode 100644
index 00000000000..1028f72b29b
--- /dev/null
+++ b/chromium/components/ntp_tiles/icon_cacher_impl.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_NTP_TILES_ICON_CACHER_IMPL_H_
+#define COMPONENTS_NTP_TILES_ICON_CACHER_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/ntp_tiles/icon_cacher.h"
+#include "components/ntp_tiles/popular_sites.h"
+
+namespace favicon {
+class FaviconService;
+} // namespace favicon
+
+namespace favicon_base {
+struct FaviconImageResult;
+} // namespace favicon_base
+
+namespace gfx {
+class Image;
+} // namespace gfx
+
+namespace image_fetcher {
+class ImageFetcher;
+} // namespace image_fetcher
+
+namespace ntp_tiles {
+
+class IconCacherImpl : public IconCacher {
+ public:
+ IconCacherImpl(favicon::FaviconService* favicon_service,
+ std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher);
+ ~IconCacherImpl() override;
+
+ void StartFetch(PopularSites::Site site,
+ const base::Callback<void(bool)>& done) override;
+
+ private:
+ void OnGetFaviconImageForPageURLFinished(
+ PopularSites::Site site,
+ const base::Callback<void(bool)>& done,
+ const favicon_base::FaviconImageResult& result);
+
+ void OnFaviconDownloaded(PopularSites::Site site,
+ const base::Callback<void(bool)>& done,
+ const std::string& id,
+ const gfx::Image& fetched_image);
+
+ base::CancelableTaskTracker tracker_;
+ favicon::FaviconService* const favicon_service_;
+ std::unique_ptr<image_fetcher::ImageFetcher> const image_fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(IconCacherImpl);
+};
+
+} // namespace ntp_tiles
+
+#endif // COMPONENTS_NTP_TILES_ICON_CACHER_IMPL_H_
diff --git a/chromium/components/ntp_tiles/icon_cacher_unittest.cc b/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
index 2a65f959445..f80289c8ae1 100644
--- a/chromium/components/ntp_tiles/icon_cacher_unittest.cc
+++ b/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/ntp_tiles/icon_cacher.h"
+#include "components/ntp_tiles/icon_cacher_impl.h"
+
+#include <utility>
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ptr_util.h"
@@ -121,7 +123,7 @@ TEST_F(IconCacherTest, LargeCached) {
PreloadIcon(site_.url, site_.large_icon_url, favicon_base::TOUCH_ICON, 128,
128);
- IconCacher cacher(&favicon_service_, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_));
cacher.StartFetch(site_, BindMockFunction(&done));
loop.Run();
EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::FAVICON));
@@ -142,7 +144,7 @@ TEST_F(IconCacherTest, LargeNotCachedAndFetchSucceeded) {
EXPECT_CALL(done, Call(true)).WillOnce(Quit(&loop));
}
- IconCacher cacher(&favicon_service_, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_));
cacher.StartFetch(site_, BindMockFunction(&done));
loop.Run();
EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::FAVICON));
@@ -165,7 +167,7 @@ TEST_F(IconCacherTest, SmallNotCachedAndFetchSucceeded) {
EXPECT_CALL(done, Call(true)).WillOnce(Quit(&loop));
}
- IconCacher cacher(&favicon_service_, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_));
cacher.StartFetch(site_, BindMockFunction(&done));
loop.Run();
EXPECT_TRUE(IconIsCachedFor(site_.url, favicon_base::FAVICON));
@@ -186,7 +188,7 @@ TEST_F(IconCacherTest, LargeNotCachedAndFetchFailed) {
EXPECT_CALL(done, Call(false)).WillOnce(Quit(&loop));
}
- IconCacher cacher(&favicon_service_, std::move(image_fetcher_));
+ IconCacherImpl cacher(&favicon_service_, std::move(image_fetcher_));
cacher.StartFetch(site_, BindMockFunction(&done));
loop.Run();
EXPECT_FALSE(IconIsCachedFor(site_.url, favicon_base::FAVICON));
diff --git a/chromium/components/ntp_tiles/json_unsafe_parser.cc b/chromium/components/ntp_tiles/json_unsafe_parser.cc
new file mode 100644
index 00000000000..dc00533bb54
--- /dev/null
+++ b/chromium/components/ntp_tiles/json_unsafe_parser.cc
@@ -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.
+
+#include "components/ntp_tiles/json_unsafe_parser.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/json/json_parser.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+
+namespace ntp_tiles {
+
+void JsonUnsafeParser::Parse(const std::string& unsafe_json,
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ [](const std::string& unsafe_json,
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback) {
+ std::string error_msg;
+ int error_line, error_column;
+ std::unique_ptr<base::Value> value =
+ base::JSONReader::ReadAndReturnError(
+ unsafe_json, base::JSON_ALLOW_TRAILING_COMMAS, nullptr,
+ &error_msg, &error_line, &error_column);
+ if (value) {
+ success_callback.Run(std::move(value));
+ } else {
+ error_callback.Run(base::StringPrintf(
+ "%s (%d:%d)", error_msg.c_str(), error_line, error_column));
+ }
+ },
+ unsafe_json, success_callback, error_callback));
+}
+
+} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/json_unsafe_parser.h b/chromium/components/ntp_tiles/json_unsafe_parser.h
new file mode 100644
index 00000000000..4a5a1f47dcd
--- /dev/null
+++ b/chromium/components/ntp_tiles/json_unsafe_parser.h
@@ -0,0 +1,40 @@
+// 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_JSON_UNSAFE_PARSER_H_
+#define COMPONENTS_NTP_TILES_JSON_UNSAFE_PARSER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+
+namespace base {
+class Value;
+}
+
+namespace ntp_tiles {
+
+// Mimics SafeJsonParser, but parses unsafely.
+//
+// Do not use this class, unless you can't help it. On most platforms,
+// SafeJsonParser is available and is safer. If it is not available (e.g. on
+// iOS), then this class mimics its API without its safety.
+class JsonUnsafeParser {
+ public:
+ using SuccessCallback = base::Callback<void(std::unique_ptr<base::Value>)>;
+ using ErrorCallback = base::Callback<void(const std::string&)>;
+
+ // As with SafeJsonParser, runs either success_callback or error_callback on
+ // the calling thread, but not before the call returns.
+ static void Parse(const std::string& unsafe_json,
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback);
+
+ JsonUnsafeParser() = delete;
+};
+
+} // namespace ntp_tiles
+
+#endif // COMPONENTS_NTP_TILES_POPULAR_SITES_H_
diff --git a/chromium/components/ntp_tiles/metrics.cc b/chromium/components/ntp_tiles/metrics.cc
index 897a5c9be27..e407af25715 100644
--- a/chromium/components/ntp_tiles/metrics.cc
+++ b/chromium/components/ntp_tiles/metrics.cc
@@ -10,6 +10,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
#include "base/strings/stringprintf.h"
+#include "components/rappor/public/rappor_utils.h"
namespace ntp_tiles {
namespace metrics {
@@ -55,43 +56,69 @@ std::string GetSourceHistogramName(NTPTileSource source) {
} // namespace
-void RecordTileImpression(int index, NTPTileSource source) {
- UMA_HISTOGRAM_ENUMERATION("NewTabPage.SuggestionsImpression",
- static_cast<int>(index), kMaxNumTiles);
+void RecordPageImpression(const std::vector<TileImpression>& tiles,
+ rappor::RapporService* rappor_service) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.NumberOfTiles", tiles.size());
- std::string histogram =
- base::StringPrintf("NewTabPage.SuggestionsImpression.%s",
- GetSourceHistogramName(source).c_str());
- LogHistogramEvent(histogram, static_cast<int>(index), kMaxNumTiles);
-}
+ int counts_per_type[NUM_RECORDED_TILE_TYPES] = {0};
+ bool have_tile_types = false;
+ for (int index = 0; index < static_cast<int>(tiles.size()); index++) {
+ NTPTileSource source = tiles[index].source;
+ MostVisitedTileType tile_type = tiles[index].type;
+ const GURL& url = tiles[index].url;
-void RecordPageImpression(int number_of_tiles) {
- UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.NumberOfTiles", number_of_tiles);
-}
+ UMA_HISTOGRAM_ENUMERATION("NewTabPage.SuggestionsImpression", index,
+ kMaxNumTiles);
-void RecordImpressionTileTypes(
- const std::vector<MostVisitedTileType>& tile_types,
- const std::vector<NTPTileSource>& sources) {
- int counts_per_type[NUM_RECORDED_TILE_TYPES] = {0};
- for (size_t i = 0; i < tile_types.size(); ++i) {
- MostVisitedTileType tile_type = tile_types[i];
- DCHECK_LT(tile_type, NUM_RECORDED_TILE_TYPES);
+ std::string source_name = GetSourceHistogramName(source);
+ std::string impression_histogram = base::StringPrintf(
+ "NewTabPage.SuggestionsImpression.%s", source_name.c_str());
+ LogHistogramEvent(impression_histogram, index, kMaxNumTiles);
+
+ if (tile_type >= NUM_RECORDED_TILE_TYPES) {
+ continue;
+ }
+
+ have_tile_types = true;
++counts_per_type[tile_type];
UMA_HISTOGRAM_ENUMERATION("NewTabPage.TileType", tile_type,
NUM_RECORDED_TILE_TYPES);
- std::string histogram = base::StringPrintf(
- "NewTabPage.TileType.%s", GetSourceHistogramName(sources[i]).c_str());
- LogHistogramEvent(histogram, tile_type, NUM_RECORDED_TILE_TYPES);
+ std::string tile_type_histogram =
+ base::StringPrintf("NewTabPage.TileType.%s", source_name.c_str());
+ LogHistogramEvent(tile_type_histogram, tile_type, NUM_RECORDED_TILE_TYPES);
+
+ switch (tile_type) {
+ case NONE:
+ break;
+ case ICON_COLOR:
+ rappor::SampleDomainAndRegistryFromGURL(
+ rappor_service, "NTP.SuggestionsImpressions.IconsColor", url);
+ break;
+ case ICON_DEFAULT:
+ rappor::SampleDomainAndRegistryFromGURL(
+ rappor_service, "NTP.SuggestionsImpressions.IconsGray", url);
+ break;
+ case ICON_REAL:
+ rappor::SampleDomainAndRegistryFromGURL(
+ rappor_service, "NTP.SuggestionsImpressions.IconsReal", url);
+ break;
+ case NUM_RECORDED_TILE_TYPES: // Fall through.
+ case THUMBNAIL: // Fall through.
+ case UNKNOWN_TILE_TYPE:
+ NOTREACHED();
+ }
}
- UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsReal",
- counts_per_type[ICON_REAL]);
- UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsColor",
- counts_per_type[ICON_COLOR]);
- UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsGray",
- counts_per_type[ICON_DEFAULT]);
+ if (have_tile_types) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsReal",
+ counts_per_type[ICON_REAL]);
+ UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsColor",
+ counts_per_type[ICON_COLOR]);
+ UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.IconsGray",
+ counts_per_type[ICON_DEFAULT]);
+ }
}
void RecordTileClick(int index,
diff --git a/chromium/components/ntp_tiles/metrics.h b/chromium/components/ntp_tiles/metrics.h
index a7b5c0b98a5..009c4942b8b 100644
--- a/chromium/components/ntp_tiles/metrics.h
+++ b/chromium/components/ntp_tiles/metrics.h
@@ -5,9 +5,15 @@
#ifndef COMPONENTS_NTP_TILES_METRICS_H_
#define COMPONENTS_NTP_TILES_METRICS_H_
+#include <utility>
#include <vector>
#include "components/ntp_tiles/ntp_tile.h"
+#include "url/gurl.h"
+
+namespace rappor {
+class RapporService;
+} // namespace rappor
namespace ntp_tiles {
namespace metrics {
@@ -33,19 +39,27 @@ enum MostVisitedTileType {
NUM_RECORDED_TILE_TYPES,
// The item displays a thumbnail of the page. Used on desktop.
THUMBNAIL,
+ // The tile type has not been determined yet. Used on iOS, until we can detect
+ // when all tiles have loaded.
+ UNKNOWN_TILE_TYPE,
};
-// Records an impression of an individual tile.
-void RecordTileImpression(int index, NTPTileSource source);
+struct TileImpression {
+ TileImpression(NTPTileSource source,
+ MostVisitedTileType type,
+ const GURL& url)
+ : source(source), type(type), url(url) {}
-// Records an impression of the page itself, after all tiles have loaded.
-void RecordPageImpression(int number_of_tiles);
+ NTPTileSource source;
+ MostVisitedTileType type;
+ GURL url;
+};
-// Records the visual types (see above) of all visible tiles.
-// TODO(treib): Merge this with RecordPageImpression.
-void RecordImpressionTileTypes(
- const std::vector<MostVisitedTileType>& tile_types,
- const std::vector<NTPTileSource>& sources);
+// 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,
+ rappor::RapporService* rappor_service);
// Records a click on a tile.
void RecordTileClick(int index,
diff --git a/chromium/components/ntp_tiles/metrics_unittest.cc b/chromium/components/ntp_tiles/metrics_unittest.cc
new file mode 100644
index 00000000000..53438777ee5
--- /dev/null
+++ b/chromium/components/ntp_tiles/metrics_unittest.cc
@@ -0,0 +1,163 @@
+// 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/metrics.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/test/histogram_tester.h"
+#include "components/rappor/test_rappor_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ntp_tiles {
+namespace metrics {
+namespace {
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+
+TEST(RecordPageImpressionTest, ShouldRecordUmaForIcons) {
+ base::HistogramTester histogram_tester;
+ RecordPageImpression(
+ {{NTPTileSource::TOP_SITES, ICON_REAL, GURL()},
+ {NTPTileSource::TOP_SITES, ICON_REAL, GURL()},
+ {NTPTileSource::TOP_SITES, ICON_REAL, GURL()},
+ {NTPTileSource::TOP_SITES, ICON_COLOR, GURL()},
+ {NTPTileSource::TOP_SITES, ICON_COLOR, GURL()},
+ {NTPTileSource::SUGGESTIONS_SERVICE, ICON_REAL, GURL()},
+ {NTPTileSource::SUGGESTIONS_SERVICE, ICON_DEFAULT, GURL()},
+ {NTPTileSource::POPULAR, ICON_COLOR, GURL()}},
+ /*rappor_service=*/nullptr);
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.NumberOfTiles"),
+ ElementsAre(base::Bucket(/*min=*/8, /*count=*/1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
+ base::Bucket(/*min=*/1, /*count=*/1),
+ base::Bucket(/*min=*/2, /*count=*/1),
+ base::Bucket(/*min=*/3, /*count=*/1),
+ base::Bucket(/*min=*/4, /*count=*/1),
+ base::Bucket(/*min=*/5, /*count=*/1),
+ base::Bucket(/*min=*/6, /*count=*/1),
+ base::Bucket(/*min=*/7, /*count=*/1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.server"),
+ ElementsAre(base::Bucket(/*min=*/5, /*count=*/1),
+ base::Bucket(/*min=*/6, /*count=*/1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.client"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
+ base::Bucket(/*min=*/1, /*count=*/1),
+ base::Bucket(/*min=*/2, /*count=*/1),
+ base::Bucket(/*min=*/3, /*count=*/1),
+ base::Bucket(/*min=*/4, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.SuggestionsImpression.popular"),
+ ElementsAre(base::Bucket(/*min=*/7, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType"),
+ ElementsAre(base::Bucket(/*min=*/ICON_REAL, /*count=*/4),
+ base::Bucket(/*min=*/ICON_COLOR, /*count=*/3),
+ base::Bucket(/*min=*/ICON_DEFAULT, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.server"),
+ ElementsAre(base::Bucket(/*min=*/ICON_REAL, /*count=*/1),
+ base::Bucket(/*min=*/ICON_DEFAULT, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.client"),
+ ElementsAre(base::Bucket(/*min=*/ICON_REAL, /*count=*/3),
+ base::Bucket(/*min=*/ICON_COLOR, /*count=*/2)));
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.popular"),
+ ElementsAre(base::Bucket(/*min=*/ICON_COLOR, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.IconsReal"),
+ ElementsAre(base::Bucket(/*min=*/4, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.IconsColor"),
+ ElementsAre(base::Bucket(/*min=*/3, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.IconsGray"),
+ ElementsAre(base::Bucket(/*min=*/1, /*count=*/1)));
+}
+
+TEST(RecordPageImpressionTest, ShouldRecordUmaForThumbnails) {
+ base::HistogramTester histogram_tester;
+ RecordPageImpression({{NTPTileSource::TOP_SITES, THUMBNAIL, GURL()},
+ {NTPTileSource::SUGGESTIONS_SERVICE, THUMBNAIL, GURL()},
+ {NTPTileSource::POPULAR, THUMBNAIL, GURL()}},
+ /*rappor_service=*/nullptr);
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.NumberOfTiles"),
+ ElementsAre(base::Bucket(/*min=*/3, /*count=*/1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
+ base::Bucket(/*min=*/1, /*count=*/1),
+ base::Bucket(/*min=*/2, /*count=*/1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.server"),
+ ElementsAre(base::Bucket(/*min=*/1, /*count=*/1)));
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples("NewTabPage.SuggestionsImpression.client"),
+ ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(
+ "NewTabPage.SuggestionsImpression.popular"),
+ ElementsAre(base::Bucket(/*min=*/2, /*count=*/1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType"), IsEmpty());
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.server"),
+ IsEmpty());
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.client"),
+ IsEmpty());
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.TileType.popular"),
+ IsEmpty());
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.IconsReal"),
+ IsEmpty());
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.IconsColor"),
+ IsEmpty());
+ EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.IconsGray"),
+ IsEmpty());
+}
+
+TEST(RecordPageImpressionTest, ShouldRecordRappor) {
+ rappor::TestRapporServiceImpl rappor_service;
+
+ RecordPageImpression(
+ {{NTPTileSource::TOP_SITES, ICON_REAL, GURL("http://www.site1.com/")},
+ {NTPTileSource::TOP_SITES, ICON_COLOR, GURL("http://www.site2.com/")},
+ {NTPTileSource::TOP_SITES, ICON_DEFAULT, GURL("http://www.site3.com/")},
+ {NTPTileSource::TOP_SITES, THUMBNAIL, GURL("http://www.site4.com/")}},
+ &rappor_service);
+
+ // Thumbnail shouldn't get reported.
+ EXPECT_EQ(3, rappor_service.GetReportsCount());
+
+ {
+ std::string sample;
+ rappor::RapporType type;
+ EXPECT_TRUE(rappor_service.GetRecordedSampleForMetric(
+ "NTP.SuggestionsImpressions.IconsReal", &sample, &type));
+ EXPECT_EQ("site1.com", sample);
+ EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type);
+ }
+
+ {
+ std::string sample;
+ rappor::RapporType type;
+ EXPECT_TRUE(rappor_service.GetRecordedSampleForMetric(
+ "NTP.SuggestionsImpressions.IconsColor", &sample, &type));
+ EXPECT_EQ("site2.com", sample);
+ EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type);
+ }
+
+ {
+ std::string sample;
+ rappor::RapporType type;
+ EXPECT_TRUE(rappor_service.GetRecordedSampleForMetric(
+ "NTP.SuggestionsImpressions.IconsGray", &sample, &type));
+ EXPECT_EQ("site3.com", sample);
+ EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type);
+ }
+}
+
+} // namespace
+} // namespace metrics
+} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/most_visited_sites.cc b/chromium/components/ntp_tiles/most_visited_sites.cc
index 42fa3f41994..2b73d4c4ac9 100644
--- a/chromium/components/ntp_tiles/most_visited_sites.cc
+++ b/chromium/components/ntp_tiles/most_visited_sites.cc
@@ -9,6 +9,7 @@
#include <string>
#include <utility>
+#include "base/bind.h"
#include "base/callback.h"
#include "base/feature_list.h"
#include "base/strings/utf_string_conversions.h"
@@ -16,7 +17,6 @@
#include "components/ntp_tiles/constants.h"
#include "components/ntp_tiles/field_trial.h"
#include "components/ntp_tiles/icon_cacher.h"
-#include "components/ntp_tiles/metrics.h"
#include "components/ntp_tiles/pref_names.h"
#include "components/ntp_tiles/switches.h"
#include "components/pref_registry/pref_registry_syncable.h"
@@ -47,26 +47,24 @@ bool AreURLsEquivalent(const GURL& url1, const GURL& url2) {
} // namespace
-MostVisitedSites::MostVisitedSites(PrefService* prefs,
- scoped_refptr<history::TopSites> top_sites,
- SuggestionsService* suggestions,
- std::unique_ptr<PopularSites> popular_sites,
- std::unique_ptr<IconCacher> icon_cacher,
- MostVisitedSitesSupervisor* supervisor)
+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)
: prefs_(prefs),
top_sites_(top_sites),
suggestions_service_(suggestions),
popular_sites_(std::move(popular_sites)),
icon_cacher_(std::move(icon_cacher)),
- supervisor_(supervisor),
+ supervisor_(std::move(supervisor)),
observer_(nullptr),
num_sites_(0),
- waiting_for_most_visited_sites_(true),
- waiting_for_popular_sites_(true),
- recorded_impressions_(false),
top_sites_observer_(this),
- mv_source_(NTPTileSource::SUGGESTIONS_SERVICE),
- weak_ptr_factory_(this) {
+ mv_source_(NTPTileSource::TOP_SITES),
+ top_sites_weak_ptr_factory_(this) {
DCHECK(prefs_);
// top_sites_ can be null in tests.
// TODO(sfiera): have iOS use a dummy TopSites in its tests.
@@ -86,15 +84,13 @@ void MostVisitedSites::SetMostVisitedURLsObserver(Observer* observer,
observer_ = observer;
num_sites_ = num_sites;
- // The order for this condition is important, ShouldShowPopularSite() should
+ // The order for this condition is important, ShouldShowPopularSites() should
// always be called last to keep metrics as relevant as possible.
if (popular_sites_ && NeedPopularSites(prefs_, num_sites_) &&
ShouldShowPopularSites()) {
- popular_sites_->StartFetch(
- false, base::Bind(&MostVisitedSites::OnPopularSitesAvailable,
+ popular_sites_->MaybeStartFetch(
+ false, base::Bind(&MostVisitedSites::OnPopularSitesDownloaded,
base::Unretained(this)));
- } else {
- waiting_for_popular_sites_ = false;
}
if (top_sites_) {
@@ -115,6 +111,10 @@ void MostVisitedSites::SetMostVisitedURLsObserver(Observer* observer,
// SuggestionsService's cache or, if that is empty, sites from TopSites.
BuildCurrentTiles();
// Also start a request for fresh suggestions.
+ Refresh();
+}
+
+void MostVisitedSites::Refresh() {
suggestions_service_->FetchSuggestionsData();
}
@@ -137,6 +137,18 @@ void MostVisitedSites::AddOrRemoveBlacklistedUrl(const GURL& url,
}
}
+void MostVisitedSites::ClearBlacklistedUrls() {
+ if (top_sites_) {
+ // Always update the blacklist in the local TopSites.
+ top_sites_->ClearBlacklistedURLs();
+ }
+
+ // Only update the server-side blacklist if it's active.
+ if (mv_source_ == NTPTileSource::SUGGESTIONS_SERVICE) {
+ suggestions_service_->ClearBlacklist();
+ }
+}
+
void MostVisitedSites::OnBlockedSitesChanged() {
BuildCurrentTiles();
}
@@ -151,15 +163,18 @@ void MostVisitedSites::BuildCurrentTiles() {
// Get the current suggestions from cache. If the cache is empty, this will
// fall back to TopSites.
OnSuggestionsProfileAvailable(
- suggestions_service_->GetSuggestionsDataFromCache());
+ suggestions_service_->GetSuggestionsDataFromCache().value_or(
+ SuggestionsProfile()));
}
void MostVisitedSites::InitiateTopSitesQuery() {
if (!top_sites_)
return;
+ if (top_sites_weak_ptr_factory_.HasWeakPtrs())
+ return; // Ongoing query.
top_sites_->GetMostVisitedURLs(
base::Bind(&MostVisitedSites::OnMostVisitedURLsAvailable,
- weak_ptr_factory_.GetWeakPtr()),
+ top_sites_weak_ptr_factory_.GetWeakPtr()),
false);
}
@@ -175,6 +190,12 @@ base::FilePath MostVisitedSites::GetWhitelistLargeIconPath(const GURL& url) {
void MostVisitedSites::OnMostVisitedURLsAvailable(
const history::MostVisitedURLList& visited_list) {
+ // Ignore the event if tiles provided by the Suggestions Service, which take
+ // precedence.
+ if (mv_source_ == NTPTileSource::SUGGESTIONS_SERVICE) {
+ return;
+ }
+
NTPTilesVector tiles;
size_t num_tiles =
std::min(visited_list.size(), static_cast<size_t>(num_sites_));
@@ -196,7 +217,6 @@ void MostVisitedSites::OnMostVisitedURLsAvailable(
tiles.push_back(std::move(tile));
}
- waiting_for_most_visited_sites_ = false;
mv_source_ = NTPTileSource::TOP_SITES;
SaveNewTiles(std::move(tiles));
NotifyMostVisitedURLsObserver();
@@ -208,6 +228,7 @@ void MostVisitedSites::OnSuggestionsProfileAvailable(
// With no server suggestions, fall back to local TopSites.
if (num_tiles == 0 ||
!base::FeatureList::IsEnabled(kDisplaySuggestionsServiceTiles)) {
+ mv_source_ = NTPTileSource::TOP_SITES;
InitiateTopSitesQuery();
return;
}
@@ -226,11 +247,12 @@ void MostVisitedSites::OnSuggestionsProfileAvailable(
tile.url = url;
tile.source = NTPTileSource::SUGGESTIONS_SERVICE;
tile.whitelist_icon_path = GetWhitelistLargeIconPath(url);
+ tile.thumbnail_url = GURL(suggestion_pb.thumbnail());
+ tile.favicon_url = GURL(suggestion_pb.favicon_url());
tiles.push_back(std::move(tile));
}
- waiting_for_most_visited_sites_ = false;
mv_source_ = NTPTileSource::SUGGESTIONS_SERVICE;
SaveNewTiles(std::move(tiles));
NotifyMostVisitedURLsObserver();
@@ -364,25 +386,13 @@ NTPTilesVector MostVisitedSites::MergeTiles(NTPTilesVector personal_tiles,
}
void MostVisitedSites::NotifyMostVisitedURLsObserver() {
- if (!waiting_for_most_visited_sites_ && !waiting_for_popular_sites_ &&
- !recorded_impressions_) {
- // TODO(treib): Move this out of here. crbug.com/514752
- int num_tiles = static_cast<int>(current_tiles_.size());
- for (int i = 0; i < num_tiles; i++)
- metrics::RecordTileImpression(i, current_tiles_[i].source);
- metrics::RecordPageImpression(num_tiles);
- recorded_impressions_ = true;
- }
-
if (!observer_)
return;
observer_->OnMostVisitedURLsAvailable(current_tiles_);
}
-void MostVisitedSites::OnPopularSitesAvailable(bool success) {
- waiting_for_popular_sites_ = false;
-
+void MostVisitedSites::OnPopularSitesDownloaded(bool success) {
if (!success) {
LOG(WARNING) << "Download of popular sites failed";
return;
diff --git a/chromium/components/ntp_tiles/most_visited_sites.h b/chromium/components/ntp_tiles/most_visited_sites.h
index acecefaa8c6..a538d2ef19f 100644
--- a/chromium/components/ntp_tiles/most_visited_sites.h
+++ b/chromium/components/ntp_tiles/most_visited_sites.h
@@ -32,6 +32,8 @@ namespace user_prefs {
class PrefRegistrySyncable;
}
+class PrefService;
+
namespace ntp_tiles {
class IconCacher;
@@ -53,6 +55,8 @@ class MostVisitedSitesSupervisor {
~Observer() {}
};
+ virtual ~MostVisitedSitesSupervisor() {}
+
// Pass non-null to set observer, or null to remove observer.
// If setting observer, there must not yet be an observer set.
// If removing observer, there must already be one to remove.
@@ -67,17 +71,12 @@ class MostVisitedSitesSupervisor {
// If true, be conservative about suggesting sites from outside sources.
virtual bool IsChildProfile() = 0;
-
- protected:
- virtual ~MostVisitedSitesSupervisor() {}
};
// Tracks the list of most visited sites and their thumbnails.
class MostVisitedSites : public history::TopSitesObserver,
public MostVisitedSitesSupervisor::Observer {
public:
- using PopularSitesVector = std::vector<PopularSites::Site>;
-
// The observer to be notified when the list of most visited sites changes.
class Observer {
public:
@@ -98,24 +97,34 @@ class MostVisitedSites : public history::TopSitesObserver,
suggestions::SuggestionsService* suggestions,
std::unique_ptr<PopularSites> popular_sites,
std::unique_ptr<IconCacher> icon_cacher,
- MostVisitedSitesSupervisor* supervisor);
+ std::unique_ptr<MostVisitedSitesSupervisor> supervisor);
~MostVisitedSites() override;
+ // Sets the observer, and immediately fetches the current suggestions.
// Does not take ownership of |observer|, which must outlive this object and
// must not be null.
void SetMostVisitedURLsObserver(Observer* observer, int num_sites);
+ // Requests an asynchronous refresh of the suggestions. Notifies the observer
+ // once the request completes.
+ void Refresh();
+
void AddOrRemoveBlacklistedUrl(const GURL& url, bool add_url);
+ void ClearBlacklistedUrls();
// MostVisitedSitesSupervisor::Observer implementation.
void OnBlockedSitesChanged() override;
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
- private:
- friend class MostVisitedSitesTest;
+ // Workhorse for SaveNewTiles. Implemented as a separate static and public
+ // method for ease of testing.
+ static NTPTilesVector MergeTiles(NTPTilesVector personal_tiles,
+ NTPTilesVector whitelist_tiles,
+ NTPTilesVector popular_tiles);
+ private:
void BuildCurrentTiles();
// Initialize the query to Top Sites. Called if the SuggestionsService
@@ -147,17 +156,11 @@ class MostVisitedSites : public history::TopSitesObserver,
// if appropriate, and saves the new tiles.
void SaveNewTiles(NTPTilesVector personal_tiles);
- // Workhorse for SaveNewTiles above. Implemented as a separate static method
- // for ease of testing.
- static NTPTilesVector MergeTiles(NTPTilesVector personal_tiles,
- NTPTilesVector whitelist_tiles,
- NTPTilesVector popular_tiles);
-
// Notifies the observer about the availability of tiles.
// Also records impressions UMA if not done already.
void NotifyMostVisitedURLsObserver();
- void OnPopularSitesAvailable(bool success);
+ void OnPopularSitesDownloaded(bool success);
void OnIconMadeAvailable(const GURL& site_url, bool newly_available);
@@ -171,25 +174,13 @@ class MostVisitedSites : public history::TopSitesObserver,
suggestions::SuggestionsService* suggestions_service_;
std::unique_ptr<PopularSites> const popular_sites_;
std::unique_ptr<IconCacher> const icon_cacher_;
- MostVisitedSitesSupervisor* supervisor_;
+ std::unique_ptr<MostVisitedSitesSupervisor> supervisor_;
Observer* observer_;
// The maximum number of most visited sites to return.
int num_sites_;
- // True if we are still waiting for an initial set of most visited sites (from
- // either TopSites or the SuggestionsService).
- bool waiting_for_most_visited_sites_;
-
- // True if we are still waiting for the set of popular sites. Immediately set
- // to false if popular sites are disabled, or are not required.
- bool waiting_for_popular_sites_;
-
- // True if we have recorded impression metrics. They are recorded once both
- // the previous flags are false.
- bool recorded_impressions_;
-
std::unique_ptr<
suggestions::SuggestionsService::ResponseCallbackList::Subscription>
suggestions_subscription_;
@@ -202,8 +193,9 @@ class MostVisitedSites : public history::TopSitesObserver,
NTPTilesVector current_tiles_;
- // For callbacks may be run after destruction.
- base::WeakPtrFactory<MostVisitedSites> weak_ptr_factory_;
+ // For callbacks may be run after destruction, used exclusively for TopSites
+ // (since it's used to detect whether there's a query in flight).
+ base::WeakPtrFactory<MostVisitedSites> top_sites_weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(MostVisitedSites);
};
diff --git a/chromium/components/ntp_tiles/most_visited_sites_unittest.cc b/chromium/components/ntp_tiles/most_visited_sites_unittest.cc
index 6fbc9dcd087..56f737de4d1 100644
--- a/chromium/components/ntp_tiles/most_visited_sites_unittest.cc
+++ b/chromium/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -7,34 +7,714 @@
#include <stddef.h>
#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
#include <vector>
+#include "base/callback_list.h"
+#include "base/command_line.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/strings/utf_string_conversions.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "base/test/sequenced_worker_pool_owner.h"
+#include "components/history/core/browser/top_sites.h"
+#include "components/ntp_tiles/icon_cacher.h"
+#include "components/ntp_tiles/json_unsafe_parser.h"
+#include "components/ntp_tiles/popular_sites_impl.h"
+#include "components/ntp_tiles/pref_names.h"
+#include "components/ntp_tiles/switches.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ntp_tiles {
+// Defined for googletest. Must be defined in the same namespace.
+void PrintTo(const NTPTile& tile, std::ostream* os) {
+ *os << "{\"" << tile.title << "\", \"" << tile.url << "\", "
+ << static_cast<int>(tile.source) << "}";
+}
+
namespace {
-struct TitleURL {
- TitleURL(const std::string& title, const std::string& url)
- : title(base::UTF8ToUTF16(title)), url(url) {}
- TitleURL(const base::string16& title, const std::string& url)
- : title(title), url(url) {}
+using history::MostVisitedURL;
+using history::MostVisitedURLList;
+using history::TopSites;
+using suggestions::ChromeSuggestion;
+using suggestions::SuggestionsProfile;
+using suggestions::SuggestionsService;
+using testing::AtLeast;
+using testing::ByMove;
+using testing::ElementsAre;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Mock;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SizeIs;
+using testing::StrictMock;
+using testing::_;
+
+MATCHER_P3(MatchesTile,
+ title,
+ url,
+ source,
+ std::string("has title \"") + title + std::string("\" and url \"") +
+ url +
+ std::string("\" and source ") +
+ testing::PrintToString(static_cast<int>(source))) {
+ return arg.title == base::ASCIIToUTF16(title) && arg.url == GURL(url) &&
+ arg.source == source;
+}
+
+// testing::InvokeArgument<N> does not work with base::Callback, fortunately
+// gmock makes it simple to create action templates that do for the various
+// possible numbers of arguments.
+ACTION_TEMPLATE(InvokeCallbackArgument,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_1_VALUE_PARAMS(p0)) {
+ ::std::tr1::get<k>(args).Run(p0);
+}
- base::string16 title;
- std::string url;
+NTPTile MakeTile(const std::string& title,
+ const std::string& url,
+ NTPTileSource source) {
+ NTPTile tile;
+ tile.title = base::ASCIIToUTF16(title);
+ tile.url = GURL(url);
+ tile.source = source;
+ return tile;
+}
- bool operator==(const TitleURL& other) const {
- return title == other.title && url == other.url;
+ChromeSuggestion MakeSuggestion(const std::string& title,
+ const std::string& url) {
+ ChromeSuggestion suggestion;
+ suggestion.set_title(title);
+ suggestion.set_url(url);
+ return suggestion;
+}
+
+SuggestionsProfile MakeProfile(
+ const std::vector<ChromeSuggestion>& suggestions) {
+ SuggestionsProfile profile;
+ for (const ChromeSuggestion& suggestion : suggestions) {
+ *profile.add_suggestions() = suggestion;
}
+ return profile;
+}
+
+MostVisitedURL MakeMostVisitedURL(const std::string& title,
+ const std::string& url) {
+ MostVisitedURL result;
+ result.title = base::ASCIIToUTF16(title);
+ result.url = GURL(url);
+ return result;
+}
+
+class MockTopSites : public TopSites {
+ public:
+ MOCK_METHOD0(ShutdownOnUIThread, void());
+ MOCK_METHOD3(SetPageThumbnail,
+ bool(const GURL& url,
+ const gfx::Image& thumbnail,
+ const ThumbnailScore& score));
+ MOCK_METHOD3(SetPageThumbnailToJPEGBytes,
+ bool(const GURL& url,
+ const base::RefCountedMemory* memory,
+ const ThumbnailScore& score));
+ MOCK_METHOD2(GetMostVisitedURLs,
+ void(const GetMostVisitedURLsCallback& callback,
+ bool include_forced_urls));
+ MOCK_METHOD3(GetPageThumbnail,
+ bool(const GURL& url,
+ bool prefix_match,
+ scoped_refptr<base::RefCountedMemory>* bytes));
+ MOCK_METHOD2(GetPageThumbnailScore,
+ bool(const GURL& url, ThumbnailScore* score));
+ MOCK_METHOD2(GetTemporaryPageThumbnailScore,
+ bool(const GURL& url, ThumbnailScore* score));
+ MOCK_METHOD0(SyncWithHistory, void());
+ MOCK_CONST_METHOD0(HasBlacklistedItems, bool());
+ MOCK_METHOD1(AddBlacklistedURL, void(const GURL& url));
+ MOCK_METHOD1(RemoveBlacklistedURL, void(const GURL& url));
+ MOCK_METHOD1(IsBlacklisted, bool(const GURL& url));
+ MOCK_METHOD0(ClearBlacklistedURLs, void());
+ MOCK_METHOD0(StartQueryForMostVisited, base::CancelableTaskTracker::TaskId());
+ MOCK_METHOD1(IsKnownURL, bool(const GURL& url));
+ MOCK_CONST_METHOD1(GetCanonicalURLString,
+ const std::string&(const GURL& url));
+ MOCK_METHOD0(IsNonForcedFull, bool());
+ MOCK_METHOD0(IsForcedFull, bool());
+ MOCK_CONST_METHOD0(loaded, bool());
+ MOCK_METHOD0(GetPrepopulatedPages, history::PrepopulatedPageList());
+ MOCK_METHOD2(AddForcedURL, bool(const GURL& url, const base::Time& time));
+ MOCK_METHOD1(OnNavigationCommitted, void(const GURL& url));
+
+ protected:
+ ~MockTopSites() override = default;
};
-static const size_t kNumSites = 4;
+class MockSuggestionsService : public SuggestionsService {
+ public:
+ MOCK_METHOD0(FetchSuggestionsData, bool());
+ MOCK_CONST_METHOD0(GetSuggestionsDataFromCache,
+ base::Optional<SuggestionsProfile>());
+ MOCK_METHOD1(AddCallback,
+ std::unique_ptr<ResponseCallbackList::Subscription>(
+ const ResponseCallback& callback));
+ MOCK_METHOD2(GetPageThumbnail,
+ void(const GURL& url, const BitmapCallback& callback));
+ MOCK_METHOD3(GetPageThumbnailWithURL,
+ void(const GURL& url,
+ const GURL& thumbnail_url,
+ const BitmapCallback& callback));
+ MOCK_METHOD1(BlacklistURL, bool(const GURL& candidate_url));
+ MOCK_METHOD1(UndoBlacklistURL, bool(const GURL& url));
+ MOCK_METHOD0(ClearBlacklist, void());
+};
-} // namespace
+class MockMostVisitedSitesObserver : public MostVisitedSites::Observer {
+ public:
+ MOCK_METHOD1(OnMostVisitedURLsAvailable, void(const NTPTilesVector& tiles));
+ MOCK_METHOD1(OnIconMadeAvailable, void(const GURL& site_url));
+};
+
+class MockIconCacher : public IconCacher {
+ public:
+ MOCK_METHOD2(StartFetch,
+ void(PopularSites::Site site,
+ const base::Callback<void(bool)>& done));
+};
+
+class PopularSitesFactoryForTest {
+ public:
+ PopularSitesFactoryForTest(
+ bool enabled,
+ sync_preferences::TestingPrefServiceSyncable* pref_service)
+ : 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.") {
+ if (enabled) {
+ PopularSitesImpl::RegisterProfilePrefs(pref_service->registry());
+
+ prefs_->SetString(prefs::kPopularSitesOverrideCountry, "IN");
+ prefs_->SetString(prefs::kPopularSitesOverrideVersion, "7");
+
+ url_fetcher_factory_.SetFakeResponse(
+ GURL("https://www.gstatic.com/chrome/ntp/suggested_sites_IN_7.json"),
+ R"([{
+ "title": "PopularSite1",
+ "url": "http://popularsite1/",
+ "favicon_url": "http://popularsite1/favicon.ico"
+ },
+ {
+ "title": "PopularSite2",
+ "url": "http://popularsite2/",
+ "favicon_url": "http://popularsite2/favicon.ico"
+ },
+ ])",
+ net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ }
+ }
+
+ std::unique_ptr<PopularSites> New() {
+ return base::MakeUnique<PopularSitesImpl>(
+ worker_pool_owner_.pool().get(), prefs_,
+ /*template_url_service=*/nullptr,
+ /*variations_service=*/nullptr, url_request_context_.get(),
+ /*directory=*/base::FilePath(), 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
+// implementation in TopSites (which doesn't use base::CallbackList).
+class TopSitesCallbackList {
+ public:
+ // Second argument declared to match the signature of GetMostVisitedURLs().
+ void Add(const TopSites::GetMostVisitedURLsCallback& callback,
+ testing::Unused) {
+ callbacks_.push_back(callback);
+ }
+
+ void ClearAndNotify(const MostVisitedURLList& list) {
+ std::vector<TopSites::GetMostVisitedURLsCallback> callbacks;
+ callbacks.swap(callbacks_);
+ for (const auto& callback : callbacks) {
+ callback.Run(list);
+ }
+ }
+
+ bool empty() const { return callbacks_.empty(); }
+
+ private:
+ std::vector<TopSites::GetMostVisitedURLsCallback> callbacks_;
+};
+
+// Param specifies whether Popular Sites is enabled via variations.
+class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
+ protected:
+ MostVisitedSitesTest()
+ : popular_sites_factory_(/*enabled=*/GetParam(), &pref_service_),
+ mock_top_sites_(new StrictMock<MockTopSites>()) {
+ MostVisitedSites::RegisterProfilePrefs(pref_service_.registry());
+
+ if (IsPopularSitesEnabledViaVariations()) {
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableNTPPopularSites);
+ } else {
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kDisableNTPPopularSites);
+ }
+
+ // We use StrictMock to make sure the object is not used unless Popular
+ // Sites is enabled.
+ auto icon_cacher = base::MakeUnique<StrictMock<MockIconCacher>>();
+
+ if (IsPopularSitesEnabledViaVariations()) {
+ // Populate Popular Sites' internal cache by mimicking a past usage of
+ // PopularSitesImpl.
+ auto tmp_popular_sites = popular_sites_factory_.New();
+ base::RunLoop loop;
+ bool save_success = false;
+ tmp_popular_sites->MaybeStartFetch(
+ /*force_download=*/true,
+ base::Bind(
+ [](bool* save_success, base::RunLoop* loop, bool success) {
+ *save_success = success;
+ loop->Quit();
+ },
+ &save_success, &loop));
+ loop.Run();
+ EXPECT_TRUE(save_success);
+
+ // With PopularSites enabled, blacklist is exercised.
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(_))
+ .WillRepeatedly(Return(false));
+ // Mock icon cacher never replies, and we also don't verify whether the
+ // code uses it correctly.
+ EXPECT_CALL(*icon_cacher, StartFetch(_, _)).Times(AtLeast(0));
+ }
+
+ most_visited_sites_ = base::MakeUnique<MostVisitedSites>(
+ &pref_service_, mock_top_sites_, &mock_suggestions_service_,
+ popular_sites_factory_.New(), std::move(icon_cacher),
+ /*supervisor=*/nullptr);
+ }
+
+ bool IsPopularSitesEnabledViaVariations() const { return GetParam(); }
+
+ bool VerifyAndClearExpectations() {
+ base::RunLoop().RunUntilIdle();
+ const bool success =
+ Mock::VerifyAndClearExpectations(mock_top_sites_.get()) &&
+ Mock::VerifyAndClearExpectations(&mock_suggestions_service_) &&
+ Mock::VerifyAndClearExpectations(&mock_observer_);
+ // For convenience, restore the expectations for IsBlacklisted().
+ if (IsPopularSitesEnabledViaVariations()) {
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(_))
+ .WillRepeatedly(Return(false));
+ }
+ return success;
+ }
+
+ base::CallbackList<SuggestionsService::ResponseCallback::RunType>
+ suggestions_service_callbacks_;
+ TopSitesCallbackList top_sites_callbacks_;
+
+ base::MessageLoop message_loop_;
+ sync_preferences::TestingPrefServiceSyncable pref_service_;
+ PopularSitesFactoryForTest popular_sites_factory_;
+ scoped_refptr<StrictMock<MockTopSites>> mock_top_sites_;
+ StrictMock<MockSuggestionsService> mock_suggestions_service_;
+ StrictMock<MockMostVisitedSitesObserver> mock_observer_;
+ std::unique_ptr<MostVisitedSites> most_visited_sites_;
+};
+
+TEST_P(MostVisitedSitesTest, ShouldStartNoCallInConstructor) {
+ // No call to mocks expected by the mere fact of instantiating
+ // MostVisitedSites.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesTest, ShouldHandleTopSitesCacheHit) {
+ // If cached, TopSites returns the tiles synchronously, running the callback
+ // even before the function returns.
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+ .WillRepeatedly(InvokeCallbackArgument<0>(
+ MostVisitedURLList{MakeMostVisitedURL("Site 1", "http://site1/")}));
+
+ InSequence seq;
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
+ EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
+ .WillOnce(Invoke(&suggestions_service_callbacks_,
+ &SuggestionsService::ResponseCallbackList::Add));
+ EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
+ .WillOnce(Return(SuggestionsProfile())); // Empty cache.
+ if (IsPopularSitesEnabledViaVariations()) {
+ EXPECT_CALL(
+ mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 1", "http://site1/", NTPTileSource::TOP_SITES),
+ MatchesTile("PopularSite1", "http://popularsite1/",
+ NTPTileSource::POPULAR),
+ MatchesTile("PopularSite2", "http://popularsite2/",
+ NTPTileSource::POPULAR))));
+ } else {
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile(
+ "Site 1", "http://site1/", NTPTileSource::TOP_SITES))));
+ }
+ EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData())
+ .WillOnce(Return(true));
+
+ most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+ /*num_sites=*/3);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(suggestions_service_callbacks_.empty());
+ CHECK(top_sites_callbacks_.empty());
+}
+
+INSTANTIATE_TEST_CASE_P(MostVisitedSitesTest,
+ MostVisitedSitesTest,
+ ::testing::Bool());
+
+class MostVisitedSitesWithCacheHitTest : public MostVisitedSitesTest {
+ public:
+ // Constructor sets the common expectations for the case where suggestions
+ // service has cached results when the observer is registered.
+ MostVisitedSitesWithCacheHitTest() {
+ InSequence seq;
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
+ EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
+ .WillOnce(Invoke(&suggestions_service_callbacks_,
+ &SuggestionsService::ResponseCallbackList::Add));
+ EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
+ .WillOnce(Return(MakeProfile({
+ MakeSuggestion("Site 1", "http://site1/"),
+ MakeSuggestion("Site 2", "http://site2/"),
+ MakeSuggestion("Site 3", "http://site3/"),
+ })));
+ if (IsPopularSitesEnabledViaVariations()) {
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 1", "http://site1/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 2", "http://site2/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 3", "http://site3/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("PopularSite1", "http://popularsite1/",
+ NTPTileSource::POPULAR))));
+ } else {
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 1", "http://site1/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 2", "http://site2/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 3", "http://site3/",
+ NTPTileSource::SUGGESTIONS_SERVICE))));
+ }
+ EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData())
+ .WillOnce(Return(true));
+
+ most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+ /*num_sites=*/4);
+ VerifyAndClearExpectations();
+
+ EXPECT_FALSE(suggestions_service_callbacks_.empty());
+ EXPECT_TRUE(top_sites_callbacks_.empty());
+ }
+};
+
+TEST_P(MostVisitedSitesWithCacheHitTest, ShouldFavorSuggestionsServiceCache) {
+ // Constructor sets basic expectations for a suggestions service cache hit.
+}
+
+TEST_P(MostVisitedSitesWithCacheHitTest,
+ ShouldPropagateUpdateBySuggestionsService) {
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 4", "http://site4/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 5", "http://site5/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 6", "http://site6/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 7", "http://site7/",
+ NTPTileSource::SUGGESTIONS_SERVICE))));
+ suggestions_service_callbacks_.Notify(
+ MakeProfile({MakeSuggestion("Site 4", "http://site4/"),
+ MakeSuggestion("Site 5", "http://site5/"),
+ MakeSuggestion("Site 6", "http://site6/"),
+ MakeSuggestion("Site 7", "http://site7/")}));
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesWithCacheHitTest, ShouldTruncateList) {
+ EXPECT_CALL(mock_observer_, OnMostVisitedURLsAvailable(SizeIs(4)));
+ suggestions_service_callbacks_.Notify(
+ MakeProfile({MakeSuggestion("Site 4", "http://site4/"),
+ MakeSuggestion("Site 5", "http://site5/"),
+ MakeSuggestion("Site 6", "http://site6/"),
+ MakeSuggestion("Site 7", "http://site7/"),
+ MakeSuggestion("Site 8", "http://site8/")}));
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesWithCacheHitTest,
+ ShouldCompleteWithPopularSitesIffEnabled) {
+ if (IsPopularSitesEnabledViaVariations()) {
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 4", "http://site4/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("PopularSite1", "http://popularsite1/",
+ NTPTileSource::POPULAR),
+ MatchesTile("PopularSite2", "http://popularsite2/",
+ NTPTileSource::POPULAR))));
+ } else {
+ EXPECT_CALL(
+ mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile(
+ "Site 4", "http://site4/", NTPTileSource::SUGGESTIONS_SERVICE))));
+ }
+ suggestions_service_callbacks_.Notify(
+ MakeProfile({MakeSuggestion("Site 4", "http://site4/")}));
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesWithCacheHitTest,
+ ShouldSwitchToTopSitesIfEmptyUpdateBySuggestionsService) {
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+ .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add));
+ suggestions_service_callbacks_.Notify(SuggestionsProfile());
+ VerifyAndClearExpectations();
+
+ EXPECT_CALL(
+ mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 4", "http://site4/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 5", "http://site5/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 6", "http://site6/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 7", "http://site7/", NTPTileSource::TOP_SITES))));
+ top_sites_callbacks_.ClearAndNotify(
+ {MakeMostVisitedURL("Site 4", "http://site4/"),
+ MakeMostVisitedURL("Site 5", "http://site5/"),
+ MakeMostVisitedURL("Site 6", "http://site6/"),
+ MakeMostVisitedURL("Site 7", "http://site7/")});
+ base::RunLoop().RunUntilIdle();
+}
+
+INSTANTIATE_TEST_CASE_P(MostVisitedSitesWithCacheHitTest,
+ MostVisitedSitesWithCacheHitTest,
+ ::testing::Bool());
+
+class MostVisitedSitesWithEmptyCacheTest : public MostVisitedSitesTest {
+ public:
+ // Constructor sets the common expectations for the case where suggestions
+ // service doesn't have cached results when the observer is registered.
+ MostVisitedSitesWithEmptyCacheTest() {
+ InSequence seq;
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
+ EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
+ .WillOnce(Invoke(&suggestions_service_callbacks_,
+ &SuggestionsService::ResponseCallbackList::Add));
+ EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
+ .WillOnce(Return(SuggestionsProfile())); // Empty cache.
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+ .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add));
+ EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData())
+ .WillOnce(Return(true));
+
+ most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+ /*num_sites=*/3);
+ VerifyAndClearExpectations();
+
+ EXPECT_FALSE(suggestions_service_callbacks_.empty());
+ EXPECT_FALSE(top_sites_callbacks_.empty());
+ }
+};
+
+TEST_P(MostVisitedSitesWithEmptyCacheTest,
+ ShouldQueryTopSitesAndSuggestionsService) {
+ // Constructor sets basic expectations for a suggestions service cache miss.
+}
+
+TEST_P(MostVisitedSitesWithEmptyCacheTest,
+ ShouldCompleteWithPopularSitesIffEnabled) {
+ if (IsPopularSitesEnabledViaVariations()) {
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 4", "http://site4/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("PopularSite1", "http://popularsite1/",
+ NTPTileSource::POPULAR),
+ MatchesTile("PopularSite2", "http://popularsite2/",
+ NTPTileSource::POPULAR))));
+ } else {
+ EXPECT_CALL(
+ mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(MatchesTile(
+ "Site 4", "http://site4/", NTPTileSource::SUGGESTIONS_SERVICE))));
+ }
+ suggestions_service_callbacks_.Notify(
+ MakeProfile({MakeSuggestion("Site 4", "http://site4/")}));
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesWithEmptyCacheTest,
+ ShouldIgnoreTopSitesIfSuggestionsServiceFaster) {
+ // Reply from suggestions service triggers and update to our observer.
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 1", "http://site1/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 2", "http://site2/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 3", "http://site3/",
+ NTPTileSource::SUGGESTIONS_SERVICE))));
+ suggestions_service_callbacks_.Notify(
+ MakeProfile({MakeSuggestion("Site 1", "http://site1/"),
+ MakeSuggestion("Site 2", "http://site2/"),
+ MakeSuggestion("Site 3", "http://site3/")}));
+ VerifyAndClearExpectations();
+
+ // Reply from top sites is ignored (i.e. not reported to observer).
+ top_sites_callbacks_.ClearAndNotify(
+ {MakeMostVisitedURL("Site 4", "http://site4/")});
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesWithEmptyCacheTest,
+ ShouldExposeTopSitesIfSuggestionsServiceFasterButEmpty) {
+ // Empty reply from suggestions service causes no update to our observer.
+ suggestions_service_callbacks_.Notify(SuggestionsProfile());
+ VerifyAndClearExpectations();
+
+ // Reply from top sites is propagated to observer.
+ EXPECT_CALL(
+ mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 1", "http://site1/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 2", "http://site2/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 3", "http://site3/", NTPTileSource::TOP_SITES))));
+ top_sites_callbacks_.ClearAndNotify(
+ {MakeMostVisitedURL("Site 1", "http://site1/"),
+ MakeMostVisitedURL("Site 2", "http://site2/"),
+ MakeMostVisitedURL("Site 3", "http://site3/")});
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesWithEmptyCacheTest,
+ ShouldFavorSuggestionsServiceAlthoughSlower) {
+ // Reply from top sites is propagated to observer.
+ EXPECT_CALL(
+ mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 1", "http://site1/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 2", "http://site2/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 3", "http://site3/", NTPTileSource::TOP_SITES))));
+ top_sites_callbacks_.ClearAndNotify(
+ {MakeMostVisitedURL("Site 1", "http://site1/"),
+ MakeMostVisitedURL("Site 2", "http://site2/"),
+ MakeMostVisitedURL("Site 3", "http://site3/")});
+ VerifyAndClearExpectations();
+
+ // Reply from suggestions service overrides top sites.
+ InSequence seq;
+ EXPECT_CALL(mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 4", "http://site4/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 5", "http://site5/",
+ NTPTileSource::SUGGESTIONS_SERVICE),
+ MatchesTile("Site 6", "http://site6/",
+ NTPTileSource::SUGGESTIONS_SERVICE))));
+ suggestions_service_callbacks_.Notify(
+ MakeProfile({MakeSuggestion("Site 4", "http://site4/"),
+ MakeSuggestion("Site 5", "http://site5/"),
+ MakeSuggestion("Site 6", "http://site6/")}));
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesWithEmptyCacheTest,
+ ShouldIgnoreSuggestionsServiceIfSlowerAndEmpty) {
+ // Reply from top sites is propagated to observer.
+ EXPECT_CALL(
+ mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 1", "http://site1/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 2", "http://site2/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 3", "http://site3/", NTPTileSource::TOP_SITES))));
+ top_sites_callbacks_.ClearAndNotify(
+ {MakeMostVisitedURL("Site 1", "http://site1/"),
+ MakeMostVisitedURL("Site 2", "http://site2/"),
+ MakeMostVisitedURL("Site 3", "http://site3/")});
+ VerifyAndClearExpectations();
+
+ // Reply from suggestions service is empty and thus ignored. However, the
+ // current implementation issues a redundant query to TopSites.
+ // TODO(mastiz): Avoid this redundant call.
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+ .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add));
+ suggestions_service_callbacks_.Notify(SuggestionsProfile());
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesWithEmptyCacheTest, ShouldPropagateUpdateByTopSites) {
+ // Reply from top sites is propagated to observer.
+ EXPECT_CALL(
+ mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 1", "http://site1/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 2", "http://site2/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 3", "http://site3/", NTPTileSource::TOP_SITES))));
+ top_sites_callbacks_.ClearAndNotify(
+ {MakeMostVisitedURL("Site 1", "http://site1/"),
+ MakeMostVisitedURL("Site 2", "http://site2/"),
+ MakeMostVisitedURL("Site 3", "http://site3/")});
+ VerifyAndClearExpectations();
+
+ // Reply from suggestions service is empty and thus ignored. However, the
+ // current implementation issues a redundant query to TopSites.
+ // TODO(mastiz): Avoid this redundant call.
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+ .WillOnce(Invoke(&top_sites_callbacks_, &TopSitesCallbackList::Add));
+ suggestions_service_callbacks_.Notify(SuggestionsProfile());
+ VerifyAndClearExpectations();
+
+ // Update from top sites is propagated to observer.
+ EXPECT_CALL(
+ mock_observer_,
+ OnMostVisitedURLsAvailable(ElementsAre(
+ MatchesTile("Site 4", "http://site4/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 5", "http://site5/", NTPTileSource::TOP_SITES),
+ MatchesTile("Site 6", "http://site6/", NTPTileSource::TOP_SITES))));
+ top_sites_callbacks_.ClearAndNotify(
+ {MakeMostVisitedURL("Site 4", "http://site4/"),
+ MakeMostVisitedURL("Site 5", "http://site5/"),
+ MakeMostVisitedURL("Site 6", "http://site6/")});
+ base::RunLoop().RunUntilIdle();
+}
+
+INSTANTIATE_TEST_CASE_P(MostVisitedSitesWithEmptyCacheTest,
+ MostVisitedSitesWithEmptyCacheTest,
+ ::testing::Bool());
// This a test for MostVisitedSites::MergeTiles(...) method, and thus has the
// same scope as the method itself. This tests merging popular sites with
@@ -43,96 +723,73 @@ static const size_t kNumSites = 4;
// - Removing blacklisted tiles.
// - Correct host extraction from the URL.
// - Ensuring personal tiles are not duplicated in popular tiles.
-class MostVisitedSitesTest : public testing::Test {
- protected:
- void Check(const std::vector<TitleURL>& popular_sites,
- const std::vector<TitleURL>& whitelist_entry_points,
- const std::vector<TitleURL>& personal_sites,
- const std::vector<bool>& expected_sites_is_personal,
- const std::vector<TitleURL>& expected_sites) {
- NTPTilesVector personal_tiles;
- for (const TitleURL& site : personal_sites)
- personal_tiles.push_back(MakeTileFrom(site, true, false));
- NTPTilesVector whitelist_tiles;
- for (const TitleURL& site : whitelist_entry_points)
- whitelist_tiles.push_back(MakeTileFrom(site, false, true));
- NTPTilesVector popular_tiles;
- for (const TitleURL& site : popular_sites)
- popular_tiles.push_back(MakeTileFrom(site, false, false));
- NTPTilesVector result_tiles = MostVisitedSites::MergeTiles(
- std::move(personal_tiles), std::move(whitelist_tiles),
- std::move(popular_tiles));
- std::vector<TitleURL> result_sites;
- std::vector<bool> result_is_personal;
- result_sites.reserve(result_tiles.size());
- result_is_personal.reserve(result_tiles.size());
- for (const auto& tile : result_tiles) {
- result_sites.push_back(TitleURL(tile.title, tile.url.spec()));
- result_is_personal.push_back(tile.source != NTPTileSource::POPULAR);
- }
- EXPECT_EQ(expected_sites_is_personal, result_is_personal);
- EXPECT_EQ(expected_sites, result_sites);
- }
- static NTPTile MakeTileFrom(const TitleURL& title_url,
- bool is_personal,
- bool whitelist) {
- NTPTile tile;
- tile.title = title_url.title;
- tile.url = GURL(title_url.url);
- tile.source = whitelist ? NTPTileSource::WHITELIST
- : (is_personal ? NTPTileSource::TOP_SITES
- : NTPTileSource::POPULAR);
- return tile;
- }
-};
-
-TEST_F(MostVisitedSitesTest, PersonalSites) {
- std::vector<TitleURL> personal_sites{
- TitleURL("Site 1", "https://www.site1.com/"),
- TitleURL("Site 2", "https://www.site2.com/"),
- TitleURL("Site 3", "https://www.site3.com/"),
- TitleURL("Site 4", "https://www.site4.com/"),
+TEST(MostVisitedSitesMergeTest, ShouldMergeTilesWithPersonalOnly) {
+ std::vector<NTPTile> personal_tiles{
+ MakeTile("Site 1", "https://www.site1.com/", NTPTileSource::TOP_SITES),
+ MakeTile("Site 2", "https://www.site2.com/", NTPTileSource::TOP_SITES),
+ MakeTile("Site 3", "https://www.site3.com/", NTPTileSource::TOP_SITES),
+ MakeTile("Site 4", "https://www.site4.com/", NTPTileSource::TOP_SITES),
};
// Without any popular tiles, the result after merge should be the personal
// tiles.
- std::vector<bool> expected_sites_source(kNumSites, true /*personal source*/);
- Check(std::vector<TitleURL>(), std::vector<TitleURL>(), personal_sites,
- expected_sites_source, personal_sites);
+ EXPECT_THAT(MostVisitedSites::MergeTiles(std::move(personal_tiles),
+ /*whitelist_tiles=*/NTPTilesVector(),
+ /*popular_tiles=*/NTPTilesVector()),
+ ElementsAre(MatchesTile("Site 1", "https://www.site1.com/",
+ NTPTileSource::TOP_SITES),
+ MatchesTile("Site 2", "https://www.site2.com/",
+ NTPTileSource::TOP_SITES),
+ MatchesTile("Site 3", "https://www.site3.com/",
+ NTPTileSource::TOP_SITES),
+ MatchesTile("Site 4", "https://www.site4.com/",
+ NTPTileSource::TOP_SITES)));
}
-TEST_F(MostVisitedSitesTest, PopularSites) {
- std::vector<TitleURL> popular_sites{
- TitleURL("Site 1", "https://www.site1.com/"),
- TitleURL("Site 2", "https://www.site2.com/"),
- TitleURL("Site 3", "https://www.site3.com/"),
- TitleURL("Site 4", "https://www.site4.com/"),
+TEST(MostVisitedSitesMergeTest, ShouldMergeTilesWithPopularOnly) {
+ std::vector<NTPTile> popular_tiles{
+ MakeTile("Site 1", "https://www.site1.com/", NTPTileSource::POPULAR),
+ MakeTile("Site 2", "https://www.site2.com/", NTPTileSource::POPULAR),
+ MakeTile("Site 3", "https://www.site3.com/", NTPTileSource::POPULAR),
+ MakeTile("Site 4", "https://www.site4.com/", NTPTileSource::POPULAR),
};
// Without any personal tiles, the result after merge should be the popular
// tiles.
- std::vector<bool> expected_sites_source(kNumSites, false /*popular source*/);
- Check(popular_sites, std::vector<TitleURL>(), std::vector<TitleURL>(),
- expected_sites_source, popular_sites);
+ EXPECT_THAT(
+ MostVisitedSites::MergeTiles(/*personal_tiles=*/NTPTilesVector(),
+ /*whitelist_tiles=*/NTPTilesVector(),
+ /*popular_tiles=*/std::move(popular_tiles)),
+ ElementsAre(MatchesTile("Site 1", "https://www.site1.com/",
+ NTPTileSource::POPULAR),
+ MatchesTile("Site 2", "https://www.site2.com/",
+ NTPTileSource::POPULAR),
+ MatchesTile("Site 3", "https://www.site3.com/",
+ NTPTileSource::POPULAR),
+ MatchesTile("Site 4", "https://www.site4.com/",
+ NTPTileSource::POPULAR)));
}
-TEST_F(MostVisitedSitesTest, PersonalPrecedePopularSites) {
- std::vector<TitleURL> popular_sites{
- TitleURL("Site 1", "https://www.site1.com/"),
- TitleURL("Site 2", "https://www.site2.com/"),
- };
- std::vector<TitleURL> personal_sites{
- TitleURL("Site 3", "https://www.site3.com/"),
- TitleURL("Site 4", "https://www.site4.com/"),
+TEST(MostVisitedSitesMergeTest, ShouldMergeTilesFavoringPersonalOverPopular) {
+ std::vector<NTPTile> popular_tiles{
+ MakeTile("Site 1", "https://www.site1.com/", NTPTileSource::POPULAR),
+ MakeTile("Site 2", "https://www.site2.com/", NTPTileSource::POPULAR),
};
- // Personal tiles should precede popular tiles.
- std::vector<TitleURL> expected_sites{
- TitleURL("Site 3", "https://www.site3.com/"),
- TitleURL("Site 4", "https://www.site4.com/"),
- TitleURL("Site 1", "https://www.site1.com/"),
- TitleURL("Site 2", "https://www.site2.com/"),
+ std::vector<NTPTile> personal_tiles{
+ MakeTile("Site 3", "https://www.site3.com/", NTPTileSource::TOP_SITES),
+ MakeTile("Site 4", "https://www.site4.com/", NTPTileSource::TOP_SITES),
};
- std::vector<bool> expected_sites_source{true, true, false, false};
- Check(popular_sites, std::vector<TitleURL>(), personal_sites,
- expected_sites_source, expected_sites);
+ EXPECT_THAT(
+ MostVisitedSites::MergeTiles(std::move(personal_tiles),
+ /*whitelist_tiles=*/NTPTilesVector(),
+ /*popular_tiles=*/std::move(popular_tiles)),
+ ElementsAre(MatchesTile("Site 3", "https://www.site3.com/",
+ NTPTileSource::TOP_SITES),
+ MatchesTile("Site 4", "https://www.site4.com/",
+ NTPTileSource::TOP_SITES),
+ MatchesTile("Site 1", "https://www.site1.com/",
+ NTPTileSource::POPULAR),
+ MatchesTile("Site 2", "https://www.site2.com/",
+ NTPTileSource::POPULAR)));
}
+} // namespace
} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/ntp_tile.h b/chromium/components/ntp_tiles/ntp_tile.h
index ef228b54b38..e64a19fa6be 100644
--- a/chromium/components/ntp_tiles/ntp_tile.h
+++ b/chromium/components/ntp_tiles/ntp_tile.h
@@ -10,33 +10,28 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/strings/string16.h"
+#include "components/ntp_tiles/ntp_tile_source.h"
#include "url/gurl.h"
namespace ntp_tiles {
-// The source of an NTP tile.
-// A Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ntp
-enum class NTPTileSource {
- // Tile comes from the personal top sites list, based on local history.
- TOP_SITES,
- // Tile comes from the suggestions service, based on synced history.
- SUGGESTIONS_SERVICE,
- // Tile is regionally popular.
- POPULAR,
- // Tile is on an custodian-managed whitelist.
- WHITELIST
-};
-
// A suggested site shown on the New Tab Page.
struct NTPTile {
base::string16 title;
GURL url;
NTPTileSource source;
- // Only valid for source == WHITELIST (empty otherwise).
+ // Empty unless whitelists are enabled and this site is in a whitelist.
+ // However, may be non-empty even if |source| is not |WHITELIST|, if this tile
+ // is also available from another, higher-priority source.
base::FilePath whitelist_icon_path;
+ // Only valid for source == SUGGESTIONS_SERVICE (empty otherwise).
+ // May point to a local chrome:// URL or to a remote one. May be empty.
+ GURL thumbnail_url;
+ // This won't be empty, but might 404 etc.
+ GURL favicon_url;
+
NTPTile();
NTPTile(const NTPTile&);
~NTPTile();
diff --git a/chromium/components/ntp_tiles/ntp_tile_source.h b/chromium/components/ntp_tiles/ntp_tile_source.h
new file mode 100644
index 00000000000..1ac85a91b71
--- /dev/null
+++ b/chromium/components/ntp_tiles/ntp_tile_source.h
@@ -0,0 +1,28 @@
+// 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_NTP_TILE_SOURCE_H_
+#define COMPONENTS_NTP_TILES_NTP_TILE_SOURCE_H_
+
+namespace ntp_tiles {
+
+// The source of an NTP tile.
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ntp
+enum class NTPTileSource {
+ // Tile comes from the personal top sites list, based on local history.
+ TOP_SITES,
+ // Tile comes from the suggestions service, based on synced history.
+ SUGGESTIONS_SERVICE,
+ // Tile is regionally popular.
+ POPULAR,
+ // Tile is on a custodian-managed whitelist.
+ WHITELIST,
+
+ LAST = WHITELIST
+};
+
+} // namespace ntp_tiles
+
+#endif // COMPONENTS_NTP_TILES_NTP_TILE_SOURCE_H_
diff --git a/chromium/components/ntp_tiles/popular_sites.h b/chromium/components/ntp_tiles/popular_sites.h
index 639aedf4046..dde8459ba5b 100644
--- a/chromium/components/ntp_tiles/popular_sites.h
+++ b/chromium/components/ntp_tiles/popular_sites.h
@@ -5,48 +5,23 @@
#ifndef COMPONENTS_NTP_TILES_POPULAR_SITES_H_
#define COMPONENTS_NTP_TILES_POPULAR_SITES_H_
-#include <memory>
#include <string>
#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"
-#include "net/url_request/url_fetcher_delegate.h"
#include "url/gurl.h"
namespace base {
-class Value;
+class ListValue;
}
-namespace net {
-class URLRequestContextGetter;
-}
-
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
-namespace variations {
-class VariationsService;
-}
-
-class PrefService;
-class TemplateURLService;
-
namespace ntp_tiles {
-using ParseJSONCallback = base::Callback<void(
- const std::string& unsafe_json,
- const base::Callback<void(std::unique_ptr<base::Value>)>& success_callback,
- const base::Callback<void(const std::string&)>& error_callback)>;
-
-// Downloads and provides a list of suggested popular sites, for display on
-// the NTP when there are not enough personalized tiles. Caches the downloaded
-// file on disk to avoid re-downloading on every startup.
-class PopularSites : public net::URLFetcherDelegate {
+// Interface to provide a list of suggested popular sites, for display on the
+// NTP when there are not enough personalized tiles.
+class PopularSites {
public:
struct Site {
Site(const base::string16& title,
@@ -64,76 +39,35 @@ class PopularSites : public net::URLFetcherDelegate {
GURL thumbnail_url;
};
+ using SitesVector = std::vector<Site>;
using FinishedCallback = base::Callback<void(bool /* success */)>;
- PopularSites(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);
+ virtual ~PopularSites() = default;
- // Starts the process of retrieving popular sites. When they are available,
- // invokes |callback| with the result, on the same thread as the caller. Never
- // invokes |callback| before returning control to the caller, even if the
- // result is immediately known.
+ // May start the process of retrieving popular sites. If an actual download
+ // gets triggered, returns true and invokes |callback| with the result, on the
+ // same thread as the caller. Never invokes |callback| before returning
+ // control to the caller.
+ //
+ // If the result is immediately known and hence no download is triggered, the
+ // function returns false and the callback will never be executed.
//
- // Set |force_download| to enforce re-downloading the popular sites file, even
- // if it already exists on disk.
+ // Set |force_download| to enforce re-downloading the popular sites JSON, even
+ // if it already exists in cache.
//
// Must be called at most once on a given PopularSites object.
- void StartFetch(bool force_download, const FinishedCallback& callback);
-
- ~PopularSites() override;
-
- const std::vector<Site>& sites() const { return sites_; }
-
- // The URL of the file that was last downloaded.
- GURL LastURL() const;
-
- const base::FilePath& local_path() const { return local_path_; }
-
- // Register preferences used by this class.
- static void RegisterProfilePrefs(
- user_prefs::PrefRegistrySyncable* user_prefs);
-
- private:
- void OnReadFileDone(std::unique_ptr<std::string> data, bool success);
-
- // Fetch the popular sites at the given URL, overwriting any file that already
- // exists.
- void FetchPopularSites();
-
- // net::URLFetcherDelegate implementation.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
-
- void OnJsonParsed(std::unique_ptr<base::Value> json);
- void OnJsonParseFailed(const std::string& error_message);
- void OnFileWriteDone(std::unique_ptr<base::Value> json, bool success);
- void ParseSiteList(std::unique_ptr<base::Value> json);
- 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_;
- net::URLRequestContextGetter* const download_context_;
- base::FilePath const local_path_;
- ParseJSONCallback parse_json_;
-
- // Set by StartFetch() and called after fetch completes.
- FinishedCallback callback_;
-
- std::unique_ptr<net::URLFetcher> fetcher_;
- bool is_fallback_;
- std::vector<Site> sites_;
- GURL pending_url_;
-
- base::WeakPtrFactory<PopularSites> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(PopularSites);
+ virtual bool MaybeStartFetch(bool force_download,
+ const FinishedCallback& callback) = 0;
+
+ // Returns the list of available sites.
+ virtual const SitesVector& sites() const = 0;
+
+ // Various internals exposed publicly for diagnostic pages only.
+ virtual GURL GetLastURLFetched() const = 0;
+ virtual GURL GetURLToFetch() = 0;
+ virtual std::string GetCountryToFetch() = 0;
+ virtual std::string GetVersionToFetch() = 0;
+ virtual const base::ListValue* GetCachedJson() = 0;
};
} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/popular_sites.cc b/chromium/components/ntp_tiles/popular_sites_impl.cc
index 02120f37ec3..ce69227f271 100644
--- a/chromium/components/ntp_tiles/popular_sites.cc
+++ b/chromium/components/ntp_tiles/popular_sites_impl.cc
@@ -2,21 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/ntp_tiles/popular_sites.h"
+#include "components/ntp_tiles/popular_sites_impl.h"
#include <stddef.h>
+#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
-#include "base/files/important_file_writer.h"
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/task_runner_util.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
@@ -48,11 +45,15 @@ const char kPopularSitesURLFormat[] =
"https://www.gstatic.com/chrome/ntp/suggested_sites_%s_%s.json";
const char kPopularSitesDefaultCountryCode[] = "DEFAULT";
const char kPopularSitesDefaultVersion[] = "5";
-const char kPopularSitesLocalFilename[] = "suggested_sites.json";
const int kPopularSitesRedownloadIntervalHours = 24;
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& country,
const std::string& version) {
@@ -101,63 +102,27 @@ std::string GetVariationVersion() {
"version");
}
-// Determine the country code to use. In order of precedence:
-// - The explicit "override country" pref set by the user.
-// - The country code from the field trial config (variation parameter).
-// - The Google country code if Google is the default search engine (and the
-// "--enable-ntp-search-engine-country-detection" switch is present).
-// - The country provided by the VariationsService.
-// - A default fallback.
-std::string GetCountryToUse(const PrefService* prefs,
- const TemplateURLService* template_url_service,
- VariationsService* variations_service) {
- std::string country_code =
- prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideCountry);
-
- if (country_code.empty())
- country_code = GetVariationCountry();
-
- if (country_code.empty())
- country_code = GetDefaultSearchEngineCountryCode(template_url_service);
-
- if (country_code.empty() && variations_service)
- country_code = variations_service->GetStoredPermanentCountry();
-
-#if defined(OS_IOS)
- if (country_code.empty())
- country_code = GetDeviceCountryCode();
-#endif
-
- if (country_code.empty())
- country_code = kPopularSitesDefaultCountryCode;
-
- return base::ToUpperASCII(country_code);
-}
-
-// Determine the version to use. In order of precedence:
-// - The explicit "override version" pref set by the user.
-// - The version from the field trial config (variation parameter).
-// - A default fallback.
-std::string GetVersionToUse(const PrefService* prefs) {
- std::string version =
- prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideVersion);
-
- if (version.empty())
- version = GetVariationVersion();
-
- if (version.empty())
- version = kPopularSitesDefaultVersion;
-
- return version;
-}
+PopularSites::SitesVector ParseSiteList(const base::ListValue& list) {
+ PopularSites::SitesVector sites;
+ for (size_t i = 0; i < list.GetSize(); i++) {
+ const base::DictionaryValue* item;
+ if (!list.GetDictionary(i, &item))
+ continue;
+ base::string16 title;
+ std::string url;
+ if (!item->GetString("title", &title) || !item->GetString("url", &url))
+ continue;
+ std::string favicon_url;
+ item->GetString("favicon_url", &favicon_url);
+ std::string thumbnail_url;
+ item->GetString("thumbnail_url", &thumbnail_url);
+ std::string large_icon_url;
+ item->GetString("large_icon_url", &large_icon_url);
-// Must run on the blocking thread pool.
-bool WriteJsonToFile(const base::FilePath& local_path,
- const base::Value* json) {
- std::string json_string;
- return base::JSONWriter::Write(*json, &json_string) &&
- base::ImportantFileWriter::WriteFileAtomically(local_path,
- json_string);
+ sites.emplace_back(title, GURL(url), GURL(favicon_url),
+ GURL(large_icon_url), GURL(thumbnail_url));
+ }
+ return sites;
}
} // namespace
@@ -177,7 +142,7 @@ PopularSites::Site::Site(const Site& other) = default;
PopularSites::Site::~Site() {}
-PopularSites::PopularSites(
+PopularSitesImpl::PopularSitesImpl(
const scoped_refptr<base::SequencedWorkerPool>& blocking_pool,
PrefService* prefs,
const TemplateURLService* template_url_service,
@@ -191,17 +156,23 @@ PopularSites::PopularSites(
template_url_service_(template_url_service),
variations_(variations_service),
download_context_(download_context),
- local_path_(directory.empty()
- ? base::FilePath()
- : directory.AppendASCII(kPopularSitesLocalFilename)),
parse_json_(std::move(parse_json)),
is_fallback_(false),
- weak_ptr_factory_(this) {}
+ 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));
+ }
+}
-PopularSites::~PopularSites() {}
+PopularSitesImpl::~PopularSitesImpl() {}
-void PopularSites::StartFetch(bool force_download,
- const FinishedCallback& callback) {
+bool PopularSitesImpl::MaybeStartFetch(bool force_download,
+ const FinishedCallback& callback) {
DCHECK(!callback_);
callback_ = callback;
@@ -213,46 +184,101 @@ void PopularSites::StartFetch(bool force_download,
base::TimeDelta::FromHours(kPopularSitesRedownloadIntervalHours);
const bool download_time_is_future = base::Time::Now() < last_download_time;
- const std::string country =
- GetCountryToUse(prefs_, template_url_service_, variations_);
- const std::string version = GetVersionToUse(prefs_);
-
- const GURL override_url =
- GURL(prefs_->GetString(ntp_tiles::prefs::kPopularSitesOverrideURL));
- pending_url_ = override_url.is_valid() ? override_url
- : GetPopularSitesURL(country, version);
+ pending_url_ = GetURLToFetch();
const bool url_changed =
pending_url_.spec() != prefs_->GetString(kPopularSitesURLPref);
- // No valid path to save to. Immediately post failure.
- if (local_path_.empty()) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- base::Bind(callback_, false));
- return;
- }
-
// Download forced, or we need to download a new file.
if (force_download || download_time_is_future ||
(time_since_last_download > redownload_interval) || url_changed) {
FetchPopularSites();
- return;
+ return true;
}
- std::unique_ptr<std::string> file_data(new std::string);
- std::string* file_data_ptr = file_data.get();
- base::PostTaskAndReplyWithResult(
- blocking_runner_.get(), FROM_HERE,
- base::Bind(&base::ReadFileToString, local_path_, file_data_ptr),
- base::Bind(&PopularSites::OnReadFileDone, weak_ptr_factory_.GetWeakPtr(),
- base::Passed(std::move(file_data))));
+ const base::ListValue* json = prefs_->GetList(kPopularSitesJsonPref);
+ if (!json) {
+ // Cache didn't exist.
+ FetchPopularSites();
+ return true;
+ } else {
+ // Note that we don't run the callback.
+ sites_ = ParseSiteList(*json);
+ return false;
+ }
}
-GURL PopularSites::LastURL() const {
+const PopularSites::SitesVector& PopularSitesImpl::sites() const {
+ return sites_;
+}
+
+GURL PopularSitesImpl::GetLastURLFetched() const {
return GURL(prefs_->GetString(kPopularSitesURLPref));
}
+GURL PopularSitesImpl::GetURLToFetch() {
+ const std::string country = GetCountryToFetch();
+ const std::string version = GetVersionToFetch();
+
+ const GURL override_url =
+ GURL(prefs_->GetString(ntp_tiles::prefs::kPopularSitesOverrideURL));
+ return override_url.is_valid() ? override_url
+ : GetPopularSitesURL(country, version);
+}
+
+// Determine the country code to use. In order of precedence:
+// - The explicit "override country" pref set by the user.
+// - The country code from the field trial config (variation parameter).
+// - The Google country code if Google is the default search engine (and the
+// "--enable-ntp-search-engine-country-detection" switch is present).
+// - The country provided by the VariationsService.
+// - A default fallback.
+std::string PopularSitesImpl::GetCountryToFetch() {
+ std::string country_code =
+ prefs_->GetString(ntp_tiles::prefs::kPopularSitesOverrideCountry);
+
+ if (country_code.empty())
+ country_code = GetVariationCountry();
+
+ if (country_code.empty())
+ country_code = GetDefaultSearchEngineCountryCode(template_url_service_);
+
+ if (country_code.empty() && variations_)
+ country_code = variations_->GetStoredPermanentCountry();
+
+#if defined(OS_IOS)
+ if (country_code.empty())
+ country_code = GetDeviceCountryCode();
+#endif
+
+ if (country_code.empty())
+ country_code = kPopularSitesDefaultCountryCode;
+
+ return base::ToUpperASCII(country_code);
+}
+
+// Determine the version to use. In order of precedence:
+// - The explicit "override version" pref set by the user.
+// - The version from the field trial config (variation parameter).
+// - A default fallback.
+std::string PopularSitesImpl::GetVersionToFetch() {
+ std::string version =
+ prefs_->GetString(ntp_tiles::prefs::kPopularSitesOverrideVersion);
+
+ if (version.empty())
+ version = GetVariationVersion();
+
+ if (version.empty())
+ version = kPopularSitesDefaultVersion;
+
+ return version;
+}
+
+const base::ListValue* PopularSitesImpl::GetCachedJson() {
+ return prefs_->GetList(kPopularSitesJsonPref);
+}
+
// static
-void PopularSites::RegisterProfilePrefs(
+void PopularSitesImpl::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* user_prefs) {
user_prefs->RegisterStringPref(ntp_tiles::prefs::kPopularSitesOverrideURL,
std::string());
@@ -263,24 +289,10 @@ void PopularSites::RegisterProfilePrefs(
user_prefs->RegisterInt64Pref(kPopularSitesLastDownloadPref, 0);
user_prefs->RegisterStringPref(kPopularSitesURLPref, std::string());
+ user_prefs->RegisterListPref(kPopularSitesJsonPref);
}
-void PopularSites::OnReadFileDone(std::unique_ptr<std::string> data,
- bool success) {
- if (success) {
- auto json = base::JSONReader::Read(*data, base::JSON_ALLOW_TRAILING_COMMAS);
- if (json) {
- ParseSiteList(std::move(json));
- } else {
- OnJsonParseFailed("previously-fetched JSON was no longer parseable");
- }
- } else {
- // File didn't exist, or couldn't be read for some other reason.
- FetchPopularSites();
- }
-}
-
-void PopularSites::FetchPopularSites() {
+void PopularSitesImpl::FetchPopularSites() {
fetcher_ = URLFetcher::Create(pending_url_, URLFetcher::GET, this);
data_use_measurement::DataUseUserData::AttachToFetcher(
fetcher_.get(), data_use_measurement::DataUseUserData::NTP_TILES);
@@ -291,7 +303,7 @@ void PopularSites::FetchPopularSites() {
fetcher_->Start();
}
-void PopularSites::OnURLFetchComplete(const net::URLFetcher* source) {
+void PopularSitesImpl::OnURLFetchComplete(const net::URLFetcher* source) {
DCHECK_EQ(fetcher_.get(), source);
std::unique_ptr<net::URLFetcher> free_fetcher = std::move(fetcher_);
@@ -303,76 +315,36 @@ void PopularSites::OnURLFetchComplete(const net::URLFetcher* source) {
return;
}
- parse_json_.Run(
- json_string,
- base::Bind(&PopularSites::OnJsonParsed, weak_ptr_factory_.GetWeakPtr()),
- base::Bind(&PopularSites::OnJsonParseFailed,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void PopularSites::OnJsonParsed(std::unique_ptr<base::Value> json) {
- const base::Value* json_ptr = json.get();
- base::PostTaskAndReplyWithResult(
- blocking_runner_.get(), FROM_HERE,
- base::Bind(&WriteJsonToFile, local_path_, json_ptr),
- base::Bind(&PopularSites::OnFileWriteDone, weak_ptr_factory_.GetWeakPtr(),
- base::Passed(std::move(json))));
-}
-
-void PopularSites::OnJsonParseFailed(const std::string& error_message) {
- DLOG(WARNING) << "JSON parsing failed: " << error_message;
- OnDownloadFailed();
-}
-
-void PopularSites::OnFileWriteDone(std::unique_ptr<base::Value> json,
- bool success) {
- if (success) {
- prefs_->SetInt64(kPopularSitesLastDownloadPref,
- base::Time::Now().ToInternalValue());
- prefs_->SetString(kPopularSitesURLPref, pending_url_.spec());
- ParseSiteList(std::move(json));
- } else {
- DLOG(WARNING) << "Could not write file to "
- << local_path_.LossyDisplayName();
- OnDownloadFailed();
- }
+ parse_json_.Run(json_string, base::Bind(&PopularSitesImpl::OnJsonParsed,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&PopularSitesImpl::OnJsonParseFailed,
+ weak_ptr_factory_.GetWeakPtr()));
}
-void PopularSites::ParseSiteList(std::unique_ptr<base::Value> json) {
- base::ListValue* list = nullptr;
- if (!json || !json->GetAsList(&list)) {
+void PopularSitesImpl::OnJsonParsed(std::unique_ptr<base::Value> json) {
+ std::unique_ptr<base::ListValue> list =
+ base::ListValue::From(std::move(json));
+ if (!list) {
DLOG(WARNING) << "JSON is not a list";
- sites_.clear();
- callback_.Run(false);
+ OnDownloadFailed();
return;
}
- std::vector<PopularSites::Site> sites;
- for (size_t i = 0; i < list->GetSize(); i++) {
- base::DictionaryValue* item;
- if (!list->GetDictionary(i, &item))
- continue;
- base::string16 title;
- std::string url;
- if (!item->GetString("title", &title) || !item->GetString("url", &url))
- continue;
- std::string favicon_url;
- item->GetString("favicon_url", &favicon_url);
- std::string thumbnail_url;
- item->GetString("thumbnail_url", &thumbnail_url);
- std::string large_icon_url;
- item->GetString("large_icon_url", &large_icon_url);
-
- sites.push_back(PopularSites::Site(title, GURL(url), GURL(favicon_url),
- GURL(large_icon_url),
- GURL(thumbnail_url)));
- }
+ prefs_->Set(kPopularSitesJsonPref, *list);
+ prefs_->SetInt64(kPopularSitesLastDownloadPref,
+ base::Time::Now().ToInternalValue());
+ prefs_->SetString(kPopularSitesURLPref, pending_url_.spec());
- sites_.swap(sites);
+ sites_ = ParseSiteList(*list);
callback_.Run(true);
}
-void PopularSites::OnDownloadFailed() {
+void PopularSitesImpl::OnJsonParseFailed(const std::string& error_message) {
+ DLOG(WARNING) << "JSON parsing failed: " << error_message;
+ OnDownloadFailed();
+}
+
+void PopularSitesImpl::OnDownloadFailed() {
if (!is_fallback_) {
DLOG(WARNING) << "Download country site list failed";
is_fallback_ = true;
diff --git a/chromium/components/ntp_tiles/popular_sites_impl.h b/chromium/components/ntp_tiles/popular_sites_impl.h
new file mode 100644
index 00000000000..17280da01a4
--- /dev/null
+++ b/chromium/components/ntp_tiles/popular_sites_impl.h
@@ -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.
+
+#ifndef COMPONENTS_NTP_TILES_POPULAR_SITES_IMPL_H_
+#define COMPONENTS_NTP_TILES_POPULAR_SITES_IMPL_H_
+
+#include <memory>
+#include <string>
+#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"
+#include "components/ntp_tiles/popular_sites.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
+
+namespace base {
+class Value;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace variations {
+class VariationsService;
+}
+
+class PrefService;
+class TemplateURLService;
+
+namespace ntp_tiles {
+
+using ParseJSONCallback = base::Callback<void(
+ const std::string& unsafe_json,
+ const base::Callback<void(std::unique_ptr<base::Value>)>& success_callback,
+ const base::Callback<void(const std::string&)>& error_callback)>;
+
+// Actual (non-test) implementation of the PopularSites interface. Caches the
+// downloaded file on disk to avoid re-downloading on every startup.
+class PopularSitesImpl : public PopularSites, public net::URLFetcherDelegate {
+ 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;
+
+ // PopularSites implementation.
+ bool MaybeStartFetch(bool force_download,
+ const FinishedCallback& callback) override;
+ const SitesVector& sites() const override;
+ GURL GetLastURLFetched() const override;
+ GURL GetURLToFetch() override;
+ std::string GetCountryToFetch() override;
+ std::string GetVersionToFetch() override;
+ const base::ListValue* GetCachedJson() override;
+
+ // Register preferences used by this class.
+ static void RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* user_prefs);
+
+ private:
+ // Fetch the popular sites at the given URL, overwriting any cache in prefs
+ // that already exists.
+ void FetchPopularSites();
+
+ // net::URLFetcherDelegate implementation.
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ void OnJsonParsed(std::unique_ptr<base::Value> json);
+ void OnJsonParseFailed(const std::string& error_message);
+ 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_;
+ net::URLRequestContextGetter* const download_context_;
+ ParseJSONCallback parse_json_;
+
+ // Set by MaybeStartFetch() and called after fetch completes.
+ FinishedCallback callback_;
+
+ std::unique_ptr<net::URLFetcher> fetcher_;
+ bool is_fallback_;
+ SitesVector sites_;
+ GURL pending_url_;
+
+ base::WeakPtrFactory<PopularSitesImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PopularSitesImpl);
+};
+
+} // namespace ntp_tiles
+
+#endif // COMPONENTS_NTP_TILES_POPULAR_SITES_IMPL_H_
diff --git a/chromium/components/ntp_tiles/popular_sites_unittest.cc b/chromium/components/ntp_tiles/popular_sites_impl_unittest.cc
index c12e7dbb857..86fbfde2fd2 100644
--- a/chromium/components/ntp_tiles/popular_sites_unittest.cc
+++ b/chromium/components/ntp_tiles/popular_sites_impl_unittest.cc
@@ -2,24 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/ntp_tiles/popular_sites.h"
+#include "components/ntp_tiles/popular_sites_impl.h"
+#include <map>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.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_parser.h"
#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/optional.h"
#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
#include "base/test/sequenced_worker_pool_owner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
+#include "components/ntp_tiles/json_unsafe_parser.h"
#include "components/ntp_tiles/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "net/http/http_status_code.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_status.h"
@@ -49,42 +53,6 @@ using TestPopularSiteVector = std::vector<TestPopularSite>;
return ::testing::Eq(GURL(s));
}
-// Copied from iOS code. Perhaps should be in a shared location.
-class JsonUnsafeParser {
- public:
- using SuccessCallback = base::Callback<void(std::unique_ptr<base::Value>)>;
- using ErrorCallback = base::Callback<void(const std::string&)>;
-
- // As with SafeJsonParser, runs either success_callback or error_callback on
- // the calling thread, but not before the call returns.
- static void Parse(const std::string& unsafe_json,
- const SuccessCallback& success_callback,
- const ErrorCallback& error_callback) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::Bind(
- [](const std::string& unsafe_json,
- const SuccessCallback& success_callback,
- const ErrorCallback& error_callback) {
- std::string error_msg;
- int error_line, error_column;
- std::unique_ptr<base::Value> value =
- base::JSONReader::ReadAndReturnError(
- unsafe_json, base::JSON_ALLOW_TRAILING_COMMAS, nullptr,
- &error_msg, &error_line, &error_column);
- if (value) {
- success_callback.Run(std::move(value));
- } else {
- error_callback.Run(base::StringPrintf(
- "%s (%d:%d)", error_msg.c_str(), error_line, error_column));
- }
- },
- unsafe_json, success_callback, error_callback));
- }
-
- JsonUnsafeParser() = delete;
-};
-
class PopularSitesTest : public ::testing::Test {
protected:
PopularSitesTest()
@@ -105,7 +73,7 @@ class PopularSitesTest : public ::testing::Test {
},
worker_pool_owner_(2, "PopularSitesTest."),
url_fetcher_factory_(nullptr) {
- PopularSites::RegisterProfilePrefs(prefs_.registry());
+ PopularSitesImpl::RegisterProfilePrefs(prefs_.registry());
CHECK(scoped_cache_dir_.CreateUniqueTempDir());
cache_dir_ = scoped_cache_dir_.GetPath();
}
@@ -142,28 +110,31 @@ class PopularSitesTest : public ::testing::Test {
net::URLRequestStatus::SUCCESS);
}
- bool FetchPopularSites(bool force_download,
- std::vector<PopularSites::Site>* sites) {
+ // Returns an optional bool representing whether the completion callback was
+ // called at all, and if yes which was the returned bool value.
+ base::Optional<bool> FetchPopularSites(bool force_download,
+ PopularSites::SitesVector* sites) {
scoped_refptr<net::TestURLRequestContextGetter> url_request_context(
new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get()));
- PopularSites popular_sites(worker_pool_owner_.pool().get(), &prefs_,
- /*template_url_service=*/nullptr,
- /*variations_service=*/nullptr,
- url_request_context.get(), cache_dir_,
- base::Bind(JsonUnsafeParser::Parse));
+ PopularSitesImpl popular_sites(worker_pool_owner_.pool().get(), &prefs_,
+ /*template_url_service=*/nullptr,
+ /*variations_service=*/nullptr,
+ url_request_context.get(), cache_dir_,
+ base::Bind(JsonUnsafeParser::Parse));
base::RunLoop loop;
- bool save_success = false;
- popular_sites.StartFetch(
- force_download,
- base::Bind(
- [](bool* save_success, base::RunLoop* loop, bool success) {
- *save_success = success;
- loop->Quit();
- },
- &save_success, &loop));
- loop.Run();
+ base::Optional<bool> save_success;
+ if (popular_sites.MaybeStartFetch(
+ force_download, base::Bind(
+ [](base::Optional<bool>* save_success,
+ base::RunLoop* loop, bool success) {
+ save_success->emplace(success);
+ loop->Quit();
+ },
+ &save_success, &loop))) {
+ loop.Run();
+ }
*sites = popular_sites.sites();
return save_success;
}
@@ -176,7 +147,7 @@ class PopularSitesTest : public ::testing::Test {
base::SequencedWorkerPoolOwner worker_pool_owner_;
base::ScopedTempDir scoped_cache_dir_;
base::FilePath cache_dir_;
- user_prefs::TestingPrefServiceSyncable prefs_;
+ sync_preferences::TestingPrefServiceSyncable prefs_;
net::FakeURLFetcherFactory url_fetcher_factory_;
};
@@ -186,8 +157,9 @@ TEST_F(PopularSitesTest, Basic) {
"https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json",
{kWikipedia});
- std::vector<PopularSites::Site> sites;
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ PopularSites::SitesVector sites;
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::Optional<bool>(true)));
ASSERT_THAT(sites.size(), Eq(1u));
EXPECT_THAT(sites[0].title, Str16Eq("Wikipedia, fhta Ph'nglui mglw'nafh"));
@@ -205,8 +177,9 @@ TEST_F(PopularSitesTest, Fallback) {
"https://www.gstatic.com/chrome/ntp/suggested_sites_DEFAULT_5.json",
{kYouTube, kChromium});
- std::vector<PopularSites::Site> sites;
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ PopularSites::SitesVector sites;
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::Optional<bool>(true)));
ASSERT_THAT(sites.size(), Eq(2u));
EXPECT_THAT(sites[0].title, Str16Eq("YouTube"));
@@ -228,32 +201,44 @@ TEST_F(PopularSitesTest, Failure) {
RespondWith404(
"https://www.gstatic.com/chrome/ntp/suggested_sites_DEFAULT_5.json");
- std::vector<PopularSites::Site> sites;
- EXPECT_FALSE(FetchPopularSites(/*force_download=*/false, &sites));
+ PopularSites::SitesVector sites;
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::Optional<bool>(false)));
ASSERT_THAT(sites, IsEmpty());
}
-TEST_F(PopularSitesTest, FailsWithoutFetchIfNoCacheDir) {
+TEST_F(PopularSitesTest, ClearsCacheFileFromOldVersions) {
SetCountryAndVersion("ZZ", "9");
- std::vector<PopularSites::Site> sites;
- cache_dir_ = base::FilePath(); // Override with invalid file path.
- EXPECT_FALSE(FetchPopularSites(/*force_download=*/false, &sites));
+ 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, UsesCachedFile) {
+TEST_F(PopularSitesTest, UsesCachedJson) {
SetCountryAndVersion("ZZ", "9");
RespondWithJSON(
"https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json",
{kWikipedia});
// First request succeeds and gets cached.
- std::vector<PopularSites::Site> sites;
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ PopularSites::SitesVector sites;
+ ASSERT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::Optional<bool>(true)));
// File disappears from server, but we don't need it because it's cached.
RespondWith404(
"https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json");
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::nullopt));
EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/"));
}
@@ -266,15 +251,17 @@ TEST_F(PopularSitesTest, CachesEmptyFile) {
{kWikipedia});
// First request succeeds and caches empty suggestions list (no fallback).
- std::vector<PopularSites::Site> sites;
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ PopularSites::SitesVector sites;
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::Optional<bool>(true)));
EXPECT_THAT(sites, IsEmpty());
// File appears on server, but we continue to use our cached empty file.
RespondWithJSON(
"https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json",
{kWikipedia});
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::nullopt));
EXPECT_THAT(sites, IsEmpty());
}
@@ -285,15 +272,17 @@ TEST_F(PopularSitesTest, DoesntUseCachedFileIfDownloadForced) {
{kWikipedia});
// First request succeeds and gets cached.
- std::vector<PopularSites::Site> sites;
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/true, &sites));
+ PopularSites::SitesVector sites;
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/true, &sites),
+ Eq(base::Optional<bool>(true)));
EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/"));
// File disappears from server. Download is forced, so we get the new file.
RespondWithJSON(
"https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json",
{kChromium});
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/true, &sites));
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/true, &sites),
+ Eq(base::Optional<bool>(true)));
EXPECT_THAT(sites[0].url, URLEq("https://www.chromium.org/"));
}
@@ -305,16 +294,18 @@ TEST_F(PopularSitesTest, RefetchesAfterCountryMoved) {
"https://www.gstatic.com/chrome/ntp/suggested_sites_ZX_9.json",
{kChromium});
- std::vector<PopularSites::Site> sites;
+ PopularSites::SitesVector sites;
// First request (in ZZ) saves Wikipedia.
SetCountryAndVersion("ZZ", "9");
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::Optional<bool>(true)));
EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/"));
// Second request (now in ZX) saves Chromium.
SetCountryAndVersion("ZX", "9");
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ base::Optional<bool>(true));
EXPECT_THAT(sites[0].url, URLEq("https://www.chromium.org/"));
}
@@ -327,14 +318,16 @@ TEST_F(PopularSitesTest, DoesntCacheInvalidFile) {
"https://www.gstatic.com/chrome/ntp/suggested_sites_DEFAULT_5.json");
// First request falls back and gets nothing there either.
- std::vector<PopularSites::Site> sites;
- EXPECT_FALSE(FetchPopularSites(/*force_download=*/false, &sites));
+ PopularSites::SitesVector sites;
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::Optional<bool>(false)));
// Second request refetches ZZ_9, which now has data.
RespondWithJSON(
"https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json",
{kChromium});
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::Optional<bool>(true)));
ASSERT_THAT(sites.size(), Eq(1u));
EXPECT_THAT(sites[0].url, URLEq("https://www.chromium.org/"));
}
@@ -348,8 +341,9 @@ TEST_F(PopularSitesTest, RefetchesAfterFallback) {
{kWikipedia});
// First request falls back.
- std::vector<PopularSites::Site> sites;
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ PopularSites::SitesVector sites;
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::Optional<bool>(true)));
ASSERT_THAT(sites.size(), Eq(1u));
EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/"));
@@ -357,7 +351,8 @@ TEST_F(PopularSitesTest, RefetchesAfterFallback) {
RespondWithJSON(
"https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json",
{kChromium});
- EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites));
+ EXPECT_THAT(FetchPopularSites(/*force_download=*/false, &sites),
+ Eq(base::Optional<bool>(true)));
ASSERT_THAT(sites.size(), Eq(1u));
EXPECT_THAT(sites[0].url, URLEq("https://www.chromium.org/"));
}
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
new file mode 100644
index 00000000000..9f876ee7efb
--- /dev/null
+++ b/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.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/ntp_tiles/webui/ntp_tiles_internals_message_handler.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.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"
+#include "components/prefs/pref_service.h"
+#include "components/url_formatter/url_fixer.h"
+#include "url/gurl.h"
+
+namespace ntp_tiles {
+
+NTPTilesInternalsMessageHandlerClient::NTPTilesInternalsMessageHandlerClient() =
+ default;
+NTPTilesInternalsMessageHandlerClient::
+ ~NTPTilesInternalsMessageHandlerClient() = default;
+
+NTPTilesInternalsMessageHandler::NTPTilesInternalsMessageHandler()
+ : client_(nullptr), site_count_(8) {}
+
+NTPTilesInternalsMessageHandler::~NTPTilesInternalsMessageHandler() = default;
+
+void NTPTilesInternalsMessageHandler::RegisterMessages(
+ NTPTilesInternalsMessageHandlerClient* client) {
+ client_ = client;
+
+ client_->RegisterMessageCallback(
+ "registerForEvents",
+ base::Bind(&NTPTilesInternalsMessageHandler::HandleRegisterForEvents,
+ base::Unretained(this)));
+
+ client_->RegisterMessageCallback(
+ "update", base::Bind(&NTPTilesInternalsMessageHandler::HandleUpdate,
+ base::Unretained(this)));
+}
+
+void NTPTilesInternalsMessageHandler::HandleRegisterForEvents(
+ const base::ListValue* args) {
+ if (!client_->SupportsNTPTiles()) {
+ return;
+ }
+ DCHECK(args->empty());
+
+ SendSourceInfo();
+
+ most_visited_sites_ = client_->MakeMostVisitedSites();
+ most_visited_sites_->SetMostVisitedURLsObserver(this, site_count_);
+}
+
+void NTPTilesInternalsMessageHandler::HandleUpdate(
+ const base::ListValue* args) {
+ if (!client_->SupportsNTPTiles()) {
+ return;
+ }
+ const base::DictionaryValue* dict = nullptr;
+ DCHECK_EQ(1u, args->GetSize());
+ args->GetDictionary(0, &dict);
+ DCHECK(dict);
+
+ PrefService* prefs = client_->GetPrefs();
+
+ if (client_->DoesSourceExist(ntp_tiles::NTPTileSource::POPULAR)) {
+ std::string url;
+ dict->GetString("popular.overrideURL", &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 country;
+ dict->GetString("popular.overrideCountry", &country);
+ if (country.empty()) {
+ prefs->ClearPref(ntp_tiles::prefs::kPopularSitesOverrideCountry);
+ } else {
+ prefs->SetString(ntp_tiles::prefs::kPopularSitesOverrideCountry, country);
+ }
+
+ std::string version;
+ dict->GetString("popular.overrideVersion", &version);
+ if (version.empty()) {
+ prefs->ClearPref(ntp_tiles::prefs::kPopularSitesOverrideVersion);
+ } else {
+ prefs->SetString(ntp_tiles::prefs::kPopularSitesOverrideVersion, version);
+ }
+ }
+
+ // Recreate to pick up new values.
+ // TODO(sfiera): refresh MostVisitedSites without re-creating it, as soon as
+ // that will pick up changes to the Popular Sites overrides.
+ most_visited_sites_ = client_->MakeMostVisitedSites();
+ most_visited_sites_->SetMostVisitedURLsObserver(this, site_count_);
+ SendSourceInfo();
+}
+
+void NTPTilesInternalsMessageHandler::SendSourceInfo() {
+ PrefService* prefs = client_->GetPrefs();
+ base::DictionaryValue value;
+
+ value.SetBoolean("topSites",
+ client_->DoesSourceExist(NTPTileSource::TOP_SITES));
+ value.SetBoolean(
+ "suggestionsService",
+ client_->DoesSourceExist(NTPTileSource::SUGGESTIONS_SERVICE));
+ value.SetBoolean("whitelist",
+ client_->DoesSourceExist(NTPTileSource::WHITELIST));
+
+ if (client_->DoesSourceExist(NTPTileSource::POPULAR)) {
+ auto popular_sites = client_->MakePopularSites();
+ value.SetString("popular.url", popular_sites->GetURLToFetch().spec());
+ value.SetString("popular.country", popular_sites->GetCountryToFetch());
+ value.SetString("popular.version", popular_sites->GetVersionToFetch());
+
+ value.SetString(
+ "popular.overrideURL",
+ prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideURL));
+ value.SetString(
+ "popular.overrideCountry",
+ prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideCountry));
+ value.SetString(
+ "popular.overrideVersion",
+ prefs->GetString(ntp_tiles::prefs::kPopularSitesOverrideVersion));
+ } else {
+ value.SetBoolean("popular", false);
+ }
+
+ client_->CallJavascriptFunction(
+ "chrome.ntp_tiles_internals.receiveSourceInfo", value);
+}
+
+void NTPTilesInternalsMessageHandler::SendTiles(const NTPTilesVector& tiles) {
+ auto sites_list = base::MakeUnique<base::ListValue>();
+ for (const NTPTile& tile : tiles) {
+ auto entry = base::MakeUnique<base::DictionaryValue>();
+ entry->SetString("title", tile.title);
+ entry->SetString("url", tile.url.spec());
+ entry->SetInteger("source", static_cast<int>(tile.source));
+ entry->SetString("whitelistIconPath",
+ tile.whitelist_icon_path.LossyDisplayName());
+ sites_list->Append(std::move(entry));
+ }
+
+ base::DictionaryValue result;
+ result.Set("sites", std::move(sites_list));
+ client_->CallJavascriptFunction("chrome.ntp_tiles_internals.receiveSites",
+ result);
+}
+
+void NTPTilesInternalsMessageHandler::OnMostVisitedURLsAvailable(
+ const NTPTilesVector& tiles) {
+ SendTiles(tiles);
+}
+
+void NTPTilesInternalsMessageHandler::OnIconMadeAvailable(
+ const GURL& site_url) {}
+
+} // 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
new file mode 100644
index 00000000000..23727465e84
--- /dev/null
+++ b/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h
@@ -0,0 +1,61 @@
+// 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_NTP_TILES_INTERNALS_MESSAGE_HANDLER_H_
+#define COMPONENTS_NTP_TILES_WEBUI_NTP_TILES_INTERNALS_MESSAGE_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/ntp_tiles/most_visited_sites.h"
+
+namespace base {
+class ListValue;
+} // namespace base
+
+namespace ntp_tiles {
+
+class MostVisitedSites;
+class NTPTilesInternalsMessageHandlerClient;
+
+// Implements the WebUI message handler for chrome://ntp-tiles-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 NTPTilesInternalsMessageHandler : public MostVisitedSites::Observer {
+ public:
+ NTPTilesInternalsMessageHandler();
+ ~NTPTilesInternalsMessageHandler() override;
+
+ // Called when the WebUI page's JavaScript has loaded and it is ready to
+ // receive RegisterMessageCallback() calls. |client| must outlive this object.
+ void RegisterMessages(NTPTilesInternalsMessageHandlerClient* client);
+
+ private:
+ // Callbacks registered in RegisterMessages().
+ void HandleRegisterForEvents(const base::ListValue* args);
+ void HandleUpdate(const base::ListValue* args);
+
+ void SendSourceInfo();
+ void SendTiles(const NTPTilesVector& tiles);
+
+ // MostVisitedSites::Observer.
+ void OnMostVisitedURLsAvailable(const NTPTilesVector& tiles) override;
+ void OnIconMadeAvailable(const GURL& site_url) override;
+
+ // Bridge to embedder's API.
+ NTPTilesInternalsMessageHandlerClient* client_;
+
+ int site_count_;
+ std::unique_ptr<MostVisitedSites> most_visited_sites_;
+
+ DISALLOW_COPY_AND_ASSIGN(NTPTilesInternalsMessageHandler);
+};
+
+} // namespace ntp_tiles
+
+#endif // COMPONENTS_NTP_TILES_WEBUI_NTP_TILES_INTERNALS_MESSAGE_HANDLER_H_
diff --git a/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.cc b/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.cc
new file mode 100644
index 00000000000..3a5f3069b61
--- /dev/null
+++ b/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.cc
@@ -0,0 +1,14 @@
+// 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/ntp_tiles_internals_message_handler_client.h"
+
+namespace ntp_tiles {
+
+NTPTilesInternalsMessageHandlerClient::NTPTilesInternalsMessageHandlerClient() =
+ default;
+NTPTilesInternalsMessageHandlerClient::
+ ~NTPTilesInternalsMessageHandlerClient() = default;
+
+} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.h b/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.h
new file mode 100644
index 00000000000..d26972d5fc6
--- /dev/null
+++ b/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.h
@@ -0,0 +1,74 @@
+// 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_NTP_TILES_INTERNALS_MESSAGE_HANDLER_CLIENT_H_
+#define COMPONENTS_NTP_TILES_WEBUI_NTP_TILES_INTERNALS_MESSAGE_HANDLER_CLIENT_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "components/ntp_tiles/ntp_tile_source.h"
+
+class PrefService;
+
+namespace base {
+class Value;
+class ListValue;
+} // namespace base
+
+namespace ntp_tiles {
+
+class MostVisitedSites;
+class PopularSites;
+
+// Implemented by embedders to hook up NTPTilesInternalsMessageHandler.
+class NTPTilesInternalsMessageHandlerClient {
+ public:
+ // Returns the PrefService for the embedder and containing WebUI page.
+ virtual PrefService* GetPrefs() = 0;
+
+ // False if in a browser mode (e.g. incognito) where tiles aren't supported.
+ virtual bool SupportsNTPTiles() = 0;
+
+ // Returns true if the given source is enabled (even if, in practice, none of
+ // the tiles would come from it).
+ virtual bool DoesSourceExist(NTPTileSource source) = 0;
+
+ // Creates a new MostVisitedSites based on the context pf the WebUI page.
+ virtual std::unique_ptr<ntp_tiles::MostVisitedSites>
+ MakeMostVisitedSites() = 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;
+
+ // Convenience function for CallJavascriptFunctionVector().
+ template <typename... Arg>
+ void CallJavascriptFunction(const std::string& name, const Arg&... arg) {
+ CallJavascriptFunctionVector(name, {&arg...});
+ }
+
+ protected:
+ NTPTilesInternalsMessageHandlerClient();
+ virtual ~NTPTilesInternalsMessageHandlerClient();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NTPTilesInternalsMessageHandlerClient);
+};
+
+} // namespace ntp_tiles
+
+#endif // COMPONENTS_NTP_TILES_WEBUI_NTP_TILES_INTERNALS_MESSAGE_HANDLER_CLIENT_H_
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
index 50f2252944b..29e69b93eb4 100644
--- a/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.cc
+++ b/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.cc
@@ -6,10 +6,9 @@
#include "base/bind.h"
#include "base/callback.h"
-#include "base/files/file_util.h"
+#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
-#include "base/task_runner_util.h"
#include "base/values.h"
#include "components/ntp_tiles/popular_sites.h"
#include "components/ntp_tiles/pref_names.h"
@@ -18,17 +17,6 @@
#include "components/url_formatter/url_fixer.h"
#include "url/gurl.h"
-namespace {
-
-std::string ReadFileToString(const base::FilePath& path) {
- std::string result;
- if (!base::ReadFileToString(path, &result))
- result.clear();
- return result;
-}
-
-} // namespace
-
namespace ntp_tiles {
PopularSitesInternalsMessageHandlerClient::
@@ -66,10 +54,7 @@ void PopularSitesInternalsMessageHandler::HandleRegisterForEvents(
SendOverrides();
popular_sites_ = web_ui_->MakePopularSites();
- popular_sites_->StartFetch(
- false,
- base::Bind(&PopularSitesInternalsMessageHandler::OnPopularSitesAvailable,
- base::Unretained(this), false));
+ SendSites();
}
void PopularSitesInternalsMessageHandler::HandleUpdate(
@@ -101,25 +86,23 @@ void PopularSitesInternalsMessageHandler::HandleUpdate(
prefs->SetString(ntp_tiles::prefs::kPopularSitesOverrideVersion, version);
popular_sites_ = web_ui_->MakePopularSites();
- popular_sites_->StartFetch(
+ popular_sites_->MaybeStartFetch(
true,
base::Bind(&PopularSitesInternalsMessageHandler::OnPopularSitesAvailable,
- base::Unretained(this), true));
+ base::Unretained(this)));
}
void PopularSitesInternalsMessageHandler::HandleViewJson(
const base::ListValue* args) {
DCHECK_EQ(0u, args->GetSize());
- const base::FilePath& path = popular_sites_->local_path();
- base::PostTaskAndReplyWithResult(
- web_ui_->GetBlockingPool()
- ->GetTaskRunnerWithShutdownBehavior(
- base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)
- .get(),
- FROM_HERE, base::Bind(&ReadFileToString, path),
- base::Bind(&PopularSitesInternalsMessageHandler::SendJson,
- weak_ptr_factory_.GetWeakPtr()));
+ 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() {
@@ -152,7 +135,7 @@ void PopularSitesInternalsMessageHandler::SendSites() {
base::DictionaryValue result;
result.Set("sites", std::move(sites_list));
- result.SetString("url", popular_sites_->LastURL().spec());
+ result.SetString("url", popular_sites_->GetLastURLFetched().spec());
web_ui_->CallJavascriptFunction("chrome.popular_sites_internals.receiveSites",
result);
}
@@ -163,10 +146,8 @@ void PopularSitesInternalsMessageHandler::SendJson(const std::string& json) {
}
void PopularSitesInternalsMessageHandler::OnPopularSitesAvailable(
- bool explicit_request,
bool success) {
- if (explicit_request)
- SendDownloadResult(success);
+ SendDownloadResult(success);
SendSites();
}
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
index 3dbc74522e6..c0e05d0492b 100644
--- a/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.h
+++ b/chromium/components/ntp_tiles/webui/popular_sites_internals_message_handler.h
@@ -47,7 +47,7 @@ class PopularSitesInternalsMessageHandler {
void SendJson(const std::string& json);
// Completion handler for popular_sites_->StartFetch().
- void OnPopularSitesAvailable(bool explicit_request, bool success);
+ void OnPopularSitesAvailable(bool success);
// Bridge to embedder's API.
PopularSitesInternalsMessageHandlerClient* web_ui_;
diff --git a/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.css b/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.css
new file mode 100644
index 00000000000..6de21e508de
--- /dev/null
+++ b/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.css
@@ -0,0 +1,54 @@
+/* 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. */
+
+html {
+ font-size: 20px;
+}
+
+div.section {
+ width: 100%;
+ display: inline-block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+div.section.hidden {
+ display: none;
+}
+
+h2 {
+ color: rgb(74, 142, 230);
+ font-size: 100%;
+ margin-bottom: 0;
+}
+
+.err {
+ color: red;
+}
+
+.section-details {
+ width: 100%;
+}
+
+.section-details th {
+ background: rgb(239, 243, 255);
+}
+
+.section-details td {
+ vertical-align: top;
+}
+
+.section-details td.detail {
+ text-align: right;
+ width: 1px;
+ white-space: nowrap;
+}
+
+.section-details td.value input {
+ width: 100%;
+}
+
+#json-value {
+ font-size: 75%;
+}
diff --git a/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.html b/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
new file mode 100644
index 00000000000..0fb7e6f250e
--- /dev/null
+++ b/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
@@ -0,0 +1,117 @@
+<!--
+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.
+-->
+<!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>NTP Tiles 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="ntp_tiles_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="ntp_tiles_internals.js"></script>
+</head>
+
+<body>
+<div>
+ <div id="sources" class="section">
+ <h2>Sources</h2>
+ <table class="section-details">
+ <tbody jsselect="topSites">
+ <tr>
+ <th colspan="2">TOP_SITES</th>
+ </tr>
+ <tr>
+ <td class="detail">enabled</td>
+ <td class="value" jsdisplay="$this">yes</td>
+ <td class="value" jsdisplay="!$this">no</td>
+ </tr>
+ <tr>
+ <th colspan="2">SUGGESTIONS_SERVICE</th>
+ </tr>
+ </tbody>
+ <tbody jsselect="suggestionsService">
+ <tr>
+ <td class="detail">enabled</td>
+ <td class="value" jsdisplay="$this">yes</td>
+ <td class="value" jsdisplay="!$this">no</td>
+ </tr>
+ </tbody>
+ <tbody jsselect="popular">
+ <tr>
+ <th colspan="2">POPULAR</th>
+ </tr>
+ <tr jsdisplay="$this">
+ <td class="detail">URL</td>
+ <td class="value"><input id="override-url" type="text" jsvalues="value:overrideURL;placeholder:url"></td>
+ </tr>
+ <tr jsdisplay="$this">
+ <td class="detail">Country</td>
+ <td class="value"><input id="override-country" type="text" jsvalues="value:overrideCountry;placeholder:country"></td>
+ </tr>
+ <tr jsdisplay="$this">
+ <td class="detail">Version</td>
+ <td class="value"><input id="override-version" type="text" jsvalues="values:overrideVersion;placeholder:version"></td>
+ </tr>
+ <tr jsdisplay="!$this">
+ <td class="detail">enabled</td>
+ <td class="value">no</td>
+ </tr>
+ </tbody>
+ <tbody jsselect="whitelist">
+ <tr>
+ <th colspan="2">WHITELIST</th>
+ </tr>
+ <tr>
+ <td class="detail">enabled</td>
+ <td class="value" jsdisplay="$this">yes</td>
+ <td class="value" jsdisplay="!$this">no</td>
+ </tr>
+ <tr jsskip="true">
+ <th colspan="2"><input id="submit-update" type="submit" value="Update"></input></th>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div id="sites" class="section">
+ <h2>Sites</h2>
+ <table class="section-details">
+ <tbody jsselect="sites">
+ <tr>
+ <th colspan="2" jscontent="title"></th>
+ </tr>
+ <tr>
+ <td class="detail">Source</td>
+ <td class="value" jsdisplay="source &lt; 0">???</td>
+ <td class="value" jsdisplay="source == 0">TOP_SITES</td>
+ <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>
+ </tr>
+ <tr>
+ <td class="detail">URL</td>
+ <td class="value"><a jsvalues="href:url" jscontent="url"></a></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
+
+</body>
+</html>
+
diff --git a/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.js b/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.js
new file mode 100644
index 00000000000..bcb05d5e68c
--- /dev/null
+++ b/chromium/components/ntp_tiles/webui/resources/ntp_tiles_internals.js
@@ -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.
+
+cr.define('chrome.ntp_tiles_internals', function() {
+ 'use strict';
+
+ var initialize = function() {
+ receiveSites({});
+
+ $('submit-update').addEventListener('click', function(event) {
+ event.preventDefault();
+ chrome.send('update', [{
+ "popular": {
+ "overrideURL": $('override-url').value,
+ "overrideCountry": $('override-country').value,
+ "overrideVersion": $('override-version').value,
+ },
+ }])
+ });
+
+ chrome.send('registerForEvents');
+ }
+
+ var receiveSourceInfo = function(state) {
+ jstProcess(new JsEvalContext(state), $('sources'));
+ }
+
+ var receiveSites = function(sites) {
+ jstProcess(new JsEvalContext(sites), $('sites'));
+ }
+
+ // Return an object with all of the exports.
+ return {
+ initialize: initialize,
+ receiveSourceInfo: receiveSourceInfo,
+ receiveSites: receiveSites,
+ };
+});
+
+document.addEventListener('DOMContentLoaded',
+ chrome.ntp_tiles_internals.initialize);
+
diff --git a/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.html b/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.html
index d82e0940dd0..81d8145c963 100644
--- a/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.html
+++ b/chromium/components/ntp_tiles/webui/resources/popular_sites_internals.html
@@ -8,8 +8,7 @@ found in the LICENSE file.
<head>
<meta charset="utf-8">
<if expr="is_android or is_ios">
-<meta name="viewport" content="width=device-width, initial-scale=1.0,
- maximum-scale=1.0, user-scalable=no">
+<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">
diff --git a/chromium/components/offline_pages/BUILD.gn b/chromium/components/offline_pages/BUILD.gn
deleted file mode 100644
index 123c1e4068d..00000000000
--- a/chromium/components/offline_pages/BUILD.gn
+++ /dev/null
@@ -1,121 +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("offline_pages") {
- sources = [
- "archive_manager.cc",
- "archive_manager.h",
- "client_namespace_constants.cc",
- "client_namespace_constants.h",
- "client_policy_controller.cc",
- "client_policy_controller.h",
- "offline_event_logger.cc",
- "offline_event_logger.h",
- "offline_page_archiver.h",
- "offline_page_client_policy.h",
- "offline_page_item.cc",
- "offline_page_item.h",
- "offline_page_metadata_store.cc",
- "offline_page_metadata_store.h",
- "offline_page_metadata_store_sql.cc",
- "offline_page_metadata_store_sql.h",
- "offline_page_model.cc",
- "offline_page_model.h",
- "offline_page_model_event_logger.cc",
- "offline_page_model_event_logger.h",
- "offline_page_model_impl.cc",
- "offline_page_model_impl.h",
- "offline_page_model_query.cc",
- "offline_page_model_query.h",
- "offline_page_storage_manager.cc",
- "offline_page_storage_manager.h",
- "offline_page_types.h",
- "offline_store_types.h",
- "snapshot_controller.cc",
- "snapshot_controller.h",
- ]
-
- deps = [
- ":switches",
- "//base",
- "//components/keyed_service/core",
- "//net",
- "//sql:sql",
- "//url",
- ]
-}
-
-static_library("test_support") {
- testonly = true
- sources = [
- "offline_page_test_archiver.cc",
- "offline_page_test_archiver.h",
- "offline_page_test_store.cc",
- "offline_page_test_store.h",
- "stub_offline_page_model.cc",
- "stub_offline_page_model.h",
- ]
-
- deps = [
- ":offline_pages",
- ":switches",
- "//base",
- "//components/keyed_service/core",
- "//testing/gtest",
- "//url",
- ]
-}
-
-static_library("switches") {
- sources = [
- "offline_page_feature.cc",
- "offline_page_feature.h",
- ]
-
- deps = [
- "//base",
- "//components/version_info",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
- sources = [
- "archive_manager_unittest.cc",
- "client_policy_controller_unittest.cc",
- "offline_page_metadata_store_impl_unittest.cc",
- "offline_page_model_event_logger_unittest.cc",
- "offline_page_model_impl_unittest.cc",
- "offline_page_model_query_unittest.cc",
- "offline_page_storage_manager_unittest.cc",
- "snapshot_controller_unittest.cc",
- ]
-
- deps = [
- ":offline_pages",
- ":switches",
- ":test_support",
- "//base",
- "//base/test:test_support",
- "//sql:sql",
- "//testing/gtest",
- "//url",
- ]
-}
-
-if (is_android) {
- java_cpp_enum("offline_page_model_enums_java") {
- sources = [
- "background/request_notifier.h",
- "background/request_queue_results.h",
- "background/save_page_request.h",
- "offline_page_types.h",
- "offline_store_types.h",
- ]
- }
-}
diff --git a/chromium/components/offline_pages/background/change_requests_state_task.cc b/chromium/components/offline_pages/background/change_requests_state_task.cc
deleted file mode 100644
index f78665aa82d..00000000000
--- a/chromium/components/offline_pages/background/change_requests_state_task.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/change_requests_state_task.h"
-
-#include "base/bind.h"
-
-namespace offline_pages {
-
-ChangeRequestsStateTask::ChangeRequestsStateTask(
- RequestQueueStore* store,
- const std::vector<int64_t>& request_ids,
- const SavePageRequest::RequestState new_state,
- const RequestQueueStore::UpdateCallback& callback)
- : store_(store),
- request_ids_(request_ids.begin(), request_ids.end()),
- new_state_(new_state),
- callback_(callback),
- weak_ptr_factory_(this) {}
-
-ChangeRequestsStateTask::~ChangeRequestsStateTask() {}
-
-void ChangeRequestsStateTask::Run() {
- ReadRequests();
-}
-
-void ChangeRequestsStateTask::ReadRequests() {
- if (request_ids_.empty()) {
- CompleteEarly(ItemActionStatus::NOT_FOUND);
- return;
- }
-
- store_->GetRequests(base::Bind(&ChangeRequestsStateTask::SelectItemsToUpdate,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void ChangeRequestsStateTask::SelectItemsToUpdate(
- bool success,
- std::vector<std::unique_ptr<SavePageRequest>> requests) {
- if (!success) {
- CompleteEarly(ItemActionStatus::STORE_ERROR);
- return;
- }
-
- std::vector<SavePageRequest> items_to_update;
- for (const auto& request : requests) {
- // If this request is in our list, update it.
- if (request_ids_.count(request->request_id()) > 0) {
- request->set_request_state(new_state_);
- items_to_update.push_back(*request);
- // Items that are missing before the update will be marked as not found
- // before the callback.
- request_ids_.erase(request->request_id());
- }
- }
-
- if (items_to_update.empty()) {
- CompleteEarly(ItemActionStatus::NOT_FOUND);
- return;
- }
-
- store_->UpdateRequests(items_to_update,
- base::Bind(&ChangeRequestsStateTask::UpdateCompleted,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void ChangeRequestsStateTask::UpdateCompleted(
- std::unique_ptr<UpdateRequestsResult> update_result) {
- CompleteWithStatus(std::move(update_result), ItemActionStatus::NOT_FOUND);
-}
-
-void ChangeRequestsStateTask::CompleteEarly(ItemActionStatus status) {
- // TODO(fgorski): store_->state() once implemented
- std::unique_ptr<UpdateRequestsResult> result(
- new UpdateRequestsResult(StoreState::LOADED));
- CompleteWithStatus(std::move(result), status);
-}
-
-void ChangeRequestsStateTask::CompleteWithStatus(
- std::unique_ptr<UpdateRequestsResult> result,
- ItemActionStatus status) {
- // Mark items as not found, if they are still in the request IDs set.
- for (int64_t request_id : request_ids_)
- result->item_statuses.push_back(std::make_pair(request_id, status));
- callback_.Run(std::move(result));
- TaskComplete();
-}
-
-} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/offliner_factory.h b/chromium/components/offline_pages/background/offliner_factory.h
deleted file mode 100644
index 0cfae0a40ea..00000000000
--- a/chromium/components/offline_pages/background/offliner_factory.h
+++ /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.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_FACTORY_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_FACTORY_H_
-
-#include "base/macros.h"
-
-namespace offline_pages {
-
-class OfflinerPolicy;
-class Offliner;
-
-// An interface to a factory which can create a background offliner.
-// The returned factory is owned by the offliner, which is allowed to cache the
-// factory and return it again later.
-class OfflinerFactory {
- public:
- OfflinerFactory() {}
- virtual ~OfflinerFactory() {}
-
- virtual Offliner* GetOffliner(const OfflinerPolicy* policy) = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(OfflinerFactory);
-};
-
-} // namespace offline_pages
-
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_FACTORY_H_
diff --git a/chromium/components/offline_pages/background/pick_request_task_factory.cc b/chromium/components/offline_pages/background/pick_request_task_factory.cc
deleted file mode 100644
index 8d2a00eb4b1..00000000000
--- a/chromium/components/offline_pages/background/pick_request_task_factory.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/offline_pages/background/pick_request_task_factory.h"
-
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_notifier.h"
-
-namespace offline_pages {
-
-// Capture the common parameters that we will need to make a pick task,
-// and use them when making tasks. Create this once each session, and
-// use it to build a picker task when needed.
-PickRequestTaskFactory::PickRequestTaskFactory(
- OfflinerPolicy* policy,
- RequestNotifier* notifier,
- RequestCoordinatorEventLogger* event_logger)
- : policy_(policy), notifier_(notifier), event_logger_(event_logger) {}
-
-PickRequestTaskFactory::~PickRequestTaskFactory() {}
-
-std::unique_ptr<PickRequestTask> PickRequestTaskFactory::CreatePickerTask(
- RequestQueueStore* store,
- const PickRequestTask::RequestPickedCallback& picked_callback,
- const PickRequestTask::RequestNotPickedCallback& not_picked_callback,
- const PickRequestTask::RequestCountCallback& request_count_callback,
- DeviceConditions& conditions,
- std::set<int64_t>& disabled_requests) {
- std::unique_ptr<PickRequestTask> task(new PickRequestTask(
- store, policy_, notifier_, event_logger_, picked_callback,
- not_picked_callback, request_count_callback, conditions,
- disabled_requests));
- return task;
-}
-
-} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/pick_request_task_factory.h b/chromium/components/offline_pages/background/pick_request_task_factory.h
deleted file mode 100644
index 78d5c311fe1..00000000000
--- a/chromium/components/offline_pages/background/pick_request_task_factory.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_FACTORY_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_FACTORY_H_
-
-#include <stdint.h>
-
-#include <set>
-
-#include "components/offline_pages/background/pick_request_task.h"
-
-namespace offline_pages {
-
-class DeviceContitions;
-class OfflinerPolicy;
-class RequestCoordinatorEventLogger;
-class RequestNotifier;
-class RequestQueueStore;
-
-class PickRequestTaskFactory {
- public:
- PickRequestTaskFactory(OfflinerPolicy* policy,
- RequestNotifier* notifier,
- RequestCoordinatorEventLogger* event_logger);
-
- ~PickRequestTaskFactory();
-
- std::unique_ptr<PickRequestTask> CreatePickerTask(
- RequestQueueStore* store,
- const PickRequestTask::RequestPickedCallback& picked_callback,
- const PickRequestTask::RequestNotPickedCallback& not_picked_callback,
- const PickRequestTask::RequestCountCallback& request_count_callback,
- DeviceConditions& conditions,
- std::set<int64_t>& disabled_requests);
-
- private:
- // Unowned pointer to the Policy
- OfflinerPolicy* policy_;
- // Unowned pointer to the notifier
- RequestNotifier* notifier_;
- // Unowned pointer to the EventLogger
- RequestCoordinatorEventLogger* event_logger_;
-};
-
-} // namespace offline_pages
-
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_FACTORY_H_
diff --git a/chromium/components/offline_pages/content/background_loader/BUILD.gn b/chromium/components/offline_pages/content/background_loader/BUILD.gn
new file mode 100644
index 00000000000..b552743994a
--- /dev/null
+++ b/chromium/components/offline_pages/content/background_loader/BUILD.gn
@@ -0,0 +1,46 @@
+# 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.
+
+if (is_android) {
+ import("//build/config/android/rules.gni")
+}
+
+source_set("background_loader") {
+ sources = [
+ "background_loader_contents.cc",
+ "background_loader_contents.h",
+ ]
+
+ deps = [
+ "//content/public/browser",
+ "//url",
+ ]
+}
+
+source_set("test_support") {
+ testonly = true
+
+ sources = [
+ "background_loader_contents_stub.cc",
+ "background_loader_contents_stub.h",
+ ]
+
+ deps = [
+ ":background_loader",
+ "//content/test:test_support",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "background_loader_contents_unittest.cc",
+ ]
+ deps = [
+ ":background_loader",
+ "//base",
+ "//content/test:test_support",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/offline_pages/content/background_loader/DEPS b/chromium/components/offline_pages/content/background_loader/DEPS
index 1c35d9ca694..d79a7d00f02 100644
--- a/chromium/components/offline_pages/content/background_loader/DEPS
+++ b/chromium/components/offline_pages/content/background_loader/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+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
new file mode 100644
index 00000000000..5998fc78f3a
--- /dev/null
+++ b/chromium/components/offline_pages/content/background_loader/background_loader_contents.cc
@@ -0,0 +1,122 @@
+// 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/offline_pages/content/background_loader/background_loader_contents.h"
+
+#include "content/public/browser/web_contents.h"
+
+namespace background_loader {
+
+BackgroundLoaderContents::BackgroundLoaderContents(
+ content::BrowserContext* browser_context)
+ : browser_context_(browser_context) {
+ web_contents_.reset(content::WebContents::Create(
+ content::WebContents::CreateParams(browser_context_)));
+ web_contents_->SetDelegate(this);
+}
+
+BackgroundLoaderContents::~BackgroundLoaderContents() {}
+
+void BackgroundLoaderContents::LoadPage(const GURL& url) {
+ web_contents_->GetController().LoadURL(
+ url /* url to be loaded */,
+ content::Referrer() /* Default referrer policy, no referring url */,
+ ui::PAGE_TRANSITION_LINK /* page transition type: clicked on link */,
+ std::string() /* extra headers */);
+}
+
+void BackgroundLoaderContents::Cancel() {
+ web_contents_->Close();
+}
+
+bool BackgroundLoaderContents::IsNeverVisible(
+ content::WebContents* web_contents) {
+ // Background, so not visible.
+ return true;
+}
+
+void BackgroundLoaderContents::CloseContents(content::WebContents* source) {
+ // Do nothing. Other pages should not be able to close a background page.
+ NOTREACHED();
+}
+
+bool BackgroundLoaderContents::ShouldSuppressDialogs(
+ content::WebContents* source) {
+ // Dialog prompts are not actionable in the background.
+ return true;
+}
+
+bool BackgroundLoaderContents::ShouldFocusPageAfterCrash() {
+ // Background page should never be focused.
+ return false;
+}
+
+void BackgroundLoaderContents::CanDownload(
+ const GURL& url,
+ const std::string& request_method,
+ const base::Callback<void(bool)>& callback) {
+ // Do not download anything.
+ callback.Run(false);
+}
+
+bool BackgroundLoaderContents::ShouldCreateWebContents(
+ content::WebContents* web_contents,
+ content::SiteInstance* source_site_instance,
+ int32_t route_id,
+ int32_t main_frame_route_id,
+ int32_t main_frame_widget_route_id,
+ WindowContainerType window_container_type,
+ const GURL& opener_url,
+ const std::string& frame_name,
+ const GURL& target_url,
+ const std::string& partition_id,
+ content::SessionStorageNamespace* session_storage_namespace) {
+ // Background pages should not create other webcontents/tabs.
+ return false;
+}
+
+void BackgroundLoaderContents::AddNewContents(
+ content::WebContents* source,
+ content::WebContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_rect,
+ bool user_gesture,
+ bool* was_blocked) {
+ // Pop-ups should be blocked;
+ // background pages should not create other contents
+ if (was_blocked != nullptr)
+ *was_blocked = true;
+}
+
+#if defined(OS_ANDROID)
+bool BackgroundLoaderContents::ShouldBlockMediaRequest(const GURL& url) {
+ // Background pages should not have access to media.
+ return true;
+}
+#endif
+
+void BackgroundLoaderContents::RequestMediaAccessPermission(
+ content::WebContents* contents,
+ const content::MediaStreamRequest& request,
+ const content::MediaResponseCallback& callback) {
+ // No permissions granted, act as if dismissed.
+ callback.Run(
+ content::MediaStreamDevices(),
+ content::MediaStreamRequestResult::MEDIA_DEVICE_PERMISSION_DISMISSED,
+ std::unique_ptr<content::MediaStreamUI>());
+}
+
+bool BackgroundLoaderContents::CheckMediaAccessPermission(
+ content::WebContents* contents,
+ const GURL& security_origin,
+ content::MediaStreamType type) {
+ return false; // No permissions granted.
+}
+
+BackgroundLoaderContents::BackgroundLoaderContents()
+ : browser_context_(nullptr) {
+ web_contents_.reset();
+}
+
+} // namespace background_loader
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 3ea2460d2d2..43b6ceae485 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
@@ -7,12 +7,12 @@
#include <string>
-#include "base/callback.h"
#include "content/public/browser/web_contents_delegate.h"
#include "url/gurl.h"
namespace content {
class WebContents;
+class BrowserContext;
}
namespace background_loader {
@@ -28,9 +28,9 @@ class BackgroundLoaderContents : public content::WebContentsDelegate {
// Loads the URL in a WebContents. Will call observe on all current observers
// with the created WebContents.
- void LoadPage(const GURL& url);
+ virtual void LoadPage(const GURL& url);
// Cancels loading of the current page. Calls Close() on internal WebContents.
- void Cancel();
+ virtual void Cancel();
// Returns the inner web contents.
content::WebContents* web_contents() { return web_contents_.get(); }
@@ -44,11 +44,13 @@ class BackgroundLoaderContents : public content::WebContentsDelegate {
const base::Callback<void(bool)>& callback) override;
bool ShouldCreateWebContents(
- content::WebContents* contents,
+ content::WebContents* web_contents,
+ content::SiteInstance* source_site_instance,
int32_t route_id,
int32_t main_frame_route_id,
int32_t main_frame_widget_route_id,
WindowContainerType window_container_type,
+ const GURL& opener_url,
const std::string& frame_name,
const GURL& target_url,
const std::string& partition_id,
@@ -74,6 +76,11 @@ class BackgroundLoaderContents : public content::WebContentsDelegate {
content::MediaStreamType type) override;
private:
+ friend class BackgroundLoaderContentsTest;
+ friend class BackgroundLoaderContentsStub;
+
+ BackgroundLoaderContents();
+
std::unique_ptr<content::WebContents> web_contents_;
content::BrowserContext* browser_context_;
diff --git a/chromium/components/offline_pages/content/background_loader/background_loader_contents_stub.cc b/chromium/components/offline_pages/content/background_loader/background_loader_contents_stub.cc
new file mode 100644
index 00000000000..2acbe965c41
--- /dev/null
+++ b/chromium/components/offline_pages/content/background_loader/background_loader_contents_stub.cc
@@ -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.
+
+#include "components/offline_pages/content/background_loader/background_loader_contents_stub.h"
+
+#include "base/logging.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/web_contents_tester.h"
+
+namespace background_loader {
+
+BackgroundLoaderContentsStub::BackgroundLoaderContentsStub(
+ content::BrowserContext* browser_context)
+ : BackgroundLoaderContents(), is_loading_(false) {
+ BackgroundLoaderContents::web_contents_.reset(
+ content::WebContentsTester::CreateTestWebContents(browser_context, NULL));
+ web_contents_.get()->SetDelegate(this);
+}
+
+BackgroundLoaderContentsStub::~BackgroundLoaderContentsStub() {}
+
+void BackgroundLoaderContentsStub::LoadPage(const GURL& url) {
+ is_loading_ = true;
+}
+
+} // namespace background_loader
diff --git a/chromium/components/offline_pages/content/background_loader/background_loader_contents_stub.h b/chromium/components/offline_pages/content/background_loader/background_loader_contents_stub.h
new file mode 100644
index 00000000000..5b13baba1ac
--- /dev/null
+++ b/chromium/components/offline_pages/content/background_loader/background_loader_contents_stub.h
@@ -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.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CONTENT_BACKGROUND_LOADER_BACKGROUND_LOADER_CONTENTS_STUB_H_
+#define COMPONENTS_OFFLINE_PAGES_CONTENT_BACKGROUND_LOADER_BACKGROUND_LOADER_CONTENTS_STUB_H_
+
+#include "components/offline_pages/content/background_loader/background_loader_contents.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace background_loader {
+
+// Stub BackgroundLoaderContents for testing use.
+class BackgroundLoaderContentsStub : public BackgroundLoaderContents {
+ public:
+ BackgroundLoaderContentsStub(content::BrowserContext* browser_context);
+ ~BackgroundLoaderContentsStub() override;
+
+ void LoadPage(const GURL& url) override;
+ bool is_loading() const { return is_loading_; }
+
+ private:
+ bool is_loading_;
+ DISALLOW_COPY_AND_ASSIGN(BackgroundLoaderContentsStub);
+};
+
+} // namespace background_loader
+#endif // COMPONENTS_OFFLINE_PAGES_CONTENT_BACKGROUND_LOADER_BACKGROUND_LOADER_CONTENTS_STUB_H_
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
new file mode 100644
index 00000000000..f430467532f
--- /dev/null
+++ b/chromium/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
@@ -0,0 +1,149 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/content/background_loader/background_loader_contents.h"
+
+#include "base/synchronization/waitable_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace background_loader {
+
+class BackgroundLoaderContentsTest : public testing::Test {
+ public:
+ BackgroundLoaderContentsTest();
+ ~BackgroundLoaderContentsTest() override;
+
+ void SetUp() override;
+ void TearDown() override;
+
+ BackgroundLoaderContents* contents() { return contents_.get(); }
+
+ void DownloadCallback(bool download);
+
+ bool download() { return download_; }
+
+ void MediaAccessCallback(const content::MediaStreamDevices& devices,
+ content::MediaStreamRequestResult result,
+ std::unique_ptr<content::MediaStreamUI> ui);
+ content::MediaStreamDevices devices() { return devices_; }
+ content::MediaStreamRequestResult request_result() { return request_result_; }
+ content::MediaStreamUI* media_stream_ui() { return media_stream_ui_.get(); }
+
+ void WaitForSignal() { waiter_.Wait(); }
+
+ private:
+ std::unique_ptr<BackgroundLoaderContents> contents_;
+ bool download_;
+ content::MediaStreamDevices devices_;
+ content::MediaStreamRequestResult request_result_;
+ std::unique_ptr<content::MediaStreamUI> media_stream_ui_;
+ base::WaitableEvent waiter_;
+};
+
+BackgroundLoaderContentsTest::BackgroundLoaderContentsTest()
+ : download_(false),
+ waiter_(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED){};
+
+BackgroundLoaderContentsTest::~BackgroundLoaderContentsTest(){};
+
+void BackgroundLoaderContentsTest::SetUp() {
+ contents_.reset(new BackgroundLoaderContents());
+ download_ = false;
+}
+
+void BackgroundLoaderContentsTest::TearDown() {
+ contents_.reset();
+}
+
+void BackgroundLoaderContentsTest::DownloadCallback(bool download) {
+ download_ = download;
+ waiter_.Signal();
+}
+
+void BackgroundLoaderContentsTest::MediaAccessCallback(
+ const content::MediaStreamDevices& devices,
+ content::MediaStreamRequestResult result,
+ std::unique_ptr<content::MediaStreamUI> ui) {
+ devices_ = devices;
+ request_result_ = result;
+ media_stream_ui_.reset(ui.get());
+ waiter_.Signal();
+}
+
+TEST_F(BackgroundLoaderContentsTest, NotVisible) {
+ ASSERT_TRUE(contents()->IsNeverVisible(nullptr));
+}
+
+TEST_F(BackgroundLoaderContentsTest, SuppressDialogs) {
+ ASSERT_TRUE(contents()->ShouldSuppressDialogs(nullptr));
+}
+
+TEST_F(BackgroundLoaderContentsTest, DoesNotFocusAfterCrash) {
+ ASSERT_FALSE(contents()->ShouldFocusPageAfterCrash());
+}
+
+TEST_F(BackgroundLoaderContentsTest, CannotDownload) {
+ contents()->CanDownload(
+ GURL::EmptyGURL(), std::string(),
+ base::Bind(&BackgroundLoaderContentsTest::DownloadCallback,
+ base::Unretained(this)));
+ WaitForSignal();
+ ASSERT_FALSE(download());
+}
+
+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 */,
+ WINDOW_CONTAINER_TYPE_NORMAL /* window_container_type */,
+ GURL() /* opener_url */, "foo" /* frame_name */,
+ GURL::EmptyGURL() /* target_url */, "bar" /* partition_id */,
+ nullptr /* session_storage_namespace */));
+}
+
+TEST_F(BackgroundLoaderContentsTest, ShouldNotAddNewContents) {
+ bool blocked;
+ contents()->AddNewContents(
+ nullptr /* source */, nullptr /* new_contents */,
+ WindowOpenDisposition::CURRENT_TAB /* disposition */,
+ gfx::Rect() /* initial_rect */, false /* user_gesture */,
+ &blocked /* was_blocked */);
+ ASSERT_TRUE(blocked);
+}
+
+TEST_F(BackgroundLoaderContentsTest, DoesNotGiveMediaAccessPermission) {
+ content::MediaStreamRequest request(
+ 0 /* render_process_id */, 0 /* render_frame_id */,
+ 0 /* page_request_id */, GURL::EmptyGURL() /* security_origin */,
+ false /* user_gesture */,
+ content::MediaStreamRequestType::MEDIA_DEVICE_ACCESS /* request_type */,
+ std::string() /* requested_audio_device_id */,
+ std::string() /* requested_video_device_id */,
+ content::MediaStreamType::MEDIA_TAB_AUDIO_CAPTURE /* audio_type */,
+ content::MediaStreamType::MEDIA_TAB_VIDEO_CAPTURE /* video_type */,
+ false /* disable_local_echo */);
+ contents()->RequestMediaAccessPermission(
+ nullptr /* contents */, request /* request */,
+ base::Bind(&BackgroundLoaderContentsTest::MediaAccessCallback,
+ base::Unretained(this)));
+ WaitForSignal();
+ // No devices allowed.
+ ASSERT_TRUE(devices().empty());
+ // Permission has been dismissed rather than denied.
+ ASSERT_EQ(
+ content::MediaStreamRequestResult::MEDIA_DEVICE_PERMISSION_DISMISSED,
+ request_result());
+ ASSERT_EQ(nullptr, media_stream_ui());
+}
+
+TEST_F(BackgroundLoaderContentsTest, CheckMediaAccessPermissionFalse) {
+ ASSERT_FALSE(contents()->CheckMediaAccessPermission(
+ nullptr /* contents */, GURL::EmptyGURL() /* security_origin */,
+ content::MediaStreamType::MEDIA_TAB_VIDEO_CAPTURE /* type */));
+}
+
+} // namespace background_loader
diff --git a/chromium/components/offline_pages/core/BUILD.gn b/chromium/components/offline_pages/core/BUILD.gn
index d84ddee4082..198ddcdeb5f 100644
--- a/chromium/components/offline_pages/core/BUILD.gn
+++ b/chromium/components/offline_pages/core/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# 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.
@@ -8,6 +8,36 @@ if (is_android) {
static_library("core") {
sources = [
+ "archive_manager.cc",
+ "archive_manager.h",
+ "client_namespace_constants.cc",
+ "client_namespace_constants.h",
+ "client_policy_controller.cc",
+ "client_policy_controller.h",
+ "offline_event_logger.cc",
+ "offline_event_logger.h",
+ "offline_page_archiver.h",
+ "offline_page_client_policy.h",
+ "offline_page_item.cc",
+ "offline_page_item.h",
+ "offline_page_metadata_store.cc",
+ "offline_page_metadata_store.h",
+ "offline_page_metadata_store_sql.cc",
+ "offline_page_metadata_store_sql.h",
+ "offline_page_model.cc",
+ "offline_page_model.h",
+ "offline_page_model_event_logger.cc",
+ "offline_page_model_event_logger.h",
+ "offline_page_model_impl.cc",
+ "offline_page_model_impl.h",
+ "offline_page_model_query.cc",
+ "offline_page_model_query.h",
+ "offline_page_storage_manager.cc",
+ "offline_page_storage_manager.h",
+ "offline_page_types.h",
+ "offline_store_types.h",
+ "snapshot_controller.cc",
+ "snapshot_controller.h",
"task.cc",
"task.h",
"task_queue.cc",
@@ -15,35 +45,85 @@ static_library("core") {
]
deps = [
+ ":switches",
"//base",
+ "//components/keyed_service/core",
+ "//net",
+ "//sql:sql",
+ "//url",
]
}
static_library("test_support") {
testonly = true
sources = [
+ "offline_page_test_archiver.cc",
+ "offline_page_test_archiver.h",
+ "offline_page_test_store.cc",
+ "offline_page_test_store.h",
+ "stub_offline_page_model.cc",
+ "stub_offline_page_model.h",
"test_task.cc",
"test_task.h",
]
deps = [
":core",
+ ":switches",
"//base",
+ "//components/keyed_service/core",
+ "//testing/gtest",
+ "//url",
+ ]
+}
+
+static_library("switches") {
+ sources = [
+ "offline_page_feature.cc",
+ "offline_page_feature.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/version_info",
]
}
source_set("unit_tests") {
testonly = true
sources = [
+ "archive_manager_unittest.cc",
+ "client_policy_controller_unittest.cc",
+ "offline_event_logger_unittest.cc",
+ "offline_page_metadata_store_impl_unittest.cc",
+ "offline_page_model_event_logger_unittest.cc",
+ "offline_page_model_impl_unittest.cc",
+ "offline_page_storage_manager_unittest.cc",
+ "snapshot_controller_unittest.cc",
"task_queue_unittest.cc",
"task_unittest.cc",
]
deps = [
":core",
+ ":switches",
":test_support",
"//base",
"//base/test:test_support",
- "//testing/gtest:gtest",
+ "//sql:sql",
+ "//testing/gtest",
+ "//url",
]
}
+
+if (is_android) {
+ java_cpp_enum("offline_page_model_enums_java") {
+ sources = [
+ "background/request_notifier.h",
+ "background/request_queue_results.h",
+ "background/save_page_request.h",
+ "offline_page_types.h",
+ "offline_store_types.h",
+ ]
+ }
+}
diff --git a/chromium/components/offline_pages/DEPS b/chromium/components/offline_pages/core/DEPS
index a2326888f13..a2326888f13 100644
--- a/chromium/components/offline_pages/DEPS
+++ b/chromium/components/offline_pages/core/DEPS
diff --git a/chromium/components/offline_pages/archive_manager.cc b/chromium/components/offline_pages/core/archive_manager.cc
index 8df6b541e34..cd81776772c 100644
--- a/chromium/components/offline_pages/archive_manager.cc
+++ b/chromium/components/offline_pages/core/archive_manager.cc
@@ -12,7 +12,7 @@
#include "base/sequenced_task_runner.h"
#include "base/sys_info.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/archive_manager.h"
+#include "components/offline_pages/core/archive_manager.h"
namespace offline_pages {
diff --git a/chromium/components/offline_pages/archive_manager.h b/chromium/components/offline_pages/core/archive_manager.h
index 23e29c14e29..dc92e7de79b 100644
--- a/chromium/components/offline_pages/archive_manager.h
+++ b/chromium/components/offline_pages/core/archive_manager.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_ARCHIVE_MANAGER_H_
-#define COMPONENTS_OFFLINE_PAGES_ARCHIVE_MANAGER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_ARCHIVE_MANAGER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_ARCHIVE_MANAGER_H_
#include <set>
#include <vector>
@@ -75,4 +75,4 @@ class ArchiveManager {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_ARCHIVE_MANAGER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_ARCHIVE_MANAGER_H_
diff --git a/chromium/components/offline_pages/archive_manager_unittest.cc b/chromium/components/offline_pages/core/archive_manager_unittest.cc
index bc50b4f4175..57f868a872c 100644
--- a/chromium/components/offline_pages/archive_manager_unittest.cc
+++ b/chromium/components/offline_pages/core/archive_manager_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/archive_manager.h"
+#include "components/offline_pages/core/archive_manager.h"
#include <algorithm>
#include <memory>
@@ -20,8 +20,8 @@
namespace offline_pages {
namespace {
-const base::FilePath::CharType kMissingArchivePath[] = FILE_PATH_LITERAL(
- "missing_archive.path");
+const base::FilePath::CharType kMissingArchivePath[] =
+ FILE_PATH_LITERAL("missing_archive.path");
} // namespace
enum class CallbackStatus {
@@ -188,8 +188,8 @@ TEST_F(ArchiveManagerTest, DeleteMultipleArchivesSomeDoNotExist) {
TEST_F(ArchiveManagerTest, DeleteMultipleArchivesNoneExist) {
base::FilePath archive_path_1 = temp_path().Append(kMissingArchivePath);
- base::FilePath archive_path_2 = temp_path().Append(FILE_PATH_LITERAL(
- "other_missing_file.mhtml"));
+ base::FilePath archive_path_2 =
+ temp_path().Append(FILE_PATH_LITERAL("other_missing_file.mhtml"));
base::FilePath archive_path_3;
EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_3));
diff --git a/chromium/components/offline_pages/background/BUILD.gn b/chromium/components/offline_pages/core/background/BUILD.gn
index 67676b7d767..e3282b12ac6 100644
--- a/chromium/components/offline_pages/background/BUILD.gn
+++ b/chromium/components/offline_pages/core/background/BUILD.gn
@@ -8,11 +8,21 @@ if (is_android) {
static_library("background_offliner") {
sources = [
+ "add_request_task.cc",
+ "add_request_task.h",
"change_requests_state_task.cc",
"change_requests_state_task.h",
+ "cleanup_task.cc",
+ "cleanup_task.h",
+ "cleanup_task_factory.cc",
+ "cleanup_task_factory.h",
"connection_notifier.cc",
"connection_notifier.h",
"device_conditions.h",
+ "get_requests_task.cc",
+ "get_requests_task.h",
+ "initialize_store_task.cc",
+ "initialize_store_task.h",
"mark_attempt_aborted_task.cc",
"mark_attempt_aborted_task.h",
"mark_attempt_completed_task.cc",
@@ -20,12 +30,13 @@ static_library("background_offliner") {
"mark_attempt_started_task.cc",
"mark_attempt_started_task.h",
"offliner.h",
- "offliner_factory.h",
"offliner_policy.h",
+ "offliner_policy_utils.cc",
+ "offliner_policy_utils.h",
"pick_request_task.cc",
"pick_request_task.h",
- "pick_request_task_factory.cc",
- "pick_request_task_factory.h",
+ "reconcile_task.cc",
+ "reconcile_task.h",
"remove_requests_task.cc",
"remove_requests_task.h",
"request_coordinator.cc",
@@ -35,8 +46,6 @@ static_library("background_offliner") {
"request_notifier.h",
"request_queue.cc",
"request_queue.h",
- "request_queue_in_memory_store.cc",
- "request_queue_in_memory_store.h",
"request_queue_results.h",
"request_queue_store.h",
"request_queue_store_sql.cc",
@@ -51,23 +60,47 @@ static_library("background_offliner") {
deps = [
"//base",
"//components/keyed_service/core",
- "//components/offline_pages:offline_pages",
- "//components/offline_pages:switches",
"//components/offline_pages/core",
+ "//components/offline_pages/core:switches",
"//net",
"//sql:sql",
"//url",
]
}
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "network_quality_provider_stub.cc",
+ "network_quality_provider_stub.h",
+ "offliner_stub.cc",
+ "offliner_stub.h",
+ "request_queue_in_memory_store.cc",
+ "request_queue_in_memory_store.h",
+ "scheduler_stub.cc",
+ "scheduler_stub.h",
+ ]
+
+ deps = [
+ ":background_offliner",
+ "//base",
+ "//net",
+ ]
+}
+
source_set("unit_tests") {
testonly = true
sources = [
+ "add_request_task_unittest.cc",
"change_requests_state_task_unittest.cc",
+ "cleanup_task_unittest.cc",
+ "get_requests_task_unittest.cc",
+ "initialize_store_task_unittest.cc",
"mark_attempt_aborted_task_unittest.cc",
"mark_attempt_completed_task_unittest.cc",
"mark_attempt_started_task_unittest.cc",
"pick_request_task_unittest.cc",
+ "reconcile_task_unittest.cc",
"remove_requests_task_unittest.cc",
"request_coordinator_event_logger_unittest.cc",
"request_coordinator_unittest.cc",
@@ -78,10 +111,11 @@ source_set("unit_tests") {
deps = [
":background_offliner",
+ ":test_support",
"//base",
"//base/test:test_support",
- "//components/offline_pages:offline_pages",
- "//components/offline_pages:switches",
+ "//components/offline_pages/core",
+ "//components/offline_pages/core:switches",
"//testing/gtest",
"//url",
]
diff --git a/chromium/components/offline_pages/background/DEPS b/chromium/components/offline_pages/core/background/DEPS
index 6fff87d325a..6fff87d325a 100644
--- a/chromium/components/offline_pages/background/DEPS
+++ b/chromium/components/offline_pages/core/background/DEPS
diff --git a/chromium/components/offline_pages/core/background/add_request_task.cc b/chromium/components/offline_pages/core/background/add_request_task.cc
new file mode 100644
index 00000000000..822afcb8b55
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/add_request_task.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/add_request_task.h"
+
+#include "base/bind.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+AddRequestTask::AddRequestTask(RequestQueueStore* store,
+ const SavePageRequest& request,
+ const RequestQueueStore::AddCallback& callback)
+ : store_(store),
+ request_(request),
+ callback_(callback),
+ weak_ptr_factory_(this) {}
+
+AddRequestTask::~AddRequestTask() {}
+
+void AddRequestTask::Run() {
+ AddRequest();
+}
+
+void AddRequestTask::AddRequest() {
+ store_->AddRequest(request_, base::Bind(&AddRequestTask::CompleteWithResult,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void AddRequestTask::CompleteWithResult(ItemActionStatus status) {
+ callback_.Run(status);
+ TaskComplete();
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/add_request_task.h b/chromium/components/offline_pages/core/background/add_request_task.h
new file mode 100644
index 00000000000..9544f74b8ca
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/add_request_task.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_ADD_REQUEST_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_ADD_REQUEST_TASK_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class AddRequestTask : public Task {
+ public:
+ AddRequestTask(RequestQueueStore* store,
+ const SavePageRequest& request,
+ const RequestQueueStore::AddCallback& callback);
+ ~AddRequestTask() override;
+
+ // Task implementation:
+ void Run() override;
+
+ private:
+ // Step 1: Add the request to the store.
+ void AddRequest();
+ // Step 2: Calls the callback with result, completes the task.
+ void CompleteWithResult(ItemActionStatus status);
+
+ // Store from which requests will be read.
+ RequestQueueStore* store_;
+ // Request to be added.
+ SavePageRequest request_;
+ // Callback used to return the read results.
+ RequestQueueStore::AddCallback callback_;
+
+ base::WeakPtrFactory<AddRequestTask> weak_ptr_factory_;
+ DISALLOW_COPY_AND_ASSIGN(AddRequestTask);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_ADD_REQUEST_TASK_H_
diff --git a/chromium/components/offline_pages/core/background/add_request_task_unittest.cc b/chromium/components/offline_pages/core/background/add_request_task_unittest.cc
new file mode 100644
index 00000000000..17b86e19e44
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/add_request_task_unittest.cc
@@ -0,0 +1,196 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/add_request_task.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+const int64_t kRequestId1 = 42;
+const int64_t kRequestId2 = 44;
+const GURL kUrl1("http://example.com");
+const GURL kUrl2("http://otherexample.com");
+const ClientId kClientId1("download", "1234");
+const ClientId kClientId2("download", "5678");
+} // namespace
+
+class AddRequestTaskTest : public testing::Test {
+ public:
+ AddRequestTaskTest();
+ ~AddRequestTaskTest() override;
+
+ void PumpLoop();
+ void ClearResults();
+
+ void InitializeStore(RequestQueueStore* store);
+
+ void AddRequestDone(ItemActionStatus status);
+
+ void GetRequestsCallback(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+ bool callback_called() const { return callback_called_; }
+
+ ItemActionStatus last_status() const { return status_; }
+
+ const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
+ return requests_;
+ }
+
+ private:
+ void InitializeStoreDone(bool success);
+
+ bool callback_called_;
+ ItemActionStatus status_;
+ std::vector<std::unique_ptr<SavePageRequest>> requests_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+AddRequestTaskTest::AddRequestTaskTest()
+ : callback_called_(false),
+ status_(ItemActionStatus::NOT_FOUND),
+ task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+AddRequestTaskTest::~AddRequestTaskTest() {}
+
+void AddRequestTaskTest::PumpLoop() {
+ task_runner_->RunUntilIdle();
+}
+
+void AddRequestTaskTest::ClearResults() {
+ callback_called_ = false;
+ status_ = ItemActionStatus::NOT_FOUND;
+ requests_.clear();
+}
+
+void AddRequestTaskTest::InitializeStore(RequestQueueStore* store) {
+ store->Initialize(base::Bind(&AddRequestTaskTest::InitializeStoreDone,
+ base::Unretained(this)));
+ PumpLoop();
+}
+
+void AddRequestTaskTest::AddRequestDone(ItemActionStatus status) {
+ status_ = status;
+ callback_called_ = true;
+}
+
+void AddRequestTaskTest::GetRequestsCallback(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests) {
+ requests_ = std::move(requests);
+}
+
+void AddRequestTaskTest::InitializeStoreDone(bool success) {
+ ASSERT_TRUE(success);
+}
+
+TEST_F(AddRequestTaskTest, AddSingleRequest) {
+ RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+ base::Time creation_time = base::Time::Now();
+ SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
+ true);
+ AddRequestTask task(
+ &store, request_1,
+ base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
+ task.Run();
+ PumpLoop();
+ EXPECT_TRUE(callback_called());
+ EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
+
+ store.GetRequests(base::Bind(&AddRequestTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ ASSERT_EQ(1ul, last_requests().size());
+ EXPECT_EQ(kRequestId1, last_requests().at(0)->request_id());
+ EXPECT_EQ(kUrl1, last_requests().at(0)->url());
+ EXPECT_EQ(kClientId1, last_requests().at(0)->client_id());
+ EXPECT_EQ(creation_time, last_requests().at(0)->creation_time());
+ EXPECT_TRUE(last_requests().at(0)->user_requested());
+}
+
+TEST_F(AddRequestTaskTest, AddMultipleRequests) {
+ RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+ base::Time creation_time_1 = base::Time::Now();
+ SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time_1,
+ true);
+ AddRequestTask task(
+ &store, request_1,
+ base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
+ task.Run();
+ PumpLoop();
+ EXPECT_TRUE(callback_called());
+ EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
+
+ ClearResults();
+ base::Time creation_time_2 = base::Time::Now();
+ SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time_2,
+ true);
+ AddRequestTask task_2(
+ &store, request_2,
+ base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
+ task_2.Run();
+ PumpLoop();
+ EXPECT_TRUE(callback_called());
+ EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
+
+ store.GetRequests(base::Bind(&AddRequestTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ ASSERT_EQ(2ul, last_requests().size());
+ int request_2_index =
+ last_requests().at(0)->request_id() == kRequestId2 ? 0 : 1;
+ EXPECT_EQ(kRequestId2, last_requests().at(request_2_index)->request_id());
+ EXPECT_EQ(kUrl2, last_requests().at(request_2_index)->url());
+ EXPECT_EQ(kClientId2, last_requests().at(request_2_index)->client_id());
+ EXPECT_EQ(creation_time_2,
+ last_requests().at(request_2_index)->creation_time());
+ EXPECT_TRUE(last_requests().at(request_2_index)->user_requested());
+}
+
+TEST_F(AddRequestTaskTest, AddDuplicateRequest) {
+ RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+ base::Time creation_time_1 = base::Time::Now();
+ SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time_1,
+ true);
+ AddRequestTask task(
+ &store, request_1,
+ base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
+ task.Run();
+ PumpLoop();
+ EXPECT_TRUE(callback_called());
+ EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
+
+ ClearResults();
+ base::Time creation_time_2 = base::Time::Now();
+ // This was has the same request ID.
+ SavePageRequest request_2(kRequestId1, kUrl2, kClientId2, creation_time_2,
+ true);
+ AddRequestTask task_2(
+ &store, request_2,
+ base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
+ task.Run();
+ PumpLoop();
+ EXPECT_TRUE(callback_called());
+ EXPECT_EQ(ItemActionStatus::ALREADY_EXISTS, last_status());
+
+ store.GetRequests(base::Bind(&AddRequestTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ ASSERT_EQ(1ul, last_requests().size());
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/change_requests_state_task.cc b/chromium/components/offline_pages/core/background/change_requests_state_task.cc
new file mode 100644
index 00000000000..da540451421
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/change_requests_state_task.cc
@@ -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.
+
+#include "components/offline_pages/core/background/change_requests_state_task.h"
+
+#include "base/bind.h"
+
+namespace offline_pages {
+
+ChangeRequestsStateTask::ChangeRequestsStateTask(
+ RequestQueueStore* store,
+ const std::vector<int64_t>& request_ids,
+ const SavePageRequest::RequestState new_state,
+ const RequestQueueStore::UpdateCallback& callback)
+ : store_(store),
+ request_ids_(request_ids.begin(), request_ids.end()),
+ new_state_(new_state),
+ callback_(callback),
+ weak_ptr_factory_(this) {}
+
+ChangeRequestsStateTask::~ChangeRequestsStateTask() {}
+
+void ChangeRequestsStateTask::Run() {
+ ReadRequests();
+}
+
+void ChangeRequestsStateTask::ReadRequests() {
+ std::vector<int64_t> request_ids(request_ids_.begin(), request_ids_.end());
+ store_->GetRequestsByIds(request_ids,
+ base::Bind(&ChangeRequestsStateTask::UpdateRequests,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ChangeRequestsStateTask::UpdateRequests(
+ std::unique_ptr<UpdateRequestsResult> read_result) {
+ if (read_result->store_state != StoreState::LOADED ||
+ read_result->updated_items.empty()) {
+ UpdateCompleted(std::move(read_result));
+ return;
+ }
+
+ // We are only going to make an update to the items that were found. Statuses
+ // of the missing items will be added at the end.
+ std::vector<SavePageRequest> items_to_update;
+ for (auto request : read_result->updated_items) {
+ request.set_request_state(new_state_);
+ items_to_update.push_back(request);
+ }
+
+ store_->UpdateRequests(items_to_update,
+ base::Bind(&ChangeRequestsStateTask::UpdateCompleted,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ChangeRequestsStateTask::UpdateCompleted(
+ std::unique_ptr<UpdateRequestsResult> update_result) {
+ // Because the first step might not have found some of the items, we should
+ // look their IDs now and include in the final result as not found.
+
+ // Look up the missing items by removing the items present in the result
+ // statuses from original list of request IDs.
+ for (const auto& id_status_pair : update_result->item_statuses)
+ request_ids_.erase(id_status_pair.first);
+
+ // Update the final result for the items that are left in |request_ids_|, as
+ // these are identified as being missing from the final result.
+ for (int64_t request_id : request_ids_) {
+ update_result->item_statuses.push_back(
+ std::make_pair(request_id, ItemActionStatus::NOT_FOUND));
+ }
+
+ callback_.Run(std::move(update_result));
+ TaskComplete();
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/change_requests_state_task.h b/chromium/components/offline_pages/core/background/change_requests_state_task.h
index 2213c687948..63544791d2c 100644
--- a/chromium/components/offline_pages/background/change_requests_state_task.h
+++ b/chromium/components/offline_pages/core/background/change_requests_state_task.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
#include <stdint.h>
@@ -12,8 +12,8 @@
#include <vector>
#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
#include "components/offline_pages/core/task.h"
namespace offline_pages {
@@ -32,21 +32,15 @@ class ChangeRequestsStateTask : public Task {
private:
// Step 1. Reading the requests.
void ReadRequests();
- // Step 2. Selecting requests to be updated. Calls update.
- void SelectItemsToUpdate(
- bool success,
- std::vector<std::unique_ptr<SavePageRequest>> requests);
+ // Step 2. Updates available requests.
+ void UpdateRequests(std::unique_ptr<UpdateRequestsResult> read_result);
// Step 3. Processes update result, calls callback.
void UpdateCompleted(std::unique_ptr<UpdateRequestsResult> update_result);
- // Completions.
- void CompleteEarly(ItemActionStatus status);
- void CompleteWithStatus(std::unique_ptr<UpdateRequestsResult> result,
- ItemActionStatus status);
-
// Store that this task updates.
RequestQueueStore* store_;
- // Request IDs to be updated.
+ // Request IDs to be updated. Kept as a set to remove duplicates and simplify
+ // the look up of requests that are not found in step 3.
std::unordered_set<int64_t> request_ids_;
// New state to be set on all entries.
SavePageRequest::RequestState new_state_;
@@ -60,4 +54,4 @@ class ChangeRequestsStateTask : public Task {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
diff --git a/chromium/components/offline_pages/background/change_requests_state_task_unittest.cc b/chromium/components/offline_pages/core/background/change_requests_state_task_unittest.cc
index c878655ef3c..98826a2a78c 100644
--- a/chromium/components/offline_pages/background/change_requests_state_task_unittest.cc
+++ b/chromium/components/offline_pages/core/background/change_requests_state_task_unittest.cc
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/change_requests_state_task.h"
+#include "components/offline_pages/core/background/change_requests_state_task.h"
#include <memory>
#include "base/bind.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -30,16 +30,17 @@ class ChangeRequestsStateTaskTest : public testing::Test {
void PumpLoop();
- void SetUpStore(RequestQueueStore* store);
-
- void AddRequestDone(ItemActionStatus status);
-
+ void InitializeStore(RequestQueueStore* store);
+ void AddItemsToStore(RequestQueueStore* store);
void ChangeRequestsStateCallback(
std::unique_ptr<UpdateRequestsResult> result);
UpdateRequestsResult* last_result() const { return result_.get(); }
private:
+ void InitializeStoreDone(bool success);
+ void AddRequestDone(ItemActionStatus status);
+
std::unique_ptr<UpdateRequestsResult> result_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
@@ -55,7 +56,14 @@ void ChangeRequestsStateTaskTest::PumpLoop() {
task_runner_->RunUntilIdle();
}
-void ChangeRequestsStateTaskTest::SetUpStore(RequestQueueStore* store) {
+void ChangeRequestsStateTaskTest::InitializeStore(RequestQueueStore* store) {
+ store->Initialize(
+ base::Bind(&ChangeRequestsStateTaskTest::InitializeStoreDone,
+ base::Unretained(this)));
+ PumpLoop();
+}
+
+void ChangeRequestsStateTaskTest::AddItemsToStore(RequestQueueStore* store) {
base::Time creation_time = base::Time::Now();
SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
true);
@@ -70,17 +78,23 @@ void ChangeRequestsStateTaskTest::SetUpStore(RequestQueueStore* store) {
PumpLoop();
}
-void ChangeRequestsStateTaskTest::AddRequestDone(ItemActionStatus status) {
- ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
void ChangeRequestsStateTaskTest::ChangeRequestsStateCallback(
std::unique_ptr<UpdateRequestsResult> result) {
result_ = std::move(result);
}
+void ChangeRequestsStateTaskTest::InitializeStoreDone(bool success) {
+ ASSERT_TRUE(success);
+}
+
+void ChangeRequestsStateTaskTest::AddRequestDone(ItemActionStatus status) {
+ ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
TEST_F(ChangeRequestsStateTaskTest, UpdateWhenStoreEmpty) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+
std::vector<int64_t> request_ids{kRequestId1};
ChangeRequestsStateTask task(
&store, request_ids, SavePageRequest::RequestState::PAUSED,
@@ -98,7 +112,9 @@ TEST_F(ChangeRequestsStateTaskTest, UpdateWhenStoreEmpty) {
TEST_F(ChangeRequestsStateTaskTest, UpdateSingleItem) {
RequestQueueInMemoryStore store;
- SetUpStore(&store);
+ InitializeStore(&store);
+ AddItemsToStore(&store);
+
std::vector<int64_t> request_ids{kRequestId1};
ChangeRequestsStateTask task(
&store, request_ids, SavePageRequest::RequestState::PAUSED,
@@ -118,7 +134,9 @@ TEST_F(ChangeRequestsStateTaskTest, UpdateSingleItem) {
TEST_F(ChangeRequestsStateTaskTest, UpdateMultipleItems) {
RequestQueueInMemoryStore store;
- SetUpStore(&store);
+ InitializeStore(&store);
+ AddItemsToStore(&store);
+
std::vector<int64_t> request_ids{kRequestId1, kRequestId2};
ChangeRequestsStateTask task(
&store, request_ids, SavePageRequest::RequestState::PAUSED,
@@ -127,24 +145,37 @@ TEST_F(ChangeRequestsStateTaskTest, UpdateMultipleItems) {
task.Run();
PumpLoop();
ASSERT_TRUE(last_result());
- EXPECT_EQ(2UL, last_result()->item_statuses.size());
- EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+ ASSERT_EQ(2UL, last_result()->item_statuses.size());
+
+ // Calculating the position of the items in the vector here, because it does
+ // not matter, and might be platform dependent.
+ // |index_id_1| is expected to correspond to |kRequestId1|.
+ int index_id_1 =
+ last_result()->item_statuses.at(0).first == kRequestId1 ? 0 : 1;
+ // |index_id_2| is expected to correspond to |kRequestId2|.
+ int index_id_2 = 1 - index_id_1;
+
+ EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(index_id_1).first);
EXPECT_EQ(ItemActionStatus::SUCCESS,
- last_result()->item_statuses.at(0).second);
- EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(1).first);
+ last_result()->item_statuses.at(index_id_1).second);
+ EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(index_id_2).first);
EXPECT_EQ(ItemActionStatus::SUCCESS,
- last_result()->item_statuses.at(1).second);
- EXPECT_EQ(2UL, last_result()->updated_items.size());
- EXPECT_EQ(kRequestId1, last_result()->updated_items.at(0).request_id());
+ last_result()->item_statuses.at(index_id_2).second);
+ ASSERT_EQ(2UL, last_result()->updated_items.size());
+ EXPECT_EQ(kRequestId1,
+ last_result()->updated_items.at(index_id_1).request_id());
EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
- last_result()->updated_items.at(0).request_state());
- EXPECT_EQ(kRequestId2, last_result()->updated_items.at(1).request_id());
+ last_result()->updated_items.at(index_id_1).request_state());
+ EXPECT_EQ(kRequestId2,
+ last_result()->updated_items.at(index_id_2).request_id());
EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
- last_result()->updated_items.at(1).request_state());
+ last_result()->updated_items.at(index_id_2).request_state());
}
TEST_F(ChangeRequestsStateTaskTest, EmptyRequestsList) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+
std::vector<int64_t> request_ids;
ChangeRequestsStateTask task(
&store, request_ids, SavePageRequest::RequestState::PAUSED,
@@ -159,7 +190,9 @@ TEST_F(ChangeRequestsStateTaskTest, EmptyRequestsList) {
TEST_F(ChangeRequestsStateTaskTest, UpdateMissingItem) {
RequestQueueInMemoryStore store;
- SetUpStore(&store);
+ InitializeStore(&store);
+ AddItemsToStore(&store);
+
std::vector<int64_t> request_ids{kRequestId1, kRequestId3};
ChangeRequestsStateTask task(
&store, request_ids, SavePageRequest::RequestState::PAUSED,
@@ -168,7 +201,7 @@ TEST_F(ChangeRequestsStateTaskTest, UpdateMissingItem) {
task.Run();
PumpLoop();
ASSERT_TRUE(last_result());
- EXPECT_EQ(2UL, last_result()->item_statuses.size());
+ ASSERT_EQ(2UL, last_result()->item_statuses.size());
EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
EXPECT_EQ(ItemActionStatus::SUCCESS,
last_result()->item_statuses.at(0).second);
diff --git a/chromium/components/offline_pages/core/background/cleanup_task.cc b/chromium/components/offline_pages/core/background/cleanup_task.cc
new file mode 100644
index 00000000000..84e2a7864d4
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/cleanup_task.cc
@@ -0,0 +1,104 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/cleanup_task.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/offliner_policy_utils.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+CleanupTask::CleanupTask(RequestQueueStore* store,
+ OfflinerPolicy* policy,
+ RequestNotifier* notifier,
+ RequestCoordinatorEventLogger* event_logger)
+ : store_(store),
+ policy_(policy),
+ notifier_(notifier),
+ event_logger_(event_logger),
+ weak_ptr_factory_(this) {}
+
+CleanupTask::~CleanupTask() {}
+
+void CleanupTask::Run() {
+ GetRequests();
+}
+
+void CleanupTask::GetRequests() {
+ // Get all the requests from the queue, we will classify them in the callback.
+ store_->GetRequests(
+ base::Bind(&CleanupTask::Prune, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CleanupTask::Prune(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests) {
+ // If there is nothing to do, return right away.
+ if (requests.empty()) {
+ TaskComplete();
+ return;
+ }
+
+ // Get the expired requests to be removed from the queue.
+ std::vector<int64_t> expired_request_ids;
+ GetExpiredRequestIds(std::move(requests), &expired_request_ids);
+
+ // Continue processing by handling expired requests, if any.
+ if (expired_request_ids.size() == 0) {
+ TaskComplete();
+ return;
+ }
+
+ // TODO(petewil): Add UMA saying why we remove them
+ // TODO(petewil): Round trip the reason for deleting through the RQ
+ store_->RemoveRequests(expired_request_ids,
+ base::Bind(&CleanupTask::OnRequestsExpired,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CleanupTask::OnRequestsExpired(
+ std::unique_ptr<UpdateRequestsResult> result) {
+ RequestNotifier::BackgroundSavePageResult save_page_result(
+ RequestNotifier::BackgroundSavePageResult::EXPIRED);
+ for (const auto& request : result->updated_items) {
+ event_logger_->RecordDroppedSavePageRequest(
+ request.client_id().name_space, save_page_result, request.request_id());
+ notifier_->NotifyCompleted(request, save_page_result);
+ }
+
+ // The task is now done, return control to the task queue.
+ TaskComplete();
+}
+
+void CleanupTask::GetExpiredRequestIds(
+ std::vector<std::unique_ptr<SavePageRequest>> requests,
+ std::vector<int64_t>* expired_request_ids) {
+ for (auto& request : requests) {
+ // Check for requests past their expiration time or with too many tries. If
+ // it is not still valid, push the request and the reason onto the deletion
+ // list.
+ OfflinerPolicyUtils::RequestExpirationStatus status =
+ OfflinerPolicyUtils::CheckRequestExpirationStatus(request.get(),
+ policy_);
+
+ // If we are not working on this request in an offliner, and it is not
+ // valid, put it on a list for removal. We make the exception for current
+ // requests because the request might expire after being chosen and before
+ // we call cleanup, and we shouldn't delete the request while offlining it.
+ if (status != OfflinerPolicyUtils::RequestExpirationStatus::VALID &&
+ request->request_state() != SavePageRequest::RequestState::OFFLINING) {
+ // TODO(petewil): Push both request and reason, will need to change type
+ // of list to pairs.
+ expired_request_ids->push_back(request->request_id());
+ }
+ }
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/cleanup_task.h b/chromium/components/offline_pages/core/background/cleanup_task.h
new file mode 100644
index 00000000000..fbe8e0c4e5b
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/cleanup_task.h
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class OfflinerPolicy;
+class RequestCoordinatorEventLogger;
+class RequestNotifier;
+class RequestQueueStore;
+
+class CleanupTask : public Task {
+ public:
+ CleanupTask(RequestQueueStore* store,
+ OfflinerPolicy* policy,
+ RequestNotifier* notifier,
+ RequestCoordinatorEventLogger* logger);
+ ~CleanupTask() override;
+
+ // TaskQueue::Task implementation, starts the async chain
+ void Run() override;
+
+ private:
+ // Step 1. get results from the store
+ void GetRequests();
+
+ // Step 2. Prune stale requests
+ void Prune(bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+ // Step 3. Send delete notifications for the expired requests.
+ void OnRequestsExpired(std::unique_ptr<UpdateRequestsResult> result);
+
+ // Build a list of IDs whose request has expired.
+ void GetExpiredRequestIds(
+ std::vector<std::unique_ptr<SavePageRequest>> requests,
+ std::vector<int64_t>* expired_request_ids);
+
+ // Member variables, all pointers are not owned here.
+ RequestQueueStore* store_;
+ OfflinerPolicy* policy_;
+ RequestNotifier* notifier_;
+ RequestCoordinatorEventLogger* event_logger_;
+ // Allows us to pass a weak pointer to callbacks.
+ base::WeakPtrFactory<CleanupTask> weak_ptr_factory_;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_H_
diff --git a/chromium/components/offline_pages/core/background/cleanup_task_factory.cc b/chromium/components/offline_pages/core/background/cleanup_task_factory.cc
new file mode 100644
index 00000000000..dea66576932
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/cleanup_task_factory.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/cleanup_task_factory.h"
+
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+
+namespace offline_pages {
+
+// Capture the common parameters that we will need to make a pick task,
+// and use them when making tasks. Create this once each session, and
+// use it to build a picker task when needed.
+CleanupTaskFactory::CleanupTaskFactory(
+ OfflinerPolicy* policy,
+ RequestNotifier* notifier,
+ RequestCoordinatorEventLogger* event_logger)
+ : policy_(policy), notifier_(notifier), event_logger_(event_logger) {
+ DCHECK(policy);
+ DCHECK(notifier);
+ DCHECK(event_logger);
+}
+
+CleanupTaskFactory::~CleanupTaskFactory() {}
+
+std::unique_ptr<CleanupTask> CleanupTaskFactory::CreateCleanupTask(
+ RequestQueueStore* store) {
+ std::unique_ptr<CleanupTask> task(
+ new CleanupTask(store, policy_, notifier_, event_logger_));
+ return task;
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/cleanup_task_factory.h b/chromium/components/offline_pages/core/background/cleanup_task_factory.h
new file mode 100644
index 00000000000..dda4a74c89f
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/cleanup_task_factory.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_FACTORY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_FACTORY_H_
+
+#include <stdint.h>
+
+#include <set>
+
+#include "components/offline_pages/core/background/cleanup_task.h"
+
+namespace offline_pages {
+
+class OfflinerPolicy;
+class RequestCoordinatorEventLogger;
+class RequestNotifier;
+class RequestQueueStore;
+
+class CleanupTaskFactory {
+ public:
+ CleanupTaskFactory(OfflinerPolicy* policy,
+ RequestNotifier* notifier,
+ RequestCoordinatorEventLogger* event_logger);
+
+ ~CleanupTaskFactory();
+
+ std::unique_ptr<CleanupTask> CreateCleanupTask(RequestQueueStore* store);
+
+ private:
+ // Unowned pointer to the Policy
+ OfflinerPolicy* policy_;
+ // Unowned pointer to the notifier
+ RequestNotifier* notifier_;
+ // Unowned pointer to the EventLogger
+ RequestCoordinatorEventLogger* event_logger_;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_FACTORY_H_
diff --git a/chromium/components/offline_pages/core/background/cleanup_task_unittest.cc b/chromium/components/offline_pages/core/background/cleanup_task_unittest.cc
new file mode 100644
index 00000000000..acfb33b1284
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/cleanup_task_unittest.cc
@@ -0,0 +1,279 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/cleanup_task.h"
+
+#include <memory>
+#include <set>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+// Data for request 1.
+const int64_t kRequestId1 = 17;
+const GURL kUrl1("https://google.com");
+const ClientId kClientId1("bookmark", "1234");
+// Data for request 2.
+const int64_t kRequestId2 = 42;
+const GURL kUrl2("http://nytimes.com");
+const ClientId kClientId2("bookmark", "5678");
+const bool kUserRequested = true;
+
+// Default request
+const SavePageRequest kEmptyRequest(0UL,
+ GURL(""),
+ ClientId("", ""),
+ base::Time(),
+ true);
+} // namespace
+
+// TODO: Refactor this stub class into its own file, use in Pick Request Task
+// Test too.
+// Helper class needed by the CleanupTask
+class RequestNotifierStub : public RequestNotifier {
+ public:
+ RequestNotifierStub()
+ : last_expired_request_(kEmptyRequest), total_expired_requests_(0) {}
+
+ void NotifyAdded(const SavePageRequest& request) override {}
+ void NotifyChanged(const SavePageRequest& request) override {}
+
+ void NotifyCompleted(const SavePageRequest& request,
+ BackgroundSavePageResult status) override {
+ last_expired_request_ = request;
+ last_request_expiration_status_ = status;
+ total_expired_requests_++;
+ }
+
+ const SavePageRequest& last_expired_request() {
+ return last_expired_request_;
+ }
+
+ RequestCoordinator::BackgroundSavePageResult
+ last_request_expiration_status() {
+ return last_request_expiration_status_;
+ }
+
+ int32_t total_expired_requests() { return total_expired_requests_; }
+
+ private:
+ BackgroundSavePageResult last_request_expiration_status_;
+ SavePageRequest last_expired_request_;
+ int32_t total_expired_requests_;
+};
+
+class CleanupTaskTest : public testing::Test {
+ public:
+ CleanupTaskTest();
+
+ ~CleanupTaskTest() override;
+
+ void SetUp() override;
+
+ void PumpLoop();
+
+ void AddRequestDone(ItemActionStatus status);
+
+ void GetRequestsCallback(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+ void QueueRequests(const SavePageRequest& request1,
+ const SavePageRequest& request2);
+
+ // Reset the factory and the task using the current policy.
+ void MakeFactoryAndTask();
+
+ RequestNotifierStub* GetNotifier() { return notifier_.get(); }
+
+ CleanupTask* task() { return task_.get(); }
+ RequestQueueStore* store() { return store_.get(); }
+ OfflinerPolicy* policy() { return policy_.get(); }
+ std::vector<std::unique_ptr<SavePageRequest>>& found_requests() {
+ return found_requests_;
+ }
+
+ protected:
+ void InitializeStoreDone(bool success);
+
+ std::unique_ptr<RequestQueueStore> store_;
+ std::unique_ptr<RequestNotifierStub> notifier_;
+ std::unique_ptr<SavePageRequest> last_picked_;
+ std::unique_ptr<OfflinerPolicy> policy_;
+ RequestCoordinatorEventLogger event_logger_;
+ std::unique_ptr<CleanupTaskFactory> factory_;
+ std::unique_ptr<CleanupTask> task_;
+ std::vector<std::unique_ptr<SavePageRequest>> found_requests_;
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+CleanupTaskTest::CleanupTaskTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+CleanupTaskTest::~CleanupTaskTest() {}
+
+void CleanupTaskTest::SetUp() {
+ DeviceConditions conditions;
+ store_.reset(new RequestQueueInMemoryStore());
+ policy_.reset(new OfflinerPolicy());
+ notifier_.reset(new RequestNotifierStub());
+ MakeFactoryAndTask();
+
+ store_->Initialize(base::Bind(&CleanupTaskTest::InitializeStoreDone,
+ base::Unretained(this)));
+ PumpLoop();
+}
+
+void CleanupTaskTest::PumpLoop() {
+ task_runner_->RunUntilIdle();
+}
+
+void CleanupTaskTest::AddRequestDone(ItemActionStatus status) {
+ ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
+void CleanupTaskTest::GetRequestsCallback(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests) {
+ found_requests_ = std::move(requests);
+}
+
+// Test helper to queue the two given requests.
+void CleanupTaskTest::QueueRequests(const SavePageRequest& request1,
+ const SavePageRequest& request2) {
+ DeviceConditions conditions;
+ std::set<int64_t> disabled_requests;
+ // Add test requests on the Queue.
+ store_->AddRequest(request1, base::Bind(&CleanupTaskTest::AddRequestDone,
+ base::Unretained(this)));
+ store_->AddRequest(request2, base::Bind(&CleanupTaskTest::AddRequestDone,
+ base::Unretained(this)));
+
+ // Pump the loop to give the async queue the opportunity to do the adds.
+ PumpLoop();
+}
+
+void CleanupTaskTest::MakeFactoryAndTask() {
+ factory_.reset(
+ new CleanupTaskFactory(policy_.get(), notifier_.get(), &event_logger_));
+ DeviceConditions conditions;
+ task_ = factory_->CreateCleanupTask(store_.get());
+}
+
+void CleanupTaskTest::InitializeStoreDone(bool success) {
+ ASSERT_TRUE(success);
+}
+
+TEST_F(CleanupTaskTest, CleanupExpiredRequest) {
+ base::Time creation_time = base::Time::Now();
+ base::Time expired_time =
+ creation_time - base::TimeDelta::FromSeconds(
+ policy()->GetRequestExpirationTimeInSeconds() + 10);
+ // Request2 will be expired, request1 will be current.
+ SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+ kUserRequested);
+ SavePageRequest request2(kRequestId2, kUrl2, kClientId2, expired_time,
+ kUserRequested);
+ QueueRequests(request1, request2);
+
+ // Initiate cleanup.
+ task()->Run();
+ PumpLoop();
+
+ // See what is left in the queue, should be just the other request.
+ store()->GetRequests(base::Bind(&CleanupTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ EXPECT_EQ(1UL, found_requests().size());
+ EXPECT_EQ(kRequestId1, found_requests().at(0)->request_id());
+}
+
+TEST_F(CleanupTaskTest, CleanupStartCountExceededRequest) {
+ base::Time creation_time = base::Time::Now();
+ // Request2 will have an exceeded start count.
+ SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+ kUserRequested);
+ SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+ kUserRequested);
+ request2.set_started_attempt_count(policy()->GetMaxStartedTries());
+ QueueRequests(request1, request2);
+
+ // Initiate cleanup.
+ task()->Run();
+ PumpLoop();
+
+ // See what is left in the queue, should be just the other request.
+ store()->GetRequests(base::Bind(&CleanupTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ EXPECT_EQ(1UL, found_requests().size());
+ EXPECT_EQ(kRequestId1, found_requests().at(0)->request_id());
+}
+
+TEST_F(CleanupTaskTest, CleanupCompletionCountExceededRequest) {
+ base::Time creation_time = base::Time::Now();
+ // Request2 will have an exceeded completion count.
+ SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+ kUserRequested);
+ SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+ kUserRequested);
+ request2.set_completed_attempt_count(policy()->GetMaxCompletedTries());
+ QueueRequests(request1, request2);
+
+ // Initiate cleanup.
+ task()->Run();
+ PumpLoop();
+
+ // See what is left in the queue, should be just the other request.
+ store()->GetRequests(base::Bind(&CleanupTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ EXPECT_EQ(1UL, found_requests().size());
+ EXPECT_EQ(kRequestId1, found_requests().at(0)->request_id());
+}
+
+TEST_F(CleanupTaskTest, IgnoreRequestInProgress) {
+ base::Time creation_time = base::Time::Now();
+ // Both requests will have an exceeded completion count.
+ // The first request will be marked as started.
+ SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+ kUserRequested);
+ request1.set_completed_attempt_count(policy()->GetMaxCompletedTries());
+ request1.MarkAttemptStarted(creation_time);
+ SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+ kUserRequested);
+ request2.set_completed_attempt_count(policy()->GetMaxCompletedTries());
+ QueueRequests(request1, request2);
+
+ // Initiate cleanup.
+ task()->Run();
+ PumpLoop();
+
+ // See what is left in the queue, request1 should be left in the queue even
+ // though it is expired because it was listed as in-progress while cleaning.
+ // Request2 should have been cleaned out of the queue.
+ store()->GetRequests(base::Bind(&CleanupTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ EXPECT_EQ(1UL, found_requests().size());
+ EXPECT_EQ(kRequestId1, found_requests().at(0)->request_id());
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/connection_notifier.cc b/chromium/components/offline_pages/core/background/connection_notifier.cc
index 82b90a88073..6d2f98dd24e 100644
--- a/chromium/components/offline_pages/background/connection_notifier.cc
+++ b/chromium/components/offline_pages/core/background/connection_notifier.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/connection_notifier.h"
+#include "components/offline_pages/core/background/connection_notifier.h"
namespace offline_pages {
diff --git a/chromium/components/offline_pages/background/connection_notifier.h b/chromium/components/offline_pages/core/background/connection_notifier.h
index 3e3535ab2b0..36730e6908b 100644
--- a/chromium/components/offline_pages/background/connection_notifier.h
+++ b/chromium/components/offline_pages/core/background/connection_notifier.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_CONNECTION_NOTIFIER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_CONNECTION_NOTIFIER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_CONNECTION_NOTIFIER_H_
+#define COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_CONNECTION_NOTIFIER_H_
#include "base/callback.h"
#include "net/base/network_change_notifier.h"
@@ -31,4 +31,4 @@ class ConnectionNotifier
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_CONNECTION_NOTIFIER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_CONNECTION_NOTIFIER_H_
diff --git a/chromium/components/offline_pages/background/device_conditions.h b/chromium/components/offline_pages/core/background/device_conditions.h
index b96306a4d4b..25245a594e5 100644
--- a/chromium/components/offline_pages/background/device_conditions.h
+++ b/chromium/components/offline_pages/core/background/device_conditions.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_DEVICE_CONDITIONS_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_DEVICE_CONDITIONS_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_DEVICE_CONDITIONS_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_DEVICE_CONDITIONS_H_
#include "net/base/network_change_notifier.h"
@@ -21,7 +21,8 @@ class DeviceConditions {
net_connection_type_(net_connection_type) {}
DeviceConditions()
- : power_connected_(true), battery_percentage_(75),
+ : power_connected_(true),
+ battery_percentage_(75),
net_connection_type_(net::NetworkChangeNotifier::CONNECTION_WIFI) {}
// Returns whether power is connected.
@@ -45,4 +46,4 @@ class DeviceConditions {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_DEVICE_CONDITIONS_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_DEVICE_CONDITIONS_H_
diff --git a/chromium/components/offline_pages/core/background/get_requests_task.cc b/chromium/components/offline_pages/core/background/get_requests_task.cc
new file mode 100644
index 00000000000..9444a3dad11
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/get_requests_task.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/get_requests_task.h"
+
+#include <vector>
+
+#include "base/bind.h"
+
+namespace offline_pages {
+
+GetRequestsTask::GetRequestsTask(
+ RequestQueueStore* store,
+ const RequestQueueStore::GetRequestsCallback& callback)
+ : store_(store), callback_(callback), weak_ptr_factory_(this) {}
+
+GetRequestsTask::~GetRequestsTask() {}
+
+void GetRequestsTask::Run() {
+ ReadRequest();
+}
+
+void GetRequestsTask::ReadRequest() {
+ store_->GetRequests(base::Bind(&GetRequestsTask::CompleteWithResult,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void GetRequestsTask::CompleteWithResult(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests) {
+ callback_.Run(success, std::move(requests));
+ TaskComplete();
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/get_requests_task.h b/chromium/components/offline_pages/core/background/get_requests_task.h
new file mode 100644
index 00000000000..00333c0894c
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/get_requests_task.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_GET_REQUESTS_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_GET_REQUESTS_TASK_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class GetRequestsTask : public Task {
+ public:
+ GetRequestsTask(RequestQueueStore* store,
+ const RequestQueueStore::GetRequestsCallback& callback);
+ ~GetRequestsTask() override;
+
+ // Task implementation:
+ void Run() override;
+
+ private:
+ // Step 1: Read the requests from he store.
+ void ReadRequest();
+ // Step 2: Calls the callback with result, completes the task.
+ void CompleteWithResult(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+ // Store from which requests will be read.
+ RequestQueueStore* store_;
+ // Callback used to return the read results.
+ RequestQueueStore::GetRequestsCallback callback_;
+
+ base::WeakPtrFactory<GetRequestsTask> weak_ptr_factory_;
+ DISALLOW_COPY_AND_ASSIGN(GetRequestsTask);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_GET_REQUESTS_TASK_H_
diff --git a/chromium/components/offline_pages/core/background/get_requests_task_unittest.cc b/chromium/components/offline_pages/core/background/get_requests_task_unittest.cc
new file mode 100644
index 00000000000..9ebfa7c9f9c
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/get_requests_task_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/get_requests_task.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+const int64_t kRequestId1 = 42;
+const int64_t kRequestId2 = 44;
+const GURL kUrl1("http://example.com");
+const GURL kUrl2("http://otherexample.com");
+const ClientId kClientId1("download", "1234");
+const ClientId kClientId2("download", "5678");
+} // namespace
+
+class GetRequestsTaskTest : public testing::Test {
+ public:
+ GetRequestsTaskTest();
+ ~GetRequestsTaskTest() override;
+
+ void PumpLoop();
+
+ void InitializeStore(RequestQueueStore* store);
+ void AddItemsToStore(RequestQueueStore* store);
+ void GetRequestsCallback(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+ bool callback_called() const { return callback_called_; }
+
+ bool last_call_successful() const { return success_; }
+
+ const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
+ return requests_;
+ }
+
+ private:
+ void InitializeStoreDone(bool success);
+ void AddRequestDone(ItemActionStatus status);
+
+ bool callback_called_;
+ bool success_;
+ std::vector<std::unique_ptr<SavePageRequest>> requests_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+GetRequestsTaskTest::GetRequestsTaskTest()
+ : callback_called_(false),
+ success_(false),
+ task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+GetRequestsTaskTest::~GetRequestsTaskTest() {}
+
+void GetRequestsTaskTest::PumpLoop() {
+ task_runner_->RunUntilIdle();
+}
+
+void GetRequestsTaskTest::InitializeStore(RequestQueueStore* store) {
+ store->Initialize(base::Bind(&GetRequestsTaskTest::InitializeStoreDone,
+ base::Unretained(this)));
+ PumpLoop();
+}
+
+void GetRequestsTaskTest::AddItemsToStore(RequestQueueStore* store) {
+ base::Time creation_time = base::Time::Now();
+ SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
+ true);
+ store->AddRequest(request_1, base::Bind(&GetRequestsTaskTest::AddRequestDone,
+ base::Unretained(this)));
+ creation_time = base::Time::Now();
+ SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time,
+ true);
+ store->AddRequest(request_2, base::Bind(&GetRequestsTaskTest::AddRequestDone,
+ base::Unretained(this)));
+ PumpLoop();
+}
+
+void GetRequestsTaskTest::GetRequestsCallback(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests) {
+ callback_called_ = true;
+ success_ = success;
+ requests_ = std::move(requests);
+}
+
+void GetRequestsTaskTest::InitializeStoreDone(bool success) {
+ ASSERT_TRUE(success);
+}
+
+void GetRequestsTaskTest::AddRequestDone(ItemActionStatus status) {
+ ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
+TEST_F(GetRequestsTaskTest, GetFromEmptyStore) {
+ RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+ GetRequestsTask task(&store,
+ base::Bind(&GetRequestsTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ task.Run();
+ PumpLoop();
+ EXPECT_TRUE(callback_called());
+ EXPECT_TRUE(last_call_successful());
+ EXPECT_TRUE(last_requests().empty());
+}
+
+TEST_F(GetRequestsTaskTest, GetMultipleRequests) {
+ RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+ AddItemsToStore(&store);
+
+ GetRequestsTask task(&store,
+ base::Bind(&GetRequestsTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ task.Run();
+ PumpLoop();
+ EXPECT_TRUE(callback_called());
+ EXPECT_TRUE(last_call_successful());
+ ASSERT_EQ(2UL, last_requests().size());
+
+ int id_1_index = last_requests().at(0)->request_id() == kRequestId1 ? 0 : 1;
+ int id_2_index = 1 - id_1_index;
+ EXPECT_EQ(kRequestId1, last_requests().at(id_1_index)->request_id());
+ EXPECT_EQ(kRequestId2, last_requests().at(id_2_index)->request_id());
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/initialize_store_task.cc b/chromium/components/offline_pages/core/background/initialize_store_task.cc
new file mode 100644
index 00000000000..8c680755cf4
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/initialize_store_task.cc
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/initialize_store_task.h"
+
+#include "base/bind.h"
+
+namespace offline_pages {
+
+const int kRestartAttemptsMaximum = 3;
+
+InitializeStoreTask::InitializeStoreTask(
+ RequestQueueStore* store,
+ const RequestQueueStore::InitializeCallback& callback)
+ : store_(store),
+ reset_attempts_left_(kRestartAttemptsMaximum),
+ callback_(callback),
+ weak_ptr_factory_(this) {}
+
+InitializeStoreTask::~InitializeStoreTask() {}
+
+void InitializeStoreTask::Run() {
+ InitializeStore();
+}
+
+void InitializeStoreTask::InitializeStore() {
+ store_->Initialize(base::Bind(&InitializeStoreTask::CompleteIfSuccessful,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void InitializeStoreTask::CompleteIfSuccessful(bool success) {
+ if (success) {
+ callback_.Run(true);
+ TaskComplete();
+ return;
+ }
+
+ TryToResetStore();
+}
+
+void InitializeStoreTask::TryToResetStore() {
+ if (reset_attempts_left_ == 0) {
+ callback_.Run(false);
+ TaskComplete();
+ return;
+ }
+
+ reset_attempts_left_--;
+ store_->Reset(base::Bind(&InitializeStoreTask::OnStoreResetDone,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void InitializeStoreTask::OnStoreResetDone(bool success) {
+ if (success)
+ InitializeStore();
+ else
+ TryToResetStore();
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/initialize_store_task.h b/chromium/components/offline_pages/core/background/initialize_store_task.h
new file mode 100644
index 00000000000..0cceee8ae8a
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/initialize_store_task.h
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_INITIALIZE_STORE_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_INITIALIZE_STORE_TASK_H_
+
+#include <stdint.h>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+// Task performing a request queue store initialization and reset if one is
+// necessary. Reset is triggered in case the initialization fails. If reset
+// fails (perhaps not able to delete the folder or raze the database in case of
+// SQLite-backed store) a new attempt can be made. Number of attempts is limited
+// by |reset_attempts_left_|. Successful reset will be followed by another
+// attempt to initialize the database.
+//
+// Task is completed when either the store is correctly initialized or there are
+// not more reset attempts left.
+class InitializeStoreTask : public Task {
+ public:
+ InitializeStoreTask(RequestQueueStore* store,
+ const RequestQueueStore::InitializeCallback& callback);
+ ~InitializeStoreTask() override;
+
+ // TaskQueue::Task implementation.
+ void Run() override;
+
+ private:
+ // Step 1. Initialize store.
+ void InitializeStore();
+ // Step 2a. Completes initialization if successful or tries to reset if there
+ // are more attempts left.
+ void CompleteIfSuccessful(bool success);
+ // Step 2b. Reset store in case initialization fails.
+ void TryToResetStore();
+ // Step 3. If 2b was successful go to step 1, else try 2b again.
+ void OnStoreResetDone(bool success);
+
+ // Store that this task initializes.
+ RequestQueueStore* store_;
+ // Number of attempts left to reset and reinitialize the store.
+ int reset_attempts_left_;
+ // Callback to complete the task.
+ RequestQueueStore::InitializeCallback callback_;
+
+ base::WeakPtrFactory<InitializeStoreTask> weak_ptr_factory_;
+ DISALLOW_COPY_AND_ASSIGN(InitializeStoreTask);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_INITIALIZE_STORE_TASK_H_
diff --git a/chromium/components/offline_pages/core/background/initialize_store_task_unittest.cc b/chromium/components/offline_pages/core/background/initialize_store_task_unittest.cc
new file mode 100644
index 00000000000..c016b73e7c3
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/initialize_store_task_unittest.cc
@@ -0,0 +1,107 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/initialize_store_task.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+using TestScenario = RequestQueueInMemoryStore::TestScenario;
+
+class InitializeStoreTaskTest : public testing::Test {
+ public:
+ InitializeStoreTaskTest();
+ ~InitializeStoreTaskTest() override;
+
+ void PumpLoop();
+ void RunNextStep();
+
+ void InitializeCallback(bool success);
+
+ bool callback_called() const { return callback_called_; }
+
+ bool last_call_successful() const { return success_; }
+
+ private:
+ bool callback_called_;
+ bool success_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+InitializeStoreTaskTest::InitializeStoreTaskTest()
+ : callback_called_(false),
+ success_(false),
+ task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+InitializeStoreTaskTest::~InitializeStoreTaskTest() {}
+
+void InitializeStoreTaskTest::PumpLoop() {
+ task_runner_->RunUntilIdle();
+}
+
+void InitializeStoreTaskTest::RunNextStep() {
+ // Only runs tasks that are already scheduled and available. If running these
+ // tasks post new tasks to the runner, they will not be triggered. This allows
+ // for running InitializeStoreTask one step at a time.
+ task_runner_->RunPendingTasks();
+}
+
+void InitializeStoreTaskTest::InitializeCallback(bool success) {
+ callback_called_ = true;
+ success_ = success;
+}
+
+TEST_F(InitializeStoreTaskTest, SuccessfulInitialization) {
+ RequestQueueInMemoryStore store;
+ InitializeStoreTask task(
+ &store, base::Bind(&InitializeStoreTaskTest::InitializeCallback,
+ base::Unretained(this)));
+ task.Run();
+ PumpLoop();
+ EXPECT_TRUE(callback_called());
+ EXPECT_TRUE(last_call_successful());
+ EXPECT_EQ(StoreState::LOADED, store.state());
+}
+
+TEST_F(InitializeStoreTaskTest, SuccessfulReset) {
+ RequestQueueInMemoryStore store(TestScenario::LOAD_FAILED_RESET_SUCCESS);
+ InitializeStoreTask task(
+ &store, base::Bind(&InitializeStoreTaskTest::InitializeCallback,
+ base::Unretained(this)));
+ task.Run();
+ EXPECT_FALSE(callback_called());
+ EXPECT_EQ(StoreState::FAILED_LOADING, store.state());
+
+ RunNextStep();
+ EXPECT_FALSE(callback_called());
+ EXPECT_EQ(StoreState::NOT_LOADED, store.state());
+
+ PumpLoop();
+ EXPECT_TRUE(callback_called());
+ EXPECT_TRUE(last_call_successful());
+ EXPECT_EQ(StoreState::LOADED, store.state());
+}
+
+TEST_F(InitializeStoreTaskTest, FailedReset) {
+ RequestQueueInMemoryStore store(TestScenario::LOAD_FAILED_RESET_FAILED);
+ InitializeStoreTask task(
+ &store, base::Bind(&InitializeStoreTaskTest::InitializeCallback,
+ base::Unretained(this)));
+ task.Run();
+ PumpLoop();
+ EXPECT_TRUE(callback_called());
+ EXPECT_FALSE(last_call_successful());
+ EXPECT_EQ(StoreState::FAILED_RESET, store.state());
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/mark_attempt_aborted_task.cc b/chromium/components/offline_pages/core/background/mark_attempt_aborted_task.cc
index 150baac4062..007dfa2aa58 100644
--- a/chromium/components/offline_pages/background/mark_attempt_aborted_task.cc
+++ b/chromium/components/offline_pages/core/background/mark_attempt_aborted_task.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/mark_attempt_aborted_task.h"
+#include "components/offline_pages/core/background/mark_attempt_aborted_task.h"
#include "base/bind.h"
diff --git a/chromium/components/offline_pages/background/mark_attempt_aborted_task.h b/chromium/components/offline_pages/core/background/mark_attempt_aborted_task.h
index 11a2c3a90b8..4deb15ff921 100644
--- a/chromium/components/offline_pages/background/mark_attempt_aborted_task.h
+++ b/chromium/components/offline_pages/core/background/mark_attempt_aborted_task.h
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
#include <stdint.h>
-#include "components/offline_pages/background/update_request_task.h"
+#include "components/offline_pages/core/background/update_request_task.h"
#include "components/offline_pages/core/task.h"
namespace offline_pages {
@@ -28,4 +28,4 @@ class MarkAttemptAbortedTask : public UpdateRequestTask {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
diff --git a/chromium/components/offline_pages/background/mark_attempt_aborted_task_unittest.cc b/chromium/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc
index ba6f249b46b..bc15169e305 100644
--- a/chromium/components/offline_pages/background/mark_attempt_aborted_task_unittest.cc
+++ b/chromium/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc
@@ -2,16 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/mark_attempt_aborted_task.h"
+#include "components/offline_pages/core/background/mark_attempt_aborted_task.h"
#include <memory>
#include "base/bind.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/change_requests_state_task.h"
-#include "components/offline_pages/background/mark_attempt_started_task.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/change_requests_state_task.h"
+#include "components/offline_pages/core/background/mark_attempt_started_task.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -29,10 +29,8 @@ class MarkAttemptAbortedTaskTest : public testing::Test {
void PumpLoop();
+ void InitializeStore(RequestQueueStore* store);
void AddItemToStore(RequestQueueStore* store);
-
- void AddRequestDone(ItemActionStatus status);
-
void ChangeRequestsStateCallback(
std::unique_ptr<UpdateRequestsResult> result);
@@ -41,6 +39,9 @@ class MarkAttemptAbortedTaskTest : public testing::Test {
UpdateRequestsResult* last_result() const { return result_.get(); }
private:
+ void InitializeStoreDone(bool success);
+ void AddRequestDone(ItemActionStatus status);
+
std::unique_ptr<UpdateRequestsResult> result_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
@@ -56,6 +57,12 @@ void MarkAttemptAbortedTaskTest::PumpLoop() {
task_runner_->RunUntilIdle();
}
+void MarkAttemptAbortedTaskTest::InitializeStore(RequestQueueStore* store) {
+ store->Initialize(base::Bind(&MarkAttemptAbortedTaskTest::InitializeStoreDone,
+ base::Unretained(this)));
+ PumpLoop();
+}
+
void MarkAttemptAbortedTaskTest::AddItemToStore(RequestQueueStore* store) {
base::Time creation_time = base::Time::Now();
SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
@@ -66,10 +73,6 @@ void MarkAttemptAbortedTaskTest::AddItemToStore(RequestQueueStore* store) {
PumpLoop();
}
-void MarkAttemptAbortedTaskTest::AddRequestDone(ItemActionStatus status) {
- ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
void MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback(
std::unique_ptr<UpdateRequestsResult> result) {
result_ = std::move(result);
@@ -79,8 +82,18 @@ void MarkAttemptAbortedTaskTest::ClearResults() {
result_.reset(nullptr);
}
+void MarkAttemptAbortedTaskTest::InitializeStoreDone(bool success) {
+ ASSERT_TRUE(success);
+}
+
+void MarkAttemptAbortedTaskTest::AddRequestDone(ItemActionStatus status) {
+ ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenStoreEmpty) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+
MarkAttemptAbortedTask task(
&store, kRequestId1,
base::Bind(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
@@ -97,6 +110,7 @@ TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenStoreEmpty) {
TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenExists) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
AddItemToStore(&store);
// First mark attempt started.
@@ -127,7 +141,9 @@ TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenExists) {
TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenItemMissing) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
AddItemToStore(&store);
+
MarkAttemptAbortedTask task(
&store, kRequestId2,
base::Bind(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
@@ -144,6 +160,7 @@ TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenItemMissing) {
TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenPaused) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
AddItemToStore(&store);
// First mark attempt started.
@@ -183,5 +200,4 @@ TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenPaused) {
last_result()->updated_items.at(0).request_state());
}
-
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/mark_attempt_completed_task.cc b/chromium/components/offline_pages/core/background/mark_attempt_completed_task.cc
index d3ec900f488..8f646dbf906 100644
--- a/chromium/components/offline_pages/background/mark_attempt_completed_task.cc
+++ b/chromium/components/offline_pages/core/background/mark_attempt_completed_task.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/mark_attempt_completed_task.h"
+#include "components/offline_pages/core/background/mark_attempt_completed_task.h"
#include <utility>
diff --git a/chromium/components/offline_pages/background/mark_attempt_completed_task.h b/chromium/components/offline_pages/core/background/mark_attempt_completed_task.h
index 7a1d3b3b2f5..f3e3e4c4b8b 100644
--- a/chromium/components/offline_pages/background/mark_attempt_completed_task.h
+++ b/chromium/components/offline_pages/core/background/mark_attempt_completed_task.h
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
#include <stdint.h>
#include <memory>
-#include "components/offline_pages/background/request_queue_results.h"
-#include "components/offline_pages/background/update_request_task.h"
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/background/update_request_task.h"
#include "components/offline_pages/core/task.h"
namespace offline_pages {
@@ -30,4 +30,4 @@ class MarkAttemptCompletedTask : public UpdateRequestTask {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
diff --git a/chromium/components/offline_pages/background/mark_attempt_completed_task_unittest.cc b/chromium/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc
index f33ba854c0d..e47046b012d 100644
--- a/chromium/components/offline_pages/background/mark_attempt_completed_task_unittest.cc
+++ b/chromium/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/mark_attempt_completed_task.h"
+#include "components/offline_pages/core/background/mark_attempt_completed_task.h"
#include <memory>
#include <utility>
@@ -10,7 +10,7 @@
#include "base/bind.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -28,16 +28,17 @@ class MarkAttemptCompletedTaskTest : public testing::Test {
void PumpLoop();
+ void InitializeStore(RequestQueueStore* store);
void AddStartedItemToStore(RequestQueueStore* store);
-
- void AddRequestDone(ItemActionStatus status);
-
void ChangeRequestsStateCallback(
std::unique_ptr<UpdateRequestsResult> result);
UpdateRequestsResult* last_result() const { return result_.get(); }
private:
+ void InitializeStoreDone(bool success);
+ void AddRequestDone(ItemActionStatus status);
+
std::unique_ptr<UpdateRequestsResult> result_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
@@ -53,6 +54,13 @@ void MarkAttemptCompletedTaskTest::PumpLoop() {
task_runner_->RunUntilIdle();
}
+void MarkAttemptCompletedTaskTest::InitializeStore(RequestQueueStore* store) {
+ store->Initialize(
+ base::Bind(&MarkAttemptCompletedTaskTest::InitializeStoreDone,
+ base::Unretained(this)));
+ PumpLoop();
+}
+
void MarkAttemptCompletedTaskTest::AddStartedItemToStore(
RequestQueueStore* store) {
base::Time creation_time = base::Time::Now();
@@ -65,17 +73,22 @@ void MarkAttemptCompletedTaskTest::AddStartedItemToStore(
PumpLoop();
}
-void MarkAttemptCompletedTaskTest::AddRequestDone(ItemActionStatus status) {
- ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
void MarkAttemptCompletedTaskTest::ChangeRequestsStateCallback(
std::unique_ptr<UpdateRequestsResult> result) {
result_ = std::move(result);
}
+void MarkAttemptCompletedTaskTest::InitializeStoreDone(bool success) {
+ ASSERT_TRUE(success);
+}
+
+void MarkAttemptCompletedTaskTest::AddRequestDone(ItemActionStatus status) {
+ ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
TEST_F(MarkAttemptCompletedTaskTest, MarkAttemptCompletedWhenExists) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
AddStartedItemToStore(&store);
MarkAttemptCompletedTask task(
@@ -98,6 +111,7 @@ TEST_F(MarkAttemptCompletedTaskTest, MarkAttemptCompletedWhenExists) {
TEST_F(MarkAttemptCompletedTaskTest, MarkAttemptCompletedWhenItemMissing) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
// Add request 1 to the store.
AddStartedItemToStore(&store);
// Try to mark request 2 (not in the store).
diff --git a/chromium/components/offline_pages/background/mark_attempt_started_task.cc b/chromium/components/offline_pages/core/background/mark_attempt_started_task.cc
index 95776fefad1..429b67a6315 100644
--- a/chromium/components/offline_pages/background/mark_attempt_started_task.cc
+++ b/chromium/components/offline_pages/core/background/mark_attempt_started_task.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/mark_attempt_started_task.h"
+#include "components/offline_pages/core/background/mark_attempt_started_task.h"
#include <utility>
diff --git a/chromium/components/offline_pages/background/mark_attempt_started_task.h b/chromium/components/offline_pages/core/background/mark_attempt_started_task.h
index b0a93c6f23a..93673a949e5 100644
--- a/chromium/components/offline_pages/background/mark_attempt_started_task.h
+++ b/chromium/components/offline_pages/core/background/mark_attempt_started_task.h
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
#include <stdint.h>
-#include "components/offline_pages/background/update_request_task.h"
+#include "components/offline_pages/core/background/update_request_task.h"
#include "components/offline_pages/core/task.h"
namespace offline_pages {
@@ -28,4 +28,4 @@ class MarkAttemptStartedTask : public UpdateRequestTask {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
diff --git a/chromium/components/offline_pages/background/mark_attempt_started_task_unittest.cc b/chromium/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc
index 0b466802aec..17da2ca901f 100644
--- a/chromium/components/offline_pages/background/mark_attempt_started_task_unittest.cc
+++ b/chromium/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/mark_attempt_started_task.h"
+#include "components/offline_pages/core/background/mark_attempt_started_task.h"
#include <memory>
#include "base/bind.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -27,16 +27,17 @@ class MarkAttemptStartedTaskTest : public testing::Test {
void PumpLoop();
+ void InitializeStore(RequestQueueStore* store);
void AddItemToStore(RequestQueueStore* store);
-
- void AddRequestDone(ItemActionStatus status);
-
void ChangeRequestsStateCallback(
std::unique_ptr<UpdateRequestsResult> result);
UpdateRequestsResult* last_result() const { return result_.get(); }
private:
+ void InitializeStoreDone(bool success);
+ void AddRequestDone(ItemActionStatus status);
+
std::unique_ptr<UpdateRequestsResult> result_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
@@ -52,6 +53,12 @@ void MarkAttemptStartedTaskTest::PumpLoop() {
task_runner_->RunUntilIdle();
}
+void MarkAttemptStartedTaskTest::InitializeStore(RequestQueueStore* store) {
+ store->Initialize(base::Bind(&MarkAttemptStartedTaskTest::InitializeStoreDone,
+ base::Unretained(this)));
+ PumpLoop();
+}
+
void MarkAttemptStartedTaskTest::AddItemToStore(RequestQueueStore* store) {
base::Time creation_time = base::Time::Now();
SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
@@ -62,17 +69,23 @@ void MarkAttemptStartedTaskTest::AddItemToStore(RequestQueueStore* store) {
PumpLoop();
}
-void MarkAttemptStartedTaskTest::AddRequestDone(ItemActionStatus status) {
- ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
void MarkAttemptStartedTaskTest::ChangeRequestsStateCallback(
std::unique_ptr<UpdateRequestsResult> result) {
result_ = std::move(result);
}
+void MarkAttemptStartedTaskTest::InitializeStoreDone(bool success) {
+ ASSERT_TRUE(success);
+}
+
+void MarkAttemptStartedTaskTest::AddRequestDone(ItemActionStatus status) {
+ ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenStoreEmpty) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+
MarkAttemptStartedTask task(
&store, kRequestId1,
base::Bind(&MarkAttemptStartedTaskTest::ChangeRequestsStateCallback,
@@ -89,6 +102,7 @@ TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenStoreEmpty) {
TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenExists) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
AddItemToStore(&store);
MarkAttemptStartedTask task(
@@ -117,7 +131,9 @@ TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenExists) {
TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenItemMissing) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
AddItemToStore(&store);
+
MarkAttemptStartedTask task(
&store, kRequestId2,
base::Bind(&MarkAttemptStartedTaskTest::ChangeRequestsStateCallback,
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
new file mode 100644
index 00000000000..aaca69530b1
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/network_quality_provider_stub.cc
@@ -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.
+
+#include "components/offline_pages/core/background/network_quality_provider_stub.h"
+
+namespace offline_pages {
+
+const char kOfflineNQPKey[] = "OfflineNQP";
+
+NetworkQualityProviderStub::NetworkQualityProviderStub()
+ : connection_type_(
+ net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G) {}
+
+NetworkQualityProviderStub::~NetworkQualityProviderStub() {}
+
+// static
+NetworkQualityProviderStub* NetworkQualityProviderStub::GetUserData(
+ base::SupportsUserData* supports_user_data) {
+ return static_cast<NetworkQualityProviderStub*>(
+ supports_user_data->GetUserData(&kOfflineNQPKey));
+}
+
+// static
+void NetworkQualityProviderStub::SetUserData(
+ base::SupportsUserData* supports_user_data,
+ NetworkQualityProviderStub* stub) {
+ DCHECK(supports_user_data);
+ DCHECK(stub);
+ supports_user_data->SetUserData(&kOfflineNQPKey, stub);
+}
+
+void NetworkQualityProviderStub::AddEffectiveConnectionTypeObserver(
+ net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer) {}
+
+void NetworkQualityProviderStub::RemoveEffectiveConnectionTypeObserver(
+ net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer) {}
+
+net::EffectiveConnectionType
+NetworkQualityProviderStub::GetEffectiveConnectionType() const {
+ return connection_type_;
+}
+} // namespace offline_pages
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
new file mode 100644
index 00000000000..1c13a17b3ea
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/network_quality_provider_stub.h
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_NETWORK_QUALITY_PROVIDER_STUB_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_NETWORK_QUALITY_PROVIDER_STUB_H_
+
+#include "base/supports_user_data.h"
+#include "net/nqe/effective_connection_type.h"
+#include "net/nqe/network_quality_estimator.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 {
+ public:
+ NetworkQualityProviderStub();
+ ~NetworkQualityProviderStub() override;
+
+ static NetworkQualityProviderStub* GetUserData(
+ base::SupportsUserData* supports_user_data);
+ static void SetUserData(base::SupportsUserData* supports_user_data,
+ NetworkQualityProviderStub* stub);
+
+ net::EffectiveConnectionType GetEffectiveConnectionType() const override;
+
+ void AddEffectiveConnectionTypeObserver(
+ net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
+ override;
+
+ void RemoveEffectiveConnectionTypeObserver(
+ net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
+ override;
+
+ void SetEffectiveConnectionTypeForTest(net::EffectiveConnectionType type) {
+ connection_type_ = type;
+ }
+
+ private:
+ net::EffectiveConnectionType connection_type_;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_NETWORK_QUALITY_PROVIDER_STUB_H_
diff --git a/chromium/components/offline_pages/background/offliner.h b/chromium/components/offline_pages/core/background/offliner.h
index cafd068634d..a7a5718596c 100644
--- a/chromium/components/offline_pages/background/offliner.h
+++ b/chromium/components/offline_pages/core/background/offliner.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_H_
#include <string>
@@ -30,20 +30,29 @@ class Offliner {
SAVED = 2,
// RequestCoordinator canceled request.
REQUEST_COORDINATOR_CANCELED = 3,
- // Prerendering was canceled.
- PRERENDERING_CANCELED = 4,
- // Prerendering failed to load page.
- PRERENDERING_FAILED = 5,
+ // Loading was canceled.
+ LOADING_CANCELED = 4,
+ // Loader failed to load page.
+ LOADING_FAILED = 5,
// Failed to save loaded page.
SAVE_FAILED = 6,
// Foreground transition canceled request.
FOREGROUND_CANCELED = 7,
// RequestCoordinator canceled request attempt per time limit.
REQUEST_COORDINATOR_TIMED_OUT = 8,
- // The loader did not accept/start the request.
- PRERENDERING_NOT_STARTED = 9,
- // Prerendering failed with hard error so should not retry the request.
- PRERENDERING_FAILED_NO_RETRY = 10,
+ // Deprecated. The RequestCoordinator did not start loading the request.
+ DEPRECATED_LOADING_NOT_STARTED = 9,
+ // Loader failed with hard error so should not retry the request.
+ LOADING_FAILED_NO_RETRY = 10,
+ // Loader failed with some error that suggests we should not continue
+ // processing another request at this time.
+ LOADING_FAILED_NO_NEXT = 11,
+ // The RequestCoordinator tried to start loading the request but the
+ // loading request was not accepted.
+ LOADING_NOT_ACCEPTED = 12,
+ // The RequestCoordinator did not start loading the request because
+ // updating the status in the request queue failed.
+ QUEUE_UPDATE_FAILED = 13,
// NOTE: insert new values above this line and update histogram enum too.
STATUS_COUNT
};
@@ -59,9 +68,8 @@ class Offliner {
// Processes |request| to load and save an offline page.
// Returns whether the request was accepted or not. |callback| is guaranteed
// to be called if the request was accepted and |Cancel()| is not called.
- virtual bool LoadAndSave(
- const SavePageRequest& request,
- const CompletionCallback& callback) = 0;
+ virtual bool LoadAndSave(const SavePageRequest& request,
+ const CompletionCallback& callback) = 0;
// Clears the currently processing request, if any, and skips running its
// CompletionCallback.
@@ -72,4 +80,4 @@ class Offliner {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_H_
diff --git a/chromium/components/offline_pages/background/offliner_policy.h b/chromium/components/offline_pages/core/background/offliner_policy.h
index 7ffd45496a4..3a579ec6615 100644
--- a/chromium/components/offline_pages/background/offliner_policy.h
+++ b/chromium/components/offline_pages/core/background/offliner_policy.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_POLICY_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_POLICY_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_H_
namespace {
// The max number of started tries is to guard against pages that make the
@@ -89,9 +89,7 @@ class OfflinerPolicy {
// completed, but failed.
int GetMaxCompletedTries() const { return max_completed_tries_; }
- bool PowerRequired(bool user_requested) const {
- return (!user_requested);
- }
+ bool PowerRequired(bool user_requested) const { return (!user_requested); }
bool UnmeteredNetworkRequired(bool user_requested) const {
return !(user_requested);
@@ -147,5 +145,4 @@ class OfflinerPolicy {
};
} // namespace offline_pages
-
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_POLICY_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_H_
diff --git a/chromium/components/offline_pages/core/background/offliner_policy_utils.cc b/chromium/components/offline_pages/core/background/offliner_policy_utils.cc
new file mode 100644
index 00000000000..a9383f2423e
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/offliner_policy_utils.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/offliner_policy_utils.h"
+
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+// static function to check request expiration or cleanup status.
+OfflinerPolicyUtils::RequestExpirationStatus
+OfflinerPolicyUtils::CheckRequestExpirationStatus(
+ const SavePageRequest* request,
+ const OfflinerPolicy* policy) {
+ DCHECK(request);
+ DCHECK(policy);
+
+ if (base::Time::Now() - request->creation_time() >=
+ base::TimeDelta::FromSeconds(
+ policy->GetRequestExpirationTimeInSeconds())) {
+ return RequestExpirationStatus::EXPIRED;
+ }
+ if (request->started_attempt_count() >= policy->GetMaxStartedTries())
+ return RequestExpirationStatus::START_COUNT_EXCEEDED;
+
+ if (request->completed_attempt_count() >= policy->GetMaxCompletedTries())
+ return RequestExpirationStatus::COMPLETION_COUNT_EXCEEDED;
+
+ return RequestExpirationStatus::VALID;
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/offliner_policy_utils.h b/chromium/components/offline_pages/core/background/offliner_policy_utils.h
new file mode 100644
index 00000000000..2fc50c6b22b
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/offliner_policy_utils.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_UTILS_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_UTILS_H_
+
+namespace offline_pages {
+
+class OfflinerPolicy;
+class SavePageRequest;
+
+class OfflinerPolicyUtils {
+ public:
+ enum class RequestExpirationStatus {
+ VALID,
+ EXPIRED,
+ START_COUNT_EXCEEDED,
+ COMPLETION_COUNT_EXCEEDED,
+ };
+
+ static RequestExpirationStatus CheckRequestExpirationStatus(
+ const SavePageRequest* request,
+ const OfflinerPolicy* policy);
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_UTILS_H_
diff --git a/chromium/components/offline_pages/core/background/offliner_stub.cc b/chromium/components/offline_pages/core/background/offliner_stub.cc
new file mode 100644
index 00000000000..0455fe08d71
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/offliner_stub.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/offline_pages/core/background/offliner_stub.h"
+
+#include "base/bind.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+OfflinerStub::OfflinerStub()
+ : disable_loading_(false), enable_callback_(false), cancel_called_(false) {}
+
+OfflinerStub::~OfflinerStub() {}
+
+bool OfflinerStub::LoadAndSave(const SavePageRequest& request,
+ const CompletionCallback& callback) {
+ if (disable_loading_)
+ return false;
+
+ callback_ = callback;
+
+ // Post the callback on the run loop.
+ if (enable_callback_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, request, Offliner::RequestStatus::SAVED));
+ }
+ return true;
+}
+
+void OfflinerStub::Cancel() {
+ cancel_called_ = true;
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/offliner_stub.h b/chromium/components/offline_pages/core/background/offliner_stub.h
new file mode 100644
index 00000000000..f7922606364
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/offliner_stub.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_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_STUB_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_STUB_H_
+
+#include "components/offline_pages/core/background/offliner.h"
+
+namespace offline_pages {
+
+// Test class stubbing out the functionality of Offliner.
+// It is only used for test support.
+class OfflinerStub : public Offliner {
+ public:
+ OfflinerStub();
+ ~OfflinerStub() override;
+
+ bool LoadAndSave(const SavePageRequest& request,
+ const CompletionCallback& callback) override;
+
+ void Cancel() override;
+
+ void disable_loading() { disable_loading_ = true; }
+
+ void enable_callback(bool enable) { enable_callback_ = enable; }
+
+ bool cancel_called() { return cancel_called_; }
+
+ private:
+ CompletionCallback callback_;
+ bool disable_loading_;
+ bool enable_callback_;
+ bool cancel_called_;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_STUB_H_
diff --git a/chromium/components/offline_pages/background/pick_request_task.cc b/chromium/components/offline_pages/core/background/pick_request_task.cc
index dd936f54e41..006d08b47af 100644
--- a/chromium/components/offline_pages/background/pick_request_task.cc
+++ b/chromium/components/offline_pages/core/background/pick_request_task.cc
@@ -2,17 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/pick_request_task.h"
+#include "components/offline_pages/core/background/pick_request_task.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/time/time.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/offliner_policy_utils.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
namespace {
template <typename T>
@@ -20,6 +21,9 @@ int signum(T t) {
return (T(0) < t) - (t < T(0));
}
+bool kCleanupNeeded = true;
+bool kNonUserRequestsFound = true;
+
#define CALL_MEMBER_FUNCTION(object, ptrToMember) ((object)->*(ptrToMember))
} // namespace
@@ -27,8 +31,6 @@ namespace offline_pages {
PickRequestTask::PickRequestTask(RequestQueueStore* store,
OfflinerPolicy* policy,
- RequestNotifier* notifier,
- RequestCoordinatorEventLogger* event_logger,
RequestPickedCallback picked_callback,
RequestNotPickedCallback not_picked_callback,
RequestCountCallback request_count_callback,
@@ -36,8 +38,6 @@ PickRequestTask::PickRequestTask(RequestQueueStore* store,
const std::set<int64_t>& disabled_requests)
: store_(store),
policy_(policy),
- notifier_(notifier),
- event_logger_(event_logger),
picked_callback_(picked_callback),
not_picked_callback_(not_picked_callback),
request_count_callback_(request_count_callback),
@@ -49,42 +49,26 @@ PickRequestTask::PickRequestTask(RequestQueueStore* store,
PickRequestTask::~PickRequestTask() {}
void PickRequestTask::Run() {
+ GetRequests();
+}
+
+void PickRequestTask::GetRequests() {
// Get all the requests from the queue, we will classify them in the callback.
- store_->GetRequests(base::Bind(&PickRequestTask::ChooseAndPrune,
- weak_ptr_factory_.GetWeakPtr()));
+ store_->GetRequests(
+ base::Bind(&PickRequestTask::Choose, weak_ptr_factory_.GetWeakPtr()));
}
-void PickRequestTask::ChooseAndPrune(
+void PickRequestTask::Choose(
bool success,
std::vector<std::unique_ptr<SavePageRequest>> requests) {
// If there is nothing to do, return right away.
if (requests.empty()) {
request_count_callback_.Run(requests.size(), 0);
- not_picked_callback_.Run(false);
+ not_picked_callback_.Run(!kNonUserRequestsFound, !kCleanupNeeded);
TaskComplete();
return;
}
- // Get the expired requests to be removed from the queue, and the valid ones
- // from which to pick the next request.
- std::vector<std::unique_ptr<SavePageRequest>> valid_requests;
- std::vector<int64_t> expired_request_ids;
- SplitRequests(std::move(requests), &valid_requests, &expired_request_ids);
-
- // Continue processing by choosing a request.
- ChooseRequestAndCallback(std::move(valid_requests));
-
- // Continue processing by handling expired requests, if any.
- if (expired_request_ids.size() == 0) {
- TaskComplete();
- return;
- }
-
- RemoveStaleRequests(std::move(expired_request_ids));
-}
-
-void PickRequestTask::ChooseRequestAndCallback(
- std::vector<std::unique_ptr<SavePageRequest>> valid_requests) {
// Pick the most deserving request for our conditions.
const SavePageRequest* picked_request = nullptr;
@@ -98,84 +82,57 @@ void PickRequestTask::ChooseRequestAndCallback(
// TODO(petewil): Consider replacing this bool with a better named enum.
bool non_user_requested_tasks_remaining = false;
+ bool cleanup_needed = false;
size_t available_request_count = 0;
// Iterate once through the requests, keeping track of best candidate.
- for (unsigned i = 0; i < valid_requests.size(); ++i) {
+ for (unsigned i = 0; i < requests.size(); ++i) {
+ // If the request is expired or has exceeded the retry count, skip it.
+ if (OfflinerPolicyUtils::CheckRequestExpirationStatus(requests[i].get(),
+ policy_) !=
+ OfflinerPolicyUtils::RequestExpirationStatus::VALID) {
+ cleanup_needed = true;
+ continue;
+ }
+
// If the request is on the disabled list, skip it.
- auto search = disabled_requests_.find(valid_requests[i]->request_id());
+ auto search = disabled_requests_.find(requests[i]->request_id());
if (search != disabled_requests_.end())
continue;
+
// If there are non-user-requested tasks remaining, we need to make sure
// that they are scheduled after we run out of user requested tasks. Here we
// detect if any exist. If we don't find any user-requested tasks, we will
// inform the "not_picked_callback_" that it needs to schedule a task for
// non-user-requested items, which have different network and power needs.
- if (!valid_requests[i]->user_requested())
+ if (!requests[i]->user_requested())
non_user_requested_tasks_remaining = true;
- if (valid_requests[i]->request_state() ==
+ if (requests[i]->request_state() ==
SavePageRequest::RequestState::AVAILABLE) {
available_request_count++;
}
- if (!RequestConditionsSatisfied(valid_requests[i].get()))
+ if (!RequestConditionsSatisfied(requests[i].get()))
continue;
- if (IsNewRequestBetter(picked_request, valid_requests[i].get(), comparator))
- picked_request = valid_requests[i].get();
+ if (IsNewRequestBetter(picked_request, requests[i].get(), comparator))
+ picked_request = requests[i].get();
}
// Report the request queue counts.
- request_count_callback_.Run(valid_requests.size(), available_request_count);
+ request_count_callback_.Run(requests.size(), available_request_count);
// If we have a best request to try next, get the request coodinator to
// start it. Otherwise return that we have no candidates.
- if (picked_request != nullptr)
- picked_callback_.Run(*picked_request);
- else
- not_picked_callback_.Run(non_user_requested_tasks_remaining);
-}
-
-// Continue the async part of the processing by deleting the expired requests.
-// TODO(petewil): Does that really need to be done on the task queue? Hard to
-// see how we need to wait for it before starting the next task. OTOH, we'd hate
-// to do a second slow DB operation to get entries a second time, and waiting
-// until this is done will make sure other gets don't see these old entries.
-// Consider moving this to a fresh task type to clean the queue.
-void PickRequestTask::RemoveStaleRequests(
- std::vector<int64_t> stale_request_ids) {
- store_->RemoveRequests(stale_request_ids,
- base::Bind(&PickRequestTask::OnRequestsExpired,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void PickRequestTask::OnRequestsExpired(
- std::unique_ptr<UpdateRequestsResult> result) {
- RequestNotifier::BackgroundSavePageResult save_page_result(
- RequestNotifier::BackgroundSavePageResult::EXPIRED);
- for (const auto& request : result->updated_items) {
- event_logger_->RecordDroppedSavePageRequest(
- request.client_id().name_space, save_page_result, request.request_id());
- notifier_->NotifyCompleted(request, save_page_result);
+ if (picked_request != nullptr) {
+ picked_callback_.Run(*picked_request, cleanup_needed);
+ } else {
+ not_picked_callback_.Run(non_user_requested_tasks_remaining,
+ cleanup_needed);
}
- // The task is now done, return control to the task queue.
TaskComplete();
}
-void PickRequestTask::SplitRequests(
- std::vector<std::unique_ptr<SavePageRequest>> requests,
- std::vector<std::unique_ptr<SavePageRequest>>* valid_requests,
- std::vector<int64_t>* expired_request_ids) {
- for (auto& request : requests) {
- if (base::Time::Now() - request->creation_time() >=
- base::TimeDelta::FromSeconds(kRequestExpirationTimeInSeconds)) {
- expired_request_ids->push_back(request->request_id());
- } else {
- valid_requests->push_back(std::move(request));
- }
- }
-}
-
// Filter out requests that don't meet the current conditions. For instance, if
// this is a predictive request, and we are not on WiFi, it should be ignored
// this round.
@@ -200,26 +157,10 @@ bool PickRequestTask::RequestConditionsSatisfied(
return false;
}
- // If we have already started this page the max number of times, it is not
- // eligible to try again.
- if (request->started_attempt_count() >= policy_->GetMaxStartedTries())
- return false;
-
- // If we have already completed trying this page the max number of times, it
- // is not eligible to try again.
- if (request->completed_attempt_count() >= policy_->GetMaxCompletedTries())
- return false;
-
// If the request is paused, do not consider it.
if (request->request_state() == SavePageRequest::RequestState::PAUSED)
return false;
- // If the request is expired, do not consider it.
- base::TimeDelta requestAge = base::Time::Now() - request->creation_time();
- if (requestAge > base::TimeDelta::FromSeconds(
- policy_->GetRequestExpirationTimeInSeconds()))
- return false;
-
// If this request is not active yet, return false.
// TODO(petewil): If the only reason we return nothing to do is that we have
// inactive requests, we still want to try again later after their activation
diff --git a/chromium/components/offline_pages/background/pick_request_task.h b/chromium/components/offline_pages/core/background/pick_request_task.h
index 56646f504b4..ef046124b1e 100644
--- a/chromium/components/offline_pages/background/pick_request_task.h
+++ b/chromium/components/offline_pages/core/background/pick_request_task.h
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_PICK_REQUEST_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_PICK_REQUEST_TASK_H_
#include <set>
#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_results.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/background/save_page_request.h"
#include "components/offline_pages/core/task.h"
namespace offline_pages {
@@ -17,8 +17,6 @@ namespace offline_pages {
class DeviceConditions;
class OfflinerPolicy;
class PickRequestTask;
-class RequestCoordinatorEventLogger;
-class RequestNotifier;
class RequestQueueStore;
typedef bool (PickRequestTask::*RequestCompareFunction)(
@@ -28,19 +26,19 @@ typedef bool (PickRequestTask::*RequestCompareFunction)(
class PickRequestTask : public Task {
public:
// Callback to report when a request was available.
- typedef base::Callback<void(const SavePageRequest& request)>
+ typedef base::Callback<void(const SavePageRequest& request,
+ bool cleanup_needed)>
RequestPickedCallback;
// Callback to report when no request was available.
- typedef base::Callback<void(bool)> RequestNotPickedCallback;
+ typedef base::Callback<void(bool non_user_requests, bool cleanup_needed)>
+ RequestNotPickedCallback;
// Callback to report available total and available queued request counts.
typedef base::Callback<void(size_t, size_t)> RequestCountCallback;
PickRequestTask(RequestQueueStore* store,
OfflinerPolicy* policy,
- RequestNotifier* notifier,
- RequestCoordinatorEventLogger* event_logger,
RequestPickedCallback picked_callback,
RequestNotPickedCallback not_picked_callback,
RequestCountCallback request_count_callback,
@@ -53,29 +51,15 @@ class PickRequestTask : public Task {
void Run() override;
private:
- // Step 1, handle getting the results from the store.
- void ChooseAndPrune(bool request,
- std::vector<std::unique_ptr<SavePageRequest>> requests);
+ // Step 1. get the requests
+ void GetRequests();
- // Step 2a. Handle choosing an entry, and calling the right picked callback
- // and the request count callback.
- void ChooseRequestAndCallback(
- std::vector<std::unique_ptr<SavePageRequest>> valid_requests);
-
- // Step 2b. Handle deleting stale entries and notifying observers.
- void RemoveStaleRequests(std::vector<int64_t> stale_request_ids);
-
- // Step 3. Send delete notifications for the expired requests.
- void OnRequestsExpired(std::unique_ptr<UpdateRequestsResult> result);
+ // Step 2. pick a request that we like best from available requests.
+ void Choose(bool get_succeeded,
+ std::vector<std::unique_ptr<SavePageRequest>> requests);
// Helper functions.
- // Split requests into valid and expired categories.
- void SplitRequests(
- std::vector<std::unique_ptr<SavePageRequest>> requests,
- std::vector<std::unique_ptr<SavePageRequest>>* valid_requests,
- std::vector<int64_t>* expired_request_ids);
-
// Determine if this request has device conditions appropriate for running it.
bool RequestConditionsSatisfied(const SavePageRequest* request);
@@ -105,8 +89,6 @@ class PickRequestTask : public Task {
// Member variables, all pointers are not owned here.
RequestQueueStore* store_;
OfflinerPolicy* policy_;
- RequestNotifier* notifier_;
- RequestCoordinatorEventLogger* event_logger_;
RequestPickedCallback picked_callback_;
RequestNotPickedCallback not_picked_callback_;
RequestCountCallback request_count_callback_;
@@ -118,4 +100,4 @@ class PickRequestTask : public Task {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_PICK_REQUEST_TASK_H_
diff --git a/chromium/components/offline_pages/background/pick_request_task_unittest.cc b/chromium/components/offline_pages/core/background/pick_request_task_unittest.cc
index de06835940a..f1e249b1ab6 100644
--- a/chromium/components/offline_pages/background/pick_request_task_unittest.cc
+++ b/chromium/components/offline_pages/core/background/pick_request_task_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/pick_request_task.h"
+#include "components/offline_pages/core/background/pick_request_task.h"
#include <memory>
#include <set>
@@ -10,14 +10,15 @@
#include "base/bind.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -95,9 +96,10 @@ class PickRequestTaskTest : public testing::Test {
void AddRequestDone(ItemActionStatus status);
- void RequestPicked(const SavePageRequest& request);
+ void RequestPicked(const SavePageRequest& request, bool cleanup_needed);
- void RequestNotPicked(const bool non_user_requested_tasks_remaining);
+ void RequestNotPicked(const bool non_user_requested_tasks_remaining,
+ bool cleanup_needed);
void RequestCountCallback(size_t total_count, size_t available_count);
@@ -105,7 +107,7 @@ class PickRequestTaskTest : public testing::Test {
const SavePageRequest& request2);
// Reset the factory and the task using the current policy.
- void MakeFactoryAndTask();
+ void MakePickRequestTask();
RequestNotifierStub* GetNotifier() { return notifier_.get(); }
@@ -114,15 +116,17 @@ class PickRequestTaskTest : public testing::Test {
void TaskCompletionCallback(Task* completed_task);
protected:
+ void InitializeStoreDone(bool success);
+
std::unique_ptr<RequestQueueStore> store_;
std::unique_ptr<RequestNotifierStub> notifier_;
std::unique_ptr<SavePageRequest> last_picked_;
std::unique_ptr<OfflinerPolicy> policy_;
RequestCoordinatorEventLogger event_logger_;
std::set<int64_t> disabled_requests_;
- std::unique_ptr<PickRequestTaskFactory> factory_;
std::unique_ptr<PickRequestTask> task_;
bool request_queue_not_picked_called_;
+ bool cleanup_needed_;
size_t total_request_count_;
size_t available_request_count_;
bool task_complete_called_;
@@ -143,12 +147,17 @@ void PickRequestTaskTest::SetUp() {
store_.reset(new RequestQueueInMemoryStore());
policy_.reset(new OfflinerPolicy());
notifier_.reset(new RequestNotifierStub());
- MakeFactoryAndTask();
+ MakePickRequestTask();
request_queue_not_picked_called_ = false;
total_request_count_ = 9999;
available_request_count_ = 9999;
task_complete_called_ = false;
last_picked_.reset();
+ cleanup_needed_ = false;
+
+ store_->Initialize(base::Bind(&PickRequestTaskTest::InitializeStoreDone,
+ base::Unretained(this)));
+ PumpLoop();
}
void PickRequestTaskTest::PumpLoop() {
@@ -161,12 +170,15 @@ void PickRequestTaskTest::TaskCompletionCallback(Task* completed_task) {
void PickRequestTaskTest::AddRequestDone(ItemActionStatus status) {}
-void PickRequestTaskTest::RequestPicked(const SavePageRequest& request) {
+void PickRequestTaskTest::RequestPicked(const SavePageRequest& request,
+ const bool cleanup_needed) {
last_picked_.reset(new SavePageRequest(request));
+ cleanup_needed_ = cleanup_needed;
}
void PickRequestTaskTest::RequestNotPicked(
- const bool non_user_requested_tasks_remaining) {
+ const bool non_user_requested_tasks_remaining,
+ const bool cleanup_needed) {
request_queue_not_picked_called_ = true;
}
@@ -191,24 +203,26 @@ void PickRequestTaskTest::QueueRequests(const SavePageRequest& request1,
PumpLoop();
}
-void PickRequestTaskTest::MakeFactoryAndTask() {
- factory_.reset(new PickRequestTaskFactory(policy_.get(), notifier_.get(),
- &event_logger_));
+void PickRequestTaskTest::MakePickRequestTask() {
DeviceConditions conditions;
- task_ = factory_->CreatePickerTask(
- store_.get(),
+ task_.reset(new PickRequestTask(
+ store_.get(), policy_.get(),
base::Bind(&PickRequestTaskTest::RequestPicked, base::Unretained(this)),
base::Bind(&PickRequestTaskTest::RequestNotPicked,
base::Unretained(this)),
base::Bind(&PickRequestTaskTest::RequestCountCallback,
base::Unretained(this)),
- conditions, disabled_requests_);
+ conditions, disabled_requests_));
task_->SetTaskCompletionCallbackForTesting(
task_runner_.get(),
base::Bind(&PickRequestTaskTest::TaskCompletionCallback,
base::Unretained(this)));
}
+void PickRequestTaskTest::InitializeStoreDone(bool success) {
+ ASSERT_TRUE(success);
+}
+
TEST_F(PickRequestTaskTest, PickFromEmptyQueue) {
task()->Run();
PumpLoop();
@@ -219,8 +233,8 @@ TEST_F(PickRequestTaskTest, PickFromEmptyQueue) {
PumpLoop();
EXPECT_TRUE(request_queue_not_picked_called_);
- EXPECT_EQ((size_t) 0, total_request_count_);
- EXPECT_EQ((size_t) 0, available_request_count_);
+ EXPECT_EQ(0UL, total_request_count_);
+ EXPECT_EQ(0UL, available_request_count_);
EXPECT_TRUE(task_complete_called_);
}
@@ -229,7 +243,7 @@ TEST_F(PickRequestTaskTest, ChooseRequestWithHigherRetryCount) {
policy_.reset(new OfflinerPolicy(
kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
- MakeFactoryAndTask();
+ MakePickRequestTask();
base::Time creation_time = base::Time::Now();
SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
@@ -245,8 +259,8 @@ TEST_F(PickRequestTaskTest, ChooseRequestWithHigherRetryCount) {
EXPECT_EQ(kRequestId2, last_picked_->request_id());
EXPECT_FALSE(request_queue_not_picked_called_);
- EXPECT_EQ((size_t) 2, total_request_count_);
- EXPECT_EQ((size_t) 2, available_request_count_);
+ EXPECT_EQ(2UL, total_request_count_);
+ EXPECT_EQ(2UL, available_request_count_);
EXPECT_TRUE(task_complete_called_);
}
@@ -274,7 +288,7 @@ TEST_F(PickRequestTaskTest, ChooseEarlierRequest) {
policy_.reset(new OfflinerPolicy(
kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds));
- MakeFactoryAndTask();
+ MakePickRequestTask();
base::Time creation_time1 =
base::Time::Now() - base::TimeDelta::FromSeconds(10);
@@ -300,7 +314,7 @@ TEST_F(PickRequestTaskTest, ChooseSameTimeRequestWithHigherRetryCount) {
policy_.reset(new OfflinerPolicy(
kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
- MakeFactoryAndTask();
+ MakePickRequestTask();
base::Time creation_time = base::Time::Now();
SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
@@ -324,7 +338,7 @@ TEST_F(PickRequestTaskTest, ChooseRequestWithLowerRetryCount) {
policy_.reset(new OfflinerPolicy(
!kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
- MakeFactoryAndTask();
+ MakePickRequestTask();
base::Time creation_time = base::Time::Now();
SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
@@ -348,7 +362,7 @@ TEST_F(PickRequestTaskTest, ChooseLaterRequest) {
policy_.reset(new OfflinerPolicy(
kPreferUntried, !kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds));
- MakeFactoryAndTask();
+ MakePickRequestTask();
base::Time creation_time1 =
base::Time::Now() - base::TimeDelta::FromSeconds(10);
@@ -385,13 +399,10 @@ TEST_F(PickRequestTaskTest, ChooseNonExpiredRequest) {
EXPECT_EQ(kRequestId1, last_picked_->request_id());
EXPECT_FALSE(request_queue_not_picked_called_);
- EXPECT_EQ(kRequestId2, GetNotifier()->last_expired_request().request_id());
- EXPECT_EQ(RequestNotifier::BackgroundSavePageResult::EXPIRED,
- GetNotifier()->last_request_expiration_status());
- EXPECT_EQ(1, GetNotifier()->total_expired_requests());
- EXPECT_EQ((size_t) 1, total_request_count_);
- EXPECT_EQ((size_t) 1, available_request_count_);
+ EXPECT_EQ(2UL, total_request_count_);
+ EXPECT_EQ(1UL, available_request_count_);
EXPECT_TRUE(task_complete_called_);
+ EXPECT_TRUE(cleanup_needed_);
}
TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededStartLimit) {
@@ -414,11 +425,10 @@ TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededStartLimit) {
EXPECT_EQ(kRequestId2, last_picked_->request_id());
EXPECT_FALSE(request_queue_not_picked_called_);
- // TODO(dougarnett): Counts should be 1 here once requests exceeding start
- // count get cleaned up from the queue.
- EXPECT_EQ((size_t) 2, total_request_count_);
- EXPECT_EQ((size_t) 2, available_request_count_);
+ EXPECT_EQ(2UL, total_request_count_);
+ EXPECT_EQ(1UL, available_request_count_);
EXPECT_TRUE(task_complete_called_);
+ EXPECT_TRUE(cleanup_needed_);
}
TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededCompletionLimit) {
@@ -442,6 +452,7 @@ TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededCompletionLimit) {
EXPECT_EQ(kRequestId2, last_picked_->request_id());
EXPECT_FALSE(request_queue_not_picked_called_);
EXPECT_TRUE(task_complete_called_);
+ EXPECT_TRUE(cleanup_needed_);
}
TEST_F(PickRequestTaskTest, ChooseRequestThatIsNotDisabled) {
@@ -452,7 +463,7 @@ TEST_F(PickRequestTaskTest, ChooseRequestThatIsNotDisabled) {
// put request 2 on disabled list, ensure request1 picked instead,
// even though policy would prefer 2.
disabled_requests_.insert(kRequestId2);
- MakeFactoryAndTask();
+ MakePickRequestTask();
base::Time creation_time = base::Time::Now();
SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
@@ -474,8 +485,8 @@ TEST_F(PickRequestTaskTest, ChooseRequestThatIsNotDisabled) {
EXPECT_EQ(kRequestId1, last_picked_->request_id());
EXPECT_FALSE(request_queue_not_picked_called_);
- EXPECT_EQ((size_t) 2, total_request_count_);
- EXPECT_EQ((size_t) 1, available_request_count_);
+ EXPECT_EQ(2UL, total_request_count_);
+ EXPECT_EQ(1UL, available_request_count_);
EXPECT_TRUE(task_complete_called_);
}
diff --git a/chromium/components/offline_pages/core/background/reconcile_task.cc b/chromium/components/offline_pages/core/background/reconcile_task.cc
new file mode 100644
index 00000000000..9b8427530b5
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/reconcile_task.cc
@@ -0,0 +1,75 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/reconcile_task.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+ReconcileTask::ReconcileTask(RequestQueueStore* store,
+ const RequestQueueStore::UpdateCallback& callback)
+ : store_(store), callback_(callback), weak_ptr_factory_(this) {}
+
+ReconcileTask::~ReconcileTask() {}
+
+void ReconcileTask::Run() {
+ GetRequests();
+}
+
+void ReconcileTask::GetRequests() {
+ // Get all the requests from the queue, we will reconcile them in the
+ // callback.
+ store_->GetRequests(
+ base::Bind(&ReconcileTask::Reconcile, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ReconcileTask::Reconcile(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests) {
+ // If there is nothing to do, return right away, no need to call the callback,
+ // since the state of the notifications did not change.
+ if (requests.empty()) {
+ TaskComplete();
+ return;
+ }
+
+ // Check for tasks in the OFFLINING state, and change the state back to
+ // AVAILABLE.
+ std::vector<SavePageRequest> items_to_update;
+ for (auto& request : requests) {
+ if (request->request_state() == SavePageRequest::RequestState::OFFLINING) {
+ request->set_request_state(SavePageRequest::RequestState::AVAILABLE);
+ items_to_update.push_back(*request.get());
+ // TODO(petewil): Consider adding UMA to see how often chrome gets killed
+ // while processing a request.
+ }
+ }
+
+ // If there is no work (most common case), just return, no need for a
+ // callback.
+ if (items_to_update.empty()) {
+ TaskComplete();
+ return;
+ }
+
+ store_->UpdateRequests(items_to_update,
+ base::Bind(&ReconcileTask::UpdateCompleted,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ReconcileTask::UpdateCompleted(
+ std::unique_ptr<UpdateRequestsResult> update_result) {
+ // Send a notification to the UI that these items have updated.
+ callback_.Run(std::move(update_result));
+ TaskComplete();
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/reconcile_task.h b/chromium/components/offline_pages/core/background/reconcile_task.h
new file mode 100644
index 00000000000..f03b31fa3be
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/reconcile_task.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.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_RECONCILE_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_RECONCILE_TASK_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+// The reconcile task should be run at request coordinator startup to find any
+// tasks that were offlining when chrome died, and change the state back to
+// available.
+class ReconcileTask : public Task {
+ public:
+ ReconcileTask(RequestQueueStore* store,
+ const RequestQueueStore::UpdateCallback& callback);
+ ~ReconcileTask() override;
+
+ // TaskQueue::Task implementation:
+ // Starts the async chain.
+ void Run() override;
+
+ private:
+ // Step 1. Get results from the store.
+ void GetRequests();
+
+ // Step 2. Flip OFFLINING requests to AVAILABLE and put back in queue.
+ void Reconcile(bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+ // Step 3. Processes update result.
+ void UpdateCompleted(std::unique_ptr<UpdateRequestsResult> update_result);
+
+ // Member variables, all pointers are not owned here.
+ RequestQueueStore* store_;
+ // Callback to complete the task.
+ RequestQueueStore::UpdateCallback callback_;
+ // Allows us to pass a weak pointer to callbacks.
+ base::WeakPtrFactory<ReconcileTask> weak_ptr_factory_;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_RECONCILE_TASK_H_
diff --git a/chromium/components/offline_pages/core/background/reconcile_task_unittest.cc b/chromium/components/offline_pages/core/background/reconcile_task_unittest.cc
new file mode 100644
index 00000000000..e587723a0e4
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/reconcile_task_unittest.cc
@@ -0,0 +1,219 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/reconcile_task.h"
+
+#include <memory>
+#include <set>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+// Data for request 1.
+const int64_t kRequestId1 = 17;
+const GURL kUrl1("https://google.com");
+const ClientId kClientId1("bookmark", "1234");
+// Data for request 2.
+const int64_t kRequestId2 = 42;
+const GURL kUrl2("http://nytimes.com");
+const ClientId kClientId2("bookmark", "5678");
+const bool kUserRequested = true;
+
+// Default request
+const SavePageRequest kEmptyRequest(0UL,
+ GURL(""),
+ ClientId("", ""),
+ base::Time(),
+ true);
+} // namespace
+
+class ReconcileTaskTest : public testing::Test {
+ public:
+ ReconcileTaskTest();
+
+ ~ReconcileTaskTest() override;
+
+ void SetUp() override;
+
+ void PumpLoop();
+
+ void AddRequestDone(ItemActionStatus status);
+
+ void GetRequestsCallback(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+ void ReconcileCallback(std::unique_ptr<UpdateRequestsResult> result);
+
+ void QueueRequests(const SavePageRequest& request1,
+ const SavePageRequest& request2);
+
+ // Reset the factory and the task using the current policy.
+ void MakeTask();
+
+ ReconcileTask* task() { return task_.get(); }
+ RequestQueueStore* store() { return store_.get(); }
+ std::vector<std::unique_ptr<SavePageRequest>>& found_requests() {
+ return found_requests_;
+ }
+
+ protected:
+ void InitializeStoreDone(bool success);
+
+ std::unique_ptr<RequestQueueStore> store_;
+ std::unique_ptr<ReconcileTask> task_;
+ std::vector<std::unique_ptr<SavePageRequest>> found_requests_;
+ bool reconcile_called_;
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+ReconcileTaskTest::ReconcileTaskTest()
+ : reconcile_called_(false),
+ task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+ReconcileTaskTest::~ReconcileTaskTest() {}
+
+void ReconcileTaskTest::SetUp() {
+ DeviceConditions conditions;
+ store_.reset(new RequestQueueInMemoryStore());
+ MakeTask();
+
+ store_->Initialize(base::Bind(&ReconcileTaskTest::InitializeStoreDone,
+ base::Unretained(this)));
+ PumpLoop();
+}
+
+void ReconcileTaskTest::PumpLoop() {
+ task_runner_->RunUntilIdle();
+}
+
+void ReconcileTaskTest::AddRequestDone(ItemActionStatus status) {
+ ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
+void ReconcileTaskTest::GetRequestsCallback(
+ bool success,
+ std::vector<std::unique_ptr<SavePageRequest>> requests) {
+ found_requests_ = std::move(requests);
+}
+
+void ReconcileTaskTest::ReconcileCallback(
+ std::unique_ptr<UpdateRequestsResult> result) {
+ reconcile_called_ = true;
+ // Make sure the item in the callback is now AVAILABLE.
+ EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
+ result->updated_items.at(0).request_state());
+}
+
+// Test helper to queue the two given requests.
+void ReconcileTaskTest::QueueRequests(const SavePageRequest& request1,
+ const SavePageRequest& request2) {
+ DeviceConditions conditions;
+ std::set<int64_t> disabled_requests;
+ // Add test requests on the Queue.
+ store_->AddRequest(request1, base::Bind(&ReconcileTaskTest::AddRequestDone,
+ base::Unretained(this)));
+ store_->AddRequest(request2, base::Bind(&ReconcileTaskTest::AddRequestDone,
+ base::Unretained(this)));
+
+ // Pump the loop to give the async queue the opportunity to do the adds.
+ PumpLoop();
+}
+
+void ReconcileTaskTest::MakeTask() {
+ task_.reset(new ReconcileTask(
+ store_.get(), base::Bind(&ReconcileTaskTest::ReconcileCallback,
+ base::Unretained(this))));
+}
+
+void ReconcileTaskTest::InitializeStoreDone(bool success) {
+ ASSERT_TRUE(success);
+}
+
+TEST_F(ReconcileTaskTest, Reconcile) {
+ base::Time creation_time = base::Time::Now();
+ // Request2 will be expired, request1 will be current.
+ SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+ kUserRequested);
+ request1.set_request_state(SavePageRequest::RequestState::PAUSED);
+ SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+ kUserRequested);
+ request2.set_request_state(SavePageRequest::RequestState::OFFLINING);
+ QueueRequests(request1, request2);
+
+ // Initiate cleanup.
+ task()->Run();
+ PumpLoop();
+
+ // See what is left in the queue, should be just the other request.
+ store()->GetRequests(base::Bind(&ReconcileTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ EXPECT_EQ(2UL, found_requests().size());
+
+ // in case requests come back in a different order, check which is where.
+ int request1_index = 0;
+ int request2_index = 1;
+ if (found_requests().at(0)->request_id() != kRequestId1) {
+ request1_index = 1;
+ request2_index = 0;
+ }
+ EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
+ found_requests().at(request1_index)->request_state());
+ EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
+ found_requests().at(request2_index)->request_state());
+ EXPECT_TRUE(reconcile_called_);
+}
+
+TEST_F(ReconcileTaskTest, NothingToReconcile) {
+ base::Time creation_time = base::Time::Now();
+ // Request2 will be expired, request1 will be current.
+ SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+ kUserRequested);
+ request1.set_request_state(SavePageRequest::RequestState::PAUSED);
+ SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+ kUserRequested);
+ request2.set_request_state(SavePageRequest::RequestState::AVAILABLE);
+ QueueRequests(request1, request2);
+
+ // Initiate cleanup.
+ task()->Run();
+ PumpLoop();
+
+ // See what is left in the queue, should be just the other request.
+ store()->GetRequests(base::Bind(&ReconcileTaskTest::GetRequestsCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ EXPECT_EQ(2UL, found_requests().size());
+
+ // in case requests come back in a different order, check which is where.
+ int request1_index = 0;
+ int request2_index = 1;
+ if (found_requests().at(0)->request_id() != kRequestId1) {
+ request1_index = 1;
+ request2_index = 0;
+ }
+ // Requests should still be in their starting states.
+ EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
+ found_requests().at(request1_index)->request_state());
+ EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
+ found_requests().at(request2_index)->request_state());
+ // In this case, we do not expect the reconcile callback to be called.
+ EXPECT_FALSE(reconcile_called_);
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/remove_requests_task.cc b/chromium/components/offline_pages/core/background/remove_requests_task.cc
index 15d45fe6acf..f958e670814 100644
--- a/chromium/components/offline_pages/background/remove_requests_task.cc
+++ b/chromium/components/offline_pages/core/background/remove_requests_task.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/remove_requests_task.h"
+#include "components/offline_pages/core/background/remove_requests_task.h"
#include "base/bind.h"
diff --git a/chromium/components/offline_pages/background/remove_requests_task.h b/chromium/components/offline_pages/core/background/remove_requests_task.h
index a110134e7ea..6372d3d1822 100644
--- a/chromium/components/offline_pages/background/remove_requests_task.h
+++ b/chromium/components/offline_pages/core/background/remove_requests_task.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REMOVE_REQUESTS_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REMOVE_REQUESTS_TASK_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REMOVE_REQUESTS_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REMOVE_REQUESTS_TASK_H_
#include <stdint.h>
@@ -11,8 +11,8 @@
#include <vector>
#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
#include "components/offline_pages/core/task.h"
namespace offline_pages {
@@ -50,4 +50,4 @@ class RemoveRequestsTask : public Task {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REMOVE_REQUESTS_TASK_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REMOVE_REQUESTS_TASK_H_
diff --git a/chromium/components/offline_pages/background/remove_requests_task_unittest.cc b/chromium/components/offline_pages/core/background/remove_requests_task_unittest.cc
index f83ed8ac89a..5846dc2e3ec 100644
--- a/chromium/components/offline_pages/background/remove_requests_task_unittest.cc
+++ b/chromium/components/offline_pages/core/background/remove_requests_task_unittest.cc
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/remove_requests_task.h"
+#include "components/offline_pages/core/background/remove_requests_task.h"
#include <memory>
#include "base/bind.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -30,15 +30,16 @@ class RemoveRequestsTaskTest : public testing::Test {
void PumpLoop();
+ void InitializeStore(RequestQueueStore* store);
void AddRequestsToStore(RequestQueueStore* store);
-
- void AddRequestDone(ItemActionStatus status);
-
void RemoveRequestsCallback(std::unique_ptr<UpdateRequestsResult> result);
UpdateRequestsResult* last_result() const { return result_.get(); }
private:
+ void InitializeStoreDone(bool succesS);
+ void AddRequestDone(ItemActionStatus status);
+
std::unique_ptr<UpdateRequestsResult> result_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
@@ -54,6 +55,12 @@ void RemoveRequestsTaskTest::PumpLoop() {
task_runner_->RunUntilIdle();
}
+void RemoveRequestsTaskTest::InitializeStore(RequestQueueStore* store) {
+ store->Initialize(base::Bind(&RemoveRequestsTaskTest::InitializeStoreDone,
+ base::Unretained(this)));
+ PumpLoop();
+}
+
void RemoveRequestsTaskTest::AddRequestsToStore(RequestQueueStore* store) {
base::Time creation_time = base::Time::Now();
SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
@@ -69,17 +76,23 @@ void RemoveRequestsTaskTest::AddRequestsToStore(RequestQueueStore* store) {
PumpLoop();
}
-void RemoveRequestsTaskTest::AddRequestDone(ItemActionStatus status) {
- ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
void RemoveRequestsTaskTest::RemoveRequestsCallback(
std::unique_ptr<UpdateRequestsResult> result) {
result_ = std::move(result);
}
+void RemoveRequestsTaskTest::InitializeStoreDone(bool success) {
+ ASSERT_TRUE(success);
+}
+
+void RemoveRequestsTaskTest::AddRequestDone(ItemActionStatus status) {
+ ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
TEST_F(RemoveRequestsTaskTest, RemoveWhenStoreEmpty) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+
std::vector<int64_t> request_ids{kRequestId1};
RemoveRequestsTask task(
&store, request_ids,
@@ -97,7 +110,9 @@ TEST_F(RemoveRequestsTaskTest, RemoveWhenStoreEmpty) {
TEST_F(RemoveRequestsTaskTest, RemoveSingleItem) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
AddRequestsToStore(&store);
+
std::vector<int64_t> request_ids{kRequestId1};
RemoveRequestsTask task(
&store, request_ids,
@@ -116,7 +131,9 @@ TEST_F(RemoveRequestsTaskTest, RemoveSingleItem) {
TEST_F(RemoveRequestsTaskTest, RemoveMultipleItems) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
AddRequestsToStore(&store);
+
std::vector<int64_t> request_ids{kRequestId1, kRequestId2};
RemoveRequestsTask task(
&store, request_ids,
@@ -139,6 +156,8 @@ TEST_F(RemoveRequestsTaskTest, RemoveMultipleItems) {
TEST_F(RemoveRequestsTaskTest, DeleteWithEmptyIdList) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
+
std::vector<int64_t> request_ids;
RemoveRequestsTask task(
&store, request_ids,
@@ -153,7 +172,9 @@ TEST_F(RemoveRequestsTaskTest, DeleteWithEmptyIdList) {
TEST_F(RemoveRequestsTaskTest, RemoveMissingItem) {
RequestQueueInMemoryStore store;
+ InitializeStore(&store);
AddRequestsToStore(&store);
+
std::vector<int64_t> request_ids{kRequestId1, kRequestId3};
RemoveRequestsTask task(
&store, request_ids,
diff --git a/chromium/components/offline_pages/background/request_coordinator.cc b/chromium/components/offline_pages/core/background/request_coordinator.cc
index 529838669cf..83a844b0980 100644
--- a/chromium/components/offline_pages/background/request_coordinator.cc
+++ b/chromium/components/offline_pages/core/background/request_coordinator.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
#include <limits>
#include <utility>
@@ -14,13 +14,13 @@
#include "base/rand_util.h"
#include "base/sys_info.h"
#include "base/time/time.h"
-#include "components/offline_pages/background/offliner_factory.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/background/offliner.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
namespace offline_pages {
@@ -157,7 +157,7 @@ bool IsSingleSuccessResult(const UpdateRequestsResult* result) {
RequestCoordinator::RequestCoordinator(
std::unique_ptr<OfflinerPolicy> policy,
- std::unique_ptr<OfflinerFactory> factory,
+ std::unique_ptr<Offliner> offliner,
std::unique_ptr<RequestQueue> queue,
std::unique_ptr<Scheduler> scheduler,
net::NetworkQualityEstimator::NetworkQualityProvider*
@@ -166,11 +166,9 @@ RequestCoordinator::RequestCoordinator(
is_busy_(false),
is_starting_(false),
processing_state_(ProcessingWindowState::STOPPED),
- use_test_connection_type_(false),
- test_connection_type_(),
- offliner_(nullptr),
+ use_test_device_conditions_(false),
+ offliner_(std::move(offliner)),
policy_(std::move(policy)),
- factory_(std::move(factory)),
queue_(std::move(queue)),
scheduler_(std::move(scheduler)),
policy_controller_(new ClientPolicyController()),
@@ -178,12 +176,20 @@ RequestCoordinator::RequestCoordinator(
active_request_(nullptr),
last_offlining_status_(Offliner::RequestStatus::UNKNOWN),
scheduler_callback_(base::Bind(&EmptySchedulerCallback)),
- immediate_schedule_callback_(base::Bind(&EmptySchedulerCallback)),
+ internal_start_processing_callback_(base::Bind(&EmptySchedulerCallback)),
weak_ptr_factory_(this) {
DCHECK(policy_ != nullptr);
- std::unique_ptr<PickRequestTaskFactory> picker_factory(
- new PickRequestTaskFactory(policy_.get(), this, &event_logger_));
- queue_->SetPickerFactory(std::move(picker_factory));
+ std::unique_ptr<CleanupTaskFactory> cleanup_factory(
+ new CleanupTaskFactory(policy_.get(), this, &event_logger_));
+ queue_->SetCleanupFactory(std::move(cleanup_factory));
+ // If we exited with any items left in the OFFLINING state, move them back to
+ // the AVAILABLE state, and update the UI by sending notifications. Do this
+ // before we cleanup, so any requests that are now OFFLINING which have
+ // expired can be legitimate candidates for cleanup.
+ queue_->ReconcileRequests(base::Bind(&RequestCoordinator::ReconcileCallback,
+ weak_ptr_factory_.GetWeakPtr()));
+ // Do a cleanup of expired or over tried requests at startup time.
+ queue_->CleanupRequestQueue();
}
RequestCoordinator::~RequestCoordinator() {}
@@ -417,15 +423,6 @@ void RequestCoordinator::ResumeRequests(
ScheduleAsNeeded();
}
-net::NetworkChangeNotifier::ConnectionType
-RequestCoordinator::GetConnectionType() {
- // If we have a connection type set for test, use that.
- if (use_test_connection_type_)
- return test_connection_type_;
-
- return net::NetworkChangeNotifier::GetConnectionType();
-}
-
void RequestCoordinator::AddRequestResultCallback(
RequestAvailability availability,
AddRequestResult result,
@@ -464,6 +461,12 @@ void RequestCoordinator::UpdateMultipleRequestsCallback(
StartImmediatelyIfConnected();
}
+void RequestCoordinator::ReconcileCallback(
+ std::unique_ptr<UpdateRequestsResult> result) {
+ for (const auto& request : result->updated_items)
+ NotifyChanged(request);
+}
+
void RequestCoordinator::HandleRemovedRequestsAndCallback(
const RemoveRequestsCallback& callback,
RequestNotifier::BackgroundSavePageResult status,
@@ -490,8 +493,7 @@ void RequestCoordinator::ScheduleAsNeeded() {
weak_ptr_factory_.GetWeakPtr()));
}
-void RequestCoordinator::StopProcessing(
- Offliner::RequestStatus stop_status) {
+void RequestCoordinator::StopProcessing(Offliner::RequestStatus stop_status) {
processing_state_ = ProcessingWindowState::STOPPED;
StopPrerendering(stop_status);
@@ -508,19 +510,32 @@ void RequestCoordinator::HandleWatchdogTimeout() {
// Returns true if the caller should expect a callback, false otherwise. For
// instance, this would return false if a request is already in progress.
-bool RequestCoordinator::StartProcessing(
+bool RequestCoordinator::StartScheduledProcessing(
const DeviceConditions& device_conditions,
const base::Callback<void(bool)>& callback) {
DVLOG(2) << "Scheduled " << __func__;
+ current_conditions_.reset(new DeviceConditions(device_conditions));
return StartProcessingInternal(ProcessingWindowState::SCHEDULED_WINDOW,
- device_conditions, callback);
+ callback);
}
+// Returns true if the caller should expect a callback, false otherwise.
+bool RequestCoordinator::StartImmediateProcessing(
+ const base::Callback<void(bool)>& callback) {
+ UpdateCurrentConditionsFromAndroid();
+ OfflinerImmediateStartStatus immediate_start_status =
+ TryImmediateStart(callback);
+ UMA_HISTOGRAM_ENUMERATION(
+ "OfflinePages.Background.ImmediateStartStatus", immediate_start_status,
+ RequestCoordinator::OfflinerImmediateStartStatus::STATUS_COUNT);
+ return immediate_start_status == OfflinerImmediateStartStatus::STARTED;
+}
+
+// The current_conditions_ must be set sometime before calling
+// StartProcessingInternal on all calling code paths.
bool RequestCoordinator::StartProcessingInternal(
const ProcessingWindowState processing_state,
- const DeviceConditions& device_conditions,
const base::Callback<void(bool)>& callback) {
- current_conditions_.reset(new DeviceConditions(device_conditions));
if (is_starting_ || is_busy_)
return false;
processing_state_ = processing_state;
@@ -536,14 +551,12 @@ bool RequestCoordinator::StartProcessingInternal(
}
void RequestCoordinator::StartImmediatelyIfConnected() {
- OfflinerImmediateStartStatus immediate_start_status = TryImmediateStart();
- UMA_HISTOGRAM_ENUMERATION(
- "OfflinePages.Background.ImmediateStartStatus", immediate_start_status,
- RequestCoordinator::OfflinerImmediateStartStatus::STATUS_COUNT);
+ StartImmediateProcessing(internal_start_processing_callback_);
}
RequestCoordinator::OfflinerImmediateStartStatus
-RequestCoordinator::TryImmediateStart() {
+RequestCoordinator::TryImmediateStart(
+ const base::Callback<void(bool)>& callback) {
DVLOG(2) << "Immediate " << __func__;
// Make sure not already busy processing.
if (is_busy_)
@@ -554,11 +567,11 @@ RequestCoordinator::TryImmediateStart() {
!offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled()) {
DVLOG(2) << "low end device, returning";
// Let the scheduler know we are done processing and failed due to svelte.
- immediate_schedule_callback_.Run(false);
+ callback.Run(false);
return OfflinerImmediateStartStatus::NOT_STARTED_ON_SVELTE;
}
- if (GetConnectionType() ==
+ if (current_conditions_->GetNetConnectionType() ==
net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE) {
RequestConnectedEventForStarting();
return OfflinerImmediateStartStatus::NO_CONNECTION;
@@ -568,13 +581,8 @@ RequestCoordinator::TryImmediateStart() {
ClearConnectedEventRequest();
}
- // Start processing with manufactured conservative battery conditions
- // (i.e., assume no battery).
- // TODO(dougarnett): Obtain actual battery conditions (from Android/Java).
-
- DeviceConditions device_conditions(false, 0, GetConnectionType());
if (StartProcessingInternal(ProcessingWindowState::IMMEDIATE_WINDOW,
- device_conditions, immediate_schedule_callback_))
+ callback))
return OfflinerImmediateStartStatus::STARTED;
else
return OfflinerImmediateStartStatus::NOT_ACCEPTED;
@@ -595,8 +603,32 @@ void RequestCoordinator::HandleConnectedEventForStarting() {
StartImmediatelyIfConnected();
}
+void RequestCoordinator::UpdateCurrentConditionsFromAndroid() {
+ // If we have already set the connection type for testing, don't get it from
+ // android, but use what the test already set up.
+ if (use_test_device_conditions_)
+ return;
+
+ current_conditions_ = base::MakeUnique<DeviceConditions>(
+ scheduler_->GetCurrentDeviceConditions());
+}
+
void RequestCoordinator::TryNextRequest(bool is_start_of_processing) {
is_starting_ = true;
+
+ // If this is the first call, the device conditions are current, no need to
+ // update them.
+ // TODO(petewil): Now that we can get conditions any time, consider getting
+ // them now instead of passing them in earlier when we start scheduled
+ // processing.
+ if (!is_start_of_processing) {
+ // Get current device conditions from the Java side across the bridge.
+ // NetworkChangeNotifier will not have the right conditions if chromium is
+ // in the background in android, so prefer to always get the conditions via
+ // the android APIs.
+ UpdateCurrentConditionsFromAndroid();
+ }
+
base::TimeDelta processing_time_budget;
if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
processing_time_budget = base::TimeDelta::FromSeconds(
@@ -607,14 +639,8 @@ void RequestCoordinator::TryNextRequest(bool is_start_of_processing) {
policy_->GetProcessingTimeBudgetForImmediateLoadInSeconds());
}
- // Determine connection type. If just starting processing, the best source is
- // from the current device conditions (they are fresh and motivated starting
- // processing whereas NetworkChangeNotifier may lag reality).
- net::NetworkChangeNotifier::ConnectionType connection_type;
- if (is_start_of_processing)
- connection_type = current_conditions_->GetNetConnectionType();
- else
- connection_type = GetConnectionType();
+ net::NetworkChangeNotifier::ConnectionType connection_type =
+ current_conditions_->GetNetConnectionType();
// If there is no network or no time left in the budget, return to the
// scheduler. We do not remove the pending scheduler task that was set
@@ -641,8 +667,8 @@ void RequestCoordinator::TryNextRequest(bool is_start_of_processing) {
// Ask request queue to make a new PickRequestTask object, then put it on the
// task queue.
queue_->PickNextRequest(
- base::Bind(&RequestCoordinator::RequestPicked,
- weak_ptr_factory_.GetWeakPtr()),
+ policy_.get(), base::Bind(&RequestCoordinator::RequestPicked,
+ weak_ptr_factory_.GetWeakPtr()),
base::Bind(&RequestCoordinator::RequestNotPicked,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&RequestCoordinator::RequestCounts,
@@ -655,7 +681,8 @@ void RequestCoordinator::TryNextRequest(bool is_start_of_processing) {
}
// Called by the request picker when a request has been picked.
-void RequestCoordinator::RequestPicked(const SavePageRequest& request) {
+void RequestCoordinator::RequestPicked(const SavePageRequest& request,
+ bool cleanup_needed) {
DVLOG(2) << request.url() << " " << __func__;
is_starting_ = false;
@@ -664,10 +691,15 @@ void RequestCoordinator::RequestPicked(const SavePageRequest& request) {
// Send the request on to the offliner.
SendRequestToOffliner(request);
}
+
+ // Schedule a queue cleanup if needed.
+ if (cleanup_needed)
+ queue_->CleanupRequestQueue();
}
void RequestCoordinator::RequestNotPicked(
- bool non_user_requested_tasks_remaining) {
+ bool non_user_requested_tasks_remaining,
+ bool cleanup_needed) {
DVLOG(2) << __func__;
is_starting_ = false;
@@ -683,6 +715,10 @@ void RequestCoordinator::RequestNotPicked(
scheduler_->Schedule(GetTriggerConditions(!kUserRequest));
}
+ // Schedule a queue cleanup if needed.
+ if (cleanup_needed)
+ queue_->CleanupRequestQueue();
+
// Let the scheduler know we are done processing.
scheduler_callback_.Run(true);
}
@@ -740,10 +776,10 @@ void RequestCoordinator::SendRequestToOffliner(const SavePageRequest& request) {
if (processing_state_ == ProcessingWindowState::STOPPED)
return;
- GetOffliner();
if (!offliner_) {
- DVLOG(0) << "Unable to create Offliner. "
- << "Cannot background offline page.";
+ // TODO(chili,petewil): We should have UMA here to track frequency of this,
+ // if it happens at all.
+ DVLOG(0) << "Offliner crashed. Cannot background offline page.";
return;
}
@@ -772,8 +808,7 @@ void RequestCoordinator::StartOffliner(
update_result->item_statuses.at(0).first != request_id ||
update_result->item_statuses.at(0).second != ItemActionStatus::SUCCESS) {
is_busy_ = false;
- // TODO(fgorski): what is the best result? Do we create a new status?
- StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
+ StopProcessing(Offliner::QUEUE_UPDATE_FAILED);
DVLOG(1) << "Failed to mark attempt started: " << request_id;
UpdateRequestResult request_result =
update_result->store_state != StoreState::LOADED
@@ -812,7 +847,7 @@ void RequestCoordinator::StartOffliner(
} else {
is_busy_ = false;
DVLOG(0) << "Unable to start LoadAndSave";
- StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
+ StopProcessing(Offliner::LOADING_NOT_ACCEPTED);
// We need to undo the MarkAttemptStarted that brought us to this
// method since we didn't success in starting after all.
@@ -847,7 +882,7 @@ void RequestCoordinator::UpdateRequestForCompletedAttempt(
const SavePageRequest& request,
Offliner::RequestStatus status) {
if (status == Offliner::RequestStatus::FOREGROUND_CANCELED ||
- status == Offliner::RequestStatus::PRERENDERING_CANCELED) {
+ status == Offliner::RequestStatus::LOADING_CANCELED) {
// Update the request for the canceled attempt.
// TODO(dougarnett): See if we can conclusively identify other attempt
// aborted cases to treat this way (eg, for Render Process Killed).
@@ -856,9 +891,9 @@ void RequestCoordinator::UpdateRequestForCompletedAttempt(
// Remove the request from the queue if it succeeded.
RemoveAttemptedRequest(request,
RequestNotifier::BackgroundSavePageResult::SUCCESS);
- } else if (status == Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY) {
+ } else if (status == Offliner::RequestStatus::LOADING_FAILED_NO_RETRY) {
RemoveAttemptedRequest(
- request, RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE);
+ request, RequestNotifier::BackgroundSavePageResult::LOADING_FAILURE);
} else if (request.completed_attempt_count() + 1 >=
policy_->GetMaxCompletedTries()) {
// Remove from the request queue if we exceeded max retries. The +1
@@ -888,11 +923,12 @@ bool RequestCoordinator::ShouldTryNextRequest(
case Offliner::RequestStatus::SAVE_FAILED:
case Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED:
case Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT:
- case Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY:
+ case Offliner::RequestStatus::LOADING_FAILED:
+ case Offliner::RequestStatus::LOADING_FAILED_NO_RETRY:
return true;
case Offliner::RequestStatus::FOREGROUND_CANCELED:
- case Offliner::RequestStatus::PRERENDERING_CANCELED:
- case Offliner::RequestStatus::PRERENDERING_FAILED:
+ case Offliner::RequestStatus::LOADING_CANCELED:
+ case Offliner::RequestStatus::LOADING_FAILED_NO_NEXT:
// No further processing in this service window.
return false;
default:
@@ -974,12 +1010,6 @@ void RequestCoordinator::NotifyChanged(const SavePageRequest& request) {
observer.OnChanged(request);
}
-void RequestCoordinator::GetOffliner() {
- if (!offliner_) {
- offliner_ = factory_->GetOffliner(policy_.get());
- }
-}
-
ClientPolicyController* RequestCoordinator::GetPolicyController() {
return policy_controller_.get();
}
diff --git a/chromium/components/offline_pages/background/request_coordinator.h b/chromium/components/offline_pages/core/background/request_coordinator.h
index a71fe1102a6..09606f2e71b 100644
--- a/chromium/components/offline_pages/background/request_coordinator.h
+++ b/chromium/components/offline_pages/core/background/request_coordinator.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_H_
#include <memory>
#include <set>
@@ -18,13 +18,12 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/keyed_service/core/keyed_service.h"
-#include "components/offline_pages/background/connection_notifier.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/scheduler.h"
+#include "components/offline_pages/core/background/connection_notifier.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/scheduler.h"
#include "net/nqe/network_quality_estimator.h"
#include "url/gurl.h"
@@ -32,12 +31,8 @@ namespace offline_pages {
struct ClientId;
class OfflinerPolicy;
-class OfflinerFactory;
class Offliner;
-class RequestPicker;
-class RequestQueue;
class SavePageRequest;
-class Scheduler;
class ClientPolicyController;
// Coordinates queueing and processing save page later requests.
@@ -74,7 +69,7 @@ class RequestCoordinator : public KeyedService,
GetRequestsCallback;
RequestCoordinator(std::unique_ptr<OfflinerPolicy> policy,
- std::unique_ptr<OfflinerFactory> factory,
+ std::unique_ptr<Offliner> offliner,
std::unique_ptr<RequestQueue> queue,
std::unique_ptr<Scheduler> scheduler,
net::NetworkQualityEstimator::NetworkQualityProvider*
@@ -104,11 +99,24 @@ class RequestCoordinator : public KeyedService,
// Get all save page request items in the callback.
void GetAllRequests(const GetRequestsCallback& callback);
- // Starts processing of one or more queued save page later requests.
+ // Starts processing of one or more queued save page later requests
+ // in scheduled background mode.
// Returns whether processing was started and that caller should expect
// a callback. If processing was already active, returns false.
- bool StartProcessing(const DeviceConditions& device_conditions,
- const base::Callback<void(bool)>& callback);
+ bool StartScheduledProcessing(const DeviceConditions& device_conditions,
+ const base::Callback<void(bool)>& callback);
+
+ // Attempts to starts processing of one or more queued save page later
+ // requests (if device conditions are suitable) in immediate mode
+ // (opposed to scheduled background mode). This method is suitable to call
+ // when there is some user action that suggests the user wants to do this
+ // operation now, if possible, vs. trying to do it in the background when
+ // idle.
+ // Returns whether processing was started and that caller should expect
+ // a callback. If processing was already active or some condition was
+ // not suitable for immediate processing (e.g., network or low-end device),
+ // returns false.
+ bool StartImmediateProcessing(const base::Callback<void(bool)>& callback);
// Stops the current request processing if active. This is a way for
// caller to abort processing; otherwise, processing will complete on
@@ -133,12 +141,12 @@ class RequestCoordinator : public KeyedService,
scheduler_callback_ = callback;
}
- // A way to set the callback which would be called if the request will be
- // scheduled immediately. Used by testing harness to determine if a request
- // has been processed.
- void SetImmediateScheduleCallbackForTest(
+ // A way to set the callback which would be called if processing will be
+ // triggered immediately internally by the coordinator. Used by testing
+ // harness to determine if a request has been processed.
+ void SetInternalStartProcessingCallbackForTest(
const base::Callback<void(bool)> callback) {
- immediate_schedule_callback_ = callback;
+ internal_start_processing_callback_ = callback;
}
void StartImmediatelyForTest() { StartImmediatelyIfConnected(); }
@@ -173,16 +181,14 @@ class RequestCoordinator : public KeyedService,
return last_offlining_status_;
}
- bool is_busy() {
- return is_busy_;
- }
+ bool is_busy() { return is_busy_; }
// Returns whether processing is starting (before it is decided to actually
// process a request (is_busy()) at this time or not.
bool is_starting() { return is_starting_; }
// Tracks whether the last offlining attempt got canceled. This is reset by
- // the next StartProcessing() call.
+ // the next call to start processing.
bool is_canceled() {
return processing_state_ == ProcessingWindowState::STOPPED;
}
@@ -239,6 +245,8 @@ class RequestCoordinator : public KeyedService,
void UpdateMultipleRequestsCallback(
std::unique_ptr<UpdateRequestsResult> result);
+ void ReconcileCallback(std::unique_ptr<UpdateRequestsResult> result);
+
void HandleRemovedRequestsAndCallback(
const RemoveRequestsCallback& callback,
RequestNotifier::BackgroundSavePageResult status,
@@ -248,14 +256,14 @@ class RequestCoordinator : public KeyedService,
std::unique_ptr<UpdateRequestsResult> result);
bool StartProcessingInternal(const ProcessingWindowState processing_state,
- const DeviceConditions& device_conditions,
const base::Callback<void(bool)>& callback);
// Start processing now if connected (but with conservative assumption
// as to other device conditions).
void StartImmediatelyIfConnected();
- OfflinerImmediateStartStatus TryImmediateStart();
+ OfflinerImmediateStartStatus TryImmediateStart(
+ const base::Callback<void(bool)>& callback);
// Requests a callback upon the next network connection to start processing.
void RequestConnectedEventForStarting();
@@ -271,12 +279,13 @@ class RequestCoordinator : public KeyedService,
void ScheduleAsNeeded();
// Callback from the request picker when it has chosen our next request.
- void RequestPicked(const SavePageRequest& request);
+ void RequestPicked(const SavePageRequest& request, bool cleanup_needed);
// Callback from the request picker when no more requests are in the queue.
// The parameter is a signal for what (if any) conditions to schedule future
// processing for.
- void RequestNotPicked(bool non_user_requested_tasks_remaining);
+ void RequestNotPicked(bool non_user_requested_tasks_remaining,
+ bool cleanup_needed);
// Callback from request picker that receives the current available queued
// request count as well as the total queued request count (which may be
@@ -320,6 +329,9 @@ class RequestCoordinator : public KeyedService,
// processing window (vs. continuing within a current processing window).
void TryNextRequest(bool is_start_of_processing);
+ // Cross the JNI Bridge and get the current device conditions from Android.
+ void UpdateCurrentConditionsFromAndroid();
+
// If there is an active request in the list, cancel that request.
bool CancelActiveRequestIfItMatches(const std::vector<int64_t>& request_ids);
@@ -341,21 +353,8 @@ class RequestCoordinator : public KeyedService,
const std::string& name_space,
std::unique_ptr<UpdateRequestsResult> result);
- // Returns the appropriate offliner to use, getting a new one from the factory
- // if needed.
- void GetOffliner();
-
- // Method to wrap calls to getting the connection type so it can be
- // changed for tests.
- net::NetworkChangeNotifier::ConnectionType GetConnectionType();
-
- void SetNetworkConditionsForTest(
- net::NetworkChangeNotifier::ConnectionType connection) {
- use_test_connection_type_ = true;
- test_connection_type_ = connection;
- }
-
void SetDeviceConditionsForTest(const DeviceConditions& current_conditions) {
+ use_test_device_conditions_ = true;
current_conditions_.reset(new DeviceConditions(current_conditions));
}
@@ -376,12 +375,13 @@ class RequestCoordinator : public KeyedService,
bool is_starting_;
// Identifies the type of current processing window or if processing stopped.
ProcessingWindowState processing_state_;
- // True if we should use the test connection type instead of the actual type.
- bool use_test_connection_type_;
+ // True if we should use the test device conditions instead of actual
+ // conditions.
+ bool use_test_device_conditions_;
// For use by tests, a fake network connection type
net::NetworkChangeNotifier::ConnectionType test_connection_type_;
- // Unowned pointer to the current offliner, if any.
- Offliner* offliner_;
+ // Owned pointer to the current offliner.
+ std::unique_ptr<Offliner> offliner_;
base::Time operation_start_time_;
// The observers.
base::ObserverList<Observer> observers_;
@@ -389,8 +389,6 @@ class RequestCoordinator : public KeyedService,
std::unique_ptr<DeviceConditions> current_conditions_;
// RequestCoordinator takes over ownership of the policy
std::unique_ptr<OfflinerPolicy> policy_;
- // OfflinerFactory. Used to create offline pages. Owned.
- std::unique_ptr<OfflinerFactory> factory_;
// RequestQueue. Used to store incoming requests. Owned.
std::unique_ptr<RequestQueue> queue_;
// Scheduler. Used to request a callback when network is available. Owned.
@@ -407,14 +405,20 @@ class RequestCoordinator : public KeyedService,
// A set of request_ids that we are holding off until the download manager is
// done with them.
std::set<int64_t> disabled_requests_;
- // Calling this returns to the scheduler across the JNI bridge.
+ // The processing callback to call when processing the current processing
+ // window stops. It is set from the Start*Processing() call that triggered
+ // the processing or it may be the |internal_start_processing_callback_| if
+ // processing was triggered internally.
+ // For StartScheduledProcessing() processing, calling its callback returns
+ // to the scheduler across the JNI bridge.
base::Callback<void(bool)> scheduler_callback_;
+ // Callback invoked when internally triggered processing is done. It is
+ // kept as a class member so that it may be overridden for test visibility.
+ base::Callback<void(bool)> internal_start_processing_callback_;
// Logger to record events.
RequestCoordinatorEventLogger event_logger_;
// Timer to watch for pre-render attempts running too long.
base::OneShotTimer watchdog_timer_;
- // Callback invoked when an immediate request is done (default empty).
- base::Callback<void(bool)> immediate_schedule_callback_;
// Used for potential immediate processing when we get network connection.
std::unique_ptr<ConnectionNotifier> connection_notifier_;
// Allows us to pass a weak pointer to callbacks.
@@ -425,4 +429,4 @@ class RequestCoordinator : public KeyedService,
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_H_
diff --git a/chromium/components/offline_pages/background/request_coordinator_event_logger.cc b/chromium/components/offline_pages/core/background/request_coordinator_event_logger.cc
index 1585265311c..a4d8edf2e72 100644
--- a/chromium/components/offline_pages/background/request_coordinator_event_logger.cc
+++ b/chromium/components/offline_pages/core/background/request_coordinator_event_logger.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
namespace offline_pages {
@@ -19,20 +19,26 @@ static std::string OfflinerRequestStatusToString(
return "SAVED";
case Offliner::REQUEST_COORDINATOR_CANCELED:
return "REQUEST_COORDINATOR_CANCELED";
- case Offliner::PRERENDERING_CANCELED:
- return "PRERENDERING_CANCELED";
- case Offliner::PRERENDERING_FAILED:
- return "PRERENDERING_FAILED";
+ case Offliner::LOADING_CANCELED:
+ return "LOADING_CANCELED";
+ case Offliner::LOADING_FAILED:
+ return "LOADING_FAILED";
case Offliner::SAVE_FAILED:
return "SAVE_FAILED";
case Offliner::FOREGROUND_CANCELED:
return "FOREGROUND_CANCELED";
case Offliner::REQUEST_COORDINATOR_TIMED_OUT:
return "REQUEST_COORDINATOR_TIMED_OUT";
- case Offliner::PRERENDERING_NOT_STARTED:
- return "PRERENDERING_NOT_STARTED";
- case Offliner::PRERENDERING_FAILED_NO_RETRY:
- return "PRERENDERING_FAILED_NO_RETRY";
+ case Offliner::DEPRECATED_LOADING_NOT_STARTED:
+ return "DEPRECATED_LOADING_NOT_STARTED";
+ case Offliner::LOADING_FAILED_NO_RETRY:
+ return "LOADING_FAILED_NO_RETRY";
+ case Offliner::LOADING_FAILED_NO_NEXT:
+ return "LOADING_FAILED_NO_NEXT";
+ case Offliner::LOADING_NOT_ACCEPTED:
+ return "LOADING_NOT_ACCEPTED";
+ case Offliner::QUEUE_UPDATE_FAILED:
+ return "QUEUE_UPDATE_FAILED";
default:
NOTREACHED();
return std::to_string(static_cast<int>(request_status));
@@ -44,10 +50,10 @@ static std::string BackgroundSavePageResultToString(
switch (result) {
case RequestNotifier::BackgroundSavePageResult::SUCCESS:
return "SUCCESS";
- case RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE:
- return "PRERENDER_FAILURE";
- case RequestNotifier::BackgroundSavePageResult::PRERENDER_CANCELED:
- return "PRERENDER_CANCELED";
+ case RequestNotifier::BackgroundSavePageResult::LOADING_FAILURE:
+ return "LOADING_FAILURE";
+ case RequestNotifier::BackgroundSavePageResult::LOADING_CANCELED:
+ return "LOADING_CANCELED";
case RequestNotifier::BackgroundSavePageResult::FOREGROUND_CANCELED:
return "FOREGROUND_CANCELED";
case RequestNotifier::BackgroundSavePageResult::SAVE_FAILED:
diff --git a/chromium/components/offline_pages/background/request_coordinator_event_logger.h b/chromium/components/offline_pages/core/background/request_coordinator_event_logger.h
index ec070794f65..92bd00941c6 100644
--- a/chromium/components/offline_pages/background/request_coordinator_event_logger.h
+++ b/chromium/components/offline_pages/core/background/request_coordinator_event_logger.h
@@ -2,16 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
#include <stdint.h>
#include <string>
-#include "components/offline_pages/background/offliner.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue_results.h"
-#include "components/offline_pages/offline_event_logger.h"
+#include "components/offline_pages/core/background/offliner.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/offline_event_logger.h"
namespace offline_pages {
@@ -35,4 +35,4 @@ class RequestCoordinatorEventLogger : public OfflineEventLogger {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
diff --git a/chromium/components/offline_pages/background/request_coordinator_event_logger_unittest.cc b/chromium/components/offline_pages/core/background/request_coordinator_event_logger_unittest.cc
index eed1f45b325..8aa7f6ca351 100644
--- a/chromium/components/offline_pages/background/request_coordinator_event_logger_unittest.cc
+++ b/chromium/components/offline_pages/core/background/request_coordinator_event_logger_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/offline_pages/background/request_coordinator_unittest.cc b/chromium/components/offline_pages/core/background/request_coordinator_unittest.cc
index 9d6954a463a..6f606be8331 100644
--- a/chromium/components/offline_pages/background/request_coordinator_unittest.cc
+++ b/chromium/components/offline_pages/core/background/request_coordinator_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
#include <memory>
#include <string>
@@ -19,16 +19,17 @@
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner.h"
-#include "components/offline_pages/background/offliner_factory.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/pick_request_task_factory.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/background/scheduler.h"
-#include "components/offline_pages/offline_page_feature.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/network_quality_provider_stub.h"
+#include "components/offline_pages/core/background/offliner.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/offliner_stub.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/background/scheduler.h"
+#include "components/offline_pages/core/background/scheduler_stub.h"
+#include "components/offline_pages/core/offline_page_feature.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -52,135 +53,6 @@ const bool kUserRequested = true;
const int kAttemptCount = 1;
} // namespace
-class SchedulerStub : public Scheduler {
- public:
- SchedulerStub()
- : schedule_called_(false),
- backup_schedule_called_(false),
- unschedule_called_(false),
- schedule_delay_(0L),
- conditions_(false, 0, false) {}
-
- void Schedule(const TriggerConditions& trigger_conditions) override {
- schedule_called_ = true;
- conditions_ = trigger_conditions;
- }
-
- void BackupSchedule(const TriggerConditions& trigger_conditions,
- long delay_in_seconds) override {
- backup_schedule_called_ = true;
- schedule_delay_ = delay_in_seconds;
- conditions_ = trigger_conditions;
- }
-
- // Unschedules the currently scheduled task, if any.
- void Unschedule() override {
- unschedule_called_ = true;
- }
-
- bool schedule_called() const { return schedule_called_; }
-
- bool backup_schedule_called() const { return backup_schedule_called_;}
-
- bool unschedule_called() const { return unschedule_called_; }
-
- TriggerConditions const* conditions() const { return &conditions_; }
-
- private:
- bool schedule_called_;
- bool backup_schedule_called_;
- bool unschedule_called_;
- long schedule_delay_;
- TriggerConditions conditions_;
-};
-
-class OfflinerStub : public Offliner {
- public:
- OfflinerStub()
- : request_(kRequestId1, kUrl1, kClientId1, base::Time::Now(),
- kUserRequested),
- disable_loading_(false),
- enable_callback_(false),
- cancel_called_(false) {}
-
- bool LoadAndSave(const SavePageRequest& request,
- const CompletionCallback& callback) override {
- if (disable_loading_)
- return false;
-
- callback_ = callback;
- request_ = request;
- // Post the callback on the run loop.
- if (enable_callback_) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::Bind(callback, request, Offliner::RequestStatus::SAVED));
- }
- return true;
- }
-
- void Cancel() override { cancel_called_ = true; }
-
- void disable_loading() {
- disable_loading_ = true;
- }
-
- void enable_callback(bool enable) {
- enable_callback_ = enable;
- }
-
- bool cancel_called() { return cancel_called_; }
-
- private:
- CompletionCallback callback_;
- SavePageRequest request_;
- bool disable_loading_;
- bool enable_callback_;
- bool cancel_called_;
-};
-
-class OfflinerFactoryStub : public OfflinerFactory {
- public:
- OfflinerFactoryStub() : offliner_(nullptr) {}
-
- Offliner* GetOffliner(const OfflinerPolicy* policy) override {
- if (offliner_.get() == nullptr) {
- offliner_.reset(new OfflinerStub());
- }
- return offliner_.get();
- }
-
- private:
- std::unique_ptr<OfflinerStub> offliner_;
-};
-
-class NetworkQualityEstimatorStub
- : public net::NetworkQualityEstimator::NetworkQualityProvider {
- public:
- NetworkQualityEstimatorStub()
- : connection_type_(
- net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G) {}
-
- net::EffectiveConnectionType GetEffectiveConnectionType() const override {
- return connection_type_;
- }
-
- void AddEffectiveConnectionTypeObserver(
- net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
- override {}
-
- void RemoveEffectiveConnectionTypeObserver(
- net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
- override {}
-
- void SetEffectiveConnectionTypeForTest(net::EffectiveConnectionType type) {
- connection_type_ = type;
- }
-
- private:
- net::EffectiveConnectionType connection_type_;
-};
-
class ObserverStub : public RequestCoordinator::Observer {
public:
ObserverStub()
@@ -230,8 +102,7 @@ class ObserverStub : public RequestCoordinator::Observer {
SavePageRequest::RequestState state_;
};
-class RequestCoordinatorTest
- : public testing::Test {
+class RequestCoordinatorTest : public testing::Test {
public:
RequestCoordinatorTest();
~RequestCoordinatorTest() override;
@@ -240,29 +111,23 @@ class RequestCoordinatorTest
void PumpLoop();
- RequestCoordinator* coordinator() {
- return coordinator_.get();
- }
+ RequestCoordinator* coordinator() { return coordinator_.get(); }
- bool is_busy() {
- return coordinator_->is_busy();
- }
+ bool is_busy() { return coordinator_->is_busy(); }
bool is_starting() { return coordinator_->is_starting(); }
- // Empty callback function.
- void ImmediateScheduleCallbackFunction(bool result) {
- immediate_schedule_callback_called_ = true;
- immediate_schedule_callback_result_ = result;
+ // Test processing callback function.
+ void ProcessingCallbackFunction(bool result) {
+ processing_callback_called_ = true;
+ processing_callback_result_ = result;
}
// Callback function which releases a wait for it.
- void WaitingCallbackFunction(bool result) {
- waiter_.Signal();
- }
+ void WaitingCallbackFunction(bool result) { waiter_.Signal(); }
net::NetworkChangeNotifier::ConnectionType GetConnectionType() {
- return coordinator()->GetConnectionType();
+ return coordinator()->current_conditions_->GetNetConnectionType();
}
// Callback for Add requests.
@@ -297,32 +162,29 @@ class RequestCoordinatorTest
return last_remove_results_;
}
- void DisableLoading() {
- offliner_->disable_loading();
- }
+ void DisableLoading() { offliner_->disable_loading(); }
void EnableOfflinerCallback(bool enable) {
offliner_->enable_callback(enable);
}
- void SetNetworkConditionsForTest(
- net::NetworkChangeNotifier::ConnectionType connection) {
- coordinator()->SetNetworkConditionsForTest(connection);
- }
-
void SetEffectiveConnectionTypeForTest(net::EffectiveConnectionType type) {
network_quality_estimator_->SetEffectiveConnectionTypeForTest(type);
}
void SetNetworkConnected(bool connected) {
if (connected) {
- SetNetworkConditionsForTest(
- net::NetworkChangeNotifier::ConnectionType::CONNECTION_3G);
+ DeviceConditions device_conditions(
+ !kPowerRequired, kBatteryPercentageHigh,
+ net::NetworkChangeNotifier::CONNECTION_3G);
+ SetDeviceConditionsForTest(device_conditions);
SetEffectiveConnectionTypeForTest(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G);
} else {
- SetNetworkConditionsForTest(
- net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE);
+ DeviceConditions device_conditions(
+ !kPowerRequired, kBatteryPercentageHigh,
+ net::NetworkChangeNotifier::CONNECTION_NONE);
+ SetDeviceConditionsForTest(device_conditions);
SetEffectiveConnectionTypeForTest(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_OFFLINE);
}
@@ -357,17 +219,19 @@ class RequestCoordinatorTest
else
coordinator_->disabled_requests_.clear();
- coordinator_->RequestNotPicked(non_user_requested_tasks_remaining);
+ coordinator_->RequestNotPicked(non_user_requested_tasks_remaining, false);
}
void SetDeviceConditionsForTest(DeviceConditions device_conditions) {
coordinator_->SetDeviceConditionsForTest(device_conditions);
}
- void WaitForCallback() {
- waiter_.Wait();
+ DeviceConditions* GetDeviceConditions() {
+ return coordinator()->current_conditions_.get();
}
+ void WaitForCallback() { waiter_.Wait(); }
+
void AdvanceClockBy(base::TimeDelta delta) {
task_runner_->FastForwardBy(delta);
}
@@ -386,17 +250,17 @@ class RequestCoordinatorTest
DeviceConditions device_conditions() { return device_conditions_; }
- base::Callback<void(bool)> immediate_callback() {
- return immediate_callback_;
+ base::Callback<void(bool)> processing_callback() {
+ return processing_callback_;
}
base::Callback<void(bool)> waiting_callback() { return waiting_callback_; }
- bool immediate_schedule_callback_called() const {
- return immediate_schedule_callback_called_;
+ bool processing_callback_called() const {
+ return processing_callback_called_;
}
- bool immediate_schedule_callback_result() const {
- return immediate_schedule_callback_result_;
+ bool processing_callback_result() const {
+ return processing_callback_result_;
}
const base::HistogramTester& histograms() const { return histogram_tester_; }
@@ -407,15 +271,15 @@ class RequestCoordinatorTest
std::vector<std::unique_ptr<SavePageRequest>> last_requests_;
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
- std::unique_ptr<NetworkQualityEstimatorStub> network_quality_estimator_;
+ std::unique_ptr<NetworkQualityProviderStub> network_quality_estimator_;
std::unique_ptr<RequestCoordinator> coordinator_;
OfflinerStub* offliner_;
base::WaitableEvent waiter_;
ObserverStub observer_;
- bool immediate_schedule_callback_called_;
- bool immediate_schedule_callback_result_;
+ bool processing_callback_called_;
+ bool processing_callback_result_;
DeviceConditions device_conditions_;
- base::Callback<void(bool)> immediate_callback_;
+ base::Callback<void(bool)> processing_callback_;
base::Callback<void(bool)> waiting_callback_;
base::HistogramTester histogram_tester_;
};
@@ -427,8 +291,8 @@ RequestCoordinatorTest::RequestCoordinatorTest()
offliner_(nullptr),
waiter_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED),
- immediate_schedule_callback_called_(false),
- immediate_schedule_callback_result_(false),
+ processing_callback_called_(false),
+ processing_callback_result_(false),
device_conditions_(!kPowerRequired,
kBatteryPercentageHigh,
net::NetworkChangeNotifier::CONNECTION_3G) {}
@@ -437,33 +301,28 @@ RequestCoordinatorTest::~RequestCoordinatorTest() {}
void RequestCoordinatorTest::SetUp() {
std::unique_ptr<OfflinerPolicy> policy(new OfflinerPolicy());
- std::unique_ptr<OfflinerFactory> offliner_factory(new OfflinerFactoryStub());
+ std::unique_ptr<OfflinerStub> offliner(new OfflinerStub());
// Save the offliner for use by the tests.
- offliner_ = reinterpret_cast<OfflinerStub*>(
- offliner_factory->GetOffliner(policy.get()));
- std::unique_ptr<RequestQueueInMemoryStore>
- store(new RequestQueueInMemoryStore());
+ offliner_ = reinterpret_cast<OfflinerStub*>(offliner.get());
+ std::unique_ptr<RequestQueueInMemoryStore> store(
+ new RequestQueueInMemoryStore());
std::unique_ptr<RequestQueue> queue(new RequestQueue(std::move(store)));
std::unique_ptr<Scheduler> scheduler_stub(new SchedulerStub());
- network_quality_estimator_.reset(new NetworkQualityEstimatorStub());
+ network_quality_estimator_.reset(new NetworkQualityProviderStub());
coordinator_.reset(new RequestCoordinator(
- std::move(policy), std::move(offliner_factory), std::move(queue),
+ std::move(policy), std::move(offliner), std::move(queue),
std::move(scheduler_stub), network_quality_estimator_.get()));
coordinator_->AddObserver(&observer_);
SetNetworkConnected(true);
- std::unique_ptr<PickRequestTaskFactory> picker_factory(
- new PickRequestTaskFactory(
- coordinator_->policy(),
- static_cast<RequestNotifier*>(coordinator_.get()),
- coordinator_->GetLogger()));
- coordinator_->queue()->SetPickerFactory(std::move(picker_factory));
- immediate_callback_ =
- base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction,
+ processing_callback_ =
+ base::Bind(&RequestCoordinatorTest::ProcessingCallbackFunction,
base::Unretained(this));
// Override the normal immediate callback with a wait releasing callback.
waiting_callback_ = base::Bind(
&RequestCoordinatorTest::WaitingCallbackFunction, base::Unretained(this));
SetDeviceConditionsForTest(device_conditions_);
+ // Ensure not low-end device so immediate start can happen for most tests.
+ SetIsLowEndDeviceForTest(false);
EnableOfflinerCallback(true);
}
@@ -505,7 +364,7 @@ void RequestCoordinatorTest::SetupForOfflinerDoneCallbackTest(
// Override the processing callback for test visiblity.
base::Callback<void(bool)> callback =
- base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction,
+ base::Bind(&RequestCoordinatorTest::ProcessingCallbackFunction,
base::Unretained(this));
coordinator()->SetProcessingCallbackForTest(callback);
@@ -516,7 +375,8 @@ void RequestCoordinatorTest::SetupForOfflinerDoneCallbackTest(
}
void RequestCoordinatorTest::SendOfflinerDoneCallback(
- const SavePageRequest& request, Offliner::RequestStatus status) {
+ const SavePageRequest& request,
+ Offliner::RequestStatus status) {
// Using the fact that the test class is a friend, call to the callback
coordinator_->OfflinerDoneCallback(request, status);
}
@@ -539,12 +399,15 @@ SavePageRequest RequestCoordinatorTest::AddRequest2() {
return request2;
}
-TEST_F(RequestCoordinatorTest, StartProcessingWithNoRequests) {
- EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
- immediate_callback()));
+TEST_F(RequestCoordinatorTest, StartScheduledProcessingWithNoRequests) {
+ // Set low-end device status to actual status.
+ SetIsLowEndDeviceForTest(base::SysInfo::IsLowEndDevice());
+
+ EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(),
+ processing_callback()));
PumpLoop();
- EXPECT_TRUE(immediate_schedule_callback_called());
+ EXPECT_TRUE(processing_callback_called());
// Verify queue depth UMA for starting scheduled processing on empty queue.
if (base::SysInfo::IsLowEndDevice()) {
@@ -557,40 +420,95 @@ TEST_F(RequestCoordinatorTest, StartProcessingWithNoRequests) {
}
}
-TEST_F(RequestCoordinatorTest, StartProcessingWithRequestInProgress) {
+TEST_F(RequestCoordinatorTest, StartScheduledProcessingWithRequestInProgress) {
// Start processing for this request.
- EXPECT_NE(
- coordinator()->SavePageLater(
- kUrl1, kClientId1, kUserRequested,
- RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
+ EXPECT_NE(coordinator()->SavePageLater(
+ kUrl1, kClientId1, kUserRequested,
+ RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+ 0);
// Ensure that the forthcoming request does not finish - we simulate it being
// in progress by asking it to skip making the completion callback.
EnableOfflinerCallback(false);
// Sending the request to the offliner should make it busy.
- EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
- immediate_callback()));
+ EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(),
+ processing_callback()));
+ PumpLoop();
+
+ EXPECT_TRUE(is_busy());
+ // Since the offliner is disabled, this callback should not be called.
+ EXPECT_FALSE(processing_callback_called());
+
+ // Now trying to start processing should return false since already busy.
+ EXPECT_FALSE(coordinator()->StartScheduledProcessing(device_conditions(),
+ processing_callback()));
+}
+
+TEST_F(RequestCoordinatorTest, StartImmediateProcessingWithNoRequests) {
+ EXPECT_TRUE(coordinator()->StartImmediateProcessing(processing_callback()));
+ PumpLoop();
+
+ EXPECT_TRUE(processing_callback_called());
+
+ histograms().ExpectBucketCount("OfflinePages.Background.ImmediateStartStatus",
+ 0 /* STARTED */, 1);
+}
+
+TEST_F(RequestCoordinatorTest, StartImmediateProcessingOnSvelte) {
+ // Set as low-end device to verfiy immediate processing will not start.
+ SetIsLowEndDeviceForTest(true);
+
+ EXPECT_FALSE(coordinator()->StartImmediateProcessing(processing_callback()));
+ histograms().ExpectBucketCount("OfflinePages.Background.ImmediateStartStatus",
+ 5 /* NOT_STARTED_ON_SVELTE */, 1);
+}
+
+TEST_F(RequestCoordinatorTest, StartImmediateProcessingWhenDisconnected) {
+ DeviceConditions disconnected_conditions(
+ !kPowerRequired, kBatteryPercentageHigh,
+ net::NetworkChangeNotifier::CONNECTION_NONE);
+ SetDeviceConditionsForTest(disconnected_conditions);
+ EXPECT_FALSE(coordinator()->StartImmediateProcessing(processing_callback()));
+ histograms().ExpectBucketCount("OfflinePages.Background.ImmediateStartStatus",
+ 3 /* NO_CONNECTION */, 1);
+}
+
+TEST_F(RequestCoordinatorTest, StartImmediateProcessingWithRequestInProgress) {
+ // Start processing for this request.
+ EXPECT_NE(coordinator()->SavePageLater(
+ kUrl1, kClientId1, kUserRequested,
+ RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+ 0);
+
+ // Disable the automatic offliner callback.
+ EnableOfflinerCallback(false);
+
+ // Sending the request to the offliner should make it busy.
+ EXPECT_TRUE(coordinator()->StartImmediateProcessing(processing_callback()));
PumpLoop();
EXPECT_TRUE(is_busy());
// Since the offliner is disabled, this callback should not be called.
- EXPECT_FALSE(immediate_schedule_callback_called());
+ EXPECT_FALSE(processing_callback_called());
+
+ // Now trying to start processing should return false since already busy.
+ EXPECT_FALSE(coordinator()->StartImmediateProcessing(processing_callback()));
- // Now trying to start processing on another request should return false.
- EXPECT_FALSE(coordinator()->StartProcessing(device_conditions(),
- immediate_callback()));
+ histograms().ExpectBucketCount("OfflinePages.Background.ImmediateStartStatus",
+ 1 /* BUSY */, 1);
}
TEST_F(RequestCoordinatorTest, SavePageLater) {
// The user-requested request which gets processed by SavePageLater
// would invoke user request callback.
- coordinator()->SetImmediateScheduleCallbackForTest(immediate_callback());
+ coordinator()->SetInternalStartProcessingCallbackForTest(
+ processing_callback());
- EXPECT_NE(
- coordinator()->SavePageLater(
- kUrl1, kClientId1, kUserRequested,
- RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
+ EXPECT_NE(coordinator()->SavePageLater(
+ kUrl1, kClientId1, kUserRequested,
+ RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+ 0);
// Expect that a request got placed on the queue.
coordinator()->queue()->GetRequests(base::Bind(
@@ -598,7 +516,7 @@ TEST_F(RequestCoordinatorTest, SavePageLater) {
// Wait for callbacks to finish, both request queue and offliner.
PumpLoop();
- EXPECT_TRUE(immediate_schedule_callback_called());
+ EXPECT_TRUE(processing_callback_called());
// Check the request queue is as expected.
EXPECT_EQ(1UL, last_requests().size());
@@ -612,26 +530,24 @@ TEST_F(RequestCoordinatorTest, SavePageLater) {
EXPECT_EQ(coordinator()
->GetTriggerConditions(last_requests()[0]->user_requested())
.minimum_battery_percentage,
- scheduler_stub->conditions()->minimum_battery_percentage);
+ scheduler_stub->trigger_conditions()->minimum_battery_percentage);
// Check that the observer got the notification that a page is available
EXPECT_TRUE(observer().added_called());
// Verify queue depth UMA for starting immediate processing.
- if (base::SysInfo::IsLowEndDevice()) {
- histograms().ExpectBucketCount(
- "OfflinePages.Background.ImmediateStart.AvailableRequestCount.Svelte",
- 1, 1);
- } else {
- histograms().ExpectBucketCount(
- "OfflinePages.Background.ImmediateStart.AvailableRequestCount", 1, 1);
- }
+ histograms().ExpectBucketCount(
+ "OfflinePages.Background.ImmediateStart.AvailableRequestCount", 1, 1);
}
TEST_F(RequestCoordinatorTest, SavePageLaterFailed) {
+ // Set low-end device status to actual status.
+ SetIsLowEndDeviceForTest(base::SysInfo::IsLowEndDevice());
+
// The user-requested request which gets processed by SavePageLater
// would invoke user request callback.
- coordinator()->SetImmediateScheduleCallbackForTest(immediate_callback());
+ coordinator()->SetInternalStartProcessingCallbackForTest(
+ processing_callback());
EXPECT_TRUE(
coordinator()->SavePageLater(
@@ -639,20 +555,19 @@ TEST_F(RequestCoordinatorTest, SavePageLaterFailed) {
RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER) != 0);
// Expect that a request got placed on the queue.
- coordinator()->queue()->GetRequests(
- base::Bind(&RequestCoordinatorTest::GetRequestsDone,
- base::Unretained(this)));
+ coordinator()->queue()->GetRequests(base::Bind(
+ &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
// Wait for callbacks to finish, both request queue and offliner.
PumpLoop();
// On low-end devices the callback will be called with false since the
// processing started but failed due to svelte devices.
- EXPECT_TRUE(immediate_schedule_callback_called());
+ EXPECT_TRUE(processing_callback_called());
if (base::SysInfo::IsLowEndDevice()) {
- EXPECT_FALSE(immediate_schedule_callback_result());
+ EXPECT_FALSE(processing_callback_result());
} else {
- EXPECT_TRUE(immediate_schedule_callback_result());
+ EXPECT_TRUE(processing_callback_result());
}
// Check the request queue is as expected.
@@ -661,13 +576,13 @@ TEST_F(RequestCoordinatorTest, SavePageLaterFailed) {
EXPECT_EQ(kClientId1, last_requests().at(0)->client_id());
// Expect that the scheduler got notified.
- SchedulerStub* scheduler_stub = reinterpret_cast<SchedulerStub*>(
- coordinator()->scheduler());
+ SchedulerStub* scheduler_stub =
+ reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
EXPECT_TRUE(scheduler_stub->schedule_called());
EXPECT_EQ(coordinator()
->GetTriggerConditions(last_requests()[0]->user_requested())
.minimum_battery_percentage,
- scheduler_stub->conditions()->minimum_battery_percentage);
+ scheduler_stub->trigger_conditions()->minimum_battery_percentage);
// Check that the observer got the notification that a page is available
EXPECT_TRUE(observer().added_called());
@@ -675,20 +590,19 @@ TEST_F(RequestCoordinatorTest, SavePageLaterFailed) {
TEST_F(RequestCoordinatorTest, OfflinerDoneRequestSucceeded) {
// Add a request to the queue, wait for callbacks to finish.
- offline_pages::SavePageRequest request(
- kRequestId1, kUrl1, kClientId1, base::Time::Now(), kUserRequested);
+ offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+ base::Time::Now(), kUserRequested);
SetupForOfflinerDoneCallbackTest(&request);
// Call the OfflinerDoneCallback to simulate the page being completed, wait
// for callbacks.
SendOfflinerDoneCallback(request, Offliner::RequestStatus::SAVED);
PumpLoop();
- EXPECT_TRUE(immediate_schedule_callback_called());
+ EXPECT_TRUE(processing_callback_called());
// Verify the request gets removed from the queue, and wait for callbacks.
- coordinator()->queue()->GetRequests(
- base::Bind(&RequestCoordinatorTest::GetRequestsDone,
- base::Unretained(this)));
+ coordinator()->queue()->GetRequests(base::Bind(
+ &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
PumpLoop();
// We should not find any requests in the queue anymore.
@@ -719,7 +633,7 @@ TEST_F(RequestCoordinatorTest, OfflinerDoneRequestSucceededButLostNetwork) {
// for callbacks.
SendOfflinerDoneCallback(request, Offliner::RequestStatus::SAVED);
PumpLoop();
- EXPECT_TRUE(immediate_schedule_callback_called());
+ EXPECT_TRUE(processing_callback_called());
// Verify not busy with 2nd request (since no connection).
EXPECT_FALSE(is_busy());
@@ -733,8 +647,8 @@ TEST_F(RequestCoordinatorTest, OfflinerDoneRequestSucceededButLostNetwork) {
TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailed) {
// Add a request to the queue, wait for callbacks to finish.
- offline_pages::SavePageRequest request(
- kRequestId1, kUrl1, kClientId1, base::Time::Now(), kUserRequested);
+ offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+ base::Time::Now(), kUserRequested);
request.set_completed_attempt_count(kMaxCompletedTries - 1);
SetupForOfflinerDoneCallbackTest(&request);
// Stop processing before completing the second request on the queue.
@@ -746,28 +660,26 @@ TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailed) {
// Call the OfflinerDoneCallback to simulate the request failed, wait
// for callbacks.
- SendOfflinerDoneCallback(request,
- Offliner::RequestStatus::PRERENDERING_FAILED);
+ SendOfflinerDoneCallback(request, Offliner::RequestStatus::LOADING_FAILED);
PumpLoop();
- // For retriable failure, processing should stop and scheduler callback
- // called (so that request can be retried first next processing window).
- EXPECT_TRUE(immediate_schedule_callback_called());
+ // For retriable failure, processing should continue to 2nd request so
+ // no scheduler callback yet.
+ EXPECT_FALSE(processing_callback_called());
- // TODO(dougarnett): Consider injecting mock RequestPicker for this test
- // and verifying that there is no attempt to pick another request following
- // this failure code.
+ // Busy processing 2nd request.
+ EXPECT_TRUE(is_busy());
- coordinator()->queue()->GetRequests(
- base::Bind(&RequestCoordinatorTest::GetRequestsDone,
- base::Unretained(this)));
+ coordinator()->queue()->GetRequests(base::Bind(
+ &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
PumpLoop();
// Now just one request in the queue since failed request removed
// (max number of attempts exceeded).
EXPECT_EQ(1UL, last_requests().size());
// Check that the observer got the notification that we failed (and the
- // subsequent notification that the request was removed).
+ // subsequent notification that the request was removed) since we exceeded
+ // retry count.
EXPECT_TRUE(observer().completed_called());
EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED,
observer().last_status());
@@ -786,17 +698,16 @@ TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailedNoRetryFailure) {
// Call the OfflinerDoneCallback to simulate the request failed, wait
// for callbacks.
- SendOfflinerDoneCallback(
- request, Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY);
+ SendOfflinerDoneCallback(request,
+ Offliner::RequestStatus::LOADING_FAILED_NO_RETRY);
PumpLoop();
// For no retry failure, processing should continue to 2nd request so
// no scheduler callback yet.
- EXPECT_FALSE(immediate_schedule_callback_called());
+ EXPECT_FALSE(processing_callback_called());
- // TODO(dougarnett): Consider injecting mock RequestPicker for this test
- // and verifying that there is as attempt to pick another request following
- // this non-retryable failure code.
+ // Busy processing 2nd request.
+ EXPECT_TRUE(is_busy());
coordinator()->queue()->GetRequests(base::Bind(
&RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
@@ -807,14 +718,46 @@ TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailedNoRetryFailure) {
// Check that the observer got the notification that we failed (and the
// subsequent notification that the request was removed).
EXPECT_TRUE(observer().completed_called());
- EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::PRERENDER_FAILURE,
+ EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::LOADING_FAILURE,
observer().last_status());
}
+TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailedNoNextFailure) {
+ // Add a request to the queue, wait for callbacks to finish.
+ offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+ base::Time::Now(), kUserRequested);
+ SetupForOfflinerDoneCallbackTest(&request);
+ EnableOfflinerCallback(false);
+
+ // Add second request to the queue to check handling when first fails.
+ AddRequest2();
+ PumpLoop();
+
+ // Call the OfflinerDoneCallback to simulate the request failed, wait
+ // for callbacks.
+ SendOfflinerDoneCallback(request,
+ Offliner::RequestStatus::LOADING_FAILED_NO_NEXT);
+ PumpLoop();
+
+ // For no next failure, processing should not continue to 2nd request so
+ // expect scheduler callback.
+ EXPECT_TRUE(processing_callback_called());
+
+ // Not busy for NO_NEXT failure.
+ EXPECT_FALSE(is_busy());
+
+ coordinator()->queue()->GetRequests(base::Bind(
+ &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+ PumpLoop();
+
+ // Both requests still in queue.
+ EXPECT_EQ(2UL, last_requests().size());
+}
+
TEST_F(RequestCoordinatorTest, OfflinerDoneForegroundCancel) {
// Add a request to the queue, wait for callbacks to finish.
- offline_pages::SavePageRequest request(
- kRequestId1, kUrl1, kClientId1, base::Time::Now(), kUserRequested);
+ offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+ base::Time::Now(), kUserRequested);
SetupForOfflinerDoneCallbackTest(&request);
// Call the OfflinerDoneCallback to simulate the request failed, wait
@@ -822,7 +765,7 @@ TEST_F(RequestCoordinatorTest, OfflinerDoneForegroundCancel) {
SendOfflinerDoneCallback(request,
Offliner::RequestStatus::FOREGROUND_CANCELED);
PumpLoop();
- EXPECT_TRUE(immediate_schedule_callback_called());
+ EXPECT_TRUE(processing_callback_called());
// Verify the request is not removed from the queue, and wait for callbacks.
coordinator()->queue()->GetRequests(base::Bind(
@@ -843,10 +786,9 @@ TEST_F(RequestCoordinatorTest, OfflinerDonePrerenderingCancel) {
// Call the OfflinerDoneCallback to simulate the request failed, wait
// for callbacks.
- SendOfflinerDoneCallback(request,
- Offliner::RequestStatus::PRERENDERING_CANCELED);
+ SendOfflinerDoneCallback(request, Offliner::RequestStatus::LOADING_CANCELED);
PumpLoop();
- EXPECT_TRUE(immediate_schedule_callback_called());
+ EXPECT_TRUE(processing_callback_called());
// Verify the request is not removed from the queue, and wait for callbacks.
coordinator()->queue()->GetRequests(base::Bind(
@@ -864,7 +806,8 @@ TEST_F(RequestCoordinatorTest, OfflinerDonePrerenderingCancel) {
// If one item completes, and there are no more user requeted items left,
// we should make a scheduler entry for a non-user requested item.
TEST_F(RequestCoordinatorTest, RequestNotPickedDisabledItemsRemain) {
- coordinator()->StartProcessing(device_conditions(), immediate_callback());
+ coordinator()->StartScheduledProcessing(device_conditions(),
+ processing_callback());
EXPECT_TRUE(is_starting());
// Call RequestNotPicked, simulating a request on the disabled list.
@@ -884,7 +827,8 @@ TEST_F(RequestCoordinatorTest, RequestNotPickedDisabledItemsRemain) {
// If one item completes, and there are no more user requeted items left,
// we should make a scheduler entry for a non-user requested item.
TEST_F(RequestCoordinatorTest, RequestNotPickedNonUserRequestedItemsRemain) {
- coordinator()->StartProcessing(device_conditions(), immediate_callback());
+ coordinator()->StartScheduledProcessing(device_conditions(),
+ processing_callback());
EXPECT_TRUE(is_starting());
// Call RequestNotPicked, and make sure we pick schedule a task for non user
@@ -893,7 +837,7 @@ TEST_F(RequestCoordinatorTest, RequestNotPickedNonUserRequestedItemsRemain) {
PumpLoop();
EXPECT_FALSE(is_starting());
- EXPECT_TRUE(immediate_schedule_callback_called());
+ EXPECT_TRUE(processing_callback_called());
// The scheduler should have been called to schedule the non-user requested
// task.
@@ -901,7 +845,8 @@ TEST_F(RequestCoordinatorTest, RequestNotPickedNonUserRequestedItemsRemain) {
reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
EXPECT_TRUE(scheduler_stub->schedule_called());
EXPECT_TRUE(scheduler_stub->unschedule_called());
- const Scheduler::TriggerConditions* conditions = scheduler_stub->conditions();
+ const Scheduler::TriggerConditions* conditions =
+ scheduler_stub->trigger_conditions();
EXPECT_EQ(conditions->require_power_connected,
coordinator()->policy()->PowerRequired(!kUserRequested));
EXPECT_EQ(
@@ -930,7 +875,8 @@ TEST_F(RequestCoordinatorTest, SchedulerGetsLeastRestrictiveConditions) {
// priority.
SchedulerStub* scheduler_stub =
reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
- const Scheduler::TriggerConditions* conditions = scheduler_stub->conditions();
+ const Scheduler::TriggerConditions* conditions =
+ scheduler_stub->trigger_conditions();
EXPECT_TRUE(scheduler_stub->schedule_called());
EXPECT_EQ(conditions->require_power_connected,
coordinator()->policy()->PowerRequired(kUserRequested));
@@ -940,32 +886,35 @@ TEST_F(RequestCoordinatorTest, SchedulerGetsLeastRestrictiveConditions) {
coordinator()->policy()->UnmeteredNetworkRequired(kUserRequested));
}
-TEST_F(RequestCoordinatorTest, StartProcessingWithLoadingDisabled) {
+TEST_F(RequestCoordinatorTest, StartScheduledProcessingWithLoadingDisabled) {
// Add a request to the queue, wait for callbacks to finish.
AddRequest1();
PumpLoop();
DisableLoading();
- EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
- immediate_callback()));
+ EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(),
+ processing_callback()));
// Let the async callbacks in the request coordinator run.
PumpLoop();
- EXPECT_TRUE(immediate_schedule_callback_called());
+ EXPECT_TRUE(processing_callback_called());
EXPECT_FALSE(is_starting());
- EXPECT_EQ(Offliner::PRERENDERING_NOT_STARTED, last_offlining_status());
+ EXPECT_EQ(Offliner::LOADING_NOT_ACCEPTED, last_offlining_status());
}
+// TODO(dougarnett): Add StartScheduledProcessing test for QUEUE_UPDATE_FAILED.
+
// This tests a StopProcessing call before we have actually started the
-// prerenderer.
-TEST_F(RequestCoordinatorTest, StartProcessingThenStopProcessingImmediately) {
+// offliner.
+TEST_F(RequestCoordinatorTest,
+ StartScheduledProcessingThenStopProcessingImmediately) {
// Add a request to the queue, wait for callbacks to finish.
AddRequest1();
PumpLoop();
- EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
- immediate_callback()));
+ EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(),
+ processing_callback()));
EXPECT_TRUE(is_starting());
// Now, quick, before it can do much (we haven't called PumpLoop), cancel it.
@@ -973,7 +922,7 @@ TEST_F(RequestCoordinatorTest, StartProcessingThenStopProcessingImmediately) {
// Let the async callbacks in the request coordinator run.
PumpLoop();
- EXPECT_TRUE(immediate_schedule_callback_called());
+ EXPECT_TRUE(processing_callback_called());
EXPECT_FALSE(is_starting());
@@ -987,7 +936,8 @@ TEST_F(RequestCoordinatorTest, StartProcessingThenStopProcessingImmediately) {
}
// This tests a StopProcessing call after the prerenderer has been started.
-TEST_F(RequestCoordinatorTest, StartProcessingThenStopProcessingLater) {
+TEST_F(RequestCoordinatorTest,
+ StartScheduledProcessingThenStopProcessingLater) {
// Add a request to the queue, wait for callbacks to finish.
AddRequest1();
PumpLoop();
@@ -995,8 +945,8 @@ TEST_F(RequestCoordinatorTest, StartProcessingThenStopProcessingLater) {
// Ensure the start processing request stops before the completion callback.
EnableOfflinerCallback(false);
- EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
- immediate_callback()));
+ EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(),
+ processing_callback()));
EXPECT_TRUE(is_starting());
// Let all the async parts of the start processing pipeline run to completion.
@@ -1008,7 +958,7 @@ TEST_F(RequestCoordinatorTest, StartProcessingThenStopProcessingLater) {
observer().Clear();
// Since the offliner is disabled, this callback should not be called.
- EXPECT_FALSE(immediate_schedule_callback_called());
+ EXPECT_FALSE(processing_callback_called());
// Coordinator should now be busy.
EXPECT_TRUE(is_busy());
@@ -1046,13 +996,13 @@ TEST_F(RequestCoordinatorTest, RemoveInflightRequest) {
// Ensure the start processing request stops before the completion callback.
EnableOfflinerCallback(false);
- EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
- immediate_callback()));
+ EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(),
+ processing_callback()));
// Let all the async parts of the start processing pipeline run to completion.
PumpLoop();
// Since the offliner is disabled, this callback should not be called.
- EXPECT_FALSE(immediate_schedule_callback_called());
+ EXPECT_FALSE(processing_callback_called());
// Remove the request while it is processing.
std::vector<int64_t> request_ids{kRequestId1};
@@ -1117,15 +1067,14 @@ TEST_F(RequestCoordinatorTest, EnableForOffliner) {
TEST_F(RequestCoordinatorTest, WatchdogTimeoutForScheduledProcessing) {
// Build a request to use with the pre-renderer, and put it on the queue.
- offline_pages::SavePageRequest request(
- kRequestId1, kUrl1, kClientId1, base::Time::Now(), kUserRequested);
+ offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+ base::Time::Now(), kUserRequested);
// Set request to allow one more completed attempt.
int max_tries = coordinator()->policy()->GetMaxCompletedTries();
request.set_completed_attempt_count(max_tries - 1);
coordinator()->queue()->AddRequest(
- request,
- base::Bind(&RequestCoordinatorTest::AddRequestDone,
- base::Unretained(this)));
+ request, base::Bind(&RequestCoordinatorTest::AddRequestDone,
+ base::Unretained(this)));
PumpLoop();
// Ensure that the new request does not finish - we simulate it being
@@ -1133,15 +1082,16 @@ TEST_F(RequestCoordinatorTest, WatchdogTimeoutForScheduledProcessing) {
EnableOfflinerCallback(false);
// Sending the request to the offliner.
- EXPECT_TRUE(
- coordinator()->StartProcessing(device_conditions(), waiting_callback()));
+ EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(),
+ waiting_callback()));
PumpLoop();
// Advance the mock clock far enough to cause a watchdog timeout
AdvanceClockBy(base::TimeDelta::FromSeconds(
coordinator()
->policy()
- ->GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds() + 1));
+ ->GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds() +
+ 1));
PumpLoop();
// Wait for timeout to expire. Use a TaskRunner with a DelayedTaskRunner
@@ -1156,17 +1106,14 @@ TEST_F(RequestCoordinatorTest, WatchdogTimeoutForScheduledProcessing) {
}
TEST_F(RequestCoordinatorTest, WatchdogTimeoutForImmediateProcessing) {
- // If low end device, pretend it is not so that immediate start happens.
- SetIsLowEndDeviceForTest(false);
-
// Ensure that the new request does not finish - we simulate it being
// in progress by asking it to skip making the completion callback.
EnableOfflinerCallback(false);
- EXPECT_NE(
- coordinator()->SavePageLater(
- kUrl1, kClientId1, kUserRequested,
- RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
+ EXPECT_NE(coordinator()->SavePageLater(
+ kUrl1, kClientId1, kUserRequested,
+ RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+ 0);
PumpLoop();
// Verify that immediate start from adding the request did happen.
@@ -1176,7 +1123,8 @@ TEST_F(RequestCoordinatorTest, WatchdogTimeoutForImmediateProcessing) {
AdvanceClockBy(base::TimeDelta::FromSeconds(
coordinator()
->policy()
- ->GetSinglePageTimeLimitForImmediateLoadInSeconds() - 1));
+ ->GetSinglePageTimeLimitForImmediateLoadInSeconds() -
+ 1));
PumpLoop();
// Verify still busy.
@@ -1205,8 +1153,8 @@ TEST_F(RequestCoordinatorTest, TimeBudgetExceeded) {
PumpLoop();
// Sending the request to the offliner.
- EXPECT_TRUE(
- coordinator()->StartProcessing(device_conditions(), waiting_callback()));
+ EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(),
+ waiting_callback()));
PumpLoop();
// Advance the mock clock far enough to exceed our time budget.
@@ -1218,9 +1166,8 @@ TEST_F(RequestCoordinatorTest, TimeBudgetExceeded) {
// TryNextRequest should decide that there is no more work to be done,
// and call back to the scheduler, even though there is another request in the
// queue. Both requests should be left in the queue.
- coordinator()->queue()->GetRequests(
- base::Bind(&RequestCoordinatorTest::GetRequestsDone,
- base::Unretained(this)));
+ coordinator()->queue()->GetRequests(base::Bind(
+ &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
PumpLoop();
// We should find two requests in the queue.
@@ -1234,12 +1181,12 @@ TEST_F(RequestCoordinatorTest, TryNextRequestWithNoNetwork) {
AddRequest2();
PumpLoop();
- // Set up for the call to StartProcessing.
+ // Set up for the call to StartScheduledProcessing.
EnableOfflinerCallback(false);
// Sending the request to the offliner.
- EXPECT_TRUE(
- coordinator()->StartProcessing(device_conditions(), waiting_callback()));
+ EXPECT_TRUE(coordinator()->StartScheduledProcessing(device_conditions(),
+ waiting_callback()));
PumpLoop();
EXPECT_TRUE(coordinator()->is_busy());
@@ -1292,6 +1239,9 @@ TEST_F(RequestCoordinatorTest, GetAllRequests) {
#define MAYBE_PauseAndResumeObserver PauseAndResumeObserver
#endif
TEST_F(RequestCoordinatorTest, MAYBE_PauseAndResumeObserver) {
+ // Set low-end device status to actual status.
+ SetIsLowEndDeviceForTest(base::SysInfo::IsLowEndDevice());
+
// Add a request to the queue.
AddRequest1();
PumpLoop();
@@ -1351,19 +1301,13 @@ TEST_F(RequestCoordinatorTest,
// Turn off the callback so that the request stops before processing in
// PumpLoop.
EnableOfflinerCallback(false);
- EXPECT_NE(
- coordinator()->SavePageLater(
- kUrl1, kClientId1, kUserRequested,
- RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
+ EXPECT_NE(coordinator()->SavePageLater(
+ kUrl1, kClientId1, kUserRequested,
+ RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+ 0);
PumpLoop();
- // Now whether processing triggered immediately depends on whether test
- // is run on svelte device or not.
- if (base::SysInfo::IsLowEndDevice()) {
- EXPECT_FALSE(is_busy());
- } else {
- EXPECT_TRUE(is_busy());
- }
+ EXPECT_TRUE(is_busy());
}
TEST_F(RequestCoordinatorTest,
@@ -1404,9 +1348,6 @@ TEST_F(RequestCoordinatorTest,
}
TEST_F(RequestCoordinatorTest, SavePageDoesntStartProcessingWhenDisconnected) {
- // If low end device, pretend it is not so that immediate start allowed.
- SetIsLowEndDeviceForTest(false);
-
SetNetworkConnected(false);
EnableOfflinerCallback(false);
EXPECT_NE(
@@ -1426,8 +1367,9 @@ TEST_F(RequestCoordinatorTest, SavePageDoesntStartProcessingWhenDisconnected) {
TEST_F(RequestCoordinatorTest,
SavePageDoesStartProcessingWhenPoorlyConnected) {
// Set specific network type for 2G with poor effective connection.
- SetNetworkConditionsForTest(
- net::NetworkChangeNotifier::ConnectionType::CONNECTION_2G);
+ DeviceConditions device_conditions(!kPowerRequired, kBatteryPercentageHigh,
+ net::NetworkChangeNotifier::CONNECTION_2G);
+ SetDeviceConditionsForTest(device_conditions);
SetEffectiveConnectionTypeForTest(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
@@ -1435,10 +1377,10 @@ TEST_F(RequestCoordinatorTest,
// PumpLoop.
EnableOfflinerCallback(false);
- EXPECT_NE(
- coordinator()->SavePageLater(
- kUrl1, kClientId1, kUserRequested,
- RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
+ EXPECT_NE(coordinator()->SavePageLater(
+ kUrl1, kClientId1, kUserRequested,
+ RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+ 0);
PumpLoop();
EXPECT_TRUE(is_busy());
}
@@ -1480,13 +1422,7 @@ TEST_F(RequestCoordinatorTest,
EXPECT_FALSE(is_busy());
PumpLoop();
- // Now whether processing triggered immediately depends on whether test
- // is run on svelte device or not.
- if (base::SysInfo::IsLowEndDevice()) {
- EXPECT_FALSE(is_busy());
- } else {
- EXPECT_TRUE(is_busy());
- }
+ EXPECT_TRUE(is_busy());
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/request_notifier.h b/chromium/components/offline_pages/core/background/request_notifier.h
index 611eac3c4a3..ef3c62f69f7 100644
--- a/chromium/components/offline_pages/background/request_notifier.h
+++ b/chromium/components/offline_pages/core/background/request_notifier.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_NOTIFIER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_NOTIFIER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_NOTIFIER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_NOTIFIER_H_
namespace offline_pages {
@@ -17,8 +17,8 @@ class RequestNotifier {
// GENERATED_JAVA_ENUM_PACKAGE:org.chromium.components.offlinepages
enum class BackgroundSavePageResult {
SUCCESS,
- PRERENDER_FAILURE,
- PRERENDER_CANCELED,
+ LOADING_FAILURE,
+ LOADING_CANCELED,
FOREGROUND_CANCELED,
SAVE_FAILED,
EXPIRED,
@@ -42,4 +42,4 @@ class RequestNotifier {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_NOTIFIER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_NOTIFIER_H_
diff --git a/chromium/components/offline_pages/background/request_queue.cc b/chromium/components/offline_pages/core/background/request_queue.cc
index d25a72f3f30..406f0ae0ce4 100644
--- a/chromium/components/offline_pages/background/request_queue.cc
+++ b/chromium/components/offline_pages/core/background/request_queue.cc
@@ -2,22 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/request_queue.h"
+#include "components/offline_pages/core/background/request_queue.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/change_requests_state_task.h"
-#include "components/offline_pages/background/mark_attempt_aborted_task.h"
-#include "components/offline_pages/background/mark_attempt_completed_task.h"
-#include "components/offline_pages/background/mark_attempt_started_task.h"
-#include "components/offline_pages/background/pick_request_task.h"
-#include "components/offline_pages/background/pick_request_task_factory.h"
-#include "components/offline_pages/background/remove_requests_task.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/add_request_task.h"
+#include "components/offline_pages/core/background/change_requests_state_task.h"
+#include "components/offline_pages/core/background/get_requests_task.h"
+#include "components/offline_pages/core/background/initialize_store_task.h"
+#include "components/offline_pages/core/background/mark_attempt_aborted_task.h"
+#include "components/offline_pages/core/background/mark_attempt_completed_task.h"
+#include "components/offline_pages/core/background/mark_attempt_started_task.h"
+#include "components/offline_pages/core/background/pick_request_task.h"
+#include "components/offline_pages/core/background/reconcile_task.h"
+#include "components/offline_pages/core/background/remove_requests_task.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
namespace offline_pages {
@@ -61,19 +64,25 @@ void AddRequestDone(const RequestQueue::AddRequestCallback& callback,
} // namespace
RequestQueue::RequestQueue(std::unique_ptr<RequestQueueStore> store)
- : store_(std::move(store)), weak_ptr_factory_(this) {}
+ : store_(std::move(store)), weak_ptr_factory_(this) {
+ Initialize();
+}
RequestQueue::~RequestQueue() {}
void RequestQueue::GetRequests(const GetRequestsCallback& callback) {
- store_->GetRequests(base::Bind(&GetRequestsDone, callback));
+ std::unique_ptr<Task> task(new GetRequestsTask(
+ store_.get(), base::Bind(&GetRequestsDone, callback)));
+ task_queue_.AddTask(std::move(task));
}
void RequestQueue::AddRequest(const SavePageRequest& request,
const AddRequestCallback& callback) {
// TODO(fgorski): check that request makes sense.
// TODO(fgorski): check that request does not violate policy.
- store_->AddRequest(request, base::Bind(&AddRequestDone, callback, request));
+ std::unique_ptr<AddRequestTask> task(new AddRequestTask(
+ store_.get(), request, base::Bind(&AddRequestDone, callback, request)));
+ task_queue_.AddTask(std::move(task));
}
void RequestQueue::RemoveRequests(const std::vector<int64_t>& request_ids,
@@ -113,17 +122,16 @@ void RequestQueue::MarkAttemptCompleted(int64_t request_id,
task_queue_.AddTask(std::move(task));
}
-void RequestQueue::PurgeRequests(const PurgeRequestsCallback& callback) {}
-
void RequestQueue::PickNextRequest(
+ OfflinerPolicy* policy,
PickRequestTask::RequestPickedCallback picked_callback,
PickRequestTask::RequestNotPickedCallback not_picked_callback,
PickRequestTask::RequestCountCallback request_count_callback,
DeviceConditions& conditions,
std::set<int64_t>& disabled_requests) {
// Using the PickerContext, create a picker task.
- std::unique_ptr<Task> task(picker_factory_->CreatePickerTask(
- store_.get(), picked_callback, not_picked_callback,
+ std::unique_ptr<Task> task(new PickRequestTask(
+ store_.get(), policy, picked_callback, not_picked_callback,
request_count_callback, conditions, disabled_requests));
// Queue up the picking task, it will call one of the callbacks when it
@@ -131,4 +139,31 @@ void RequestQueue::PickNextRequest(
task_queue_.AddTask(std::move(task));
}
+void RequestQueue::ReconcileRequests(const UpdateCallback& callback) {
+ std::unique_ptr<Task> task(new ReconcileTask(store_.get(), callback));
+
+ // Queue up the reconcile task.
+ task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::CleanupRequestQueue() {
+ // Create a cleanup task.
+ std::unique_ptr<Task> task(cleanup_factory_->CreateCleanupTask(store_.get()));
+
+ // Queue up the cleanup task.
+ task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::Initialize() {
+ std::unique_ptr<Task> task(new InitializeStoreTask(
+ store_.get(), base::Bind(&RequestQueue::InitializeStoreDone,
+ weak_ptr_factory_.GetWeakPtr())));
+ task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::InitializeStoreDone(bool success) {
+ // TODO(fgorski): Result can be ignored for now. Report UMA in future.
+ // No need to pass the result up to RequestCoordinator.
+}
+
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/request_queue.h b/chromium/components/offline_pages/core/background/request_queue.h
index 7aced8d0319..8325dd07173 100644
--- a/chromium/components/offline_pages/background/request_queue.h
+++ b/chromium/components/offline_pages/core/background/request_queue.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_H_
#include <stdint.h>
@@ -15,24 +15,23 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/pick_request_task.h"
-#include "components/offline_pages/background/pick_request_task_factory.h"
-#include "components/offline_pages/background/request_queue_results.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/cleanup_task_factory.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/pick_request_task.h"
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_store_types.h"
#include "components/offline_pages/core/task_queue.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_store_types.h"
namespace offline_pages {
+class CleanupTaskFactory;
class RequestQueueStore;
-class PickRequestTaskFactory;
// Class responsible for managing save page requests.
class RequestQueue {
public:
-
// Callback used for |GetRequests|.
typedef base::Callback<void(GetRequestsResult,
std::vector<std::unique_ptr<SavePageRequest>>)>
@@ -91,38 +90,40 @@ class RequestQueue {
// Make a task to pick the next request, and report our choice to the
// callbacks.
void PickNextRequest(
+ OfflinerPolicy* policy,
PickRequestTask::RequestPickedCallback picked_callback,
PickRequestTask::RequestNotPickedCallback not_picked_callback,
PickRequestTask::RequestCountCallback request_count_callback,
DeviceConditions& conditions,
std::set<int64_t>& disabled_requests);
+ // Reconcile any requests that were active the last time chrome exited.
+ void ReconcileRequests(const UpdateCallback& callback);
+
+ // Cleanup requests that have expired, exceeded the start or completed retry
+ // limit.
+ void CleanupRequestQueue();
+
// Takes ownership of the factory. We use a setter to allow users of the
- // request queue to not need a PickerFactory to create it, since we have lots
+ // request queue to not need a CleanupFactory to create it, since we have lots
// of code using the request queue. The request coordinator will set a
- // factory before calling PickNextRequest.
- void SetPickerFactory(std::unique_ptr<PickRequestTaskFactory> factory) {
- picker_factory_ = std::move(factory);
+ // factory before calling CleanupRequestQueue.
+ void SetCleanupFactory(std::unique_ptr<CleanupTaskFactory> factory) {
+ cleanup_factory_ = std::move(factory);
}
private:
- // Callback used by |PurgeRequests|.
- typedef base::Callback<void(UpdateRequestResult,
- int /* removed requests count */)>
- PurgeRequestsCallback;
-
- // Purges the queue, removing the requests that are no longer relevant, e.g.
- // expired request. Result is returned through |callback| carries the number
- // of removed requests.
- void PurgeRequests(const PurgeRequestsCallback& callback);
+ // Store initialization functions.
+ void Initialize();
+ void InitializeStoreDone(bool success);
std::unique_ptr<RequestQueueStore> store_;
// Task queue to serialize store access.
TaskQueue task_queue_;
- // Builds PickRequestTask objects.
- std::unique_ptr<PickRequestTaskFactory> picker_factory_;
+ // Builds CleanupTask objects.
+ std::unique_ptr<CleanupTaskFactory> cleanup_factory_;
// Allows us to pass a weak pointer to callbacks.
base::WeakPtrFactory<RequestQueue> weak_ptr_factory_;
@@ -132,4 +133,4 @@ class RequestQueue {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_H_
diff --git a/chromium/components/offline_pages/background/request_queue_in_memory_store.cc b/chromium/components/offline_pages/core/background/request_queue_in_memory_store.cc
index f46f9a10013..69e06b8cada 100644
--- a/chromium/components/offline_pages/background/request_queue_in_memory_store.cc
+++ b/chromium/components/offline_pages/core/background/request_queue_in_memory_store.cc
@@ -2,23 +2,37 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
#include <unordered_set>
#include "base/bind.h"
#include "base/location.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/save_page_request.h"
namespace offline_pages {
-RequestQueueInMemoryStore::RequestQueueInMemoryStore() {}
+RequestQueueInMemoryStore::RequestQueueInMemoryStore()
+ : state_(StoreState::NOT_LOADED), scenario_(TestScenario::SUCCESSFUL) {}
+
+RequestQueueInMemoryStore::RequestQueueInMemoryStore(TestScenario scenario)
+ : state_(StoreState::NOT_LOADED), scenario_(scenario) {}
RequestQueueInMemoryStore::~RequestQueueInMemoryStore() {}
+void RequestQueueInMemoryStore::Initialize(const InitializeCallback& callback) {
+ if (scenario_ == TestScenario::SUCCESSFUL)
+ state_ = StoreState::LOADED;
+ else
+ state_ = StoreState::FAILED_LOADING;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, state_ == StoreState::LOADED));
+}
+
void RequestQueueInMemoryStore::GetRequests(
const GetRequestsCallback& callback) {
+ DCHECK_NE(state_, StoreState::NOT_LOADED);
std::vector<std::unique_ptr<SavePageRequest>> result_requests;
for (const auto& id_request_pair : requests_) {
std::unique_ptr<SavePageRequest> request(
@@ -33,6 +47,7 @@ void RequestQueueInMemoryStore::GetRequests(
void RequestQueueInMemoryStore::GetRequestsByIds(
const std::vector<int64_t>& request_ids,
const UpdateCallback& callback) {
+ DCHECK_NE(state_, StoreState::NOT_LOADED);
std::unique_ptr<UpdateRequestsResult> result(
new UpdateRequestsResult(state()));
@@ -59,6 +74,7 @@ void RequestQueueInMemoryStore::GetRequestsByIds(
void RequestQueueInMemoryStore::AddRequest(const SavePageRequest& request,
const AddCallback& callback) {
+ DCHECK_NE(state_, StoreState::NOT_LOADED);
RequestsMap::iterator iter = requests_.find(request.request_id());
ItemActionStatus status;
if (iter == requests_.end()) {
@@ -75,6 +91,7 @@ void RequestQueueInMemoryStore::AddRequest(const SavePageRequest& request,
void RequestQueueInMemoryStore::UpdateRequests(
const std::vector<SavePageRequest>& requests,
const RequestQueue::UpdateCallback& callback) {
+ DCHECK_NE(state_, StoreState::NOT_LOADED);
std::unique_ptr<UpdateRequestsResult> result(
new UpdateRequestsResult(state()));
@@ -99,6 +116,7 @@ void RequestQueueInMemoryStore::UpdateRequests(
void RequestQueueInMemoryStore::RemoveRequests(
const std::vector<int64_t>& request_ids,
const UpdateCallback& callback) {
+ DCHECK_NE(state_, StoreState::NOT_LOADED);
std::unique_ptr<UpdateRequestsResult> result(
new UpdateRequestsResult(StoreState::LOADED));
@@ -122,13 +140,19 @@ void RequestQueueInMemoryStore::RemoveRequests(
}
void RequestQueueInMemoryStore::Reset(const ResetCallback& callback) {
- requests_.clear();
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- base::Bind(callback, true));
+ if (scenario_ != TestScenario::LOAD_FAILED_RESET_FAILED) {
+ requests_.clear();
+ state_ = StoreState::NOT_LOADED;
+ scenario_ = TestScenario::SUCCESSFUL;
+ } else {
+ state_ = StoreState::FAILED_RESET;
+ }
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, state_ == StoreState::NOT_LOADED));
}
StoreState RequestQueueInMemoryStore::state() const {
- return StoreState::LOADED;
+ return state_;
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/request_queue_in_memory_store.h b/chromium/components/offline_pages/core/background/request_queue_in_memory_store.h
index 63b18e3decb..907a6a9cf79 100644
--- a/chromium/components/offline_pages/background/request_queue_in_memory_store.h
+++ b/chromium/components/offline_pages/core/background/request_queue_in_memory_store.h
@@ -2,26 +2,34 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
#include <stdint.h>
#include <map>
#include "base/macros.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
namespace offline_pages {
// Interface for classes storing save page requests.
class RequestQueueInMemoryStore : public RequestQueueStore {
public:
+ enum class TestScenario {
+ SUCCESSFUL,
+ LOAD_FAILED_RESET_SUCCESS,
+ LOAD_FAILED_RESET_FAILED,
+ };
+
RequestQueueInMemoryStore();
+ explicit RequestQueueInMemoryStore(TestScenario scenario);
~RequestQueueInMemoryStore() override;
// RequestQueueStore implementaiton.
+ void Initialize(const InitializeCallback& callback) override;
void GetRequests(const GetRequestsCallback& callback) override;
void GetRequestsByIds(const std::vector<int64_t>& request_ids,
const UpdateCallback& callback) override;
@@ -31,17 +39,18 @@ class RequestQueueInMemoryStore : public RequestQueueStore {
const UpdateCallback& callback) override;
void RemoveRequests(const std::vector<int64_t>& request_ids,
const UpdateCallback& callback) override;
-
void Reset(const ResetCallback& callback) override;
StoreState state() const override;
private:
typedef std::map<int64_t, SavePageRequest> RequestsMap;
RequestsMap requests_;
+ StoreState state_;
+ TestScenario scenario_;
DISALLOW_COPY_AND_ASSIGN(RequestQueueInMemoryStore);
};
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
diff --git a/chromium/components/offline_pages/background/request_queue_results.h b/chromium/components/offline_pages/core/background/request_queue_results.h
index ae5326c75b0..5468fd2e9ef 100644
--- a/chromium/components/offline_pages/background/request_queue_results.h
+++ b/chromium/components/offline_pages/core/background/request_queue_results.h
@@ -5,8 +5,8 @@
#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/offline_store_types.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_store_types.h"
namespace offline_pages {
diff --git a/chromium/components/offline_pages/background/request_queue_store.h b/chromium/components/offline_pages/core/background/request_queue_store.h
index bb2e5966e23..5bf2738508d 100644
--- a/chromium/components/offline_pages/background/request_queue_store.h
+++ b/chromium/components/offline_pages/core/background/request_queue_store.h
@@ -2,16 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_H_
#include <stdint.h>
#include <vector>
#include "base/callback.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/offline_store_types.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_store_types.h"
namespace offline_pages {
@@ -26,15 +26,19 @@ class RequestQueueStore {
using UpdateCallback = RequestQueue::UpdateCallback;
+ typedef base::Callback<void(bool /* success */)> InitializeCallback;
+ typedef base::Callback<void(bool /* success */)> ResetCallback;
typedef base::Callback<void(
bool /* success */,
std::vector<std::unique_ptr<SavePageRequest>> /* requests */)>
GetRequestsCallback;
typedef base::Callback<void(ItemActionStatus)> AddCallback;
- typedef base::Callback<void(bool /* success */)> ResetCallback;
virtual ~RequestQueueStore(){};
+ // Initializes the store. Should be called before any other methods.
+ virtual void Initialize(const InitializeCallback& callback) = 0;
+
// Gets all of the requests from the store.
virtual void GetRequests(const GetRequestsCallback& callback) = 0;
@@ -60,7 +64,7 @@ class RequestQueueStore {
virtual void RemoveRequests(const std::vector<int64_t>& request_ids,
const UpdateCallback& callback) = 0;
- // Resets the store.
+ // Resets the store (removes any existing data).
virtual void Reset(const ResetCallback& callback) = 0;
// Gets the store state.
@@ -69,4 +73,4 @@ class RequestQueueStore {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_H_
diff --git a/chromium/components/offline_pages/background/request_queue_store_sql.cc b/chromium/components/offline_pages/core/background/request_queue_store_sql.cc
index b008ed33141..5e5672a595f 100644
--- a/chromium/components/offline_pages/background/request_queue_store_sql.cc
+++ b/chromium/components/offline_pages/core/background/request_queue_store_sql.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/request_queue_store_sql.h"
+#include "components/offline_pages/core/background/request_queue_store_sql.h"
#include <unordered_set>
@@ -13,7 +13,7 @@
#include "base/logging.h"
#include "base/sequenced_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/save_page_request.h"
#include "sql/connection.h"
#include "sql/statement.h"
#include "sql/transaction.h"
@@ -24,7 +24,7 @@ template class StoreUpdateResult<SavePageRequest>;
namespace {
-using StoreStateCallback = base::Callback<void(StoreState)>;
+using SuccessCallback = base::Callback<void(bool)>;
// This is a macro instead of a const so that
// it can be used inline in other SQL statements below.
@@ -363,28 +363,23 @@ void RemoveRequestsSync(sql::Connection* db,
void OpenConnectionSync(sql::Connection* db,
scoped_refptr<base::SingleThreadTaskRunner> runner,
const base::FilePath& path,
- const StoreStateCallback& callback) {
- StoreState state =
- InitDatabase(db, path) ? StoreState::LOADED : StoreState::FAILED_LOADING;
- runner->PostTask(FROM_HERE, base::Bind(callback, state));
+ const SuccessCallback& callback) {
+ bool success = InitDatabase(db, path);
+ runner->PostTask(FROM_HERE, base::Bind(callback, success));
}
void ResetSync(sql::Connection* db,
const base::FilePath& db_file_path,
scoped_refptr<base::SingleThreadTaskRunner> runner,
- const StoreStateCallback& callback) {
+ const SuccessCallback& callback) {
// This method deletes the content of the whole store and reinitializes it.
- bool success = db->Raze();
- db->Close();
- StoreState state;
- if (!success)
- state = StoreState::FAILED_RESET;
- if (InitDatabase(db, db_file_path))
- state = StoreState::LOADED;
- else
- state = StoreState::FAILED_LOADING;
-
- runner->PostTask(FROM_HERE, base::Bind(callback, state));
+ bool success = true;
+ if (db) {
+ success = db->Raze();
+ db->Close();
+ }
+ success = base::DeleteFile(db_file_path, true /* recursive */) && success;
+ runner->PostTask(FROM_HERE, base::Bind(callback, success));
}
} // anonymous namespace
@@ -394,31 +389,33 @@ RequestQueueStoreSQL::RequestQueueStoreSQL(
const base::FilePath& path)
: background_task_runner_(std::move(background_task_runner)),
db_file_path_(path.AppendASCII("RequestQueue.db")),
- weak_ptr_factory_(this) {
- OpenConnection();
-}
+ state_(StoreState::NOT_LOADED),
+ weak_ptr_factory_(this) {}
RequestQueueStoreSQL::~RequestQueueStoreSQL() {
if (db_.get())
background_task_runner_->DeleteSoon(FROM_HERE, db_.release());
}
-bool RequestQueueStoreSQL::CheckDb(const base::Closure& callback) {
- DCHECK(db_.get());
- if (!db_.get()) {
- // Nothing to do, but post a callback instead of calling directly
- // to preserve the async style behavior to prevent bugs.
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
- return false;
- }
- return true;
+void RequestQueueStoreSQL::Initialize(const InitializeCallback& callback) {
+ DCHECK(!db_);
+ db_.reset(new sql::Connection());
+ background_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&OpenConnectionSync, db_.get(),
+ base::ThreadTaskRunnerHandle::Get(), db_file_path_,
+ base::Bind(&RequestQueueStoreSQL::OnOpenConnectionDone,
+ weak_ptr_factory_.GetWeakPtr(), callback)));
}
void RequestQueueStoreSQL::GetRequests(const GetRequestsCallback& callback) {
DCHECK(db_.get());
- std::vector<std::unique_ptr<SavePageRequest>> requests;
- if (!CheckDb(base::Bind(callback, false, base::Passed(&requests))))
+ if (!CheckDb()) {
+ std::vector<std::unique_ptr<SavePageRequest>> requests;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, false, base::Passed(&requests)));
return;
+ }
background_task_runner_->PostTask(
FROM_HERE, base::Bind(&GetRequestsSync, db_.get(),
@@ -428,7 +425,7 @@ void RequestQueueStoreSQL::GetRequests(const GetRequestsCallback& callback) {
void RequestQueueStoreSQL::GetRequestsByIds(
const std::vector<int64_t>& request_ids,
const UpdateCallback& callback) {
- if (!db_.get()) {
+ if (!CheckDb()) {
PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), request_ids,
callback);
return;
@@ -442,8 +439,11 @@ void RequestQueueStoreSQL::GetRequestsByIds(
void RequestQueueStoreSQL::AddRequest(const SavePageRequest& request,
const AddCallback& callback) {
- if (!CheckDb(base::Bind(callback, ItemActionStatus::STORE_ERROR)))
+ if (!CheckDb()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, ItemActionStatus::STORE_ERROR));
return;
+ }
background_task_runner_->PostTask(
FROM_HERE,
@@ -454,7 +454,7 @@ void RequestQueueStoreSQL::AddRequest(const SavePageRequest& request,
void RequestQueueStoreSQL::UpdateRequests(
const std::vector<SavePageRequest>& requests,
const UpdateCallback& callback) {
- if (!db_.get()) {
+ if (!CheckDb()) {
PostStoreErrorForAllRequests(base::ThreadTaskRunnerHandle::Get(), requests,
callback);
return;
@@ -469,7 +469,7 @@ void RequestQueueStoreSQL::UpdateRequests(
void RequestQueueStoreSQL::RemoveRequests(
const std::vector<int64_t>& request_ids,
const UpdateCallback& callback) {
- if (!db_.get()) {
+ if (!CheckDb()) {
PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), request_ids,
callback);
return;
@@ -482,10 +482,6 @@ void RequestQueueStoreSQL::RemoveRequests(
}
void RequestQueueStoreSQL::Reset(const ResetCallback& callback) {
- DCHECK(db_.get());
- if (!CheckDb(base::Bind(callback, false)))
- return;
-
background_task_runner_->PostTask(
FROM_HERE,
base::Bind(&ResetSync, db_.get(), db_file_path_,
@@ -494,36 +490,28 @@ void RequestQueueStoreSQL::Reset(const ResetCallback& callback) {
weak_ptr_factory_.GetWeakPtr(), callback)));
}
-void RequestQueueStoreSQL::OpenConnection() {
- DCHECK(!db_);
- db_.reset(new sql::Connection());
- background_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&OpenConnectionSync, db_.get(),
- base::ThreadTaskRunnerHandle::Get(), db_file_path_,
- base::Bind(&RequestQueueStoreSQL::OnOpenConnectionDone,
- weak_ptr_factory_.GetWeakPtr())));
+StoreState RequestQueueStoreSQL::state() const {
+ return state_;
}
-void RequestQueueStoreSQL::OnOpenConnectionDone(StoreState state) {
+void RequestQueueStoreSQL::OnOpenConnectionDone(
+ const InitializeCallback& callback,
+ bool success) {
DCHECK(db_.get());
-
- state_ = state;
-
- // Unfortunately we were not able to open DB connection.
- if (state_ != StoreState::LOADED)
- db_.reset();
+ state_ = success ? StoreState::LOADED : StoreState::FAILED_LOADING;
+ callback.Run(success);
}
void RequestQueueStoreSQL::OnResetDone(const ResetCallback& callback,
- StoreState state) {
- // Complete connection initialization post reset.
- OnOpenConnectionDone(state);
- callback.Run(state == StoreState::LOADED);
+ bool success) {
+ state_ = success ? StoreState::NOT_LOADED : StoreState::FAILED_RESET;
+ db_.reset();
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ base::Bind(callback, success));
}
-StoreState RequestQueueStoreSQL::state() const {
- return state_;
+bool RequestQueueStoreSQL::CheckDb() const {
+ return db_ && state_ == StoreState::LOADED;
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/background/request_queue_store_sql.h b/chromium/components/offline_pages/core/background/request_queue_store_sql.h
index 0be2eb5700e..6bd9b9a4ccd 100644
--- a/chromium/components/offline_pages/background/request_queue_store_sql.h
+++ b/chromium/components/offline_pages/core/background/request_queue_store_sql.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
#include <stdint.h>
#include <memory>
@@ -11,7 +11,7 @@
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
namespace base {
class SequencedTaskRunner;
@@ -32,6 +32,7 @@ class RequestQueueStoreSQL : public RequestQueueStore {
~RequestQueueStoreSQL() override;
// RequestQueueStore implementation.
+ void Initialize(const InitializeCallback& callback) override;
void GetRequests(const GetRequestsCallback& callback) override;
// Note: current implementation of this method makes a SQL query per ID. This
// is OK as long as number of IDs stays low, which is a typical case.
@@ -48,15 +49,14 @@ class RequestQueueStoreSQL : public RequestQueueStore {
StoreState state() const override;
private:
- // Helper functions to return immediately if no database is found.
- bool CheckDb(const base::Closure& callback);
+ // Used to finalize DB connection initialization.
+ void OnOpenConnectionDone(const InitializeCallback& callback, bool success);
- // Used to initialize DB connection.
- void OpenConnection();
- void OnOpenConnectionDone(StoreState state);
+ // Used to finalize DB connection reset.
+ void OnResetDone(const ResetCallback& callback, bool success);
- // Used to finalize connection reset.
- void OnResetDone(const ResetCallback& callback, StoreState state);
+ // Helper function to return immediately if no database is found.
+ bool CheckDb() const;
// Background thread where all SQL access should be run.
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
@@ -76,4 +76,4 @@ class RequestQueueStoreSQL : public RequestQueueStore {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
diff --git a/chromium/components/offline_pages/background/request_queue_store_unittest.cc b/chromium/components/offline_pages/core/background/request_queue_store_unittest.cc
index c4264c4aefd..7d697c6e328 100644
--- a/chromium/components/offline_pages/background/request_queue_store_unittest.cc
+++ b/chromium/components/offline_pages/core/background/request_queue_store_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/request_queue_store.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
#include <memory>
@@ -11,10 +11,10 @@
#include "base/files/scoped_temp_dir.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "components/offline_pages/background/request_queue_store_sql.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_store_sql.h"
+#include "components/offline_pages/core/background/save_page_request.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -32,9 +32,9 @@ const ClientId kClientId2("async", "5678");
const bool kUserRequested = true;
enum class LastResult {
- kNone,
- kFalse,
- kTrue,
+ RESULT_NONE,
+ RESULT_FALSE,
+ RESULT_TRUE,
};
} // namespace
@@ -51,7 +51,9 @@ class RequestQueueStoreTestBase : public testing::Test {
void PumpLoop();
void ClearResults();
+ void InitializeStore(RequestQueueStore* store);
+ void InitializeCallback(bool success);
// Callback used for get requests.
void GetRequestsDone(bool result,
std::vector<std::unique_ptr<SavePageRequest>> requests);
@@ -88,7 +90,7 @@ class RequestQueueStoreTestBase : public testing::Test {
};
RequestQueueStoreTestBase::RequestQueueStoreTestBase()
- : last_result_(LastResult::kNone),
+ : last_result_(LastResult::RESULT_NONE),
last_update_status_(UpdateStatus::FAILED),
last_add_status_(ItemActionStatus::NOT_FOUND),
task_runner_(new base::TestSimpleTaskRunner),
@@ -106,17 +108,29 @@ void RequestQueueStoreTestBase::PumpLoop() {
}
void RequestQueueStoreTestBase::ClearResults() {
- last_result_ = LastResult::kNone;
+ last_result_ = LastResult::RESULT_NONE;
last_update_status_ = UpdateStatus::FAILED;
last_add_status_ = ItemActionStatus::NOT_FOUND;
last_requests_.clear();
last_update_result_.reset(nullptr);
}
+void RequestQueueStoreTestBase::InitializeStore(RequestQueueStore* store) {
+ store->Initialize(base::Bind(&RequestQueueStoreTestBase::InitializeCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+ ClearResults();
+}
+
+void RequestQueueStoreTestBase::InitializeCallback(bool success) {
+ last_result_ = success ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE;
+}
+
void RequestQueueStoreTestBase::GetRequestsDone(
bool result,
std::vector<std::unique_ptr<SavePageRequest>> requests) {
- last_result_ = result ? LastResult::kTrue : LastResult::kFalse;
+ last_result_ = result ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE;
last_requests_ = std::move(requests);
}
@@ -134,7 +148,7 @@ void RequestQueueStoreTestBase::UpdateRequestDone(
}
void RequestQueueStoreTestBase::ResetDone(bool result) {
- last_result_ = result ? LastResult::kTrue : LastResult::kFalse;
+ last_result_ = result ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE;
}
// Defines interface for the store factory.
@@ -191,16 +205,20 @@ TYPED_TEST_CASE(RequestQueueStoreTest, StoreTypes);
TYPED_TEST(RequestQueueStoreTest, GetRequestsEmpty) {
std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+ this->InitializeStore(store.get());
+
store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
base::Unretained(this)));
- ASSERT_EQ(LastResult::kNone, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
this->PumpLoop();
- ASSERT_EQ(LastResult::kTrue, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
ASSERT_TRUE(this->last_requests().empty());
}
TYPED_TEST(RequestQueueStoreTest, GetRequestsByIds) {
std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+ this->InitializeStore(store.get());
+
base::Time creation_time = base::Time::Now();
SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
kUserRequested);
@@ -260,9 +278,11 @@ TYPED_TEST(RequestQueueStoreTest, GetRequestsByIds) {
TYPED_TEST(RequestQueueStoreTest, AddRequest) {
std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+ this->InitializeStore(store.get());
+
base::Time creation_time = base::Time::Now();
- SavePageRequest request(
- kRequestId, kUrl, kClientId, creation_time, kUserRequested);
+ SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+ kUserRequested);
store->AddRequest(request,
base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
@@ -275,9 +295,9 @@ TYPED_TEST(RequestQueueStoreTest, AddRequest) {
this->ClearResults();
store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
base::Unretained(this)));
- ASSERT_EQ(LastResult::kNone, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
this->PumpLoop();
- ASSERT_EQ(LastResult::kTrue, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
ASSERT_EQ(1ul, this->last_requests().size());
ASSERT_EQ(request, *(this->last_requests()[0].get()));
@@ -294,17 +314,19 @@ TYPED_TEST(RequestQueueStoreTest, AddRequest) {
this->ClearResults();
store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
base::Unretained(this)));
- ASSERT_EQ(LastResult::kNone, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
this->PumpLoop();
- ASSERT_EQ(LastResult::kTrue, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
ASSERT_EQ(1ul, this->last_requests().size());
}
TYPED_TEST(RequestQueueStoreTest, UpdateRequest) {
std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+ this->InitializeStore(store.get());
+
base::Time creation_time = base::Time::Now();
- SavePageRequest original_request(
- kRequestId, kUrl, kClientId, creation_time, kUserRequested);
+ SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
+ kUserRequested);
store->AddRequest(original_request,
base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
base::Unretained(this)));
@@ -346,15 +368,17 @@ TYPED_TEST(RequestQueueStoreTest, UpdateRequest) {
this->ClearResults();
store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
base::Unretained(this)));
- ASSERT_EQ(LastResult::kNone, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
this->PumpLoop();
- ASSERT_EQ(LastResult::kTrue, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
ASSERT_EQ(1ul, this->last_requests().size());
ASSERT_EQ(updated_request, *(this->last_requests()[0].get()));
}
TYPED_TEST(RequestQueueStoreTest, RemoveRequests) {
std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+ this->InitializeStore(store.get());
+
base::Time creation_time = base::Time::Now();
SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
kUserRequested);
@@ -392,7 +416,7 @@ TYPED_TEST(RequestQueueStoreTest, RemoveRequests) {
store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
base::Unretained(this)));
this->PumpLoop();
- ASSERT_EQ(LastResult::kTrue, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
ASSERT_TRUE(this->last_requests().empty());
this->ClearResults();
@@ -417,9 +441,11 @@ TYPED_TEST(RequestQueueStoreTest, RemoveRequests) {
TYPED_TEST(RequestQueueStoreTest, ResetStore) {
std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+ this->InitializeStore(store.get());
+
base::Time creation_time = base::Time::Now();
- SavePageRequest original_request(
- kRequestId, kUrl, kClientId, creation_time, kUserRequested);
+ SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
+ kUserRequested);
store->AddRequest(original_request,
base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
base::Unretained(this)));
@@ -428,15 +454,16 @@ TYPED_TEST(RequestQueueStoreTest, ResetStore) {
store->Reset(base::Bind(&RequestQueueStoreTestBase::ResetDone,
base::Unretained(this)));
- ASSERT_EQ(LastResult::kNone, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
this->PumpLoop();
- ASSERT_EQ(LastResult::kTrue, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
this->ClearResults();
+ this->InitializeStore(store.get());
store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
base::Unretained(this)));
this->PumpLoop();
- ASSERT_EQ(LastResult::kTrue, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
ASSERT_TRUE(this->last_requests().empty());
}
@@ -447,9 +474,11 @@ class RequestQueueStoreSQLTest
// restarts.
TEST_F(RequestQueueStoreSQLTest, SaveCloseReopenRead) {
std::unique_ptr<RequestQueueStore> store(BuildStore());
+ this->InitializeStore(store.get());
+
base::Time creation_time = base::Time::Now();
- SavePageRequest original_request(
- kRequestId, kUrl, kClientId, creation_time, kUserRequested);
+ SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
+ kUserRequested);
store->AddRequest(original_request,
base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
base::Unretained(this)));
@@ -460,11 +489,13 @@ TEST_F(RequestQueueStoreSQLTest, SaveCloseReopenRead) {
// intact. First reset is done separately to release DB lock.
store.reset();
store = BuildStore();
+ this->InitializeStore(store.get());
+
store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
base::Unretained(this)));
- ASSERT_EQ(LastResult::kNone, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
this->PumpLoop();
- ASSERT_EQ(LastResult::kTrue, this->last_result());
+ ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
ASSERT_EQ(1ul, this->last_requests().size());
ASSERT_TRUE(original_request == *(this->last_requests().at(0).get()));
}
diff --git a/chromium/components/offline_pages/background/request_queue_unittest.cc b/chromium/components/offline_pages/core/background/request_queue_unittest.cc
index 8ba805a9b7f..3f5dc45564d 100644
--- a/chromium/components/offline_pages/background/request_queue_unittest.cc
+++ b/chromium/components/offline_pages/core/background/request_queue_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/request_queue.h"
+#include "components/offline_pages/core/background/request_queue.h"
#include <memory>
#include <utility>
@@ -10,13 +10,13 @@
#include "base/bind.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -105,9 +105,7 @@ class RequestQueueTest : public testing::Test {
RequestQueue* queue() { return queue_.get(); }
AddRequestResult last_add_result() const { return last_add_result_; }
- SavePageRequest* last_added_request() {
- return last_added_request_.get();
- }
+ SavePageRequest* last_added_request() { return last_added_request_.get(); }
UpdateRequestResult last_update_result() const { return last_update_result_; }
@@ -201,8 +199,8 @@ TEST_F(RequestQueueTest, GetRequestsEmpty) {
TEST_F(RequestQueueTest, AddRequest) {
base::Time creation_time = base::Time::Now();
- SavePageRequest request(
- kRequestId, kUrl, kClientId, creation_time, kUserRequested);
+ SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+ kUserRequested);
queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
base::Unretained(this)));
PumpLoop();
@@ -219,8 +217,8 @@ TEST_F(RequestQueueTest, AddRequest) {
TEST_F(RequestQueueTest, RemoveRequest) {
base::Time creation_time = base::Time::Now();
- SavePageRequest request(
- kRequestId, kUrl, kClientId, creation_time, kUserRequested);
+ SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+ kUserRequested);
queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
base::Unretained(this)));
PumpLoop();
@@ -363,14 +361,14 @@ TEST_F(RequestQueueTest, PauseAndResume) {
// listing multiple items and removing the right item.
TEST_F(RequestQueueTest, MultipleRequestsAddGetRemove) {
base::Time creation_time = base::Time::Now();
- SavePageRequest request1(
- kRequestId, kUrl, kClientId, creation_time, kUserRequested);
+ SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
+ kUserRequested);
queue()->AddRequest(request1, base::Bind(&RequestQueueTest::AddRequestDone,
base::Unretained(this)));
PumpLoop();
ASSERT_EQ(request1.request_id(), last_added_request()->request_id());
- SavePageRequest request2(
- kRequestId2, kUrl2, kClientId2, creation_time, kUserRequested);
+ SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+ kUserRequested);
queue()->AddRequest(request2, base::Bind(&RequestQueueTest::AddRequestDone,
base::Unretained(this)));
PumpLoop();
@@ -535,8 +533,6 @@ TEST_F(RequestQueueTest, MarkAttemptCompleted) {
update_requests_result()->updated_items.at(0).request_state());
}
-// Request expiration is detected during the call to pick a request, which
-// is why this test calls PickNextRequest().
TEST_F(RequestQueueTest, CleanStaleRequests) {
// Create a request that is already expired.
base::Time creation_time =
@@ -554,21 +550,14 @@ TEST_F(RequestQueueTest, CleanStaleRequests) {
OfflinerPolicy policy;
RequestNotifierStub notifier;
RequestCoordinatorEventLogger event_logger;
- std::unique_ptr<PickRequestTaskFactory> picker_factory(
- new PickRequestTaskFactory(&policy, &notifier, &event_logger));
- queue()->SetPickerFactory(std::move(picker_factory));
+ std::unique_ptr<CleanupTaskFactory> cleanup_factory(
+ new CleanupTaskFactory(&policy, &notifier, &event_logger));
+ queue()->SetCleanupFactory(std::move(cleanup_factory));
// Do a pick and clean operation, which will remove stale entries.
DeviceConditions conditions;
std::set<int64_t> disabled_list;
- queue()->PickNextRequest(
- base::Bind(&RequestQueueTest::RequestPickedCallback,
- base::Unretained(this)),
- base::Bind(&RequestQueueTest::RequestNotPickedCallback,
- base::Unretained(this)),
- base::Bind(&RequestQueueTest::RequestCountCallback,
- base::Unretained(this)),
- conditions, disabled_list);
+ queue()->CleanupRequestQueue();
this->PumpLoop();
diff --git a/chromium/components/offline_pages/background/save_page_request.cc b/chromium/components/offline_pages/core/background/save_page_request.cc
index e1efccb2ff5..02019dec523 100644
--- a/chromium/components/offline_pages/background/save_page_request.cc
+++ b/chromium/components/offline_pages/core/background/save_page_request.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/save_page_request.h"
namespace offline_pages {
@@ -52,8 +52,7 @@ SavePageRequest::SavePageRequest(const SavePageRequest& other)
SavePageRequest::~SavePageRequest() {}
bool SavePageRequest::operator==(const SavePageRequest& other) const {
- return request_id_ == other.request_id_ &&
- url_ == other.url_ &&
+ return request_id_ == other.request_id_ && url_ == other.url_ &&
client_id_ == other.client_id_ &&
creation_time_ == other.creation_time_ &&
activation_time_ == other.activation_time_ &&
diff --git a/chromium/components/offline_pages/background/save_page_request.h b/chromium/components/offline_pages/core/background/save_page_request.h
index cddda755187..9f9e9ed0ee8 100644
--- a/chromium/components/offline_pages/background/save_page_request.h
+++ b/chromium/components/offline_pages/core/background/save_page_request.h
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_SAVE_PAGE_REQUEST_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_SAVE_PAGE_REQUEST_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SAVE_PAGE_REQUEST_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SAVE_PAGE_REQUEST_H_
#include <stdint.h>
#include "base/time/time.h"
-#include "components/offline_pages/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_item.h"
#include "url/gurl.h"
namespace offline_pages {
@@ -18,9 +18,9 @@ class SavePageRequest {
public:
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offlinepages
enum class RequestState {
- AVAILABLE = 0, // Request can be scheduled when preconditions are met.
- PAUSED = 1, // Request is not available until it is unpaused.
- OFFLINING = 2, // Request is actively offlining.
+ AVAILABLE = 0, // Request can be scheduled when preconditions are met.
+ PAUSED = 1, // Request is not available until it is unpaused.
+ OFFLINING = 2, // Request is actively offlining.
};
SavePageRequest(int64_t request_id,
@@ -46,7 +46,7 @@ class SavePageRequest {
void MarkAttemptCompleted();
// Marks attempt as aborted. This will change the state of an OFFLINING
- // request to be AVAILABLE. It will not change the state of a PAUSED request
+ // request to be AVAILABLE. It will not change the state of a PAUSED request.
void MarkAttemptAborted();
// Mark the attempt as paused. It is not available for future prerendering
@@ -124,4 +124,4 @@ class SavePageRequest {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_SAVE_PAGE_REQUEST_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SAVE_PAGE_REQUEST_H_
diff --git a/chromium/components/offline_pages/background/save_page_request_unittest.cc b/chromium/components/offline_pages/core/background/save_page_request_unittest.cc
index 74da9b638aa..7fc2dcd6f2a 100644
--- a/chromium/components/offline_pages/background/save_page_request_unittest.cc
+++ b/chromium/components/offline_pages/core/background/save_page_request_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/save_page_request.h"
+#include "components/offline_pages/core/background/save_page_request.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -24,8 +24,8 @@ SavePageRequestTest::~SavePageRequestTest() {}
TEST_F(SavePageRequestTest, CreatePendingReqeust) {
base::Time creation_time = base::Time::Now();
- SavePageRequest request(
- kRequestId, kUrl, kClientId, creation_time, kUserRequested);
+ SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+ kUserRequested);
ASSERT_EQ(kRequestId, request.request_id());
ASSERT_EQ(kUrl, request.url());
ASSERT_EQ(kClientId, request.client_id());
@@ -57,8 +57,7 @@ TEST_F(SavePageRequestTest, StartAndCompleteRequest) {
// Attempt time, attempt count and status will though.
ASSERT_EQ(start_time, request.last_attempt_time());
ASSERT_EQ(1, request.started_attempt_count());
- ASSERT_EQ(SavePageRequest::RequestState::OFFLINING,
- request.request_state());
+ ASSERT_EQ(SavePageRequest::RequestState::OFFLINING, request.request_state());
request.MarkAttemptCompleted();
@@ -91,8 +90,7 @@ TEST_F(SavePageRequestTest, StartAndAbortRequest) {
// Attempt time and attempt count will though.
ASSERT_EQ(start_time, request.last_attempt_time());
ASSERT_EQ(1, request.started_attempt_count());
- ASSERT_EQ(SavePageRequest::RequestState::OFFLINING,
- request.request_state());
+ ASSERT_EQ(SavePageRequest::RequestState::OFFLINING, request.request_state());
request.MarkAttemptAborted();
diff --git a/chromium/components/offline_pages/background/scheduler.h b/chromium/components/offline_pages/core/background/scheduler.h
index b4a36fed4ae..818c216df06 100644
--- a/chromium/components/offline_pages/background/scheduler.h
+++ b/chromium/components/offline_pages/core/background/scheduler.h
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_SCHEDULER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_SCHEDULER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_H_
+
+#include "components/offline_pages/core/background/device_conditions.h"
namespace offline_pages {
@@ -40,8 +42,11 @@ class Scheduler {
// Unschedules the currently scheduled task, if any.
virtual void Unschedule() = 0;
+
+ // Get the current device conditions from the android APIs.
+ virtual const DeviceConditions& GetCurrentDeviceConditions() = 0;
};
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_SCHEDULER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_H_
diff --git a/chromium/components/offline_pages/core/background/scheduler_stub.cc b/chromium/components/offline_pages/core/background/scheduler_stub.cc
new file mode 100644
index 00000000000..f80c60c3eee
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/scheduler_stub.cc
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/scheduler_stub.h"
+
+namespace {
+const int kBatteryPercentageHigh = 75;
+const bool kPowerRequired = true;
+} // namespace
+
+namespace offline_pages {
+
+SchedulerStub::SchedulerStub()
+ : schedule_called_(false),
+ backup_schedule_called_(false),
+ unschedule_called_(false),
+ get_current_device_conditions_called_(false),
+ schedule_delay_(0L),
+ device_conditions_(kPowerRequired,
+ kBatteryPercentageHigh,
+ net::NetworkChangeNotifier::CONNECTION_2G),
+ trigger_conditions_(false, 0, false) {}
+
+SchedulerStub::~SchedulerStub() {}
+
+void SchedulerStub::Schedule(const TriggerConditions& trigger_conditions) {
+ schedule_called_ = true;
+ trigger_conditions_ = trigger_conditions;
+}
+
+void SchedulerStub::BackupSchedule(const TriggerConditions& trigger_conditions,
+ long delay_in_seconds) {
+ backup_schedule_called_ = true;
+ schedule_delay_ = delay_in_seconds;
+ trigger_conditions_ = trigger_conditions;
+}
+
+void SchedulerStub::Unschedule() {
+ unschedule_called_ = true;
+}
+
+const DeviceConditions& SchedulerStub::GetCurrentDeviceConditions() {
+ get_current_device_conditions_called_ = true;
+ return device_conditions_;
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/background/scheduler_stub.h b/chromium/components/offline_pages/core/background/scheduler_stub.h
new file mode 100644
index 00000000000..fa57d1feb9c
--- /dev/null
+++ b/chromium/components/offline_pages/core/background/scheduler_stub.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.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_STUB_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_STUB_H_
+
+#include "components/offline_pages/core/background/scheduler.h"
+
+namespace offline_pages {
+
+// Test class stubbing out the functionality of Scheduler.
+// It is only used for test support.
+class SchedulerStub : public Scheduler {
+ public:
+ SchedulerStub();
+ ~SchedulerStub() override;
+
+ void Schedule(const TriggerConditions& trigger_conditions) override;
+
+ void BackupSchedule(const TriggerConditions& trigger_conditions,
+ long delay_in_seconds) override;
+
+ // Unschedules the currently scheduled task, if any.
+ void Unschedule() override;
+
+ // Get the current device conditions from the android APIs.
+ const DeviceConditions& GetCurrentDeviceConditions() override;
+
+ bool schedule_called() const { return schedule_called_; }
+
+ bool backup_schedule_called() const { return backup_schedule_called_; }
+
+ bool unschedule_called() const { return unschedule_called_; }
+
+ TriggerConditions const* trigger_conditions() const {
+ return &trigger_conditions_;
+ }
+
+ private:
+ bool schedule_called_;
+ bool backup_schedule_called_;
+ bool unschedule_called_;
+ bool get_current_device_conditions_called_;
+ long schedule_delay_;
+ DeviceConditions device_conditions_;
+ TriggerConditions trigger_conditions_;
+};
+
+} // namespace offline_pages
+
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_STUB_H_
diff --git a/chromium/components/offline_pages/background/update_request_task.cc b/chromium/components/offline_pages/core/background/update_request_task.cc
index 08ccf1cb44b..ac5592f58f6 100644
--- a/chromium/components/offline_pages/background/update_request_task.cc
+++ b/chromium/components/offline_pages/core/background/update_request_task.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/background/update_request_task.h"
+#include "components/offline_pages/core/background/update_request_task.h"
#include <vector>
diff --git a/chromium/components/offline_pages/background/update_request_task.h b/chromium/components/offline_pages/core/background/update_request_task.h
index ad332bd9bb0..bef04f5b373 100644
--- a/chromium/components/offline_pages/background/update_request_task.h
+++ b/chromium/components/offline_pages/core/background/update_request_task.h
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_UPDATE_REQUEST_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_UPDATE_REQUEST_TASK_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_UPDATE_REQUEST_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_UPDATE_REQUEST_TASK_H_
#include <stdint.h>
#include <memory>
#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
#include "components/offline_pages/core/task.h"
namespace offline_pages {
@@ -63,4 +63,4 @@ class UpdateRequestTask : public Task {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_BACKGROUND_UPDATE_REQUEST_TASK_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_UPDATE_REQUEST_TASK_H_
diff --git a/chromium/components/offline_pages/client_namespace_constants.cc b/chromium/components/offline_pages/core/client_namespace_constants.cc
index d2f11bef2e6..1a1385f8e95 100644
--- a/chromium/components/offline_pages/client_namespace_constants.cc
+++ b/chromium/components/offline_pages/core/client_namespace_constants.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/client_namespace_constants.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
namespace offline_pages {
diff --git a/chromium/components/offline_pages/client_namespace_constants.h b/chromium/components/offline_pages/core/client_namespace_constants.h
index 823af09cc00..7991a511792 100644
--- a/chromium/components/offline_pages/client_namespace_constants.h
+++ b/chromium/components/offline_pages/core/client_namespace_constants.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_CLIENT_NAMESPACE_CONSTANTS_H_
-#define COMPONENTS_OFFLINE_PAGES_CLIENT_NAMESPACE_CONSTANTS_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_NAMESPACE_CONSTANTS_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_NAMESPACE_CONSTANTS_H_
#include "build/build_config.h"
@@ -23,4 +23,4 @@ extern const char kDefaultNamespace[];
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_CLIENT_NAMESPACE_CONSTANTS_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_NAMESPACE_CONSTANTS_H_
diff --git a/chromium/components/offline_pages/client_policy_controller.cc b/chromium/components/offline_pages/core/client_policy_controller.cc
index ae223d400f1..c2db0518641 100644
--- a/chromium/components/offline_pages/client_policy_controller.cc
+++ b/chromium/components/offline_pages/core/client_policy_controller.cc
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/client_policy_controller.h"
+#include "components/offline_pages/core/client_policy_controller.h"
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/time/time.h"
-#include "components/offline_pages/client_namespace_constants.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
using LifetimeType = offline_pages::LifetimePolicy::LifetimeType;
diff --git a/chromium/components/offline_pages/client_policy_controller.h b/chromium/components/offline_pages/core/client_policy_controller.h
index 8e2d21a4268..5e5cd21467a 100644
--- a/chromium/components/offline_pages/client_policy_controller.h
+++ b/chromium/components/offline_pages/core/client_policy_controller.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_CLIENT_POLICY_CONTROLLER_H_
-#define COMPONENTS_OFFLINE_PAGES_CLIENT_POLICY_CONTROLLER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_POLICY_CONTROLLER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_POLICY_CONTROLLER_H_
#include <stdint.h>
@@ -13,7 +13,7 @@
#include <vector>
#include "base/time/time.h"
-#include "components/offline_pages/offline_page_client_policy.h"
+#include "components/offline_pages/core/offline_page_client_policy.h"
namespace offline_pages {
@@ -74,4 +74,4 @@ class ClientPolicyController {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_CLIENT_POLICY_CONTROLLER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_POLICY_CONTROLLER_H_
diff --git a/chromium/components/offline_pages/client_policy_controller_unittest.cc b/chromium/components/offline_pages/core/client_policy_controller_unittest.cc
index 930b1d7bddb..e3721a8cdd3 100644
--- a/chromium/components/offline_pages/client_policy_controller_unittest.cc
+++ b/chromium/components/offline_pages/core/client_policy_controller_unittest.cc
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/client_policy_controller.h"
+#include "components/offline_pages/core/client_policy_controller.h"
#include <algorithm>
#include "base/bind.h"
#include "base/time/time.h"
-#include "components/offline_pages/client_namespace_constants.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
using LifetimeType = offline_pages::LifetimePolicy::LifetimeType;
diff --git a/chromium/components/offline_pages/downloads/BUILD.gn b/chromium/components/offline_pages/core/downloads/BUILD.gn
index 41f709f7d93..ad8ea055b78 100644
--- a/chromium/components/offline_pages/downloads/BUILD.gn
+++ b/chromium/components/offline_pages/core/downloads/BUILD.gn
@@ -19,8 +19,8 @@ static_library("offline_pages_ui_adapter") {
deps = [
"//base",
- "//components/offline_pages:offline_pages",
- "//components/offline_pages/background:background_offliner",
+ "//components/offline_pages/core",
+ "//components/offline_pages/core/background:background_offliner",
"//url",
]
}
@@ -36,9 +36,9 @@ source_set("unit_tests") {
":offline_pages_ui_adapter",
"//base",
"//base/test:test_support",
- "//components/offline_pages:offline_pages",
- "//components/offline_pages:test_support",
- "//components/offline_pages/background:background_offliner",
+ "//components/offline_pages/core",
+ "//components/offline_pages/core:test_support",
+ "//components/offline_pages/core/background:background_offliner",
"//testing/gtest",
]
}
diff --git a/chromium/components/offline_pages/downloads/download_notifying_observer.cc b/chromium/components/offline_pages/core/downloads/download_notifying_observer.cc
index ea5745cb5e6..cca7406a97d 100644
--- a/chromium/components/offline_pages/downloads/download_notifying_observer.cc
+++ b/chromium/components/offline_pages/core/downloads/download_notifying_observer.cc
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/downloads/download_notifying_observer.h"
+#include "components/offline_pages/core/downloads/download_notifying_observer.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/downloads/download_ui_adapter.h"
-#include "components/offline_pages/downloads/offline_page_download_notifier.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/downloads/download_ui_adapter.h"
+#include "components/offline_pages/core/downloads/offline_page_download_notifier.h"
namespace offline_pages {
namespace {
diff --git a/chromium/components/offline_pages/downloads/download_notifying_observer.h b/chromium/components/offline_pages/core/downloads/download_notifying_observer.h
index f89a5439416..6980102b47f 100644
--- a/chromium/components/offline_pages/downloads/download_notifying_observer.h
+++ b/chromium/components/offline_pages/core/downloads/download_notifying_observer.h
@@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
-#define COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
#include <memory>
#include "base/guid.h"
#include "base/macros.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/client_policy_controller.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/client_policy_controller.h"
namespace offline_pages {
@@ -60,4 +60,4 @@ class DownloadNotifyingObserver : public RequestCoordinator::Observer,
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
diff --git a/chromium/components/offline_pages/downloads/download_notifying_observer_unittest.cc b/chromium/components/offline_pages/core/downloads/download_notifying_observer_unittest.cc
index 6943b129c08..6db87708702 100644
--- a/chromium/components/offline_pages/downloads/download_notifying_observer_unittest.cc
+++ b/chromium/components/offline_pages/core/downloads/download_notifying_observer_unittest.cc
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/downloads/download_notifying_observer.h"
+#include "components/offline_pages/core/downloads/download_notifying_observer.h"
#include "base/memory/ptr_util.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/downloads/offline_page_download_notifier.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/downloads/offline_page_download_notifier.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -224,7 +224,7 @@ TEST_F(DownloadNotifyingObserverTest, OnCompletedFailure) {
SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
kTestCreationTime, kTestUserRequested);
observer()->OnCompleted(
- request, RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE);
+ request, RequestNotifier::BackgroundSavePageResult::LOADING_FAILURE);
EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
notifier()->last_notification_type());
EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
diff --git a/chromium/components/offline_pages/downloads/download_ui_adapter.cc b/chromium/components/offline_pages/core/downloads/download_ui_adapter.cc
index bb398cb8f02..193bfdb7f67 100644
--- a/chromium/components/offline_pages/downloads/download_ui_adapter.cc
+++ b/chromium/components/offline_pages/core/downloads/download_ui_adapter.cc
@@ -2,17 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/downloads/download_ui_adapter.h"
+#include "components/offline_pages/core/downloads/download_ui_adapter.h"
#include "base/bind.h"
#include "base/guid.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/downloads/download_ui_item.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/downloads/download_ui_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
namespace offline_pages {
@@ -30,10 +30,9 @@ DownloadUIAdapter::DownloadUIAdapter(OfflinePageModel* model)
: model_(model),
state_(State::NOT_LOADED),
observers_count_(0),
- weak_ptr_factory_(this) {
-}
+ weak_ptr_factory_(this) {}
-DownloadUIAdapter::~DownloadUIAdapter() { }
+DownloadUIAdapter::~DownloadUIAdapter() {}
// static
DownloadUIAdapter* DownloadUIAdapter::FromOfflinePageModel(
@@ -59,9 +58,9 @@ void DownloadUIAdapter::AddObserver(Observer* observer) {
// Don't just invoke it from here to avoid reentrancy in the client.
if (state_ == State::LOADED) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(&DownloadUIAdapter::NotifyItemsLoaded,
- weak_ptr_factory_.GetWeakPtr(),
- base::Unretained(observer)));
+ FROM_HERE,
+ base::Bind(&DownloadUIAdapter::NotifyItemsLoaded,
+ weak_ptr_factory_.GetWeakPtr(), base::Unretained(observer)));
}
}
@@ -80,15 +79,23 @@ void DownloadUIAdapter::OfflinePageModelLoaded(OfflinePageModel* model) {
// This signal is not used here.
}
-void DownloadUIAdapter::OfflinePageModelChanged(OfflinePageModel* model) {
+void DownloadUIAdapter::OfflinePageAdded(OfflinePageModel* model,
+ const OfflinePageItem& added_page) {
DCHECK(model == model_);
- model_->GetAllPages(
- base::Bind(&DownloadUIAdapter::OnOfflinePagesChanged,
- weak_ptr_factory_.GetWeakPtr()));
+ if (!IsVisibleInUI(added_page.client_id))
+ return;
+
+ const std::string& guid = added_page.client_id.id;
+ DCHECK(items_.find(guid) == items_.end());
+
+ items_[guid] = base::MakeUnique<ItemInfo>(added_page);
+ const DownloadUIItem& item = *(items_[guid]->ui_item);
+ for (Observer& observer : observers_)
+ observer.ItemAdded(item);
}
-void DownloadUIAdapter::OfflinePageDeleted(
- int64_t offline_id, const ClientId& client_id) {
+void DownloadUIAdapter::OfflinePageDeleted(int64_t offline_id,
+ const ClientId& client_id) {
if (!IsVisibleInUI(client_id))
return;
std::string guid = client_id.id;
@@ -107,8 +114,8 @@ std::vector<const DownloadUIItem*> DownloadUIAdapter::GetAllItems() const {
return result;
}
-const DownloadUIItem*
- DownloadUIAdapter::GetItem(const std::string& guid) const {
+const DownloadUIItem* DownloadUIAdapter::GetItem(
+ const std::string& guid) const {
DownloadUIItems::const_iterator it = items_.find(guid);
if (it == items_.end())
return nullptr;
@@ -123,16 +130,12 @@ void DownloadUIAdapter::DeleteItem(const std::string& guid) {
std::vector<int64_t> page_ids;
page_ids.push_back(it->second->offline_id);
- // TODO(dimich): This should be ExpirePages(...Now()..) when Expire is
- // firing Observer method. The resulting Observer notification will update
- // local cache.
model_->DeletePagesByOfflineId(
page_ids, base::Bind(&DownloadUIAdapter::OnDeletePagesDone,
weak_ptr_factory_.GetWeakPtr()));
}
-int64_t DownloadUIAdapter::GetOfflineIdByGuid(
- const std::string& guid) const {
+int64_t DownloadUIAdapter::GetOfflineIdByGuid(const std::string& guid) const {
// TODO(dimich): when requests are also in the cache, filter them out.
// Requests do not yet have offline ID.
DownloadUIItems::const_iterator it = items_.find(guid);
@@ -146,9 +149,8 @@ int64_t DownloadUIAdapter::GetOfflineIdByGuid(
void DownloadUIAdapter::LoadCache() {
// TODO(dimich): Add fetching from RequestQueue as well.
state_ = State::LOADING;
- model_->GetAllPages(
- base::Bind(&DownloadUIAdapter::OnOfflinePagesLoaded,
- weak_ptr_factory_.GetWeakPtr()));
+ model_->GetAllPages(base::Bind(&DownloadUIAdapter::OnOfflinePagesLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
}
void DownloadUIAdapter::ClearCache() {
@@ -185,27 +187,6 @@ void DownloadUIAdapter::NotifyItemsLoaded(Observer* observer) {
observer->ItemsLoaded();
}
-// This method is only called by OPM when a single item added.
-// TODO(dimich): change OPM to have real OnPageAdded/OnPageUpdated and
-// simplify this code.
-void DownloadUIAdapter::OnOfflinePagesChanged(
- const MultipleOfflinePageItemResult& pages) {
- std::vector<std::string> added_guids;
- for (const auto& page : pages) {
- if (!IsVisibleInUI(page.client_id)) // Item should be filtered out.
- continue;
- const std::string& guid = page.client_id.id;
- if (items_.find(guid) != items_.end()) // Item already exists.
- continue;
- items_[guid] = base::MakeUnique<ItemInfo>(page);
- added_guids.push_back(guid);
- }
- for (auto& guid : added_guids) {
- const DownloadUIItem& item = *(items_.find(guid)->second->ui_item.get());
- for (Observer& observer : observers_)
- observer.ItemAdded(item);
- }
-}
void DownloadUIAdapter::OnDeletePagesDone(DeletePageResult result) {
// TODO(dimich): Consider adding UMA to record user actions.
diff --git a/chromium/components/offline_pages/downloads/download_ui_adapter.h b/chromium/components/offline_pages/core/downloads/download_ui_adapter.h
index a8835fe039f..7ab6ace92d1 100644
--- a/chromium/components/offline_pages/downloads/download_ui_adapter.h
+++ b/chromium/components/offline_pages/core/downloads/download_ui_adapter.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGE_DOWNLOADS_DOWNLOAD_UI_ADAPTER_H_
-#define COMPONENTS_OFFLINE_PAGE_DOWNLOADS_DOWNLOAD_UI_ADAPTER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_UI_ADAPTER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_UI_ADAPTER_H_
#include <map>
#include <memory>
@@ -12,9 +12,9 @@
#include "base/observer_list.h"
#include "base/supports_user_data.h"
-#include "components/offline_pages/downloads/download_ui_item.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/offline_page_types.h"
+#include "components/offline_pages/core/downloads/download_ui_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_types.h"
#include "url/gurl.h"
namespace offline_pages {
@@ -80,16 +80,13 @@ class DownloadUIAdapter : public OfflinePageModel::Observer,
// OfflinePageModel::Observer
void OfflinePageModelLoaded(OfflinePageModel* model) override;
- void OfflinePageModelChanged(OfflinePageModel* model) override;
+ void OfflinePageAdded(OfflinePageModel* model,
+ const OfflinePageItem& added_page) override;
void OfflinePageDeleted(int64_t offline_id,
const ClientId& client_id) override;
private:
- enum class State {
- NOT_LOADED,
- LOADING,
- LOADED
- };
+ enum class State { NOT_LOADED, LOADING, LOADED };
struct ItemInfo {
explicit ItemInfo(const OfflinePageItem& page);
@@ -111,7 +108,6 @@ class DownloadUIAdapter : public OfflinePageModel::Observer,
// Task callbacks.
void OnOfflinePagesLoaded(const MultipleOfflinePageItemResult& pages);
void NotifyItemsLoaded(Observer* observer);
- void OnOfflinePagesChanged(const MultipleOfflinePageItemResult& pages);
void OnDeletePagesDone(DeletePageResult result);
// Always valid, this class is a member of the model.
diff --git a/chromium/components/offline_pages/downloads/download_ui_adapter_unittest.cc b/chromium/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc
index ef21b922942..c65dc37d075 100644
--- a/chromium/components/offline_pages/downloads/download_ui_adapter_unittest.cc
+++ b/chromium/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/downloads/download_ui_adapter.h"
+#include "components/offline_pages/core/downloads/download_ui_adapter.h"
#include <stdint.h>
@@ -20,9 +20,9 @@
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/stub_offline_page_model.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -33,8 +33,8 @@ static const int kTestOfflineId1 = 1;
static const int kTestOfflineId2 = 2;
static const int kTestOfflineId3 = 3;
static const char kTestUrl[] = "http://foo.com/bar.mhtml";
-static const char kTestGuid1[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc1";
-static const char kTestGuid2[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc2";
+static const char kTestGuid1[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc1";
+static const char kTestGuid2[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc2";
static const char kTestBadGuid[] = "ccccccc-cccc-0ccc-0ccc-ccccccccccc0";
static const ClientId kTestClientIdOtherNamespace(kLastNNamespace, kTestGuid1);
static const ClientId kTestClientIdOtherGuid(kLastNNamespace, kTestBadGuid);
@@ -56,12 +56,8 @@ class MockOfflinePageModel : public StubOfflinePageModel {
policy_controller_(new ClientPolicyController()) {
adapter.reset(new DownloadUIAdapter(this));
// Add one page.
- OfflinePageItem page(GURL(kTestUrl),
- kTestOfflineId1,
- kTestClientId1,
- kTestFilePath,
- kFileSize,
- kTestCreationTime);
+ OfflinePageItem page(GURL(kTestUrl), kTestOfflineId1, kTestClientId1,
+ kTestFilePath, kFileSize, kTestCreationTime);
page.title = kTestTitle;
pages[kTestOfflineId1] = page;
}
@@ -82,9 +78,9 @@ class MockOfflinePageModel : public StubOfflinePageModel {
// PostTask instead of just running callback to simpulate the real class.
void GetAllPages(const MultipleOfflinePageItemCallback& callback) override {
- task_runner_->PostTask(
- FROM_HERE, base::Bind(&MockOfflinePageModel::GetAllPagesImpl,
- base::Unretained(this), callback));
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&MockOfflinePageModel::GetAllPagesImpl,
+ base::Unretained(this), callback));
}
void GetAllPagesImpl(const MultipleOfflinePageItemCallback& callback) {
@@ -95,7 +91,7 @@ class MockOfflinePageModel : public StubOfflinePageModel {
}
void DeletePageAndNotifyAdapter(const std::string& guid) {
- for(const auto& page : pages) {
+ for (const auto& page : pages) {
if (page.second.client_id.id == guid) {
observer_->OfflinePageDeleted(page.second.offline_id,
page.second.client_id);
@@ -108,7 +104,7 @@ class MockOfflinePageModel : public StubOfflinePageModel {
void AddPageAndNotifyAdapter(const OfflinePageItem& page) {
EXPECT_EQ(pages.end(), pages.find(page.offline_id));
pages[page.offline_id] = page;
- observer_->OfflinePageModelChanged(this);
+ observer_->OfflinePageAdded(this, page);
}
ClientPolicyController* GetPolicyController() override {
@@ -129,9 +125,8 @@ class MockOfflinePageModel : public StubOfflinePageModel {
DISALLOW_COPY_AND_ASSIGN(MockOfflinePageModel);
};
-class DownloadUIAdapterTest
- : public testing::Test,
- public DownloadUIAdapter::Observer {
+class DownloadUIAdapterTest : public testing::Test,
+ public DownloadUIAdapter::Observer {
public:
DownloadUIAdapterTest();
~DownloadUIAdapterTest() override;
@@ -158,12 +153,9 @@ class DownloadUIAdapterTest
};
DownloadUIAdapterTest::DownloadUIAdapterTest()
- : items_loaded(false),
- task_runner_(new base::TestMockTimeTaskRunner) {
-}
+ : items_loaded(false), task_runner_(new base::TestMockTimeTaskRunner) {}
-DownloadUIAdapterTest::~DownloadUIAdapterTest() {
-}
+DownloadUIAdapterTest::~DownloadUIAdapterTest() {}
void DownloadUIAdapterTest::SetUp() {
model.reset(new MockOfflinePageModel(task_runner_.get()));
@@ -215,11 +207,8 @@ TEST_F(DownloadUIAdapterTest, InitialItemConversion) {
TEST_F(DownloadUIAdapterTest, ItemDeletedAdded) {
PumpLoop();
// Add page, notify adapter.
- OfflinePageItem page(GURL(kTestUrl),
- kTestOfflineId2,
- kTestClientId2,
- base::FilePath(kTestFilePath),
- kFileSize,
+ OfflinePageItem page(GURL(kTestUrl), kTestOfflineId2, kTestClientId2,
+ base::FilePath(kTestFilePath), kFileSize,
kTestCreationTime);
model->AddPageAndNotifyAdapter(page);
PumpLoop();
@@ -236,22 +225,16 @@ TEST_F(DownloadUIAdapterTest, ItemDeletedAdded) {
TEST_F(DownloadUIAdapterTest, ItemWithWrongNamespace) {
PumpLoop();
- OfflinePageItem page1(GURL(kTestUrl),
- kTestOfflineId2,
- kTestClientIdOtherNamespace,
- base::FilePath(kTestFilePath),
- kFileSize,
- kTestCreationTime);
+ OfflinePageItem page1(
+ GURL(kTestUrl), kTestOfflineId2, kTestClientIdOtherNamespace,
+ base::FilePath(kTestFilePath), kFileSize, kTestCreationTime);
model->AddPageAndNotifyAdapter(page1);
PumpLoop();
// Should not add the page with wrong namespace.
EXPECT_EQ(0UL, added_guids.size());
- OfflinePageItem page2(GURL(kTestUrl),
- kTestOfflineId3,
- kTestClientIdOtherGuid,
- base::FilePath(kTestFilePath),
- kFileSize,
+ OfflinePageItem page2(GURL(kTestUrl), kTestOfflineId3, kTestClientIdOtherGuid,
+ base::FilePath(kTestFilePath), kFileSize,
kTestCreationTime);
model->AddPageAndNotifyAdapter(page2);
PumpLoop();
@@ -259,26 +242,14 @@ TEST_F(DownloadUIAdapterTest, ItemWithWrongNamespace) {
EXPECT_EQ(0UL, added_guids.size());
}
-TEST_F(DownloadUIAdapterTest, ItemUpdated) {
+TEST_F(DownloadUIAdapterTest, ItemAdded) {
PumpLoop();
// Clear the initial page and replace it with updated one.
model->pages.clear();
- // Add page with the same offline_id/guid, notify adapter.
- // This should generate 'updated' notification.
- OfflinePageItem page1(GURL(kTestUrl),
- kTestOfflineId1,
- kTestClientId1,
- base::FilePath(kTestFilePath),
- kFileSize,
- kTestCreationTime);
// Add a new page which did not exist before.
- OfflinePageItem page2(GURL(kTestUrl),
- kTestOfflineId2,
- kTestClientId2,
- base::FilePath(kTestFilePath),
- kFileSize,
+ OfflinePageItem page2(GURL(kTestUrl), kTestOfflineId2, kTestClientId2,
+ base::FilePath(kTestFilePath), kFileSize,
kTestCreationTime);
- model->AddPageAndNotifyAdapter(page1);
model->AddPageAndNotifyAdapter(page2);
PumpLoop();
EXPECT_EQ(1UL, added_guids.size());
diff --git a/chromium/components/offline_pages/downloads/download_ui_item.cc b/chromium/components/offline_pages/core/downloads/download_ui_item.cc
index 13af20ba27c..c5bdef82028 100644
--- a/chromium/components/offline_pages/downloads/download_ui_item.cc
+++ b/chromium/components/offline_pages/core/downloads/download_ui_item.cc
@@ -2,16 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/downloads/download_ui_item.h"
+#include "components/offline_pages/core/downloads/download_ui_item.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/offline_page_item.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_page_item.h"
namespace offline_pages {
-DownloadUIItem::DownloadUIItem()
- : total_bytes(0) {
-}
+DownloadUIItem::DownloadUIItem() : total_bytes(0) {}
DownloadUIItem::DownloadUIItem(const OfflinePageItem& page)
: guid(page.client_id.id),
@@ -35,7 +33,6 @@ DownloadUIItem::DownloadUIItem(const DownloadUIItem& other)
start_time(other.start_time),
total_bytes(other.total_bytes) {}
-DownloadUIItem::~DownloadUIItem() {
-}
+DownloadUIItem::~DownloadUIItem() {}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/downloads/download_ui_item.h b/chromium/components/offline_pages/core/downloads/download_ui_item.h
index 801c1ef8e10..aea7cf5cceb 100644
--- a/chromium/components/offline_pages/downloads/download_ui_item.h
+++ b/chromium/components/offline_pages/core/downloads/download_ui_item.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
-#define COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
#include <stdint.h>
@@ -48,4 +48,4 @@ struct DownloadUIItem {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
diff --git a/chromium/components/offline_pages/downloads/offline_page_download_notifier.h b/chromium/components/offline_pages/core/downloads/offline_page_download_notifier.h
index 74921852440..fefabe4a289 100644
--- a/chromium/components/offline_pages/downloads/offline_page_download_notifier.h
+++ b/chromium/components/offline_pages/core/downloads/offline_page_download_notifier.h
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
-#define COMPONENTS_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
-#include "components/offline_pages/downloads/download_ui_item.h"
+#include "components/offline_pages/core/downloads/download_ui_item.h"
namespace offline_pages {
@@ -35,4 +35,4 @@ struct OfflinePageDownloadNotifier {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
diff --git a/chromium/components/offline_pages/offline_event_logger.cc b/chromium/components/offline_pages/core/offline_event_logger.cc
index 810c2a667e6..09d2b7f90f5 100644
--- a/chromium/components/offline_pages/offline_event_logger.cc
+++ b/chromium/components/offline_pages/core/offline_event_logger.cc
@@ -4,15 +4,21 @@
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
-#include "components/offline_pages/offline_event_logger.h"
+#include "components/offline_pages/core/offline_event_logger.h"
namespace offline_pages {
+extern const size_t kMaxLogCount = 50;
+
OfflineEventLogger::OfflineEventLogger()
- : activities_(kMaxLogCount), is_logging_(false) {}
+ : activities_(0), is_logging_(false), client_(nullptr) {}
OfflineEventLogger::~OfflineEventLogger() {}
+void OfflineEventLogger::Clear() {
+ activities_.clear();
+}
+
void OfflineEventLogger::SetIsLogging(bool is_logging) {
is_logging_ = is_logging;
}
@@ -21,39 +27,36 @@ bool OfflineEventLogger::GetIsLogging() {
return is_logging_;
}
-void OfflineEventLogger::Clear() {
- activities_.clear();
+void OfflineEventLogger::GetLogs(std::vector<std::string>* records) {
+ DCHECK(records);
+ records->insert(records->end(), activities_.begin(), activities_.end());
}
void OfflineEventLogger::RecordActivity(const std::string& activity) {
- if (!is_logging_) {
+ if (!is_logging_ || activity.empty())
return;
- }
- if (activities_.size() == kMaxLogCount)
- activities_.pop_back();
base::Time::Exploded current_time;
base::Time::Now().LocalExplode(&current_time);
std::string date_string = base::StringPrintf(
- "%d %02d %02d %02d:%02d:%02d",
- current_time.year,
- current_time.month,
- current_time.day_of_month,
- current_time.hour,
- current_time.minute,
+ "%d %02d %02d %02d:%02d:%02d", current_time.year, current_time.month,
+ current_time.day_of_month, current_time.hour, current_time.minute,
current_time.second);
+ if (client_)
+ client_->CustomLog(activity);
+
+ if (activities_.size() == kMaxLogCount)
+ activities_.pop_back();
+
activities_.push_front(date_string + ": " + activity);
}
-void OfflineEventLogger::GetLogs(std::vector<std::string>* records) {
- DCHECK(records);
- for (std::deque<std::string>::iterator it = activities_.begin();
- it != activities_.end(); it++) {
- if (!it->empty())
- records->push_back(*it);
- }
+void OfflineEventLogger::SetClient(Client* client) {
+ DCHECK(client);
+ SetIsLogging(true);
+ client_ = client;
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/offline_event_logger.h b/chromium/components/offline_pages/core/offline_event_logger.h
index 95ac36cc791..40bb9c45a3a 100644
--- a/chromium/components/offline_pages/offline_event_logger.h
+++ b/chromium/components/offline_pages/core/offline_event_logger.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_EVENT_LOGGER_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_EVENT_LOGGER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_EVENT_LOGGER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_EVENT_LOGGER_H_
#include <deque>
#include <string>
@@ -13,8 +13,9 @@
namespace offline_pages {
-// Maximum number of recorded Logs to keep track of at any moment.
-static const size_t kMaxLogCount = 50;
+// Maximum number of recorded Logs to keep track of at any moment. Defined in
+// offline_event_logger.cc.
+extern const size_t kMaxLogCount;
// Facilitates the logging of events. Subclasses should create methods that
// call RecordActivity to write into the log. |SetIsLogging|, |GetLogs|, and
@@ -27,6 +28,16 @@ static const size_t kMaxLogCount = 50;
// This log only keeps track of the last |kMaxLogCount| events.
class OfflineEventLogger {
public:
+ // This client interface should be implemented by the class which provides the
+ // ability to pipe the log somewhere else (Eg. a java class which can write
+ // logs into a file). It's optional and uses SetClient() to attach the client
+ // to the event logger instance.
+ class Client {
+ public:
+ virtual ~Client(){};
+ virtual void CustomLog(const std::string& message) = 0;
+ };
+
OfflineEventLogger();
~OfflineEventLogger();
@@ -43,17 +54,22 @@ class OfflineEventLogger {
// Dumps the current activity list into |records|.
void GetLogs(std::vector<std::string>* records);
- protected:
// Write the activity into the cycling log if we are currently logging.
void RecordActivity(const std::string& activity);
+ // Sets the client for custom logging process if needed.
+ void SetClient(Client* client);
+
private:
// Recorded offline page activities.
std::deque<std::string> activities_;
// Whether we are currently recording logs or not.
bool is_logging_;
+
+ // Not owned.
+ Client* client_;
};
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_EVENT_LOGGER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_EVENT_LOGGER_H_
diff --git a/chromium/components/offline_pages/core/offline_event_logger_unittest.cc b/chromium/components/offline_pages/core/offline_event_logger_unittest.cc
new file mode 100644
index 00000000000..e30fae74062
--- /dev/null
+++ b/chromium/components/offline_pages/core/offline_event_logger_unittest.cc
@@ -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.
+
+#include "components/offline_pages/core/offline_event_logger.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+const char kMessage[] = "Message is ";
+const int kTimeLength = 21;
+
+class EventLoggerTestClient : public OfflineEventLogger::Client {
+ public:
+ void CustomLog(const std::string& message) override {
+ last_log_message_ = message;
+ }
+
+ const std::string& last_log_message() { return last_log_message_; }
+
+ private:
+ std::string last_log_message_;
+};
+
+} // namespace
+
+TEST(OfflineEventLoggerTest, SettingClientEnableLogging) {
+ OfflineEventLogger logger;
+ EventLoggerTestClient client;
+ logger.SetClient(&client);
+ EXPECT_TRUE(logger.GetIsLogging());
+}
+
+TEST(OfflineEventLoggerTest, SettingClientAndLog) {
+ OfflineEventLogger logger;
+ EventLoggerTestClient client;
+ logger.SetClient(&client);
+
+ logger.SetIsLogging(true);
+ for (size_t i = 0; i < kMaxLogCount + 1; ++i)
+ logger.RecordActivity(kMessage + std::to_string(i));
+ std::vector<std::string> log;
+ logger.GetLogs(&log);
+
+ EXPECT_EQ(kMaxLogCount, log.size());
+ EXPECT_EQ(client.last_log_message(), log[0].substr(kTimeLength));
+ EXPECT_EQ(std::string(kMessage) + std::to_string(kMaxLogCount),
+ client.last_log_message());
+}
+
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/offline_page_archiver.h b/chromium/components/offline_pages/core/offline_page_archiver.h
index ca605bef35c..17631406db3 100644
--- a/chromium/components/offline_pages/offline_page_archiver.h
+++ b/chromium/components/offline_pages/core/offline_page_archiver.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ARCHIVER_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ARCHIVER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ARCHIVER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ARCHIVER_H_
#include <stdint.h>
@@ -58,6 +58,14 @@ class OfflinePageArchiver {
// there was a security error.
};
+ // Describes the parameters to control how to create an archive.
+ struct CreateArchiveParams {
+ CreateArchiveParams() : remove_popup_overlay(false) {}
+
+ // Whether to remove popup overlay that obstructs viewing normal content.
+ bool remove_popup_overlay;
+ };
+
typedef base::Callback<void(OfflinePageArchiver* /* archiver */,
ArchiverResult /* result */,
const GURL& /* url */,
@@ -68,14 +76,14 @@ class OfflinePageArchiver {
virtual ~OfflinePageArchiver() {}
- // Starts creating the archive in the |archives_dir| with |archive_id| added
- // to the archive filename. Once archive is created |callback| will be called
+ // Starts creating the archive in the |archives_dir| per
+ // |create_archive_params|. Once archive is created |callback| will be called
// with the result and additional information.
virtual void CreateArchive(const base::FilePath& archives_dir,
- int64_t archive_id,
+ const CreateArchiveParams& create_archive_params,
const CreateArchiveCallback& callback) = 0;
};
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ARCHIVER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ARCHIVER_H_
diff --git a/chromium/components/offline_pages/offline_page_client_policy.h b/chromium/components/offline_pages/core/offline_page_client_policy.h
index b52d51dcaed..4c70894522b 100644
--- a/chromium/components/offline_pages/offline_page_client_policy.h
+++ b/chromium/components/offline_pages/core/offline_page_client_policy.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_CLIENT_POLICY_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_CLIENT_POLICY_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_CLIENT_POLICY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_CLIENT_POLICY_H_
#include <stdint.h>
@@ -42,7 +42,7 @@ struct LifetimePolicy {
LifetimePolicy(LifetimeType init_lifetime_type, size_t init_page_limit)
: lifetime_type(init_lifetime_type),
expiration_period(base::TimeDelta::FromDays(0)),
- page_limit(init_page_limit){};
+ page_limit(init_page_limit) {}
};
// The struct describing feature set of the offline pages.
@@ -60,7 +60,7 @@ struct FeaturePolicy {
: is_supported_by_download(false),
is_supported_by_recent_tabs(false),
only_shown_in_original_tab(false),
- is_removed_on_cache_reset(true){};
+ is_removed_on_cache_reset(true) {}
};
// The struct describing policies for various namespaces (Bookmark, Last-N etc.)
@@ -94,7 +94,7 @@ struct OfflinePageClientPolicy {
: OfflinePageClientPolicy(namespace_val,
lifetime_policy_val,
pages_allowed_per_url_val,
- FeaturePolicy()){};
+ FeaturePolicy()) {}
};
class OfflinePageClientPolicyBuilder {
@@ -106,7 +106,7 @@ class OfflinePageClientPolicyBuilder {
: policy_(
OfflinePageClientPolicy(name_space,
LifetimePolicy(lifetime_type, page_limit),
- pages_allowed_per_url)){};
+ pages_allowed_per_url)) {}
~OfflinePageClientPolicyBuilder() {}
@@ -152,4 +152,4 @@ class OfflinePageClientPolicyBuilder {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_CLIENT_POLICY_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_CLIENT_POLICY_H_
diff --git a/chromium/components/offline_pages/offline_page_feature.cc b/chromium/components/offline_pages/core/offline_page_feature.cc
index f8aeaf030a2..ef2b4700ca4 100644
--- a/chromium/components/offline_pages/offline_page_feature.cc
+++ b/chromium/components/offline_pages/core/offline_page_feature.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_feature.h"
#include <string>
@@ -10,17 +10,14 @@
namespace offline_pages {
-const base::Feature kOfflineBookmarksFeature {
- "OfflineBookmarks", base::FEATURE_DISABLED_BY_DEFAULT
-};
+const base::Feature kOfflineBookmarksFeature{"OfflineBookmarks",
+ base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kOffliningRecentPagesFeature {
- "OfflineRecentPages", base::FEATURE_DISABLED_BY_DEFAULT
-};
+const base::Feature kOffliningRecentPagesFeature{
+ "OfflineRecentPages", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kOfflinePagesCTFeature {
- "OfflinePagesCT", base::FEATURE_ENABLED_BY_DEFAULT
-};
+const base::Feature kOfflinePagesCTFeature{"OfflinePagesCT",
+ base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kOfflinePagesSharingFeature{
"OfflinePagesSharing", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -31,8 +28,11 @@ const base::Feature kOfflinePagesSvelteConcurrentLoadingFeature{
const base::Feature kBackgroundLoaderForDownloadsFeature{
"BackgroundLoadingForDownloads", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kOfflinePagesAsyncDownloadFeature {
- "OfflinePagesAsyncDownload", base::FEATURE_DISABLED_BY_DEFAULT
+const base::Feature kOfflinePagesAsyncDownloadFeature{
+ "OfflinePagesAsyncDownload", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kNewBackgroundLoaderFeature {
+ "BackgroundLoader", base::FEATURE_DISABLED_BY_DEFAULT
};
bool IsOfflineBookmarksEnabled() {
@@ -40,7 +40,7 @@ bool IsOfflineBookmarksEnabled() {
}
bool IsOffliningRecentPagesEnabled() {
- return base::FeatureList::IsEnabled(kOffliningRecentPagesFeature);
+ return base::FeatureList::IsEnabled(kOffliningRecentPagesFeature);
}
bool IsOfflinePagesSvelteConcurrentLoadingEnabled() {
@@ -64,4 +64,8 @@ bool IsOfflinePagesAsyncDownloadEnabled() {
return base::FeatureList::IsEnabled(kOfflinePagesAsyncDownloadFeature);
}
+bool ShouldUseNewBackgroundLoader() {
+ return base::FeatureList::IsEnabled(kNewBackgroundLoaderFeature);
+}
+
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/offline_page_feature.h b/chromium/components/offline_pages/core/offline_page_feature.h
index 7ba846cd3fd..3210afb696e 100644
--- a/chromium/components/offline_pages/offline_page_feature.h
+++ b/chromium/components/offline_pages/core/offline_page_feature.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_FEATURE_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_FEATURE_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_FEATURE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_FEATURE_H_
#include "base/feature_list.h"
#include "build/build_config.h"
@@ -17,6 +17,7 @@ extern const base::Feature kOfflinePagesCTFeature;
extern const base::Feature kOfflinePagesSharingFeature;
extern const base::Feature kBackgroundLoaderForDownloadsFeature;
extern const base::Feature kOfflinePagesAsyncDownloadFeature;
+extern const base::Feature kNewBackgroundLoaderFeature;
// Returns true if saving bookmarked pages for offline viewing is enabled.
bool IsOfflineBookmarksEnabled();
@@ -40,6 +41,10 @@ bool IsOfflinePagesSvelteConcurrentLoadingEnabled();
// Returns true if downloading a page asynchonously is enabled.
bool IsOfflinePagesAsyncDownloadEnabled();
+// Returns true if we should use background loader rather than prerenderer
+// to offline pages.
+bool ShouldUseNewBackgroundLoader();
+
} // namespace offline_pages
#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_FEATURE_H_
diff --git a/chromium/components/offline_pages/offline_page_item.cc b/chromium/components/offline_pages/core/offline_page_item.cc
index 0a8a76311b6..7d83079a2d8 100644
--- a/chromium/components/offline_pages/offline_page_item.cc
+++ b/chromium/components/offline_pages/core/offline_page_item.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_item.h"
namespace offline_pages {
@@ -56,25 +56,17 @@ OfflinePageItem::OfflinePageItem(const GURL& url,
OfflinePageItem::OfflinePageItem(const OfflinePageItem& other) = default;
-OfflinePageItem::~OfflinePageItem() {
-}
+OfflinePageItem::~OfflinePageItem() {}
bool OfflinePageItem::operator==(const OfflinePageItem& other) const {
- return url == other.url &&
- offline_id == other.offline_id &&
- client_id == other.client_id &&
- file_path == other.file_path &&
+ return url == other.url && offline_id == other.offline_id &&
+ client_id == other.client_id && file_path == other.file_path &&
creation_time == other.creation_time &&
last_access_time == other.last_access_time &&
- expiration_time == other.expiration_time &&
access_count == other.access_count &&
title == other.title &&
flags == other.flags &&
original_url == other.original_url;
}
-bool OfflinePageItem::IsExpired() const {
- return creation_time < expiration_time;
-}
-
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/offline_page_item.h b/chromium/components/offline_pages/core/offline_page_item.h
index 2ec13e17998..bbc2380e509 100644
--- a/chromium/components/offline_pages/offline_page_item.h
+++ b/chromium/components/offline_pages/core/offline_page_item.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ITEM_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ITEM_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ITEM_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ITEM_H_
#include <stdint.h>
@@ -56,9 +56,6 @@ struct OfflinePageItem {
bool operator==(const OfflinePageItem& other) const;
- // Returns whether the offline page is expired.
- bool IsExpired() const;
-
// The URL of the page. This is the last committed URL. In the case that
// redirects occur, access |original_url| for the original URL.
GURL url;
@@ -78,8 +75,6 @@ struct OfflinePageItem {
base::Time creation_time;
// The time when the offline archive was last accessed.
base::Time last_access_time;
- // The time when the offline page was expired.
- base::Time expiration_time;
// Number of times that the offline archive has been accessed.
int access_count;
// The title of the page at the time it was saved.
@@ -93,4 +88,4 @@ struct OfflinePageItem {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ITEM_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ITEM_H_
diff --git a/chromium/components/offline_pages/offline_page_metadata_store.cc b/chromium/components/offline_pages/core/offline_page_metadata_store.cc
index 24af10b37a3..761d34983ea 100644
--- a/chromium/components/offline_pages/offline_page_metadata_store.cc
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store.cc
@@ -2,15 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_metadata_store.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
namespace offline_pages {
template class StoreUpdateResult<OfflinePageItem>;
-OfflinePageMetadataStore::OfflinePageMetadataStore() {
-}
-
OfflinePageMetadataStore::~OfflinePageMetadataStore() {
}
diff --git a/chromium/components/offline_pages/offline_page_metadata_store.h b/chromium/components/offline_pages/core/offline_page_metadata_store.h
index 8e0e656ef73..d96fce7530a 100644
--- a/chromium/components/offline_pages/offline_page_metadata_store.h
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store.h
@@ -2,18 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_H_
#include <stdint.h>
#include <vector>
#include "base/callback.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_store_types.h"
-
-class GURL;
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_store_types.h"
namespace offline_pages {
@@ -38,15 +36,14 @@ class OfflinePageMetadataStore {
LOAD_STATUS_COUNT
};
+ typedef base::Callback<void(bool /* success */)> InitializeCallback;
+ typedef base::Callback<void(bool /* success */)> ResetCallback;
typedef base::Callback<void(const std::vector<OfflinePageItem>&)>
LoadCallback;
- typedef base::Callback<void(bool)> InitializeCallback;
typedef base::Callback<void(ItemActionStatus)> AddCallback;
typedef base::Callback<void(std::unique_ptr<OfflinePagesUpdateResult>)>
UpdateCallback;
- typedef base::Callback<void(bool)> ResetCallback;
- OfflinePageMetadataStore();
virtual ~OfflinePageMetadataStore();
// Initializes the store. Should be called before any other methods.
@@ -77,4 +74,4 @@ class OfflinePageMetadataStore {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_H_
diff --git a/chromium/components/offline_pages/offline_page_metadata_store_impl_unittest.cc b/chromium/components/offline_pages/core/offline_page_metadata_store_impl_unittest.cc
index 351fa3602ec..36abbcc98f9 100644
--- a/chromium/components/offline_pages/offline_page_metadata_store_impl_unittest.cc
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store_impl_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_metadata_store.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include <stdint.h>
@@ -15,9 +15,9 @@
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_metadata_store_sql.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_model.h"
#include "sql/connection.h"
#include "sql/statement.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -149,7 +149,7 @@ void BuildTestStoreWithSchemaFromM54(const base::FilePath& file) {
"client_id VARCHAR NOT NULL, "
"online_url VARCHAR NOT NULL, "
"offline_url VARCHAR NOT NULL DEFAULT '', "
- "file_path VARCHAR NOT NULL "
+ "file_path VARCHAR NOT NULL, "
"title VARCHAR NOT NULL DEFAULT ''"
")"));
ASSERT_TRUE(connection.CommitTransaction());
@@ -158,7 +158,7 @@ void BuildTestStoreWithSchemaFromM54(const base::FilePath& file) {
"(offline_id, creation_time, file_size, version, "
"last_access_time, access_count, client_namespace, "
"client_id, online_url, file_path, expiration_time, title) "
- "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
statement.BindInt64(0, kOfflineId);
statement.BindInt(1, 0);
statement.BindInt64(2, kFileSize);
@@ -225,6 +225,51 @@ void BuildTestStoreWithSchemaFromM55(const base::FilePath& file) {
connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "original_url"));
}
+void BuildTestStoreWithSchemaFromM56(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, "
+ "expiration_time INTEGER NOT NULL DEFAULT 0, "
+ "client_namespace VARCHAR NOT NULL, "
+ "client_id VARCHAR NOT NULL, "
+ "online_url VARCHAR NOT NULL, "
+ "file_path VARCHAR NOT NULL, "
+ "title VARCHAR NOT NULL DEFAULT '', "
+ "original_url VARCHAR NOT NULL DEFAULT ''"
+ ")"));
+ 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, expiration_time, 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.BindInt64(9, base::Time::Now().ToInternalValue());
+ statement.BindString16(10, base::UTF8ToUTF16("Test title"));
+ statement.BindCString(11, kOriginalTestURL);
+ ASSERT_TRUE(statement.Run());
+ ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
+ ASSERT_TRUE(
+ connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "expiration_time"));
+}
+
class OfflinePageMetadataStoreFactory {
public:
OfflinePageMetadataStore* BuildStore(const base::FilePath& file_path) {
@@ -260,6 +305,13 @@ class OfflinePageMetadataStoreFactory {
base::ThreadTaskRunnerHandle::Get(), file_path);
return store;
}
+
+ OfflinePageMetadataStore* BuildStoreM56(const base::FilePath& file_path) {
+ BuildTestStoreWithSchemaFromM56(file_path);
+ OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
+ base::ThreadTaskRunnerHandle::Get(), file_path);
+ return store;
+ }
};
enum CalledCallback { NONE, LOAD, ADD, UPDATE, REMOVE, RESET };
@@ -280,6 +332,7 @@ class OfflinePageMetadataStoreTest : public testing::Test {
std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM53();
std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM54();
std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM55();
+ std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM56();
void PumpLoop();
@@ -379,8 +432,6 @@ void OfflinePageMetadataStoreTest::CheckThatOfflinePageCanBeSaved(
OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
base::FilePath(kFilePath), kFileSize);
offline_page.title = base::UTF8ToUTF16("a title");
- base::Time expiration_time = base::Time::Now();
- offline_page.expiration_time = expiration_time;
offline_page.original_url = GURL(kOriginalTestURL);
store->AddOfflinePage(offline_page,
@@ -457,7 +508,7 @@ OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM53() {
std::unique_ptr<OfflinePageMetadataStore>
OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM54() {
std::unique_ptr<OfflinePageMetadataStore> store(
- factory_.BuildStoreM53(temp_directory_.GetPath()));
+ factory_.BuildStoreM54(temp_directory_.GetPath()));
PumpLoop();
store->Initialize(
base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
@@ -486,6 +537,22 @@ OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM55() {
return store;
}
+std::unique_ptr<OfflinePageMetadataStore>
+OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM56() {
+ std::unique_ptr<OfflinePageMetadataStore> store(
+ factory_.BuildStoreM56(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) {
@@ -574,7 +641,7 @@ TEST_F(OfflinePageMetadataStoreTest, LoadVersion52Store) {
std::unique_ptr<OfflinePageMetadataStore> store(
BuildStoreWithSchemaFromM52());
- CheckThatStoreHasOneItem();
+ OfflinePageItem item = CheckThatStoreHasOneItem();
CheckThatOfflinePageCanBeSaved(std::move(store));
}
@@ -587,36 +654,42 @@ TEST_F(OfflinePageMetadataStoreTest, LoadVersion53Store) {
BuildStoreWithSchemaFromM53());
OfflinePageItem item = CheckThatStoreHasOneItem();
- // We should have a valid expiration time after upgrade.
- EXPECT_NE(base::Time::FromInternalValue(0),
- offline_pages_[0].expiration_time);
-
CheckThatOfflinePageCanBeSaved(std::move(store));
}
// Loads a string with schema from M54.
-// Because for now we only reduce the number of fields it just makes sure there
-// are no crashes in the process.
+// This test case would crash if it's not handling correctly when we're loading
+// old version stores.
// TODO(romax): Move this to sql_unittest.
TEST_F(OfflinePageMetadataStoreTest, LoadVersion54Store) {
std::unique_ptr<OfflinePageMetadataStore> store(
BuildStoreWithSchemaFromM54());
OfflinePageItem item = CheckThatStoreHasOneItem();
-
CheckThatOfflinePageCanBeSaved(std::move(store));
}
// Loads a string with schema from M55.
-// Because for now we only reduce the number of fields it just makes sure there
-// are no crashes in the process.
+// This test case would crash if it's not handling correctly when we're loading
+// old version stores.
// TODO(romax): Move this to sql_unittest.
TEST_F(OfflinePageMetadataStoreTest, LoadVersion55Store) {
std::unique_ptr<OfflinePageMetadataStore> store(
BuildStoreWithSchemaFromM55());
OfflinePageItem item = CheckThatStoreHasOneItem();
+ CheckThatOfflinePageCanBeSaved(std::move(store));
+}
+// Loads a string with schema from M56.
+// This test case would crash if it's not handling correctly when we're loading
+// old version stores.
+// TODO(romax): Move this to sql_unittest.
+TEST_F(OfflinePageMetadataStoreTest, LoadVersion56Store) {
+ std::unique_ptr<OfflinePageMetadataStore> store(
+ BuildStoreWithSchemaFromM56());
+
+ OfflinePageItem item = CheckThatStoreHasOneItem();
CheckThatOfflinePageCanBeSaved(std::move(store));
}
@@ -632,8 +705,6 @@ TEST_F(OfflinePageMetadataStoreTest, AddSameOfflinePageTwice) {
OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
base::FilePath(kFilePath), kFileSize);
offline_page.title = base::UTF8ToUTF16("a title");
- base::Time expiration_time = base::Time::Now();
- offline_page.expiration_time = expiration_time;
store->AddOfflinePage(offline_page,
base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
@@ -734,7 +805,6 @@ TEST_F(OfflinePageMetadataStoreTest, AddRemoveMultipleOfflinePages) {
OfflinePageItem offline_page_2(GURL("https://other.page.com"), 5678LL,
kTestClientId2, file_path_2, 12345,
base::Time::Now());
- offline_page_2.expiration_time = base::Time::Now();
offline_page_2.original_url = GURL("https://example.com/bar");
store->AddOfflinePage(offline_page_2,
base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
@@ -790,7 +860,6 @@ TEST_F(OfflinePageMetadataStoreTest, AddRemoveMultipleOfflinePages) {
EXPECT_EQ(offline_page_2.creation_time, offline_pages_[0].creation_time);
EXPECT_EQ(offline_page_2.last_access_time,
offline_pages_[0].last_access_time);
- EXPECT_EQ(offline_page_2.expiration_time, offline_pages_[0].expiration_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);
}
@@ -822,7 +891,6 @@ TEST_F(OfflinePageMetadataStoreTest, UpdateOfflinePage) {
// Then update some data.
offline_page.file_size = kFileSize + 1;
offline_page.access_count++;
- offline_page.expiration_time = base::Time::Now();
offline_page.original_url = GURL("https://example.com/bar");
std::vector<OfflinePageItem> items_to_update;
items_to_update.push_back(offline_page);
diff --git a/chromium/components/offline_pages/offline_page_metadata_store_sql.cc b/chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc
index 4fbd76519f5..c8e3ee0e953 100644
--- a/chromium/components/offline_pages/offline_page_metadata_store_sql.cc
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
#include "base/bind.h"
#include "base/files/file_path.h"
@@ -13,7 +13,7 @@
#include "base/sequenced_task_runner.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_item.h"
#include "sql/connection.h"
#include "sql/statement.h"
#include "sql/transaction.h"
@@ -33,7 +33,6 @@ bool CreateOfflinePagesTable(sql::Connection* db) {
" file_size INTEGER NOT NULL,"
" last_access_time INTEGER NOT NULL,"
" access_count INTEGER NOT NULL,"
- " expiration_time INTEGER NOT NULL DEFAULT 0,"
" client_namespace VARCHAR NOT NULL,"
" client_id VARCHAR NOT NULL,"
" online_url VARCHAR NOT NULL,"
@@ -76,12 +75,12 @@ bool UpgradeFrom53(sql::Connection* db) {
const char kSql[] =
"INSERT INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, creation_time, file_size, last_access_time, "
- "access_count, expiration_time, client_namespace, client_id, "
- "online_url, file_path) "
+ "access_count, client_namespace, client_id, online_url, "
+ "file_path) "
"SELECT "
"offline_id, creation_time, file_size, last_access_time, "
- "access_count, expiration_time, client_namespace, client_id, "
- "online_url, file_path "
+ "access_count, client_namespace, client_id, online_url, "
+ "file_path "
"FROM temp_" OFFLINE_PAGES_TABLE_NAME;
return UpgradeWithQuery(db, kSql);
}
@@ -90,12 +89,12 @@ bool UpgradeFrom54(sql::Connection* db) {
const char kSql[] =
"INSERT INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, creation_time, file_size, last_access_time, "
- "access_count, expiration_time, client_namespace, client_id, "
- "online_url, file_path, title) "
+ "access_count, client_namespace, client_id, online_url, "
+ "file_path, title) "
"SELECT "
"offline_id, creation_time, file_size, last_access_time, "
- "access_count, expiration_time, client_namespace, client_id, "
- "online_url, file_path, title "
+ "access_count, client_namespace, client_id, online_url, "
+ "file_path, title "
"FROM temp_" OFFLINE_PAGES_TABLE_NAME;
return UpgradeWithQuery(db, kSql);
}
@@ -104,12 +103,26 @@ bool UpgradeFrom55(sql::Connection* db) {
const char kSql[] =
"INSERT INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, creation_time, file_size, last_access_time, "
- "access_count, expiration_time, client_namespace, client_id, "
- "online_url, file_path, title) "
+ "access_count, client_namespace, client_id, online_url, "
+ "file_path, title) "
+ "SELECT "
+ "offline_id, creation_time, file_size, last_access_time, "
+ "access_count, client_namespace, client_id, online_url, "
+ "file_path, title "
+ "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
+ return UpgradeWithQuery(db, kSql);
+}
+
+bool UpgradeFrom56(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, expiration_time, client_namespace, client_id, "
- "online_url, file_path, title "
+ "access_count, client_namespace, client_id, online_url, "
+ "file_path, title, original_url "
"FROM temp_" OFFLINE_PAGES_TABLE_NAME;
return UpgradeWithQuery(db, kSql);
}
@@ -127,7 +140,8 @@ bool CreateSchema(sql::Connection* db) {
}
// Upgrade section. Details are described in the header file.
- if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "expiration_time")) {
+ if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "expiration_time") &&
+ !db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "title")) {
if (!UpgradeFrom52(db))
return false;
} else if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "title")) {
@@ -139,6 +153,9 @@ bool CreateSchema(sql::Connection* db) {
} else if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "original_url")) {
if (!UpgradeFrom55(db))
return false;
+ } else if (db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "expiration_time")) {
+ if (!UpgradeFrom56(db))
+ return false;
}
// TODO(fgorski): Add indices here.
@@ -183,18 +200,15 @@ OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) {
base::Time last_access_time =
base::Time::FromInternalValue(statement->ColumnInt64(3));
int access_count = statement->ColumnInt(4);
- base::Time expiration_time =
- base::Time::FromInternalValue(statement->ColumnInt64(5));
- ClientId client_id(statement->ColumnString(6), statement->ColumnString(7));
- GURL url(statement->ColumnString(8));
- base::FilePath path(GetPathFromUTF8String(statement->ColumnString(9)));
- base::string16 title = statement->ColumnString16(10);
- GURL original_url(statement->ColumnString(11));
+ ClientId client_id(statement->ColumnString(5), statement->ColumnString(6));
+ GURL url(statement->ColumnString(7));
+ base::FilePath path(GetPathFromUTF8String(statement->ColumnString(8)));
+ base::string16 title = statement->ColumnString16(9);
+ GURL original_url(statement->ColumnString(10));
OfflinePageItem item(url, id, client_id, path, file_size, creation_time);
item.last_access_time = last_access_time;
item.access_count = access_count;
- item.expiration_time = expiration_time;
item.title = title;
item.original_url = original_url;
return item;
@@ -207,9 +221,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, "
- "expiration_time, title, original_url)"
+ "title, original_url)"
" VALUES "
- " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt64(0, item.offline_id);
@@ -221,9 +235,8 @@ ItemActionStatus Insert(sql::Connection* db, const OfflinePageItem& item) {
statement.BindInt64(6, item.creation_time.ToInternalValue());
statement.BindInt64(7, item.last_access_time.ToInternalValue());
statement.BindInt(8, item.access_count);
- statement.BindInt64(9, item.expiration_time.ToInternalValue());
- statement.BindString16(10, item.title);
- statement.BindString(11, item.original_url.spec());
+ statement.BindString16(9, item.title);
+ statement.BindString(10, item.original_url.spec());
if (!statement.Run())
return ItemActionStatus::STORE_ERROR;
if (db->GetLastChangeCount() == 0)
@@ -236,7 +249,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 = ?, expiration_time = ?, title = ?, original_url = ?"
+ " access_count = ?, title = ?, original_url = ?"
" WHERE offline_id = ?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
@@ -248,10 +261,9 @@ bool Update(sql::Connection* db, const OfflinePageItem& item) {
statement.BindInt64(5, item.creation_time.ToInternalValue());
statement.BindInt64(6, item.last_access_time.ToInternalValue());
statement.BindInt(7, item.access_count);
- statement.BindInt64(8, item.expiration_time.ToInternalValue());
- statement.BindString16(9, item.title);
- statement.BindString(10, item.original_url.spec());
- statement.BindInt64(11, item.offline_id);
+ statement.BindString16(8, item.title);
+ statement.BindString(9, item.original_url.spec());
+ statement.BindInt64(10, item.offline_id);
return statement.Run() && db->GetLastChangeCount() > 0;
}
@@ -596,7 +608,7 @@ void OfflinePageMetadataStoreSQL::OnResetDone(const ResetCallback& callback,
base::Bind(callback, success));
}
-bool OfflinePageMetadataStoreSQL::CheckDb() {
+bool OfflinePageMetadataStoreSQL::CheckDb() const {
return db_ && state_ == StoreState::LOADED;
}
diff --git a/chromium/components/offline_pages/offline_page_metadata_store_sql.h b/chromium/components/offline_pages/core/offline_page_metadata_store_sql.h
index 71e3c0f8e0f..04265262562 100644
--- a/chromium/components/offline_pages/offline_page_metadata_store_sql.h
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store_sql.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_SQL_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_SQL_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_SQL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_SQL_H_
#include <stdint.h>
@@ -12,7 +12,7 @@
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/offline_page_metadata_store.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
namespace base {
class SequencedTaskRunner;
@@ -35,6 +35,8 @@ namespace offline_pages {
// * In M55 we dropped the following fields (never used): version, status,
// offline_url, user_initiated.
// * In M56 original_url was added.
+// * In M57 expiration_time was dropped. Existing expired pages would be
+// removed when metadata consistency check happens.
//
// Here is a procedure to update the schema for this store:
// * Decide how to detect that the store is on a particular version, which
@@ -79,7 +81,7 @@ class OfflinePageMetadataStoreSQL : public OfflinePageMetadataStore {
void OnResetDone(const ResetCallback& callback, bool success);
// Checks whether a valid DB connection is present and store state is LOADED.
- bool CheckDb();
+ bool CheckDb() const;
// Background thread where all SQL access should be run.
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
@@ -100,4 +102,4 @@ class OfflinePageMetadataStoreSQL : public OfflinePageMetadataStore {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_SQL_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_SQL_H_
diff --git a/chromium/components/offline_pages/offline_page_model.cc b/chromium/components/offline_pages/core/offline_page_model.cc
index 24b4dd097f2..e8d7edeb33b 100644
--- a/chromium/components/offline_pages/offline_page_model.cc
+++ b/chromium/components/offline_pages/core/offline_page_model.cc
@@ -2,21 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_model.h"
#include "url/gurl.h"
namespace offline_pages {
+const int64_t OfflinePageModel::kInvalidOfflineId;
+
OfflinePageModel::SavePageParams::SavePageParams()
- : proposed_offline_id(OfflinePageModel::kInvalidOfflineId) {
-}
+ : 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;
}
// static
diff --git a/chromium/components/offline_pages/offline_page_model.h b/chromium/components/offline_pages/core/offline_page_model.h
index 5eb2cba101a..434687e9e11 100644
--- a/chromium/components/offline_pages/offline_page_model.h
+++ b/chromium/components/offline_pages/core/offline_page_model.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_H_
#include <stdint.h>
@@ -13,21 +13,17 @@
#include <vector>
#include "base/supports_user_data.h"
-#include "components/offline_pages/offline_event_logger.h"
-#include "components/offline_pages/offline_page_archiver.h"
-#include "components/offline_pages/offline_page_model_query.h"
-#include "components/offline_pages/offline_page_storage_manager.h"
-#include "components/offline_pages/offline_page_types.h"
+#include "components/offline_pages/core/offline_event_logger.h"
+#include "components/offline_pages/core/offline_page_archiver.h"
+#include "components/offline_pages/core/offline_page_model_query.h"
+#include "components/offline_pages/core/offline_page_storage_manager.h"
+#include "components/offline_pages/core/offline_page_types.h"
class GURL;
-namespace base {
-class Time;
-} // namespace base
namespace offline_pages {
struct ClientId;
-struct OfflinePageItem;
// Service for saving pages offline, storing the offline copy and metadata, and
// retrieving them upon request.
@@ -74,6 +70,9 @@ class OfflinePageModel : public base::SupportsUserData {
// The original URL of the page to save. Empty if no redirect occurs.
GURL original_url;
+
+ // Whether the page is being saved in the background.
+ bool is_background;
};
// Observer of the OfflinePageModel.
@@ -82,9 +81,9 @@ class OfflinePageModel : public base::SupportsUserData {
// Invoked when the model has finished loading.
virtual void OfflinePageModelLoaded(OfflinePageModel* model) = 0;
- // Invoked when the model is being updated, due to adding, removing or
- // updating an offline page.
- virtual void OfflinePageModelChanged(OfflinePageModel* model) = 0;
+ // Invoked when the model is being updated due to adding an offline page.
+ virtual void OfflinePageAdded(OfflinePageModel* model,
+ const OfflinePageItem& added_page) = 0;
// Invoked when an offline copy related to |offline_id| was deleted.
virtual void OfflinePageDeleted(int64_t offline_id,
@@ -155,10 +154,6 @@ class OfflinePageModel : public base::SupportsUserData {
// Gets all offline pages.
virtual void GetAllPages(const MultipleOfflinePageItemCallback& callback) = 0;
- // Gets all offline pages including expired ones.
- virtual void GetAllPagesWithExpired(
- const MultipleOfflinePageItemCallback& callback) = 0;
-
// Gets all offline ids where the offline page has the matching client id.
virtual void GetOfflineIdsForClientId(
const ClientId& client_id,
@@ -176,12 +171,6 @@ class OfflinePageModel : public base::SupportsUserData {
URLSearchMode url_search_mode,
const MultipleOfflinePageItemCallback& callback) = 0;
- // Marks pages with |offline_ids| as expired and deletes the associated
- // archive files.
- virtual void ExpirePages(const std::vector<int64_t>& offline_ids,
- const base::Time& expiration_time,
- const base::Callback<void(bool)>& callback) = 0;
-
// Returns the policy controller.
virtual ClientPolicyController* GetPolicyController() = 0;
@@ -194,4 +183,4 @@ class OfflinePageModel : public base::SupportsUserData {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_H_
diff --git a/chromium/components/offline_pages/offline_page_model_event_logger.cc b/chromium/components/offline_pages/core/offline_page_model_event_logger.cc
index 0ca9d95d110..fe688a1eb05 100644
--- a/chromium/components/offline_pages/offline_page_model_event_logger.cc
+++ b/chromium/components/offline_pages/core/offline_page_model_event_logger.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_model_event_logger.h"
+#include "components/offline_pages/core/offline_page_model_event_logger.h"
namespace offline_pages {
diff --git a/chromium/components/offline_pages/offline_page_model_event_logger.h b/chromium/components/offline_pages/core/offline_page_model_event_logger.h
index f472ca0b355..fce89677de6 100644
--- a/chromium/components/offline_pages/offline_page_model_event_logger.h
+++ b/chromium/components/offline_pages/core/offline_page_model_event_logger.h
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
-#include "components/offline_pages/offline_event_logger.h"
+#include "components/offline_pages/core/offline_event_logger.h"
namespace offline_pages {
@@ -35,4 +35,4 @@ class OfflinePageModelEventLogger : public OfflineEventLogger {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
diff --git a/chromium/components/offline_pages/offline_page_model_event_logger_unittest.cc b/chromium/components/offline_pages/core/offline_page_model_event_logger_unittest.cc
index 1cc6fa26862..c623a0ffdb3 100644
--- a/chromium/components/offline_pages/offline_page_model_event_logger_unittest.cc
+++ b/chromium/components/offline_pages/core/offline_page_model_event_logger_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_model_event_logger.h"
+#include "components/offline_pages/core/offline_page_model_event_logger.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -76,4 +76,4 @@ TEST(OfflinePageModelEventLoggerTest, DoesNotExceedMaxSize) {
EXPECT_EQ(kMaxLogCount, log.size());
}
-} // namespace offline_internals
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/offline_page_model_impl.cc b/chromium/components/offline_pages/core/offline_page_model_impl.cc
index 627f7f8c48c..8c20f690926 100644
--- a/chromium/components/offline_pages/offline_page_model_impl.cc
+++ b/chromium/components/offline_pages/core/offline_page_model_impl.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_model_impl.h"
+#include "components/offline_pages/core/offline_page_model_impl.h"
#include <algorithm>
#include <limits>
@@ -19,12 +19,12 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/clock.h"
#include "base/time/time.h"
-#include "components/offline_pages/archive_manager.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model_query.h"
-#include "components/offline_pages/offline_page_storage_manager.h"
+#include "components/offline_pages/core/archive_manager.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model_query.h"
+#include "components/offline_pages/core/offline_page_storage_manager.h"
#include "url/gurl.h"
using ArchiverResult = offline_pages::OfflinePageArchiver::ArchiverResult;
@@ -128,13 +128,12 @@ void ReportStorageHistogramsAfterDelete(
}
void ReportSavePageResultHistogramAfterSave(const ClientId& client_id,
- SavePageResult result) {
+ SavePageResult result) {
// The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
// macro adapted to allow for a dynamically suffixed histogram name.
// Note: The factory creates and owns the histogram.
base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
- AddHistogramSuffix(client_id, "OfflinePages.SavePageResult"),
- 1,
+ AddHistogramSuffix(client_id, "OfflinePages.SavePageResult"), 1,
static_cast<int>(SavePageResult::RESULT_COUNT),
static_cast<int>(SavePageResult::RESULT_COUNT) + 1,
base::HistogramBase::kUmaTargetedHistogramFlag);
@@ -202,8 +201,8 @@ void ReportPageHistogramAfterSave(
// Note: The factory creates and owns the histogram.
// Reported as Kb between 1Kb and 10Mb.
histogram = base::Histogram::FactoryGet(
- AddHistogramSuffix(offline_page.client_id, "OfflinePages.PageSize"),
- 1, 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
+ AddHistogramSuffix(offline_page.client_id, "OfflinePages.PageSize"), 1,
+ 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add(offline_page.file_size / 1024);
if (policy_controller_->IsSupportedByDownload(
@@ -252,39 +251,39 @@ void ReportPageHistogramsAfterDelete(
// macro adapted to allow for a dynamically suffixed histogram name.
// Note: The factory creates and owns the histogram.
base::HistogramBase* histogram = base::Histogram::FactoryGet(
- AddHistogramSuffix(client_id, "OfflinePages.PageLifetime"),
- 1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
+ AddHistogramSuffix(client_id, "OfflinePages.PageLifetime"), 1,
+ max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add((delete_time - page.creation_time).InMinutes());
histogram = base::Histogram::FactoryGet(
- AddHistogramSuffix(
- client_id, "OfflinePages.DeletePage.TimeSinceLastOpen"),
+ AddHistogramSuffix(client_id,
+ "OfflinePages.DeletePage.TimeSinceLastOpen"),
1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add((delete_time - page.last_access_time).InMinutes());
histogram = base::Histogram::FactoryGet(
- AddHistogramSuffix(
- client_id, "OfflinePages.DeletePage.LastOpenToCreated"),
+ AddHistogramSuffix(client_id,
+ "OfflinePages.DeletePage.LastOpenToCreated"),
1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add((page.last_access_time - page.creation_time).InMinutes());
// Reported as Kb between 1Kb and 10Mb.
histogram = base::Histogram::FactoryGet(
- AddHistogramSuffix(client_id, "OfflinePages.DeletePage.PageSize"),
- 1, 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
+ AddHistogramSuffix(client_id, "OfflinePages.DeletePage.PageSize"), 1,
+ 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add(page.file_size / 1024);
histogram = base::Histogram::FactoryGet(
- AddHistogramSuffix(client_id, "OfflinePages.DeletePage.AccessCount"),
- 1, 1000000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
+ AddHistogramSuffix(client_id, "OfflinePages.DeletePage.AccessCount"), 1,
+ 1000000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add(page.access_count);
}
if (deleted_pages.size() > 1) {
UMA_HISTOGRAM_COUNTS("OfflinePages.BatchDelete.Count",
static_cast<int32_t>(deleted_pages.size()));
- UMA_HISTOGRAM_MEMORY_KB(
- "OfflinePages.BatchDelete.TotalPageSize", total_size / 1024);
+ UMA_HISTOGRAM_MEMORY_KB("OfflinePages.BatchDelete.TotalPageSize",
+ total_size / 1024);
}
}
@@ -294,13 +293,12 @@ void ReportPageHistogramsAfterAccess(const OfflinePageItem& offline_page_item,
// macro adapted to allow for a dynamically suffixed histogram name.
// Note: The factory creates and owns the histogram.
base::HistogramBase* histogram = base::Histogram::FactoryGet(
- AddHistogramSuffix(
- offline_page_item.client_id,
- offline_page_item.access_count == 0 ?
- "OfflinePages.FirstOpenSinceCreated" :
- "OfflinePages.OpenSinceLastOpen"),
- 1, kMaxOpenedPageHistogramBucket.InMinutes(), 50,
- base::HistogramBase::kUmaTargetedHistogramFlag);
+ AddHistogramSuffix(offline_page_item.client_id,
+ offline_page_item.access_count == 0
+ ? "OfflinePages.FirstOpenSinceCreated"
+ : "OfflinePages.OpenSinceLastOpen"),
+ 1, kMaxOpenedPageHistogramBucket.InMinutes(), 50,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add(
(access_time - offline_page_item.last_access_time).InMinutes());
}
@@ -363,8 +361,12 @@ void OfflinePageModelImpl::SavePage(
if (offline_id == kInvalidOfflineId)
offline_id = GenerateOfflineId();
+ OfflinePageArchiver::CreateArchiveParams create_archive_params;
+ // If the page is being saved in the background, we should try to remove the
+ // popup overlay that obstructs viewing the normal content.
+ create_archive_params.remove_popup_overlay = save_page_params.is_background;
archiver->CreateArchive(
- archives_dir_, offline_id,
+ archives_dir_, create_archive_params,
base::Bind(&OfflinePageModelImpl::OnCreateArchiveDone,
weak_ptr_factory_.GetWeakPtr(), save_page_params, offline_id,
GetCurrentTime(), callback));
@@ -380,7 +382,7 @@ void OfflinePageModelImpl::MarkPageAccessedWhenLoadDone(int64_t offline_id) {
DCHECK(is_loaded_);
auto iter = offline_pages_.find(offline_id);
- if (iter == offline_pages_.end() || iter->second.IsExpired())
+ if (iter == offline_pages_.end())
return;
// Make a copy of the cached item and update it. The cached item should only
@@ -392,7 +394,7 @@ void OfflinePageModelImpl::MarkPageAccessedWhenLoadDone(int64_t offline_id) {
offline_page_item.last_access_time = GetCurrentTime();
offline_page_item.access_count++;
- std::vector<OfflinePageItem> items = { offline_page_item };
+ std::vector<OfflinePageItem> items = {offline_page_item};
store_->UpdateOfflinePages(
items, base::Bind(&OfflinePageModelImpl::OnMarkPageAccesseDone,
weak_ptr_factory_.GetWeakPtr(), offline_page_item));
@@ -414,7 +416,7 @@ void OfflinePageModelImpl::DoDeletePagesByOfflineId(
std::vector<base::FilePath> paths_to_delete;
for (const auto& offline_id : offline_ids) {
auto iter = offline_pages_.find(offline_id);
- if (iter != offline_pages_.end() && !iter->second.IsExpired()) {
+ if (iter != offline_pages_.end()) {
paths_to_delete.push_back(iter->second.file_path);
}
}
@@ -435,16 +437,14 @@ void OfflinePageModelImpl::DeletePagesByClientIds(
const std::vector<ClientId>& client_ids,
const DeletePageCallback& callback) {
OfflinePageModelQueryBuilder builder;
- builder
- .SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
- client_ids)
- .AllowExpiredPages(true);
+ builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
+ client_ids);
auto delete_pages = base::Bind(&OfflinePageModelImpl::DeletePages,
weak_ptr_factory_.GetWeakPtr(), callback);
- RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(builder.Build(GetPolicyController())),
- delete_pages));
+ RunWhenLoaded(base::Bind(
+ &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(builder.Build(GetPolicyController())), delete_pages));
}
void OfflinePageModelImpl::DeletePages(
@@ -459,16 +459,40 @@ void OfflinePageModelImpl::DeletePages(
DoDeletePagesByOfflineId(offline_ids, callback);
}
+void OfflinePageModelImpl::GetPagesMatchingQuery(
+ std::unique_ptr<OfflinePageModelQuery> query,
+ const MultipleOfflinePageItemCallback& callback) {
+ RunWhenLoaded(base::Bind(
+ &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+ weak_ptr_factory_.GetWeakPtr(), base::Passed(&query), callback));
+}
+
+void OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone(
+ std::unique_ptr<OfflinePageModelQuery> query,
+ const MultipleOfflinePageItemCallback& callback) {
+ DCHECK(query);
+ DCHECK(is_loaded_);
+
+ MultipleOfflinePageItemResult offline_pages_result;
+
+ for (const auto& id_page_pair : offline_pages_) {
+ if (query->Matches(id_page_pair.second))
+ offline_pages_result.emplace_back(id_page_pair.second);
+ }
+
+ callback.Run(offline_pages_result);
+}
+
void OfflinePageModelImpl::GetPagesByClientIds(
const std::vector<ClientId>& client_ids,
const MultipleOfflinePageItemCallback& callback) {
OfflinePageModelQueryBuilder builder;
builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
client_ids);
- RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(builder.Build(GetPolicyController())),
- callback));
+ RunWhenLoaded(
+ base::Bind(&OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(builder.Build(GetPolicyController())), callback));
}
void OfflinePageModelImpl::DeleteCachedPagesByURLPredicate(
@@ -512,45 +536,19 @@ void OfflinePageModelImpl::CheckPagesExistOffline(
callback.Run(result);
},
callback);
- RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(builder.Build(GetPolicyController())),
- pages_to_urls));
-}
-
-void OfflinePageModelImpl::GetPagesMatchingQuery(
- std::unique_ptr<OfflinePageModelQuery> query,
- const MultipleOfflinePageItemCallback& callback) {
- DCHECK(query);
-
- MultipleOfflinePageItemResult offline_pages_result;
-
- for (const auto& id_page_pair : offline_pages_) {
- if (query->Matches(id_page_pair.second))
- offline_pages_result.emplace_back(id_page_pair.second);
- }
-
- callback.Run(offline_pages_result);
+ RunWhenLoaded(base::Bind(
+ &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(builder.Build(GetPolicyController())), pages_to_urls));
}
void OfflinePageModelImpl::GetAllPages(
const MultipleOfflinePageItemCallback& callback) {
OfflinePageModelQueryBuilder builder;
- RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(builder.Build(GetPolicyController())),
- callback));
-}
-
-void OfflinePageModelImpl::GetAllPagesWithExpired(
- const MultipleOfflinePageItemCallback& callback) {
- OfflinePageModelQueryBuilder builder;
- builder.AllowExpiredPages(true);
-
- RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(builder.Build(GetPolicyController())),
- callback));
+ RunWhenLoaded(
+ base::Bind(&OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(builder.Build(GetPolicyController())), callback));
}
void OfflinePageModelImpl::GetOfflineIdsForClientId(
@@ -564,6 +562,7 @@ void OfflinePageModelImpl::GetOfflineIdsForClientId(
void OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone(
const ClientId& client_id,
const MultipleOfflineIdCallback& callback) const {
+ DCHECK(is_loaded_);
callback.Run(MaybeGetOfflineIdsForClientId(client_id));
}
@@ -575,10 +574,8 @@ const std::vector<int64_t> OfflinePageModelImpl::MaybeGetOfflineIdsForClientId(
// We want only all pages, including those marked for deletion.
// TODO(fgorski): actually use an index rather than linear scan.
for (const auto& id_page_pair : offline_pages_) {
- if (id_page_pair.second.client_id == client_id &&
- !id_page_pair.second.IsExpired()) {
+ if (id_page_pair.second.client_id == client_id)
results.push_back(id_page_pair.second.offline_id);
- }
}
return results;
}
@@ -605,10 +602,10 @@ void OfflinePageModelImpl::GetPageByOfflineId(
},
callback);
- RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(builder.Build(GetPolicyController())),
- multiple_callback));
+ RunWhenLoaded(base::Bind(
+ &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(builder.Build(GetPolicyController())), multiple_callback));
}
void OfflinePageModelImpl::GetPagesByURL(
@@ -625,6 +622,7 @@ void OfflinePageModelImpl::GetPagesByURLWhenLoadDone(
const GURL& url,
URLSearchMode url_search_mode,
const MultipleOfflinePageItemCallback& callback) const {
+ DCHECK(is_loaded_);
std::vector<OfflinePageItem> result;
GURL::Replacements remove_params;
@@ -634,8 +632,6 @@ void OfflinePageModelImpl::GetPagesByURLWhenLoadDone(
url.ReplaceComponents(remove_params);
for (const auto& id_page_pair : offline_pages_) {
- if (id_page_pair.second.IsExpired())
- continue;
// First, search by last committed URL with fragment stripped.
if (url_without_fragment ==
id_page_pair.second.url.ReplaceComponents(remove_params)) {
@@ -661,65 +657,6 @@ void OfflinePageModelImpl::CheckMetadataConsistency() {
weak_ptr_factory_.GetWeakPtr()));
}
-void OfflinePageModelImpl::ExpirePages(
- const std::vector<int64_t>& offline_ids,
- const base::Time& expiration_time,
- const base::Callback<void(bool)>& callback) {
- std::vector<base::FilePath> paths_to_delete;
- std::vector<OfflinePageItem> items_to_update;
- for (int64_t offline_id : offline_ids) {
- auto iter = offline_pages_.find(offline_id);
- if (iter == offline_pages_.end())
- continue;
-
- OfflinePageItem offline_page = iter->second;
- paths_to_delete.push_back(offline_page.file_path);
- offline_page.expiration_time = expiration_time;
-
- items_to_update.push_back(offline_page);
- }
-
- store_->UpdateOfflinePages(
- items_to_update,
- base::Bind(&OfflinePageModelImpl::OnExpirePageDone,
- weak_ptr_factory_.GetWeakPtr(), expiration_time));
-
- if (paths_to_delete.empty()) {
- callback.Run(true);
- return;
- }
- archive_manager_->DeleteMultipleArchives(paths_to_delete, callback);
-}
-
-void OfflinePageModelImpl::OnExpirePageDone(
- const base::Time& expiration_time,
- std::unique_ptr<OfflinePagesUpdateResult> result) {
- UMA_HISTOGRAM_BOOLEAN("OfflinePages.ExpirePage.StoreUpdateResult",
- result->updated_items.size() > 0);
- for (const auto& expired_page : result->updated_items) {
- const auto& iter = offline_pages_.find(expired_page.offline_id);
- if (iter == offline_pages_.end())
- continue;
-
- iter->second.expiration_time = expiration_time;
- ClientId client_id = iter->second.client_id;
- offline_event_logger_.RecordPageExpired(
- std::to_string(expired_page.offline_id));
- base::HistogramBase* histogram = base::Histogram::FactoryGet(
- AddHistogramSuffix(client_id, "OfflinePages.ExpirePage.PageLifetime"),
- 1, base::TimeDelta::FromDays(30).InMinutes(), 50,
- base::HistogramBase::kUmaTargetedHistogramFlag);
- histogram->Add((expiration_time - iter->second.creation_time).InMinutes());
- histogram = base::Histogram::FactoryGet(
- AddHistogramSuffix(client_id,
- "OfflinePages.ExpirePage.TimeSinceLastAccess"),
- 1, base::TimeDelta::FromDays(30).InMinutes(), 50,
- base::HistogramBase::kUmaTargetedHistogramFlag);
- histogram->Add(
- (expiration_time - iter->second.last_access_time).InMinutes());
- }
-}
-
ClientPolicyController* OfflinePageModelImpl::GetPolicyController() {
return policy_controller_.get();
}
@@ -775,15 +712,17 @@ void OfflinePageModelImpl::OnCreateArchiveDone(
store_->AddOfflinePage(offline_page_item,
base::Bind(&OfflinePageModelImpl::OnAddOfflinePageDone,
weak_ptr_factory_.GetWeakPtr(), archiver,
- callback, offline_page_item));
+ file_path, callback, offline_page_item));
}
void OfflinePageModelImpl::OnAddOfflinePageDone(
OfflinePageArchiver* archiver,
+ const base::FilePath& file_path,
const SavePageCallback& callback,
const OfflinePageItem& offline_page,
ItemActionStatus status) {
SavePageResult result;
+
if (status == ItemActionStatus::SUCCESS) {
offline_pages_[offline_page.offline_id] = offline_page;
result = SavePageResult::SUCCESS;
@@ -793,6 +732,8 @@ void OfflinePageModelImpl::OnAddOfflinePageDone(
offline_page.client_id.name_space, offline_page.url.spec(),
std::to_string(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;
} else {
result = SavePageResult::STORE_FAILURE;
@@ -806,8 +747,13 @@ void OfflinePageModelImpl::OnAddOfflinePageDone(
}
DeletePendingArchiver(archiver);
+
+ // We don't want to notify observers if the add failed.
+ if (result != SavePageResult::SUCCESS)
+ return;
+
for (Observer& observer : observers_)
- observer.OfflinePageModelChanged(this);
+ observer.OfflinePageAdded(this, offline_page);
}
void OfflinePageModelImpl::OnMarkPageAccesseDone(
@@ -1034,11 +980,11 @@ void OfflinePageModelImpl::InformDeletePageDone(
void OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths(
const std::set<base::FilePath>& archive_paths) {
- ExpirePagesMissingArchiveFile(archive_paths);
+ DeletePagesMissingArchiveFile(archive_paths);
DeleteOrphanedArchives(archive_paths);
}
-void OfflinePageModelImpl::ExpirePagesMissingArchiveFile(
+void OfflinePageModelImpl::DeletePagesMissingArchiveFile(
const std::set<base::FilePath>& archive_paths) {
std::vector<int64_t> ids_of_pages_missing_archive_file;
for (const auto& id_page_pair : offline_pages_) {
@@ -1049,20 +995,22 @@ void OfflinePageModelImpl::ExpirePagesMissingArchiveFile(
if (ids_of_pages_missing_archive_file.empty())
return;
- ExpirePages(
- ids_of_pages_missing_archive_file, GetCurrentTime(),
- base::Bind(&OfflinePageModelImpl::OnExpirePagesMissingArchiveFileDone,
+ DeletePagesByOfflineId(
+ ids_of_pages_missing_archive_file,
+ base::Bind(&OfflinePageModelImpl::OnDeletePagesMissingArchiveFileDone,
weak_ptr_factory_.GetWeakPtr(),
ids_of_pages_missing_archive_file));
}
-void OfflinePageModelImpl::OnExpirePagesMissingArchiveFileDone(
+void OfflinePageModelImpl::OnDeletePagesMissingArchiveFileDone(
const std::vector<int64_t>& offline_ids,
- bool success) {
+ DeletePageResult result) {
UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.PagesMissingArchiveFileCount",
static_cast<int32_t>(offline_ids.size()));
- UMA_HISTOGRAM_BOOLEAN(
- "OfflinePages.Consistency.ExpirePagesMissingArchiveFileResult", success);
+ UMA_HISTOGRAM_ENUMERATION(
+ "OfflinePages.Consistency.DeletePagesMissingArchiveFileResult",
+ static_cast<int>(result),
+ static_cast<int>(DeletePageResult::RESULT_COUNT));
}
void OfflinePageModelImpl::DeleteOrphanedArchives(
@@ -1109,14 +1057,14 @@ void OfflinePageModelImpl::ClearStorageIfNeeded(
storage_manager_->ClearPagesIfNeeded(callback);
}
-void OfflinePageModelImpl::OnStorageCleared(size_t expired_page_count,
+void OfflinePageModelImpl::OnStorageCleared(size_t deleted_page_count,
ClearStorageResult result) {
UMA_HISTOGRAM_ENUMERATION("OfflinePages.ClearStorageResult",
static_cast<int>(result),
static_cast<int>(ClearStorageResult::RESULT_COUNT));
- if (expired_page_count > 0) {
- UMA_HISTOGRAM_COUNTS("OfflinePages.ExpirePage.BatchSize",
- static_cast<int32_t>(expired_page_count));
+ if (deleted_page_count > 0) {
+ UMA_HISTOGRAM_COUNTS("OfflinePages.ClearStorageBatchSize",
+ static_cast<int32_t>(deleted_page_count));
}
}
diff --git a/chromium/components/offline_pages/offline_page_model_impl.h b/chromium/components/offline_pages/core/offline_page_model_impl.h
index 28563290eb8..d0ca9bb588b 100644
--- a/chromium/components/offline_pages/offline_page_model_impl.h
+++ b/chromium/components/offline_pages/core/offline_page_model_impl.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_IMPL_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_IMPL_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_IMPL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_IMPL_H_
#include <stdint.h>
@@ -26,19 +26,18 @@
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "components/keyed_service/core/keyed_service.h"
-#include "components/offline_pages/offline_page_archiver.h"
-#include "components/offline_pages/offline_page_metadata_store.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/offline_page_model_event_logger.h"
-#include "components/offline_pages/offline_page_storage_manager.h"
-#include "components/offline_pages/offline_page_types.h"
-#include "components/offline_pages/offline_store_types.h"
+#include "components/offline_pages/core/offline_page_archiver.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_model_event_logger.h"
+#include "components/offline_pages/core/offline_page_storage_manager.h"
+#include "components/offline_pages/core/offline_page_types.h"
+#include "components/offline_pages/core/offline_store_types.h"
class GURL;
namespace base {
class Clock;
class SequencedTaskRunner;
-class TimeDelta;
class TimeTicks;
} // namespace base
@@ -90,8 +89,6 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
const std::set<GURL>& urls,
const CheckPagesExistOfflineCallback& callback) override;
void GetAllPages(const MultipleOfflinePageItemCallback& callback) override;
- void GetAllPagesWithExpired(
- const MultipleOfflinePageItemCallback& callback) override;
void GetOfflineIdsForClientId(
const ClientId& client_id,
const MultipleOfflineIdCallback& callback) override;
@@ -102,9 +99,6 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
const GURL& url,
URLSearchMode url_search_mode,
const MultipleOfflinePageItemCallback& callback) override;
- void ExpirePages(const std::vector<int64_t>& offline_ids,
- const base::Time& expiration_time,
- const base::Callback<void(bool)>& callback) override;
ClientPolicyController* GetPolicyController() override;
// Methods for testing only:
@@ -130,15 +124,12 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
// Callback for ensuring archive directory is created.
void OnEnsureArchivesDirCreatedDone(const base::TimeTicks& start_time);
- void CheckPagesExistOfflineAfterLoadDone(
- const std::set<GURL>& urls,
- const CheckPagesExistOfflineCallback& callback);
+ void GetPagesMatchingQueryWhenLoadDone(
+ std::unique_ptr<OfflinePageModelQuery> query,
+ const MultipleOfflinePageItemCallback& callback);
void GetOfflineIdsForClientIdWhenLoadDone(
const ClientId& client_id,
const MultipleOfflineIdCallback& callback) const;
- void GetPageByOfflineIdWhenLoadDone(
- int64_t offline_id,
- const SingleOfflinePageItemCallback& callback) const;
const std::vector<int64_t> MaybeGetOfflineIdsForClientId(
const ClientId& client_id) const;
void GetPagesByURLWhenLoadDone(
@@ -147,6 +138,11 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
const MultipleOfflinePageItemCallback& callback) const;
void MarkPageAccessedWhenLoadDone(int64_t offline_id);
+ // Check the consistency between metadata store and archives on disk,
+ // would delete the metadata entries which don't have an associated
+ // archive and the archives which doesn't have a metadata in the store.
+ // The expired pages (from previous versions) would also be cleared
+ // during this process.
void CheckMetadataConsistency();
// Callback for loading pages from the offline page metadata store.
@@ -173,6 +169,7 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
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);
@@ -198,14 +195,14 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
// Callbacks for checking metadata consistency.
void CheckMetadataConsistencyForArchivePaths(
const std::set<base::FilePath>& archive_paths);
- // Callback called after headless archives deleted. Orphaned archives are
- // archives files on disk which are not pointed to by any of the page items
- // in metadata store.
- void ExpirePagesMissingArchiveFile(
+ // Callbacks which would be called after orphaned archives are deleted.
+ // Orphaned archives are the files on disk which are not pointed to by any of
+ // the page entries in the metadata store.
+ void DeletePagesMissingArchiveFile(
const std::set<base::FilePath>& archive_paths);
- void OnExpirePagesMissingArchiveFileDone(
+ void OnDeletePagesMissingArchiveFileDone(
const std::vector<int64_t>& offline_ids,
- bool success);
+ DeletePageResult result);
void DeleteOrphanedArchives(const std::set<base::FilePath>& archive_paths);
void OnDeleteOrphanedArchivesDone(const std::vector<base::FilePath>& archives,
bool success);
@@ -235,16 +232,12 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
void DoDeleteCachedPagesByURLPredicate(const UrlPredicate& predicate,
const DeletePageCallback& callback);
- // Callback completing page expiration.
- void OnExpirePageDone(const base::Time& expiration_time,
- std::unique_ptr<OfflinePagesUpdateResult> result);
-
- // Clears expired pages if there are any.
+ // Clears expired pages if there are any or we're running out of storage.
void ClearStorageIfNeeded(
const OfflinePageStorageManager::ClearStorageCallback& callback);
// Callback completing storage clearing.
- void OnStorageCleared(size_t expired_page_count,
+ void OnStorageCleared(size_t cleared_page_count,
OfflinePageStorageManager::ClearStorageResult result);
// Post task to clear storage.
@@ -301,4 +294,4 @@ class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_IMPL_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_IMPL_H_
diff --git a/chromium/components/offline_pages/offline_page_model_impl_unittest.cc b/chromium/components/offline_pages/core/offline_page_model_impl_unittest.cc
index 6a01066fa29..b5806b222b7 100644
--- a/chromium/components/offline_pages/offline_page_model_impl_unittest.cc
+++ b/chromium/components/offline_pages/core/offline_page_model_impl_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_model_impl.h"
+#include "components/offline_pages/core/offline_page_model_impl.h"
#include <stdint.h>
#include <algorithm>
@@ -24,15 +24,15 @@
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_client_policy.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_storage_manager.h"
-#include "components/offline_pages/offline_page_test_archiver.h"
-#include "components/offline_pages/offline_page_test_store.h"
-#include "components/offline_pages/offline_page_types.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_client_policy.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_storage_manager.h"
+#include "components/offline_pages/core/offline_page_test_archiver.h"
+#include "components/offline_pages/core/offline_page_test_store.h"
+#include "components/offline_pages/core/offline_page_types.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -77,7 +77,8 @@ class OfflinePageModelImplTest
// OfflinePageModel::Observer implementation.
void OfflinePageModelLoaded(OfflinePageModel* model) override;
- void OfflinePageModelChanged(OfflinePageModel* model) override;
+ void OfflinePageAdded(OfflinePageModel* model,
+ const OfflinePageItem& added_page) override;
void OfflinePageDeleted(int64_t offline_id,
const ClientId& client_id) override;
@@ -126,6 +127,11 @@ class OfflinePageModelImplTest
void DeletePagesByClientIds(const std::vector<ClientId>& client_ids);
// Saves the page without waiting for it to finish.
+ void SavePageWithParamsAsync(
+ const OfflinePageModel::SavePageParams& save_page_params,
+ std::unique_ptr<OfflinePageArchiver> archiver);
+
+ // Saves the page without waiting for it to finish.
void SavePageWithArchiverAsync(
const GURL& url,
const ClientId& client_id,
@@ -232,17 +238,18 @@ void OfflinePageModelImplTest::OfflinePageModelLoaded(OfflinePageModel* model) {
ASSERT_EQ(model_.get(), model);
}
-void OfflinePageModelImplTest::OfflinePageModelChanged(
- 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::OfflinePageAdded(
+ OfflinePageModel* model,
+ const OfflinePageItem& added_page) {
+ ASSERT_EQ(model_.get(), model);
+}
+
void OfflinePageModelImplTest::SetLastPathCreatedByArchiver(
const base::FilePath& file_path) {
last_archiver_path_ = file_path;
@@ -318,6 +325,15 @@ OfflinePageTestStore* OfflinePageModelImplTest::GetStore() {
return static_cast<OfflinePageTestStore*>(model()->GetStoreForTesting());
}
+void OfflinePageModelImplTest::SavePageWithParamsAsync(
+ const OfflinePageModel::SavePageParams& save_page_params,
+ std::unique_ptr<OfflinePageArchiver> archiver) {
+ model()->SavePage(
+ save_page_params,
+ std::move(archiver),
+ base::Bind(&OfflinePageModelImplTest::OnSavePageDone, AsWeakPtr()));
+}
+
void OfflinePageModelImplTest::SavePageWithArchiverAsync(
const GURL& url,
const ClientId& client_id,
@@ -327,10 +343,8 @@ void OfflinePageModelImplTest::SavePageWithArchiverAsync(
save_page_params.url = url;
save_page_params.client_id = client_id;
save_page_params.original_url = original_url;
- model()->SavePage(
- save_page_params,
- std::move(archiver),
- base::Bind(&OfflinePageModelImplTest::OnSavePageDone, AsWeakPtr()));
+ save_page_params.is_background = false;
+ SavePageWithParamsAsync(save_page_params, std::move(archiver));
}
void OfflinePageModelImplTest::SavePageWithArchiver(
@@ -482,7 +496,7 @@ bool OfflinePageModelImplTest::HasPages(std::string name_space) {
TEST_F(OfflinePageModelImplTest, SavePageSuccessful) {
EXPECT_FALSE(HasPages(kTestClientNamespace));
- std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
+ std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
SavePageWithArchiverAsync(
kTestUrl, kTestClientId1, kTestUrl2, std::move(archiver));
@@ -578,6 +592,8 @@ TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverTwoPages) {
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);
@@ -635,6 +651,24 @@ TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverTwoPages) {
EXPECT_EQ(0, page2->flags);
}
+TEST_F(OfflinePageModelImplTest, SavePageOnBackground) {
+ std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
+ kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
+ // archiver_ptr will be valid until after first PumpLoop() is called.
+ OfflinePageTestArchiver* archiver_ptr = archiver.get();
+
+ OfflinePageModel::SavePageParams save_page_params;
+ save_page_params.url = kTestUrl;
+ save_page_params.client_id = kTestClientId1;
+ save_page_params.is_background = true;
+ SavePageWithParamsAsync(save_page_params, std::move(archiver));
+ EXPECT_TRUE(archiver_ptr->create_archive_called());
+ // |remove_popup_overlay| should be turned on on background mode.
+ EXPECT_TRUE(archiver_ptr->create_archive_params().remove_popup_overlay);
+
+ PumpLoop();
+}
+
TEST_F(OfflinePageModelImplTest, MarkPageAccessed) {
SavePage(kTestUrl, kTestClientId1);
@@ -1117,66 +1151,18 @@ TEST_F(OfflinePageModelImplTest, DownloadMetrics) {
"OfflinePages.DownloadDeletedPageDuplicateCount", 2, 1);
}
-TEST_F(OfflinePageModelImplTest, ExpirePages) {
- // We will save 3 pages and then expire 2 of them.
- std::pair<SavePageResult, int64_t> saved_pages[3];
- saved_pages[0] = SavePage(kTestUrl, kTestClientId1);
- saved_pages[1] = SavePage(kTestUrl2, kTestClientId2);
- saved_pages[2] = SavePage(kTestUrl3, kTestClientId3);
-
- for (const auto& save_result : saved_pages) {
- ASSERT_EQ(OfflinePageModel::SavePageResult::SUCCESS,
- std::get<0>(save_result));
- }
-
- // First two pages will be expired.
- std::vector<int64_t> pages_to_expire = {std::get<1>(saved_pages[0]),
- std::get<1>(saved_pages[1])};
- // Pages are marked as expired if they have an expiration_time set.
- base::Time expiration_time =
- base::Time::Now() + base::TimeDelta::FromMinutes(5);
-
- model()->ExpirePages(
- pages_to_expire, expiration_time,
- base::Bind(&OfflinePageModelImplTest::OnPagesExpired, AsWeakPtr()));
- PumpLoop();
-
- const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
- for (const auto& offline_page : offline_pages) {
- if (std::find(pages_to_expire.begin(), pages_to_expire.end(),
- offline_page.offline_id) != pages_to_expire.end()) {
- EXPECT_EQ(expiration_time, offline_page.expiration_time);
- EXPECT_TRUE(offline_page.IsExpired());
- } else {
- EXPECT_EQ(base::Time(), offline_page.expiration_time);
- EXPECT_FALSE(offline_page.IsExpired());
- }
- }
- EXPECT_TRUE(last_expire_page_result());
-}
-
TEST_F(OfflinePageModelImplTest, GetPagesByClientIds) {
- // We will save 3 pages. One will be expired.
+ // We will save 2 pages.
std::pair<SavePageResult, int64_t> saved_pages[3];
saved_pages[0] = SavePage(kTestUrl, kTestClientId1);
saved_pages[1] = SavePage(kTestUrl2, kTestClientId2);
- saved_pages[2] = SavePage(kTestUrl3, kTestClientId3);
for (const auto& save_result : saved_pages) {
ASSERT_EQ(OfflinePageModel::SavePageResult::SUCCESS,
std::get<0>(save_result));
}
- std::vector<int64_t> pages_to_expire = {std::get<1>(saved_pages[0])};
- // Pages are marked as expired if they have an expiration_time set.
- base::Time expiration_time =
- base::Time::Now() + base::TimeDelta::FromMinutes(5);
- model()->ExpirePages(
- pages_to_expire, expiration_time,
- base::Bind(&OfflinePageModelImplTest::OnPagesExpired, AsWeakPtr()));
- PumpLoop();
-
- std::vector<ClientId> client_ids = {kTestClientId1, kTestClientId2};
+ std::vector<ClientId> client_ids = {kTestClientId2};
std::vector<OfflinePageItem> offline_pages = GetPagesByClientIds(client_ids);
EXPECT_EQ(1U, offline_pages.size());
@@ -1271,6 +1257,37 @@ TEST_F(OfflinePageModelImplTest, StoreResetFailed) {
EXPECT_EQ(SavePageResult::STORE_FAILURE, result.first);
}
+TEST_F(OfflinePageModelImplTest, GetPagesMatchingQuery) {
+ std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
+ kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
+ SavePageWithArchiverAsync(kTestUrl, kTestClientId1, kTestUrl2,
+ std::move(archiver));
+ PumpLoop();
+
+ std::vector<ClientId> client_ids{kTestClientId1};
+ OfflinePageModelQueryBuilder builder;
+ builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
+ client_ids);
+
+ MultipleOfflinePageItemResult offline_pages;
+ model()->GetPagesMatchingQuery(
+ builder.Build(model()->GetPolicyController()),
+ base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
+ AsWeakPtr(), base::Unretained(&offline_pages)));
+ PumpLoop();
+
+ 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(last_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);
+}
+
TEST(CommandLineFlagsTest, OfflineBookmarks) {
// Disabled by default.
EXPECT_FALSE(offline_pages::IsOfflineBookmarksEnabled());
diff --git a/chromium/components/offline_pages/offline_page_model_query.cc b/chromium/components/offline_pages/core/offline_page_model_query.cc
index 57691cbe6d7..6aa192b6713 100644
--- a/chromium/components/offline_pages/offline_page_model_query.cc
+++ b/chromium/components/offline_pages/core/offline_page_model_query.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_model_query.h"
+#include "components/offline_pages/core/offline_page_model_query.h"
#include <algorithm>
#include <unordered_set>
@@ -61,19 +61,12 @@ OfflinePageModelQueryBuilder::RequireRestrictedToOriginalTab(
return *this;
}
-OfflinePageModelQueryBuilder& OfflinePageModelQueryBuilder::AllowExpiredPages(
- bool allow_expired) {
- allow_expired_ = allow_expired;
- return *this;
-}
-
std::unique_ptr<OfflinePageModelQuery> OfflinePageModelQueryBuilder::Build(
ClientPolicyController* controller) {
DCHECK(controller);
auto query = base::MakeUnique<OfflinePageModelQuery>();
- query->allow_expired_ = allow_expired_;
query->urls_ = std::make_pair(
urls_.first, std::set<GURL>(urls_.second.begin(), urls_.second.end()));
urls_ = std::make_pair(Requirement::UNSET, std::vector<GURL>());
@@ -165,14 +158,7 @@ OfflinePageModelQuery::GetRestrictedToUrls() const {
return urls_;
}
-bool OfflinePageModelQuery::GetAllowExpired() const {
- return allow_expired_;
-}
-
bool OfflinePageModelQuery::Matches(const OfflinePageItem& item) const {
- if (!allow_expired_ && item.IsExpired())
- return false;
-
switch (offline_ids_.first) {
case Requirement::UNSET:
break;
diff --git a/chromium/components/offline_pages/offline_page_model_query.h b/chromium/components/offline_pages/core/offline_page_model_query.h
index ddc8e837d83..526c3707ab9 100644
--- a/chromium/components/offline_pages/offline_page_model_query.h
+++ b/chromium/components/offline_pages/core/offline_page_model_query.h
@@ -2,16 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_QUERY_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_QUERY_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_QUERY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_QUERY_H_
#include <set>
#include <vector>
#include "base/memory/ptr_util.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_types.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_types.h"
namespace offline_pages {
@@ -40,8 +40,6 @@ class OfflinePageModelQuery {
std::pair<Requirement, std::set<ClientId>> GetRestrictedToClientIds() const;
std::pair<Requirement, std::set<GURL>> GetRestrictedToUrls() const;
- bool GetAllowExpired() const;
-
// This is the workhorse function that is used by the in-memory offline page
// model, given a page it will find out whether that page matches the query.
bool Matches(const OfflinePageItem& page) const;
@@ -49,8 +47,6 @@ class OfflinePageModelQuery {
private:
friend class OfflinePageModelQueryBuilder;
- bool allow_expired_ = false;
-
std::unique_ptr<std::set<std::string>> restricted_to_namespaces_;
std::pair<Requirement, std::set<int64_t>> offline_ids_;
@@ -61,7 +57,7 @@ class OfflinePageModelQuery {
};
// Used to create an offline page model query. QueryBuilders without
-// modifications create queries that allow all pages that are not expired.
+// modifications create queries that allow all pages.
// Can restrict results by policies provided by |ClientPolicyController|, or by
// individual features of pages. Each restriction comes with a |Requirement|
// that can be used to specify whether the input restriction should include or
@@ -110,10 +106,6 @@ class OfflinePageModelQueryBuilder {
OfflinePageModelQueryBuilder& RequireRestrictedToOriginalTab(
Requirement original_tab);
- // Resets whether we return expired pages. If called multiple times the bit
- // is overwritten and |allow_expired| from the last call is saved.
- OfflinePageModelQueryBuilder& AllowExpiredPages(bool allow_expired);
-
// Builds the query using the namespace policies provided by |controller|
// This resets the internal state. |controller| should not be |nullptr|.
std::unique_ptr<OfflinePageModelQuery> Build(
@@ -135,11 +127,9 @@ class OfflinePageModelQueryBuilder {
Requirement shown_as_recently_visited_site_ = Requirement::UNSET;
Requirement restricted_to_original_tab_ = Requirement::UNSET;
- bool allow_expired_ = false;
-
DISALLOW_COPY_AND_ASSIGN(OfflinePageModelQueryBuilder);
};
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_QUERY_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_QUERY_H_
diff --git a/chromium/components/offline_pages/offline_page_model_query_unittest.cc b/chromium/components/offline_pages/core/offline_page_model_query_unittest.cc
index f8e300646fd..fc05633013b 100644
--- a/chromium/components/offline_pages/offline_page_model_query_unittest.cc
+++ b/chromium/components/offline_pages/core/offline_page_model_query_unittest.cc
@@ -5,9 +5,9 @@
#include "base/time/time.h"
#include "components/offline_pages/client_namespace_constants.h"
#include "components/offline_pages/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_model_query.h"
#include "components/offline_pages/offline_page_client_policy.h"
#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model_query.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -37,14 +37,6 @@ class OfflinePageModelQueryTest : public testing::Test {
ClientPolicyController policy_;
OfflinePageModelQueryBuilder builder_;
- const OfflinePageItem expired_page() {
- OfflinePageItem expiredTestItem(GURL("https://ktestitem1.com"), 3,
- kClientId1, base::FilePath(), 3);
- expiredTestItem.expiration_time = base::Time::Now();
-
- return expiredTestItem;
- }
-
const OfflinePageItem download_page() {
return OfflinePageItem(GURL("https://download.com"), 4,
{kDownloadNamespace, "id1"}, base::FilePath(), 4);
@@ -81,13 +73,11 @@ TEST_F(OfflinePageModelQueryTest, DefaultValues) {
std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
EXPECT_NE(nullptr, query.get());
- EXPECT_FALSE(query->GetAllowExpired());
EXPECT_EQ(Requirement::UNSET, query->GetRestrictedToOfflineIds().first);
EXPECT_FALSE(query->GetRestrictedToNamespaces().first);
EXPECT_TRUE(query->Matches(kTestItem1));
EXPECT_TRUE(query->Matches(kTestItem2));
- EXPECT_FALSE(query->Matches(expired_page()));
}
TEST_F(OfflinePageModelQueryTest, OfflinePageIdsSet_Exclude) {
@@ -288,18 +278,6 @@ TEST_F(OfflinePageModelQueryTest, UrlsReplace) {
EXPECT_TRUE(query->Matches(kTestItem2));
}
-TEST_F(OfflinePageModelQueryTest, AllowExpired) {
- std::unique_ptr<OfflinePageModelQuery> query =
- builder_.AllowExpiredPages(true).Build(&policy_);
-
- EXPECT_NE(nullptr, query.get());
- EXPECT_TRUE(query->GetAllowExpired());
-
- EXPECT_TRUE(query->Matches(kTestItem1));
- EXPECT_TRUE(query->Matches(kTestItem2));
- EXPECT_TRUE(query->Matches(expired_page()));
-}
-
TEST_F(OfflinePageModelQueryTest, RequireSupportedByDownload_Only) {
builder_.RequireSupportedByDownload(Requirement::INCLUDE_MATCHING);
std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
diff --git a/chromium/components/offline_pages/offline_page_storage_manager.cc b/chromium/components/offline_pages/core/offline_page_storage_manager.cc
index c26a7e0d2b9..efe9ec2f856 100644
--- a/chromium/components/offline_pages/offline_page_storage_manager.cc
+++ b/chromium/components/offline_pages/core/offline_page_storage_manager.cc
@@ -2,17 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_storage_manager.h"
+#include "components/offline_pages/core/offline_page_storage_manager.h"
#include <algorithm>
#include "base/bind.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_client_policy.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_client_policy.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
using LifetimeType = offline_pages::LifetimePolicy::LifetimeType;
@@ -69,68 +69,43 @@ void OfflinePageStorageManager::OnGetAllPagesDoneForClearingPages(
const ClearStorageCallback& callback,
const ArchiveManager::StorageStats& stats,
const MultipleOfflinePageItemResult& pages) {
- std::vector<int64_t> page_ids_to_expire;
- std::vector<int64_t> page_ids_to_remove;
- GetPageIdsToClear(pages, stats, &page_ids_to_expire, &page_ids_to_remove);
- model_->ExpirePages(
- page_ids_to_expire, clear_time_,
- base::Bind(&OfflinePageStorageManager::OnPagesExpired,
- weak_ptr_factory_.GetWeakPtr(), callback,
- page_ids_to_expire.size(), page_ids_to_remove));
-}
-
-void OfflinePageStorageManager::OnPagesExpired(
- const ClearStorageCallback& callback,
- size_t pages_expired,
- const std::vector<int64_t>& page_ids_to_remove,
- bool expiration_succeeded) {
- // We want to delete the outdated page records regardless the expiration
- // succeeded or not.
+ std::vector<int64_t> page_ids_to_clear;
+ GetPageIdsToClear(pages, stats, &page_ids_to_clear);
model_->DeletePagesByOfflineId(
- page_ids_to_remove,
- base::Bind(&OfflinePageStorageManager::OnOutdatedPagesCleared,
- weak_ptr_factory_.GetWeakPtr(), callback, pages_expired,
- expiration_succeeded));
+ page_ids_to_clear,
+ base::Bind(&OfflinePageStorageManager::OnExpiredPagesCleared,
+ weak_ptr_factory_.GetWeakPtr(), callback,
+ page_ids_to_clear.size()));
}
-void OfflinePageStorageManager::OnOutdatedPagesCleared(
+void OfflinePageStorageManager::OnExpiredPagesCleared(
const ClearStorageCallback& callback,
size_t pages_cleared,
- bool expiration_succeeded,
DeletePageResult result) {
+ last_clear_time_ = clear_time_;
ClearStorageResult clear_result = ClearStorageResult::SUCCESS;
- if (!expiration_succeeded) {
- clear_result = ClearStorageResult::EXPIRE_FAILURE;
- if (result != DeletePageResult::SUCCESS)
- clear_result = ClearStorageResult::EXPIRE_AND_DELETE_FAILURES;
- } else if (result != DeletePageResult::SUCCESS) {
+ if (result != DeletePageResult::SUCCESS)
clear_result = ClearStorageResult::DELETE_FAILURE;
- }
- last_clear_time_ = clear_time_;
callback.Run(pages_cleared, clear_result);
}
void OfflinePageStorageManager::GetPageIdsToClear(
const MultipleOfflinePageItemResult& pages,
const ArchiveManager::StorageStats& stats,
- std::vector<int64_t>* page_ids_to_expire,
- std::vector<int64_t>* page_ids_to_remove) {
+ std::vector<int64_t>* page_ids_to_clear) {
// TODO(romax): See how persistent should be considered here.
// Creating a map from namespace to a vector of page items.
// Sort each vector based on last accessed time and all pages after index
- // min{size(), page_limit} should be expired. And then start iterating
- // backwards to expire pages.
+ // min{size(), page_limit} should be deleted.
std::map<std::string, std::vector<OfflinePageItem>> pages_map;
std::vector<OfflinePageItem> kept_pages;
int64_t kept_pages_size = 0;
for (const auto& page : pages) {
- if (!page.IsExpired()) {
+ if (IsExpired(page))
+ page_ids_to_clear->push_back(page.offline_id);
+ else
pages_map[page.client_id.name_space].push_back(page);
- } else if (clear_time_ - page.expiration_time >=
- constants::kRemovePageItemInterval) {
- page_ids_to_remove->push_back(page.offline_id);
- }
}
for (auto& iter : pages_map) {
@@ -139,6 +114,10 @@ void OfflinePageStorageManager::GetPageIdsToClear(
LifetimePolicy policy =
policy_controller_->GetPolicy(name_space).lifetime_policy;
+ // Storage manager only manages temporary offlined pages, so we shouldn't
+ // clear any persistent pages here.
+ if (policy.lifetime_type == LifetimeType::PERSISTENT)
+ continue;
std::sort(page_list.begin(), page_list.end(),
[](const OfflinePageItem& a, const OfflinePageItem& b) -> bool {
@@ -148,15 +127,14 @@ void OfflinePageStorageManager::GetPageIdsToClear(
size_t page_list_size = page_list.size();
size_t pos = 0;
while (pos < page_list_size &&
- (policy.page_limit == kUnlimitedPages || pos < policy.page_limit) &&
- !ShouldBeExpired(page_list.at(pos))) {
+ (policy.page_limit == kUnlimitedPages || pos < policy.page_limit)) {
kept_pages_size += page_list.at(pos).file_size;
kept_pages.push_back(page_list.at(pos));
pos++;
}
for (; pos < page_list_size; pos++)
- page_ids_to_expire->push_back(page_list.at(pos).offline_id);
+ page_ids_to_clear->push_back(page_list.at(pos).offline_id);
}
// If we're still over the clear threshold, we're going to clear remaining
@@ -176,7 +154,7 @@ void OfflinePageStorageManager::GetPageIdsToClear(
size_t pos = 0;
while (pos < kept_pages_size && space_to_release > 0) {
space_to_release -= kept_pages.at(pos).file_size;
- page_ids_to_expire->push_back(kept_pages.at(pos).offline_id);
+ page_ids_to_clear->push_back(kept_pages.at(pos).offline_id);
pos++;
}
}
@@ -206,8 +184,7 @@ OfflinePageStorageManager::ShouldClearPages(
return ClearMode::NOT_NEEDED;
}
-bool OfflinePageStorageManager::ShouldBeExpired(
- const OfflinePageItem& page) const {
+bool OfflinePageStorageManager::IsExpired(const OfflinePageItem& page) const {
const LifetimePolicy& policy =
policy_controller_->GetPolicy(page.client_id.name_space).lifetime_policy;
return policy.lifetime_type == LifetimeType::TEMPORARY &&
diff --git a/chromium/components/offline_pages/offline_page_storage_manager.h b/chromium/components/offline_pages/core/offline_page_storage_manager.h
index 914212d2cd2..7b206c7ed02 100644
--- a/chromium/components/offline_pages/offline_page_storage_manager.h
+++ b/chromium/components/offline_pages/core/offline_page_storage_manager.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_STORAGE_MANAGER_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_STORAGE_MANAGER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_STORAGE_MANAGER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_STORAGE_MANAGER_H_
#include <stdint.h>
@@ -13,8 +13,8 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
-#include "components/offline_pages/archive_manager.h"
-#include "components/offline_pages/offline_page_types.h"
+#include "components/offline_pages/core/archive_manager.h"
+#include "components/offline_pages/core/offline_page_types.h"
namespace base {
class Clock;
@@ -42,7 +42,7 @@ class ClientPolicyController;
class OfflinePageModel;
// This class is used for storage management of offline pages. It provides
-// a ClearPagesIfNeeded method which is used to clear expired offline pages
+// a ClearPagesIfNeeded method which is used to clear outdated offline pages
// based on last_access_time and lifetime policy of its namespace.
// It has its own throttle mechanism so calling the method would not be
// guaranteed to clear the pages immediately.
@@ -52,11 +52,12 @@ class OfflinePageModel;
class OfflinePageStorageManager {
public:
enum class ClearStorageResult {
- SUCCESS, // Cleared successfully.
- UNNECESSARY, // No expired pages.
- EXPIRE_FAILURE, // Expiration failed.
- DELETE_FAILURE, // Deletion failed.
- EXPIRE_AND_DELETE_FAILURES, // Both expiration and deletion failed.
+ SUCCESS, // Cleared successfully.
+ UNNECESSARY, // No expired pages.
+ DEPRECATED_EXPIRE_FAILURE, // Expiration failed. (DEPRECATED)
+ DELETE_FAILURE, // Deletion failed.
+ DEPRECATED_EXPIRE_AND_DELETE_FAILURES, // Both expiration and deletion
+ // failed. (DEPRECATED)
// 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.
@@ -64,8 +65,8 @@ class OfflinePageStorageManager {
};
// Callback used when calling ClearPagesIfNeeded.
- // size_t: the number of expired pages.
- // ClearStorageResult: result of expiring pages in storage.
+ // size_t: the number of cleared pages.
+ // ClearStorageResult: result of clearing pages in storage.
typedef base::Callback<void(size_t, ClearStorageResult)> ClearStorageCallback;
explicit OfflinePageStorageManager(OfflinePageModel* model,
@@ -87,11 +88,11 @@ class OfflinePageStorageManager {
private:
// Enum indicating how to clear the pages.
enum class ClearMode {
- // Using normal expiration logic to expire pages. Will reduce the storage
+ // Using normal expiration logic to clear pages. Will reduce the storage
// usage down below the threshold.
DEFAULT,
- // No need to expire any page (no pages in the model or no expired
- // pages and we're not exceeding the storage limit.)
+ // No need to clear any page (no pages in the model or no expired pages and
+ // we're not exceeding the storage limit.)
NOT_NEEDED,
};
@@ -106,32 +107,22 @@ class OfflinePageStorageManager {
const ArchiveManager::StorageStats& storage_stats,
const MultipleOfflinePageItemResult& pages);
- // Callback called after expired pages have been deleted.
- void OnPagesExpired(const ClearStorageCallback& callback,
- size_t pages_to_clear,
- const std::vector<int64_t>& page_ids_to_remove,
- bool expiration_succeeded);
-
- // Callback called after clearing outdated pages from model.
- void OnOutdatedPagesCleared(const ClearStorageCallback& callback,
- size_t pages_cleared,
- bool expiration_succeeded,
- DeletePageResult result);
-
- // Gets offline IDs of both pages that should be expired and the ones that
- // need to be removed from metadata store. |page_ids_to_expire| will have
- // the pages to be expired, |page_ids_to_remove| will have the pages to be
- // removed.
+ // Callback called after clearing expired pages from model.
+ void OnExpiredPagesCleared(const ClearStorageCallback& callback,
+ size_t pages_cleared,
+ DeletePageResult result);
+
+ // Gets offline IDs of pages that should be cleared based on current |stats|
+ // and return the IDs in |page_ids_to_clear|.
void GetPageIdsToClear(const MultipleOfflinePageItemResult& pages,
const ArchiveManager::StorageStats& stats,
- std::vector<int64_t>* page_ids_to_expire,
- std::vector<int64_t>* page_ids_to_remove);
+ std::vector<int64_t>* page_ids_to_clear);
// Determines if manager should clear pages.
ClearMode ShouldClearPages(const ArchiveManager::StorageStats& storage_stats);
- // Returns true if |page| is expired comparing to |clear_time_|.
- bool ShouldBeExpired(const OfflinePageItem& page) const;
+ // Returns true if |page| is should be cleared based on |clear_time_|.
+ bool IsExpired(const OfflinePageItem& page) const;
// Returns true if we're currently doing a cleanup.
bool IsInProgress() const;
@@ -162,4 +153,4 @@ class OfflinePageStorageManager {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_STORAGE_MANAGER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_STORAGE_MANAGER_H_
diff --git a/chromium/components/offline_pages/offline_page_storage_manager_unittest.cc b/chromium/components/offline_pages/core/offline_page_storage_manager_unittest.cc
index 372037a7e23..bc3208a2dcc 100644
--- a/chromium/components/offline_pages/offline_page_storage_manager_unittest.cc
+++ b/chromium/components/offline_pages/core/offline_page_storage_manager_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_storage_manager.h"
+#include "components/offline_pages/core/offline_page_storage_manager.h"
#include <stdint.h>
#include <map>
@@ -12,12 +12,12 @@
#include "base/files/file_path.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
-#include "components/offline_pages/archive_manager.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model_impl.h"
-#include "components/offline_pages/offline_page_types.h"
+#include "components/offline_pages/core/archive_manager.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model_impl.h"
+#include "components/offline_pages/core/offline_page_types.h"
#include "testing/gtest/include/gtest/gtest.h"
using LifetimePolicy = offline_pages::LifetimePolicy;
@@ -35,9 +35,7 @@ const int64_t kFreeSpaceNormal = 100 * (1 << 20);
enum TestOptions {
DEFAULT = 1 << 0,
- EXPIRE_FAILURE = 1 << 1,
- DELETE_FAILURE = 1 << 2,
- EXPIRE_AND_DELETE_FAILURES = EXPIRE_FAILURE | DELETE_FAILURE,
+ DELETE_FAILURE = 1 << 1,
};
struct PageSettings {
@@ -72,12 +70,10 @@ class OfflinePageTestModel : public OfflinePageModelImpl {
void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
const DeletePageCallback& callback) override;
- void ExpirePages(const std::vector<int64_t>& offline_ids,
- const base::Time& expiration_time,
- const base::Callback<void(bool)>& callback) override;
-
void AddPages(const PageSettings& setting);
+ // The |removed_pages_| would not be cleared in during a test, so the number
+ // of removed pages will be accumulative in a single test case.
const std::vector<OfflinePageItem>& GetRemovedPages() {
return removed_pages_;
}
@@ -100,19 +96,6 @@ class OfflinePageTestModel : public OfflinePageModelImpl {
int64_t next_offline_id_;
};
-void OfflinePageTestModel::ExpirePages(
- const std::vector<int64_t>& offline_ids,
- const base::Time& expiration_time,
- const base::Callback<void(bool)>& callback) {
- for (const auto id : offline_ids)
- pages_.at(id).expiration_time = expiration_time;
- if (options_ & TestOptions::EXPIRE_FAILURE) {
- callback.Run(false);
- return;
- }
- callback.Run(true);
-}
-
void OfflinePageTestModel::DeletePagesByOfflineId(
const std::vector<int64_t>& offline_ids,
const DeletePageCallback& callback) {
@@ -129,10 +112,8 @@ void OfflinePageTestModel::DeletePagesByOfflineId(
int64_t OfflinePageTestModel::GetTotalSize() const {
int64_t res = 0;
- for (const auto& id_page_pair : pages_) {
- if (!id_page_pair.second.IsExpired())
- res += id_page_pair.second.file_size;
- }
+ for (const auto& id_page_pair : pages_)
+ res += id_page_pair.second.file_size;
return res;
}
@@ -274,7 +255,7 @@ TEST_F(OfflinePageStorageManagerTest, TestClearPagesLessThanLimit) {
EXPECT_EQ(2, last_cleared_page_count());
EXPECT_EQ(1, total_cleared_times());
EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
+ EXPECT_EQ(2, static_cast<int>(model()->GetRemovedPages().size()));
}
TEST_F(OfflinePageStorageManagerTest, TestClearPagesMoreThanLimit) {
@@ -285,7 +266,7 @@ TEST_F(OfflinePageStorageManagerTest, TestClearPagesMoreThanLimit) {
EXPECT_EQ(45, last_cleared_page_count());
EXPECT_EQ(1, total_cleared_times());
EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
+ EXPECT_EQ(45, static_cast<int>(model()->GetRemovedPages().size()));
}
TEST_F(OfflinePageStorageManagerTest, TestClearPagesMoreFreshPages) {
@@ -297,7 +278,7 @@ TEST_F(OfflinePageStorageManagerTest, TestClearPagesMoreFreshPages) {
EXPECT_EQ(1, last_cleared_page_count());
EXPECT_EQ(1, total_cleared_times());
EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
+ EXPECT_EQ(1, static_cast<int>(model()->GetRemovedPages().size()));
}
TEST_F(OfflinePageStorageManagerTest, TestDeleteAsyncPages) {
@@ -321,26 +302,6 @@ TEST_F(OfflinePageStorageManagerTest, TestDeletionFailed) {
EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
}
-TEST_F(OfflinePageStorageManagerTest, TestRemoveFromStoreFailure) {
- Initialize(std::vector<PageSettings>({{kBookmarkNamespace, 10, 10}}), {0, 0},
- TestOptions::EXPIRE_FAILURE);
- clock()->Advance(base::TimeDelta::FromMinutes(30));
- TryClearPages();
- EXPECT_EQ(10, last_cleared_page_count());
- EXPECT_EQ(1, total_cleared_times());
- EXPECT_EQ(ClearStorageResult::EXPIRE_FAILURE, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
-}
-
-TEST_F(OfflinePageStorageManagerTest, TestBothFailure) {
- Initialize(std::vector<PageSettings>({{kBookmarkNamespace, 10, 10}}), {0, 0},
- TestOptions::EXPIRE_AND_DELETE_FAILURES);
- clock()->Advance(base::TimeDelta::FromMinutes(30));
- TryClearPages();
- EXPECT_EQ(ClearStorageResult::EXPIRE_AND_DELETE_FAILURES,
- last_clear_storage_result());
-}
-
TEST_F(OfflinePageStorageManagerTest, TestStorageTimeInterval) {
Initialize(std::vector<PageSettings>(
{{kBookmarkNamespace, 10, 10}, {kLastNNamespace, 10, 10}}));
@@ -349,7 +310,7 @@ TEST_F(OfflinePageStorageManagerTest, TestStorageTimeInterval) {
EXPECT_EQ(20, last_cleared_page_count());
EXPECT_EQ(1, total_cleared_times());
EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
+ EXPECT_EQ(20, static_cast<int>(model()->GetRemovedPages().size()));
// Advance clock so we go over the gap, but no expired pages.
clock()->Advance(constants::kClearStorageInterval +
@@ -358,7 +319,7 @@ TEST_F(OfflinePageStorageManagerTest, TestStorageTimeInterval) {
EXPECT_EQ(0, last_cleared_page_count());
EXPECT_EQ(2, total_cleared_times());
EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
+ EXPECT_EQ(20, static_cast<int>(model()->GetRemovedPages().size()));
// Advance clock so we are still in the gap, should be unnecessary.
clock()->Advance(constants::kClearStorageInterval -
@@ -367,25 +328,7 @@ TEST_F(OfflinePageStorageManagerTest, TestStorageTimeInterval) {
EXPECT_EQ(0, last_cleared_page_count());
EXPECT_EQ(3, total_cleared_times());
EXPECT_EQ(ClearStorageResult::UNNECESSARY, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
-}
-
-TEST_F(OfflinePageStorageManagerTest, TestTwoStepExpiration) {
- Initialize(std::vector<PageSettings>({{kBookmarkNamespace, 10, 10}}));
- clock()->Advance(base::TimeDelta::FromMinutes(30));
- TryClearPages();
- EXPECT_EQ(10, last_cleared_page_count());
- EXPECT_EQ(1, total_cleared_times());
- EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
-
- clock()->Advance(constants::kRemovePageItemInterval +
- base::TimeDelta::FromDays(1));
- TryClearPages();
- EXPECT_EQ(10, last_cleared_page_count());
- EXPECT_EQ(2, total_cleared_times());
- EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- EXPECT_EQ(10, static_cast<int>(model()->GetRemovedPages().size()));
+ EXPECT_EQ(20, static_cast<int>(model()->GetRemovedPages().size()));
}
TEST_F(OfflinePageStorageManagerTest, TestClearMultipleTimes) {
@@ -401,7 +344,7 @@ TEST_F(OfflinePageStorageManagerTest, TestClearMultipleTimes) {
EXPECT_EQ(1, last_cleared_page_count());
EXPECT_EQ(1, total_cleared_times());
EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
+ EXPECT_EQ(1, static_cast<int>(model()->GetRemovedPages().size()));
// Advance the clock by expiration period of last_n namespace, should be
// expiring all pages left in the namespace.
@@ -410,7 +353,7 @@ TEST_F(OfflinePageStorageManagerTest, TestClearMultipleTimes) {
EXPECT_EQ(100, last_cleared_page_count());
EXPECT_EQ(2, total_cleared_times());
EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
+ EXPECT_EQ(101, static_cast<int>(model()->GetRemovedPages().size()));
// Only 1 ms passes and no changes in pages, so no need to clear page.
clock()->Advance(base::TimeDelta::FromMilliseconds(1));
@@ -418,7 +361,7 @@ TEST_F(OfflinePageStorageManagerTest, TestClearMultipleTimes) {
EXPECT_EQ(0, last_cleared_page_count());
EXPECT_EQ(3, total_cleared_times());
EXPECT_EQ(ClearStorageResult::UNNECESSARY, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
+ EXPECT_EQ(101, static_cast<int>(model()->GetRemovedPages().size()));
// Adding more fresh pages to make it go over limit.
clock()->Advance(base::TimeDelta::FromMinutes(5));
@@ -433,20 +376,19 @@ TEST_F(OfflinePageStorageManagerTest, TestClearMultipleTimes) {
model()->GetTotalSize());
EXPECT_EQ(4, total_cleared_times());
EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
- int expired_page_count = last_cleared_page_count();
+ int deleted_page_total = last_cleared_page_count() + 101;
+ EXPECT_EQ(deleted_page_total,
+ static_cast<int>(model()->GetRemovedPages().size()));
- // After more days, all pages should be expired and .
- clock()->Advance(constants::kRemovePageItemInterval +
- base::TimeDelta::FromDays(1));
+ // After more days, all pages should be deleted.
+ clock()->Advance(base::TimeDelta::FromDays(30));
TryClearPages();
- EXPECT_EQ(0, model()->GetTotalSize());
+ EXPECT_EQ(40 * kTestFileSize, model()->GetTotalSize());
EXPECT_EQ(5, total_cleared_times());
EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
- // Number of removed pages should be the ones expired above and all the pages
- // initially created for last_n namespace.
- EXPECT_EQ(expired_page_count + 101,
- static_cast<int>(model()->GetRemovedPages().size()));
+ // Number of removed pages should be all the temporary pages initially
+ // created plus 400 more pages added above for bookmark namespace.
+ EXPECT_EQ(131 + 400, static_cast<int>(model()->GetRemovedPages().size()));
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/offline_page_test_archiver.cc b/chromium/components/offline_pages/core/offline_page_test_archiver.cc
index 8ee7f37cd2c..145e5acbbe2 100644
--- a/chromium/components/offline_pages/offline_page_test_archiver.cc
+++ b/chromium/components/offline_pages/core/offline_page_test_archiver.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_test_archiver.h"
+#include "components/offline_pages/core/offline_page_test_archiver.h"
#include "base/bind.h"
#include "base/files/file_util.h"
@@ -34,11 +34,12 @@ OfflinePageTestArchiver::~OfflinePageTestArchiver() {
void OfflinePageTestArchiver::CreateArchive(
const base::FilePath& archives_dir,
- int64_t archive_id,
+ const CreateArchiveParams& create_archive_params,
const CreateArchiveCallback& callback) {
create_archive_called_ = true;
callback_ = callback;
archives_dir_ = archives_dir;
+ create_archive_params_ = create_archive_params;
if (!delayed_)
CompleteCreateArchive();
}
diff --git a/chromium/components/offline_pages/offline_page_test_archiver.h b/chromium/components/offline_pages/core/offline_page_test_archiver.h
index 97ba2af7607..6d7c04e8753 100644
--- a/chromium/components/offline_pages/offline_page_test_archiver.h
+++ b/chromium/components/offline_pages/core/offline_page_test_archiver.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_ARCHIVER_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_ARCHIVER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_ARCHIVER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_ARCHIVER_H_
#include <stddef.h>
#include <stdint.h>
@@ -12,7 +12,7 @@
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string16.h"
-#include "components/offline_pages/offline_page_archiver.h"
+#include "components/offline_pages/core/offline_page_archiver.h"
class GURL;
@@ -46,11 +46,11 @@ class OfflinePageTestArchiver : public OfflinePageArchiver {
// OfflinePageArchiver implementation:
void CreateArchive(const base::FilePath& archives_dir,
- int64_t archive_id,
+ const CreateArchiveParams& create_archive_params,
const CreateArchiveCallback& callback) override;
// Completes the creation of archive. Should be used with |set_delayed| set to
- // ture.
+ // true.
void CompleteCreateArchive();
// When set to true, |CompleteCreateArchive| should be called explicitly for
@@ -62,6 +62,10 @@ class OfflinePageTestArchiver : public OfflinePageArchiver {
// TODO(fgorski): See if we can move this to the constructor.
void set_filename(const base::FilePath& filename) { filename_ = filename; }
+ const CreateArchiveParams& create_archive_params() const {
+ return create_archive_params_;
+ }
+
bool create_archive_called() const { return create_archive_called_; }
private:
@@ -69,6 +73,7 @@ class OfflinePageTestArchiver : public OfflinePageArchiver {
Observer* observer_;
GURL url_;
base::FilePath archives_dir_;
+ CreateArchiveParams create_archive_params_;
base::FilePath filename_;
ArchiverResult result_;
int64_t size_to_report_;
@@ -83,4 +88,4 @@ class OfflinePageTestArchiver : public OfflinePageArchiver {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_ARCHIVER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_ARCHIVER_H_
diff --git a/chromium/components/offline_pages/offline_page_test_store.cc b/chromium/components/offline_pages/core/offline_page_test_store.cc
index 59f4906c2d8..556c12a2356 100644
--- a/chromium/components/offline_pages/offline_page_test_store.cc
+++ b/chromium/components/offline_pages/core/offline_page_test_store.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/offline_page_test_store.h"
+#include "components/offline_pages/core/offline_page_test_store.h"
#include <map>
diff --git a/chromium/components/offline_pages/offline_page_test_store.h b/chromium/components/offline_pages/core/offline_page_test_store.h
index 2304138ffd2..7b7599d8c75 100644
--- a/chromium/components/offline_pages/offline_page_test_store.h
+++ b/chromium/components/offline_pages/core/offline_page_test_store.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_STORE_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_STORE_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_STORE_H_
#include <stdint.h>
@@ -13,8 +13,8 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_metadata_store.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
namespace offline_pages {
@@ -74,4 +74,4 @@ class OfflinePageTestStore : public OfflinePageMetadataStore {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_STORE_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_STORE_H_
diff --git a/chromium/components/offline_pages/offline_page_types.h b/chromium/components/offline_pages/core/offline_page_types.h
index 8dbfa3d3f62..ae9e298af8e 100644
--- a/chromium/components/offline_pages/offline_page_types.h
+++ b/chromium/components/offline_pages/core/offline_page_types.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TYPES_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TYPES_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TYPES_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TYPES_H_
#include <stdint.h>
@@ -11,7 +11,7 @@
#include <vector>
#include "base/callback.h"
-#include "components/offline_pages/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_item.h"
class GURL;
@@ -75,4 +75,4 @@ typedef base::Callback<void(const MultipleOfflinePageItemResult&)>
typedef base::Callback<bool(const GURL&)> UrlPredicate;
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TYPES_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TYPES_H_
diff --git a/chromium/components/offline_pages/offline_store_types.h b/chromium/components/offline_pages/core/offline_store_types.h
index a4b3704b215..77d6a6a9bdf 100644
--- a/chromium/components/offline_pages/offline_store_types.h
+++ b/chromium/components/offline_pages/core/offline_store_types.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_STORE_TYPES_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_STORE_TYPES_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_STORE_TYPES_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_STORE_TYPES_H_
#include <stdint.h>
@@ -40,8 +40,7 @@ typedef std::vector<std::pair<int64_t, ItemActionStatus>> MultipleItemStatuses;
template <typename T>
class StoreUpdateResult {
public:
- explicit StoreUpdateResult(StoreState state)
- : store_state(state) {}
+ explicit StoreUpdateResult(StoreState state) : store_state(state) {}
~StoreUpdateResult() {}
// List of Offline ID to item action status mappings.
@@ -58,4 +57,4 @@ class StoreUpdateResult {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_STORE_TYPES_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_STORE_TYPES_H_
diff --git a/chromium/components/offline_pages/request_header/BUILD.gn b/chromium/components/offline_pages/core/request_header/BUILD.gn
index 711623cc348..711623cc348 100644
--- a/chromium/components/offline_pages/request_header/BUILD.gn
+++ b/chromium/components/offline_pages/core/request_header/BUILD.gn
diff --git a/chromium/components/offline_pages/request_header/offline_page_header.cc b/chromium/components/offline_pages/core/request_header/offline_page_header.cc
index db7b7c9e56d..9c58e7d82ed 100644
--- a/chromium/components/offline_pages/request_header/offline_page_header.cc
+++ b/chromium/components/offline_pages/core/request_header/offline_page_header.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/request_header/offline_page_header.h"
+#include "components/offline_pages/core/request_header/offline_page_header.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
@@ -82,8 +82,7 @@ std::string ReasonToString(OfflinePageHeader::Reason reason) {
OfflinePageHeader::OfflinePageHeader()
: did_fail_parsing_for_test(false),
need_to_persist(false),
- reason(Reason::NONE) {
-}
+ reason(Reason::NONE) {}
OfflinePageHeader::OfflinePageHeader(const std::string& header_value)
: did_fail_parsing_for_test(false),
@@ -108,13 +107,13 @@ std::string OfflinePageHeader::GetCompleteHeaderString() const {
value += "=";
value += need_to_persist ? "1" : "0";
- value += " " ;
+ value += " ";
value += kOfflinePageHeaderReasonKey;
value += "=";
value += ReasonToString(reason);
if (!id.empty()) {
- value += " " ;
+ value += " ";
value += kOfflinePageHeaderIDKey;
value += "=";
value += id;
diff --git a/chromium/components/offline_pages/request_header/offline_page_header.h b/chromium/components/offline_pages/core/request_header/offline_page_header.h
index 370fe667817..07175c6f0b1 100644
--- a/chromium/components/offline_pages/request_header/offline_page_header.h
+++ b/chromium/components/offline_pages/core/request_header/offline_page_header.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
-#define COMPONENTS_OFFLINE_PAGES_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
#include <string>
@@ -41,12 +41,7 @@ extern const char kOfflinePageHeaderIDKey[];
// loading behaviors.
struct OfflinePageHeader {
public:
- enum class Reason {
- NONE,
- NET_ERROR,
- DOWNLOAD,
- RELOAD
- };
+ enum class Reason { NONE, NET_ERROR, DOWNLOAD, RELOAD };
OfflinePageHeader();
@@ -77,4 +72,4 @@ struct OfflinePageHeader {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
diff --git a/chromium/components/offline_pages/request_header/offline_page_header_unittest.cc b/chromium/components/offline_pages/core/request_header/offline_page_header_unittest.cc
index 744a66f66f5..636c3bdbe96 100644
--- a/chromium/components/offline_pages/request_header/offline_page_header_unittest.cc
+++ b/chromium/components/offline_pages/core/request_header/offline_page_header_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/request_header/offline_page_header.h"
+#include "components/offline_pages/core/request_header/offline_page_header.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -64,7 +64,7 @@ TEST_F(OfflinePageHeaderTest, Parse) {
EXPECT_EQ("a1b2", id);
EXPECT_TRUE(ParseFromHeaderValue("persist=1 reason=download id=a1b2",
- &need_to_persist, &reason, &id));
+ &need_to_persist, &reason, &id));
EXPECT_TRUE(need_to_persist);
EXPECT_EQ(OfflinePageHeader::Reason::DOWNLOAD, reason);
EXPECT_EQ("a1b2", id);
diff --git a/chromium/components/offline_pages/snapshot_controller.cc b/chromium/components/offline_pages/core/snapshot_controller.cc
index 0ff9841847d..aa0b4270b02 100644
--- a/chromium/components/offline_pages/snapshot_controller.cc
+++ b/chromium/components/offline_pages/core/snapshot_controller.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/snapshot_controller.h"
+#include "components/offline_pages/core/snapshot_controller.h"
#include "base/bind.h"
#include "base/location.h"
@@ -29,8 +29,7 @@ SnapshotController::SnapshotController(
: task_runner_(task_runner),
client_(client),
state_(State::READY),
- delay_after_document_available_ms_(
- kDefaultDelayAfterDocumentAvailableMs),
+ delay_after_document_available_ms_(kDefaultDelayAfterDocumentAvailableMs),
delay_after_document_on_load_completed_ms_(
kDelayAfterDocumentOnLoadCompletedMs),
weak_ptr_factory_(this) {}
@@ -43,8 +42,7 @@ SnapshotController::SnapshotController(
: task_runner_(task_runner),
client_(client),
state_(State::READY),
- delay_after_document_available_ms_(
- delay_after_document_available_ms),
+ delay_after_document_available_ms_(delay_after_document_available_ms),
delay_after_document_on_load_completed_ms_(
delay_after_document_on_load_completed_ms),
weak_ptr_factory_(this) {}
@@ -74,8 +72,7 @@ void SnapshotController::DocumentAvailableInMainFrame() {
task_runner_->PostDelayedTask(
FROM_HERE, base::Bind(&SnapshotController::MaybeStartSnapshot,
weak_ptr_factory_.GetWeakPtr()),
- base::TimeDelta::FromMilliseconds(
- delay_after_document_available_ms_));
+ base::TimeDelta::FromMilliseconds(delay_after_document_available_ms_));
}
void SnapshotController::DocumentOnLoadCompletedInMainFrame() {
@@ -107,5 +104,4 @@ size_t SnapshotController::GetDelayAfterDocumentOnLoadCompletedForTest() {
return delay_after_document_on_load_completed_ms_;
}
-
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/snapshot_controller.h b/chromium/components/offline_pages/core/snapshot_controller.h
index c3ec36e7ec9..6a7bc64822f 100644
--- a/chromium/components/offline_pages/snapshot_controller.h
+++ b/chromium/components/offline_pages/core/snapshot_controller.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_SNAPSHOT_CONTROLLER_H_
-#define COMPONENTS_OFFLINE_PAGES_SNAPSHOT_CONTROLLER_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_SNAPSHOT_CONTROLLER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_SNAPSHOT_CONTROLLER_H_
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
@@ -32,7 +32,7 @@ class SnapshotController {
};
// Client of the SnapshotController.
- class Client {
+ class Client {
public:
// Invoked at a good moment to start a snapshot. May be invoked multiple
// times, but not in overlapping manner - waits until
@@ -94,4 +94,4 @@ class SnapshotController {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_SNAPSHOT_CONTROLLER_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_SNAPSHOT_CONTROLLER_H_
diff --git a/chromium/components/offline_pages/snapshot_controller_unittest.cc b/chromium/components/offline_pages/core/snapshot_controller_unittest.cc
index df561bc3f6b..390a96e952b 100644
--- a/chromium/components/offline_pages/snapshot_controller_unittest.cc
+++ b/chromium/components/offline_pages/core/snapshot_controller_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/snapshot_controller.h"
+#include "components/offline_pages/core/snapshot_controller.h"
#include "base/bind.h"
#include "base/run_loop.h"
@@ -14,9 +14,8 @@
namespace offline_pages {
-class SnapshotControllerTest
- : public testing::Test,
- public SnapshotController::Client {
+class SnapshotControllerTest : public testing::Test,
+ public SnapshotController::Client {
public:
SnapshotControllerTest();
~SnapshotControllerTest() override;
@@ -49,11 +48,9 @@ class SnapshotControllerTest
SnapshotControllerTest::SnapshotControllerTest()
: task_runner_(new base::TestMockTimeTaskRunner),
snapshot_started_(true),
- snapshot_count_(0) {
-}
+ snapshot_count_(0) {}
-SnapshotControllerTest::~SnapshotControllerTest() {
-}
+SnapshotControllerTest::~SnapshotControllerTest() {}
void SnapshotControllerTest::SetUp() {
controller_.reset(new SnapshotController(task_runner_, this));
diff --git a/chromium/components/offline_pages/stub_offline_page_model.cc b/chromium/components/offline_pages/core/stub_offline_page_model.cc
index fbfda38cb2a..dcc69c4b4d8 100644
--- a/chromium/components/offline_pages/stub_offline_page_model.cc
+++ b/chromium/components/offline_pages/core/stub_offline_page_model.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/offline_pages/stub_offline_page_model.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
namespace offline_pages {
@@ -36,8 +36,6 @@ void StubOfflinePageModel::CheckPagesExistOffline(
const CheckPagesExistOfflineCallback& callback) {}
void StubOfflinePageModel::GetAllPages(
const MultipleOfflinePageItemCallback& callback) {}
-void StubOfflinePageModel::GetAllPagesWithExpired(
- const MultipleOfflinePageItemCallback& callback) {}
void StubOfflinePageModel::GetOfflineIdsForClientId(
const ClientId& client_id,
const MultipleOfflineIdCallback& callback) {}
@@ -48,10 +46,6 @@ void StubOfflinePageModel::GetPagesByURL(
const GURL& url,
URLSearchMode url_search_mode,
const MultipleOfflinePageItemCallback& callback) {}
-void StubOfflinePageModel::ExpirePages(
- const std::vector<int64_t>& offline_ids,
- const base::Time& expiration_time,
- const base::Callback<void(bool)>& callback) {}
ClientPolicyController* StubOfflinePageModel::GetPolicyController() {
return &policy_controller_;
}
diff --git a/chromium/components/offline_pages/stub_offline_page_model.h b/chromium/components/offline_pages/core/stub_offline_page_model.h
index a1031fa3e18..6d3bc8e37c1 100644
--- a/chromium/components/offline_pages/stub_offline_page_model.h
+++ b/chromium/components/offline_pages/core/stub_offline_page_model.h
@@ -2,16 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_OFFLINE_PAGES_STUB_OFFLINE_PAGE_MODEL_H_
-#define COMPONENTS_OFFLINE_PAGES_STUB_OFFLINE_PAGE_MODEL_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_STUB_OFFLINE_PAGE_MODEL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_STUB_OFFLINE_PAGE_MODEL_H_
#include <set>
#include <string>
#include <vector>
#include "components/keyed_service/core/keyed_service.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_model.h"
namespace offline_pages {
@@ -46,8 +46,6 @@ class StubOfflinePageModel : public OfflinePageModel, public KeyedService {
const std::set<GURL>& urls,
const CheckPagesExistOfflineCallback& callback) override;
void GetAllPages(const MultipleOfflinePageItemCallback& callback) override;
- void GetAllPagesWithExpired(
- const MultipleOfflinePageItemCallback& callback) override;
void GetOfflineIdsForClientId(
const ClientId& client_id,
const MultipleOfflineIdCallback& callback) override;
@@ -58,9 +56,6 @@ class StubOfflinePageModel : public OfflinePageModel, public KeyedService {
const GURL& url,
URLSearchMode url_search_mode,
const MultipleOfflinePageItemCallback& callback) override;
- void ExpirePages(const std::vector<int64_t>& offline_ids,
- const base::Time& expiration_time,
- const base::Callback<void(bool)>& callback) override;
ClientPolicyController* GetPolicyController() override;
bool is_loaded() const override;
OfflineEventLogger* GetLogger() override;
@@ -72,4 +67,4 @@ class StubOfflinePageModel : public OfflinePageModel, public KeyedService {
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_STUB_OFFLINE_PAGE_MODEL_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_STUB_OFFLINE_PAGE_MODEL_H_
diff --git a/chromium/components/offline_pages/core/task.h b/chromium/components/offline_pages/core/task.h
index 4d77679ce07..8c259c33649 100644
--- a/chromium/components/offline_pages/core/task.h
+++ b/chromium/components/offline_pages/core/task.h
@@ -19,7 +19,9 @@ namespace offline_pages {
// * Derive your new task from offline_pages::Task.
// * Implement your task with as many async operations on the controlled
// resource as is required. (In general the smaller the task the better.)
-// * Whenever the task is terminated, call |Complete|.
+// * Whenever the task is terminated, call |Task::TaskComplete|. This step is
+// mandatory to ensure |TaskQueue| can pick another task. It should be called
+// once, but in every situation when task is exited.
// * To schedule task for execution call |TaskQueue::AddTask|.
//
// If there is a chance that a task callback will come after the task is
@@ -37,8 +39,6 @@ class Task {
// first step of the task should be implemented by overloading this method.
// The task should define an additional method for every asynchronous step,
// with each step setting up the next step as a callback.
- // TODO(fgorski): Consider alternative: protected RunImpl(), so that we can
- // add things like UMA in the Run method.
virtual void Run() = 0;
// Sets the callback normally used by |TaskQueue| for testing. See
diff --git a/chromium/components/offline_pages/core/task_queue.cc b/chromium/components/offline_pages/core/task_queue.cc
index 6f97413bd76..777a82d3583 100644
--- a/chromium/components/offline_pages/core/task_queue.cc
+++ b/chromium/components/offline_pages/core/task_queue.cc
@@ -18,7 +18,7 @@ void TaskQueue::AddTask(std::unique_ptr<Task> task) {
base::ThreadTaskRunnerHandle::Get(),
base::Bind(&TaskQueue::TaskCompleted, weak_ptr_factory_.GetWeakPtr()));
tasks_.push(std::move(task));
- MaybeStartTask();
+ StartTaskIfAvailable();
}
bool TaskQueue::HasPendingTasks() const {
@@ -29,7 +29,7 @@ bool TaskQueue::HasRunningTask() const {
return current_task_.get() != nullptr;
}
-void TaskQueue::MaybeStartTask() {
+void TaskQueue::StartTaskIfAvailable() {
DVLOG(2) << "running? " << HasRunningTask() << ", pending? "
<< HasPendingTasks() << " " << __func__;
if (HasRunningTask() || !HasPendingTasks())
@@ -44,7 +44,7 @@ void TaskQueue::TaskCompleted(Task* task) {
DCHECK_EQ(task, current_task_.get());
if (task == current_task_.get()) {
current_task_.reset(nullptr);
- MaybeStartTask();
+ StartTaskIfAvailable();
}
}
diff --git a/chromium/components/offline_pages/core/task_queue.h b/chromium/components/offline_pages/core/task_queue.h
index 941c47632a9..4d685603f18 100644
--- a/chromium/components/offline_pages/core/task_queue.h
+++ b/chromium/components/offline_pages/core/task_queue.h
@@ -43,7 +43,7 @@ class TaskQueue {
// Checks whether there are any tasks to run, as well as whether no task is
// currently running. When both are met, it will start the next task in the
// queue.
- void MaybeStartTask();
+ void StartTaskIfAvailable();
// Callback for informing the queue that a task was completed.
void TaskCompleted(Task* task);
diff --git a/chromium/components/omnibox/browser/BUILD.gn b/chromium/components/omnibox/browser/BUILD.gn
index 781c46fb34f..b0e6a38ed13 100644
--- a/chromium/components/omnibox/browser/BUILD.gn
+++ b/chromium/components/omnibox/browser/BUILD.gn
@@ -77,6 +77,8 @@ static_library("browser") {
"omnibox_switches.h",
"omnibox_view.cc",
"omnibox_view.h",
+ "physical_web_node.cc",
+ "physical_web_node.h",
"physical_web_provider.cc",
"physical_web_provider.h",
"scored_history_match.cc",
@@ -95,6 +97,8 @@ static_library("browser") {
"shortcuts_provider.h",
"suggestion_answer.cc",
"suggestion_answer.h",
+ "titled_url_match_utils.cc",
+ "titled_url_match_utils.h",
"url_index_private_data.cc",
"url_index_private_data.h",
"url_prefix.cc",
@@ -243,6 +247,7 @@ source_set("unit_tests") {
"shortcuts_database_unittest.cc",
"shortcuts_provider_unittest.cc",
"suggestion_answer_unittest.cc",
+ "titled_url_match_utils_unittest.cc",
"url_prefix_unittest.cc",
"zero_suggest_provider_unittest.cc",
]
@@ -257,6 +262,7 @@ source_set("unit_tests") {
"//components/history/core/test",
"//components/open_from_clipboard:test_support",
"//components/physical_web/data_source",
+ "//components/physical_web/data_source:test_support",
"//components/prefs:test_support",
"//components/search",
"//components/search_engines",
diff --git a/chromium/components/onc/docs/onc_spec.html b/chromium/components/onc/docs/onc_spec.html
index 2ef234452cb..3c6daf1591e 100644
--- a/chromium/components/onc/docs/onc_spec.html
+++ b/chromium/components/onc/docs/onc_spec.html
@@ -2397,6 +2397,17 @@
registered
</dd>
+ <dt class="field">SignalStrength</dt>
+ <dd>
+ <span class="field_meta">
+ (optional, read-only)
+ <span class="type">integer</span>
+ </span>
+ The current signal strength for this network in the range [0, 100],
+ provided by the system. If the network is not in range this field will
+ be set to '0' or not present.
+ </dd>
+
<dt class="field">SIMLockStatus</dt>
<dd>
<span class="field_meta">(optional, read-only, provided only
diff --git a/chromium/components/os_crypt/keychain_password_mac.h b/chromium/components/os_crypt/keychain_password_mac.h
index f044f04e3e1..2de865114c4 100644
--- a/chromium/components/os_crypt/keychain_password_mac.h
+++ b/chromium/components/os_crypt/keychain_password_mac.h
@@ -27,6 +27,10 @@ class KeychainPassword {
// empty string is returned.
std::string GetPassword() const;
+ // The service and account names used in Chrome's Safe Storage keychain item.
+ static const char service_name[];
+ static const char account_name[];
+
private:
const crypto::AppleKeychain& keychain_;
diff --git a/chromium/components/os_crypt/keychain_password_mac.mm b/chromium/components/os_crypt/keychain_password_mac.mm
index 1f60b9f0572..2b38db266f9 100644
--- a/chromium/components/os_crypt/keychain_password_mac.mm
+++ b/chromium/components/os_crypt/keychain_password_mac.mm
@@ -47,28 +47,23 @@ std::string AddRandomPasswordToKeychain(const AppleKeychain& keychain,
} // namespace
-std::string KeychainPassword::GetPassword() const {
- // These two strings ARE indeed user facing. But they are used to access
- // the encryption keyword. So as to not lose encrypted data when system
- // locale changes we DO NOT LOCALIZE.
+// These two strings ARE indeed user facing. But they are used to access
+// the encryption keyword. So as to not lose encrypted data when system
+// locale changes we DO NOT LOCALIZE.
#if defined(GOOGLE_CHROME_BUILD)
- const std::string service_name = "Chrome Safe Storage";
- const std::string account_name = "Chrome";
+const char KeychainPassword::service_name[] = "Chrome Safe Storage";
+const char KeychainPassword::account_name[] = "Chrome";
#else
- const std::string service_name = "Chromium Safe Storage";
- const std::string account_name = "Chromium";
+const char KeychainPassword::service_name[] = "Chromium Safe Storage";
+const char KeychainPassword::account_name[] = "Chromium";
#endif
+std::string KeychainPassword::GetPassword() const {
UInt32 password_length = 0;
void* password_data = NULL;
- OSStatus error = keychain_.FindGenericPassword(NULL,
- service_name.size(),
- service_name.data(),
- account_name.size(),
- account_name.data(),
- &password_length,
- &password_data,
- NULL);
+ OSStatus error = keychain_.FindGenericPassword(
+ nullptr, strlen(service_name), service_name, strlen(account_name),
+ account_name, &password_length, &password_data, NULL);
if (error == noErr) {
std::string password =
diff --git a/chromium/components/os_crypt/libsecret_util_linux.cc b/chromium/components/os_crypt/libsecret_util_linux.cc
index 611b0fede1e..b14bbb2e860 100644
--- a/chromium/components/os_crypt/libsecret_util_linux.cc
+++ b/chromium/components/os_crypt/libsecret_util_linux.cc
@@ -19,7 +19,7 @@ namespace {
// create, to explain its existence.
const char kExplanationMessage[] =
"Because of quirks in the gnome libsecret API, Chrome needs to store a "
- "dummy entry to quarantee that this keyring was properly unlocked. More "
+ "dummy entry to guarantee that this keyring was properly unlocked. More "
"details at http://crbug.com/660005.";
} // namespace
diff --git a/chromium/components/ownership/owner_key_util.h b/chromium/components/ownership/owner_key_util.h
index 07cd1b4c6b4..94d0e1aed78 100644
--- a/chromium/components/ownership/owner_key_util.h
+++ b/chromium/components/ownership/owner_key_util.h
@@ -20,8 +20,6 @@ typedef struct PK11SlotInfoStr PK11SlotInfo;
namespace ownership {
-class OwnerKeyUtilTest;
-
// This class is a ref-counted wrapper around a plain public key.
class OWNERSHIP_EXPORT PublicKey
: public base::RefCountedThreadSafe<PublicKey> {
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
index 6ad1349549a..4ee5bf13936 100644
--- a/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.h
+++ b/chromium/components/packed_ct_ev_whitelist/packed_ct_ev_whitelist.h
@@ -15,10 +15,6 @@
#include "base/version.h"
#include "net/cert/ct_ev_whitelist.h"
-namespace base {
-class FilePath;
-}
-
namespace packed_ct_ev_whitelist {
// An implementation of the EVCertsWhitelist that gets its data packed using
diff --git a/chromium/components/pageinfo_strings.grdp b/chromium/components/pageinfo_strings.grdp
index 36e825733e1..d5246fa4098 100644
--- a/chromium/components/pageinfo_strings.grdp
+++ b/chromium/components/pageinfo_strings.grdp
@@ -1,13 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
+ <!-- Summary strings -->
+ <message name="IDS_PAGEINFO_SECURE_SUMMARY" desc="A short summary phrase at the top of the Page Info bubble (which shows when you click the lock icon) that indicates that the connection to the current website is secure.">
+ Secure connection
+ </message>
+ <message name="IDS_PAGEINFO_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_PAGEINFO_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.">
+ Your connection to this site is not secure
+ </message>
+ <message name="IDS_PAGEINFO_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.">
+ This site contains malware
+ </message>
+ <message name="IDS_PAGEINFO_SOCIAL_ENGINEERING_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 social engineering.">
+ This site is deceptive
+ </message>
+ <message name="IDS_PAGEINFO_UNWANTED_SOFTWARE_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 unwanted software.">
+ This site contains harmful programs
+ </message>
+
+ <!-- Detail strings -->
+ <message name="IDS_PAGEINFO_SECURE_DETAILS" desc="A short paragraph explaining a secure site to the user.">
+ Your information (for example, passwords or credit card numbers) is private when it is sent to this site.
+ </message>
+ <message name="IDS_PAGEINFO_MIXED_CONTENT_DETAILS" desc="A short paragraph explaining a partially insecure site to the user.">
+ Attackers might be able to see the images you’re looking at on this site and trick you by modifying them.
+ </message>
+ <message name="IDS_PAGEINFO_NOT_SECURE_DETAILS" desc="A short paragraph explaining a non-secure site to the user.">
+ You should not enter any sensitive information on this site (for example, passwords or credit cards), because it could be stolen by attackers.
+ </message>
+ <message name="IDS_PAGEINFO_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.">
+ You have chosen to disable security warnings for this site.
+ </message>
+ <message name="IDS_PAGEINFO_MALWARE_DETAILS" desc="A short paragraph explaining to the user that the current website has been flagged as containing malware.">
+ 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).
+ </message>
+ <message name="IDS_PAGEINFO_SOCIAL_ENGINEERING_DETAILS" desc="A short paragraph explaining to the user that the current website has been flagged as social engineering.">
+ Attackers on this site may trick you into doing something dangerous like installing software or revealing your personal information (for example, passwords, phone numbers, or credit cards).
+ </message>
+ <message name="IDS_PAGEINFO_UNWANTED_SOFTWARE_DETAILS" desc="A short paragraph explaining to the user that the current website has been flagged as containing unwanted software.">
+ Attackers on this site might try to trick you into installing programs that harm your browsing experience (for example, by changing your homepage or showing extra ads on sites you visit).
+ </message>
+
<message name="IDS_PAGE_INFO_HELP_CENTER_LINK" desc="This is the text of the link pointing to the Help Center. This appears at the bottom of the SSL dialog and 'this' refers to the sections within the bubble.">
What do these mean?
</message>
- <message name="IDS_PAGE_INFO_SECURITY_TAB_DEPRECATED_SIGNATURE_ALGORITHM_MINOR" desc="The security summary phrase in the page information panel for a security problem where the site's certificate expires in 2016 and contains a SHA1 signature in the chain (as opposed to 2017 or later). Such certificates will not be supported in a future version of Chrome.">
- The certificate for this site expires in 2016, and the certificate chain contains a certificate signed using SHA-1.
- </message>
- <message name="IDS_PAGE_INFO_SECURITY_TAB_DEPRECATED_SIGNATURE_ALGORITHM_MAJOR" desc="The security summary phrase in the page information panel for a security problem where the site's certificate expires in 2017 or later and contains a SHA1 signature in the chain (as opposed to 2016). Such certificates will not be supported in a future version of Chrome.">
- The certificate for this site expires in 2017 or later, and the certificate chain contains a certificate signed using SHA-1.
+ <message name="IDS_PAGE_INFO_SECURITY_TAB_DEPRECATED_SIGNATURE_ALGORITHM" desc="The security summary phrase in the page information panel for a security problem where the site's certificate chain contains a SHA1 signature. Such certificates are treated as errors except when a policy override is present.">
+ The certificate chain for this site contains a certificate signed using SHA-1.
</message>
<message name="IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_CONNECTION_TEXT" desc="The text of the connection section when the connection is encrypted.">
Your connection to <ph name="DOMAIN">$1<ex>www.google.com</ex></ph> is encrypted using a modern cipher suite.
@@ -39,9 +79,6 @@
<message name="IDS_PAGE_INFO_SECURITY_TAB_NO_REVOCATION_MECHANISM" desc="The text of the identity section when there is no certificate revocation mechanism.">
The certificate does not specify a mechanism to check whether it has been revoked.
</message>
- <message name="IDS_PAGE_INFO_SECURITY_TAB_RENEGOTIATION_MESSAGE" desc="This message is displayed when the server hasn't been updated to fix a recent security issues. TLS here is an acronym and need not be translated. 'renegotiation' is a technical term describing a process of agreeing on a new set of security parameters and secrets. 'extension' here should be taken to mean 'amendment' rather than elongation.">
- The server does not support the TLS renegotiation extension.
- </message>
<message name="IDS_PAGE_INFO_SECURITY_TAB_SSL_VERSION" desc="This message gives the version of the SSL protocol used to protect the HTTPS connection.">
The connection uses <ph name="SSL_VERSION">$1<ex>TLS 1.0</ex></ph>.
</message>
diff --git a/chromium/components/pairing/controller_pairing_controller.h b/chromium/components/pairing/controller_pairing_controller.h
index 614c7c1b9d9..2eca3040b92 100644
--- a/chromium/components/pairing/controller_pairing_controller.h
+++ b/chromium/components/pairing/controller_pairing_controller.h
@@ -10,14 +10,6 @@
#include "base/macros.h"
-namespace chromeos {
-class UserContext;
-}
-
-namespace content {
-class BrowserContext;
-}
-
namespace pairing_chromeos {
class ControllerPairingController {
diff --git a/chromium/components/password_manager/OWNERS b/chromium/components/password_manager/OWNERS
index a1dcd02dd01..bc2ebbbc034 100644
--- a/chromium/components/password_manager/OWNERS
+++ b/chromium/components/password_manager/OWNERS
@@ -1,7 +1,6 @@
dvadym@chromium.org
engedy@chromium.org
gcasto@chromium.org
-isherman@chromium.org
melandory@chromium.org
mkwst@chromium.org
vabr@chromium.org
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 03c3425118f..a2b367aa9a9 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
@@ -43,6 +43,19 @@ ContentPasswordManagerDriver::ContentPasswordManagerDriver(
// 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
+ // cases, after the log manager got ready later,
+ // ContentPasswordManagerDriverFactory::RequestSendLoggingAvailability() will
+ // call ContentPasswordManagerDriver::SendLoggingAvailability() on |this| to
+ // do it actually.
+ if (client_->GetLogManager()) {
+ // Do not call the virtual method SendLoggingAvailability from a constructor
+ // here, inline its steps instead.
+ GetPasswordAutofillAgent()->SetLoggingState(
+ client_->GetLogManager()->IsLoggingActive());
+ }
}
ContentPasswordManagerDriver::~ContentPasswordManagerDriver() {
@@ -273,8 +286,10 @@ void ContentPasswordManagerDriver::ShowPasswordSuggestions(
key, text_direction, typed_username, options, bounds);
}
-void ContentPasswordManagerDriver::PasswordAutofillAgentConstructed() {
- SendLoggingAvailability();
+void ContentPasswordManagerDriver::ShowNotSecureWarning(
+ base::i18n::TextDirection text_direction,
+ const gfx::RectF& bounds) {
+ password_autofill_manager_.OnShowNotSecureWarning(text_direction, bounds);
}
void ContentPasswordManagerDriver::RecordSavePasswordProgress(
@@ -308,8 +323,8 @@ ContentPasswordManagerDriver::GetAutofillAgent() {
const autofill::mojom::PasswordAutofillAgentPtr&
ContentPasswordManagerDriver::GetPasswordAutofillAgent() {
if (!password_autofill_agent_) {
- autofill::mojom::PasswordAutofillAgentRequest request =
- mojo::GetProxy(&password_autofill_agent_);
+ autofill::mojom::PasswordAutofillAgentRequest request(
+ &password_autofill_agent_);
// Some test codes may have no initialized remote interfaces.
if (render_frame_host_->GetRemoteInterfaces()) {
render_frame_host_->GetRemoteInterfaces()->GetInterface(
@@ -324,7 +339,7 @@ const autofill::mojom::PasswordGenerationAgentPtr&
ContentPasswordManagerDriver::GetPasswordGenerationAgent() {
if (!password_gen_agent_) {
render_frame_host_->GetRemoteInterfaces()->GetInterface(
- mojo::GetProxy(&password_gen_agent_));
+ mojo::MakeRequest(&password_gen_agent_));
}
return password_gen_agent_;
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 77df860ab18..7188605e470 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
@@ -23,7 +23,6 @@
#include "third_party/WebKit/public/platform/modules/sensitive_input_visibility/sensitive_input_visibility_service.mojom.h"
namespace autofill {
-class AutofillManager;
struct PasswordForm;
}
@@ -31,11 +30,6 @@ namespace content {
struct FrameNavigateParams;
struct LoadCommittedDetails;
class RenderFrameHost;
-class WebContents;
-}
-
-namespace IPC {
-class Message;
}
namespace password_manager {
@@ -110,7 +104,8 @@ class ContentPasswordManagerDriver
const base::string16& typed_username,
int options,
const gfx::RectF& bounds) override;
- void PasswordAutofillAgentConstructed() override;
+ void ShowNotSecureWarning(base::i18n::TextDirection text_direction,
+ const gfx::RectF& bounds) override;
void RecordSavePasswordProgress(const std::string& log) override;
void SaveGenerationFieldDetectedByClassifier(
const autofill::PasswordForm& password_form,
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 da9172b2220..2c2e4cdc221 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
@@ -19,11 +19,6 @@
#include "content/public/browser/web_contents_observer.h"
#include "third_party/WebKit/public/platform/modules/sensitive_input_visibility/sensitive_input_visibility_service.mojom.h"
-namespace autofill {
-class AutofillManager;
-struct PasswordForm;
-}
-
namespace content {
class WebContents;
}
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 e186d0fe014..0606690226b 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
@@ -5,6 +5,7 @@
#include "components/password_manager/content/browser/content_password_manager_driver.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "components/autofill/content/common/autofill_agent.mojom.h"
#include "components/autofill/core/browser/test_autofill_client.h"
@@ -130,16 +131,14 @@ class ContentPasswordManagerDriverTest
FakePasswordAutofillAgent fake_agent_;
};
-TEST_P(ContentPasswordManagerDriverTest,
- AnswerToNotificationsAboutLoggingState) {
+TEST_P(ContentPasswordManagerDriverTest, SendLoggingStateInCtor) {
const bool should_allow_logging = GetParam();
+ EXPECT_CALL(log_manager_, IsLoggingActive())
+ .WillRepeatedly(Return(should_allow_logging));
std::unique_ptr<ContentPasswordManagerDriver> driver(
new ContentPasswordManagerDriver(main_rfh(), &password_manager_client_,
&autofill_client_));
- EXPECT_CALL(log_manager_, IsLoggingActive())
- .WillRepeatedly(Return(should_allow_logging));
- driver->SendLoggingAvailability();
if (should_allow_logging) {
bool logging_activated = false;
EXPECT_TRUE(WasLoggingActivationMessageSent(&logging_activated));
@@ -151,20 +150,22 @@ TEST_P(ContentPasswordManagerDriverTest,
}
}
-TEST_P(ContentPasswordManagerDriverTest, AnswerToIPCPingsAboutLoggingState) {
+TEST_P(ContentPasswordManagerDriverTest, SendLoggingStateAfterLogManagerReady) {
const bool should_allow_logging = GetParam();
+ EXPECT_CALL(password_manager_client_, GetLogManager())
+ .WillOnce(Return(nullptr));
std::unique_ptr<ContentPasswordManagerDriver> driver(
new ContentPasswordManagerDriver(main_rfh(), &password_manager_client_,
&autofill_client_));
+ // Because log manager is not ready yet, should have no logging state sent.
+ EXPECT_FALSE(WasLoggingActivationMessageSent(nullptr));
+ // Log manager is ready, send logging state actually.
+ EXPECT_CALL(password_manager_client_, GetLogManager())
+ .WillOnce(Return(&log_manager_));
EXPECT_CALL(log_manager_, IsLoggingActive())
.WillRepeatedly(Return(should_allow_logging));
driver->SendLoggingAvailability();
- WasLoggingActivationMessageSent(nullptr);
-
- // Ping the driver for logging activity update.
- driver->PasswordAutofillAgentConstructed();
-
bool logging_activated = false;
EXPECT_TRUE(WasLoggingActivationMessageSent(&logging_activated));
EXPECT_EQ(should_allow_logging, logging_activated);
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 2f53fc7f58f..7c01beaf281 100644
--- a/chromium/components/password_manager/content/browser/credential_manager_impl.cc
+++ b/chromium/components/password_manager/content/browser/credential_manager_impl.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
@@ -15,6 +16,8 @@
#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/credential_manager_logger.h"
+#include "components/password_manager/core/browser/form_fetcher_impl.h"
+#include "components/password_manager/core/browser/form_saver.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"
@@ -74,9 +77,14 @@ void CredentialManagerImpl::Store(const CredentialInfo& credential,
std::unique_ptr<autofill::PasswordForm> form(
CreatePasswordFormFromCredentialInfo(credential, origin));
- form_manager_.reset(new CredentialManagerPasswordFormManager(
- client_, GetDriver(), *CreateObservedPasswordFormFromOrigin(origin),
- std::move(form), this));
+ std::unique_ptr<autofill::PasswordForm> observed_form =
+ CreateObservedPasswordFormFromOrigin(origin);
+ // Create a custom form fetcher with suppressed HTTP->HTTPS migration.
+ auto form_fetcher = base::MakeUnique<FormFetcherImpl>(
+ PasswordStore::FormDigest(*observed_form), client_, false);
+ form_manager_ = base::MakeUnique<CredentialManagerPasswordFormManager>(
+ client_, GetDriver(), *observed_form, std::move(form), this, nullptr,
+ std::move(form_fetcher));
}
void CredentialManagerImpl::OnProvisionalSaveComplete() {
@@ -84,6 +92,14 @@ void CredentialManagerImpl::OnProvisionalSaveComplete() {
DCHECK(client_->IsSavingAndFillingEnabledForCurrentPage());
const autofill::PasswordForm& form = form_manager_->pending_credentials();
+ if (form_manager_->IsPendingCredentialsPublicSuffixMatch()) {
+ // Having a credential with a PSL match implies there is no credential with
+ // an exactly matching origin and username. In order to avoid showing a save
+ // bubble to the user Save() is called directly.
+ form_manager_->Save();
+ return;
+ }
+
if (!form.federation_origin.unique()) {
// If this is a federated credential, check it against the federated matches
// produced by the PasswordFormManager. If a match is found, update it and
@@ -118,46 +134,19 @@ void CredentialManagerImpl::RequireUserMediation(
CredentialManagerLogger(client_->GetLogManager())
.LogRequireUserMediation(web_contents()->GetLastCommittedURL());
}
+ // Send acknowledge response back.
+ callback.Run();
+
PasswordStore* store = GetPasswordStore();
if (!store || !client_->IsSavingAndFillingEnabledForCurrentPage() ||
- !client_->OnCredentialManagerUsed()) {
- callback.Run();
+ !client_->OnCredentialManagerUsed())
return;
- }
-
- if (store->affiliated_match_helper()) {
- store->affiliated_match_helper()->GetAffiliatedAndroidRealms(
- GetSynthesizedFormForOrigin(),
- base::Bind(&CredentialManagerImpl::ScheduleRequireMediationTask,
- weak_factory_.GetWeakPtr(), callback));
- } else {
- std::vector<std::string> no_affiliated_realms;
- ScheduleRequireMediationTask(callback, no_affiliated_realms);
- }
-}
-void CredentialManagerImpl::ScheduleRequireMediationTask(
- const RequireUserMediationCallback& callback,
- const std::vector<std::string>& android_realms) {
- DCHECK(GetPasswordStore());
if (!pending_require_user_mediation_) {
pending_require_user_mediation_.reset(
- new CredentialManagerPendingRequireUserMediationTask(
- this, web_contents()->GetLastCommittedURL().GetOrigin(),
- android_realms));
-
- // This will result in a callback to
- // CredentialManagerPendingRequireUserMediationTask::
- // OnGetPasswordStoreResults().
- GetPasswordStore()->GetAutofillableLogins(
- pending_require_user_mediation_.get());
- } else {
- pending_require_user_mediation_->AddOrigin(
- web_contents()->GetLastCommittedURL().GetOrigin());
+ new CredentialManagerPendingRequireUserMediationTask(this));
}
-
- // Send acknowledge response back.
- callback.Run();
+ pending_require_user_mediation_->AddOrigin(GetSynthesizedFormForOrigin());
}
void CredentialManagerImpl::Get(bool zero_click_only,
@@ -204,38 +193,13 @@ void CredentialManagerImpl::Get(bool zero_click_only,
return;
}
- if (store->affiliated_match_helper()) {
- store->affiliated_match_helper()->GetAffiliatedAndroidRealms(
- GetSynthesizedFormForOrigin(),
- base::Bind(&CredentialManagerImpl::ScheduleRequestTask,
- weak_factory_.GetWeakPtr(), callback, zero_click_only,
- include_passwords, federations));
- } else {
- std::vector<std::string> no_affiliated_realms;
- ScheduleRequestTask(callback, zero_click_only, include_passwords,
- federations, no_affiliated_realms);
- }
-}
-
-void CredentialManagerImpl::ScheduleRequestTask(
- const GetCallback& callback,
- bool zero_click_only,
- bool include_passwords,
- const std::vector<GURL>& federations,
- const std::vector<std::string>& android_realms) {
- DCHECK(GetPasswordStore());
pending_request_.reset(new CredentialManagerPendingRequestTask(
this, base::Bind(&RunMojoGetCallback, callback), zero_click_only,
- web_contents()->GetLastCommittedURL().GetOrigin(), include_passwords,
- federations, android_realms));
-
+ include_passwords, federations));
// This will result in a callback to
// PendingRequestTask::OnGetPasswordStoreResults().
- GetPasswordStore()->GetAutofillableLogins(pending_request_.get());
-}
-
-PasswordStore* CredentialManagerImpl::GetPasswordStore() {
- return client_ ? client_->GetPasswordStore() : nullptr;
+ GetPasswordStore()->GetLogins(GetSynthesizedFormForOrigin(),
+ pending_request_.get());
}
bool CredentialManagerImpl::IsZeroClickAllowed() const {
@@ -305,6 +269,15 @@ PasswordManagerClient* CredentialManagerImpl::client() const {
return client_;
}
+PasswordStore* CredentialManagerImpl::GetPasswordStore() {
+ return client_ ? client_->GetPasswordStore() : nullptr;
+}
+
+void CredentialManagerImpl::DoneRequiringUserMediation() {
+ DCHECK(pending_require_user_mediation_);
+ pending_require_user_mediation_.reset();
+}
+
PasswordStore::FormDigest CredentialManagerImpl::GetSynthesizedFormForOrigin()
const {
PasswordStore::FormDigest digest = {
@@ -314,9 +287,4 @@ PasswordStore::FormDigest CredentialManagerImpl::GetSynthesizedFormForOrigin()
return digest;
}
-void CredentialManagerImpl::DoneRequiringUserMediation() {
- DCHECK(pending_require_user_mediation_);
- pending_require_user_mediation_.reset();
-}
-
} // namespace password_manager
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 ac088f796b0..3b3f5cd97ba 100644
--- a/chromium/components/password_manager/content/browser/credential_manager_impl.h
+++ b/chromium/components/password_manager/content/browser/credential_manager_impl.h
@@ -67,7 +67,6 @@ class CredentialManagerImpl
void SendPasswordForm(const SendCredentialCallback& send_callback,
const autofill::PasswordForm* form) override;
PasswordManagerClient* client() const override;
- PasswordStore::FormDigest GetSynthesizedFormForOrigin() const override;
// CredentialManagerPendingSignedOutTaskDelegate:
PasswordStore* GetPasswordStore() override;
@@ -76,27 +75,14 @@ class CredentialManagerImpl
// CredentialManagerPasswordFormManagerDelegate:
void OnProvisionalSaveComplete() override;
+ // Returns FormDigest for the current URL.
+ PasswordStore::FormDigest GetSynthesizedFormForOrigin() const;
+
private:
// Returns the driver for the current main frame.
// Virtual for testing.
virtual base::WeakPtr<PasswordManagerDriver> GetDriver();
- // Schedules a CredentiaManagerPendingRequestTask (during
- // |OnRequestCredential()|) after the PasswordStore's AffiliationMatchHelper
- // grabs a list of realms related to the current web origin.
- void ScheduleRequestTask(const GetCallback& callback,
- bool zero_click_only,
- bool include_passwords,
- const std::vector<GURL>& federations,
- const std::vector<std::string>& android_realms);
-
- // Schedules a CredentialManagerPendingRequireUserMediationTask after the
- // AffiliationMatchHelper grabs a list of realms related to the current
- // web origin.
- void ScheduleRequireMediationTask(
- const RequireUserMediationCallback& callback,
- const std::vector<std::string>& android_realms);
-
PasswordManagerClient* client_;
std::unique_ptr<CredentialManagerPasswordFormManager> form_manager_;
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 a3b064cd34c..103f8300f84 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
@@ -35,6 +35,8 @@
#include "content/public/test/test_renderer_host.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/url_constants.h"
using content::BrowserContext;
using content::WebContents;
@@ -197,6 +199,12 @@ void GetCredentialCallback(bool* called,
*out_info = info;
}
+GURL HttpURLFromHttps(const GURL& https_url) {
+ GURL::Replacements rep;
+ rep.SetSchemeStr(url::kHttpScheme);
+ return https_url.ReplaceComponents(rep);
+}
+
} // namespace
class CredentialManagerImplTest : public content::RenderViewHostTestHarness {
@@ -227,7 +235,7 @@ class CredentialManagerImplTest : public content::RenderViewHostTestHarness {
form_.icon_url = GURL("https://example.com/icon.png");
form_.password_value = base::ASCIIToUTF16("Password");
form_.origin = web_contents()->GetLastCommittedURL().GetOrigin();
- form_.signon_realm = form_.origin.spec();
+ form_.signon_realm = form_.origin.GetOrigin().spec();
form_.scheme = autofill::PasswordForm::SCHEME_HTML;
form_.skip_zero_click = false;
@@ -251,7 +259,8 @@ class CredentialManagerImplTest : public content::RenderViewHostTestHarness {
origin_path_form_.display_name = base::ASCIIToUTF16("Display Name 2");
origin_path_form_.password_value = base::ASCIIToUTF16("Password 2");
origin_path_form_.origin = GURL("https://example.com/path");
- origin_path_form_.signon_realm = origin_path_form_.origin.spec();
+ origin_path_form_.signon_realm =
+ origin_path_form_.origin.GetOrigin().spec();
origin_path_form_.scheme = autofill::PasswordForm::SCHEME_HTML;
origin_path_form_.skip_zero_click = false;
@@ -259,7 +268,7 @@ class CredentialManagerImplTest : public content::RenderViewHostTestHarness {
subdomain_form_.display_name = base::ASCIIToUTF16("Display Name 2");
subdomain_form_.password_value = base::ASCIIToUTF16("Password 2");
subdomain_form_.origin = GURL("https://subdomain.example.com/path");
- subdomain_form_.signon_realm = subdomain_form_.origin.spec();
+ subdomain_form_.signon_realm = subdomain_form_.origin.GetOrigin().spec();
subdomain_form_.scheme = autofill::PasswordForm::SCHEME_HTML;
subdomain_form_.skip_zero_click = false;
@@ -267,7 +276,8 @@ class CredentialManagerImplTest : public content::RenderViewHostTestHarness {
cross_origin_form_.display_name = base::ASCIIToUTF16("Display Name");
cross_origin_form_.password_value = base::ASCIIToUTF16("Password");
cross_origin_form_.origin = GURL("https://example.net/");
- cross_origin_form_.signon_realm = cross_origin_form_.origin.spec();
+ cross_origin_form_.signon_realm =
+ cross_origin_form_.origin.GetOrigin().spec();
cross_origin_form_.scheme = autofill::PasswordForm::SCHEME_HTML;
cross_origin_form_.skip_zero_click = false;
@@ -406,7 +416,8 @@ TEST_F(CredentialManagerImplTest, CredentialManagerOnStore) {
RunAllPendingTasks();
EXPECT_TRUE(called);
- EXPECT_TRUE(client_->pending_manager()->HasCompletedMatching());
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING,
+ client_->pending_manager()->form_fetcher()->GetState());
autofill::PasswordForm new_form =
client_->pending_manager()->pending_credentials();
@@ -439,7 +450,8 @@ TEST_F(CredentialManagerImplTest, CredentialManagerOnStoreFederated) {
RunAllPendingTasks();
EXPECT_TRUE(called);
- EXPECT_TRUE(client_->pending_manager()->HasCompletedMatching());
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING,
+ client_->pending_manager()->form_fetcher()->GetState());
autofill::PasswordForm new_form =
client_->pending_manager()->pending_credentials();
@@ -477,7 +489,8 @@ TEST_F(CredentialManagerImplTest, StoreFederatedAfterPassword) {
RunAllPendingTasks();
EXPECT_TRUE(called);
- EXPECT_TRUE(client_->pending_manager()->HasCompletedMatching());
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING,
+ client_->pending_manager()->form_fetcher()->GetState());
client_->pending_manager()->Save();
RunAllPendingTasks();
@@ -516,6 +529,94 @@ TEST_F(CredentialManagerImplTest, CredentialManagerStoreOverwrite) {
passwords[form_.signon_realm][0].password_value);
}
+TEST_F(CredentialManagerImplTest,
+ CredentialManagerStorePSLMatchDoesNotTriggerBubble) {
+ autofill::PasswordForm psl_form = subdomain_form_;
+ psl_form.username_value = form_.username_value;
+ psl_form.password_value = form_.password_value;
+ store_->AddLogin(psl_form);
+
+ // Calling 'Store' with a new credential that is a PSL match for an existing
+ // credential with identical username and password should result in a silent
+ // save without prompting the user.
+ CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD);
+ EXPECT_CALL(*client_, PromptUserToSavePasswordPtr(_, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyStorePasswordCalled());
+ bool called = false;
+ CallStore(info, base::Bind(&RespondCallback, &called));
+ RunAllPendingTasks();
+ EXPECT_TRUE(called);
+
+ // Check that both credentials are present in the password store.
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ EXPECT_EQ(2U, passwords.size());
+ EXPECT_EQ(1U, passwords[form_.signon_realm].size());
+ EXPECT_EQ(1U, passwords[psl_form.signon_realm].size());
+}
+
+TEST_F(CredentialManagerImplTest,
+ CredentialManagerStorePSLMatchWithDifferentUsernameTriggersBubble) {
+ base::string16 delta = base::ASCIIToUTF16("_totally_different");
+ autofill::PasswordForm psl_form = subdomain_form_;
+ psl_form.username_value = form_.username_value + delta;
+ psl_form.password_value = form_.password_value;
+ store_->AddLogin(psl_form);
+
+ // Calling 'Store' with a new credential that is a PSL match for an existing
+ // credential but has a different username should prompt the user and not
+ // result in a silent save.
+ CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD);
+ EXPECT_CALL(*client_, PromptUserToSavePasswordPtr(_, _))
+ .Times(testing::Exactly(1));
+ EXPECT_CALL(*client_, NotifyStorePasswordCalled());
+ bool called = false;
+ CallStore(info, base::Bind(&RespondCallback, &called));
+ RunAllPendingTasks();
+ EXPECT_TRUE(called);
+
+ // Check that only the initial credential is present in the password store
+ // and the new one is still pending.
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ EXPECT_EQ(1U, passwords.size());
+ EXPECT_EQ(1U, passwords[psl_form.signon_realm].size());
+
+ const auto& pending_cred = client_->pending_manager()->pending_credentials();
+ EXPECT_EQ(info.id, pending_cred.username_value);
+ EXPECT_EQ(info.password, pending_cred.password_value);
+}
+
+TEST_F(CredentialManagerImplTest,
+ CredentialManagerStorePSLMatchWithDifferentPasswordTriggersBubble) {
+ base::string16 delta = base::ASCIIToUTF16("_totally_different");
+ autofill::PasswordForm psl_form = subdomain_form_;
+ psl_form.username_value = form_.username_value;
+ psl_form.password_value = form_.password_value + delta;
+ store_->AddLogin(psl_form);
+
+ // Calling 'Store' with a new credential that is a PSL match for an existing
+ // credential but has a different password should prompt the user and not
+ // result in a silent save.
+ CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD);
+ EXPECT_CALL(*client_, PromptUserToSavePasswordPtr(_, _))
+ .Times(testing::Exactly(1));
+ EXPECT_CALL(*client_, NotifyStorePasswordCalled());
+ bool called = false;
+ CallStore(info, base::Bind(&RespondCallback, &called));
+ RunAllPendingTasks();
+ EXPECT_TRUE(called);
+
+ // Check that only the initial credential is present in the password store
+ // and the new one is still pending.
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ EXPECT_EQ(1U, passwords.size());
+ EXPECT_EQ(1U, passwords[psl_form.signon_realm].size());
+
+ const auto& pending_cred = client_->pending_manager()->pending_credentials();
+ EXPECT_EQ(info.id, pending_cred.username_value);
+ EXPECT_EQ(info.password, pending_cred.password_value);
+}
+
TEST_F(CredentialManagerImplTest, CredentialManagerStoreOverwriteZeroClick) {
form_.skip_zero_click = true;
store_->AddLogin(form_);
@@ -570,7 +671,6 @@ TEST_F(CredentialManagerImplTest, CredentialManagerGetOverwriteZeroClick) {
form_.skip_zero_click = true;
form_.username_element = base::ASCIIToUTF16("username-element");
form_.password_element = base::ASCIIToUTF16("password-element");
- form_.signon_realm = "this is a realm";
form_.origin = GURL("https://example.com/old_form.html");
store_->AddLogin(form_);
RunAllPendingTasks();
@@ -736,6 +836,34 @@ TEST_F(CredentialManagerImplTest,
}
TEST_F(CredentialManagerImplTest,
+ CredentialManagerOnRequestCredentialWithPSLCredential) {
+ store_->AddLogin(subdomain_form_);
+ subdomain_form_.is_public_suffix_match = true;
+ EXPECT_CALL(*client_,
+ PromptUserToChooseCredentialsPtr(
+ UnorderedElementsAre(Pointee(subdomain_form_)), _, _));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr()).Times(0);
+
+ ExpectCredentialType(false, true, std::vector<GURL>(),
+ CredentialType::CREDENTIAL_TYPE_PASSWORD);
+}
+
+TEST_F(CredentialManagerImplTest,
+ CredentialManagerOnRequestCredentialWithPSLAndNormalCredentials) {
+ store_->AddLogin(form_);
+ store_->AddLogin(origin_path_form_);
+ store_->AddLogin(subdomain_form_);
+
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(
+ UnorderedElementsAre(Pointee(origin_path_form_),
+ Pointee(form_)),
+ _, _));
+
+ ExpectCredentialType(false, true, std::vector<GURL>(),
+ CredentialType::CREDENTIAL_TYPE_PASSWORD);
+}
+
+TEST_F(CredentialManagerImplTest,
CredentialManagerOnRequestCredentialWithEmptyAndNonemptyUsernames) {
store_->AddLogin(form_);
autofill::PasswordForm empty = form_;
@@ -752,7 +880,7 @@ TEST_F(CredentialManagerImplTest,
TEST_F(CredentialManagerImplTest,
CredentialManagerOnRequestCredentialWithDuplicates) {
- // Add 8 credentials. Two buckets of duplicates, one empty username and one
+ // Add 6 credentials. Two buckets of duplicates, one empty username and one
// federated one. There should be just 3 in the account chooser.
form_.preferred = true;
form_.username_element = base::ASCIIToUTF16("username_element");
@@ -761,10 +889,6 @@ TEST_F(CredentialManagerImplTest,
empty.username_value.clear();
store_->AddLogin(empty);
autofill::PasswordForm duplicate = form_;
- duplicate.username_element = base::ASCIIToUTF16("username_element1");
- duplicate.is_public_suffix_match = true;
- store_->AddLogin(duplicate);
- duplicate = form_;
duplicate.username_element = base::ASCIIToUTF16("username_element2");
duplicate.preferred = false;
store_->AddLogin(duplicate);
@@ -772,10 +896,6 @@ TEST_F(CredentialManagerImplTest,
origin_path_form_.preferred = true;
store_->AddLogin(origin_path_form_);
duplicate = origin_path_form_;
- duplicate.username_element = base::ASCIIToUTF16("username_element3");
- duplicate.is_public_suffix_match = true;
- store_->AddLogin(duplicate);
- duplicate = origin_path_form_;
duplicate.username_element = base::ASCIIToUTF16("username_element4");
duplicate.preferred = false;
store_->AddLogin(duplicate);
@@ -849,16 +969,7 @@ TEST_F(
.Times(testing::Exactly(0));
EXPECT_CALL(*client_, NotifyUserAutoSigninPtr()).Times(testing::Exactly(0));
- bool called = false;
- mojom::CredentialManagerError error;
- base::Optional<CredentialInfo> credential;
- CallGet(true, true, federations,
- base::Bind(&GetCredentialCallback, &called, &error, &credential));
-
- RunAllPendingTasks();
-
- EXPECT_TRUE(called);
- EXPECT_EQ(mojom::CredentialManagerError::SUCCESS, error);
+ ExpectZeroClickSignInFailure(true, true, federations);
}
TEST_F(CredentialManagerImplTest,
@@ -1290,15 +1401,23 @@ TEST_F(CredentialManagerImplTest,
// in.
store_->AddLogin(affiliated_form1_);
- auto mock_helper = base::WrapUnique(new MockAffiliatedMatchHelper);
- store_->SetAffiliatedMatchHelper(std::move(mock_helper));
+ store_->SetAffiliatedMatchHelper(
+ base::MakeUnique<MockAffiliatedMatchHelper>());
- std::vector<GURL> federations;
std::vector<std::string> affiliated_realms;
+ PasswordStore::FormDigest digest =
+ cm_service_impl_->GetSynthesizedFormForOrigin();
+ // First expect affiliations for the HTTPS domain.
static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
- ->ExpectCallToGetAffiliatedAndroidRealms(
- cm_service_impl_->GetSynthesizedFormForOrigin(), affiliated_realms);
+ ->ExpectCallToGetAffiliatedAndroidRealms(digest, affiliated_realms);
+
+ digest.origin = HttpURLFromHttps(digest.origin);
+ digest.signon_realm = digest.origin.spec();
+ // The second call happens for HTTP as the migration is triggered.
+ static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
+ ->ExpectCallToGetAffiliatedAndroidRealms(digest, affiliated_realms);
+ std::vector<GURL> federations;
ExpectZeroClickSignInFailure(true, true, federations);
}
@@ -1323,6 +1442,40 @@ TEST_F(CredentialManagerImplTest,
CredentialType::CREDENTIAL_TYPE_PASSWORD);
}
+TEST_F(CredentialManagerImplTest, ZeroClickWithPSLCredential) {
+ subdomain_form_.skip_zero_click = false;
+ store_->AddLogin(subdomain_form_);
+
+ ExpectZeroClickSignInFailure(true, true, std::vector<GURL>());
+}
+
+TEST_F(CredentialManagerImplTest, ZeroClickWithPSLAndNormalCredentials) {
+ form_.password_value.clear();
+ form_.federation_origin = url::Origin(GURL("https://google.com/"));
+ form_.signon_realm = "federation://" + form_.origin.host() + "/google.com";
+ form_.skip_zero_click = false;
+ store_->AddLogin(form_);
+ store_->AddLogin(subdomain_form_);
+
+ std::vector<GURL> federations = {GURL("https://google.com/")};
+ ExpectZeroClickSignInSuccess(true, true, federations,
+ CredentialType::CREDENTIAL_TYPE_FEDERATED);
+}
+
+TEST_F(CredentialManagerImplTest, ZeroClickAfterMigratingHttpCredential) {
+ // There is an http credential saved. It should be migrated and used for auto
+ // sign-in.
+ form_.origin = HttpURLFromHttps(form_.origin);
+ form_.signon_realm = form_.origin.GetOrigin().spec();
+ // That is the default value for old credentials.
+ form_.skip_zero_click = true;
+ store_->AddLogin(form_);
+
+ std::vector<GURL> federations;
+ ExpectZeroClickSignInSuccess(true, true, federations,
+ CredentialType::CREDENTIAL_TYPE_PASSWORD);
+}
+
TEST_F(CredentialManagerImplTest, GetSynthesizedFormForOrigin) {
PasswordStore::FormDigest synthesized =
cm_service_impl_->GetSynthesizedFormForOrigin();
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 ded86d62ad2..406524faea0 100644
--- a/chromium/components/password_manager/content/renderer/credential_manager_client.h
+++ b/chromium/components/password_manager/content/renderer/credential_manager_client.h
@@ -24,8 +24,6 @@ class RenderView;
namespace password_manager {
-struct CredentialInfo;
-
// The CredentialManagerClient implements the Blink platform interface
// WebCredentialManagerClient, and acts as an intermediary between Blink-side
// calls to 'navigator.credential.*' and the password manager internals which
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 e995de40262..d52a00ac0c5 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
@@ -10,6 +10,7 @@
#include <tuple>
#include "base/location.h"
+#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/renderer/render_frame.h"
diff --git a/chromium/components/password_manager/core/browser/BUILD.gn b/chromium/components/password_manager/core/browser/BUILD.gn
index 120a7ff04eb..c76864f1c7e 100644
--- a/chromium/components/password_manager/core/browser/BUILD.gn
+++ b/chromium/components/password_manager/core/browser/BUILD.gn
@@ -52,6 +52,8 @@ static_library("browser") {
"form_saver.h",
"form_saver_impl.cc",
"form_saver_impl.h",
+ "http_password_migrator.cc",
+ "http_password_migrator.h",
"import/csv_reader.cc",
"import/csv_reader.h",
"import/password_csv_reader.cc",
@@ -95,6 +97,12 @@ static_library("browser") {
"password_manager_settings_migration_experiment.h",
"password_manager_util.cc",
"password_manager_util.h",
+ "password_reuse_detection_manager.cc",
+ "password_reuse_detection_manager.h",
+ "password_reuse_detector.cc",
+ "password_reuse_detector.h",
+ "password_reuse_detector_consumer.cc",
+ "password_reuse_detector_consumer.h",
"password_store.cc",
"password_store.h",
"password_store_change.cc",
@@ -178,6 +186,8 @@ static_library("test_support") {
"fake_affiliation_api.h",
"fake_affiliation_fetcher.cc",
"fake_affiliation_fetcher.h",
+ "fake_form_fetcher.cc",
+ "fake_form_fetcher.h",
"mock_affiliated_match_helper.cc",
"mock_affiliated_match_helper.h",
"mock_affiliation_consumer.cc",
@@ -256,12 +266,14 @@ source_set("unit_tests") {
"affiliation_utils_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_migrator_unittest.cc",
"import/csv_reader_unittest.cc",
"import/password_csv_reader_unittest.cc",
"import/password_importer_unittest.cc",
@@ -276,6 +288,8 @@ source_set("unit_tests") {
"password_manager_settings_migration_experiment_unittest.cc",
"password_manager_unittest.cc",
"password_manager_util_unittest.cc",
+ "password_reuse_detection_manager_unittest.cc",
+ "password_reuse_detector_unittest.cc",
"password_store_default_unittest.cc",
"password_store_origin_unittest.h",
"password_store_unittest.cc",
diff --git a/chromium/components/password_manager/core/browser/DEPS b/chromium/components/password_manager/core/browser/DEPS
index 00a574b8e12..fac484e01a7 100644
--- a/chromium/components/password_manager/core/browser/DEPS
+++ b/chromium/components/password_manager/core/browser/DEPS
@@ -3,6 +3,7 @@ include_rules = [
"+components/keyed_service/core",
"+components/pref_registry",
"+components/security_state",
+ "+components/sync/base",
"+components/sync/driver",
"+components/url_formatter",
"+components/variations",
diff --git a/chromium/components/password_manager/core/browser/affiliated_match_helper.h b/chromium/components/password_manager/core/browser/affiliated_match_helper.h
index 35d31a6256f..7e51c9611c1 100644
--- a/chromium/components/password_manager/core/browser/affiliated_match_helper.h
+++ b/chromium/components/password_manager/core/browser/affiliated_match_helper.h
@@ -22,10 +22,6 @@ namespace autofill {
struct PasswordForm;
} // namespace autofill
-namespace base {
-struct SingleThreadedTaskRunner;
-} // namespace base
-
namespace password_manager {
class AffiliationService;
diff --git a/chromium/components/password_manager/core/browser/affiliation_backend.cc b/chromium/components/password_manager/core/browser/affiliation_backend.cc
index f7881bf8f57..4ed027fbbf1 100644
--- a/chromium/components/password_manager/core/browser/affiliation_backend.cc
+++ b/chromium/components/password_manager/core/browser/affiliation_backend.cc
@@ -10,6 +10,7 @@
#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"
@@ -88,12 +89,12 @@ void AffiliationBackend::CancelPrefetch(const FacetURI& facet_uri,
const base::Time& keep_fresh_until) {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
- FacetManager* facet_manager = facet_managers_.get(facet_uri);
- if (!facet_manager)
+ auto facet_manager_it = facet_managers_.find(facet_uri);
+ if (facet_manager_it == facet_managers_.end())
return;
- facet_manager->CancelPrefetch(keep_fresh_until);
+ facet_manager_it->second->CancelPrefetch(keep_fresh_until);
- if (facet_manager->CanBeDiscarded())
+ if (facet_manager_it->second->CanBeDiscarded())
facet_managers_.erase(facet_uri);
}
@@ -121,12 +122,12 @@ void AffiliationBackend::DeleteCache(const base::FilePath& db_path) {
FacetManager* AffiliationBackend::GetOrCreateFacetManager(
const FacetURI& facet_uri) {
- if (!facet_managers_.contains(facet_uri)) {
- std::unique_ptr<FacetManager> new_manager(
- new FacetManager(facet_uri, this, clock_.get()));
- facet_managers_.add(facet_uri, std::move(new_manager));
+ 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_managers_.get(facet_uri);
+ return facet_manager.get();
}
void AffiliationBackend::DiscardCachedDataIfNoLongerNeeded(
@@ -136,9 +137,11 @@ void AffiliationBackend::DiscardCachedDataIfNoLongerNeeded(
// 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) {
- FacetManager* facet_manager = facet_managers_.get(facet_uri);
- if (facet_manager && !facet_manager->CanCachedDataBeDiscarded())
+ 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());
@@ -148,12 +151,12 @@ void AffiliationBackend::DiscardCachedDataIfNoLongerNeeded(
void AffiliationBackend::OnSendNotification(const FacetURI& facet_uri) {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
- FacetManager* facet_manager = facet_managers_.get(facet_uri);
- if (!facet_manager)
+ auto facet_manager_it = facet_managers_.find(facet_uri);
+ if (facet_manager_it == facet_managers_.end())
return;
- facet_manager->NotifyAtRequestedTime();
+ facet_manager_it->second->NotifyAtRequestedTime();
- if (facet_manager->CanBeDiscarded())
+ if (facet_manager_it->second->CanBeDiscarded())
facet_managers_.erase(facet_uri);
}
@@ -204,9 +207,10 @@ void AffiliationBackend::OnFetchSucceeded(
// data. See: https://crbug.com/478832.
for (const auto& facet_uri : affiliated_facets) {
- if (!facet_managers_.contains(facet_uri))
+ auto facet_manager_it = facet_managers_.find(facet_uri);
+ if (facet_manager_it == facet_managers_.end())
continue;
- FacetManager* facet_manager = facet_managers_.get(facet_uri);
+ FacetManager* facet_manager = facet_manager_it->second.get();
facet_manager->OnFetchSucceeded(affiliation);
if (facet_manager->CanBeDiscarded())
facet_managers_.erase(facet_uri);
diff --git a/chromium/components/password_manager/core/browser/affiliation_backend.h b/chromium/components/password_manager/core/browser/affiliation_backend.h
index 5b2a05e4994..f99fcac9894 100644
--- a/chromium/components/password_manager/core/browser/affiliation_backend.h
+++ b/chromium/components/password_manager/core/browser/affiliation_backend.h
@@ -8,9 +8,9 @@
#include <stddef.h>
#include <map>
+#include <unordered_map>
#include <vector>
-#include "base/containers/scoped_ptr_hash_map.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
@@ -153,7 +153,7 @@ class AffiliationBackend : public FacetManagerHost,
// Contains a FacetManager for each facet URI that need ongoing attention. To
// save memory, managers are discarded as soon as they become redundant.
- base::ScopedPtrHashMap<FacetURI, std::unique_ptr<FacetManager>>
+ std::unordered_map<FacetURI, std::unique_ptr<FacetManager>, FacetURIHash>
facet_managers_;
base::WeakPtrFactory<AffiliationBackend> weak_ptr_factory_;
diff --git a/chromium/components/password_manager/core/browser/affiliation_utils.h b/chromium/components/password_manager/core/browser/affiliation_utils.h
index fcf8b7e9054..4d463c9837d 100644
--- a/chromium/components/password_manager/core/browser/affiliation_utils.h
+++ b/chromium/components/password_manager/core/browser/affiliation_utils.h
@@ -60,10 +60,6 @@ namespace autofill {
struct PasswordForm;
} // namespace autofill
-namespace base {
-class CommandLine;
-} // namespace base
-
namespace password_manager {
// Encapsulates a facet URI in canonical form.
@@ -190,18 +186,12 @@ std::string GetHumanReadableOriginForAndroidUri(const FacetURI facet_uri);
// For logging use only.
std::ostream& operator<<(std::ostream& os, const FacetURI& facet_uri);
-} // namespace password_manager
-
-// Provide a hash function so that hash_sets and maps can contain FacetURIs.
-namespace BASE_HASH_NAMESPACE {
-
-template <>
-struct hash<password_manager::FacetURI> {
- size_t operator()(const password_manager::FacetURI& facet_uri) const {
- return hash<std::string>()(facet_uri.potentially_invalid_spec());
+struct FacetURIHash {
+ size_t operator()(const FacetURI& facet_uri) const {
+ return std::hash<std::string>()(facet_uri.potentially_invalid_spec());
}
};
-} // namespace BASE_HASH_NAMESPACE
+} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_UTILS_H_
diff --git a/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.cc b/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.cc
index 0bd75818127..db8803d8e57 100644
--- a/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.cc
+++ b/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.cc
@@ -75,6 +75,8 @@ BrowserSavePasswordProgressLogger::BrowserSavePasswordProgressLogger(
DCHECK(log_manager_);
}
+BrowserSavePasswordProgressLogger::~BrowserSavePasswordProgressLogger() {}
+
void BrowserSavePasswordProgressLogger::LogFormSignatures(
SavePasswordProgressLogger::StringID label,
const autofill::PasswordForm& form) {
@@ -110,11 +112,24 @@ void BrowserSavePasswordProgressLogger::LogFormStructure(
SendLog(message);
}
+void BrowserSavePasswordProgressLogger::LogSuccessiveOrigins(
+ StringID label,
+ const GURL& old_origin,
+ const GURL& new_origin) {
+ std::string message = GetStringFromID(label) + ": {\n";
+ message +=
+ GetStringFromID(STRING_ORIGIN) + ": " + ScrubURL(old_origin) + "\n";
+ message +=
+ GetStringFromID(STRING_ORIGIN) + ": " + ScrubURL(new_origin) + "\n";
+ message += "}";
+ SendLog(message);
+}
+
std::string BrowserSavePasswordProgressLogger::FormStructureToFieldsLogString(
const autofill::FormStructure& form_structure) {
std::string result;
result += GetStringFromID(STRING_FIELDS) + ": " + "\n";
- for (const autofill::AutofillField* field : form_structure) {
+ for (const auto& field : form_structure) {
std::string field_info = ScrubElementID(field->name) + ": " +
ScrubNonDigit(field->FieldSignatureAsStr()) +
", " + ScrubElementID(field->form_control_type);
@@ -144,7 +159,10 @@ std::string BrowserSavePasswordProgressLogger::FormStructureToFieldsLogString(
return result;
}
-BrowserSavePasswordProgressLogger::~BrowserSavePasswordProgressLogger() {}
+void BrowserSavePasswordProgressLogger::LogString(StringID label,
+ const std::string& s) {
+ LogValue(label, base::StringValue(s));
+}
void BrowserSavePasswordProgressLogger::SendLog(const std::string& log) {
log_manager_->LogSavePasswordProgress(log);
diff --git a/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.h b/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.h
index 27090fd3ef8..d5c94232dec 100644
--- a/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.h
+++ b/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.h
@@ -9,6 +9,7 @@
#include "base/macros.h"
#include "components/autofill/core/common/save_password_progress_logger.h"
+#include "url/gurl.h"
namespace autofill {
class FormStructure;
@@ -34,6 +35,16 @@ class BrowserSavePasswordProgressLogger
// sanitized and passed to SendLog for display.
void LogFormStructure(StringID label, const autofill::FormStructure& form);
+ // Browser-specific addition to the base class' Log* methods. The input is
+ // sanitized and passed to SendLog for display.
+ void LogSuccessiveOrigins(StringID label,
+ const GURL& old_origin,
+ const GURL& new_origin);
+
+ // Browser-specific addition to the base class' Log* methods. The input is
+ // passed to SendLog for display.
+ void LogString(StringID label, const std::string& s);
+
protected:
// autofill::SavePasswordProgressLogger:
void SendLog(const std::string& log) override;
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 e26a6883abb..0677a17f1c3 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
@@ -6,8 +6,10 @@
#include <utility>
+#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/form_saver_impl.h"
#include "components/password_manager/core/browser/password_manager_client.h"
@@ -22,29 +24,51 @@ CredentialManagerPasswordFormManager::CredentialManagerPasswordFormManager(
base::WeakPtr<PasswordManagerDriver> driver,
const PasswordForm& observed_form,
std::unique_ptr<autofill::PasswordForm> saved_form,
- CredentialManagerPasswordFormManagerDelegate* delegate)
- : PasswordFormManager(
- driver->GetPasswordManager(),
- client,
- driver,
- observed_form,
- base::WrapUnique(new FormSaverImpl(client->GetPasswordStore()))),
+ CredentialManagerPasswordFormManagerDelegate* delegate,
+ std::unique_ptr<FormSaver> form_saver,
+ std::unique_ptr<FormFetcher> form_fetcher)
+ : PasswordFormManager(driver->GetPasswordManager(),
+ client,
+ driver,
+ observed_form,
+ (form_saver ? std::move(form_saver)
+ : base::MakeUnique<FormSaverImpl>(
+ client->GetPasswordStore())),
+ form_fetcher.get()),
delegate_(delegate),
- saved_form_(std::move(saved_form)) {
+ saved_form_(std::move(saved_form)),
+ form_fetcher_(std::move(form_fetcher)),
+ weak_factory_(this) {
DCHECK(saved_form_);
+ // This condition is only false on iOS.
+ if (form_fetcher_)
+ form_fetcher_->Fetch();
}
CredentialManagerPasswordFormManager::~CredentialManagerPasswordFormManager() {
}
-void CredentialManagerPasswordFormManager::OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
- PasswordFormManager::OnGetPasswordStoreResults(std::move(results));
+void CredentialManagerPasswordFormManager::ProcessMatches(
+ const std::vector<const PasswordForm*>& non_federated,
+ size_t filtered_count) {
+ PasswordFormManager::ProcessMatches(non_federated, filtered_count);
// Mark the form as "preferred", as we've been told by the API that this is
// indeed the credential set that the user used to sign into the site.
saved_form_->preferred = true;
ProvisionallySave(*saved_form_, IGNORE_OTHER_POSSIBLE_USERNAMES);
+
+ // Notify the delegate. This might result in deleting |this|, while
+ // ProcessMatches is being called from FormFetcherImpl, owned by |this|. If
+ // done directly, once ProcessMatches returns, the FormFetcherImpl will be
+ // used after free. Therefore the call is posted to a separate task.
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&CredentialManagerPasswordFormManager::NotifyDelegate,
+ weak_factory_.GetWeakPtr()));
+}
+
+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 141f7769cc7..d41f8079297 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
@@ -15,7 +15,6 @@ struct PasswordForm;
namespace password_manager {
-class CredentialManagerDispatcher;
class PasswordManagerClient;
class PasswordManagerDriver;
@@ -34,7 +33,9 @@ class CredentialManagerPasswordFormManager : public PasswordFormManager {
// Given a |client| and an |observed_form|, kick off the process of fetching
// matching logins from the password store; if |observed_form| doesn't map to
// a blacklisted origin, provisionally save |saved_form|. Once saved, let the
- // delegate know that it's safe to poke at the UI.
+ // delegate know that it's safe to poke at the UI. |form_fetcher| is passed
+ // to PasswordFormManager. |form_saver| can be null, in which case it is
+ // created automatically.
//
// This class does not take ownership of |delegate|.
CredentialManagerPasswordFormManager(
@@ -42,15 +43,28 @@ class CredentialManagerPasswordFormManager : public PasswordFormManager {
base::WeakPtr<PasswordManagerDriver> driver,
const autofill::PasswordForm& observed_form,
std::unique_ptr<autofill::PasswordForm> saved_form,
- CredentialManagerPasswordFormManagerDelegate* delegate);
+ CredentialManagerPasswordFormManagerDelegate* delegate,
+ std::unique_ptr<FormSaver> form_saver,
+ std::unique_ptr<FormFetcher> form_fetcher);
~CredentialManagerPasswordFormManager() override;
- void OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+ void ProcessMatches(
+ const std::vector<const autofill::PasswordForm*>& non_federated,
+ size_t filtered_count) override;
+
+#if defined(UNIT_TEST)
+ FormFetcher* form_fetcher() const { return form_fetcher_.get(); }
+#endif // defined(UNIT_TEST)
private:
+ // Calls OnProvisionalSaveComplete on |delegate_|.
+ void NotifyDelegate();
+
CredentialManagerPasswordFormManagerDelegate* delegate_;
std::unique_ptr<autofill::PasswordForm> saved_form_;
+ std::unique_ptr<FormFetcher> form_fetcher_;
+
+ base::WeakPtrFactory<CredentialManagerPasswordFormManager> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CredentialManagerPasswordFormManager);
};
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
new file mode 100644
index 00000000000..88caa117d77
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/credential_manager_password_form_manager_unittest.cc
@@ -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.
+
+#include "components/password_manager/core/browser/credential_manager_password_form_manager.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/fake_form_fetcher.h"
+#include "components/password_manager/core/browser/stub_form_saver.h"
+#include "components/password_manager/core/browser/stub_password_manager_client.h"
+#include "components/password_manager/core/browser/stub_password_manager_driver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using autofill::PasswordForm;
+using ::testing::Invoke;
+
+namespace password_manager {
+
+namespace {
+
+class MockDelegate : public CredentialManagerPasswordFormManagerDelegate {
+ public:
+ MOCK_METHOD0(OnProvisionalSaveComplete, void());
+};
+
+} // namespace
+
+class CredentialManagerPasswordFormManagerTest : public testing::Test {
+ public:
+ CredentialManagerPasswordFormManagerTest() = default;
+
+ protected:
+ // Necessary for callbacks, and for TestAutofillDriver.
+ base::MessageLoop message_loop_;
+
+ StubPasswordManagerClient client_;
+ StubPasswordManagerDriver driver_;
+
+ DISALLOW_COPY_AND_ASSIGN(CredentialManagerPasswordFormManagerTest);
+};
+
+// Test that aborting early does not cause use after free.
+TEST_F(CredentialManagerPasswordFormManagerTest, AbortEarly) {
+ 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>());
+
+ auto deleter = [&form_manager]() { form_manager.reset(); };
+
+ // Simulate that the PasswordStore responded to the FormFetcher. As a result,
+ // |form_manager| should call the delegate's OnProvisionalSaveComplete, which
+ // in turn should delete |form_fetcher|.
+ EXPECT_CALL(delegate, OnProvisionalSaveComplete()).WillOnce(Invoke(deleter));
+ static_cast<FakeFormFetcher*>(form_manager->form_fetcher())
+ ->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
+ // Check that |form_manager| was not deleted yet; doing so would have caused
+ // use after free during SetNonFederated.
+ EXPECT_TRUE(form_manager);
+
+ base::RunLoop().RunUntilIdle();
+
+ // Ultimately, |form_fetcher| should have been deleted. It just should happen
+ // after it finishes executing.
+ EXPECT_FALSE(form_manager);
+}
+
+} // 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 e00b02c58ef..a3a3747dfc5 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
@@ -55,15 +55,21 @@ bool IsBetterMatch(const autofill::PasswordForm& form1,
void FilterDuplicates(
std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) {
std::vector<std::unique_ptr<autofill::PasswordForm>> federated_forms;
- std::map<base::string16, std::unique_ptr<autofill::PasswordForm>> credentials;
+ // The key is [username, signon_realm]. signon_realm is used only for PSL
+ // matches because those entries have it in the UI.
+ std::map<std::pair<base::string16, std::string>,
+ std::unique_ptr<autofill::PasswordForm>>
+ credentials;
for (auto& form : *forms) {
if (!form->federation_origin.unique()) {
federated_forms.push_back(std::move(form));
} else {
- auto it = credentials.find(form->username_value);
- if (it == credentials.end() || IsBetterMatch(*form, *it->second)) {
- credentials[form->username_value] = std::move(form);
- }
+ auto key = std::make_pair(
+ form->username_value,
+ form->is_public_suffix_match ? form->signon_realm : std::string());
+ auto it = credentials.find(key);
+ if (it == credentials.end() || IsBetterMatch(*form, *it->second))
+ credentials[key] = std::move(form);
}
}
forms->clear();
@@ -73,22 +79,39 @@ void FilterDuplicates(
std::back_inserter(*forms));
}
+// Sift |forms| for the account chooser so it doesn't have empty usernames or
+// duplicates.
+void FilterDuplicatesAndEmptyUsername(
+ std::vector<std::unique_ptr<autofill::PasswordForm>>* forms,
+ bool* has_empty_username,
+ bool* has_duplicates) {
+ // Remove empty usernames from the list.
+ auto begin_empty =
+ std::remove_if(forms->begin(), forms->end(),
+ [](const std::unique_ptr<autofill::PasswordForm>& form) {
+ return form->username_value.empty();
+ });
+ *has_empty_username = (begin_empty != forms->end());
+ forms->erase(begin_empty, forms->end());
+
+ const size_t size_before = forms->size();
+ FilterDuplicates(forms);
+ *has_duplicates = (size_before != forms->size());
+}
+
} // namespace
CredentialManagerPendingRequestTask::CredentialManagerPendingRequestTask(
CredentialManagerPendingRequestTaskDelegate* delegate,
const SendCredentialCallback& callback,
bool request_zero_click_only,
- const GURL& request_origin,
bool include_passwords,
- const std::vector<GURL>& request_federations,
- const std::vector<std::string>& affiliated_realms)
+ const std::vector<GURL>& request_federations)
: delegate_(delegate),
send_callback_(callback),
zero_click_only_(request_zero_click_only),
- origin_(request_origin),
- include_passwords_(include_passwords),
- affiliated_realms_(affiliated_realms.begin(), affiliated_realms.end()) {
+ origin_(delegate_->GetOrigin()),
+ include_passwords_(include_passwords) {
CHECK(!delegate_->client()->DidLastPageLoadEncounterSSLErrors());
for (const GURL& federation : request_federations)
federations_.insert(url::Origin(federation.GetOrigin()).Serialize());
@@ -99,6 +122,22 @@ CredentialManagerPendingRequestTask::~CredentialManagerPendingRequestTask() =
void CredentialManagerPendingRequestTask::OnGetPasswordStoreResults(
std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+ if (results.empty()) {
+ // Try to migrate the HTTP passwords and process them later.
+ http_migrator_ = base::MakeUnique<HttpPasswordMigrator>(
+ origin_, delegate_->client()->GetPasswordStore(), this);
+ return;
+ }
+ ProcessForms(std::move(results));
+}
+
+void CredentialManagerPendingRequestTask::ProcessMigratedForms(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms) {
+ ProcessForms(std::move(forms));
+}
+
+void CredentialManagerPendingRequestTask::ProcessForms(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
using metrics_util::LogCredentialManagerGetResult;
metrics_util::CredentialManagerGetMediation mediation_status =
zero_click_only_ ? metrics_util::CREDENTIAL_MANAGER_GET_UNMEDIATED
@@ -111,7 +150,7 @@ void CredentialManagerPendingRequestTask::OnGetPasswordStoreResults(
}
std::vector<std::unique_ptr<autofill::PasswordForm>> local_results;
- std::vector<std::unique_ptr<autofill::PasswordForm>> affiliated_results;
+ std::vector<std::unique_ptr<autofill::PasswordForm>> psl_results;
for (auto& form : results) {
// Ensure that the form we're looking at matches the password and
// federation filters provided.
@@ -125,42 +164,18 @@ void CredentialManagerPendingRequestTask::OnGetPasswordStoreResults(
// PasswordForm definition: scheme, host, port and path.
// GURL definition: scheme, host, and port.
// So we can't compare them directly.
- if (form->origin.GetOrigin() == origin_.GetOrigin()) {
+ if (form->is_affiliation_based_match ||
+ form->origin.GetOrigin() == origin_.GetOrigin()) {
local_results.push_back(std::move(form));
- } else if (affiliated_realms_.count(form->signon_realm) &&
- AffiliatedMatchHelper::IsValidAndroidCredential(
- PasswordStore::FormDigest(*form))) {
- form->is_affiliation_based_match = true;
- affiliated_results.push_back(std::move(form));
+ } else if (form->is_public_suffix_match) {
+ psl_results.push_back(std::move(form));
}
}
- if (!affiliated_results.empty()) {
- password_manager_util::TrimUsernameOnlyCredentials(&affiliated_results);
- std::move(affiliated_results.begin(), affiliated_results.end(),
- std::back_inserter(local_results));
- }
-
- // Remove empty usernames from the list.
- auto begin_empty =
- std::remove_if(local_results.begin(), local_results.end(),
- [](const std::unique_ptr<autofill::PasswordForm>& form) {
- return form->username_value.empty();
- });
- const bool has_empty_username = (begin_empty != local_results.end());
- local_results.erase(begin_empty, local_results.end());
-
- const size_t local_results_size = local_results.size();
- FilterDuplicates(&local_results);
- const bool has_duplicates = (local_results_size != local_results.size());
-
- if (local_results.empty()) {
- LogCredentialManagerGetResult(
- metrics_util::CREDENTIAL_MANAGER_GET_NONE_EMPTY_STORE,
- mediation_status);
- delegate_->SendCredential(send_callback_, CredentialInfo());
- return;
- }
+ bool has_empty_username = false;
+ bool has_duplicates = false;
+ FilterDuplicatesAndEmptyUsername(&local_results, &has_empty_username,
+ &has_duplicates);
// We only perform zero-click sign-in when the result is completely
// unambigious. If there is one and only one entry, and zero-click is
@@ -168,8 +183,8 @@ void CredentialManagerPendingRequestTask::OnGetPasswordStoreResults(
//
// Moreover, we only return such a credential if the user has opted-in via the
// first-run experience.
- bool can_use_autosignin = local_results.size() == 1u &&
- delegate_->IsZeroClickAllowed();
+ const bool can_use_autosignin =
+ local_results.size() == 1u && delegate_->IsZeroClickAllowed();
if (can_use_autosignin && !local_results[0]->skip_zero_click &&
!password_bubble_experiment::ShouldShowAutoSignInPromptFirstRunExperience(
delegate_->client()->GetPrefs())) {
@@ -186,41 +201,56 @@ void CredentialManagerPendingRequestTask::OnGetPasswordStoreResults(
return;
}
- // Otherwise, return an empty credential if we're in zero-click-only mode
- // or if the user chooses not to return a credential, and the credential the
- // user chooses if they pick one.
- std::unique_ptr<autofill::PasswordForm> potential_autosignin_form(
- can_use_autosignin ? new autofill::PasswordForm(*local_results[0])
- : nullptr);
- if (!zero_click_only_)
- ReportAccountChooserUsabilityMetrics(has_duplicates, has_empty_username);
- if (zero_click_only_ ||
- !delegate_->client()->PromptUserToChooseCredentials(
- std::move(local_results), origin_,
- base::Bind(
- &CredentialManagerPendingRequestTaskDelegate::SendPasswordForm,
- base::Unretained(delegate_), send_callback_))) {
- metrics_util::CredentialManagerGetResult get_result =
- metrics_util::CREDENTIAL_MANAGER_GET_NONE;
- if (zero_click_only_) {
- if (!can_use_autosignin)
- get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_MANY_CREDENTIALS;
- else if (potential_autosignin_form->skip_zero_click)
- get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_SIGNED_OUT;
- else
- get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_FIRST_RUN;
- }
+ if (zero_click_only_) {
+ metrics_util::CredentialManagerGetResult get_result;
+ if (local_results.empty())
+ get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_EMPTY_STORE;
+ else if (!can_use_autosignin)
+ get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_MANY_CREDENTIALS;
+ else if (local_results[0]->skip_zero_click)
+ get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_SIGNED_OUT;
+ else
+ get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_FIRST_RUN;
+
if (can_use_autosignin) {
// The user had credentials, but either chose not to share them with the
// site, or was prevented from doing so by lack of zero-click (or the
// first-run experience). So, notify the client that we could potentially
// have used zero-click.
delegate_->client()->NotifyUserCouldBeAutoSignedIn(
- std::move(potential_autosignin_form));
+ std::move(local_results[0]));
}
LogCredentialManagerGetResult(get_result, mediation_status);
delegate_->SendCredential(send_callback_, CredentialInfo());
+ return;
+ }
+
+ // Time to show the account chooser. If |local_results| is empty then it
+ // should list the PSL matches.
+ if (local_results.empty()) {
+ local_results = std::move(psl_results);
+ FilterDuplicatesAndEmptyUsername(&local_results, &has_empty_username,
+ &has_duplicates);
+ }
+
+ if (local_results.empty()) {
+ LogCredentialManagerGetResult(
+ metrics_util::CREDENTIAL_MANAGER_GET_NONE_EMPTY_STORE,
+ mediation_status);
+ delegate_->SendCredential(send_callback_, CredentialInfo());
+ return;
+ }
+
+ ReportAccountChooserUsabilityMetrics(has_duplicates, has_empty_username);
+ if (!delegate_->client()->PromptUserToChooseCredentials(
+ std::move(local_results), origin_,
+ base::Bind(
+ &CredentialManagerPendingRequestTaskDelegate::SendPasswordForm,
+ base::Unretained(delegate_), send_callback_))) {
+ LogCredentialManagerGetResult(metrics_util::CREDENTIAL_MANAGER_GET_NONE,
+ mediation_status);
+ delegate_->SendCredential(send_callback_, CredentialInfo());
}
}
diff --git a/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.h b/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.h
index 00ad0bcc72e..289a820efb4 100644
--- a/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.h
+++ b/chromium/components/password_manager/core/browser/credential_manager_pending_request_task.h
@@ -5,12 +5,14 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_CREDENTIAL_MANAGER_PENDING_REQUEST_TASK_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_CREDENTIAL_MANAGER_PENDING_REQUEST_TASK_H_
+#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
+#include "components/password_manager/core/browser/http_password_migrator.h"
#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "url/gurl.h"
@@ -37,9 +39,6 @@ class CredentialManagerPendingRequestTaskDelegate {
// Retrieves the current page origin.
virtual GURL GetOrigin() const = 0;
- // Retrieves a synthetic PasswordForm for the current page origin.
- virtual PasswordStore::FormDigest GetSynthesizedFormForOrigin() const = 0;
-
// Returns the PasswordManagerClient.
virtual PasswordManagerClient* client() const = 0;
@@ -54,33 +53,41 @@ class CredentialManagerPendingRequestTaskDelegate {
};
// Retrieves credentials from the PasswordStore.
-class CredentialManagerPendingRequestTask : public PasswordStoreConsumer {
+class CredentialManagerPendingRequestTask
+ : public PasswordStoreConsumer,
+ public HttpPasswordMigrator::Consumer {
public:
CredentialManagerPendingRequestTask(
CredentialManagerPendingRequestTaskDelegate* delegate,
const SendCredentialCallback& callback,
bool request_zero_click_only,
- const GURL& request_origin,
bool include_passwords,
- const std::vector<GURL>& request_federations,
- const std::vector<std::string>& affiliated_realms);
+ const std::vector<GURL>& request_federations);
~CredentialManagerPendingRequestTask() override;
SendCredentialCallback send_callback() const { return send_callback_; }
const GURL& origin() const { return origin_; }
- // PasswordStoreConsumer implementation.
+ // PasswordStoreConsumer:
void OnGetPasswordStoreResults(
std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
private:
+ // HttpPasswordMigrator::Consumer:
+ void ProcessMigratedForms(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms) override;
+
+ void ProcessForms(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results);
+
CredentialManagerPendingRequestTaskDelegate* delegate_; // Weak;
SendCredentialCallback send_callback_;
const bool zero_click_only_;
const GURL origin_;
const bool include_passwords_;
std::set<std::string> federations_;
- std::set<std::string> affiliated_realms_;
+
+ std::unique_ptr<HttpPasswordMigrator> http_migrator_;
DISALLOW_COPY_AND_ASSIGN(CredentialManagerPendingRequestTask);
};
diff --git a/chromium/components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.cc b/chromium/components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.cc
index 85294376828..79cfe6b1816 100644
--- a/chromium/components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.cc
+++ b/chromium/components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.cc
@@ -5,35 +5,21 @@
#include "components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.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/password_store.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "url/gurl.h"
namespace password_manager {
CredentialManagerPendingRequireUserMediationTask::
CredentialManagerPendingRequireUserMediationTask(
- CredentialManagerPendingRequireUserMediationTaskDelegate* delegate,
- const GURL& origin,
- const std::vector<std::string>& affiliated_realms)
- : delegate_(delegate),
- affiliated_realms_(affiliated_realms.begin(), affiliated_realms.end()) {
- registrable_domains_.insert(
- net::registry_controlled_domains::GetDomainAndRegistry(
- origin,
- net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
-}
+ CredentialManagerPendingRequireUserMediationTaskDelegate* delegate)
+ : delegate_(delegate), pending_requests_(0) {}
CredentialManagerPendingRequireUserMediationTask::
~CredentialManagerPendingRequireUserMediationTask() = default;
void CredentialManagerPendingRequireUserMediationTask::AddOrigin(
- const GURL& origin) {
- registrable_domains_.insert(
- net::registry_controlled_domains::GetDomainAndRegistry(
- origin,
- net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
+ const PasswordStore::FormDigest& form_digest) {
+ delegate_->GetPasswordStore()->GetLogins(form_digest, this);
+ pending_requests_++;
}
void CredentialManagerPendingRequireUserMediationTask::
@@ -41,23 +27,14 @@ void CredentialManagerPendingRequireUserMediationTask::
std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
PasswordStore* store = delegate_->GetPasswordStore();
for (const auto& form : results) {
- std::string form_registrable_domain =
- net::registry_controlled_domains::GetDomainAndRegistry(
- form->origin,
- net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
- if (registrable_domains_.count(form_registrable_domain) ||
- (affiliated_realms_.count(form->signon_realm) &&
- AffiliatedMatchHelper::IsValidAndroidCredential(
- PasswordStore::FormDigest(*form)))) {
+ if (!form->skip_zero_click) {
form->skip_zero_click = true;
- // Note that UpdateLogin ends up copying the form while posting a task to
- // update the PasswordStore, so it's fine to let |results| delete the
- // original at the end of this method.
store->UpdateLogin(*form);
}
}
-
- delegate_->DoneRequiringUserMediation();
+ pending_requests_--;
+ if (!pending_requests_)
+ delegate_->DoneRequiringUserMediation();
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.h b/chromium/components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.h
index 300160cdabc..a703bc5c0a7 100644
--- a/chromium/components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.h
+++ b/chromium/components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.h
@@ -5,26 +5,17 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_CREDENTIAL_MANAGER_PENDING_REQUIRE_USER_MEDIATION_TASK_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_CREDENTIAL_MANAGER_PENDING_REQUIRE_USER_MEDIATION_TASK_H_
-#include <set>
-#include <string>
-
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
+#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
-class GURL;
-
-namespace autofill {
-struct PasswordForm;
-} // namespace autofill
-
namespace password_manager {
-class PasswordStore;
-
// Handles mediation completion and retrieves embedder-dependent services.
class CredentialManagerPendingRequireUserMediationTaskDelegate {
public:
+ virtual ~CredentialManagerPendingRequireUserMediationTaskDelegate() {}
+
// Retrieves the PasswordStore.
virtual PasswordStore* GetPasswordStore() = 0;
@@ -36,24 +27,23 @@ class CredentialManagerPendingRequireUserMediationTaskDelegate {
class CredentialManagerPendingRequireUserMediationTask
: public PasswordStoreConsumer {
public:
- CredentialManagerPendingRequireUserMediationTask(
- CredentialManagerPendingRequireUserMediationTaskDelegate* delegate,
- const GURL& origin,
- const std::vector<std::string>& affiliated_realms);
+ explicit CredentialManagerPendingRequireUserMediationTask(
+ CredentialManagerPendingRequireUserMediationTaskDelegate* delegate);
~CredentialManagerPendingRequireUserMediationTask() override;
// Adds an origin to require user mediation.
- void AddOrigin(const GURL& origin);
+ void AddOrigin(const PasswordStore::FormDigest& form_digest);
+ private:
// PasswordStoreConsumer implementation.
void OnGetPasswordStoreResults(
std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
- private:
CredentialManagerPendingRequireUserMediationTaskDelegate* const
delegate_; // Weak.
- std::set<std::string> registrable_domains_;
- std::set<std::string> affiliated_realms_;
+
+ // Number of password store requests to be resolved.
+ int pending_requests_;
DISALLOW_COPY_AND_ASSIGN(CredentialManagerPendingRequireUserMediationTask);
};
diff --git a/chromium/components/password_manager/core/browser/fake_form_fetcher.cc b/chromium/components/password_manager/core/browser/fake_form_fetcher.cc
new file mode 100644
index 00000000000..3f0ca14b1f8
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/fake_form_fetcher.cc
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/fake_form_fetcher.h"
+
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/statistics_table.h"
+
+using autofill::PasswordForm;
+
+namespace password_manager {
+
+FakeFormFetcher::FakeFormFetcher() = default;
+
+FakeFormFetcher::~FakeFormFetcher() = default;
+
+void FakeFormFetcher::AddConsumer(Consumer* consumer) {
+ consumers_.insert(consumer);
+}
+
+FormFetcher::State FakeFormFetcher::GetState() const {
+ return state_;
+}
+
+const std::vector<InteractionsStats>& FakeFormFetcher::GetInteractionsStats()
+ const {
+ return stats_;
+}
+
+const std::vector<const autofill::PasswordForm*>&
+FakeFormFetcher::GetFederatedMatches() const {
+ return federated_;
+}
+
+void FakeFormFetcher::SetNonFederated(
+ const std::vector<const autofill::PasswordForm*>& non_federated,
+ size_t filtered_count) {
+ state_ = State::NOT_WAITING;
+ for (Consumer* consumer : consumers_) {
+ consumer->ProcessMatches(non_federated, filtered_count);
+ }
+}
+
+void FakeFormFetcher::Fetch() {
+ state_ = State::WAITING;
+}
+
+} // 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
new file mode 100644
index 00000000000..93fa7e39846
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/fake_form_fetcher.h
@@ -0,0 +1,75 @@
+// 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_CORE_BROWSER_FAKE_FORM_FETCHER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FAKE_FORM_FETCHER_H_
+
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/password_manager/core/browser/form_fetcher.h"
+#include "components/password_manager/core/browser/statistics_table.h"
+
+namespace autofill {
+struct PasswordForm;
+}
+
+namespace password_manager {
+
+struct InteractionsStats;
+
+// Test implementation of FormFetcher useful for simple fakes and as a base for
+// mocks.
+class FakeFormFetcher : public FormFetcher {
+ public:
+ FakeFormFetcher();
+
+ ~FakeFormFetcher() override;
+
+ // Registers consumers to be notified when results are set. Unlike the
+ // production version, assumes that results have not arrived yet, i.e., one
+ // has to first call AddConsumer and then SetNonFederated.
+ void AddConsumer(Consumer* consumer) override;
+
+ // Returns State::WAITING if Fetch() was called after any Set* calls, and
+ // State::NOT_WAITING otherwise.
+ State GetState() const override;
+
+ // Statistics for recent password bubble usage.
+ const std::vector<InteractionsStats>& GetInteractionsStats() const override;
+
+ void set_stats(const std::vector<InteractionsStats>& stats) {
+ state_ = State::NOT_WAITING;
+ stats_ = stats;
+ }
+
+ const std::vector<const autofill::PasswordForm*>& GetFederatedMatches()
+ const override;
+
+ void set_federated(
+ const std::vector<const autofill::PasswordForm*>& federated) {
+ state_ = State::NOT_WAITING;
+ federated_ = federated;
+ }
+
+ void SetNonFederated(
+ const std::vector<const autofill::PasswordForm*>& non_federated,
+ size_t filtered_count);
+
+ // Only sets the internal state to WAITING, no call to PasswordStore.
+ void Fetch() override;
+
+ private:
+ std::set<Consumer*> consumers_;
+ State state_ = State::NOT_WAITING;
+ std::vector<InteractionsStats> stats_;
+ std::vector<const autofill::PasswordForm*> federated_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeFormFetcher);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FAKE_FORM_FETCHER_H_
diff --git a/chromium/components/password_manager/core/browser/form_fetcher.h b/chromium/components/password_manager/core/browser/form_fetcher.h
index 24ca598ea60..e2e16423c7f 100644
--- a/chromium/components/password_manager/core/browser/form_fetcher.h
+++ b/chromium/components/password_manager/core/browser/form_fetcher.h
@@ -60,7 +60,7 @@ class FormFetcher {
virtual State GetState() const = 0;
// Statistics for recent password bubble usage.
- virtual const std::vector<const InteractionsStats*>& GetInteractionsStats()
+ virtual const std::vector<InteractionsStats>& GetInteractionsStats()
const = 0;
// Federated matches obtained from the backend. Valid only if GetState()
@@ -68,6 +68,12 @@ class FormFetcher {
virtual const std::vector<const autofill::PasswordForm*>&
GetFederatedMatches() const = 0;
+ // Fetches stored matching logins. In addition the statistics is fetched on
+ // platforms with the password bubble. This is called automatically during
+ // construction and can be called manually later as well to cause an update
+ // of the cached credentials.
+ virtual void Fetch() = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(FormFetcher);
};
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 0266bd1966f..e4f8ed6f2a4 100644
--- a/chromium/components/password_manager/core/browser/form_fetcher_impl.cc
+++ b/chromium/components/password_manager/core/browser/form_fetcher_impl.cc
@@ -7,16 +7,21 @@
#include <algorithm>
#include <utility>
-#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/credentials_filter.h"
#include "components/password_manager/core/browser/password_manager_client.h"
+#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/statistics_table.h"
using autofill::PasswordForm;
+// Shorten the name to spare line breaks. The code provides enough context
+// already.
+using Logger = autofill::SavePasswordProgressLogger;
+
namespace password_manager {
namespace {
@@ -40,25 +45,29 @@ std::vector<std::unique_ptr<PasswordForm>> SplitFederatedMatches(
return federated_matches;
}
-// Create a vector of const T* from a vector of unique_ptr<T>.
-template <typename T>
-std::vector<const T*> MakeWeakCopies(
- const std::vector<std::unique_ptr<T>>& owning) {
- std::vector<const T*> result;
- result.resize(owning.size());
- std::transform(owning.begin(), owning.end(), result.begin(),
- [](const std::unique_ptr<T>& ptr) { return ptr.get(); });
+// Create a vector of const PasswordForm from a vector of
+// unique_ptr<PasswordForm> by applying get() item-wise.
+std::vector<const PasswordForm*> MakeWeakCopies(
+ const std::vector<std::unique_ptr<PasswordForm>>& owning) {
+ std::vector<const PasswordForm*> result(owning.size());
+ std::transform(
+ owning.begin(), owning.end(), result.begin(),
+ [](const std::unique_ptr<PasswordForm>& ptr) { return ptr.get(); });
return result;
}
} // namespace
-FormFetcherImpl::FormFetcherImpl(const PasswordManagerClient* client)
- : client_(client) {}
+FormFetcherImpl::FormFetcherImpl(PasswordStore::FormDigest form_digest,
+ const PasswordManagerClient* client,
+ bool should_migrate_http_passwords)
+ : form_digest_(std::move(form_digest)),
+ client_(client),
+ should_migrate_http_passwords_(should_migrate_http_passwords) {}
FormFetcherImpl::~FormFetcherImpl() = default;
-void FormFetcherImpl::AddConsumer(Consumer* consumer) {
+void FormFetcherImpl::AddConsumer(FormFetcher::Consumer* consumer) {
DCHECK(consumer);
consumers_.insert(consumer);
if (state_ == State::NOT_WAITING)
@@ -69,20 +78,95 @@ FormFetcherImpl::State FormFetcherImpl::GetState() const {
return state_;
}
-const std::vector<const InteractionsStats*>&
-FormFetcherImpl::GetInteractionsStats() const {
- return weak_interactions_stats_;
+const std::vector<InteractionsStats>& FormFetcherImpl::GetInteractionsStats()
+ const {
+ return interactions_stats_;
}
-const std::vector<const autofill::PasswordForm*>&
-FormFetcherImpl::GetFederatedMatches() const {
+const std::vector<const PasswordForm*>& FormFetcherImpl::GetFederatedMatches()
+ const {
return weak_federated_;
}
-void FormFetcherImpl::SetResults(
- std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+void FormFetcherImpl::OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<PasswordForm>> results) {
DCHECK_EQ(State::WAITING, state_);
+ state_ = State::NOT_WAITING;
+
+ if (need_to_refetch_) {
+ // The received results are no longer up to date, need to re-request.
+ Fetch();
+ need_to_refetch_ = false;
+ return;
+ }
+
+ std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
+ if (password_manager_util::IsLoggingActive(client_)) {
+ logger.reset(
+ new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
+ logger->LogMessage(Logger::STRING_ON_GET_STORE_RESULTS_METHOD);
+ logger->LogNumber(Logger::STRING_NUMBER_RESULTS, results.size());
+ }
+
+ if (should_migrate_http_passwords_ && results.empty() &&
+ form_digest_.origin.SchemeIs(url::kHttpsScheme)) {
+ http_migrator_ = base::MakeUnique<HttpPasswordMigrator>(
+ form_digest_.origin, client_->GetPasswordStore(), this);
+ return;
+ }
+
+ ProcessPasswordStoreResults(std::move(results));
+}
+
+void FormFetcherImpl::OnGetSiteStatistics(
+ std::vector<InteractionsStats> stats) {
+ // On Windows the password request may be resolved after the statistics due to
+ // importing from IE.
+ interactions_stats_ = std::move(stats);
+}
+
+void FormFetcherImpl::ProcessMigratedForms(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms) {
+ ProcessPasswordStoreResults(std::move(forms));
+}
+
+void FormFetcherImpl::Fetch() {
+ std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
+ if (password_manager_util::IsLoggingActive(client_)) {
+ logger.reset(
+ new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
+ logger->LogMessage(Logger::STRING_FETCH_METHOD);
+ logger->LogNumber(Logger::STRING_FORM_FETCHER_STATE,
+ static_cast<int>(state_));
+ }
+
+ if (state_ == State::WAITING) {
+ // There is currently a password store query in progress, need to re-fetch
+ // store results later.
+ need_to_refetch_ = true;
+ return;
+ }
+
+ PasswordStore* password_store = client_->GetPasswordStore();
+ if (!password_store) {
+ if (logger)
+ logger->LogMessage(Logger::STRING_NO_STORE);
+ NOTREACHED();
+ return;
+ }
+ state_ = State::WAITING;
+ password_store->GetLogins(form_digest_, this);
+
+// The statistics isn't needed on mobile, only on desktop. Let's save some
+// processor cycles.
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+ // The statistics is needed for the "Save password?" bubble.
+ password_store->GetSiteStats(form_digest_.origin.GetOrigin(), this);
+#endif
+}
+void FormFetcherImpl::ProcessPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
federated_ = SplitFederatedMatches(&results);
non_federated_ = std::move(results);
@@ -96,16 +180,8 @@ void FormFetcherImpl::SetResults(
weak_non_federated_ = MakeWeakCopies(non_federated_);
weak_federated_ = MakeWeakCopies(federated_);
- state_ = State::NOT_WAITING;
-
- for (Consumer* consumer : consumers_)
+ for (FormFetcher::Consumer* consumer : consumers_)
consumer->ProcessMatches(weak_non_federated_, filtered_count_);
}
-void FormFetcherImpl::SetStats(
- std::vector<std::unique_ptr<InteractionsStats>> stats) {
- interactions_stats_ = std::move(stats);
- weak_interactions_stats_ = MakeWeakCopies(interactions_stats_);
-}
-
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/form_fetcher_impl.h b/chromium/components/password_manager/core/browser/form_fetcher_impl.h
index e46438697bd..0a7b2a556b0 100644
--- a/chromium/components/password_manager/core/browser/form_fetcher_impl.h
+++ b/chromium/components/password_manager/core/browser/form_fetcher_impl.h
@@ -11,6 +11,9 @@
#include "base/macros.h"
#include "components/password_manager/core/browser/form_fetcher.h"
+#include "components/password_manager/core/browser/http_password_migrator.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
namespace password_manager {
@@ -18,37 +21,43 @@ class PasswordManagerClient;
// Production implementation of FormFetcher. Fetches credentials associated
// with a particular origin.
-// TODO(crbug.com/621355): This class should ultimately work with PasswordStore.
-class FormFetcherImpl : public FormFetcher {
+class FormFetcherImpl : public FormFetcher,
+ public PasswordStoreConsumer,
+ public HttpPasswordMigrator::Consumer {
public:
- explicit FormFetcherImpl(const PasswordManagerClient* client);
+ // |form_digest| describes what credentials need to be retrieved and
+ // |client| serves the PasswordStore, the logging information etc.
+ FormFetcherImpl(PasswordStore::FormDigest form_digest,
+ const PasswordManagerClient* client,
+ bool should_migrate_http_passwords);
~FormFetcherImpl() override;
// FormFetcher:
- void AddConsumer(Consumer* consumer) override;
+ void AddConsumer(FormFetcher::Consumer* consumer) override;
State GetState() const override;
- const std::vector<const InteractionsStats*>& GetInteractionsStats()
- const override;
+ const std::vector<InteractionsStats>& GetInteractionsStats() const override;
const std::vector<const autofill::PasswordForm*>& GetFederatedMatches()
const override;
+ void Fetch() override;
- // TODO(crbug.com/621355): Remove this once FormFetcher becomes a
- // PasswordStoreConsumer.
- void set_state(State state) { state_ = state; }
-
- // TODO(crbug.com/621355): Remove this once FormFetcher becomes a
- // PasswordStoreConsumer.
- // Resets the results owned by |this| with new results from PasswordStore and
- // sets the state to NOT_WAITING.
- void SetResults(std::vector<std::unique_ptr<autofill::PasswordForm>> results);
+ // PasswordStoreConsumer:
+ void OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+ void OnGetSiteStatistics(std::vector<InteractionsStats> stats) override;
- // TODO(crbug.com/621355): Remove this once FormFetcher becomes a
- // PasswordStoreConsumer.
- // Resets the stats owned by |this| with new stats from PasswordStore.
- void SetStats(std::vector<std::unique_ptr<InteractionsStats>> stats);
+ // HttpPasswordMigrator::Consumer:
+ void ProcessMigratedForms(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms) override;
private:
+ // Processes password form results and forwards them to the |consumers_|.
+ void ProcessPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results);
+
+ // PasswordStore results will be fetched for this description.
+ const PasswordStore::FormDigest form_digest_;
+
// Results obtained from PasswordStore:
std::vector<std::unique_ptr<autofill::PasswordForm>> non_federated_;
@@ -58,15 +67,14 @@ class FormFetcherImpl : public FormFetcher {
std::vector<std::unique_ptr<autofill::PasswordForm>> federated_;
// Statistics for the current domain.
- std::vector<std::unique_ptr<InteractionsStats>> interactions_stats_;
+ std::vector<InteractionsStats> interactions_stats_;
// Non-owning copies of the vectors above.
std::vector<const autofill::PasswordForm*> weak_non_federated_;
std::vector<const autofill::PasswordForm*> weak_federated_;
- std::vector<const InteractionsStats*> weak_interactions_stats_;
// Consumers of the fetcher, all are assumed to outlive |this|.
- std::set<Consumer*> consumers_;
+ std::set<FormFetcher::Consumer*> consumers_;
// Client used to obtain a CredentialFilter.
const PasswordManagerClient* const client_;
@@ -78,6 +86,16 @@ class FormFetcherImpl : public FormFetcher {
// State of the fetcher.
State state_ = State::NOT_WAITING;
+ // False unless FetchDataFromPasswordStore has been called again without the
+ // password store returning results in the meantime.
+ bool need_to_refetch_ = false;
+
+ // Indicates whether HTTP passwords should be migrated to HTTPS.
+ const bool should_migrate_http_passwords_;
+
+ // Does the actual migration.
+ std::unique_ptr<HttpPasswordMigrator> http_migrator_;
+
DISALLOW_COPY_AND_ASSIGN(FormFetcherImpl);
};
diff --git a/chromium/components/password_manager/core/browser/form_fetcher_impl_unittest.cc b/chromium/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
index 7876c0179a0..9ab832fe114 100644
--- a/chromium/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
+++ b/chromium/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
@@ -11,10 +11,15 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/mock_password_store.h"
+#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/statistics_table.h"
#include "components/password_manager/core/browser/stub_credentials_filter.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
@@ -22,6 +27,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
+#include "url/url_constants.h"
using autofill::PasswordForm;
using base::ASCIIToUTF16;
@@ -29,6 +35,7 @@ using base::StringPiece;
using testing::_;
using testing::IsEmpty;
using testing::Pointee;
+using testing::Return;
using testing::UnorderedElementsAre;
namespace password_manager {
@@ -80,13 +87,18 @@ class FakePasswordManagerClient : public StubPasswordManagerClient {
filter_ = std::move(filter);
}
+ void set_store(PasswordStore* store) { store_ = store; }
+
private:
const CredentialsFilter* GetStoreResultFilter() const override {
return filter_ ? filter_.get()
: StubPasswordManagerClient::GetStoreResultFilter();
}
+ PasswordStore* GetPasswordStore() const override { return store_; }
+
std::unique_ptr<CredentialsFilter> filter_;
+ PasswordStore* store_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(FakePasswordManagerClient);
};
@@ -102,6 +114,17 @@ PasswordForm CreateNonFederated() {
return form;
}
+// Creates a dummy non-federated HTTP form with some basic arbitrary values.
+PasswordForm CreateHTTPNonFederated() {
+ PasswordForm form;
+ form.origin = GURL("http://example.in");
+ form.signon_realm = form.origin.spec();
+ form.action = GURL("http://login.example.org");
+ form.username_value = ASCIIToUTF16("user");
+ form.password_value = ASCIIToUTF16("password");
+ return form;
+}
+
// Creates a dummy federated form with some basic arbitrary values.
PasswordForm CreateFederated() {
PasswordForm form = CreateNonFederated();
@@ -110,75 +133,148 @@ PasswordForm CreateFederated() {
return form;
}
+// Creates an Android federated credential.
+PasswordForm CreateAndroidFederated() {
+ PasswordForm form = CreateFederated();
+ form.signon_realm = "android://hash@com.example.android/";
+ form.origin = GURL(form.signon_realm);
+ form.action = GURL();
+ form.is_affiliation_based_match = true;
+ return form;
+}
+
+// Small helper that wraps passed in forms in unique ptrs.
+std::vector<std::unique_ptr<PasswordForm>> make_results(
+ const std::vector<PasswordForm>& forms) {
+ std::vector<std::unique_ptr<PasswordForm>> results;
+ results.reserve(forms.size());
+ for (const auto& form : forms)
+ results.push_back(base::MakeUnique<PasswordForm>(form));
+ return results;
+}
+
+class MockFormFetcherImpl : public FormFetcherImpl {
+ public:
+ // Inherit constructors.
+ using FormFetcherImpl::FormFetcherImpl;
+
+ // Google Mock is currently unable to mock |ProcessMigratedForms| due to the
+ // presence of move-only types. In order to ensure it is called, a dummy is
+ // added which can be passed to |EXPECT_CALL|.
+ void ProcessMigratedForms(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) override {
+ FormFetcherImpl::ProcessMigratedForms(std::move(results));
+ ProcessMigratedFormsDummy();
+ }
+
+ MOCK_METHOD0(ProcessMigratedFormsDummy, void());
+};
+
+ACTION(InvokeMigratorGetResults) {
+ static_cast<HttpPasswordMigrator*>(arg0)->OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<PasswordForm>>());
+}
+
} // namespace
class FormFetcherImplTest : public testing::Test {
public:
- FormFetcherImplTest() : form_fetcher_(&client_) {}
+ FormFetcherImplTest()
+ : form_digest_(PasswordForm::SCHEME_HTML,
+ "http://accounts.google.com",
+ GURL("http://accounts.google.com/a/LoginAuth")) {
+ mock_store_ = new MockPasswordStore();
+ client_.set_store(mock_store_.get());
+
+ form_fetcher_ = base::MakeUnique<FormFetcherImpl>(
+ form_digest_, &client_, /* should_migrate_http_passwords */ false);
+ }
- ~FormFetcherImplTest() override = default;
+ ~FormFetcherImplTest() override { mock_store_->ShutdownOnUIThread(); }
protected:
+ // A wrapper around form_fetcher_.Fetch(), adding the call expectations.
+ void Fetch() {
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+ EXPECT_CALL(*mock_store_, GetSiteStatsImpl(_))
+ .WillOnce(Return(std::vector<InteractionsStats>()));
+#endif
+ EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get()));
+ form_fetcher_->Fetch();
+ base::RunLoop().RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_store_.get());
+ }
+
+ base::MessageLoop message_loop_; // Used by mock_store_.
+ PasswordStore::FormDigest form_digest_;
+ std::unique_ptr<FormFetcherImpl> form_fetcher_;
+ MockConsumer consumer_;
+ scoped_refptr<MockPasswordStore> mock_store_;
FakePasswordManagerClient client_;
- FormFetcherImpl form_fetcher_;
- testing::NiceMock<MockConsumer> consumer_;
private:
DISALLOW_COPY_AND_ASSIGN(FormFetcherImplTest);
};
-// Check that no PasswordStore results are handled correctly.
+// Check that the absence of PasswordStore results is handled correctly.
TEST_F(FormFetcherImplTest, NoStoreResults) {
+ Fetch();
EXPECT_CALL(consumer_, ProcessMatches(_, _)).Times(0);
- form_fetcher_.set_state(FormFetcher::State::WAITING);
- form_fetcher_.AddConsumer(&consumer_);
- EXPECT_EQ(FormFetcher::State::WAITING, form_fetcher_.GetState());
+ form_fetcher_->AddConsumer(&consumer_);
+ EXPECT_EQ(FormFetcher::State::WAITING, form_fetcher_->GetState());
}
// Check that empty PasswordStore results are handled correctly.
TEST_F(FormFetcherImplTest, Empty) {
- form_fetcher_.AddConsumer(&consumer_);
- form_fetcher_.set_state(FormFetcher::State::WAITING);
+ Fetch();
+ form_fetcher_->AddConsumer(&consumer_);
EXPECT_CALL(consumer_, ProcessMatches(IsEmpty(), 0u));
- form_fetcher_.SetResults(std::vector<std::unique_ptr<PasswordForm>>());
- EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_.GetState());
- EXPECT_THAT(form_fetcher_.GetFederatedMatches(), IsEmpty());
+ form_fetcher_->OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<PasswordForm>>());
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
+ EXPECT_THAT(form_fetcher_->GetFederatedMatches(), IsEmpty());
}
// Check that non-federated PasswordStore results are handled correctly.
TEST_F(FormFetcherImplTest, NonFederated) {
+ Fetch();
PasswordForm non_federated = CreateNonFederated();
- form_fetcher_.AddConsumer(&consumer_);
- form_fetcher_.set_state(FormFetcher::State::WAITING);
+ form_fetcher_->AddConsumer(&consumer_);
std::vector<std::unique_ptr<PasswordForm>> results;
results.push_back(base::MakeUnique<PasswordForm>(non_federated));
EXPECT_CALL(consumer_,
ProcessMatches(UnorderedElementsAre(Pointee(non_federated)), 0u));
- form_fetcher_.SetResults(std::move(results));
- EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_.GetState());
- EXPECT_THAT(form_fetcher_.GetFederatedMatches(), IsEmpty());
+ form_fetcher_->OnGetPasswordStoreResults(std::move(results));
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
+ EXPECT_THAT(form_fetcher_->GetFederatedMatches(), IsEmpty());
}
// Check that federated PasswordStore results are handled correctly.
TEST_F(FormFetcherImplTest, Federated) {
+ Fetch();
PasswordForm federated = CreateFederated();
- form_fetcher_.AddConsumer(&consumer_);
- form_fetcher_.set_state(FormFetcher::State::WAITING);
+ PasswordForm android_federated = CreateAndroidFederated();
+ form_fetcher_->AddConsumer(&consumer_);
std::vector<std::unique_ptr<PasswordForm>> results;
results.push_back(base::MakeUnique<PasswordForm>(federated));
+ results.push_back(base::MakeUnique<PasswordForm>(android_federated));
EXPECT_CALL(consumer_, ProcessMatches(IsEmpty(), 0u));
- form_fetcher_.SetResults(std::move(results));
- EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_.GetState());
- EXPECT_THAT(form_fetcher_.GetFederatedMatches(),
- UnorderedElementsAre(Pointee(federated)));
+ form_fetcher_->OnGetPasswordStoreResults(std::move(results));
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
+ EXPECT_THAT(
+ form_fetcher_->GetFederatedMatches(),
+ UnorderedElementsAre(Pointee(federated), Pointee(android_federated)));
}
// Check that mixed PasswordStore results are handled correctly.
TEST_F(FormFetcherImplTest, Mixed) {
+ Fetch();
PasswordForm federated1 = CreateFederated();
federated1.username_value = ASCIIToUTF16("user");
PasswordForm federated2 = CreateFederated();
federated2.username_value = ASCIIToUTF16("user_B");
+ PasswordForm federated3 = CreateAndroidFederated();
+ federated3.username_value = ASCIIToUTF16("user_B");
PasswordForm non_federated1 = CreateNonFederated();
non_federated1.username_value = ASCIIToUTF16("user");
PasswordForm non_federated2 = CreateNonFederated();
@@ -186,11 +282,11 @@ TEST_F(FormFetcherImplTest, Mixed) {
PasswordForm non_federated3 = CreateNonFederated();
non_federated3.username_value = ASCIIToUTF16("user_D");
- form_fetcher_.AddConsumer(&consumer_);
- form_fetcher_.set_state(FormFetcher::State::WAITING);
+ form_fetcher_->AddConsumer(&consumer_);
std::vector<std::unique_ptr<PasswordForm>> results;
results.push_back(base::MakeUnique<PasswordForm>(federated1));
results.push_back(base::MakeUnique<PasswordForm>(federated2));
+ results.push_back(base::MakeUnique<PasswordForm>(federated3));
results.push_back(base::MakeUnique<PasswordForm>(non_federated1));
results.push_back(base::MakeUnique<PasswordForm>(non_federated2));
results.push_back(base::MakeUnique<PasswordForm>(non_federated3));
@@ -199,14 +295,16 @@ TEST_F(FormFetcherImplTest, Mixed) {
Pointee(non_federated2),
Pointee(non_federated3)),
0u));
- form_fetcher_.SetResults(std::move(results));
- EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_.GetState());
- EXPECT_THAT(form_fetcher_.GetFederatedMatches(),
- UnorderedElementsAre(Pointee(federated1), Pointee(federated2)));
+ form_fetcher_->OnGetPasswordStoreResults(std::move(results));
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
+ EXPECT_THAT(form_fetcher_->GetFederatedMatches(),
+ UnorderedElementsAre(Pointee(federated1), Pointee(federated2),
+ Pointee(federated3)));
}
// Check that PasswordStore results are filtered correctly.
TEST_F(FormFetcherImplTest, Filtered) {
+ Fetch();
PasswordForm federated = CreateFederated();
federated.username_value = ASCIIToUTF16("user");
PasswordForm non_federated1 = CreateNonFederated();
@@ -217,8 +315,7 @@ TEST_F(FormFetcherImplTest, Filtered) {
// Set up a filter to remove all credentials with the username "user".
client_.set_filter(base::MakeUnique<NameFilter>("user"));
- form_fetcher_.AddConsumer(&consumer_);
- form_fetcher_.set_state(FormFetcher::State::WAITING);
+ form_fetcher_->AddConsumer(&consumer_);
std::vector<std::unique_ptr<PasswordForm>> results;
results.push_back(base::MakeUnique<PasswordForm>(federated));
results.push_back(base::MakeUnique<PasswordForm>(non_federated1));
@@ -228,20 +325,147 @@ TEST_F(FormFetcherImplTest, Filtered) {
EXPECT_CALL(consumer_,
ProcessMatches(UnorderedElementsAre(Pointee(non_federated2)),
kNumFiltered));
- form_fetcher_.SetResults(std::move(results));
- EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_.GetState());
+ form_fetcher_->OnGetPasswordStoreResults(std::move(results));
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
// However, federated results should not be filtered.
- EXPECT_THAT(form_fetcher_.GetFederatedMatches(),
+ EXPECT_THAT(form_fetcher_->GetFederatedMatches(),
UnorderedElementsAre(Pointee(federated)));
}
// Check that stats from PasswordStore are handled correctly.
TEST_F(FormFetcherImplTest, Stats) {
- form_fetcher_.AddConsumer(&consumer_);
- std::vector<std::unique_ptr<InteractionsStats>> stats;
- stats.push_back(base::MakeUnique<InteractionsStats>());
- form_fetcher_.SetStats(std::move(stats));
- EXPECT_EQ(1u, form_fetcher_.GetInteractionsStats().size());
+ Fetch();
+ form_fetcher_->AddConsumer(&consumer_);
+ std::vector<InteractionsStats> stats(1);
+ form_fetcher_->OnGetSiteStatistics(std::move(stats));
+ EXPECT_EQ(1u, form_fetcher_->GetInteractionsStats().size());
+}
+
+// Test that multiple calls of Fetch() are handled gracefully, and that they
+// always result in passing the most up-to-date information to the consumers.
+TEST_F(FormFetcherImplTest, Update_Reentrance) {
+ Fetch();
+ form_fetcher_->AddConsumer(&consumer_);
+ // The fetcher is currently waiting for a store response, after it fired a
+ // GetLogins request during the Fetch() above. The second and third Fetch
+ // (below) won't cause a GetLogins right now, but will ensure that a second
+ // GetLogins will be called later.
+ form_fetcher_->Fetch();
+ form_fetcher_->Fetch();
+
+ // First response from the store, should be ignored.
+ PasswordForm form_a = CreateNonFederated();
+ form_a.username_value = ASCIIToUTF16("a@gmail.com");
+ std::vector<std::unique_ptr<PasswordForm>> old_results;
+ old_results.push_back(base::MakeUnique<PasswordForm>(form_a));
+ // Because of the pending updates, the old PasswordStore results are not
+ // forwarded to the consumers.
+ EXPECT_CALL(consumer_, ProcessMatches(_, _)).Times(0);
+ // Delivering the first results will trigger the new GetLogins call, because
+ // of the Fetch() above.
+ EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get()));
+ form_fetcher_->OnGetPasswordStoreResults(std::move(old_results));
+
+ // Second response from the store should not be ignored.
+ PasswordForm form_b = CreateNonFederated();
+ form_b.username_value = ASCIIToUTF16("b@gmail.com");
+
+ PasswordForm form_c = CreateNonFederated();
+ form_c.username_value = ASCIIToUTF16("c@gmail.com");
+
+ EXPECT_CALL(consumer_,
+ ProcessMatches(
+ UnorderedElementsAre(Pointee(form_b), Pointee(form_c)), 0u));
+ std::vector<std::unique_ptr<PasswordForm>> results;
+ results.push_back(base::MakeUnique<PasswordForm>(form_b));
+ results.push_back(base::MakeUnique<PasswordForm>(form_c));
+ form_fetcher_->OnGetPasswordStoreResults(std::move(results));
+}
+
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+TEST_F(FormFetcherImplTest, FetchStatistics) {
+ InteractionsStats stats;
+ stats.origin_domain = form_digest_.origin.GetOrigin();
+ stats.username_value = ASCIIToUTF16("some username");
+ stats.dismissal_count = 5;
+ std::vector<InteractionsStats> db_stats = {stats};
+ EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get()));
+ EXPECT_CALL(*mock_store_, GetSiteStatsImpl(stats.origin_domain))
+ .WillOnce(Return(db_stats));
+ form_fetcher_->Fetch();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_THAT(form_fetcher_->GetInteractionsStats(),
+ UnorderedElementsAre(stats));
+}
+#else
+TEST_F(FormFetcherImplTest, DontFetchStatistics) {
+ EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get()));
+ EXPECT_CALL(*mock_store_, GetSiteStatsImpl(_)).Times(0);
+ form_fetcher_->Fetch();
+ base::RunLoop().RunUntilIdle();
+}
+#endif
+
+// Test that ensures HTTP passwords are not migrated on HTTP sites.
+TEST_F(FormFetcherImplTest, DoNotTryToMigrateHTTPPasswordsOnHTTPSites) {
+ GURL::Replacements http_rep;
+ http_rep.SetSchemeStr(url::kHttpScheme);
+ const GURL http_origin = form_digest_.origin.ReplaceComponents(http_rep);
+ const PasswordStore::FormDigest http_form_digest(
+ PasswordForm::SCHEME_HTML, http_origin.GetOrigin().spec(), http_origin);
+
+ // A new form fetcher is created to be able to set the form digest and
+ // migration flag.
+ const auto http_form_fetcher = base::MakeUnique<MockFormFetcherImpl>(
+ http_form_digest, &client_, /* should_migrate_http_passwords */ true);
+
+ std::vector<PasswordForm> empty_forms;
+ const PasswordForm http_form = CreateHTTPNonFederated();
+
+ // Tests that there is no attempt to migrate credentials on HTTP origins.
+ // FormFetcherImplTest::Fetch() can't be used here due to different
+ // expectations. The repeated calls to MockFormFetcherImpl::Fetch() are
+ // necessary to set the correct state.
+ EXPECT_CALL(*mock_store_, GetLogins(_, _));
+ http_form_fetcher->Fetch();
+ EXPECT_CALL(*mock_store_, GetLogins(_, _)).Times(0);
+ http_form_fetcher->OnGetPasswordStoreResults(make_results(empty_forms));
+
+ EXPECT_CALL(*mock_store_, GetLogins(_, _));
+ http_form_fetcher->Fetch();
+ EXPECT_CALL(*mock_store_, GetLogins(_, _)).Times(0);
+ http_form_fetcher->OnGetPasswordStoreResults(make_results({http_form}));
+}
+
+// Test that ensures HTTP passwords are only migrated on HTTPS sites when no
+// HTTPS credentials are available.
+TEST_F(FormFetcherImplTest, TryToMigrateHTTPPasswordsOnHTTPSSites) {
+ GURL::Replacements https_rep;
+ https_rep.SetSchemeStr(url::kHttpsScheme);
+ const GURL https_origin = form_digest_.origin.ReplaceComponents(https_rep);
+ const PasswordStore::FormDigest https_form_digest(
+ PasswordForm::SCHEME_HTML, https_origin.GetOrigin().spec(), https_origin);
+
+ // A new form fetcher is created to be able to set the form digest and
+ // migration flag.
+ const auto https_form_fetcher = base::MakeUnique<MockFormFetcherImpl>(
+ https_form_digest, &client_, /* should_migrate_http_passwords */ true);
+
+ std::vector<PasswordForm> empty_forms;
+ const PasswordForm https_form = CreateNonFederated();
+
+ // Tests that there is only an attempt to migrate credentials on HTTPS origins
+ // when no other credentials are available.
+ // FormFetcherImplTest::Fetch() can't be used here due to different
+ // expectations. The repeated calls to MockFormFetcherImpl::Fetch() are
+ // necessary to set the correct state.
+ EXPECT_CALL(*mock_store_, GetLogins(_, _));
+ https_form_fetcher->Fetch();
+ EXPECT_CALL(*mock_store_, GetLogins(_, _))
+ .WillOnce(testing::WithArg<1>(InvokeMigratorGetResults()));
+ EXPECT_CALL(*https_form_fetcher, ProcessMigratedFormsDummy());
+ https_form_fetcher->OnGetPasswordStoreResults(make_results(empty_forms));
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/http_password_migrator.cc b/chromium/components/password_manager/core/browser/http_password_migrator.cc
new file mode 100644
index 00000000000..da6eb67c31a
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/http_password_migrator.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/password_manager/core/browser/http_password_migrator.h"
+
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "url/gurl.h"
+#include "url/url_constants.h"
+
+namespace password_manager {
+
+HttpPasswordMigrator::HttpPasswordMigrator(const GURL& https_origin,
+ PasswordStore* password_store,
+ Consumer* consumer)
+ : consumer_(consumer), password_store_(password_store) {
+ DCHECK(password_store_);
+ DCHECK(https_origin.is_valid());
+ DCHECK(https_origin.SchemeIs(url::kHttpsScheme)) << https_origin;
+
+ GURL::Replacements rep;
+ rep.SetSchemeStr(url::kHttpScheme);
+ GURL http_origin = https_origin.ReplaceComponents(rep);
+ PasswordStore::FormDigest form(autofill::PasswordForm::SCHEME_HTML,
+ http_origin.GetOrigin().spec(), http_origin);
+ password_store_->GetLogins(form, this);
+}
+
+HttpPasswordMigrator::~HttpPasswordMigrator() = default;
+
+void HttpPasswordMigrator::OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+ // Android and PSL matches are ignored.
+ results.erase(
+ std::remove_if(results.begin(), results.end(),
+ [](const std::unique_ptr<autofill::PasswordForm>& form) {
+ return form->is_affiliation_based_match ||
+ form->is_public_suffix_match;
+ }),
+ results.end());
+
+ // Add the new credentials to the password store. The HTTP forms aren't
+ // removed for now.
+ for (const auto& form : results) {
+ GURL::Replacements rep;
+ rep.SetSchemeStr(url::kHttpsScheme);
+ form->origin = form->origin.ReplaceComponents(rep);
+ form->signon_realm = form->origin.spec();
+ // If |action| is not HTTPS then it's most likely obsolete. Otherwise, it
+ // may still be valid.
+ if (!form->action.SchemeIs(url::kHttpsScheme))
+ form->action = form->origin;
+ form->form_data = autofill::FormData();
+ form->generation_upload_status = autofill::PasswordForm::NO_SIGNAL_SENT;
+ form->skip_zero_click = false;
+ password_store_->AddLogin(*form);
+ }
+
+ metrics_util::LogCountHttpMigratedPasswords(results.size());
+
+ if (consumer_)
+ consumer_->ProcessMigratedForms(std::move(results));
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/http_password_migrator.h b/chromium/components/password_manager/core/browser/http_password_migrator.h
new file mode 100644
index 00000000000..cf054487eef
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/http_password_migrator.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_PASSWORD_MANAGER_CORE_BROWSER_HTTP_PASSWORD_MIGRATOR_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_HTTP_PASSWORD_MIGRATOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+
+namespace autofill {
+struct PasswordForm;
+}
+
+class GURL;
+
+namespace password_manager {
+
+class PasswordStore;
+
+// The class is responsible for migrating the passwords saved on HTTP to HTTPS
+// origin.
+class HttpPasswordMigrator : public PasswordStoreConsumer {
+ public:
+ // API to be implemented by an embedder of HttpPasswordMigrator.
+ class Consumer {
+ public:
+ virtual ~Consumer() = default;
+
+ // Notify the embedder that |forms| were migrated to HTTPS. |forms| contain
+ // the updated HTTPS scheme.
+ virtual void ProcessMigratedForms(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms) = 0;
+ };
+
+ // |https_origin| should specify a valid HTTPS URL.
+ HttpPasswordMigrator(const GURL& https_origin,
+ PasswordStore* password_store,
+ Consumer* consumer);
+ ~HttpPasswordMigrator() override;
+
+ // PasswordStoreConsumer:
+ void OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+
+ private:
+ Consumer* consumer_;
+ PasswordStore* password_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpPasswordMigrator);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_HTTP_PASSWORD_MIGRATOR_H_
diff --git a/chromium/components/password_manager/core/browser/http_password_migrator_unittest.cc b/chromium/components/password_manager/core/browser/http_password_migrator_unittest.cc
new file mode 100644
index 00000000000..e536f10a2fd
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/http_password_migrator_unittest.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/password_manager/core/browser/http_password_migrator.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/password_manager/core/browser/mock_password_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+namespace {
+
+using autofill::PasswordForm;
+using testing::_;
+using testing::ElementsAre;
+using testing::Pointee;
+
+constexpr char kTestHttpsURL[] = "https://example.org/";
+constexpr char kTestHttpURL[] = "http://example.org/";
+constexpr char kTestSubdomainHttpURL[] = "http://login.example.org/";
+
+// Creates a dummy http form with some basic arbitrary values.
+PasswordForm CreateTestForm() {
+ PasswordForm form;
+ form.origin = GURL(kTestHttpURL);
+ form.signon_realm = form.origin.spec();
+ form.action = GURL("https://example.org/action.html");
+ form.username_value = base::ASCIIToUTF16("user");
+ form.password_value = base::ASCIIToUTF16("password");
+ return form;
+}
+
+// Creates a dummy http PSL-matching form with some basic arbitrary values.
+PasswordForm CreateTestPSLForm() {
+ PasswordForm form;
+ form.origin = GURL(kTestSubdomainHttpURL);
+ form.signon_realm = form.origin.spec();
+ form.action = GURL(kTestSubdomainHttpURL);
+ form.username_value = base::ASCIIToUTF16("user2");
+ form.password_value = base::ASCIIToUTF16("password2");
+ form.is_public_suffix_match = true;
+ return form;
+}
+
+// Creates an Android credential.
+PasswordForm CreateAndroidCredential() {
+ PasswordForm form;
+ form.username_value = base::ASCIIToUTF16("user3");
+ form.password_value = base::ASCIIToUTF16("password3");
+ form.signon_realm = "android://hash@com.example.android/";
+ form.origin = GURL(form.signon_realm);
+ form.action = GURL();
+ form.is_affiliation_based_match = true;
+ return form;
+}
+
+class MockConsumer : public HttpPasswordMigrator::Consumer {
+ public:
+ MOCK_METHOD1(ProcessForms,
+ void(const std::vector<autofill::PasswordForm*>& forms));
+
+ void ProcessMigratedForms(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> forms) override {
+ std::vector<autofill::PasswordForm*> raw_forms(forms.size());
+ std::transform(forms.begin(), forms.end(), raw_forms.begin(),
+ [](const std::unique_ptr<autofill::PasswordForm>& form) {
+ return form.get();
+ });
+ ProcessForms(raw_forms);
+ }
+};
+
+class HttpPasswordMigratorTest : public testing::Test {
+ public:
+ HttpPasswordMigratorTest() {
+ mock_store_ = new testing::StrictMock<MockPasswordStore>;
+ }
+
+ ~HttpPasswordMigratorTest() override { mock_store_->ShutdownOnUIThread(); }
+
+ MockConsumer& consumer() { return consumer_; }
+ MockPasswordStore& store() { return *mock_store_; }
+
+ private:
+ base::MessageLoop message_loop_; // Used by mock_store_.
+ MockConsumer consumer_;
+ scoped_refptr<MockPasswordStore> mock_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpPasswordMigratorTest);
+};
+
+TEST_F(HttpPasswordMigratorTest, EmptyStore) {
+ PasswordStore::FormDigest form(autofill::PasswordForm::SCHEME_HTML,
+ kTestHttpURL, GURL(kTestHttpURL));
+ EXPECT_CALL(store(), GetLogins(form, _));
+ HttpPasswordMigrator migrator(GURL(kTestHttpsURL), &store(), &consumer());
+
+ EXPECT_CALL(consumer(), ProcessForms(std::vector<autofill::PasswordForm*>()));
+ migrator.OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>>());
+}
+
+TEST_F(HttpPasswordMigratorTest, FullStore) {
+ PasswordStore::FormDigest form_digest(autofill::PasswordForm::SCHEME_HTML,
+ kTestHttpURL, GURL(kTestHttpURL));
+ EXPECT_CALL(store(), GetLogins(form_digest, _));
+ HttpPasswordMigrator migrator(GURL(kTestHttpsURL), &store(), &consumer());
+
+ PasswordForm form = CreateTestForm();
+ PasswordForm psl_form = CreateTestPSLForm();
+ PasswordForm android_form = CreateAndroidCredential();
+ PasswordForm expected_form = form;
+ expected_form.origin = GURL(kTestHttpsURL);
+ expected_form.signon_realm = expected_form.origin.spec();
+
+ EXPECT_CALL(store(), AddLogin(expected_form));
+ EXPECT_CALL(consumer(), ProcessForms(ElementsAre(Pointee(expected_form))));
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results;
+ results.push_back(base::MakeUnique<PasswordForm>(psl_form));
+ results.push_back(base::MakeUnique<PasswordForm>(form));
+ results.push_back(base::MakeUnique<PasswordForm>(android_form));
+ migrator.OnGetPasswordStoreResults(std::move(results));
+}
+
+} // namespace
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/login_database.cc b/chromium/components/password_manager/core/browser/login_database.cc
index b7950ad3284..ff7d4da2794 100644
--- a/chromium/components/password_manager/core/browser/login_database.cc
+++ b/chromium/components/password_manager/core/browser/login_database.cc
@@ -15,6 +15,7 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
#include "base/numerics/safe_conversions.h"
@@ -910,7 +911,7 @@ bool LoginDatabase::RemoveLogin(const PasswordForm& form) {
bool LoginDatabase::RemoveLoginsCreatedBetween(base::Time delete_begin,
base::Time delete_end) {
#if defined(OS_IOS)
- ScopedVector<autofill::PasswordForm> forms;
+ std::vector<std::unique_ptr<PasswordForm>> forms;
if (GetLoginsCreatedBetween(delete_begin, delete_end, &forms)) {
for (size_t i = 0; i < forms.size(); i++) {
DeleteEncryptedPassword(*forms[i]);
@@ -942,7 +943,7 @@ bool LoginDatabase::RemoveLoginsSyncedBetween(base::Time delete_begin,
}
bool LoginDatabase::GetAutoSignInLogins(
- ScopedVector<autofill::PasswordForm>* forms) const {
+ std::vector<std::unique_ptr<PasswordForm>>* forms) const {
DCHECK(forms);
DCHECK(!autosignin_statement_.empty());
sql::Statement s(
@@ -1035,7 +1036,7 @@ LoginDatabase::EncryptionResult LoginDatabase::InitPasswordFormFromStatement(
bool LoginDatabase::GetLogins(
const PasswordStore::FormDigest& form,
- std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) const {
+ std::vector<std::unique_ptr<PasswordForm>>* forms) const {
DCHECK(forms);
const GURL signon_realm(form.signon_realm);
std::string registered_domain = GetRegistryControlledDomain(signon_realm);
@@ -1085,8 +1086,15 @@ bool LoginDatabase::GetLogins(
std::string regexp = "^(" + scheme + ":\\/\\/)([\\w-]+\\.)*" +
registered_domain + "(:" + port + ")?\\/$";
s.BindString(placeholder++, regexp);
- }
- if (should_federated_apply) {
+
+ if (should_federated_apply) {
+ // This regex matches any subdomain of |registered_domain|, in particular
+ // it matches the empty subdomain. Hence exact domain matches are also
+ // retrieved.
+ s.BindString(placeholder++,
+ "^federation://([\\w-]+\\.)*" + registered_domain + "/.+$");
+ }
+ } else if (should_federated_apply) {
std::string expression =
base::StringPrintf("federation://%s/%%", form.origin.host().c_str());
s.BindString(placeholder++, expression);
@@ -1099,15 +1107,11 @@ bool LoginDatabase::GetLogins(
PSL_DOMAIN_MATCH_COUNT);
}
- ScopedVector<autofill::PasswordForm> forms_scopedvector;
bool success = StatementToForms(
&s, should_PSL_matching_apply || should_federated_apply ? &form : nullptr,
- &forms_scopedvector);
- if (success) {
- *forms = password_manager_util::ConvertScopedVector(
- std::move(forms_scopedvector));
+ forms);
+ if (success)
return true;
- }
forms->clear();
return false;
}
@@ -1115,7 +1119,7 @@ bool LoginDatabase::GetLogins(
bool LoginDatabase::GetLoginsCreatedBetween(
const base::Time begin,
const base::Time end,
- ScopedVector<autofill::PasswordForm>* forms) const {
+ std::vector<std::unique_ptr<PasswordForm>>* forms) const {
DCHECK(forms);
DCHECK(!created_statement_.empty());
sql::Statement s(
@@ -1130,7 +1134,7 @@ bool LoginDatabase::GetLoginsCreatedBetween(
bool LoginDatabase::GetLoginsSyncedBetween(
const base::Time begin,
const base::Time end,
- ScopedVector<autofill::PasswordForm>* forms) const {
+ std::vector<std::unique_ptr<PasswordForm>>* forms) const {
DCHECK(forms);
DCHECK(!synced_statement_.empty());
sql::Statement s(
@@ -1144,12 +1148,12 @@ bool LoginDatabase::GetLoginsSyncedBetween(
}
bool LoginDatabase::GetAutofillableLogins(
- std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) const {
+ std::vector<std::unique_ptr<PasswordForm>>* forms) const {
return GetAllLoginsWithBlacklistSetting(false, forms);
}
bool LoginDatabase::GetBlacklistLogins(
- std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) const {
+ std::vector<std::unique_ptr<PasswordForm>>* forms) const {
return GetAllLoginsWithBlacklistSetting(true, forms);
}
@@ -1162,13 +1166,9 @@ bool LoginDatabase::GetAllLoginsWithBlacklistSetting(
db_.GetCachedStatement(SQL_FROM_HERE, blacklisted_statement_.c_str()));
s.BindInt(0, blacklisted ? 1 : 0);
- ScopedVector<autofill::PasswordForm> forms_scopedvector;
- bool success = StatementToForms(&s, nullptr, &forms_scopedvector);
- if (success) {
- *forms = password_manager_util::ConvertScopedVector(
- std::move(forms_scopedvector));
+ bool success = StatementToForms(&s, nullptr, forms);
+ if (success)
return true;
- }
forms->clear();
return false;
}
@@ -1182,7 +1182,7 @@ bool LoginDatabase::DeleteAndRecreateDatabaseFile() {
}
std::string LoginDatabase::GetEncryptedPassword(
- const autofill::PasswordForm& form) const {
+ const PasswordForm& form) const {
DCHECK(!encrypted_statement_.empty());
sql::Statement s(
db_.GetCachedStatement(SQL_FROM_HERE, encrypted_statement_.c_str()));
@@ -1204,12 +1204,12 @@ std::string LoginDatabase::GetEncryptedPassword(
bool LoginDatabase::StatementToForms(
sql::Statement* statement,
const PasswordStore::FormDigest* matched_form,
- ScopedVector<autofill::PasswordForm>* forms) {
+ std::vector<std::unique_ptr<PasswordForm>>* forms) {
PSLDomainMatchMetric psl_domain_match_metric = PSL_DOMAIN_MATCH_NONE;
forms->clear();
while (statement->Step()) {
- std::unique_ptr<PasswordForm> new_form(new PasswordForm());
+ auto new_form = base::MakeUnique<PasswordForm>();
EncryptionResult result =
InitPasswordFormFromStatement(new_form.get(), *statement);
if (result == ENCRYPTION_RESULT_SERVICE_FAILURE)
@@ -1217,21 +1217,26 @@ bool LoginDatabase::StatementToForms(
if (result == ENCRYPTION_RESULT_ITEM_FAILURE)
continue;
DCHECK_EQ(ENCRYPTION_RESULT_SUCCESS, result);
- if (matched_form && matched_form->signon_realm != new_form->signon_realm) {
- if (new_form->scheme != PasswordForm::SCHEME_HTML)
- continue; // Ignore non-HTML matches.
-
- if (IsPublicSuffixDomainMatch(new_form->signon_realm,
- matched_form->signon_realm)) {
- psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND;
- new_form->is_public_suffix_match = true;
- } else if (!new_form->federation_origin.unique() &&
- IsFederatedMatch(new_form->signon_realm,
- matched_form->origin)) {
- } else {
- continue;
+
+ if (matched_form) {
+ switch (GetMatchResult(*new_form, *matched_form)) {
+ case MatchResult::NO_MATCH:
+ continue;
+ case MatchResult::EXACT_MATCH:
+ break;
+ case MatchResult::PSL_MATCH:
+ psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND;
+ new_form->is_public_suffix_match = true;
+ break;
+ case MatchResult::FEDERATED_MATCH:
+ break;
+ case MatchResult::FEDERATED_PSL_MATCH:
+ psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND_FEDERATED;
+ new_form->is_public_suffix_match = true;
+ break;
}
}
+
forms->push_back(std::move(new_form));
}
@@ -1284,13 +1289,15 @@ void LoginDatabase::InitializeStatementStrings(const SQLTableBuilder& builder) {
std::string psl_statement = "OR signon_realm REGEXP ? ";
std::string federated_statement =
"OR (signon_realm LIKE ? AND password_type == 2) ";
+ std::string psl_federated_statement =
+ "OR (signon_realm REGEXP ? AND password_type == 2) ";
DCHECK(get_statement_psl_.empty());
get_statement_psl_ = get_statement_ + psl_statement;
DCHECK(get_statement_federated_.empty());
get_statement_federated_ = get_statement_ + federated_statement;
DCHECK(get_statement_psl_federated_.empty());
get_statement_psl_federated_ =
- get_statement_ + psl_statement + federated_statement;
+ get_statement_ + psl_statement + psl_federated_statement;
DCHECK(created_statement_.empty());
created_statement_ =
"SELECT " + all_column_names +
diff --git a/chromium/components/password_manager/core/browser/login_database.h b/chromium/components/password_manager/core/browser/login_database.h
index 4be14f0248e..c6cf73dc834 100644
--- a/chromium/components/password_manager/core/browser/login_database.h
+++ b/chromium/components/password_manager/core/browser/login_database.h
@@ -12,7 +12,6 @@
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "base/pickle.h"
#include "base/strings/string16.h"
#include "build/build_config.h"
@@ -98,14 +97,16 @@ class LoginDatabase {
bool GetLoginsCreatedBetween(
base::Time begin,
base::Time end,
- ScopedVector<autofill::PasswordForm>* forms) const WARN_UNUSED_RESULT;
+ std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) const
+ WARN_UNUSED_RESULT;
// Gets all logins synced from |begin| onwards (inclusive) and before |end|.
// You may use a null Time value to do an unbounded search in either
// direction.
- bool GetLoginsSyncedBetween(base::Time begin,
- base::Time end,
- ScopedVector<autofill::PasswordForm>* forms) const
+ bool GetLoginsSyncedBetween(
+ base::Time begin,
+ base::Time end,
+ std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) const
WARN_UNUSED_RESULT;
// Gets the complete list of not blacklisted credentials.
@@ -118,8 +119,8 @@ class LoginDatabase {
forms) const WARN_UNUSED_RESULT;
// Gets the list of auto-sign-inable credentials.
- bool GetAutoSignInLogins(ScopedVector<autofill::PasswordForm>* forms) const
- WARN_UNUSED_RESULT;
+ bool GetAutoSignInLogins(std::vector<std::unique_ptr<autofill::PasswordForm>>*
+ forms) const WARN_UNUSED_RESULT;
// Deletes the login database file on disk, and creates a new, empty database.
// This can be used after migrating passwords to some other store, to ensure
@@ -192,9 +193,10 @@ class LoginDatabase {
// Overwrites |forms| with credentials retrieved from |statement|. If
// |matched_form| is not null, filters out all results but those PSL-matching
// |*matched_form| or federated credentials for it. On success returns true.
- static bool StatementToForms(sql::Statement* statement,
- const PasswordStore::FormDigest* matched_form,
- ScopedVector<autofill::PasswordForm>* forms);
+ static bool StatementToForms(
+ sql::Statement* statement,
+ const PasswordStore::FormDigest* matched_form,
+ std::vector<std::unique_ptr<autofill::PasswordForm>>* forms);
// Initializes all the *_statement_ data members with appropriate SQL
// fragments based on |builder|.
diff --git a/chromium/components/password_manager/core/browser/login_database_ios_unittest.cc b/chromium/components/password_manager/core/browser/login_database_ios_unittest.cc
index b604d673e0e..4ada9cfe88b 100644
--- a/chromium/components/password_manager/core/browser/login_database_ios_unittest.cc
+++ b/chromium/components/password_manager/core/browser/login_database_ios_unittest.cc
@@ -116,7 +116,7 @@ TEST_F(LoginDatabaseIOSTest, UpdateLogin) {
login_db_->UpdateLogin(form);
ASSERT_EQ(1u, changes.size());
- ScopedVector<PasswordForm> forms;
+ std::vector<std::unique_ptr<PasswordForm>> forms;
EXPECT_TRUE(login_db_->GetLogins(PasswordStore::FormDigest(form), &forms));
ASSERT_EQ(1U, forms.size());
@@ -135,7 +135,7 @@ TEST_F(LoginDatabaseIOSTest, RemoveLogin) {
ignore_result(login_db_->RemoveLogin(form));
- ScopedVector<PasswordForm> forms;
+ std::vector<std::unique_ptr<PasswordForm>> forms;
EXPECT_TRUE(login_db_->GetLogins(PasswordStore::FormDigest(form), &forms));
ASSERT_EQ(0U, forms.size());
@@ -171,7 +171,7 @@ TEST_F(LoginDatabaseIOSTest, RemoveLoginsCreatedBetween) {
PasswordStore::FormDigest form = {PasswordForm::SCHEME_HTML,
"http://www.example.com", GURL()};
- ScopedVector<PasswordForm> logins;
+ std::vector<std::unique_ptr<PasswordForm>> logins;
EXPECT_TRUE(login_db_->GetLogins(form, &logins));
ASSERT_EQ(2U, logins.size());
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 757e82e1b90..e93ce3f9cba 100644
--- a/chromium/components/password_manager/core/browser/login_database_unittest.cc
+++ b/chromium/components/password_manager/core/browser/login_database_unittest.cc
@@ -12,7 +12,6 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
-#include "base/memory/scoped_vector.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
@@ -401,7 +400,7 @@ TEST_F(LoginDatabaseTest, TestFederatedMatching) {
form2.action = GURL("https://mobile.foo.com/login");
form2.signon_realm = "federation://mobile.foo.com/accounts.google.com";
form2.username_value = ASCIIToUTF16("test1@gmail.com");
- form2.type = autofill::PasswordForm::TYPE_API;
+ form2.type = PasswordForm::TYPE_API;
form2.federation_origin = url::Origin(GURL("https://accounts.google.com/"));
// Add it and make sure it is there.
@@ -414,13 +413,18 @@ TEST_F(LoginDatabaseTest, TestFederatedMatching) {
PasswordStore::FormDigest form_request = {
PasswordForm::SCHEME_HTML, "https://foo.com/", GURL("https://foo.com/")};
EXPECT_TRUE(db().GetLogins(form_request, &result));
- EXPECT_THAT(result, testing::ElementsAre(Pointee(form)));
+ // Both forms are matched, only form2 is a PSL match.
+ form.is_public_suffix_match = false;
+ form2.is_public_suffix_match = true;
+ EXPECT_THAT(result, UnorderedElementsAre(Pointee(form), Pointee(form2)));
// Match against the mobile site.
form_request.origin = GURL("https://mobile.foo.com/");
form_request.signon_realm = "https://mobile.foo.com/";
EXPECT_TRUE(db().GetLogins(form_request, &result));
+ // Both forms are matched, only form is a PSL match.
form.is_public_suffix_match = true;
+ form2.is_public_suffix_match = false;
EXPECT_THAT(result, UnorderedElementsAre(Pointee(form), Pointee(form2)));
}
@@ -510,7 +514,7 @@ TEST_F(LoginDatabaseTest, TestFederatedMatchingWithoutPSLMatching) {
form2.action = GURL("https://some.other.google.com/login");
form2.signon_realm = "federation://some.other.google.com/accounts.google.com";
form2.username_value = ASCIIToUTF16("test1@gmail.com");
- form2.type = autofill::PasswordForm::TYPE_API;
+ form2.type = PasswordForm::TYPE_API;
form2.federation_origin = url::Origin(GURL("https://accounts.google.com/"));
// Add it and make sure it is there.
@@ -535,6 +539,28 @@ TEST_F(LoginDatabaseTest, TestFederatedMatchingWithoutPSLMatching) {
EXPECT_THAT(result, testing::ElementsAre(Pointee(form2)));
}
+TEST_F(LoginDatabaseTest, TestFederatedPSLMatching) {
+ // Save a federated credential for the PSL matched site.
+ PasswordForm form;
+ form.origin = GURL("https://psl.example.com/");
+ form.action = GURL("https://psl.example.com/login");
+ form.signon_realm = "federation://psl.example.com/accounts.google.com";
+ form.username_value = ASCIIToUTF16("test1@gmail.com");
+ form.type = PasswordForm::TYPE_API;
+ form.federation_origin = url::Origin(GURL("https://accounts.google.com/"));
+ form.scheme = PasswordForm::SCHEME_HTML;
+ EXPECT_EQ(AddChangeForForm(form), db().AddLogin(form));
+
+ // Match against.
+ PasswordStore::FormDigest form_request = {PasswordForm::SCHEME_HTML,
+ "https://example.com/",
+ GURL("https://example.com/login")};
+ std::vector<std::unique_ptr<PasswordForm>> result;
+ EXPECT_TRUE(db().GetLogins(form_request, &result));
+ form.is_public_suffix_match = true;
+ EXPECT_THAT(result, testing::ElementsAre(Pointee(form)));
+}
+
// This test fails if the implementation of GetLogins uses GetCachedStatement
// instead of GetUniqueStatement, since REGEXP is in use. See
// http://crbug.com/248608.
@@ -780,13 +806,10 @@ TEST_F(LoginDatabaseTest, ClearPrivateData_SavedPasswords) {
EXPECT_EQ(4U, result.size());
result.clear();
- // TODO(crbug.com/555132) Replace |result_scopedvector| back with |result|.
- ScopedVector<PasswordForm> result_scopedvector;
// Get everything from today's date and on.
- EXPECT_TRUE(
- db().GetLoginsCreatedBetween(now, base::Time(), &result_scopedvector));
- EXPECT_EQ(2U, result_scopedvector.size());
- result_scopedvector.clear();
+ EXPECT_TRUE(db().GetLoginsCreatedBetween(now, base::Time(), &result));
+ EXPECT_EQ(2U, result.size());
+ result.clear();
// Delete everything from today's date and on.
db().RemoveLoginsCreatedBetween(now, base::Time());
@@ -825,15 +848,12 @@ TEST_F(LoginDatabaseTest, RemoveLoginsSyncedBetween) {
EXPECT_EQ(4U, result.size());
result.clear();
- // TODO(crbug.com/555132) Replace |result_scopedvector| back with |result|.
- ScopedVector<PasswordForm> result_scopedvector;
// Get everything from today's date and on.
- EXPECT_TRUE(
- db().GetLoginsSyncedBetween(now, base::Time(), &result_scopedvector));
- ASSERT_EQ(2U, result_scopedvector.size());
- EXPECT_EQ("http://3.com", result_scopedvector[0]->signon_realm);
- EXPECT_EQ("http://4.com", result_scopedvector[1]->signon_realm);
- result_scopedvector.clear();
+ EXPECT_TRUE(db().GetLoginsSyncedBetween(now, base::Time(), &result));
+ ASSERT_EQ(2U, result.size());
+ EXPECT_EQ("http://3.com", result[0]->signon_realm);
+ EXPECT_EQ("http://4.com", result[1]->signon_realm);
+ result.clear();
// Delete everything from today's date and on.
db().RemoveLoginsSyncedBetween(now, base::Time());
@@ -854,7 +874,7 @@ TEST_F(LoginDatabaseTest, RemoveLoginsSyncedBetween) {
}
TEST_F(LoginDatabaseTest, GetAutoSignInLogins) {
- ScopedVector<PasswordForm> result;
+ std::vector<std::unique_ptr<PasswordForm>> result;
GURL origin("https://example.com");
EXPECT_TRUE(AddZeroClickableLogin(&db(), "foo1", origin));
@@ -864,7 +884,7 @@ TEST_F(LoginDatabaseTest, GetAutoSignInLogins) {
EXPECT_TRUE(db().GetAutoSignInLogins(&result));
EXPECT_EQ(4U, result.size());
- for (const auto* form : result)
+ for (const auto& form : result)
EXPECT_FALSE(form->skip_zero_click);
EXPECT_TRUE(db().DisableAutoSignInForOrigin(origin));
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 ef0c961e6fc..b0e2c3acb0b 100644
--- a/chromium/components/password_manager/core/browser/mock_password_store.cc
+++ b/chromium/components/password_manager/core/browser/mock_password_store.cc
@@ -5,6 +5,7 @@
#include "components/password_manager/core/browser/mock_password_store.h"
#include "base/memory/ptr_util.h"
+#include "base/threading/thread_task_runner_handle.h"
namespace password_manager {
@@ -16,15 +17,4 @@ MockPasswordStore::MockPasswordStore()
MockPasswordStore::~MockPasswordStore() {
}
-std::vector<std::unique_ptr<InteractionsStats>>
-MockPasswordStore::GetSiteStatsImpl(const GURL& origin_domain) {
- std::vector<InteractionsStats*> stats = GetSiteStatsMock(origin_domain);
- std::vector<std::unique_ptr<InteractionsStats>> result;
- result.reserve(stats.size());
- for (auto* stat : stats) {
- result.push_back(base::WrapUnique(stat));
- }
- return result;
-}
-
} // namespace password_manager
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 9975a2cbcb9..845042ec762 100644
--- a/chromium/components/password_manager/core/browser/mock_password_store.h
+++ b/chromium/components/password_manager/core/browser/mock_password_store.h
@@ -59,12 +59,14 @@ class MockPasswordStore : public PasswordStore {
MOCK_METHOD1(FillBlacklistLogins,
bool(std::vector<std::unique_ptr<autofill::PasswordForm>>*));
MOCK_METHOD1(NotifyLoginsChanged, void(const PasswordStoreChangeList&));
- // GMock doesn't allow to return noncopyable types.
- std::vector<std::unique_ptr<InteractionsStats>> GetSiteStatsImpl(
- const GURL& origin_domain) override;
- MOCK_METHOD1(GetSiteStatsMock, std::vector<InteractionsStats*>(const GURL&));
+ MOCK_METHOD1(GetSiteStatsImpl,
+ std::vector<InteractionsStats>(const GURL& origin_domain));
MOCK_METHOD1(AddSiteStatsImpl, void(const InteractionsStats&));
MOCK_METHOD1(RemoveSiteStatsImpl, void(const GURL&));
+ MOCK_METHOD3(CheckReuse,
+ void(const base::string16&,
+ const std::string&,
+ PasswordReuseDetectorConsumer*));
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 de57fcce6e8..970d059ac93 100644
--- a/chromium/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_autofill_manager.cc
@@ -12,6 +12,7 @@
#include "base/command_line.h"
#include "base/i18n/case_conversion.h"
#include "base/logging.h"
+#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
@@ -26,7 +27,7 @@
#include "components/password_manager/core/browser/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/switches.h"
+#include "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
#include "grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
@@ -71,6 +72,7 @@ void AppendSuggestionIfMatching(
const base::string16& field_contents,
const std::string& signon_realm,
bool show_all,
+ bool is_password_field,
std::vector<autofill::Suggestion>* suggestions) {
base::string16 lower_suggestion = base::i18n::ToLower(field_suggestion);
base::string16 lower_contents = base::i18n::ToLower(field_contents);
@@ -82,7 +84,9 @@ void AppendSuggestionIfMatching(
lower_suggestion, lower_contents, true)) {
autofill::Suggestion suggestion(ReplaceEmptyUsername(field_suggestion));
suggestion.label = GetHumanReadableRealm(signon_realm);
- suggestion.frontend_id = autofill::POPUP_ITEM_ID_PASSWORD_ENTRY;
+ suggestion.frontend_id = is_password_field
+ ? autofill::POPUP_ITEM_ID_PASSWORD_ENTRY
+ : autofill::POPUP_ITEM_ID_USERNAME_ENTRY;
suggestion.match = prefix_matched_suggestion
? autofill::Suggestion::PREFIX_MATCH
: autofill::Suggestion::SUBSTRING_MATCH;
@@ -96,19 +100,23 @@ void AppendSuggestionIfMatching(
void GetSuggestions(const autofill::PasswordFormFillData& fill_data,
const base::string16& current_username,
std::vector<autofill::Suggestion>* suggestions,
- bool show_all) {
+ bool show_all,
+ bool is_password_field) {
AppendSuggestionIfMatching(fill_data.username_field.value, current_username,
- fill_data.preferred_realm, show_all, suggestions);
+ fill_data.preferred_realm, show_all,
+ is_password_field, suggestions);
for (const auto& login : fill_data.additional_logins) {
AppendSuggestionIfMatching(login.first, current_username,
- login.second.realm, show_all, suggestions);
+ login.second.realm, show_all, is_password_field,
+ suggestions);
}
for (const auto& usernames : fill_data.other_possible_usernames) {
for (size_t i = 0; i < usernames.second.size(); ++i) {
AppendSuggestionIfMatching(usernames.second[i], current_username,
- usernames.first.realm, show_all, suggestions);
+ usernames.first.realm, show_all,
+ is_password_field, suggestions);
}
}
@@ -193,7 +201,8 @@ void PasswordAutofillManager::OnShowPasswordSuggestions(
return;
}
GetSuggestions(fill_data_it->second, typed_username, &suggestions,
- options & autofill::SHOW_ALL);
+ (options & autofill::SHOW_ALL) != 0,
+ (options & autofill::IS_PASSWORD_FIELD) != 0);
form_data_key_ = key;
@@ -207,34 +216,77 @@ void PasswordAutofillManager::OnShowPasswordSuggestions(
IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE));
password_field_suggestions.frontend_id = autofill::POPUP_ITEM_ID_TITLE;
suggestions.insert(suggestions.begin(), password_field_suggestions);
+ }
- GURL origin = (fill_data_it->second).origin;
-
- bool is_context_secure = autofill_client_->IsContextSecure(origin) &&
- (!origin.is_valid() || !origin.SchemeIs("http"));
- std::string choice =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- security_state::switches::kMarkHttpAs);
- if (!is_context_secure &&
- choice == security_state::switches::
- kMarkHttpWithPasswordsOrCcWithChipAndFormWarning) {
- autofill::Suggestion password_field_http_warning_suggestion(
- l10n_util::GetStringUTF16(
- IDS_AUTOFILL_PASSWORD_HTTP_WARNING_MESSAGE));
- password_field_http_warning_suggestion.frontend_id =
- autofill::POPUP_ITEM_ID_WARNING_MESSAGE;
- suggestions.insert(suggestions.begin(),
- password_field_http_warning_suggestion);
+ GURL origin = (fill_data_it->second).origin;
+ bool is_context_secure = autofill_client_->IsContextSecure() &&
+ (!origin.is_valid() || !origin.SchemeIs("http"));
+ if (!is_context_secure && security_state::IsHttpWarningInFormEnabled()) {
+ std::string icon_str;
+
+ // Show http info icon for http sites.
+ if (origin.is_valid() && origin.SchemeIs("http")) {
+ icon_str = "httpWarning";
+ } else {
+ // Show https_invalid icon for broken https sites.
+ icon_str = "httpsInvalid";
}
+
+ autofill::Suggestion http_warning_suggestion(
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_LOGIN_HTTP_WARNING_MESSAGE),
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_HTTP_WARNING_LEARN_MORE),
+ icon_str, autofill::POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE);
+#if !defined(OS_ANDROID)
+ suggestions.insert(suggestions.begin(), autofill::Suggestion());
+ suggestions.front().frontend_id = autofill::POPUP_ITEM_ID_SEPARATOR;
+#endif
+ suggestions.insert(suggestions.begin(), http_warning_suggestion);
+
+ if (!did_show_form_not_secure_warning_) {
+ did_show_form_not_secure_warning_ = true;
+ metrics_util::LogShowedFormNotSecureWarningOnCurrentNavigation();
+ }
}
+
autofill_client_->ShowAutofillPopup(bounds,
text_direction,
suggestions,
weak_ptr_factory_.GetWeakPtr());
}
+void PasswordAutofillManager::OnShowNotSecureWarning(
+ base::i18n::TextDirection text_direction,
+ const gfx::RectF& bounds) {
+ DCHECK(security_state::IsHttpWarningInFormEnabled());
+ // TODO(estark): Other code paths in this file don't do null checks before
+ // using |autofill_client_|. It seems that these other code paths somehow
+ // short-circuit before dereferencing |autofill_client_| in cases where it's
+ // null; it would be good to understand why/how and make a firm decision about
+ // whether |autofill_client_| is allowed to be null. Ideally we would be able
+ // to get rid of such cases so that we can enable Form-Not-Secure warnings
+ // here in all cases. https://crbug.com/699217
+ if (!autofill_client_)
+ return;
+
+ std::vector<autofill::Suggestion> suggestions;
+ autofill::Suggestion http_warning_suggestion(
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_LOGIN_HTTP_WARNING_MESSAGE),
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_HTTP_WARNING_LEARN_MORE),
+ "httpWarning", autofill::POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE);
+ suggestions.insert(suggestions.begin(), http_warning_suggestion);
+
+ autofill_client_->ShowAutofillPopup(bounds, text_direction, suggestions,
+ weak_ptr_factory_.GetWeakPtr());
+
+ if (did_show_form_not_secure_warning_)
+ return;
+ did_show_form_not_secure_warning_ = true;
+ metrics_util::LogShowedFormNotSecureWarningOnCurrentNavigation();
+}
+
void PasswordAutofillManager::DidNavigateMainFrame() {
login_to_password_info_.clear();
+ did_show_form_not_secure_warning_ = false;
}
bool PasswordAutofillManager::FillSuggestionForTest(
@@ -258,6 +310,8 @@ void PasswordAutofillManager::OnPopupHidden() {
void PasswordAutofillManager::DidSelectSuggestion(const base::string16& value,
int identifier) {
ClearPreviewedForm();
+ if (identifier == autofill::POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE)
+ return;
bool success =
PreviewSuggestion(form_data_key_, GetUsernameFromSuggestion(value));
DCHECK(success);
@@ -266,9 +320,14 @@ void PasswordAutofillManager::DidSelectSuggestion(const base::string16& value,
void PasswordAutofillManager::DidAcceptSuggestion(const base::string16& value,
int identifier,
int position) {
- bool success =
- FillSuggestion(form_data_key_, GetUsernameFromSuggestion(value));
- DCHECK(success);
+ if (identifier == autofill::POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE) {
+ metrics_util::LogShowedHttpNotSecureExplanation();
+ autofill_client_->ShowHttpNotSecureExplanation();
+ } else {
+ bool success =
+ FillSuggestion(form_data_key_, GetUsernameFromSuggestion(value));
+ DCHECK(success);
+ }
autofill_client_->HideAutofillPopup();
}
@@ -291,6 +350,10 @@ void PasswordAutofillManager::ClearPreviewedForm() {
password_manager_driver_->ClearPreviewedForm();
}
+bool PasswordAutofillManager::IsCreditCardPopup() {
+ return false;
+}
+
////////////////////////////////////////////////////////////////////////////////
// PasswordAutofillManager, private:
diff --git a/chromium/components/password_manager/core/browser/password_autofill_manager.h b/chromium/components/password_manager/core/browser/password_autofill_manager.h
index c1075c324df..56a5b5c62ba 100644
--- a/chromium/components/password_manager/core/browser/password_autofill_manager.h
+++ b/chromium/components/password_manager/core/browser/password_autofill_manager.h
@@ -42,6 +42,7 @@ class PasswordAutofillManager : public autofill::AutofillPopupDelegate {
base::string16* body) override;
bool RemoveSuggestion(const base::string16& value, int identifier) override;
void ClearPreviewedForm() override;
+ bool IsCreditCardPopup() override;
// Invoked when a password mapping is added.
void OnAddPasswordFormMapping(
@@ -57,6 +58,12 @@ class PasswordAutofillManager : public autofill::AutofillPopupDelegate {
int options,
const gfx::RectF& bounds);
+ // Handles a request from the renderer to show a popup with a warning
+ // indicating that the form is not secure, used when a password field
+ // is autofilled on a non-secure page load.
+ void OnShowNotSecureWarning(base::i18n::TextDirection text_direction,
+ const gfx::RectF& bounds);
+
// Called when main frame navigates. Not called for in-page navigations.
void DidNavigateMainFrame();
@@ -106,6 +113,10 @@ class PasswordAutofillManager : public autofill::AutofillPopupDelegate {
// The driver that owns |this|.
PasswordManagerDriver* password_manager_driver_;
+ // True if the Form-Not-Secure warning has been shown on the current
+ // navigation. Used for metrics.
+ bool did_show_form_not_secure_warning_ = false;
+
autofill::AutofillClient* autofill_client_; // weak
base::WeakPtrFactory<PasswordAutofillManager> weak_ptr_factory_;
diff --git a/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index 1798226a8e8..80926358ce2 100644
--- a/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -6,8 +6,12 @@
#include "base/command_line.h"
#include "base/compiler_specific.h"
+#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/user_action_tester.h"
#include "components/autofill/core/browser/popup_item_ids.h"
#include "components/autofill/core/browser/suggestion_test_helpers.h"
#include "components/autofill/core/browser/test_autofill_client.h"
@@ -18,7 +22,7 @@
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "components/password_manager/core/browser/stub_password_manager_driver.h"
-#include "components/security_state/core/switches.h"
+#include "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -71,6 +75,7 @@ class MockAutofillClient : public autofill::TestAutofillClient {
const std::vector<Suggestion>& suggestions,
base::WeakPtr<autofill::AutofillPopupDelegate> delegate));
MOCK_METHOD0(HideAutofillPopup, void());
+ MOCK_METHOD0(ShowHttpNotSecureExplanation, void());
};
} // namespace
@@ -108,6 +113,11 @@ class PasswordAutofillManagerTest : public testing::Test {
int fill_data_id() { return fill_data_id_; }
autofill::PasswordFormFillData& fill_data() { return fill_data_; }
+ void SetHttpWarningEnabled() {
+ scoped_feature_list_.InitAndEnableFeature(
+ security_state::kHttpFormWarningFeature);
+ }
+
std::unique_ptr<PasswordAutofillManager> password_autofill_manager_;
base::string16 test_username_;
@@ -116,6 +126,7 @@ class PasswordAutofillManagerTest : public testing::Test {
private:
autofill::PasswordFormFillData fill_data_;
const int fill_data_id_;
+ base::test::ScopedFeatureList scoped_feature_list_;
// The TestAutofillDriver uses a SequencedWorkerPool which expects the
// existence of a MessageLoop.
@@ -175,38 +186,48 @@ TEST_F(PasswordAutofillManagerTest, PreviewSuggestion) {
// Test that the popup is marked as visible after recieving password
// suggestions.
TEST_F(PasswordAutofillManagerTest, ExternalDelegatePasswordSuggestions) {
- std::unique_ptr<TestPasswordManagerClient> client(
- new TestPasswordManagerClient);
- std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
- InitializePasswordAutofillManager(client.get(), autofill_client.get());
-
- gfx::RectF element_bounds;
- autofill::PasswordFormFillData data;
- data.username_field.value = test_username_;
- data.password_field.value = test_password_;
- data.preferred_realm = "http://foo.com/";
- int dummy_key = 0;
- password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
-
- EXPECT_CALL(*client->mock_driver(),
- FillSuggestion(test_username_, test_password_));
-
- // The enums must be cast to ints to prevent compile errors on linux_rel.
- EXPECT_CALL(*autofill_client,
- ShowAutofillPopup(
- _,
- _,
- SuggestionVectorIdsAre(testing::ElementsAre(
- autofill::POPUP_ITEM_ID_PASSWORD_ENTRY)),
- _));
- password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(), false,
- element_bounds);
-
- // Accepting a suggestion should trigger a call to hide the popup.
- EXPECT_CALL(*autofill_client, HideAutofillPopup());
- password_autofill_manager_->DidAcceptSuggestion(
- test_username_, autofill::POPUP_ITEM_ID_PASSWORD_ENTRY, 1);
+ for (bool is_suggestion_on_password_field : {false, true}) {
+ std::unique_ptr<TestPasswordManagerClient> client(
+ new TestPasswordManagerClient);
+ std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
+ InitializePasswordAutofillManager(client.get(), autofill_client.get());
+
+ gfx::RectF element_bounds;
+ autofill::PasswordFormFillData data;
+ data.username_field.value = test_username_;
+ data.password_field.value = test_password_;
+ data.preferred_realm = "http://foo.com/";
+ int dummy_key = 0;
+ password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
+
+ EXPECT_CALL(*client->mock_driver(),
+ FillSuggestion(test_username_, test_password_));
+
+ // The enums must be cast to ints to prevent compile errors on linux_rel.
+ auto suggestion_ids_matcher =
+ is_suggestion_on_password_field
+ ? SuggestionVectorIdsAre(
+ testing::ElementsAre(autofill::POPUP_ITEM_ID_TITLE,
+ autofill::POPUP_ITEM_ID_PASSWORD_ENTRY))
+ : SuggestionVectorIdsAre(
+ testing::ElementsAre(autofill::POPUP_ITEM_ID_USERNAME_ENTRY));
+ EXPECT_CALL(*autofill_client,
+ ShowAutofillPopup(_, _, suggestion_ids_matcher, _));
+
+ int show_suggestion_options =
+ is_suggestion_on_password_field ? autofill::IS_PASSWORD_FIELD : 0;
+ password_autofill_manager_->OnShowPasswordSuggestions(
+ dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(),
+ show_suggestion_options, element_bounds);
+
+ // Accepting a suggestion should trigger a call to hide the popup.
+ EXPECT_CALL(*autofill_client, HideAutofillPopup());
+ password_autofill_manager_->DidAcceptSuggestion(
+ test_username_, is_suggestion_on_password_field
+ ? autofill::POPUP_ITEM_ID_PASSWORD_ENTRY
+ : autofill::POPUP_ITEM_ID_USERNAME_ENTRY,
+ 1);
+ }
}
// Test that OnShowPasswordSuggestions correctly matches the given FormFieldData
@@ -578,6 +599,47 @@ TEST_F(PasswordAutofillManagerTest, PreviewAndFillEmptyUsernameSuggestion) {
testing::Mock::VerifyAndClearExpectations(client->mock_driver());
}
+// Tests that a standalone Form Not Secure warning shows up in the
+// autofill popup when PasswordAutofillManager::OnShowNotSecureWarning()
+// is called.
+TEST_F(PasswordAutofillManagerTest, ShowStandaloneNotSecureWarning) {
+ auto client = base::MakeUnique<TestPasswordManagerClient>();
+ auto autofill_client = base::MakeUnique<MockAutofillClient>();
+ InitializePasswordAutofillManager(client.get(), autofill_client.get());
+
+ gfx::RectF element_bounds;
+ autofill::PasswordFormFillData data;
+ data.username_field.value = test_username_;
+ data.password_field.value = test_password_;
+ data.origin = GURL("http://foo.test");
+
+ int dummy_key = 0;
+ password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
+
+ // String "Login not secure" shown as a warning messages if password form is
+ // on http sites.
+ const base::string16 warning_message =
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_LOGIN_HTTP_WARNING_MESSAGE);
+
+ SetHttpWarningEnabled();
+
+ auto elements = testing::ElementsAre(warning_message);
+
+ EXPECT_CALL(*autofill_client,
+ ShowAutofillPopup(element_bounds, _,
+ SuggestionVectorValuesAre(elements), _));
+ password_autofill_manager_->OnShowNotSecureWarning(base::i18n::RIGHT_TO_LEFT,
+ element_bounds);
+
+ // Accepting the warning message should trigger a call to open an explanation
+ // of the message and hide the popup.
+ EXPECT_CALL(*autofill_client, ShowHttpNotSecureExplanation());
+ EXPECT_CALL(*autofill_client, HideAutofillPopup());
+ password_autofill_manager_->DidAcceptSuggestion(
+ base::string16(), autofill::POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE,
+ 0);
+}
+
TEST_F(PasswordAutofillManagerTest, NonSecurePasswordFieldHttpWarningMessage) {
auto client = base::MakeUnique<TestPasswordManagerClient>();
auto autofill_client = base::MakeUnique<MockAutofillClient>();
@@ -595,7 +657,7 @@ TEST_F(PasswordAutofillManagerTest, NonSecurePasswordFieldHttpWarningMessage) {
// String "Login not secure" shown as a warning messages if password form is
// on http sites.
base::string16 warning_message =
- l10n_util::GetStringUTF16(IDS_AUTOFILL_PASSWORD_HTTP_WARNING_MESSAGE);
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_LOGIN_HTTP_WARNING_MESSAGE);
// String "Use password for:" shown when displaying suggestions matching a
// username and specifying that the field is a password field.
@@ -612,22 +674,87 @@ TEST_F(PasswordAutofillManagerTest, NonSecurePasswordFieldHttpWarningMessage) {
dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_,
autofill::IS_PASSWORD_FIELD, element_bounds);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- security_state::switches::kMarkHttpAs,
- security_state::switches::
- kMarkHttpWithPasswordsOrCcWithChipAndFormWarning);
+ SetHttpWarningEnabled();
// Http warning message shows for non-secure context and switch flag on, so
- // there are 3 suggestions in total, and the message comes first among
- // suggestions.
+ // there are 3 suggestions (+ 1 separator on desktop) in total, and the
+ // message comes first among suggestions.
+ auto elements = testing::ElementsAre(warning_message,
+#if !defined(OS_ANDROID)
+ base::string16(),
+#endif
+ title, test_username_);
+
EXPECT_CALL(*autofill_client,
ShowAutofillPopup(element_bounds, _,
- SuggestionVectorValuesAre(testing::ElementsAre(
- warning_message, title, test_username_)),
- _));
+ SuggestionVectorValuesAre(elements), _));
password_autofill_manager_->OnShowPasswordSuggestions(
dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_,
autofill::IS_PASSWORD_FIELD, element_bounds);
+
+ // Accepting the warning message should trigger a call to open an explanation
+ // of the message and hide the popup.
+ EXPECT_CALL(*autofill_client, ShowHttpNotSecureExplanation());
+ EXPECT_CALL(*autofill_client, HideAutofillPopup());
+ password_autofill_manager_->DidAcceptSuggestion(
+ base::string16(), autofill::POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE,
+ 0);
+}
+
+// Tests that the "Login not secure" warning shows up in non-password
+// fields of login forms.
+TEST_F(PasswordAutofillManagerTest, NonSecureUsernameFieldHttpWarningMessage) {
+ auto client = base::MakeUnique<TestPasswordManagerClient>();
+ auto autofill_client = base::MakeUnique<MockAutofillClient>();
+ InitializePasswordAutofillManager(client.get(), autofill_client.get());
+
+ gfx::RectF element_bounds;
+ autofill::PasswordFormFillData data;
+ data.username_field.value = test_username_;
+ data.password_field.value = test_password_;
+ data.origin = GURL("http://foo.test");
+
+ int dummy_key = 0;
+ password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
+
+ // String "Login not secure" shown as a warning messages if password form is
+ // on http sites.
+ base::string16 warning_message =
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_LOGIN_HTTP_WARNING_MESSAGE);
+
+ // Http warning message won't show with switch flag off.
+ EXPECT_CALL(
+ *autofill_client,
+ ShowAutofillPopup(
+ element_bounds, _,
+ SuggestionVectorValuesAre(testing::ElementsAre(test_username_)), _));
+ password_autofill_manager_->OnShowPasswordSuggestions(
+ dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_, 0, element_bounds);
+
+ SetHttpWarningEnabled();
+
+ // Http warning message shows for non-secure context and switch flag on, so
+ // there are 2 suggestions (+ 1 separator on desktop) in total, and the
+ // message comes first among suggestions.
+ auto elements = testing::ElementsAre(warning_message,
+#if !defined(OS_ANDROID)
+ base::string16(),
+#endif
+ test_username_);
+
+ EXPECT_CALL(*autofill_client,
+ ShowAutofillPopup(element_bounds, _,
+ SuggestionVectorValuesAre(elements), _));
+ password_autofill_manager_->OnShowPasswordSuggestions(
+ dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_, 0, element_bounds);
+
+ // Accepting the warning message should trigger a call to open an explanation
+ // of the message and hide the popup.
+ EXPECT_CALL(*autofill_client, ShowHttpNotSecureExplanation());
+ EXPECT_CALL(*autofill_client, HideAutofillPopup());
+ password_autofill_manager_->DidAcceptSuggestion(
+ base::string16(), autofill::POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE,
+ 0);
}
TEST_F(PasswordAutofillManagerTest, SecurePasswordFieldHttpWarningMessage) {
@@ -659,10 +786,7 @@ TEST_F(PasswordAutofillManagerTest, SecurePasswordFieldHttpWarningMessage) {
dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_,
autofill::IS_PASSWORD_FIELD, element_bounds);
- base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- security_state::switches::kMarkHttpAs,
- security_state::switches::
- kMarkHttpWithPasswordsOrCcWithChipAndFormWarning);
+ SetHttpWarningEnabled();
// Http warning message won't show for secure context, even with switch flag
// on.
@@ -676,4 +800,62 @@ TEST_F(PasswordAutofillManagerTest, SecurePasswordFieldHttpWarningMessage) {
autofill::IS_PASSWORD_FIELD, element_bounds);
}
+// Test that a user action is logged when the user selects the Form-Not-Secure
+// warning to receive more information about the warning.
+TEST_F(PasswordAutofillManagerTest, FormNotSecureUserAction) {
+ std::unique_ptr<TestPasswordManagerClient> client(
+ new TestPasswordManagerClient);
+ std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
+ InitializePasswordAutofillManager(client.get(), autofill_client.get());
+ base::UserActionTester user_action_tester;
+ password_autofill_manager_->DidAcceptSuggestion(
+ test_username_, autofill::POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE,
+ 0);
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "PasswordManager_ShowedHttpNotSecureExplanation"));
+}
+
+// Tests that the Form-Not-Secure warning is recorded in UMA, at most once per
+// navigation.
+TEST_F(PasswordAutofillManagerTest, ShowedFormNotSecureHistogram) {
+ const char kHistogram[] =
+ "PasswordManager.ShowedFormNotSecureWarningOnCurrentNavigation";
+ base::HistogramTester histograms;
+ SetHttpWarningEnabled();
+
+ auto client = base::MakeUnique<TestPasswordManagerClient>();
+ auto autofill_client = base::MakeUnique<MockAutofillClient>();
+ InitializePasswordAutofillManager(client.get(), autofill_client.get());
+
+ // Test that the standalone warning (with no autofill suggestions) records the
+ // histogram.
+ gfx::RectF element_bounds;
+ password_autofill_manager_->OnShowNotSecureWarning(base::i18n::RIGHT_TO_LEFT,
+ element_bounds);
+ histograms.ExpectUniqueSample(kHistogram, true, 1);
+
+ // Simulate a navigation, because the histogram is recorded at most once per
+ // navigation.
+ password_autofill_manager_->DidNavigateMainFrame();
+
+ // Test that the warning, when included with the normal autofill dropdown,
+ // records the histogram.
+ int dummy_key = 0;
+ autofill::PasswordFormFillData data;
+ data.username_field.value = test_username_;
+ data.password_field.value = test_password_;
+ data.origin = GURL("http://foo.test");
+ password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
+
+ password_autofill_manager_->OnShowPasswordSuggestions(
+ dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_,
+ autofill::IS_PASSWORD_FIELD, element_bounds);
+ histograms.ExpectUniqueSample(kHistogram, true, 2);
+
+ // The histogram should not be recorded again on the same navigation.
+ password_autofill_manager_->OnShowNotSecureWarning(base::i18n::RIGHT_TO_LEFT,
+ element_bounds);
+ histograms.ExpectUniqueSample(kHistogram, true, 2);
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_bubble_experiment.cc b/chromium/components/password_manager/core/browser/password_bubble_experiment.cc
index a0e5c88d13b..ff0b3ef493a 100644
--- a/chromium/components/password_manager/core/browser/password_bubble_experiment.cc
+++ b/chromium/components/password_manager/core/browser/password_bubble_experiment.cc
@@ -18,20 +18,13 @@
namespace password_bubble_experiment {
-const char kBrandingExperimentName[] = "PasswordBranding";
const char kChromeSignInPasswordPromoExperimentName[] = "SignInPasswordPromo";
const char kChromeSignInPasswordPromoThresholdParam[] = "dismissal_threshold";
const char kSmartBubbleExperimentName[] = "PasswordSmartBubble";
const char kSmartBubbleThresholdParam[] = "dismissal_count";
-const char kSmartLockBrandingGroupName[] = "SmartLockBranding";
-const char kSmartLockBrandingSavePromptOnlyGroupName[] =
- "SmartLockBrandingSavePromptOnly";
void RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(
- password_manager::prefs::kWasSavePrompFirstRunExperienceShown, false);
-
- registry->RegisterBooleanPref(
password_manager::prefs::kWasAutoSignInFirstRunExperienceShown, false,
user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
@@ -55,40 +48,6 @@ bool IsSmartLockUser(const syncer::SyncService* sync_service) {
password_manager::SYNCING_NORMAL_ENCRYPTION;
}
-SmartLockBranding GetSmartLockBrandingState(
- const syncer::SyncService* sync_service) {
- // Query the group first for correct UMA reporting.
- std::string group_name =
- base::FieldTrialList::FindFullName(kBrandingExperimentName);
- if (!IsSmartLockUser(sync_service))
- return SmartLockBranding::NONE;
- if (group_name == kSmartLockBrandingGroupName)
- return SmartLockBranding::FULL;
- if (group_name == kSmartLockBrandingSavePromptOnlyGroupName)
- return SmartLockBranding::SAVE_PROMPT_ONLY;
- return SmartLockBranding::NONE;
-}
-
-bool IsSmartLockBrandingEnabled(const syncer::SyncService* sync_service) {
- return GetSmartLockBrandingState(sync_service) == SmartLockBranding::FULL;
-}
-
-bool IsSmartLockBrandingSavePromptEnabled(
- const syncer::SyncService* sync_service) {
- return GetSmartLockBrandingState(sync_service) != SmartLockBranding::NONE;
-}
-
-bool ShouldShowSavePromptFirstRunExperience(
- const syncer::SyncService* sync_service,
- PrefService* prefs) {
- return false;
-}
-
-void RecordSavePromptFirstRunExperienceWasShown(PrefService* prefs) {
- prefs->SetBoolean(
- password_manager::prefs::kWasSavePrompFirstRunExperienceShown, true);
-}
-
bool ShouldShowAutoSignInPromptFirstRunExperience(PrefService* prefs) {
return !prefs->GetBoolean(
password_manager::prefs::kWasAutoSignInFirstRunExperienceShown);
diff --git a/chromium/components/password_manager/core/browser/password_bubble_experiment.h b/chromium/components/password_manager/core/browser/password_bubble_experiment.h
index cfb1ac6d3a5..7d900a7b95a 100644
--- a/chromium/components/password_manager/core/browser/password_bubble_experiment.h
+++ b/chromium/components/password_manager/core/browser/password_bubble_experiment.h
@@ -14,13 +14,10 @@ class SyncService;
namespace password_bubble_experiment {
-extern const char kBrandingExperimentName[];
extern const char kChromeSignInPasswordPromoExperimentName[];
extern const char kChromeSignInPasswordPromoThresholdParam[];
extern const char kSmartBubbleExperimentName[];
extern const char kSmartBubbleThresholdParam[];
-extern const char kSmartLockBrandingGroupName[];
-extern const char kSmartLockBrandingSavePromptOnlyGroupName[];
// Registers prefs which controls appearance of the first run experience for the
// Smart Lock UI, namely was first run experience shown for save prompt or auto
@@ -34,35 +31,6 @@ int GetSmartBubbleDismissalThreshold();
// A Smart Lock user is a sync user without a custom passphrase.
bool IsSmartLockUser(const syncer::SyncService* sync_service);
-enum class SmartLockBranding { NONE, FULL, SAVE_PROMPT_ONLY };
-
-// If the user is not a Smart Lock user, returns NONE. For Smart Lock users:
-// * returns NONE if the password manager should not be referred to as Smart
-// Lock anywhere;
-// * returns FULL, if it should be referred to as Smart Lock everywhere;
-// * returns SAVE_PROMPT_ONLY if it only should be referred to as Smart Lock in
-// the save password bubble.
-SmartLockBranding GetSmartLockBrandingState(
- const syncer::SyncService* sync_service);
-
-// Convenience function for checking whether the result of
-// GetSmartLockBrandingState is SmartLockBranding::FULL.
-bool IsSmartLockBrandingEnabled(const syncer::SyncService* sync_service);
-
-// Convenience function for checking whether the result of
-// GetSmartLockBrandingState is not equal to SmartLockBranding::NONE.
-bool IsSmartLockBrandingSavePromptEnabled(
- const syncer::SyncService* sync_service);
-
-// Returns true if save prompt should contain first run experience.
-bool ShouldShowSavePromptFirstRunExperience(
- const syncer::SyncService* sync_service,
- PrefService* prefs);
-
-// Sets appropriate value to the preference which controls appearance of the
-// first run experience for the save prompt.
-void RecordSavePromptFirstRunExperienceWasShown(PrefService* prefs);
-
// Returns true if first run experience for auto sign-in prompt should be shown.
bool ShouldShowAutoSignInPromptFirstRunExperience(PrefService* prefs);
diff --git a/chromium/components/password_manager/core/browser/password_bubble_experiment_unittest.cc b/chromium/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
index 4f4baa8cd4f..b9ba0531cee 100644
--- a/chromium/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
@@ -12,6 +12,7 @@
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
+#include "components/sync/base/model_type.h"
#include "components/sync/driver/fake_sync_service.h"
#include "components/variations/variations_associated_data.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -21,50 +22,8 @@ namespace password_bubble_experiment {
namespace {
-const char kSmartLockNoBrandingGroupName[] = "NoSmartLockBranding";
-
enum class CustomPassphraseState { NONE, SET };
-enum class SavePromptFirstRunExperience { NONE, PRESENT };
-
-enum class UserType { SMARTLOCK, NOT_SMARTLOCK };
-
-struct IsSmartLockBrandingEnabledTestcase {
- CustomPassphraseState passphrase_state;
- syncer::ModelType type;
- SmartLockBranding expected_branding;
- UserType expected_user_type;
-};
-
-std::ostream& operator<<(std::ostream& os,
- const IsSmartLockBrandingEnabledTestcase& testcase) {
- os << (testcase.passphrase_state == CustomPassphraseState::SET ? "{SET, "
- : "{NONE, ");
- os << (testcase.type == syncer::PASSWORDS ? "syncer::PASSWORDS, "
- : "not syncer::PASSWORDS, ");
- switch (testcase.expected_branding) {
- case SmartLockBranding::NONE:
- os << "NONE, ";
- break;
- case SmartLockBranding::FULL:
- os << "FULL, ";
- break;
- case SmartLockBranding::SAVE_PROMPT_ONLY:
- os << "SAVE_PROMPT_ONLY, ";
- break;
- }
- os << (testcase.expected_user_type == UserType::SMARTLOCK ? "SMARTLOCK}"
- : "NOT_SMARTLOCK}");
- return os;
-}
-
-struct ShouldShowSavePromptFirstRunExperienceTestcase {
- CustomPassphraseState passphrase_state;
- syncer::ModelType type;
- bool pref_value;
- SavePromptFirstRunExperience first_run_experience;
-};
-
class TestSyncService : public syncer::FakeSyncService {
public:
// FakeSyncService overrides.
@@ -128,39 +87,9 @@ class PasswordManagerPasswordBubbleExperimentTest : public testing::Test {
PrefService* prefs() { return &pref_service_; }
- void EnforceExperimentGroup(const char* group_name) {
- ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(kBrandingExperimentName,
- group_name));
- }
-
TestSyncService* sync_service() { return &fake_sync_service_; }
- void TestIsSmartLockBrandingEnabledTestcase(
- const IsSmartLockBrandingEnabledTestcase& test_case) {
- SetupFakeSyncServiceForTestCase(test_case.type, test_case.passphrase_state);
- EXPECT_EQ(test_case.expected_branding,
- GetSmartLockBrandingState(sync_service()));
- EXPECT_EQ(test_case.expected_user_type == UserType::SMARTLOCK,
- IsSmartLockUser(sync_service()));
- }
-
- void TestShouldShowSavePromptFirstRunExperienceTestcase(
- const ShouldShowSavePromptFirstRunExperienceTestcase& test_case) {
- SetupFakeSyncServiceForTestCase(test_case.type, test_case.passphrase_state);
- prefs()->SetBoolean(
- password_manager::prefs::kWasSavePrompFirstRunExperienceShown,
- test_case.pref_value);
- bool should_show_first_run_experience =
- ShouldShowSavePromptFirstRunExperience(sync_service(), prefs());
- if (test_case.first_run_experience ==
- SavePromptFirstRunExperience::PRESENT) {
- EXPECT_FALSE(should_show_first_run_experience);
- } else {
- EXPECT_FALSE(should_show_first_run_experience);
- }
- }
-
- private:
+ protected:
void SetupFakeSyncServiceForTestCase(syncer::ModelType type,
CustomPassphraseState passphrase_state) {
syncer::ModelTypeSet active_types;
@@ -171,164 +100,17 @@ class PasswordManagerPasswordBubbleExperimentTest : public testing::Test {
passphrase_state == CustomPassphraseState::SET);
}
+ private:
TestSyncService fake_sync_service_;
base::FieldTrialList field_trial_list_;
TestingPrefServiceSimple pref_service_;
};
TEST_F(PasswordManagerPasswordBubbleExperimentTest,
- IsSmartLockBrandingEnabledTestNoBranding) {
- const IsSmartLockBrandingEnabledTestcase kTestData[] = {
- {CustomPassphraseState::SET, syncer::PASSWORDS, SmartLockBranding::NONE,
- UserType::NOT_SMARTLOCK},
- {CustomPassphraseState::SET, syncer::BOOKMARKS, SmartLockBranding::NONE,
- UserType::NOT_SMARTLOCK},
- {CustomPassphraseState::NONE, syncer::PASSWORDS, SmartLockBranding::NONE,
- UserType::SMARTLOCK},
- {CustomPassphraseState::NONE, syncer::BOOKMARKS, SmartLockBranding::NONE,
- UserType::NOT_SMARTLOCK},
- };
-
- EnforceExperimentGroup(kSmartLockNoBrandingGroupName);
- for (const auto& test_case : kTestData) {
- SCOPED_TRACE(testing::Message("test_case = ") << test_case);
- TestIsSmartLockBrandingEnabledTestcase(test_case);
- }
-}
-
-TEST_F(PasswordManagerPasswordBubbleExperimentTest,
- IsSmartLockBrandingEnabledTest_FULL) {
- const IsSmartLockBrandingEnabledTestcase kTestData[] = {
- {CustomPassphraseState::SET, syncer::PASSWORDS, SmartLockBranding::NONE,
- UserType::NOT_SMARTLOCK},
- {CustomPassphraseState::SET, syncer::BOOKMARKS, SmartLockBranding::NONE,
- UserType::NOT_SMARTLOCK},
- {CustomPassphraseState::NONE, syncer::PASSWORDS, SmartLockBranding::FULL,
- UserType::SMARTLOCK},
- {CustomPassphraseState::NONE, syncer::BOOKMARKS, SmartLockBranding::NONE,
- UserType::NOT_SMARTLOCK},
- };
-
- EnforceExperimentGroup(kSmartLockBrandingGroupName);
- for (const auto& test_case : kTestData) {
- SCOPED_TRACE(testing::Message("test_case = ") << test_case);
- TestIsSmartLockBrandingEnabledTestcase(test_case);
- }
-}
-
-TEST_F(PasswordManagerPasswordBubbleExperimentTest,
- IsSmartLockBrandingEnabledTest_SAVE_PROMPT_ONLY) {
- const IsSmartLockBrandingEnabledTestcase kTestData[] = {
- {CustomPassphraseState::SET, syncer::PASSWORDS, SmartLockBranding::NONE,
- UserType::NOT_SMARTLOCK},
- {CustomPassphraseState::SET, syncer::BOOKMARKS, SmartLockBranding::NONE,
- UserType::NOT_SMARTLOCK},
- {CustomPassphraseState::NONE, syncer::PASSWORDS,
- SmartLockBranding::SAVE_PROMPT_ONLY, UserType::SMARTLOCK},
- {CustomPassphraseState::NONE, syncer::BOOKMARKS, SmartLockBranding::NONE,
- UserType::NOT_SMARTLOCK},
- };
-
- EnforceExperimentGroup(kSmartLockBrandingSavePromptOnlyGroupName);
- for (const auto& test_case : kTestData) {
- SCOPED_TRACE(testing::Message("test_case = ") << test_case);
- TestIsSmartLockBrandingEnabledTestcase(test_case);
- }
-}
-
-TEST_F(PasswordManagerPasswordBubbleExperimentTest,
- ShoulShowSavePrompBrandingGroup) {
- const struct ShouldShowSavePromptFirstRunExperienceTestcase kTestData[] = {
- {CustomPassphraseState::SET, syncer::PASSWORDS, true,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::SET, syncer::PASSWORDS, false,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::SET, syncer::BOOKMARKS, true,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::SET, syncer::BOOKMARKS, false,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::NONE, syncer::PASSWORDS, true,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::NONE, syncer::PASSWORDS, false,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::NONE, syncer::BOOKMARKS, true,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::NONE, syncer::BOOKMARKS, false,
- SavePromptFirstRunExperience::NONE},
- };
-
- EnforceExperimentGroup(kSmartLockBrandingGroupName);
- for (const auto& test_case : kTestData) {
- TestShouldShowSavePromptFirstRunExperienceTestcase(test_case);
- }
-}
-
-TEST_F(PasswordManagerPasswordBubbleExperimentTest,
- ShoulShowSavePrompNoBrandingGroup) {
- const struct ShouldShowSavePromptFirstRunExperienceTestcase kTestData[] = {
- {CustomPassphraseState::SET, syncer::PASSWORDS, true,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::SET, syncer::PASSWORDS, false,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::SET, syncer::BOOKMARKS, true,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::SET, syncer::BOOKMARKS, false,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::NONE, syncer::PASSWORDS, true,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::NONE, syncer::PASSWORDS, false,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::NONE, syncer::BOOKMARKS, true,
- SavePromptFirstRunExperience::NONE},
- {CustomPassphraseState::NONE, syncer::BOOKMARKS, false,
- SavePromptFirstRunExperience::NONE},
- };
-
- EnforceExperimentGroup(kSmartLockNoBrandingGroupName);
- for (const auto& test_case : kTestData) {
- TestShouldShowSavePromptFirstRunExperienceTestcase(test_case);
- }
-}
-
-TEST_F(PasswordManagerPasswordBubbleExperimentTest,
- RecordFirstRunExperienceWasShownTest) {
- const struct {
- bool initial_pref_value;
- bool result_pref_value;
- } kTestData[] = {
- {false, true}, {true, true},
- };
- for (const auto& test_case : kTestData) {
- // Record Save prompt first run experience.
- prefs()->SetBoolean(
- password_manager::prefs::kWasSavePrompFirstRunExperienceShown,
- test_case.initial_pref_value);
- RecordSavePromptFirstRunExperienceWasShown(prefs());
- EXPECT_EQ(
- test_case.result_pref_value,
- prefs()->GetBoolean(
- password_manager::prefs::kWasSavePrompFirstRunExperienceShown));
- // Record Auto sign-in first run experience.
- prefs()->SetBoolean(
- password_manager::prefs::kWasAutoSignInFirstRunExperienceShown,
- test_case.initial_pref_value);
- EXPECT_EQ(!test_case.initial_pref_value,
- ShouldShowAutoSignInPromptFirstRunExperience(prefs()));
- RecordAutoSignInPromptFirstRunExperienceWasShown(prefs());
- EXPECT_EQ(
- test_case.result_pref_value,
- prefs()->GetBoolean(
- password_manager::prefs::kWasAutoSignInFirstRunExperienceShown));
- EXPECT_EQ(!test_case.result_pref_value,
- ShouldShowAutoSignInPromptFirstRunExperience(prefs()));
- }
-}
-
-TEST_F(PasswordManagerPasswordBubbleExperimentTest,
ShouldShowChromeSignInPasswordPromo) {
// By default the promo is off.
EXPECT_FALSE(ShouldShowChromeSignInPasswordPromo(prefs(), nullptr));
- const struct {
+ constexpr struct {
bool was_already_clicked;
bool is_sync_allowed;
bool is_first_setup_complete;
@@ -362,4 +144,24 @@ TEST_F(PasswordManagerPasswordBubbleExperimentTest,
}
}
+TEST_F(PasswordManagerPasswordBubbleExperimentTest, IsSmartLockUser) {
+ constexpr struct {
+ syncer::ModelType type;
+ CustomPassphraseState passphrase_state;
+ bool expected_smart_lock_user;
+ } kTestData[] = {
+ {syncer::ModelType::BOOKMARKS, CustomPassphraseState::NONE, false},
+ {syncer::ModelType::BOOKMARKS, CustomPassphraseState::SET, false},
+ {syncer::ModelType::PASSWORDS, CustomPassphraseState::NONE, true},
+ {syncer::ModelType::PASSWORDS, CustomPassphraseState::SET, false},
+ };
+ for (const auto& test_case : kTestData) {
+ SCOPED_TRACE(testing::Message("#test_case = ") << (&test_case - kTestData));
+ SetupFakeSyncServiceForTestCase(test_case.type, test_case.passphrase_state);
+
+ EXPECT_EQ(test_case.expected_smart_lock_user,
+ IsSmartLockUser(sync_service()));
+ }
+}
+
} // namespace password_bubble_experiment
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 e2837adf0db..f6670796b0c 100644
--- a/chromium/components/password_manager/core/browser/password_form_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_form_manager.cc
@@ -18,7 +18,6 @@
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
-#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/proto/server.pb.h"
#include "components/autofill/core/browser/validation.h"
@@ -134,6 +133,67 @@ void CopyFieldPropertiesMasks(const PasswordForm& from, PasswordForm* to) {
}
}
+// Sets autofill types of password and new password fields in |field_types|.
+// |password_type| (the autofill type of new password field) should be equal to
+// NEW_PASSWORD, PROBABLY_NEW_PASSWORD or NOT_NEW_PASSWORD. These values
+// correspond to cases when the user confirmed password update, did nothing or
+// declined to update password respectively.
+void SetFieldLabelsOnUpdate(const autofill::ServerFieldType password_type,
+ const autofill::PasswordForm& submitted_form,
+ FieldTypeMap* field_types) {
+ DCHECK(password_type == autofill::NEW_PASSWORD ||
+ password_type == autofill::PROBABLY_NEW_PASSWORD ||
+ password_type == autofill::NOT_NEW_PASSWORD)
+ << password_type;
+ DCHECK(!submitted_form.new_password_element.empty());
+
+ (*field_types)[submitted_form.password_element] = autofill::PASSWORD;
+ (*field_types)[submitted_form.new_password_element] = password_type;
+}
+
+// Sets the autofill type of the password field stored in |submitted_form| to
+// |password_type| in |field_types| map.
+void SetFieldLabelsOnSave(const autofill::ServerFieldType password_type,
+ const autofill::PasswordForm& submitted_form,
+ FieldTypeMap* field_types) {
+ DCHECK(password_type == autofill::PASSWORD ||
+ password_type == autofill::PROBABLY_ACCOUNT_CREATION_PASSWORD ||
+ password_type == autofill::ACCOUNT_CREATION_PASSWORD ||
+ password_type == autofill::NOT_ACCOUNT_CREATION_PASSWORD)
+ << password_type;
+
+ if (!submitted_form.new_password_element.empty()) {
+ (*field_types)[submitted_form.new_password_element] = password_type;
+ } else {
+ DCHECK(!submitted_form.password_element.empty());
+ (*field_types)[submitted_form.password_element] = password_type;
+ }
+}
+
+// Label username and password fields with autofill types in |form_structure|
+// based on |field_types|. The function also adds the types to
+// |available_field_types|.
+void LabelFields(const FieldTypeMap& field_types,
+ FormStructure* form_structure,
+ autofill::ServerFieldTypeSet* available_field_types) {
+ for (size_t i = 0; i < form_structure->field_count(); ++i) {
+ autofill::AutofillField* field = form_structure->field(i);
+
+ autofill::ServerFieldType type = autofill::UNKNOWN_TYPE;
+ if (!field->name.empty()) {
+ auto iter = field_types.find(field->name);
+ if (iter != field_types.end()) {
+ type = iter->second;
+ available_field_types->insert(type);
+ }
+ }
+
+ autofill::ServerFieldTypeSet types;
+ types.insert(type);
+ field->set_possible_types(types);
+ }
+}
+
} // namespace
PasswordFormManager::PasswordFormManager(
@@ -141,9 +201,9 @@ PasswordFormManager::PasswordFormManager(
PasswordManagerClient* client,
const base::WeakPtr<PasswordManagerDriver>& driver,
const PasswordForm& observed_form,
- std::unique_ptr<FormSaver> form_saver)
+ std::unique_ptr<FormSaver> form_saver,
+ FormFetcher* form_fetcher)
: observed_form_(observed_form),
- provisionally_saved_form_(nullptr),
other_possible_username_action_(
PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES),
form_path_segments_(
@@ -167,21 +227,34 @@ PasswordFormManager::PasswordFormManager(
user_action_(kUserActionNone),
submit_result_(kSubmitResultNotSubmitted),
form_type_(kFormTypeUnspecified),
- need_to_refetch_(false),
form_saver_(std::move(form_saver)),
- form_fetcher_impl_(client),
- form_fetcher_(&form_fetcher_impl_) {
+ form_fetcher_impl_(form_fetcher
+ ? nullptr
+ : base::MakeUnique<FormFetcherImpl>(
+ PasswordStore::FormDigest(observed_form),
+ client,
+ /* should_migrate_http_passwords */ true)),
+ form_fetcher_(form_fetcher ? form_fetcher : form_fetcher_impl_.get()),
+ is_main_frame_secure_(client->IsMainFrameSecure()) {
+ if (form_fetcher_impl_)
+ form_fetcher_impl_->Fetch();
DCHECK_EQ(observed_form.scheme == PasswordForm::SCHEME_HTML,
driver != nullptr);
if (driver)
drivers_.push_back(driver);
- FetchDataFromPasswordStore();
form_fetcher_->AddConsumer(this);
}
PasswordFormManager::~PasswordFormManager() {
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);
+ }
if (submit_result_ == kSubmitResultNotSubmitted) {
if (has_generated_password_)
metrics_util::LogPasswordGenerationSubmissionEvent(
@@ -193,6 +266,10 @@ PasswordFormManager::~PasswordFormManager() {
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);
+ }
}
}
@@ -297,20 +374,20 @@ void PasswordFormManager::ProvisionallySave(
OtherPossibleUsernamesAction action) {
DCHECK_NE(RESULT_NO_MATCH, DoesManage(credentials));
- std::unique_ptr<autofill::PasswordForm> mutable_provisionally_saved_form(
+ std::unique_ptr<autofill::PasswordForm> mutable_submitted_form(
new PasswordForm(credentials));
if (credentials.IsPossibleChangePasswordForm() &&
!credentials.username_value.empty() &&
IsProbablyNotUsername(credentials.username_value)) {
- mutable_provisionally_saved_form->username_value.clear();
- mutable_provisionally_saved_form->username_element.clear();
+ mutable_submitted_form->username_value.clear();
+ mutable_submitted_form->username_element.clear();
is_possible_change_password_form_without_username_ = true;
}
- provisionally_saved_form_ = std::move(mutable_provisionally_saved_form);
+ submitted_form_ = std::move(mutable_submitted_form);
other_possible_username_action_ = action;
does_look_like_signup_form_ = credentials.does_look_like_signup_form;
- if (HasCompletedMatching())
+ if (form_fetcher_->GetState() == FormFetcher::State::NOT_WAITING)
CreatePendingCredentials();
}
@@ -352,8 +429,8 @@ void PasswordFormManager::Update(
const autofill::PasswordForm& credentials_to_update) {
if (observed_form_.IsPossibleChangePasswordForm()) {
FormStructure form_structure(credentials_to_update.form_data);
- UploadChangePasswordForm(autofill::NEW_PASSWORD,
- form_structure.FormSignatureAsStr());
+ UploadPasswordVote(autofill::NEW_PASSWORD,
+ form_structure.FormSignatureAsStr());
}
base::string16 password_to_save = pending_credentials_.password_value;
bool skip_zero_click = pending_credentials_.skip_zero_click;
@@ -371,49 +448,6 @@ void PasswordFormManager::Update(
old_primary_key ? &old_primary_key.value() : nullptr);
}
-void PasswordFormManager::FetchDataFromPasswordStore() {
- if (form_fetcher_->GetState() == FormFetcher::State::WAITING) {
- // There is currently a password store query in progress, need to re-fetch
- // store results later.
- need_to_refetch_ = true;
- return;
- }
-
- std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
- if (password_manager_util::IsLoggingActive(client_)) {
- logger.reset(
- new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
- logger->LogMessage(Logger::STRING_FETCH_LOGINS_METHOD);
- logger->LogNumber(Logger::STRING_FORM_MANAGER_STATE,
- static_cast<int>(form_fetcher_->GetState()));
- }
-
- provisionally_saved_form_.reset();
- form_fetcher_impl_.set_state(FormFetcher::State::WAITING);
-
- PasswordStore* password_store = client_->GetPasswordStore();
- if (!password_store) {
- if (logger)
- logger->LogMessage(Logger::STRING_NO_STORE);
- // TODO(crbug.com/621355): The store might be empty in some tests. Start
- // enforcing the presence of a (non-null) PasswordStore once FormFetcher is
- // fetching the credentials instead of PasswordFormManager.
- return;
- }
- password_store->GetLogins(PasswordStore::FormDigest(observed_form_), this);
-
-// The statistics isn't needed on mobile, only on desktop. Let's save some
-// processor cycles.
-#if !defined(OS_IOS) && !defined(OS_ANDROID)
- // The statistics is needed for the "Save password?" bubble.
- password_store->GetSiteStats(observed_form_.origin.GetOrigin(), this);
-#endif
-}
-
-bool PasswordFormManager::HasCompletedMatching() const {
- return form_fetcher_->GetState() == FormFetcher::State::NOT_WAITING;
-}
-
void PasswordFormManager::SetSubmittedForm(const autofill::PasswordForm& form) {
bool is_change_password_form =
!form.new_password_value.empty() && !form.password_value.empty();
@@ -527,7 +561,7 @@ void PasswordFormManager::ProcessMatches(
// If password store was slow and provisionally saved form is already here
// then create pending credentials (see http://crbug.com/470322).
- if (provisionally_saved_form_)
+ if (submitted_form_)
CreatePendingCredentials();
for (auto const& driver : drivers_)
@@ -610,36 +644,6 @@ void PasswordFormManager::ProcessLoginPrompt() {
password_manager_->AutofillHttpAuth(best_matches_, *preferred_match_);
}
-void PasswordFormManager::OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
- DCHECK_EQ(FormFetcher::State::WAITING, form_fetcher_->GetState());
-
- if (need_to_refetch_) {
- // The received results are no longer up to date, need to re-request.
- form_fetcher_impl_.set_state(FormFetcher::State::NOT_WAITING);
- FetchDataFromPasswordStore();
- need_to_refetch_ = false;
- return;
- }
-
- std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
- if (password_manager_util::IsLoggingActive(client_)) {
- logger.reset(
- new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
- logger->LogMessage(Logger::STRING_ON_GET_STORE_RESULTS_METHOD);
- logger->LogNumber(Logger::STRING_NUMBER_RESULTS, results.size());
- }
-
- form_fetcher_impl_.SetResults(std::move(results));
-}
-
-void PasswordFormManager::OnGetSiteStatistics(
- std::vector<std::unique_ptr<InteractionsStats>> stats) {
- // On Windows the password request may be resolved after the statistics due to
- // importing from IE.
- form_fetcher_impl_.SetStats(std::move(stats));
-}
-
void PasswordFormManager::ProcessUpdate() {
DCHECK_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
DCHECK(preferred_match_ || !pending_credentials_.federation_origin.unique());
@@ -678,17 +682,17 @@ bool PasswordFormManager::UpdatePendingCredentialsIfOtherPossibleUsername(
void PasswordFormManager::SendAutofillVotes(const PasswordForm& observed,
PasswordForm* pending) {
+ // Ignore |pending_structure| if its FormData has no fields. This is to
+ // weed out those credentials that were saved before FormData was added
+ // to PasswordForm. Even without this check, these FormStructure's won't
+ // be uploaded, but it makes it hard to see if we are encountering
+ // unexpected errors.
if (pending->form_data.fields.empty())
return;
FormStructure pending_structure(pending->form_data);
FormStructure observed_structure(observed.form_data);
- // Ignore |pending_structure| if its FormData has no fields. This is to
- // weed out those credentials that were saved before FormData was added
- // to PasswordForm. Even without this check, these FormStructure's won't
- // be uploaded, but it makes it hard to see if we are encountering
- // unexpected errors.
if (pending_structure.FormSignatureAsStr() !=
observed_structure.FormSignatureAsStr()) {
// Only upload if this is the first time the password has been used.
@@ -698,8 +702,7 @@ void PasswordFormManager::SendAutofillVotes(const PasswordForm& observed,
// in cases where we currently save the wrong username isn't great.
// TODO(gcasto): Determine if generation should be offered in this case.
if (pending->times_used == 1 && selected_username_.empty()) {
- if (UploadPasswordForm(pending->form_data, pending->username_element,
- autofill::ACCOUNT_CREATION_PASSWORD,
+ if (UploadPasswordVote(autofill::ACCOUNT_CREATION_PASSWORD,
observed_structure.FormSignatureAsStr())) {
pending->generation_upload_status =
autofill::PasswordForm::POSITIVE_SIGNAL_SENT;
@@ -710,63 +713,65 @@ void PasswordFormManager::SendAutofillVotes(const PasswordForm& observed,
// A signal was sent that this was an account creation form, but the
// credential is now being used on the same form again. This cancels out
// the previous vote.
- if (UploadPasswordForm(pending->form_data, base::string16(),
- autofill::NOT_ACCOUNT_CREATION_PASSWORD,
+ if (UploadPasswordVote(autofill::NOT_ACCOUNT_CREATION_PASSWORD,
std::string())) {
pending->generation_upload_status =
autofill::PasswordForm::NEGATIVE_SIGNAL_SENT;
}
+ } else if (generation_popup_was_shown_) {
+ // Even if there is no autofill vote to be sent, send the vote about the
+ // usage of the generation popup.
+ UploadPasswordVote(autofill::UNKNOWN_TYPE, std::string());
}
}
-bool PasswordFormManager::UploadPasswordForm(
- const autofill::FormData& form_data,
- const base::string16& username_field,
+bool PasswordFormManager::UploadPasswordVote(
const autofill::ServerFieldType& password_type,
const std::string& login_form_signature) {
- DCHECK(password_type == autofill::PASSWORD ||
- password_type == autofill::PROBABLY_ACCOUNT_CREATION_PASSWORD ||
- password_type == autofill::ACCOUNT_CREATION_PASSWORD ||
- password_type == autofill::NOT_ACCOUNT_CREATION_PASSWORD);
+ // Check if there is any vote to be sent.
+ bool has_autofill_vote = password_type != autofill::UNKNOWN_TYPE;
+ bool has_password_generation_vote = generation_popup_was_shown_;
+ if (!has_autofill_vote && !has_password_generation_vote)
+ return false;
+
autofill::AutofillManager* autofill_manager =
client_->GetAutofillManagerForMainFrame();
if (!autofill_manager || !autofill_manager->download_manager())
return false;
- FormStructure form_structure(form_data);
+ bool is_update = password_type == autofill::NEW_PASSWORD ||
+ password_type == autofill::PROBABLY_NEW_PASSWORD ||
+ password_type == autofill::NOT_NEW_PASSWORD;
+ // If this is an update, a vote about the observed form is sent. If the user
+ // re-uses credentials, a vote about the saved form is sent. If the user saves
+ // credentials, the observed and pending forms are the same.
+ FormStructure form_structure(is_update ? observed_form_.form_data
+ : pending_credentials_.form_data);
if (!autofill_manager->ShouldUploadForm(form_structure) ||
!form_structure.ShouldBeCrowdsourced()) {
UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", false);
return false;
}
- // Find the first password field to label. If the provided username field name
- // is not empty, then also find the first field with that name to label.
- // We don't try to label anything else.
- bool found_password_field = false;
- bool should_find_username_field = !username_field.empty();
- for (size_t i = 0; i < form_structure.field_count(); ++i) {
- autofill::AutofillField* field = form_structure.field(i);
-
- autofill::ServerFieldType type = autofill::UNKNOWN_TYPE;
- if (!found_password_field && field->form_control_type == "password") {
- type = password_type;
- found_password_field = true;
- } else if (should_find_username_field && field->name == username_field) {
- type = autofill::USERNAME;
- should_find_username_field = false;
+ autofill::ServerFieldTypeSet available_field_types;
+ if (has_autofill_vote) {
+ // A map from field names to field types.
+ FieldTypeMap field_types;
+ DCHECK(submitted_form_);
+ if (is_update) {
+ if (submitted_form_->new_password_element.empty())
+ return false;
+ SetFieldLabelsOnUpdate(password_type, *submitted_form_, &field_types);
+ } else {
+ SetFieldLabelsOnSave(password_type, *submitted_form_, &field_types);
+ if (password_type == autofill::ACCOUNT_CREATION_PASSWORD) {
+ field_types[pending_credentials_.username_element] = autofill::USERNAME;
+ }
}
-
- autofill::ServerFieldTypeSet types;
- types.insert(type);
- field->set_possible_types(types);
+ field_types[submitted_form_->confirmation_password_element] =
+ autofill::CONFIRMATION_PASSWORD;
+ LabelFields(field_types, &form_structure, &available_field_types);
}
- DCHECK(found_password_field);
- DCHECK(!should_find_username_field);
-
- autofill::ServerFieldTypeSet available_field_types;
- available_field_types.insert(password_type);
- available_field_types.insert(autofill::USERNAME);
if (generation_popup_was_shown_)
AddGeneratedVote(&form_structure);
@@ -790,91 +795,6 @@ bool PasswordFormManager::UploadPasswordForm(
return success;
}
-bool PasswordFormManager::UploadChangePasswordForm(
- const autofill::ServerFieldType& password_type,
- const std::string& login_form_signature) {
- DCHECK(password_type == autofill::NEW_PASSWORD ||
- password_type == autofill::PROBABLY_NEW_PASSWORD ||
- password_type == autofill::NOT_NEW_PASSWORD);
- if (!provisionally_saved_form_ ||
- provisionally_saved_form_->new_password_element.empty()) {
- // |new_password_element| is empty for non change password forms, for
- // example when the password was overriden.
- return false;
- }
- autofill::AutofillManager* autofill_manager =
- client_->GetAutofillManagerForMainFrame();
- if (!autofill_manager || !autofill_manager->download_manager())
- return false;
-
- // Create a map from field names to field types.
- std::map<base::string16, autofill::ServerFieldType> field_types;
- if (!pending_credentials_.username_element.empty())
- field_types[provisionally_saved_form_->username_element] =
- autofill::USERNAME;
- if (!pending_credentials_.password_element.empty())
- field_types[provisionally_saved_form_->password_element] =
- autofill::PASSWORD;
- field_types[provisionally_saved_form_->new_password_element] = password_type;
- // Find all password fields after |new_password_element| and set their type to
- // |password_type|. They are considered to be confirmation fields.
- const autofill::FormData& form_data = observed_form_.form_data;
- bool is_new_password_field_found = false;
- for (size_t i = 0; i < form_data.fields.size(); ++i) {
- const autofill::FormFieldData& field = form_data.fields[i];
- if (field.form_control_type != "password")
- continue;
- if (is_new_password_field_found) {
- field_types[field.name] = password_type;
- // We don't care about password fields after a confirmation field.
- break;
- } else if (field.name == provisionally_saved_form_->new_password_element) {
- is_new_password_field_found = true;
- }
- }
- DCHECK(is_new_password_field_found);
-
- // Create FormStructure with field type information for uploading a vote.
- FormStructure form_structure(form_data);
- if (!autofill_manager->ShouldUploadForm(form_structure) ||
- !form_structure.ShouldBeCrowdsourced())
- return false;
-
- autofill::ServerFieldTypeSet available_field_types;
- for (size_t i = 0; i < form_structure.field_count(); ++i) {
- autofill::AutofillField* field = form_structure.field(i);
- autofill::ServerFieldType type = autofill::UNKNOWN_TYPE;
- auto iter = field_types.find(field->name);
- if (iter != field_types.end()) {
- type = iter->second;
- available_field_types.insert(type);
- }
-
- autofill::ServerFieldTypeSet types;
- types.insert(type);
- field->set_possible_types(types);
- }
-
- if (generation_popup_was_shown_)
- AddGeneratedVote(&form_structure);
- if (form_classifier_outcome_ != kNoOutcome)
- AddFormClassifierVote(&form_structure);
-
- // Force uploading as these events are relatively rare and we want to make
- // sure to receive them. It also makes testing easier if these requests
- // always pass.
- form_structure.set_upload_required(UPLOAD_REQUIRED);
-
- if (password_manager_util::IsLoggingActive(client_)) {
- BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
- logger.LogFormStructure(Logger::STRING_FORM_VOTES, form_structure);
- }
-
- return autofill_manager->download_manager()->StartUploadRequest(
- form_structure, false /* was_autofilled */, available_field_types,
- login_form_signature, true /* observed_submission */);
-}
-
void PasswordFormManager::AddGeneratedVote(
autofill::FormStructure* form_structure) {
DCHECK(form_structure);
@@ -931,15 +851,14 @@ void PasswordFormManager::AddFormClassifierVote(
}
void PasswordFormManager::CreatePendingCredentials() {
- DCHECK(provisionally_saved_form_);
- base::string16 password_to_save(PasswordToSave(*provisionally_saved_form_));
+ 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.
- const PasswordForm* saved_form =
- FindBestSavedMatch(provisionally_saved_form_.get());
+ const PasswordForm* saved_form = FindBestSavedMatch(submitted_form_.get());
if (saved_form != nullptr) {
// The user signed in with a login we autofilled.
pending_credentials_ = *saved_form;
@@ -969,9 +888,8 @@ void PasswordFormManager::CreatePendingCredentials() {
UpdateMetadataForUsage(&pending_credentials_);
// Update |pending_credentials_| in order to be able correctly save it.
- pending_credentials_.origin = provisionally_saved_form_->origin;
- pending_credentials_.signon_realm =
- provisionally_saved_form_->signon_realm;
+ pending_credentials_.origin = submitted_form_->origin;
+ pending_credentials_.signon_realm = submitted_form_->signon_realm;
// Normally, the copy of the PSL matched credentials, adapted for the
// current domain, is saved automatically without asking the user, because
@@ -979,7 +897,7 @@ void PasswordFormManager::CreatePendingCredentials() {
// the user already agreed to store a password.
//
// However, if the user changes the suggested password, it might indicate
- // that the autofilled credentials and |provisionally_saved_form_|
+ // that the autofilled credentials and |submitted_form_|
// 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
@@ -987,9 +905,9 @@ void PasswordFormManager::CreatePendingCredentials() {
// |origin| and |signon_realm| to correct values.
//
// There is still the edge case when the autofilled credentials represent
- // the same account as |provisionally_saved_form_| but the stored password
+ // the same account as |submitted_form_| but the stored password
// was out of date. In that case, the user just had to manually enter the
- // new password, which is now in |provisionally_saved_form_|. The best
+ // new password, which is now in |submitted_form_|. The best
// thing would be to save automatically, and also update the original
// credentials. However, we have no way to tell if this is the case.
// This will likely happen infrequently, and the inconvenience put on the
@@ -1007,25 +925,23 @@ void PasswordFormManager::CreatePendingCredentials() {
} else if (other_possible_username_action_ ==
ALLOW_OTHER_POSSIBLE_USERNAMES &&
UpdatePendingCredentialsIfOtherPossibleUsername(
- provisionally_saved_form_->username_value)) {
+ submitted_form_->username_value)) {
// |pending_credentials_| is now set. Note we don't update
// |pending_credentials_.username_value| to |credentials.username_value|
// yet because we need to keep the original username to modify the stored
// credential.
- selected_username_ = provisionally_saved_form_->username_value;
+ selected_username_ = submitted_form_->username_value;
is_new_login_ = false;
} else if (!best_matches_.empty() &&
- provisionally_saved_form_->type !=
- autofill::PasswordForm::TYPE_API &&
- (provisionally_saved_form_
- ->IsPossibleChangePasswordFormWithoutUsername() ||
- provisionally_saved_form_->username_element.empty())) {
- const PasswordForm* best_update_match = FindBestMatchForUpdatePassword(
- provisionally_saved_form_->password_value);
+ submitted_form_->type != autofill::PasswordForm::TYPE_API &&
+ (submitted_form_->IsPossibleChangePasswordFormWithoutUsername() ||
+ submitted_form_->username_element.empty())) {
+ const PasswordForm* best_update_match =
+ FindBestMatchForUpdatePassword(submitted_form_->password_value);
retry_password_form_password_update_ =
- provisionally_saved_form_->username_element.empty() &&
- provisionally_saved_form_->new_password_element.empty();
+ submitted_form_->username_element.empty() &&
+ submitted_form_->new_password_element.empty();
is_new_login_ = false;
if (best_update_match) {
@@ -1038,14 +954,14 @@ void PasswordFormManager::CreatePendingCredentials() {
} else {
// We don't care about |pending_credentials_| if we didn't find the best
// match, since the user will select the correct one.
- pending_credentials_.origin = provisionally_saved_form_->origin;
+ pending_credentials_.origin = submitted_form_->origin;
}
} else {
CreatePendingCredentialsForNewCredentials();
}
if (!IsValidAndroidFacetURI(pending_credentials_.signon_realm)) {
- pending_credentials_.action = provisionally_saved_form_->action;
+ 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.
@@ -1054,21 +970,19 @@ void PasswordFormManager::CreatePendingCredentials() {
}
pending_credentials_.password_value = password_to_save;
- pending_credentials_.preferred = provisionally_saved_form_->preferred;
- CopyFieldPropertiesMasks(*provisionally_saved_form_, &pending_credentials_);
+ pending_credentials_.preferred = submitted_form_->preferred;
+ CopyFieldPropertiesMasks(*submitted_form_, &pending_credentials_);
// If we're dealing with an API-driven provisionally saved form, then take
// the server provided values. We don't do this for non-API forms, as
// those will never have those members set.
- if (provisionally_saved_form_->type == autofill::PasswordForm::TYPE_API) {
- pending_credentials_.skip_zero_click =
- provisionally_saved_form_->skip_zero_click;
- pending_credentials_.display_name = provisionally_saved_form_->display_name;
- pending_credentials_.federation_origin =
- provisionally_saved_form_->federation_origin;
- pending_credentials_.icon_url = provisionally_saved_form_->icon_url;
+ if (submitted_form_->type == autofill::PasswordForm::TYPE_API) {
+ pending_credentials_.skip_zero_click = submitted_form_->skip_zero_click;
+ pending_credentials_.display_name = submitted_form_->display_name;
+ pending_credentials_.federation_origin = submitted_form_->federation_origin;
+ pending_credentials_.icon_url = submitted_form_->icon_url;
// Take the correct signon_realm for federated credentials.
- pending_credentials_.signon_realm = provisionally_saved_form_->signon_realm;
+ pending_credentials_.signon_realm = submitted_form_->signon_realm;
}
if (user_action_ == kUserActionOverridePassword &&
@@ -1081,13 +995,6 @@ void PasswordFormManager::CreatePendingCredentials() {
if (has_generated_password_)
pending_credentials_.type = PasswordForm::TYPE_GENERATED;
-
- // In case of change password forms we need to leave
- // |provisionally_saved_form_| in order to be able to determine which field is
- // password and which is a new password during sending a vote in other cases
- // we can reset |provisionally_saved_form_|.
- if (!provisionally_saved_form_->IsPossibleChangePasswordForm())
- provisionally_saved_form_.reset();
}
uint32_t PasswordFormManager::ScoreResult(const PasswordForm& candidate) const {
@@ -1223,13 +1130,11 @@ void PasswordFormManager::CreatePendingCredentialsForNewCredentials() {
// User typed in a new, unknown username.
SetUserAction(kUserActionOverrideUsernameAndPassword);
pending_credentials_ = observed_form_;
- if (provisionally_saved_form_->was_parsed_using_autofill_predictions)
- pending_credentials_.username_element =
- provisionally_saved_form_->username_element;
- pending_credentials_.username_value =
- provisionally_saved_form_->username_value;
+ if (submitted_form_->was_parsed_using_autofill_predictions)
+ pending_credentials_.username_element = submitted_form_->username_element;
+ pending_credentials_.username_value = submitted_form_->username_value;
pending_credentials_.other_possible_usernames =
- provisionally_saved_form_->other_possible_usernames;
+ submitted_form_->other_possible_usernames;
// The password value will be filled in later, remove any garbage for now.
pending_credentials_.password_value.clear();
@@ -1239,17 +1144,26 @@ void PasswordFormManager::CreatePendingCredentialsForNewCredentials() {
// are likely different than those on a login form, so do not bother saving
// them. We will fill them with meaningful values during update when the user
// goes onto a real login form for the first time.
- if (!provisionally_saved_form_->new_password_element.empty()) {
+ if (!submitted_form_->new_password_element.empty()) {
pending_credentials_.password_element.clear();
}
}
void PasswordFormManager::OnNopeUpdateClicked() {
- UploadChangePasswordForm(autofill::NOT_NEW_PASSWORD, std::string());
+ UploadPasswordVote(autofill::NOT_NEW_PASSWORD, std::string());
+}
+
+void PasswordFormManager::OnNeverClicked() {
+ UploadPasswordVote(autofill::UNKNOWN_TYPE, std::string());
+ PermanentlyBlacklist();
}
-void PasswordFormManager::OnNoInteractionOnUpdate() {
- UploadChangePasswordForm(autofill::PROBABLY_NEW_PASSWORD, std::string());
+void PasswordFormManager::OnNoInteraction(bool is_update) {
+ if (is_update)
+ UploadPasswordVote(autofill::PROBABLY_NEW_PASSWORD, std::string());
+ else {
+ UploadPasswordVote(autofill::UNKNOWN_TYPE, std::string());
+ }
}
void PasswordFormManager::LogSubmitPassed() {
@@ -1279,8 +1193,9 @@ void PasswordFormManager::LogSubmitFailed() {
}
void PasswordFormManager::WipeStoreCopyIfOutdated() {
- UMA_HISTOGRAM_BOOLEAN("PasswordManager.StoreReadyWhenWiping",
- HasCompletedMatching());
+ UMA_HISTOGRAM_BOOLEAN(
+ "PasswordManager.StoreReadyWhenWiping",
+ form_fetcher_->GetState() == FormFetcher::State::NOT_WAITING);
form_saver_->WipeOutdatedCopies(pending_credentials_, &best_matches_,
&preferred_match_);
@@ -1294,25 +1209,20 @@ void PasswordFormManager::SaveGenerationFieldDetectedByClassifier(
}
void PasswordFormManager::SendVotesOnSave() {
+ if (observed_form_.IsPossibleChangePasswordFormWithoutUsername())
+ return;
+
// Upload credentials the first time they are saved. This data is used
// by password generation to help determine account creation sites.
// Credentials that have been previously used (e.g., PSL matches) are checked
// to see if they are valid account creation forms.
if (pending_credentials_.times_used == 0) {
- if (!observed_form_.IsPossibleChangePasswordFormWithoutUsername()) {
- base::string16 username_field;
autofill::ServerFieldType password_type = autofill::PASSWORD;
- if (does_look_like_signup_form_) {
- username_field = pending_credentials_.username_element;
+ if (does_look_like_signup_form_)
password_type = autofill::PROBABLY_ACCOUNT_CREATION_PASSWORD;
- }
- UploadPasswordForm(pending_credentials_.form_data, username_field,
- password_type, std::string());
- }
- } else {
- if (!observed_form_.IsPossibleChangePasswordFormWithoutUsername())
- SendAutofillVotes(observed_form_, &pending_credentials_);
- }
+ UploadPasswordVote(password_type, std::string());
+ } else
+ SendAutofillVotes(observed_form_, &pending_credentials_);
}
void PasswordFormManager::SetUserAction(UserAction user_action) {
diff --git a/chromium/components/password_manager/core/browser/password_form_manager.h b/chromium/components/password_manager/core/browser/password_form_manager.h
index 1718c9b474e..d8e5e296ee5 100644
--- a/chromium/components/password_manager/core/browser/password_form_manager.h
+++ b/chromium/components/password_manager/core/browser/password_form_manager.h
@@ -13,7 +13,6 @@
#include <vector>
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/strings/string16.h"
@@ -25,7 +24,8 @@
#include "components/password_manager/core/browser/form_fetcher_impl.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_store.h"
-#include "components/password_manager/core/browser/password_store_consumer.h"
+
+using autofill::FormStructure;
namespace password_manager {
@@ -33,18 +33,28 @@ class FormSaver;
class PasswordManager;
class PasswordManagerClient;
-// Per-password-form-{on-page, dialog} class responsible for interactions
-// between a given form, the per-tab PasswordManager, and the PasswordStore.
-class PasswordFormManager : public PasswordStoreConsumer,
- public FormFetcher::Consumer {
+// A map from field names to field types.
+using FieldTypeMap = std::map<base::string16, autofill::ServerFieldType>;
+
+// This class helps with filling the observed form (both HTML and from HTTP
+// auth) and with saving/updating the stored information about it.
+class PasswordFormManager : public FormFetcher::Consumer {
public:
- // |password_manager| owns this object
- // |form_on_page| is the form that may be submitted and could need login data.
+ // |password_manager| owns |this|, |client| and |driver| serve to
+ // communicate with embedder, |observed_form| is the associated form |this|
+ // is managing, |form_saver| is used to save/update the form and
+ // |form_fetcher| to get saved data about the form.
+ //
+ // 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
+ // required that |form_fetcher| is not null.
PasswordFormManager(PasswordManager* password_manager,
PasswordManagerClient* client,
const base::WeakPtr<PasswordManagerDriver>& driver,
const autofill::PasswordForm& observed_form,
- std::unique_ptr<FormSaver> form_saver);
+ std::unique_ptr<FormSaver> form_saver,
+ FormFetcher* form_fetcher);
~PasswordFormManager() override;
// Flags describing the result of comparing two forms as performed by
@@ -81,26 +91,6 @@ class PasswordFormManager : public PasswordStoreConsumer,
// they match. The return value is a MatchResultMask bitmask.
MatchResultMask DoesManage(const autofill::PasswordForm& form) const;
- // Retrieves potential matching logins from the database. In addition the
- // statistics is retrived on platforms with the password bubble. This is
- // called automatically during construction and can be called manually later
- // as well to cause an update of the cached credentials.
- void FetchDataFromPasswordStore();
-
- // Simple state-check to verify whether this object as received a callback
- // from the PasswordStore and completed its matching phase. Note that the
- // callback in question occurs on the same (and only) main thread from which
- // instances of this class are ever used, but it is required since it is
- // conceivable that a user (or ui test) could attempt to submit a login
- // prompt before the callback has occured, which would InvokeLater a call to
- // PasswordManager::ProvisionallySave, which would interact with this object
- // before the db has had time to answer with matching password entries.
- // This is intended to be a one-time check; if the return value is false the
- // expectation is caller will give up. This clearly won't work if you put it
- // in a loop and wait for matching to complete; you're (supposed to be) on
- // the same thread!
- bool HasCompletedMatching() const;
-
// Update |this| with the |form| that was actually submitted. Used to
// determine what type the submitted form is for
// IsIgnorableChangePasswordForm() and UMA stats.
@@ -125,12 +115,6 @@ class PasswordFormManager : public PasswordStoreConsumer,
// delayed until the data arrives.
void ProcessFrame(const base::WeakPtr<PasswordManagerDriver>& driver);
- // PasswordStoreConsumer:
- void OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
- void OnGetSiteStatistics(
- std::vector<std::unique_ptr<InteractionsStats>> stats) override;
-
// A user opted to 'never remember' passwords for this form.
// Blacklist it so that from now on when it is seen we ignore it.
// TODO(vasilii): remove the 'virtual' specifier.
@@ -195,6 +179,11 @@ class PasswordFormManager : public PasswordStoreConsumer,
// Called if the user could generate a password for this form.
void MarkGenerationAvailable() { generation_available_ = true; }
+ // Returns the provisionally saved form, if it exists, otherwise nullptr.
+ const autofill::PasswordForm* submitted_form() const {
+ return submitted_form_.get();
+ }
+
// Returns the pending credentials.
const autofill::PasswordForm& pending_credentials() const {
return pending_credentials_;
@@ -221,7 +210,7 @@ class PasswordFormManager : public PasswordStoreConsumer,
return is_possible_change_password_form_without_username_;
}
- const FormFetcher* form_fetcher() { return form_fetcher_; }
+ FormFetcher* form_fetcher() { return form_fetcher_; }
// Use this to wipe copies of |pending_credentials_| from the password store
// (and |best_matches_| as well. It will only wipe if:
@@ -236,8 +225,12 @@ class PasswordFormManager : public PasswordStoreConsumer,
// Called when the user chose not to update password.
void OnNopeUpdateClicked();
- // Called when the user didn't interact with Update UI.
- void OnNoInteractionOnUpdate();
+ // Called when the user clicked "Never" button in the "save password" prompt.
+ void OnNeverClicked();
+
+ // Called when the user didn't interact with UI. |is_update| is true iff
+ // it was the update UI.
+ void OnNoInteraction(bool is_update);
// Saves the outcome of HTML parsing based form classifier to upload proto.
void SaveGenerationFieldDetectedByClassifier(
@@ -245,6 +238,12 @@ class PasswordFormManager : public PasswordStoreConsumer,
FormSaver* form_saver() { return form_saver_.get(); }
+ protected:
+ // FormFetcher::Consumer:
+ void ProcessMatches(
+ const std::vector<const autofill::PasswordForm*>& non_federated,
+ 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
@@ -321,11 +320,6 @@ class PasswordFormManager : public PasswordStoreConsumer,
// |best_matches_|, |preferred_match_| and |non_best_matches_| accordingly.
void ScoreMatches(const std::vector<const autofill::PasswordForm*>& matches);
- // FormFetcher::Consumer:
- void ProcessMatches(
- const std::vector<const autofill::PasswordForm*>& non_federated,
- size_t filtered_count) override;
-
// Helper for Save in the case that best_matches.size() == 0, meaning
// we have no prior record of this form/username/password and the user
// has opted to 'Save Password'. The previously preferred login from
@@ -376,36 +370,11 @@ class PasswordFormManager : public PasswordStoreConsumer,
// UMA.
int GetActionsTaken() const;
- // Try to label password fields and upload |form_data|. This differs from
- // AutofillManager::OnFormSubmitted() in a few ways.
- // - This function will only label the first <input type="password"> field
- // as |password_type|. Other fields will stay unlabeled, as they
- // should have been labeled during the upload for OnFormSubmitted().
- // - If the |username_field| attribute is nonempty, we will additionally
- // label the field with that name as the username field.
- // - This function does not assume that |form| is being uploaded during
- // the same browsing session as it was originally submitted (as we may
- // not have the necessary information to classify the form at that time)
- // so it bypasses the cache and doesn't log the same quality UMA metrics.
- // |login_form_signature| may be empty. It is non-empty when the user fills
- // and submits a login form using a generated password. In this case,
- // |login_form_signature| should be set to the submitted form's signature.
- // Note that in this case, |form.FormSignature()| gives the signature for the
- // registration form on which the password was generated, rather than the
- // submitted form's signature.
- bool UploadPasswordForm(const autofill::FormData& form_data,
- const base::string16& username_field,
- const autofill::ServerFieldType& password_type,
+ // 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::ServerFieldType& password_type,
const std::string& login_form_signature);
- // Try to label username, password and new password fields of |observed_form_|
- // which is considered to be change password forms. Returns true on success.
- // |password_type| should be equal to NEW_PASSWORD, PROBABLY_NEW_PASSWORD or
- // NOT_NEW_PASSWORD. These values correspond to cases when the user conrirmed
- // password update, did nothing or declined to update password respectively.
- bool UploadChangePasswordForm(const autofill::ServerFieldType& password_type,
- const std::string& login_form_signature);
-
// Adds a vote on password generation usage to |form_structure|.
void AddGeneratedVote(autofill::FormStructure* form_structure);
@@ -483,7 +452,7 @@ class PasswordFormManager : public PasswordStoreConsumer,
const autofill::PasswordForm observed_form_;
// Stores a submitted form.
- std::unique_ptr<const autofill::PasswordForm> provisionally_saved_form_;
+ std::unique_ptr<const autofill::PasswordForm> submitted_form_;
// Stores if for creating |pending_credentials_| other possible usernames
// option should apply.
@@ -496,13 +465,9 @@ class PasswordFormManager : public PasswordStoreConsumer,
// Stores updated credentials when the form was submitted but success is still
// unknown. This variable contains credentials that are ready to be written
// (saved or updated) to a password store. It is calculated based on
- // |provisionally_saved_form_| and |best_matches_|.
+ // |submitted_form_| and |best_matches_|.
autofill::PasswordForm pending_credentials_;
- // Stores the form with generated password till the user makes successful
- // login or removes the generated password.
- std::unique_ptr<autofill::PasswordForm> presaved_form_;
-
// Whether pending_credentials_ stores a new login or is an update
// to an existing one.
bool is_new_login_;
@@ -558,7 +523,7 @@ class PasswordFormManager : public PasswordStoreConsumer,
// |observed_form_| but also on the credentials that the user submitted.
bool is_possible_change_password_form_without_username_;
- // True if |provisionally_saved_form_| looks like SignUp form according to
+ // True if |submitted_form_| looks like SignUp form according to
// local heuristics.
bool does_look_like_signup_form_ = false;
@@ -584,22 +549,22 @@ class PasswordFormManager : public PasswordStoreConsumer,
// user has entered.
FormType form_type_;
- // False unless FetchMatchingLoginsFromPasswordStore has been called again
- // without the password store returning results in the meantime.
- bool need_to_refetch_;
-
// FormSaver instance used by |this| to all tasks related to storing
// credentials.
std::unique_ptr<FormSaver> form_saver_;
// TODO(crbug.com/621355) Remove this, ultimately the form fetcher will not be
// owned by PasswordFormManager. Temporarily, this is the object which
- // |form_fetcher_| points to.
- FormFetcherImpl form_fetcher_impl_;
+ // |form_fetcher_| points to, unless set otherwise in the constructor.
+ std::unique_ptr<FormFetcherImpl> form_fetcher_impl_;
// FormFetcher instance which owns the login data from PasswordStore.
FormFetcher* const form_fetcher_;
+ // True if the main frame's visible URL, at the time this PasswordFormManager
+ // was created, is secure.
+ bool is_main_frame_secure_ = false;
+
DISALLOW_COPY_AND_ASSIGN(PasswordFormManager);
};
diff --git a/chromium/components/password_manager/core/browser/password_form_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_form_manager_unittest.cc
index 6699623c9c0..313744b97c2 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
@@ -6,17 +6,17 @@
#include <map>
#include <memory>
+#include <set>
#include <utility>
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/run_loop.h"
+#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/user_action_tester.h"
-#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/proto/server.pb.h"
#include "components/autofill/core/browser/test_autofill_client.h"
@@ -24,12 +24,11 @@
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "components/autofill/core/common/autofill_pref_names.h"
#include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/mock_password_store.h"
+#include "components/password_manager/core/browser/fake_form_fetcher.h"
#include "components/password_manager/core/browser/password_manager.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_manager_test_utils.h"
-#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/statistics_table.h"
#include "components/password_manager/core/browser/stub_credentials_filter.h"
#include "components/password_manager/core/browser/stub_form_saver.h"
@@ -50,7 +49,6 @@ using autofill::FieldPropertiesMask;
using autofill::PasswordForm;
using base::ASCIIToUTF16;
using ::testing::_;
-using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::Mock;
using ::testing::NiceMock;
@@ -64,6 +62,9 @@ namespace password_manager {
namespace {
+// Enum that describes what button the user pressed on the save prompt.
+enum SavePromptInteraction { SAVE, NEVER, NO_INTERACTION };
+
class MockFormSaver : public StubFormSaver {
public:
MockFormSaver() = default;
@@ -97,16 +98,6 @@ class MockFormSaver : public StubFormSaver {
DISALLOW_COPY_AND_ASSIGN(MockFormSaver);
};
-// Invokes the password store consumer with a copy of all forms.
-ACTION_P4(InvokeConsumer, form1, form2, form3, form4) {
- std::vector<std::unique_ptr<PasswordForm>> result;
- result.push_back(base::MakeUnique<PasswordForm>(form1));
- result.push_back(base::MakeUnique<PasswordForm>(form2));
- result.push_back(base::MakeUnique<PasswordForm>(form3));
- result.push_back(base::MakeUnique<PasswordForm>(form4));
- arg0->OnGetPasswordStoreResults(std::move(result));
-}
-
MATCHER_P(CheckUsername, username_value, "Username incorrect") {
return arg.username_value == username_value;
}
@@ -120,33 +111,28 @@ MATCHER_P2(CheckUploadedAutofillTypesAndSignature,
expected_types,
"Unexpected autofill types or form signature") {
if (form_signature != arg.FormSignatureAsStr()) {
- // An unexpected form is uploaded.
+ // Unexpected form's signature.
+ ADD_FAILURE() << "Expected form signature is " << form_signature
+ << ", but found " << arg.FormSignatureAsStr();
return false;
}
- for (const autofill::AutofillField* field : arg) {
- if (expected_types.find(field->name) == expected_types.end()) {
- if (!field->possible_types().empty()) {
- // Unexpected types are uploaded.
- return false;
- }
- } else {
- if (field->possible_types().size() > 1) {
- // Currently we expect not more than 1 type per field.
- return false;
- }
+ for (const auto& field : arg) {
+ if (field->possible_types().size() > 1) {
+ ADD_FAILURE() << field->name << " field has several possible types";
+ return false;
+ }
- if (field->possible_types().empty()) {
- if (expected_types.find(field->name) != expected_types.end()) {
- // A vote is expected but not found.
- return false;
- }
- } else {
- if (expected_types.find(field->name)->second !=
- *field->possible_types().begin()) {
- // An unexpected field type is found.
- return false;
- }
- }
+ autofill::ServerFieldType expected_vote =
+ expected_types.find(field->name) == expected_types.end()
+ ? autofill::NO_SERVER_DATA
+ : expected_types.find(field->name)->second;
+ autofill::ServerFieldType actual_vote =
+ field->possible_types().empty() ? autofill::NO_SERVER_DATA
+ : *field->possible_types().begin();
+ if (expected_vote != actual_vote) {
+ ADD_FAILURE() << field->name << " field: expected vote " << expected_vote
+ << ", but found " << actual_vote;
+ return false;
}
}
return true;
@@ -162,7 +148,7 @@ MATCHER_P2(CheckUploadedGenerationTypesAndSignature,
<< ", but found " << arg.FormSignatureAsStr();
return false;
}
- for (const autofill::AutofillField* field : arg) {
+ for (const auto& field : arg) {
if (expected_generation_types.find(field->name) ==
expected_generation_types.end()) {
if (field->generation_type() !=
@@ -192,7 +178,7 @@ MATCHER_P2(CheckUploadedFormClassifierVote,
found_generation_element,
generation_element,
"Wrong form classifier votes") {
- for (const autofill::AutofillField* field : arg) {
+ for (const auto& field : arg) {
if (found_generation_element && field->name == generation_element) {
EXPECT_EQ(field->form_classifier_outcome(),
autofill::AutofillUploadContents::Field::GENERATION_ELEMENT);
@@ -208,7 +194,7 @@ MATCHER_P2(CheckUploadedFormClassifierVote,
MATCHER_P(CheckFieldPropertiesMasksUpload,
expected_field_properties,
"Wrong field properties flags") {
- for (const autofill::AutofillField* field : arg) {
+ for (const auto& field : arg) {
autofill::FieldPropertiesMask expected_mask =
expected_field_properties.find(field->name) !=
expected_field_properties.end()
@@ -224,10 +210,6 @@ MATCHER_P(CheckFieldPropertiesMasksUpload,
return true;
}
-void ClearVector(std::vector<std::unique_ptr<PasswordForm>>* results) {
- results->clear();
-}
-
class MockAutofillDownloadManager : public autofill::AutofillDownloadManager {
public:
MockAutofillDownloadManager(
@@ -303,41 +285,15 @@ class MockPasswordManagerDriver : public StubPasswordManagerDriver {
NiceMock<MockAutofillManager> mock_autofill_manager_;
};
-class MockStoreResultFilter : public StubCredentialsFilter {
- public:
- // This method is called by StubCredentialsFilter::FilterResults.
- MOCK_CONST_METHOD1(FilterResultsPtr,
- void(std::vector<std::unique_ptr<PasswordForm>>* results));
-};
-
class TestPasswordManagerClient : public StubPasswordManagerClient {
public:
- explicit TestPasswordManagerClient(PasswordStore* password_store)
- : password_store_(password_store),
- driver_(new NiceMock<MockPasswordManagerDriver>),
- filter_all_results_(false) {
- prefs_.registry()->RegisterBooleanPref(prefs::kPasswordManagerSavingEnabled,
+ TestPasswordManagerClient()
+ : driver_(new NiceMock<MockPasswordManagerDriver>) {
+ prefs_.registry()->RegisterBooleanPref(prefs::kCredentialsEnableService,
true);
}
- // After this method is called, the filter returned by GetStoreResultFilter()
- // will filter out all forms.
- void FilterAllResults() {
- filter_all_results_ = true;
-
- // EXPECT_CALL rather than ON_CALL, because if the test needs the
- // filtering, then it needs it called.
- EXPECT_CALL(all_filter_, FilterResultsPtr(_))
- .Times(testing::AtLeast(1))
- .WillRepeatedly(testing::Invoke(ClearVector));
- }
-
- const CredentialsFilter* GetStoreResultFilter() const override {
- return filter_all_results_ ? &all_filter_ : &none_filter_;
- }
-
PrefService* GetPrefs() override { return &prefs_; }
- PasswordStore* GetPasswordStore() const override { return password_store_; }
MockPasswordManagerDriver* mock_driver() { return driver_.get(); }
@@ -351,29 +307,14 @@ class TestPasswordManagerClient : public StubPasswordManagerClient {
private:
TestingPrefServiceSimple prefs_;
- PasswordStore* password_store_;
std::unique_ptr<MockPasswordManagerDriver> driver_;
-
- // Filters to remove all and no results, respectively, in FilterResults.
- NiceMock<MockStoreResultFilter> all_filter_;
- NiceMock<MockStoreResultFilter> none_filter_;
- bool filter_all_results_;
};
} // namespace
class PasswordFormManagerTest : public testing::Test {
public:
- PasswordFormManagerTest() {}
-
- // Types of possible outcomes of simulated matching, see
- // SimulateMatchingPhase.
- enum ResultOfSimulatedMatching {
- RESULT_NO_MATCH,
- RESULT_SAVED_MATCH = 1 << 0, // Include saved_match_ in store results.
- RESULT_PSL_MATCH = 1 << 1, // Include psl_saved_match_ in store results.
- };
- typedef int ResultOfSimulatedMatchingMask;
+ PasswordFormManagerTest() { fake_form_fetcher_.Fetch(); }
void SetUp() override {
observed_form_.origin = GURL("http://accounts.google.com/a/LoginAuth");
@@ -415,42 +356,10 @@ class PasswordFormManagerTest : public testing::Test {
field.form_control_type = "password";
saved_match_.form_data.fields.push_back(field);
- mock_store_ = new NiceMock<MockPasswordStore>();
- ON_CALL(*mock_store_, GetSiteStatsMock(_))
- .WillByDefault(Return(std::vector<InteractionsStats*>()));
- client_.reset(new TestPasswordManagerClient(mock_store_.get()));
- password_manager_.reset(new PasswordManager(client_.get()));
- EXPECT_CALL(*mock_store_,
- GetLogins(PasswordStore::FormDigest(observed_form_), _));
+ password_manager_.reset(new PasswordManager(&client_));
form_manager_.reset(new PasswordFormManager(
- password_manager_.get(), client_.get(), client_.get()->driver(),
- observed_form_, base::MakeUnique<NiceMock<MockFormSaver>>()));
- Mock::VerifyAndClearExpectations(mock_store_.get());
- }
-
- void TearDown() override {
- if (mock_store_.get())
- mock_store_->ShutdownOnUIThread();
- }
-
- MockPasswordStore* mock_store() const { return mock_store_.get(); }
-
- void SimulateMatchingPhase(PasswordFormManager* p,
- ResultOfSimulatedMatchingMask result) {
- if (result == RESULT_NO_MATCH) {
- p->OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<PasswordForm>>());
- return;
- }
-
- std::vector<std::unique_ptr<PasswordForm>> result_form;
- if (result & RESULT_SAVED_MATCH) {
- result_form.push_back(base::MakeUnique<PasswordForm>(saved_match_));
- }
- if (result & RESULT_PSL_MATCH) {
- result_form.push_back(base::MakeUnique<PasswordForm>(psl_saved_match_));
- }
- p->OnGetPasswordStoreResults(std::move(result_form));
+ password_manager_.get(), &client_, client_.driver(), observed_form_,
+ base::MakeUnique<NiceMock<MockFormSaver>>(), &fake_form_fetcher_));
}
// Save saved_match() for observed_form() where |observed_form_data|,
@@ -465,28 +374,22 @@ class PasswordFormManagerTest : public testing::Test {
form.form_data = observed_form_data;
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), form,
- base::MakeUnique<NiceMock<MockFormSaver>>());
- std::vector<std::unique_ptr<PasswordForm>> result;
- result.push_back(CreateSavedMatch(false));
- result[0]->generation_upload_status = status;
- result[0]->times_used = times_used;
+ base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ PasswordForm match = CreateSavedMatch(false);
+ match.generation_upload_status = status;
+ match.times_used = times_used;
PasswordForm form_to_save(form);
form_to_save.preferred = true;
form_to_save.username_element = ASCIIToUTF16("observed-username-field");
- form_to_save.username_value = result[0]->username_value;
- form_to_save.password_value = result[0]->password_value;
+ form_to_save.username_value = match.username_value;
+ form_to_save.password_value = match.password_value;
- // When we're voting for an account creation form, we should also vote
- // for its username field.
- base::string16 username_vote =
- (field_type && *field_type == autofill::ACCOUNT_CREATION_PASSWORD)
- ? result[0]->username_element
- : base::string16();
-
- form_manager.OnGetPasswordStoreResults(std::move(result));
+ fetcher.SetNonFederated({&match}, 0u);
std::string expected_login_signature;
autofill::FormStructure observed_structure(observed_form_data);
autofill::FormStructure pending_structure(saved_match()->form_data);
@@ -496,11 +399,18 @@ class PasswordFormManagerTest : public testing::Test {
expected_login_signature = observed_structure.FormSignatureAsStr();
}
autofill::ServerFieldTypeSet expected_available_field_types;
- expected_available_field_types.insert(autofill::USERNAME);
- std::map<base::string16, autofill::ServerFieldType> expected_types;
+ FieldTypeMap expected_types;
expected_types[ASCIIToUTF16("full_name")] = autofill::UNKNOWN_TYPE;
- expected_types[saved_match()->username_element] =
- username_vote.empty() ? autofill::UNKNOWN_TYPE : autofill::USERNAME;
+
+ // When we're voting for an account creation form, we should also vote
+ // for its username field.
+ if (field_type && *field_type == autofill::ACCOUNT_CREATION_PASSWORD) {
+ expected_types[match.username_element] = autofill::USERNAME;
+ expected_available_field_types.insert(autofill::USERNAME);
+ } else {
+ expected_types[match.username_element] = autofill::UNKNOWN_TYPE;
+ }
+
if (field_type) {
expected_available_field_types.insert(*field_type);
expected_types[saved_match()->password_element] = *field_type;
@@ -526,26 +436,43 @@ class PasswordFormManagerTest : public testing::Test {
form_manager.ProvisionallySave(
form_to_save, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
form_manager.Save();
+ Mock::VerifyAndClearExpectations(
+ client()->mock_driver()->mock_autofill_download_manager());
}
// Test upload votes on change password forms. |field_type| is a vote that we
// expect to be uploaded.
- void ChangePasswordUploadTest(autofill::ServerFieldType field_type) {
+ void ChangePasswordUploadTest(autofill::ServerFieldType field_type,
+ bool has_confirmation_field) {
+ SCOPED_TRACE(testing::Message()
+ << "field_type=" << field_type
+ << " has_confirmation_field=" << has_confirmation_field);
+
// |observed_form_| should have |form_data| in order to be uploaded.
observed_form()->form_data = saved_match()->form_data;
// Turn |observed_form_| and into change password form.
observed_form()->new_password_element = ASCIIToUTF16("NewPasswd");
+ observed_form()->confirmation_password_element = ASCIIToUTF16("ConfPwd");
autofill::FormFieldData field;
field.label = ASCIIToUTF16("NewPasswd");
field.name = ASCIIToUTF16("NewPasswd");
field.form_control_type = "password";
observed_form()->form_data.fields.push_back(field);
+ autofill::FormFieldData empty_field;
+ observed_form()->form_data.fields.push_back(empty_field);
+ if (has_confirmation_field) {
+ field.label = ASCIIToUTF16("ConfPwd");
+ field.name = ASCIIToUTF16("ConfPwd");
+ field.form_control_type = "password";
+ observed_form()->form_data.fields.push_back(field);
+ }
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), *observed_form(),
- base::MakeUnique<NiceMock<MockFormSaver>>());
-
- SimulateMatchingPhase(&form_manager, RESULT_SAVED_MATCH);
+ base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
PasswordForm submitted_form(*observed_form());
@@ -572,14 +499,19 @@ class PasswordFormManagerTest : public testing::Test {
std::map<base::string16, autofill::ServerFieldType> expected_types;
expected_types[ASCIIToUTF16("full_name")] = autofill::UNKNOWN_TYPE;
- expected_types[observed_form_.username_element] = autofill::USERNAME;
+ expected_types[observed_form_.username_element] = autofill::UNKNOWN_TYPE;
expected_types[observed_form_.password_element] = autofill::PASSWORD;
expected_types[observed_form_.new_password_element] = field_type;
+ expected_types[base::string16()] = autofill::UNKNOWN_TYPE;
autofill::ServerFieldTypeSet expected_available_field_types;
- expected_available_field_types.insert(autofill::USERNAME);
expected_available_field_types.insert(autofill::PASSWORD);
expected_available_field_types.insert(field_type);
+ if (has_confirmation_field) {
+ expected_types[observed_form_.confirmation_password_element] =
+ autofill::CONFIRMATION_PASSWORD;
+ expected_available_field_types.insert(autofill::CONFIRMATION_PASSWORD);
+ }
std::string observed_form_signature =
autofill::FormStructure(observed_form()->form_data)
@@ -601,7 +533,7 @@ class PasswordFormManagerTest : public testing::Test {
form_manager.Update(*saved_match());
break;
case autofill::PROBABLY_NEW_PASSWORD:
- form_manager.OnNoInteractionOnUpdate();
+ form_manager.OnNoInteraction(true /* it is an update */);
break;
case autofill::NOT_NEW_PASSWORD:
form_manager.OnNopeUpdateClicked();
@@ -609,6 +541,8 @@ class PasswordFormManagerTest : public testing::Test {
default:
NOTREACHED();
}
+ Mock::VerifyAndClearExpectations(
+ client()->mock_driver()->mock_autofill_download_manager());
}
autofill::AutofillUploadContents::Field::PasswordGenerationType
@@ -639,13 +573,17 @@ class PasswordFormManagerTest : public testing::Test {
// The user types username and generates password on SignUp or change password
// form. The password generation might be triggered automatically or manually.
- // This function checks that correct vote is uploaded on server.
+ // This function checks that correct vote is uploaded on server. The vote must
+ // be uploaded regardless of the user's interaction with the prompt.
void GeneratedVoteUploadTest(bool is_manual_generation,
bool is_change_password_form,
- bool has_generated_password) {
+ bool has_generated_password,
+ SavePromptInteraction interaction) {
SCOPED_TRACE(testing::Message()
<< "is_manual_generation=" << is_manual_generation
- << " is_change_password_form=" << is_change_password_form);
+ << " is_change_password_form=" << is_change_password_form
+ << " has_generated_password=" << has_generated_password
+ << " interaction=" << interaction);
PasswordForm form(*observed_form());
form.form_data = saved_match()->form_data;
@@ -671,16 +609,18 @@ class PasswordFormManagerTest : public testing::Test {
saved_match()->password_value + ASCIIToUTF16("1");
}
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), form,
- base::MakeUnique<NiceMock<MockFormSaver>>());
+ base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
- std::vector<std::unique_ptr<PasswordForm>> result;
- form_manager.OnGetPasswordStoreResults(std::move(result));
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
autofill::ServerFieldTypeSet expected_available_field_types;
- expected_available_field_types.insert(autofill::USERNAME);
- expected_available_field_types.insert(autofill::PASSWORD);
+ // Don't send autofill votes if the user didn't press "Save" button.
+ if (interaction == SAVE)
+ expected_available_field_types.insert(autofill::PASSWORD);
form_manager.set_is_manual_generation(is_manual_generation);
base::string16 generation_element = is_change_password_form
@@ -711,25 +651,38 @@ class PasswordFormManagerTest : public testing::Test {
form_manager.ProvisionallySave(
submitted_form, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
- form_manager.Save();
+ switch (interaction) {
+ case SAVE:
+ form_manager.Save();
+ break;
+ case NEVER:
+ form_manager.OnNeverClicked();
+ break;
+ case NO_INTERACTION:
+ form_manager.OnNoInteraction(false /* not an update prompt*/);
+ break;
+ }
+ Mock::VerifyAndClearExpectations(
+ client()->mock_driver()->mock_autofill_download_manager());
}
PasswordForm* observed_form() { return &observed_form_; }
PasswordForm* saved_match() { return &saved_match_; }
PasswordForm* psl_saved_match() { return &psl_saved_match_; }
- std::unique_ptr<PasswordForm> CreateSavedMatch(bool blacklisted) {
- // Owned by the caller of this method.
- auto match = base::MakeUnique<PasswordForm>(saved_match_);
- match->blacklisted_by_user = blacklisted;
+ PasswordForm CreateSavedMatch(bool blacklisted) {
+ PasswordForm match = saved_match_;
+ match.blacklisted_by_user = blacklisted;
return match;
}
- TestPasswordManagerClient* client() { return client_.get(); }
+ TestPasswordManagerClient* client() { return &client_; }
PasswordManager* password_manager() { return password_manager_.get(); }
PasswordFormManager* form_manager() { return form_manager_.get(); }
+ FakeFormFetcher* fake_form_fetcher() { return &fake_form_fetcher_; }
+
// To spare typing for PasswordFormManager instances which need no driver.
const base::WeakPtr<PasswordManagerDriver> kNoDriver;
@@ -740,10 +693,10 @@ class PasswordFormManagerTest : public testing::Test {
PasswordForm observed_form_;
PasswordForm saved_match_;
PasswordForm psl_saved_match_;
- scoped_refptr<NiceMock<MockPasswordStore>> mock_store_;
- std::unique_ptr<TestPasswordManagerClient> client_;
+ TestPasswordManagerClient client_;
std::unique_ptr<PasswordManager> password_manager_;
std::unique_ptr<PasswordFormManager> form_manager_;
+ FakeFormFetcher fake_form_fetcher_;
};
class PasswordFormManagerFillOnAccountSelectTest
@@ -756,8 +709,9 @@ class PasswordFormManagerFillOnAccountSelectTest
base::test::ScopedFeatureList scoped_feature_list_;
};
+// Test provisionally saving a new login.
TEST_F(PasswordFormManagerTest, TestNewLogin) {
- SimulateMatchingPhase(form_manager(), RESULT_NO_MATCH);
+ fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
// User submits credentials for the observed form.
PasswordForm credentials = *observed_form();
@@ -788,26 +742,25 @@ TEST_F(PasswordFormManagerTest, TestNewLogin) {
EXPECT_TRUE(form_manager()->pending_credentials().new_password_value.empty());
}
+// Test provisionally saving a new login in presence of other saved logins.
TEST_F(PasswordFormManagerTest, TestAdditionalLogin) {
- // Now, suppose the user re-visits the site and wants to save an additional
- // login for the site with a new username. In this case, the matching phase
- // will yield the previously saved login.
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
- // Set up the new login.
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
+
base::string16 new_user = ASCIIToUTF16("newuser");
base::string16 new_pass = ASCIIToUTF16("newpass");
+ ASSERT_NE(new_user, saved_match()->username_value);
- PasswordForm credentials = *observed_form();
- credentials.username_value = new_user;
- credentials.password_value = new_pass;
- credentials.preferred = true;
+ PasswordForm new_login = *observed_form();
+ new_login.username_value = new_user;
+ new_login.password_value = new_pass;
+ new_login.preferred = true;
form_manager()->ProvisionallySave(
- credentials, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+ new_login, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
- // Again, the PasswordFormManager should know this is still a new login.
+ // The username value differs from the saved match, so this is a new login.
EXPECT_TRUE(form_manager()->IsNewLogin());
- // And make sure everything squares up again.
+
EXPECT_EQ(observed_form()->origin.spec(),
form_manager()->pending_credentials().origin.spec());
EXPECT_EQ(observed_form()->signon_realm,
@@ -820,16 +773,18 @@ TEST_F(PasswordFormManagerTest, TestAdditionalLogin) {
EXPECT_TRUE(form_manager()->pending_credentials().new_password_value.empty());
}
+// Test blacklisting in the presence of saved results.
TEST_F(PasswordFormManagerTest, TestBlacklist) {
saved_match()->origin = observed_form()->origin;
saved_match()->action = observed_form()->action;
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
- // Set up the new login.
- PasswordForm credentials = *observed_form();
- credentials.username_value = ASCIIToUTF16("newuser");
- credentials.password_value = ASCIIToUTF16("newpass");
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
+
+ PasswordForm new_login = *observed_form();
+ new_login.username_value = ASCIIToUTF16("newuser");
+ new_login.password_value = ASCIIToUTF16("newpass");
+ // Pretend Chrome detected a form submission with |new_login|.
form_manager()->ProvisionallySave(
- credentials, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+ new_login, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
EXPECT_TRUE(form_manager()->IsNewLogin());
EXPECT_EQ(observed_form()->origin.spec(),
@@ -839,64 +794,72 @@ TEST_F(PasswordFormManagerTest, TestBlacklist) {
const PasswordForm pending_form = form_manager()->pending_credentials();
PasswordForm actual_add_form;
+ // Now pretend the user wants to never save passwords on this origin. Chrome
+ // is supposed to only request blacklisting of a single form.
EXPECT_CALL(MockFormSaver::Get(form_manager()), PermanentlyBlacklist(_))
.WillOnce(SaveArgPointee<0>(&actual_add_form));
form_manager()->PermanentlyBlacklist();
EXPECT_EQ(pending_form, form_manager()->pending_credentials());
+ // The PasswordFormManager should have updated its knowledge of blacklisting
+ // without waiting for PasswordStore updates.
EXPECT_TRUE(form_manager()->IsBlacklisted());
EXPECT_THAT(form_manager()->blacklisted_matches(),
- ElementsAre(Pointee(actual_add_form)));
+ UnorderedElementsAre(Pointee(actual_add_form)));
}
+// Test that stored blacklisted forms are correctly evaluated for whether they
+// apply to the observed form.
TEST_F(PasswordFormManagerTest, TestBlacklistMatching) {
observed_form()->origin = GURL("http://accounts.google.com/a/LoginAuth");
observed_form()->action = GURL("http://accounts.google.com/a/Login");
observed_form()->signon_realm = "http://accounts.google.com";
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
+ base::MakeUnique<MockFormSaver>(), &fetcher);
- // Doesn't match because of PSL.
+ // Doesn't apply because it is just a PSL match of the observed form.
PasswordForm blacklisted_psl = *observed_form();
blacklisted_psl.signon_realm = "http://m.accounts.google.com";
blacklisted_psl.is_public_suffix_match = true;
blacklisted_psl.blacklisted_by_user = true;
- // Doesn't match because of different origin.
+ // Doesn't apply because of different origin.
PasswordForm blacklisted_not_match = *observed_form();
blacklisted_not_match.origin = GURL("http://google.com/a/LoginAuth");
blacklisted_not_match.blacklisted_by_user = true;
- // Doesn't match because of different username element and different page.
+ // Doesn't apply because of different username element and different page.
PasswordForm blacklisted_not_match2 = *observed_form();
blacklisted_not_match2.origin = GURL("http://accounts.google.com/a/Login123");
blacklisted_not_match2.username_element = ASCIIToUTF16("Element");
blacklisted_not_match2.blacklisted_by_user = true;
- // Doesn't match because of different PasswordForm::Scheme.
+ // Doesn't apply because of different PasswordForm::Scheme.
PasswordForm blacklisted_not_match3 = *observed_form();
blacklisted_not_match3.scheme = PasswordForm::SCHEME_BASIC;
- // Matches because of same element names, despite different page
+ // Applies because of same element names, despite different page
PasswordForm blacklisted_match = *observed_form();
blacklisted_match.origin = GURL("http://accounts.google.com/a/LoginAuth1234");
blacklisted_match.blacklisted_by_user = true;
- // Matches because of same page, despite different element names
+ // Applies because of same page, despite different element names
PasswordForm blacklisted_match2 = *observed_form();
blacklisted_match2.origin = GURL("http://accounts.google.com/a/LoginAuth");
blacklisted_match2.username_element = ASCIIToUTF16("Element");
blacklisted_match2.blacklisted_by_user = true;
- std::vector<std::unique_ptr<PasswordForm>> result;
- result.push_back(base::MakeUnique<PasswordForm>(blacklisted_psl));
- result.push_back(base::MakeUnique<PasswordForm>(blacklisted_not_match));
- result.push_back(base::MakeUnique<PasswordForm>(blacklisted_not_match2));
- result.push_back(base::MakeUnique<PasswordForm>(blacklisted_not_match3));
- result.push_back(base::MakeUnique<PasswordForm>(blacklisted_match));
- result.push_back(base::MakeUnique<PasswordForm>(blacklisted_match2));
- result.push_back(base::MakeUnique<PasswordForm>(*saved_match()));
- form_manager.OnGetPasswordStoreResults(std::move(result));
+ std::vector<const PasswordForm*> matches = {&blacklisted_psl,
+ &blacklisted_not_match,
+ &blacklisted_not_match2,
+ &blacklisted_not_match3,
+ &blacklisted_match,
+ &blacklisted_match2,
+ saved_match()};
+ fetcher.SetNonFederated(matches, 0u);
+
EXPECT_TRUE(form_manager.IsBlacklisted());
EXPECT_THAT(form_manager.blacklisted_matches(),
UnorderedElementsAre(Pointee(blacklisted_match),
@@ -905,9 +868,9 @@ TEST_F(PasswordFormManagerTest, TestBlacklistMatching) {
EXPECT_EQ(*saved_match(), *form_manager.preferred_match());
}
+// Test that even in the presence of blacklisted matches, the non-blacklisted
+// ones are still autofilled.
TEST_F(PasswordFormManagerTest, AutofillBlacklisted) {
- // Blacklisted best matches credentials should not be autofilled, but the
- // non-blacklisted should.
PasswordForm saved_form = *observed_form();
saved_form.username_value = ASCIIToUTF16("user");
saved_form.password_value = ASCIIToUTF16("pass");
@@ -916,15 +879,11 @@ TEST_F(PasswordFormManagerTest, AutofillBlacklisted) {
blacklisted.blacklisted_by_user = true;
blacklisted.username_value.clear();
- std::vector<std::unique_ptr<PasswordForm>> result;
- result.push_back(base::MakeUnique<PasswordForm>(saved_form));
- result.push_back(base::MakeUnique<PasswordForm>(blacklisted));
-
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
- form_manager()->OnGetPasswordStoreResults(std::move(result));
+ fake_form_fetcher()->SetNonFederated({&saved_form, &blacklisted}, 0u);
EXPECT_EQ(1u, form_manager()->blacklisted_matches().size());
EXPECT_TRUE(form_manager()->IsBlacklisted());
EXPECT_EQ(1u, form_manager()->best_matches().size());
@@ -938,7 +897,7 @@ TEST_F(PasswordFormManagerTest, AutofillBlacklisted) {
TEST_F(PasswordFormManagerTest,
OverriddenPSLMatchedCredentialsNotMarkedAsPSLMatched) {
// The suggestion needs to be PSL-matched.
- SimulateMatchingPhase(form_manager(), RESULT_PSL_MATCH);
+ fake_form_fetcher()->SetNonFederated({psl_saved_match()}, 0u);
// User modifies the suggested password and submits the form.
PasswordForm credentials(*observed_form());
@@ -952,10 +911,12 @@ TEST_F(PasswordFormManagerTest,
EXPECT_FALSE(form_manager()->IsPendingCredentialsPublicSuffixMatch());
}
+// Test that if a PSL-matched suggestion is saved on a new origin, its metadata
+// are correctly updated.
TEST_F(PasswordFormManagerTest, PSLMatchedCredentialsMetadataUpdated) {
- // The suggestion needs to be PSL-matched.
- saved_match()->is_public_suffix_match = true;
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ PasswordForm psl_suggestion = *saved_match();
+ psl_suggestion.is_public_suffix_match = true;
+ fake_form_fetcher()->SetNonFederated({&psl_suggestion}, 0u);
PasswordForm submitted_form(*observed_form());
submitted_form.preferred = true;
@@ -987,21 +948,23 @@ TEST_F(PasswordFormManagerTest, PSLMatchedCredentialsMetadataUpdated) {
EXPECT_EQ(expected_saved_form, actual_saved_form);
}
+// Test that when the submitted form contains a "new-password" field, then the
+// password value is taken from there.
TEST_F(PasswordFormManagerTest, TestNewLoginFromNewPasswordElement) {
- // Add a new password field to the test form. The PasswordFormManager should
- // save the password from this field, instead of the current password field.
observed_form()->new_password_element = ASCIIToUTF16("NewPasswd");
observed_form()->username_marked_by_site = true;
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
- SimulateMatchingPhase(&form_manager, RESULT_NO_MATCH);
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
// User enters current and new credentials to the observed form.
PasswordForm credentials(*observed_form());
credentials.username_value = saved_match()->username_value;
- credentials.password_value = saved_match()->password_value;
+ credentials.password_value = ASCIIToUTF16("oldpassword");
credentials.new_password_value = ASCIIToUTF16("newpassword");
credentials.preferred = true;
form_manager.ProvisionallySave(
@@ -1029,7 +992,7 @@ TEST_F(PasswordFormManagerTest, TestNewLoginFromNewPasswordElement) {
}
TEST_F(PasswordFormManagerTest, TestUpdatePassword) {
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
// User submits credentials for the observed form using a username previously
// stored, but a new password. Note that the observed form may have different
@@ -1074,10 +1037,12 @@ TEST_F(PasswordFormManagerTest, TestUpdatePasswordFromNewPasswordElement) {
// verify in the end that this did not happen.
saved_match()->submit_element.clear();
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
- SimulateMatchingPhase(&form_manager, RESULT_SAVED_MATCH);
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
PasswordForm credentials(*observed_form());
@@ -1107,7 +1072,6 @@ TEST_F(PasswordFormManagerTest, TestUpdatePasswordFromNewPasswordElement) {
.WillOnce(testing::SaveArg<0>(&new_credentials));
form_manager.Save();
- Mock::VerifyAndClearExpectations(mock_store());
// No meta-information should be updated, only the password.
EXPECT_EQ(credentials.new_password_value, new_credentials.password_value);
@@ -1116,50 +1080,35 @@ TEST_F(PasswordFormManagerTest, TestUpdatePasswordFromNewPasswordElement) {
EXPECT_EQ(saved_match()->submit_element, new_credentials.submit_element);
}
+// Test that saved results are not ignored if they differ in paths for action or
+// origin.
TEST_F(PasswordFormManagerTest, TestIgnoreResult_Paths) {
PasswordForm observed(*observed_form());
observed.origin = GURL("https://accounts.google.com/a/LoginAuth");
observed.action = GURL("https://accounts.google.com/a/Login");
observed.signon_realm = "https://accounts.google.com";
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), observed,
- base::MakeUnique<MockFormSaver>());
+ base::MakeUnique<MockFormSaver>(), &fetcher);
PasswordForm saved_form = observed;
saved_form.origin = GURL("https://accounts.google.com/a/OtherLoginAuth");
saved_form.action = GURL("https://accounts.google.com/a/OtherLogin");
- std::vector<std::unique_ptr<PasswordForm>> result;
- result.push_back(base::MakeUnique<PasswordForm>(saved_form));
- form_manager.OnGetPasswordStoreResults(std::move(result));
+ fetcher.SetNonFederated({&saved_form}, 0u);
// Different paths for action / origin are okay.
- EXPECT_FALSE(form_manager.best_matches().empty());
-}
-
-TEST_F(PasswordFormManagerTest, TestIgnoreResult_IgnoredCredentials) {
- PasswordForm observed(*observed_form());
- observed.origin = GURL("https://accounts.google.com/a/LoginAuth");
- observed.action = GURL("https://accounts.google.com/a/Login");
- observed.signon_realm = "https://accounts.google.com";
-
- PasswordFormManager form_manager(password_manager(), client(),
- client()->driver(), observed,
- base::MakeUnique<MockFormSaver>());
- client()->FilterAllResults();
-
- PasswordForm saved_form = observed;
- std::vector<std::unique_ptr<PasswordForm>> result;
- result.push_back(base::MakeUnique<PasswordForm>(saved_form));
- form_manager.OnGetPasswordStoreResults(std::move(result));
-
- // Results should be ignored if the client requests it.
- EXPECT_TRUE(form_manager.best_matches().empty());
+ EXPECT_EQ(1u, form_manager.best_matches().size());
+ EXPECT_EQ(*form_manager.best_matches().begin()->second, saved_form);
}
+// Test that saved empty action URL is updated with the submitted action URL.
TEST_F(PasswordFormManagerTest, TestEmptyAction) {
saved_match()->action = GURL();
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
+
// User logs in with the autofilled username / password from saved_match.
PasswordForm login = *observed_form();
login.username_value = saved_match()->username_value;
@@ -1167,15 +1116,18 @@ TEST_F(PasswordFormManagerTest, TestEmptyAction) {
form_manager()->ProvisionallySave(
login, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
EXPECT_FALSE(form_manager()->IsNewLogin());
- // We bless our saved PasswordForm entry with the action URL of the
+ // Chrome updates the saved PasswordForm entry with the action URL of the
// observed form.
EXPECT_EQ(observed_form()->action,
form_manager()->pending_credentials().action);
}
TEST_F(PasswordFormManagerTest, TestUpdateAction) {
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ saved_match()->action = GURL("http://accounts.google.com/a/ServiceLogin");
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
+
// User logs in with the autofilled username / password from saved_match.
+ observed_form()->action = GURL("http://accounts.google.com/a/Login");
PasswordForm login = *observed_form();
login.username_value = saved_match()->username_value;
login.password_value = saved_match()->password_value;
@@ -1183,37 +1135,40 @@ TEST_F(PasswordFormManagerTest, TestUpdateAction) {
form_manager()->ProvisionallySave(
login, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
EXPECT_FALSE(form_manager()->IsNewLogin());
- // The observed action URL is different from the previously saved one, and
- // is the same as the one that would be submitted on successful login.
- EXPECT_NE(observed_form()->action, saved_match()->action);
+ // The observed action URL is different from the previously saved one. Chrome
+ // should update the store by setting the pending credential's action URL to
+ // be that of the currently observed form.
EXPECT_EQ(observed_form()->action,
form_manager()->pending_credentials().action);
}
TEST_F(PasswordFormManagerTest, TestDynamicAction) {
- SimulateMatchingPhase(form_manager(), RESULT_NO_MATCH);
+ fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
+
+ observed_form()->action = GURL("http://accounts.google.com/a/Login");
PasswordForm login(*observed_form());
// The submitted action URL is different from the one observed on page load.
- GURL new_action = GURL("http://www.google.com/new_action");
- login.action = new_action;
+ login.action = GURL("http://www.google.com/new_action");
form_manager()->ProvisionallySave(
login, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
EXPECT_TRUE(form_manager()->IsNewLogin());
// Check that the provisionally saved action URL is the same as the submitted
// action URL, not the one observed on page load.
- EXPECT_EQ(new_action, form_manager()->pending_credentials().action);
+ EXPECT_EQ(login.action, form_manager()->pending_credentials().action);
}
+// Test that if the saved match has other possible usernames stored, and the
+// user chooses the main one, then the other possible usernames are dropped on
+// update.
TEST_F(PasswordFormManagerTest, TestAlternateUsername_NoChange) {
EXPECT_CALL(*client()->mock_driver(), AllowPasswordGenerationForForm(_));
PasswordForm saved_form = *saved_match();
- ASSERT_FALSE(saved_form.other_possible_usernames.empty());
+ saved_form.other_possible_usernames.push_back(
+ ASCIIToUTF16("other_possible@gmail.com"));
- std::vector<std::unique_ptr<PasswordForm>> result;
- result.push_back(base::MakeUnique<PasswordForm>(saved_form));
- form_manager()->OnGetPasswordStoreResults(std::move(result));
+ fake_form_fetcher()->SetNonFederated({&saved_form}, 0u);
// The saved match has the right username already.
PasswordForm login(*observed_form());
@@ -1239,22 +1194,24 @@ TEST_F(PasswordFormManagerTest, TestAlternateUsername_NoChange) {
EXPECT_TRUE(saved_result.other_possible_usernames.empty());
}
+// Test that if the saved match has other possible usernames stored, and the
+// user chooses an alternative one, then the other possible usernames are
+// dropped on update, but the main username is changed to the one chosen by the
+// user.
TEST_F(PasswordFormManagerTest, TestAlternateUsername_OtherUsername) {
- // This time use an alternate username.
-
EXPECT_CALL(*client()->mock_driver(), AllowPasswordGenerationForForm(_));
+ const base::string16 kOtherUsername =
+ ASCIIToUTF16("other_possible@gmail.com");
PasswordForm saved_form = *saved_match();
- ASSERT_FALSE(saved_form.other_possible_usernames.empty());
+ saved_form.other_possible_usernames.push_back(kOtherUsername);
- std::vector<std::unique_ptr<autofill::PasswordForm>> result;
- result.push_back(base::MakeUnique<PasswordForm>(saved_form));
- form_manager()->OnGetPasswordStoreResults(std::move(result));
+ fake_form_fetcher()->SetNonFederated({&saved_form}, 0u);
- // The saved match has the right username already.
+ // The user chooses an alternative username.
PasswordForm login(*observed_form());
login.preferred = true;
- login.username_value = saved_match()->other_possible_usernames[0];
+ login.username_value = kOtherUsername;
login.password_value = saved_match()->password_value;
form_manager()->ProvisionallySave(
@@ -1272,8 +1229,7 @@ TEST_F(PasswordFormManagerTest, TestAlternateUsername_OtherUsername) {
// |other_possible_usernames| should also be empty, but username_value should
// be changed to match |new_username|.
- EXPECT_EQ(saved_match()->other_possible_usernames[0],
- saved_result.username_value);
+ EXPECT_EQ(kOtherUsername, saved_result.username_value);
EXPECT_TRUE(saved_result.other_possible_usernames.empty());
}
@@ -1281,39 +1237,35 @@ TEST_F(PasswordFormManagerTest, TestSendNotBlacklistedMessage_NoCredentials) {
// First time sign-up attempt. Password store does not contain matching
// credentials. AllowPasswordGenerationForForm should be called to send the
// "not blacklisted" message.
- EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_))
- .Times(1);
- form_manager()->OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<PasswordForm>>());
+ EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
+ fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
}
TEST_F(PasswordFormManagerTest, TestSendNotBlacklistedMessage_Credentials) {
// Signing up on a previously visited site. Credentials are found in the
// password store, and are not blacklisted. AllowPasswordGenerationForForm
// should be called to send the "not blacklisted" message.
- EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_))
- .Times(1);
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(CreateSavedMatch(false));
- form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
+ EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
+ PasswordForm simulated_result = CreateSavedMatch(false);
+ fake_form_fetcher()->SetNonFederated({&simulated_result}, 0u);
}
TEST_F(PasswordFormManagerTest,
TestSendNotBlacklistedMessage_DroppedCredentials) {
- // There are cases, such as when a form is explicitly for creating a new
+ // There are cases, such as when a form is made explicitly for creating a new
// password, where we may ignore saved credentials. Make sure that we still
// allow generation in that case.
PasswordForm signup_form(*observed_form());
signup_form.new_password_element = base::ASCIIToUTF16("new_password_field");
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), signup_form,
- base::MakeUnique<MockFormSaver>());
- EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_))
- .Times(1);
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(CreateSavedMatch(false));
- form_manager.OnGetPasswordStoreResults(std::move(simulated_results));
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
+ PasswordForm simulated_result = CreateSavedMatch(false);
+ fetcher.SetNonFederated({&simulated_result}, 0u);
}
TEST_F(PasswordFormManagerTest,
@@ -1322,44 +1274,48 @@ TEST_F(PasswordFormManagerTest,
// password store, but they are blacklisted. AllowPasswordGenerationForForm
// is still called.
EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(CreateSavedMatch(true));
- form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
+ PasswordForm simulated_result = CreateSavedMatch(true);
+ fake_form_fetcher()->SetNonFederated({&simulated_result}, 0u);
}
-TEST_F(PasswordFormManagerTest, TestBestCredentialsByEachUsernameAreIncluded) {
- // Simulate having several matches, with 3 different usernames. Some of the
- // matches are PSL matches. One match for each username should be chosen.
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
+// Test that exactly one match for each username is chosen as a best match, even
+// though it is a PSL match.
+TEST_F(PasswordFormManagerTest, TestBestCredentialsForEachUsernameAreIncluded) {
// Add a best scoring match. It should be in |best_matches| and chosen as a
// prefferred match.
- simulated_results.push_back(base::MakeUnique<PasswordForm>(*saved_match()));
+ PasswordForm best_scoring = *saved_match();
+
// Add a match saved on another form, it has lower score. It should not be in
// |best_matches|.
- simulated_results.push_back(base::MakeUnique<PasswordForm>(*saved_match()));
- simulated_results[1]->password_element = ASCIIToUTF16("signup_password");
- simulated_results[1]->username_element = ASCIIToUTF16("signup_username");
+ PasswordForm other_form = *saved_match();
+ other_form.password_element = ASCIIToUTF16("signup_password");
+ other_form.username_element = ASCIIToUTF16("signup_username");
+
// Add a match saved on another form with a different username. It should be
// in |best_matches|.
- simulated_results.push_back(base::MakeUnique<PasswordForm>(*saved_match()));
- auto username1 = simulated_results[0]->username_value + ASCIIToUTF16("1");
- simulated_results[2]->username_value = username1;
- simulated_results[2]->password_element = ASCIIToUTF16("signup_password");
- simulated_results[2]->username_element = ASCIIToUTF16("signup_username");
+ PasswordForm other_username = other_form;
+ const base::string16 kUsername1 =
+ other_username.username_value + ASCIIToUTF16("1");
+ other_username.username_value = kUsername1;
+
// Add a PSL match, it should not be in |best_matches|.
- simulated_results.push_back(
- base::MakeUnique<PasswordForm>(*psl_saved_match()));
+ PasswordForm psl_match = *psl_saved_match();
+
// Add a PSL match with a different username. It should be in |best_matches|.
- simulated_results.push_back(
- base::MakeUnique<PasswordForm>(*psl_saved_match()));
- auto username2 = simulated_results[0]->username_value + ASCIIToUTF16("2");
- simulated_results[4]->username_value = username2;
+ PasswordForm psl_match_other = psl_match;
+ const base::string16 kUsername2 =
+ psl_match_other.username_value + ASCIIToUTF16("2");
+ psl_match_other.username_value = kUsername2;
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
- form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
+ fake_form_fetcher()->SetNonFederated(
+ {&best_scoring, &other_form, &other_username, &psl_match,
+ &psl_match_other},
+ 0u);
+
const std::map<base::string16, const PasswordForm*>& best_matches =
form_manager()->best_matches();
EXPECT_EQ(3u, best_matches.size());
@@ -1367,8 +1323,8 @@ TEST_F(PasswordFormManagerTest, TestBestCredentialsByEachUsernameAreIncluded) {
best_matches.find(saved_match()->username_value));
EXPECT_EQ(*saved_match(),
*best_matches.find(saved_match()->username_value)->second);
- EXPECT_NE(best_matches.end(), best_matches.find(username1));
- EXPECT_NE(best_matches.end(), best_matches.find(username2));
+ EXPECT_NE(best_matches.end(), best_matches.find(kUsername1));
+ EXPECT_NE(best_matches.end(), best_matches.find(kUsername2));
EXPECT_EQ(*saved_match(), *form_manager()->preferred_match());
EXPECT_EQ(2u, fill_data.additional_logins.size());
@@ -1377,8 +1333,7 @@ TEST_F(PasswordFormManagerTest, TestBestCredentialsByEachUsernameAreIncluded) {
TEST_F(PasswordFormManagerTest, TestSanitizePossibleUsernames) {
const base::string16 kUsernameOther = ASCIIToUTF16("other username");
- form_manager()->OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<PasswordForm>>());
+ fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm credentials(*observed_form());
credentials.other_possible_usernames.push_back(ASCIIToUTF16("543-43-1234"));
@@ -1408,8 +1363,7 @@ TEST_F(PasswordFormManagerTest, TestSanitizePossibleUsernamesDuplicates) {
const base::string16 kUsernameDuplicate = ASCIIToUTF16("duplicate");
const base::string16 kUsernameRandom = ASCIIToUTF16("random");
- form_manager()->OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<PasswordForm>>());
+ fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm credentials(*observed_form());
credentials.other_possible_usernames.push_back(ASCIIToUTF16("511-32-9830"));
@@ -1436,8 +1390,9 @@ TEST_F(PasswordFormManagerTest, TestSanitizePossibleUsernamesDuplicates) {
UnorderedElementsAre(kUsernameDuplicate, kUsernameRandom));
}
+// Test that if metadata stored with a form in PasswordStore are incomplete,
+// they are updated upon the next encounter.
TEST_F(PasswordFormManagerTest, TestUpdateIncompleteCredentials) {
- // We've found this form on a website:
PasswordForm encountered_form;
encountered_form.origin = GURL("http://accounts.google.com/LoginAuth");
encountered_form.signon_realm = "http://accounts.google.com/";
@@ -1448,36 +1403,35 @@ TEST_F(PasswordFormManagerTest, TestUpdateIncompleteCredentials) {
EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), encountered_form,
- base::MakeUnique<MockFormSaver>());
+ base::MakeUnique<MockFormSaver>(), &fetcher);
- // Password store only has these incomplete credentials.
- std::unique_ptr<PasswordForm> incomplete_form(new PasswordForm());
- incomplete_form->origin = GURL("http://accounts.google.com/LoginAuth");
- incomplete_form->signon_realm = "http://accounts.google.com/";
- incomplete_form->password_value = ASCIIToUTF16("my_password");
- incomplete_form->username_value = ASCIIToUTF16("my_username");
- incomplete_form->preferred = true;
- incomplete_form->scheme = PasswordForm::SCHEME_HTML;
+ PasswordForm incomplete_form;
+ incomplete_form.origin = GURL("http://accounts.google.com/LoginAuth");
+ incomplete_form.signon_realm = "http://accounts.google.com/";
+ incomplete_form.password_value = ASCIIToUTF16("my_password");
+ incomplete_form.username_value = ASCIIToUTF16("my_username");
+ incomplete_form.preferred = true;
+ incomplete_form.scheme = PasswordForm::SCHEME_HTML;
// We expect to see this form eventually sent to the Password store. It
// has password/username values from the store and 'username_element',
// 'password_element', 'submit_element' and 'action' fields copied from
// the encountered form.
- PasswordForm complete_form(*incomplete_form);
+ PasswordForm complete_form(incomplete_form);
complete_form.action = encountered_form.action;
complete_form.password_element = encountered_form.password_element;
complete_form.username_element = encountered_form.username_element;
complete_form.submit_element = encountered_form.submit_element;
- PasswordForm obsolete_form(*incomplete_form);
+ PasswordForm obsolete_form(incomplete_form);
obsolete_form.action = encountered_form.action;
// Feed the incomplete credentials to the manager.
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(std::move(incomplete_form));
- form_manager.OnGetPasswordStoreResults(std::move(simulated_results));
+ fetcher.SetNonFederated({&incomplete_form}, 0u);
form_manager.ProvisionallySave(
complete_form, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
@@ -1491,32 +1445,32 @@ TEST_F(PasswordFormManagerTest, TestUpdateIncompleteCredentials) {
form_manager.Save();
}
+// Test that public-suffix-matched credentials score lower than same-origin
+// ones.
TEST_F(PasswordFormManagerTest, TestScoringPublicSuffixMatch) {
EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
- // Simulate having two matches for this form, first comes from different
- // signon realm, but reports the same origin and action as matched form.
- // Second candidate has the same signon realm as the form, but has a different
- // origin and action. Public suffix match is the most important criterion so
- // the second candidate should be selected.
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(CreateSavedMatch(false));
- simulated_results.push_back(CreateSavedMatch(false));
- simulated_results[0]->is_public_suffix_match = true;
- simulated_results[1]->origin =
- GURL("http://accounts.google.com/a/ServiceLoginAuth2");
- simulated_results[1]->action =
- GURL("http://accounts.google.com/a/ServiceLogin2");
+ PasswordForm base_match = CreateSavedMatch(false);
+ base_match.origin = GURL("http://accounts.google.com/a/ServiceLoginAuth");
+ base_match.action = GURL("http://accounts.google.com/a/ServiceLogin");
+
+ PasswordForm psl_match = base_match;
+ psl_match.is_public_suffix_match = true;
+
+ // Change origin and action URLs to decrease the score.
+ PasswordForm same_origin_match = base_match;
+ psl_match.origin = GURL("http://accounts.google.com/a/ServiceLoginAuth2");
+ psl_match.action = GURL("http://accounts.google.com/a/ServiceLogin2");
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
- form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
+ fake_form_fetcher()->SetNonFederated({&psl_match, &same_origin_match}, 0u);
EXPECT_TRUE(fill_data.additional_logins.empty());
EXPECT_EQ(1u, form_manager()->best_matches().size());
- EXPECT_TRUE(
- !form_manager()->best_matches().begin()->second->is_public_suffix_match);
+ EXPECT_FALSE(
+ form_manager()->best_matches().begin()->second->is_public_suffix_match);
}
TEST_F(PasswordFormManagerTest, AndroidCredentialsAreAutofilled) {
@@ -1538,9 +1492,7 @@ TEST_F(PasswordFormManagerTest, AndroidCredentialsAreAutofilled) {
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(base::MakeUnique<PasswordForm>(android_login));
- form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
+ fake_form_fetcher()->SetNonFederated({&android_login}, 0u);
EXPECT_TRUE(fill_data.additional_logins.empty());
EXPECT_FALSE(fill_data.wait_for_username);
EXPECT_EQ(1u, form_manager()->best_matches().size());
@@ -1560,7 +1512,6 @@ TEST_F(PasswordFormManagerTest, AndroidCredentialsAreAutofilled) {
EXPECT_CALL(MockFormSaver::Get(form_manager()), Update(_, _, _, nullptr))
.WillOnce(testing::SaveArg<0>(&updated_credential));
form_manager()->Save();
- Mock::VerifyAndClearExpectations(mock_store());
EXPECT_EQ(android_login.username_value, updated_credential.username_value);
EXPECT_EQ(android_login.password_value, updated_credential.password_value);
@@ -1587,30 +1538,29 @@ TEST_F(PasswordFormManagerTest, AndroidCredentialsAreProtected) {
// from Android: the first has the same username as the web-based credential,
// so it should be suppressed, but the second has a different username, so it
// should be shown.
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(CreateSavedMatch(false));
- simulated_results[0]->username_value = ASCIIToUTF16(kTestUsername1);
- simulated_results[0]->password_value = ASCIIToUTF16(kTestWebPassword);
- simulated_results.push_back(base::MakeUnique<PasswordForm>());
- simulated_results[1]->signon_realm = "android://hash@com.google.android";
- simulated_results[1]->origin = GURL("android://hash@com.google.android/");
- simulated_results[1]->username_value = ASCIIToUTF16(kTestUsername1);
- simulated_results[1]->password_value = ASCIIToUTF16(kTestAndroidPassword1);
- simulated_results.push_back(
- base::MakeUnique<PasswordForm>(*simulated_results[1]));
- simulated_results[2]->username_value = ASCIIToUTF16(kTestUsername2);
- simulated_results[2]->password_value = ASCIIToUTF16(kTestAndroidPassword2);
+ PasswordForm website_login = CreateSavedMatch(false);
+ website_login.username_value = ASCIIToUTF16(kTestUsername1);
+ website_login.password_value = ASCIIToUTF16(kTestWebPassword);
+
+ PasswordForm android_same;
+ android_same.signon_realm = "android://hash@com.google.android";
+ android_same.origin = GURL("android://hash@com.google.android/");
+ android_same.username_value = ASCIIToUTF16(kTestUsername1);
+ android_same.password_value = ASCIIToUTF16(kTestAndroidPassword1);
+
+ PasswordForm android_other = android_same;
+ android_other.username_value = ASCIIToUTF16(kTestUsername2);
+ android_other.password_value = ASCIIToUTF16(kTestAndroidPassword2);
std::vector<std::unique_ptr<PasswordForm>> expected_matches;
- expected_matches.push_back(
- base::MakeUnique<PasswordForm>(*simulated_results[0]));
- expected_matches.push_back(
- base::MakeUnique<PasswordForm>(*simulated_results[2]));
+ expected_matches.push_back(base::MakeUnique<PasswordForm>(website_login));
+ expected_matches.push_back(base::MakeUnique<PasswordForm>(android_other));
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
- form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
+ fake_form_fetcher()->SetNonFederated(
+ {&website_login, &android_same, &android_other}, 0u);
EXPECT_FALSE(fill_data.wait_for_username);
EXPECT_EQ(1u, fill_data.additional_logins.size());
@@ -1634,9 +1584,9 @@ TEST_F(PasswordFormManagerTest, InvalidActionURLsDoNotMatch) {
PasswordFormManager::RESULT_ACTION_MATCH);
// Then when the observed form has an invalid URL:
PasswordForm valid_action_form(*observed_form());
- PasswordFormManager invalid_manager(password_manager(), client(),
- client()->driver(), invalid_action_form,
- base::MakeUnique<MockFormSaver>());
+ PasswordFormManager invalid_manager(
+ password_manager(), client(), client()->driver(), invalid_action_form,
+ base::MakeUnique<MockFormSaver>(), fake_form_fetcher());
EXPECT_EQ(0,
invalid_manager.DoesManage(valid_action_form) &
PasswordFormManager::RESULT_ACTION_MATCH);
@@ -1654,7 +1604,7 @@ TEST_F(PasswordFormManagerTest, EmptyActionURLsDoNotMatchNonEmpty) {
PasswordForm valid_action_form(*observed_form());
PasswordFormManager empty_action_manager(
password_manager(), client(), client()->driver(), empty_action_form,
- base::MakeUnique<MockFormSaver>());
+ base::MakeUnique<MockFormSaver>(), fake_form_fetcher());
EXPECT_EQ(0,
empty_action_manager.DoesManage(valid_action_form) &
PasswordFormManager::RESULT_ACTION_MATCH);
@@ -1669,9 +1619,9 @@ TEST_F(PasswordFormManagerTest, NonHTMLFormsDoNotMatchHTMLForms) {
// The other way round: observing a non-HTML form, don't match a HTML form.
PasswordForm html_form(*observed_form());
- PasswordFormManager non_html_manager(password_manager(), client(), kNoDriver,
- non_html_form,
- base::MakeUnique<MockFormSaver>());
+ PasswordFormManager non_html_manager(
+ password_manager(), client(), kNoDriver, non_html_form,
+ base::MakeUnique<MockFormSaver>(), fake_form_fetcher());
EXPECT_EQ(0, non_html_manager.DoesManage(html_form) &
PasswordFormManager::RESULT_HTML_ATTRIBUTES_MATCH);
}
@@ -1706,9 +1656,9 @@ TEST_F(PasswordFormManagerTest,
PasswordForm secure_observed_form(*observed_form());
secure_observed_form.origin = GURL("https://accounts.google.com/a/LoginAuth");
- PasswordFormManager secure_manager(password_manager(), client(),
- client()->driver(), secure_observed_form,
- base::MakeUnique<MockFormSaver>());
+ PasswordFormManager secure_manager(
+ password_manager(), client(), client()->driver(), secure_observed_form,
+ base::MakeUnique<MockFormSaver>(), fake_form_fetcher());
// 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) &
@@ -1761,11 +1711,7 @@ TEST_F(PasswordFormManagerTest, CorrectlyUpdatePasswordsWithSameUsername) {
third.password_value = ASCIIToUTF16("second");
third.preferred = false;
- std::vector<std::unique_ptr<PasswordForm>> result;
- result.push_back(base::MakeUnique<PasswordForm>(first));
- result.push_back(base::MakeUnique<PasswordForm>(second));
- result.push_back(base::MakeUnique<PasswordForm>(third));
- form_manager()->OnGetPasswordStoreResults(std::move(result));
+ fake_form_fetcher()->SetNonFederated({&first, &second, &third}, 0u);
// |first| scored slightly higher.
EXPECT_EQ(ASCIIToUTF16("first"),
@@ -1805,10 +1751,12 @@ TEST_F(PasswordFormManagerTest, CorrectlyUpdatePasswordsWithSameUsername) {
TEST_F(PasswordFormManagerTest, UploadFormData_NewPassword) {
// For newly saved passwords, upload a password vote for autofill::PASSWORD.
// Don't vote for the username field yet.
- PasswordFormManager form_manager(password_manager(), client(),
- client()->driver(), *saved_match(),
- base::MakeUnique<NiceMock<MockFormSaver>>());
- SimulateMatchingPhase(&form_manager, RESULT_NO_MATCH);
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
+ PasswordFormManager form_manager(
+ password_manager(), client(), client()->driver(), *saved_match(),
+ base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm form_to_save(*saved_match());
form_to_save.preferred = true;
@@ -1816,7 +1764,6 @@ TEST_F(PasswordFormManagerTest, UploadFormData_NewPassword) {
form_to_save.password_value = ASCIIToUTF16("1234");
autofill::ServerFieldTypeSet expected_available_field_types;
- expected_available_field_types.insert(autofill::USERNAME);
expected_available_field_types.insert(autofill::PASSWORD);
EXPECT_CALL(
*client()->mock_driver()->mock_autofill_download_manager(),
@@ -1824,46 +1771,48 @@ TEST_F(PasswordFormManagerTest, UploadFormData_NewPassword) {
form_manager.ProvisionallySave(
form_to_save, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
form_manager.Save();
- Mock::VerifyAndClearExpectations(&form_manager);
+}
+TEST_F(PasswordFormManagerTest, UploadFormData_NewPassword_Blacklist) {
// Do not upload a vote if the user is blacklisting the form.
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager blacklist_form_manager(
password_manager(), client(), client()->driver(), *saved_match(),
- base::MakeUnique<NiceMock<MockFormSaver>>());
- SimulateMatchingPhase(&blacklist_form_manager, RESULT_NO_MATCH);
+ base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
- expected_available_field_types.clear();
+ autofill::ServerFieldTypeSet expected_available_field_types;
expected_available_field_types.insert(autofill::USERNAME);
expected_available_field_types.insert(autofill::PASSWORD);
EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
StartUploadRequest(_, _, expected_available_field_types, _, true))
.Times(0);
blacklist_form_manager.PermanentlyBlacklist();
- Mock::VerifyAndClearExpectations(&blacklist_form_manager);
}
TEST_F(PasswordFormManagerTest, UploadPasswordForm) {
- autofill::FormData form_data;
+ autofill::FormData observed_form_data;
autofill::FormFieldData field;
field.label = ASCIIToUTF16("Email");
- field.name = ASCIIToUTF16("Email");
+ field.name = ASCIIToUTF16("observed-username-field");
field.form_control_type = "text";
- form_data.fields.push_back(field);
+ observed_form_data.fields.push_back(field);
field.label = ASCIIToUTF16("password");
field.name = ASCIIToUTF16("password");
field.form_control_type = "password";
- form_data.fields.push_back(field);
+ observed_form_data.fields.push_back(field);
// Form data is different than saved form data, account creation signal should
// be sent.
autofill::ServerFieldType field_type = autofill::ACCOUNT_CREATION_PASSWORD;
- AccountCreationUploadTest(form_data, 0, PasswordForm::NO_SIGNAL_SENT,
+ AccountCreationUploadTest(observed_form_data, 0, PasswordForm::NO_SIGNAL_SENT,
&field_type);
// Non-zero times used will not upload since we only upload a positive signal
// at most once.
- AccountCreationUploadTest(form_data, 1, PasswordForm::NO_SIGNAL_SENT,
+ AccountCreationUploadTest(observed_form_data, 1, PasswordForm::NO_SIGNAL_SENT,
nullptr);
// Same form data as saved match and POSITIVE_SIGNAL_SENT means there should
@@ -1883,8 +1832,7 @@ TEST_F(PasswordFormManagerTest, UploadPasswordForm) {
TEST_F(PasswordFormManagerTest, CorrectlySavePasswordWithoutUsernameFields) {
EXPECT_CALL(*client()->mock_driver(), AllowPasswordGenerationForForm(_));
- form_manager()->OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<PasswordForm>>());
+ fake_form_fetcher()->SetNonFederated(std::vector<const PasswordForm*>(), 0u);
PasswordForm login(*observed_form());
login.username_element.clear();
@@ -1915,43 +1863,40 @@ TEST_F(PasswordFormManagerTest, DriverDeletedBeforeStoreDone) {
// This test checks implicitly that after step 4 the PFM does not attempt
// use-after-free of the deleted driver.
std::string example_url("http://example.com");
- std::unique_ptr<PasswordForm> form(new PasswordForm);
- form->origin = GURL(example_url);
- form->signon_realm = example_url;
- form->action = GURL(example_url);
- form->username_element = ASCIIToUTF16("u");
- form->password_element = ASCIIToUTF16("p");
- form->submit_element = ASCIIToUTF16("s");
-
+ PasswordForm form;
+ form.origin = GURL(example_url);
+ form.signon_realm = example_url;
+ form.action = GURL(example_url);
+ form.username_element = ASCIIToUTF16("u");
+ form.password_element = ASCIIToUTF16("p");
+ form.submit_element = ASCIIToUTF16("s");
+
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
- client()->driver(), *form,
- base::MakeUnique<MockFormSaver>());
+ client()->driver(), form,
+ base::MakeUnique<MockFormSaver>(), &fetcher);
// Suddenly, the frame and its driver disappear.
client()->KillDriver();
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(std::move(form));
- form_manager.OnGetPasswordStoreResults(std::move(simulated_results));
+ fetcher.SetNonFederated({&form}, 0u);
}
TEST_F(PasswordFormManagerTest, PreferredMatchIsUpToDate) {
// Check that preferred_match() is always a member of best_matches().
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- std::unique_ptr<PasswordForm> form(new PasswordForm(*observed_form()));
- form->username_value = ASCIIToUTF16("username");
- form->password_value = ASCIIToUTF16("password1");
- form->preferred = false;
+ PasswordForm form = *observed_form();
+ form.username_value = ASCIIToUTF16("username");
+ form.password_value = ASCIIToUTF16("password1");
+ form.preferred = false;
- std::unique_ptr<PasswordForm> generated_form(new PasswordForm(*form));
- generated_form->type = PasswordForm::TYPE_GENERATED;
- generated_form->password_value = ASCIIToUTF16("password2");
- generated_form->preferred = true;
+ PasswordForm generated_form = form;
+ generated_form.type = PasswordForm::TYPE_GENERATED;
+ generated_form.password_value = ASCIIToUTF16("password2");
+ generated_form.preferred = true;
- simulated_results.push_back(std::move(generated_form));
- simulated_results.push_back(std::move(form));
+ fake_form_fetcher()->SetNonFederated({&form, &generated_form}, 0u);
- form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
EXPECT_EQ(1u, form_manager()->best_matches().size());
EXPECT_EQ(form_manager()->preferred_match(),
form_manager()->best_matches().begin()->second);
@@ -2006,17 +1951,19 @@ TEST_F(PasswordFormManagerTest, TestSuggestingPasswordChangeForms) {
PasswordForm observed_change_password_form = *observed_form();
observed_change_password_form.new_password_element =
base::ASCIIToUTF16("new_pwd");
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager manager_creds(
password_manager(), client(), client()->driver(),
- observed_change_password_form, base::MakeUnique<MockFormSaver>());
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(CreateSavedMatch(false));
+ observed_change_password_form, base::MakeUnique<MockFormSaver>(),
+ &fetcher);
autofill::PasswordFormFillData fill_data;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_))
.WillOnce(SaveArg<0>(&fill_data));
- manager_creds.OnGetPasswordStoreResults(std::move(simulated_results));
+ PasswordForm result = CreateSavedMatch(false);
+ fetcher.SetNonFederated({&result}, 0u);
EXPECT_EQ(1u, manager_creds.best_matches().size());
EXPECT_EQ(0u, fill_data.additional_logins.size());
EXPECT_TRUE(fill_data.wait_for_username);
@@ -2041,11 +1988,14 @@ TEST_F(PasswordFormManagerTest, TestUpdateMethod) {
// verify in the end that this did not happen.
saved_match()->submit_element.clear();
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+
+ fetcher.SetNonFederated({saved_match()}, 0u);
- SimulateMatchingPhase(&form_manager, RESULT_SAVED_MATCH);
// User submits current and new credentials to the observed form.
PasswordForm credentials(*observed_form());
credentials.username_element.clear();
@@ -2075,7 +2025,6 @@ TEST_F(PasswordFormManagerTest, TestUpdateMethod) {
.WillOnce(SaveArg<0>(&new_credentials));
form_manager.Update(*saved_match());
- Mock::VerifyAndClearExpectations(mock_store());
// No meta-information should be updated, only the password.
EXPECT_EQ(credentials.new_password_value, new_credentials.password_value);
@@ -2085,8 +2034,9 @@ TEST_F(PasswordFormManagerTest, TestUpdateMethod) {
}
TEST_F(PasswordFormManagerTest, TestUpdateNoUsernameTextfieldPresent) {
- // Add a new password field to the test form. The PasswordFormManager should
- // save the password from this field, instead of the current password field.
+ // Add a new password field to the test form and insert a |username_value|
+ // unlikely to be a real username. The PasswordFormManager should still save
+ // the password from this field, instead of the current password field.
observed_form()->new_password_element = ASCIIToUTF16("NewPasswd");
autofill::FormFieldData field;
field.label = ASCIIToUTF16("NewPasswd");
@@ -2103,11 +2053,14 @@ TEST_F(PasswordFormManagerTest, TestUpdateNoUsernameTextfieldPresent) {
// will verify in the end that this did not happen.
saved_match()->submit_element.clear();
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+
+ fetcher.SetNonFederated({saved_match()}, 0u);
- SimulateMatchingPhase(&form_manager, RESULT_SAVED_MATCH);
// User submits current and new credentials to the observed form.
PasswordForm credentials(*observed_form());
// The |username_value| contains a text that's unlikely to be real username.
@@ -2139,9 +2092,9 @@ TEST_F(PasswordFormManagerTest, TestUpdateNoUsernameTextfieldPresent) {
.WillOnce(SaveArg<0>(&new_credentials));
form_manager.Update(form_manager.pending_credentials());
- Mock::VerifyAndClearExpectations(mock_store());
- // No meta-information should be updated, only the password.
+ // No other information than password value should be updated. In particular
+ // not the username.
EXPECT_EQ(saved_match()->username_value, new_credentials.username_value);
EXPECT_EQ(credentials.new_password_value, new_credentials.password_value);
EXPECT_EQ(saved_match()->username_element, new_credentials.username_element);
@@ -2149,15 +2102,19 @@ TEST_F(PasswordFormManagerTest, TestUpdateNoUsernameTextfieldPresent) {
EXPECT_EQ(saved_match()->submit_element, new_credentials.submit_element);
}
+// Test that if WipeStoreCopyIfOutdated is called before password store
+// callback, the UMA is signalled accordingly.
TEST_F(PasswordFormManagerTest, WipeStoreCopyIfOutdated_BeforeStoreCallback) {
PasswordForm form(*saved_match());
- ASSERT_FALSE(form.password_value.empty());
+ form.password_value = ASCIIToUTF16("nonempty-password");
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), form,
- base::MakeUnique<MockFormSaver>());
- // The creation of |form_manager| caused a GetLogins call. This test does not
- // provide the callback for that call back to |form_manager| on purpose.
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ // The creation of |fetcher| keeps it waiting for store results. This test
+ // keeps the fetcher waiting on purpose.
PasswordForm submitted_form(form);
submitted_form.password_value += ASCIIToUTF16("add stuff, make it different");
@@ -2173,19 +2130,16 @@ TEST_F(PasswordFormManagerTest, WipeStoreCopyIfOutdated_BeforeStoreCallback) {
}
TEST_F(PasswordFormManagerTest, GenerationStatusChangedWithPassword) {
- std::unique_ptr<PasswordForm> generated_form(
- new PasswordForm(*observed_form()));
- generated_form->type = PasswordForm::TYPE_GENERATED;
- generated_form->username_value = ASCIIToUTF16("username");
- generated_form->password_value = ASCIIToUTF16("password2");
- generated_form->preferred = true;
-
- PasswordForm submitted_form(*generated_form);
+ PasswordForm generated_form = *observed_form();
+ generated_form.type = PasswordForm::TYPE_GENERATED;
+ generated_form.username_value = ASCIIToUTF16("username");
+ generated_form.password_value = ASCIIToUTF16("password2");
+ generated_form.preferred = true;
+
+ PasswordForm submitted_form(generated_form);
submitted_form.password_value = ASCIIToUTF16("password3");
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(std::move(generated_form));
- form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
+ fake_form_fetcher()->SetNonFederated({&generated_form}, 0u);
form_manager()->ProvisionallySave(
submitted_form, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
@@ -2201,18 +2155,15 @@ TEST_F(PasswordFormManagerTest, GenerationStatusChangedWithPassword) {
TEST_F(PasswordFormManagerTest, GenerationStatusNotUpdatedIfPasswordUnchanged) {
base::HistogramTester histogram_tester;
- std::unique_ptr<PasswordForm> generated_form(
- new PasswordForm(*observed_form()));
- generated_form->type = PasswordForm::TYPE_GENERATED;
- generated_form->username_value = ASCIIToUTF16("username");
- generated_form->password_value = ASCIIToUTF16("password2");
- generated_form->preferred = true;
+ PasswordForm generated_form = *observed_form();
+ generated_form.type = PasswordForm::TYPE_GENERATED;
+ generated_form.username_value = ASCIIToUTF16("username");
+ generated_form.password_value = ASCIIToUTF16("password2");
+ generated_form.preferred = true;
- PasswordForm submitted_form(*generated_form);
+ PasswordForm submitted_form(generated_form);
- std::vector<std::unique_ptr<PasswordForm>> simulated_results;
- simulated_results.push_back(std::move(generated_form));
- form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
+ fake_form_fetcher()->SetNonFederated({&generated_form}, 0u);
form_manager()->ProvisionallySave(
submitted_form, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
@@ -2227,124 +2178,75 @@ TEST_F(PasswordFormManagerTest, GenerationStatusNotUpdatedIfPasswordUnchanged) {
metrics_util::PASSWORD_USED, 1);
}
-TEST_F(PasswordFormManagerTest,
- FetchMatchingLoginsFromPasswordStore_Reentrance) {
- form_manager()->FetchDataFromPasswordStore();
- form_manager()->FetchDataFromPasswordStore();
-
- // First response from the store, should be ignored.
- std::unique_ptr<PasswordForm> saved_form(new PasswordForm(*saved_match()));
- saved_form->username_value = ASCIIToUTF16("a@gmail.com");
- std::vector<std::unique_ptr<PasswordForm>> results;
- results.push_back(std::move(saved_form));
- // Expect the additional call for GetLogins after the first response arrives.
- EXPECT_CALL(
- *mock_store(),
- GetLogins(PasswordStore::FormDigest(form_manager()->observed_form()),
- form_manager()));
- form_manager()->OnGetPasswordStoreResults(std::move(results));
- EXPECT_TRUE(form_manager()->best_matches().empty());
-
- // Second response from the store should not be ignored.
- saved_form.reset(new PasswordForm(*saved_match()));
- saved_form->username_value = ASCIIToUTF16("b@gmail.com");
- results.push_back(std::move(saved_form));
- saved_form.reset(new PasswordForm(*saved_match()));
- saved_form->username_value = ASCIIToUTF16("c@gmail.com");
- results.push_back(std::move(saved_form));
- form_manager()->OnGetPasswordStoreResults(std::move(results));
- EXPECT_EQ(2U, form_manager()->best_matches().size());
-}
-
+// Test that ProcessFrame is called on receiving matches from the fetcher,
+// resulting in a FillPasswordForm call.
TEST_F(PasswordFormManagerTest, ProcessFrame) {
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_));
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
}
+// Test that ProcessFrame can also be called directly, resulting in an
+// additional FillPasswordForm call.
TEST_F(PasswordFormManagerTest, ProcessFrame_MoreProcessFrameMoreFill) {
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_)).Times(2);
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
form_manager()->ProcessFrame(client()->mock_driver()->AsWeakPtr());
}
+// Test that when ProcessFrame is called on a driver added after receiving
+// matches, such driver is still told to call FillPasswordForm.
TEST_F(PasswordFormManagerTest, ProcessFrame_TwoDrivers) {
NiceMock<MockPasswordManagerDriver> second_driver;
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_));
EXPECT_CALL(second_driver, FillPasswordForm(_));
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
form_manager()->ProcessFrame(second_driver.AsWeakPtr());
}
+// Test that a second driver is added before receiving matches, such driver is
+// also told to call FillPasswordForm once the matches arrive.
TEST_F(PasswordFormManagerTest, ProcessFrame_DriverBeforeMatching) {
- NiceMock<MockPasswordManagerDriver> extra_driver;
-
- EXPECT_CALL(extra_driver, FillPasswordForm(_));
-
- // The PasswordStore was asked store for logins when |form_manager()| was
- // created, but should not respond yet.
-
- // Now add the extra driver.
- form_manager()->ProcessFrame(extra_driver.AsWeakPtr());
+ NiceMock<MockPasswordManagerDriver> second_driver;
+ form_manager()->ProcessFrame(second_driver.AsWeakPtr());
- // Password store responds.
- std::unique_ptr<PasswordForm> match(new PasswordForm(*saved_match()));
- std::vector<std::unique_ptr<PasswordForm>> result_form;
- result_form.push_back(std::move(match));
- form_manager()->OnGetPasswordStoreResults(std::move(result_form));
+ EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_));
+ EXPECT_CALL(second_driver, FillPasswordForm(_));
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
}
+// Test that if the fetcher updates the information about stored matches,
+// autofill is re-triggered.
TEST_F(PasswordFormManagerTest, ProcessFrame_StoreUpdatesCausesAutofill) {
EXPECT_CALL(*client()->mock_driver(), FillPasswordForm(_)).Times(2);
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
- form_manager()->FetchDataFromPasswordStore();
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
-}
-
-TEST_F(PasswordFormManagerTest, UpdateFormManagers_IsCalled) {
- // Let |password_manager()| create one additional PasswordFormManager.
- PasswordStoreConsumer* consumer = nullptr; // Will point to the new PFM.
- EXPECT_CALL(*mock_store(), GetLogins(_, _)).WillOnce(SaveArg<1>(&consumer));
- PasswordForm form;
- std::vector<PasswordForm> observed;
- observed.push_back(form);
- password_manager()->OnPasswordFormsParsed(client()->mock_driver(), observed);
- // Make sure that the additional PFM is in POST_MATCHING phase.
- ASSERT_TRUE(consumer);
- consumer->OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<PasswordForm>>());
-
- // Now prepare |form_manager()| for saving.
- SimulateMatchingPhase(form_manager(), RESULT_NO_MATCH);
- PasswordForm saved_form(*observed_form());
- saved_form.preferred = true;
- form_manager()->ProvisionallySave(
- saved_form, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
-
- // Firing Save() should cause PasswordManager::UpdateFormManagers to make the
- // additional PFM to call the password store again.
- EXPECT_CALL(*mock_store(), GetLogins(_, _));
- form_manager()->Save();
-}
-
-TEST_F(PasswordFormManagerTest, UploadChangePasswordForm_NEW_PASSWORD) {
- ChangePasswordUploadTest(autofill::NEW_PASSWORD);
-}
-
-TEST_F(PasswordFormManagerTest,
- UploadChangePasswordForm_PROBABLY_NEW_PASSWORD) {
- ChangePasswordUploadTest(autofill::PROBABLY_NEW_PASSWORD);
-}
-
-TEST_F(PasswordFormManagerTest, UploadChangePasswordForm_NOT_NEW_PASSWORD) {
- ChangePasswordUploadTest(autofill::NOT_NEW_PASSWORD);
+ std::vector<const PasswordForm*> matches = {saved_match()};
+ fake_form_fetcher()->SetNonFederated(matches, 0u);
+ fake_form_fetcher()->Fetch();
+ fake_form_fetcher()->SetNonFederated(matches, 0u);
+}
+
+// TODO(crbug.com/639786): Restore the following test:
+// when PasswordFormManager::Save is called, then PasswordFormManager also
+// calls PasswordManager::UpdateFormManagers.
+
+TEST_F(PasswordFormManagerTest, UploadChangePasswordForm) {
+ autofill::ServerFieldType kChangePasswordVotes[] = {
+ autofill::NEW_PASSWORD, autofill::PROBABLY_NEW_PASSWORD,
+ autofill::NOT_NEW_PASSWORD};
+ bool kFalseTrue[] = {false, true};
+ for (autofill::ServerFieldType vote : kChangePasswordVotes) {
+ for (bool has_confirmation_field : kFalseTrue)
+ ChangePasswordUploadTest(vote, has_confirmation_field);
+ }
}
TEST_F(PasswordFormManagerTest, TestUpdatePSLMatchedCredentials) {
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
- SimulateMatchingPhase(&form_manager, RESULT_SAVED_MATCH | RESULT_PSL_MATCH);
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ fetcher.SetNonFederated({saved_match(), psl_saved_match()}, 0u);
// User submits a credentials with an old username and a new password.
PasswordForm credentials(*observed_form());
@@ -2369,7 +2271,6 @@ TEST_F(PasswordFormManagerTest, TestUpdatePSLMatchedCredentials) {
StartUploadRequest(_, false, _, _, true));
form_manager.Save();
- Mock::VerifyAndClearExpectations(mock_store());
// No meta-information should be updated, only the password.
EXPECT_EQ(credentials.password_value, new_credentials.password_value);
@@ -2392,11 +2293,13 @@ TEST_F(PasswordFormManagerTest, TestUpdatePSLMatchedCredentials) {
TEST_F(PasswordFormManagerTest,
TestNotUpdatePSLMatchedCredentialsWithAnotherUsername) {
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
+ base::MakeUnique<MockFormSaver>(), &fetcher);
psl_saved_match()->username_value += ASCIIToUTF16("1");
- SimulateMatchingPhase(&form_manager, RESULT_SAVED_MATCH | RESULT_PSL_MATCH);
+ fetcher.SetNonFederated({saved_match(), psl_saved_match()}, 0u);
// User submits a credentials with an old username and a new password.
PasswordForm credentials(*observed_form());
@@ -2420,7 +2323,6 @@ TEST_F(PasswordFormManagerTest,
StartUploadRequest(_, false, _, _, true));
form_manager.Save();
- Mock::VerifyAndClearExpectations(mock_store());
// No meta-information should be updated, only the password.
EXPECT_EQ(credentials.password_value, new_credentials.password_value);
@@ -2432,11 +2334,13 @@ TEST_F(PasswordFormManagerTest,
TEST_F(PasswordFormManagerTest,
TestNotUpdatePSLMatchedCredentialsWithAnotherPassword) {
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
+ base::MakeUnique<MockFormSaver>(), &fetcher);
psl_saved_match()->password_value += ASCIIToUTF16("1");
- SimulateMatchingPhase(&form_manager, RESULT_SAVED_MATCH | RESULT_PSL_MATCH);
+ fetcher.SetNonFederated({saved_match(), psl_saved_match()}, 0u);
// User submits a credentials with an old username and a new password.
PasswordForm credentials(*observed_form());
@@ -2460,7 +2364,6 @@ TEST_F(PasswordFormManagerTest,
StartUploadRequest(_, false, _, _, true));
form_manager.Save();
- Mock::VerifyAndClearExpectations(mock_store());
// No meta-information should be updated, only the password.
EXPECT_EQ(credentials.password_value, new_credentials.password_value);
@@ -2471,10 +2374,12 @@ TEST_F(PasswordFormManagerTest,
}
TEST_F(PasswordFormManagerTest, TestNotUpdateWhenOnlyPSLMatched) {
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
- SimulateMatchingPhase(&form_manager, RESULT_PSL_MATCH);
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ fetcher.SetNonFederated({psl_saved_match()}, 0u);
// User submits a credentials with an old username and a new password.
PasswordForm credentials(*observed_form());
@@ -2493,7 +2398,6 @@ TEST_F(PasswordFormManagerTest, TestNotUpdateWhenOnlyPSLMatched) {
.WillOnce(testing::SaveArg<0>(&new_credentials));
form_manager.Save();
- Mock::VerifyAndClearExpectations(mock_store());
EXPECT_EQ(credentials.password_value, new_credentials.password_value);
EXPECT_EQ(credentials.username_value, new_credentials.username_value);
@@ -2502,41 +2406,9 @@ TEST_F(PasswordFormManagerTest, TestNotUpdateWhenOnlyPSLMatched) {
EXPECT_EQ(credentials.origin, new_credentials.origin);
}
-#if !defined(OS_IOS) && !defined(OS_ANDROID)
-TEST_F(PasswordFormManagerTest, FetchStatistics) {
- InteractionsStats stats;
- stats.origin_domain = observed_form()->origin.GetOrigin();
- stats.username_value = saved_match()->username_value;
- stats.dismissal_count = 5;
- std::vector<InteractionsStats*> db_stats;
- db_stats.push_back(new InteractionsStats(stats));
- EXPECT_CALL(*mock_store(), GetSiteStatsMock(stats.origin_domain))
- .WillOnce(Return(db_stats));
- form_manager()->FetchDataFromPasswordStore();
- base::RunLoop().RunUntilIdle();
-
- EXPECT_THAT(form_manager()->form_fetcher()->GetInteractionsStats(),
- ElementsAre(Pointee(stats)));
-}
-#else
-TEST_F(PasswordFormManagerTest, DontFetchStatistics) {
- // Because |form_manager()| is currently waiting for a PasswordStore response,
- // the response needs to be faked in order to be able to re-request another
- // one below.
- form_manager()->OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<PasswordForm>>());
- EXPECT_CALL(
- *mock_store(),
- GetLogins(PasswordStore::FormDigest(*observed_form()), form_manager()));
- EXPECT_CALL(*mock_store(), GetSiteStatsMock(_)).Times(0);
- form_manager()->FetchDataFromPasswordStore();
- base::RunLoop().RunUntilIdle();
-}
-#endif
-
TEST_F(PasswordFormManagerTest,
TestSavingOnChangePasswordFormGenerationNoStoredForms) {
- SimulateMatchingPhase(form_manager(), RESULT_NO_MATCH);
+ 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.
@@ -2571,7 +2443,7 @@ TEST_F(PasswordFormManagerTest,
}
TEST_F(PasswordFormManagerTest, TestUpdatingOnChangePasswordFormGeneration) {
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ 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
@@ -2607,7 +2479,7 @@ TEST_F(PasswordFormManagerTest, TestUpdatingOnChangePasswordFormGeneration) {
TEST_F(PasswordFormManagerTest,
TestSavingOnChangePasswordFormGenerationNoMatchedForms) {
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ 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
@@ -2658,11 +2530,12 @@ TEST_F(PasswordFormManagerTest,
field.name = ASCIIToUTF16("NewPasswd");
observed_form()->form_data.fields.push_back(field);
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
-
- SimulateMatchingPhase(&form_manager, RESULT_SAVED_MATCH);
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ fetcher.SetNonFederated({saved_match()}, 0u);
// User submits current and new credentials to the observed form.
PasswordForm submitted_form(*observed_form());
@@ -2708,17 +2581,21 @@ TEST_F(PasswordFormManagerTest,
form_manager.Update(*saved_match());
}
+// Checks uploading a vote about the usage of the password generation popup.
TEST_F(PasswordFormManagerTest, GeneratedVoteUpload) {
- // Automatic generation, sign-up form.
- GeneratedVoteUploadTest(false, false, true);
- // Automatic generation, change password form.
- GeneratedVoteUploadTest(false, true, true);
- // Manual generation, sign-up form.
- GeneratedVoteUploadTest(true, false, true);
- // Manual generation, change password form.
- GeneratedVoteUploadTest(true, true, true);
- // Generation popup was shown, but the user entered its own password.
- GeneratedVoteUploadTest(true, true, false);
+ bool kFalseTrue[] = {false, true};
+ SavePromptInteraction kSavePromptInterations[] = {SAVE, NEVER,
+ NO_INTERACTION};
+ for (bool is_manual_generation : kFalseTrue) {
+ for (bool is_change_password_form : kFalseTrue) {
+ for (bool has_generated_password : kFalseTrue) {
+ for (SavePromptInteraction interaction : kSavePromptInterations) {
+ GeneratedVoteUploadTest(is_manual_generation, is_change_password_form,
+ has_generated_password, interaction);
+ }
+ }
+ }
+ }
}
TEST_F(PasswordFormManagerTest, FormClassifierVoteUpload) {
@@ -2735,17 +2612,18 @@ TEST_F(PasswordFormManagerTest, FormClassifierVoteUpload) {
submitted_form.username_value = saved_match()->username_value;
submitted_form.password_value = saved_match()->password_value;
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(
password_manager(), client(), client()->driver(), form,
- base::MakeUnique<NiceMock<MockFormSaver>>());
+ base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
base::string16 generation_element = form.password_element;
if (found_generation_element)
form_manager.SaveGenerationFieldDetectedByClassifier(generation_element);
else
form_manager.SaveGenerationFieldDetectedByClassifier(base::string16());
- std::vector<std::unique_ptr<PasswordForm>> result;
- form_manager.OnGetPasswordStoreResults(std::move(result));
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
autofill::FormStructure form_structure(submitted_form.form_data);
@@ -2771,11 +2649,12 @@ TEST_F(PasswordFormManagerTest, FieldPropertiesMasksUpload) {
submitted_form.username_value = saved_match()->username_value;
submitted_form.password_value = saved_match()->password_value;
- PasswordFormManager form_manager(password_manager(), client(),
- client()->driver(), form,
- base::MakeUnique<NiceMock<MockFormSaver>>());
- std::vector<std::unique_ptr<PasswordForm>> result;
- form_manager.OnGetPasswordStoreResults(std::move(result));
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
+ PasswordFormManager form_manager(
+ password_manager(), client(), client()->driver(), form,
+ base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
DCHECK_EQ(3U, form.form_data.fields.size());
submitted_form.form_data.fields[1].properties_mask =
@@ -2805,10 +2684,12 @@ TEST_F(PasswordFormManagerTest, TestSavingAPIFormsWithSamePassword) {
saved_match()->username_element.clear();
saved_match()->type = autofill::PasswordForm::TYPE_API;
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(password_manager(), client(),
client()->driver(), *observed_form(),
- base::MakeUnique<MockFormSaver>());
- SimulateMatchingPhase(&form_manager, RESULT_SAVED_MATCH);
+ base::MakeUnique<MockFormSaver>(), &fetcher);
+ fetcher.SetNonFederated({saved_match()}, 0u);
// User submits new credentials with the same password as in already saved
// one.
@@ -2828,7 +2709,6 @@ TEST_F(PasswordFormManagerTest, TestSavingAPIFormsWithSamePassword) {
.WillOnce(SaveArg<0>(&new_credentials));
form_manager.Save();
- Mock::VerifyAndClearExpectations(mock_store());
EXPECT_EQ(saved_match()->username_value + ASCIIToUTF16("1"),
new_credentials.username_value);
@@ -2840,7 +2720,7 @@ TEST_F(PasswordFormManagerTest, TestSavingAPIFormsWithSamePassword) {
TEST_F(PasswordFormManagerTest, SkipZeroClickIntact) {
saved_match()->skip_zero_click = true;
psl_saved_match()->skip_zero_click = true;
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH | RESULT_PSL_MATCH);
+ fake_form_fetcher()->SetNonFederated({saved_match(), psl_saved_match()}, 0u);
EXPECT_EQ(1u, form_manager()->best_matches().size());
// User submits a credentials with an old username and a new password.
@@ -2866,31 +2746,15 @@ TEST_F(PasswordFormManagerTest, SkipZeroClickIntact) {
EXPECT_TRUE(credentials_to_update[0].skip_zero_click);
}
-TEST_F(PasswordFormManagerTest, FederatedCredentialsFiltered) {
- PasswordForm federated(*saved_match());
- federated.password_value.clear();
- federated.federation_origin =
- url::Origin(GURL("https://accounts.google.com"));
-
- std::vector<std::unique_ptr<PasswordForm>> results;
- results.push_back(base::MakeUnique<PasswordForm>(federated));
- results.push_back(base::MakeUnique<PasswordForm>(*saved_match()));
- form_manager()->OnGetPasswordStoreResults(std::move(results));
-
- EXPECT_EQ(1u, form_manager()->form_fetcher()->GetFederatedMatches().size());
- EXPECT_EQ(*form_manager()->form_fetcher()->GetFederatedMatches()[0],
- federated);
- EXPECT_EQ(1u, form_manager()->best_matches().size());
- EXPECT_EQ(*(form_manager()->best_matches().begin()->second), *saved_match());
-}
-
TEST_F(PasswordFormManagerTest, ProbablyAccountCreationUpload) {
PasswordForm form(*observed_form());
form.form_data = saved_match()->form_data;
- PasswordFormManager form_manager(password_manager(), client(),
- client()->driver(), form,
- base::MakeUnique<NiceMock<MockFormSaver>>());
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
+ PasswordFormManager form_manager(
+ password_manager(), client(), client()->driver(), form,
+ base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
PasswordForm form_to_save(form);
form_to_save.preferred = true;
@@ -2899,15 +2763,13 @@ TEST_F(PasswordFormManagerTest, ProbablyAccountCreationUpload) {
form_to_save.password_value = saved_match()->password_value;
form_to_save.does_look_like_signup_form = true;
- std::vector<std::unique_ptr<PasswordForm>> result;
- form_manager.OnGetPasswordStoreResults(std::move(result));
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
autofill::FormStructure pending_structure(form_to_save.form_data);
autofill::ServerFieldTypeSet expected_available_field_types;
std::map<base::string16, autofill::ServerFieldType> expected_types;
expected_types[ASCIIToUTF16("full_name")] = autofill::UNKNOWN_TYPE;
- expected_available_field_types.insert(autofill::USERNAME);
- expected_types[saved_match()->username_element] = autofill::USERNAME;
+ expected_types[saved_match()->username_element] = autofill::UNKNOWN_TYPE;
expected_available_field_types.insert(
autofill::PROBABLY_ACCOUNT_CREATION_PASSWORD);
expected_types[saved_match()->password_element] =
@@ -2927,14 +2789,14 @@ TEST_F(PasswordFormManagerTest, ProbablyAccountCreationUpload) {
TEST_F(PasswordFormManagerFillOnAccountSelectTest, ProcessFrame) {
EXPECT_CALL(*client()->mock_driver(),
ShowInitialPasswordAccountSuggestions(_));
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
}
// Check that PasswordFormManager records
// PasswordManager_LoginFollowingAutofill as part of processing a credential
// update.
TEST_F(PasswordFormManagerTest, ReportProcessingUpdate) {
- SimulateMatchingPhase(form_manager(), RESULT_SAVED_MATCH);
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
PasswordForm pending = *observed_form();
pending.username_value = saved_match()->username_value;
pending.password_value = saved_match()->password_value;
@@ -2942,6 +2804,8 @@ TEST_F(PasswordFormManagerTest, ReportProcessingUpdate) {
pending, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
EXPECT_FALSE(form_manager()->IsNewLogin());
+ EXPECT_CALL(*client()->mock_driver()->mock_autofill_download_manager(),
+ StartUploadRequest(_, false, _, _, true));
base::UserActionTester tester;
EXPECT_EQ(0, tester.GetActionCount("PasswordManager_LoginFollowingAutofill"));
@@ -2973,11 +2837,13 @@ TEST_F(PasswordFormManagerTest, RemoveResultsWithWrongScheme_ObservingHTML) {
PasswordForm observed = *observed_form();
observed.scheme = kCorrectScheme;
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
PasswordFormManager form_manager(
password_manager(), client(),
(kCorrectScheme == PasswordForm::SCHEME_HTML ? client()->driver()
: nullptr),
- observed, base::MakeUnique<NiceMock<MockFormSaver>>());
+ observed, base::MakeUnique<NiceMock<MockFormSaver>>(), &fetcher);
PasswordForm match = *saved_match();
match.scheme = kCorrectScheme;
@@ -2986,18 +2852,16 @@ TEST_F(PasswordFormManagerTest, RemoveResultsWithWrongScheme_ObservingHTML) {
non_match.scheme = kWrongScheme;
// First try putting the correct scheme first in returned matches.
- std::vector<const PasswordForm*> all_matches = {&match, &non_match};
static_cast<FormFetcher::Consumer*>(&form_manager)
- ->ProcessMatches(all_matches, 0u);
+ ->ProcessMatches({&match, &non_match}, 0u);
EXPECT_EQ(1u, form_manager.best_matches().size());
EXPECT_EQ(kCorrectScheme,
form_manager.best_matches().begin()->second->scheme);
// Now try putting the correct scheme last in returned matches.
- all_matches = {&non_match, &match};
static_cast<FormFetcher::Consumer*>(&form_manager)
- ->ProcessMatches(all_matches, 0u);
+ ->ProcessMatches({&non_match, &match}, 0u);
EXPECT_EQ(1u, form_manager.best_matches().size());
EXPECT_EQ(kCorrectScheme,
diff --git a/chromium/components/password_manager/core/browser/password_generation_manager.cc b/chromium/components/password_manager/core/browser/password_generation_manager.cc
index d33425534a6..d97054a4f6a 100644
--- a/chromium/components/password_manager/core/browser/password_generation_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_generation_manager.cc
@@ -30,14 +30,10 @@ void PasswordGenerationManager::DetectFormsEligibleForGeneration(
std::vector<autofill::PasswordFormGenerationData>
forms_eligible_for_generation;
- for (std::vector<autofill::FormStructure*>::const_iterator form_it =
- forms.begin();
- form_it != forms.end(); ++form_it) {
+ for (auto form_it = forms.begin(); form_it != forms.end(); ++form_it) {
autofill::FormStructure* form = *form_it;
- for (std::vector<autofill::AutofillField*>::const_iterator field_it =
- form->begin();
- field_it != form->end(); ++field_it) {
- autofill::AutofillField* field = *field_it;
+ for (auto field_it = form->begin(); field_it != form->end(); ++field_it) {
+ autofill::AutofillField* field = field_it->get();
if (field->server_type() == autofill::ACCOUNT_CREATION_PASSWORD ||
field->server_type() == autofill::NEW_PASSWORD) {
forms_eligible_for_generation.push_back(
diff --git a/chromium/components/password_manager/core/browser/password_generation_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_generation_manager_unittest.cc
index f8095609c5f..f5fe0637b3e 100644
--- a/chromium/components/password_manager/core/browser/password_generation_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_generation_manager_unittest.cc
@@ -113,7 +113,7 @@ class PasswordGenerationManagerTest : public testing::Test {
// indirectly cause those prefs to be immediately accessed.
std::unique_ptr<TestingPrefServiceSimple> prefs(
new TestingPrefServiceSimple());
- prefs->registry()->RegisterBooleanPref(prefs::kPasswordManagerSavingEnabled,
+ prefs->registry()->RegisterBooleanPref(prefs::kCredentialsEnableService,
true);
client_.reset(new MockPasswordManagerClient(std::move(prefs)));
}
@@ -267,7 +267,7 @@ TEST_F(PasswordGenerationManagerTest, UpdatePasswordSyncStateIncognito) {
// be disabled.
EXPECT_CALL(*client_, IsOffTheRecord()).WillRepeatedly(testing::Return(true));
PrefService* prefs = client_->GetPrefs();
- prefs->SetBoolean(prefs::kPasswordManagerSavingEnabled, true);
+ prefs->SetBoolean(prefs::kCredentialsEnableService, true);
EXPECT_CALL(*client_, GetPasswordSyncState())
.WillRepeatedly(testing::Return(SYNCING_NORMAL_ENCRYPTION));
diff --git a/chromium/components/password_manager/core/browser/password_manager.cc b/chromium/components/password_manager/core/browser/password_manager.cc
index 2920dec61d7..acb0e8dc328 100644
--- a/chromium/components/password_manager/core/browser/password_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_manager.cc
@@ -232,7 +232,8 @@ void PasswordManager::SetGenerationElementAndReasonForForm(
// ability to detect forms.
auto manager = base::MakeUnique<PasswordFormManager>(
this, client_, driver->AsWeakPtr(), form,
- base::WrapUnique(new FormSaverImpl(client_->GetPasswordStore())));
+ base::WrapUnique(new FormSaverImpl(client_->GetPasswordStore())),
+ nullptr);
pending_login_managers_.push_back(std::move(manager));
}
@@ -270,11 +271,22 @@ void PasswordManager::ProvisionallySavePassword(const PasswordForm& form) {
return;
}
+ bool should_block = ShouldBlockPasswordForSameOriginButDifferentScheme(form);
+ 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());
+ return;
+ }
+
auto matched_manager_it = pending_login_managers_.end();
PasswordFormManager::MatchResultMask current_match_result =
PasswordFormManager::RESULT_NO_MATCH;
- // Below, "matching" is in DoesManage-sense and "not ready" in
- // !HasCompletedMatching sense. We keep track of such PasswordFormManager
+ // Below, "matching" is in DoesManage-sense and "not ready" in the sense of
+ // FormFetcher being ready. We keep track of such PasswordFormManager
// instances for UMA.
for (auto iter = pending_login_managers_.begin();
iter != pending_login_managers_.end(); ++iter) {
@@ -332,11 +344,11 @@ void PasswordManager::ProvisionallySavePassword(const PasswordForm& form) {
manager.swap(*matched_manager_it);
pending_login_managers_.erase(matched_manager_it);
- PasswordForm provisionally_saved_form(form);
- provisionally_saved_form.preferred = true;
+ PasswordForm submitted_form(form);
+ submitted_form.preferred = true;
if (logger) {
logger->LogPasswordForm(Logger::STRING_PROVISIONALLY_SAVED_FORM,
- provisionally_saved_form);
+ submitted_form);
}
PasswordFormManager::OtherPossibleUsernamesAction action =
PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES;
@@ -347,7 +359,7 @@ void PasswordManager::ProvisionallySavePassword(const PasswordForm& form) {
Logger::STRING_IGNORE_POSSIBLE_USERNAMES,
action == PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
}
- manager->ProvisionallySave(provisionally_saved_form, action);
+ manager->ProvisionallySave(submitted_form, action);
provisional_save_manager_.swap(manager);
// Cache the user-visible URL (i.e., the one seen in the omnibox). Once the
@@ -358,7 +370,7 @@ void PasswordManager::ProvisionallySavePassword(const PasswordForm& form) {
void PasswordManager::UpdateFormManagers() {
for (const auto& form_manager : pending_login_managers_) {
- form_manager->FetchDataFromPasswordStore();
+ form_manager->form_fetcher()->Fetch();
}
}
@@ -368,6 +380,10 @@ void PasswordManager::DropFormManagers() {
all_visible_forms_.clear();
}
+bool PasswordManager::IsPasswordFieldDetectedOnPage() {
+ return !pending_login_managers_.empty();
+}
+
void PasswordManager::RecordFailure(ProvisionalSaveFailure failure,
const GURL& form_origin,
BrowserSavePasswordProgressLogger* logger) {
@@ -520,8 +536,8 @@ void PasswordManager::CreatePendingLoginManagers(
auto manager = base::MakeUnique<PasswordFormManager>(
this, client_,
(driver ? driver->AsWeakPtr() : base::WeakPtr<PasswordManagerDriver>()),
- *iter,
- base::WrapUnique(new FormSaverImpl(client_->GetPasswordStore())));
+ *iter, base::WrapUnique(new FormSaverImpl(client_->GetPasswordStore())),
+ nullptr);
pending_login_managers_.push_back(std::move(manager));
}
@@ -546,7 +562,8 @@ bool PasswordManager::CanProvisionalManagerSave() {
return false;
}
- if (!provisional_save_manager_->HasCompletedMatching()) {
+ if (provisional_save_manager_->form_fetcher()->GetState() ==
+ FormFetcher::State::WAITING) {
// We have a provisional save manager, but it didn't finish matching yet.
// We just give up.
RecordFailure(MATCHING_NOT_COMPLETE,
@@ -558,6 +575,15 @@ bool PasswordManager::CanProvisionalManagerSave() {
return true;
}
+bool PasswordManager::ShouldBlockPasswordForSameOriginButDifferentScheme(
+ const PasswordForm& form) const {
+ const GURL& old_origin = main_frame_url_.GetOrigin();
+ const GURL& new_origin = form.origin.GetOrigin();
+ return old_origin.host_piece() == new_origin.host_piece() &&
+ old_origin.SchemeIsCryptographic() &&
+ !new_origin.SchemeIsCryptographic();
+}
+
bool PasswordManager::ShouldPromptUserToSavePassword() const {
return !client_->IsAutomaticPasswordSavingEnabled() &&
(provisional_save_manager_->IsNewLogin() ||
@@ -675,15 +701,17 @@ void PasswordManager::OnLoginSuccessful() {
client_->GetStoreResultFilter()->ReportFormLoginSuccess(
*provisional_save_manager_);
- if (base::FeatureList::IsEnabled(features::kDropSyncCredential) &&
- !client_->GetStoreResultFilter()->ShouldSave(
- provisional_save_manager_->pending_credentials())) {
- provisional_save_manager_->WipeStoreCopyIfOutdated();
- RecordFailure(SYNC_CREDENTIAL,
- provisional_save_manager_->observed_form().origin,
- logger.get());
- provisional_save_manager_.reset();
- return;
+ if (base::FeatureList::IsEnabled(features::kDropSyncCredential)) {
+ DCHECK(provisional_save_manager_->submitted_form());
+ if (!client_->GetStoreResultFilter()->ShouldSave(
+ *provisional_save_manager_->submitted_form())) {
+ provisional_save_manager_->WipeStoreCopyIfOutdated();
+ RecordFailure(SYNC_CREDENTIAL,
+ provisional_save_manager_->observed_form().origin,
+ logger.get());
+ provisional_save_manager_.reset();
+ return;
+ }
}
provisional_save_manager_->LogSubmitPassed();
@@ -825,19 +853,17 @@ void PasswordManager::ProcessAutofillPredictions(
for (const autofill::FormStructure* form : forms) {
if (logger)
logger->LogFormStructure(Logger::STRING_SERVER_PREDICTIONS, *form);
- for (std::vector<autofill::AutofillField*>::const_iterator field =
- form->begin();
- field != form->end(); ++field) {
+ for (const auto& field : *form) {
autofill::PasswordFormFieldPredictionType prediction_type;
- if (ServerTypeToPrediction((*field)->server_type(), &prediction_type)) {
- predictions[form->ToFormData()][*(*field)] = prediction_type;
+ if (ServerTypeToPrediction(field->server_type(), &prediction_type)) {
+ predictions[form->ToFormData()][*field] = prediction_type;
}
// Certain fields are annotated by the browsers as "not passwords" i.e.
// they should not be treated as passwords by the Password Manager.
- if ((*field)->form_control_type == "password" &&
+ if (field->form_control_type == "password" &&
IsPredictedTypeNotPasswordPrediction(
- (*field)->Type().GetStorableType())) {
- predictions[form->ToFormData()][*(*field)] =
+ field->Type().GetStorableType())) {
+ predictions[form->ToFormData()][*field] =
autofill::PREDICTION_NOT_PASSWORD;
}
}
diff --git a/chromium/components/password_manager/core/browser/password_manager.h b/chromium/components/password_manager/core/browser/password_manager.h
index f617cb0ef07..6cb79e19a71 100644
--- a/chromium/components/password_manager/core/browser/password_manager.h
+++ b/chromium/components/password_manager/core/browser/password_manager.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/callback.h"
+#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/strings/string16.h"
@@ -171,9 +172,25 @@ class PasswordManager : public LoginModel {
// visible forms.
void DropFormManagers();
+ // Returns true if password element is detected on the current page.
+ bool IsPasswordFieldDetectedOnPage();
+
PasswordManagerClient* client() { return client_; }
+#if defined(UNIT_TEST)
+ // TODO(crbug.com/639786): Replace using this by quering the factory for
+ // mocked PasswordFormManagers.
+ const std::vector<std::unique_ptr<PasswordFormManager>>&
+ pending_login_managers() {
+ return pending_login_managers_;
+ }
+#endif
+
private:
+ FRIEND_TEST_ALL_PREFIXES(
+ PasswordManagerTest,
+ ShouldBlockPasswordForSameOriginButDifferentSchemeTest);
+
enum ProvisionalSaveFailure {
SAVING_DISABLED,
EMPTY_PASSWORD,
@@ -201,6 +218,13 @@ class PasswordManager : public LoginModel {
// non-blacklisted.
bool CanProvisionalManagerSave();
+ // Returns true if there already exists a provisionally saved password form
+ // from the same origin as |form|, but with a different and secure scheme.
+ // This prevents a potential attack where users can be tricked into saving
+ // unwanted credentials, see http://crbug.com/571580 for details.
+ bool ShouldBlockPasswordForSameOriginButDifferentScheme(
+ const autofill::PasswordForm& form) const;
+
// Returns true if the user needs to be prompted before a password can be
// saved (instead of automatically saving
// the password), based on inspecting the state of
diff --git a/chromium/components/password_manager/core/browser/password_manager_client.cc b/chromium/components/password_manager/core/browser/password_manager_client.cc
index d25ab2c115a..2c51dc644cc 100644
--- a/chromium/components/password_manager/core/browser/password_manager_client.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_client.cc
@@ -68,6 +68,10 @@ const GURL& PasswordManagerClient::GetMainFrameURL() const {
return GURL::EmptyGURL();
}
+bool PasswordManagerClient::IsMainFrameSecure() const {
+ return false;
+}
+
const LogManager* PasswordManagerClient::GetLogManager() const {
return nullptr;
}
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 51bef97b5c7..714677019bd 100644
--- a/chromium/components/password_manager/core/browser/password_manager_client.h
+++ b/chromium/components/password_manager/core/browser/password_manager_client.h
@@ -24,7 +24,6 @@ namespace password_manager {
class LogManager;
class PasswordFormManager;
class PasswordManager;
-class PasswordManagerDriver;
class PasswordStore;
enum PasswordSyncState {
@@ -185,6 +184,9 @@ class PasswordManagerClient {
// Returns the main frame URL.
virtual const GURL& GetMainFrameURL() const;
+ // Returns true if the main frame URL has a secure origin.
+ virtual bool IsMainFrameSecure() const;
+
virtual const GURL& GetLastCommittedEntryURL() const = 0;
// Use this to filter credentials before handling them in password manager.
diff --git a/chromium/components/password_manager/core/browser/password_manager_driver.h b/chromium/components/password_manager/core/browser/password_manager_driver.h
index be5723c4ec9..8ec6b5e8261 100644
--- a/chromium/components/password_manager/core/browser/password_manager_driver.h
+++ b/chromium/components/password_manager/core/browser/password_manager_driver.h
@@ -14,9 +14,7 @@
#include "components/autofill/core/common/password_form_field_prediction_map.h"
namespace autofill {
-class AutofillManager;
struct FormData;
-struct FormFieldData;
struct PasswordForm;
struct PasswordFormGenerationData;
struct PasswordFormFillData;
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 a8ccf9d1732..61172b5eb83 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
@@ -6,6 +6,7 @@
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
#include "base/numerics/safe_conversions.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
@@ -104,6 +105,16 @@ void LogSyncSigninPromoUserAction(SyncSignInUserAction action) {
CHROME_SIGNIN_ACTION_COUNT);
}
+void LogShouldBlockPasswordForSameOriginButDifferentScheme(bool should_block) {
+ UMA_HISTOGRAM_BOOLEAN(
+ "PasswordManager.ShouldBlockPasswordForSameOriginButDifferentScheme",
+ should_block);
+}
+
+void LogCountHttpMigratedPasswords(int count) {
+ UMA_HISTOGRAM_COUNTS_100("PasswordManager.HttpPasswordMigrationCount", count);
+}
+
void LogAccountChooserUsability(AccountChooserUsabilityMetric usability) {
UMA_HISTOGRAM_ENUMERATION("PasswordManager.AccountChooserDialogUsability",
usability, ACCOUNT_CHOOSER_USABILITY_COUNT);
@@ -123,6 +134,35 @@ void LogCredentialManagerGetResult(CredentialManagerGetResult result,
}
}
+void LogPasswordReuse(int password_length,
+ int saved_passwords,
+ int number_matches,
+ bool password_field_detected) {
+ UMA_HISTOGRAM_COUNTS_100("PasswordManager.PasswordReuse.PasswordLength",
+ password_length);
+ UMA_HISTOGRAM_COUNTS_1000("PasswordManager.PasswordReuse.TotalPasswords",
+ saved_passwords);
+ UMA_HISTOGRAM_COUNTS_1000("PasswordManager.PasswordReuse.NumberOfMatches",
+ number_matches);
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.PasswordReuse.PasswordFieldDetected",
+ password_field_detected ? HAS_PASSWORD_FIELD : NO_PASSWORD_FIELD,
+ PASSWORD_REUSE_PASSWORD_FIELD_DETECTED_COUNT);
+}
+
+void LogShowedHttpNotSecureExplanation() {
+ base::RecordAction(base::UserMetricsAction(
+ "PasswordManager_ShowedHttpNotSecureExplanation"));
+}
+
+void LogShowedFormNotSecureWarningOnCurrentNavigation() {
+ // Always record 'true': this is a counter of the number of times the warning
+ // is shown, to gather metrics such as the number of times the warning is
+ // shown per million page loads.
+ UMA_HISTOGRAM_BOOLEAN(
+ "PasswordManager.ShowedFormNotSecureWarningOnCurrentNavigation", true);
+}
+
} // 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 2824f109df1..35fda1748d9 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
@@ -172,6 +172,12 @@ enum CredentialManagerGetMediation {
CREDENTIAL_MANAGER_GET_UNMEDIATED
};
+enum PasswordReusePasswordFieldDetected {
+ NO_PASSWORD_FIELD,
+ HAS_PASSWORD_FIELD,
+ PASSWORD_REUSE_PASSWORD_FIELD_DETECTED_COUNT
+};
+
// A version of the UMA_HISTOGRAM_BOOLEAN macro that allows the |name|
// to vary over the program's runtime.
void LogUMAHistogramBoolean(const std::string& name, bool sample);
@@ -216,6 +222,12 @@ void LogAccountChooserUserActionManyAccounts(AccountChooserUserAction action);
// Log a user action on showing the Chrome sign in promo.
void LogSyncSigninPromoUserAction(SyncSignInUserAction action);
+// Logs whether a password was rejected due to same origin but different scheme.
+void LogShouldBlockPasswordForSameOriginButDifferentScheme(bool should_block);
+
+// Logs number of passwords migrated from HTTP to HTTPS.
+void LogCountHttpMigratedPasswords(int count);
+
// Log if the account chooser has empty username or duplicate usernames.
void LogAccountChooserUsability(AccountChooserUsabilityMetric usability);
@@ -224,6 +236,20 @@ void LogAccountChooserUsability(AccountChooserUsabilityMetric usability);
void LogCredentialManagerGetResult(CredentialManagerGetResult result,
CredentialManagerGetMediation status);
+// Log the password reuse.
+void LogPasswordReuse(int password_length,
+ int saved_passwords,
+ int number_matches,
+ bool password_field_detected);
+
+// Log when the user selects the "Login not secure" warning in the password
+// autofill dropdown to show more information about the warning.
+void LogShowedHttpNotSecureExplanation();
+
+// Log that the Form-Not-Secure warning was shown. Should be called at most once
+// per main-frame navigation.
+void LogShowedFormNotSecureWarningOnCurrentNavigation();
+
} // namespace metrics_util
} // 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 3e0817df574..4fd51933642 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
@@ -104,4 +104,8 @@ MockPasswordStoreObserver::MockPasswordStoreObserver() {}
MockPasswordStoreObserver::~MockPasswordStoreObserver() {}
+MockPasswordReuseDetectorConsumer::MockPasswordReuseDetectorConsumer() {}
+
+MockPasswordReuseDetectorConsumer::~MockPasswordReuseDetectorConsumer() {}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager_test_utils.h b/chromium/components/password_manager/core/browser/password_manager_test_utils.h
index 4f49d562ab2..8d1577315d0 100644
--- a/chromium/components/password_manager/core/browser/password_manager_test_utils.h
+++ b/chromium/components/password_manager/core/browser/password_manager_test_utils.h
@@ -83,6 +83,15 @@ class MockPasswordStoreObserver : public PasswordStore::Observer {
MOCK_METHOD1(OnLoginsChanged, void(const PasswordStoreChangeList& changes));
};
+class MockPasswordReuseDetectorConsumer : public PasswordReuseDetectorConsumer {
+ public:
+ MockPasswordReuseDetectorConsumer();
+ ~MockPasswordReuseDetectorConsumer() override;
+
+ MOCK_METHOD4(OnReuseFound,
+ void(const base::string16&, const std::string&, int, int));
+};
+
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_MANAGER_TEST_UTILS_H_
diff --git a/chromium/components/password_manager/core/browser/password_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_manager_unittest.cc
index a4a9eec4071..da9feefaf9d 100644
--- a/chromium/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_unittest.cc
@@ -33,6 +33,7 @@ using base::ASCIIToUTF16;
using testing::_;
using testing::AnyNumber;
using testing::Return;
+using testing::ReturnRef;
using testing::SaveArg;
using testing::WithArg;
@@ -67,6 +68,7 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
void(const autofill::PasswordForm&));
MOCK_METHOD0(AutomaticPasswordSaveIndicator, void());
MOCK_METHOD0(GetPrefs, PrefService*());
+ MOCK_CONST_METHOD0(GetMainFrameURL, const GURL&());
MOCK_METHOD0(GetDriver, PasswordManagerDriver*());
MOCK_CONST_METHOD0(GetStoreResultFilter, const MockStoreResultFilter*());
@@ -122,7 +124,7 @@ class PasswordManagerTest : public testing::Test {
EXPECT_CALL(client_, GetPasswordStore())
.WillRepeatedly(Return(store_.get()));
- EXPECT_CALL(*store_, GetSiteStatsMock(_)).Times(AnyNumber());
+ EXPECT_CALL(*store_, GetSiteStatsImpl(_)).Times(AnyNumber());
EXPECT_CALL(client_, GetDriver()).WillRepeatedly(Return(&driver_));
manager_.reset(new PasswordManager(&client_));
@@ -135,6 +137,9 @@ class PasswordManagerTest : public testing::Test {
.WillRepeatedly(Return(password_autofill_manager_.get()));
EXPECT_CALL(client_, DidLastPageLoadEncounterSSLErrors())
.WillRepeatedly(Return(false));
+
+ ON_CALL(client_, GetMainFrameURL())
+ .WillByDefault(ReturnRef(GURL::EmptyGURL()));
}
void TearDown() override {
@@ -386,7 +391,9 @@ TEST_F(PasswordManagerTest, FormSubmit) {
observed.push_back(form);
EXPECT_CALL(*store_, GetLogins(_, _))
.WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+ EXPECT_FALSE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsParsed(&driver_, observed);
+ EXPECT_TRUE(manager()->IsPasswordFieldDetectedOnPage());
manager()->OnPasswordFormsRendered(&driver_, observed, true);
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
@@ -402,6 +409,7 @@ TEST_F(PasswordManagerTest, FormSubmit) {
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
+ EXPECT_FALSE(manager()->IsPasswordFieldDetectedOnPage());
// Simulate saving the form, as if the info bar was accepted.
EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
@@ -588,16 +596,21 @@ TEST_F(PasswordManagerTest, SyncCredentialsNotSaved) {
}
// On a successful login with an updated password,
-// CredentialsFilter::ReportFormLoginSuccess should be called.
-TEST_F(PasswordManagerTest, ReportFormLoginSuccessCalled) {
- PasswordForm form(MakeSimpleForm());
+// CredentialsFilter::ReportFormLoginSuccess and CredentialsFilter::ShouldSave
+// should be called. The argument of ShouldSave shold be the submitted form.
+TEST_F(PasswordManagerTest, ReportFormLoginSuccessAndShouldSaveCalled) {
+ PasswordForm stored_form(MakeSimpleForm());
std::vector<PasswordForm> observed;
- observed.push_back(form);
+ PasswordForm observed_form = stored_form;
+ // Different values of |username_element| needed to ensure that it is the
+ // |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(form)));
+ .WillRepeatedly(WithArg<1>(InvokeConsumer(stored_form)));
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
@@ -605,11 +618,16 @@ TEST_F(PasswordManagerTest, ReportFormLoginSuccessCalled) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
- manager()->ProvisionallySavePassword(form);
+
+ manager()->ProvisionallySavePassword(observed_form);
// Chrome should recognise the successful login and call
// ReportFormLoginSuccess.
EXPECT_CALL(*client_.GetStoreResultFilter(), ReportFormLoginSuccess(_));
+
+ PasswordForm submitted_form = observed_form;
+ submitted_form.preferred = true;
+ EXPECT_CALL(*client_.GetStoreResultFilter(), ShouldSave(submitted_form));
EXPECT_CALL(*store_, UpdateLogin(_));
observed.clear();
manager()->OnPasswordFormsParsed(&driver_, observed);
@@ -738,6 +756,104 @@ TEST_F(PasswordManagerTest,
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
+TEST_F(PasswordManagerTest,
+ ShouldBlockPasswordForSameOriginButDifferentSchemeTest) {
+ constexpr struct {
+ const char* old_origin;
+ const char* new_origin;
+ bool result;
+ } kTestData[] = {
+ // Same origin and same scheme.
+ {"https://example.com/login", "https://example.com/login", false},
+ // Same host and same scheme, different port.
+ {"https://example.com:443/login", "https://example.com:444/login", false},
+ // Same host but different scheme (https to http).
+ {"https://example.com/login", "http://example.com/login", true},
+ // Same host but different scheme (http to https).
+ {"http://example.com/login", "https://example.com/login", false},
+ // Different TLD, same schemes.
+ {"https://example.com/login", "https://example.org/login", false},
+ // Different TLD, different schemes.
+ {"https://example.com/login", "http://example.org/login", false},
+ // Different subdomains, same schemes.
+ {"https://sub1.example.com/login", "https://sub2.example.org/login",
+ false},
+ };
+
+ PasswordForm form = MakeSimpleForm();
+ for (const auto& test_case : kTestData) {
+ SCOPED_TRACE(testing::Message("#test_case = ") << (&test_case - kTestData));
+ manager()->main_frame_url_ = GURL(test_case.old_origin);
+ form.origin = GURL(test_case.new_origin);
+ EXPECT_EQ(
+ test_case.result,
+ manager()->ShouldBlockPasswordForSameOriginButDifferentScheme(form));
+ }
+}
+
+// Tests whether two submissions to the same origin but different schemes
+// result in only saving the first submission, which has a secure scheme.
+TEST_F(PasswordManagerTest, AttemptedSavePasswordSameOriginInsecureScheme) {
+ PasswordForm secure_form(MakeSimpleForm());
+ secure_form.origin = GURL("https://example.com/login");
+ secure_form.action = GURL("https://example.com/login");
+ secure_form.signon_realm = secure_form.origin.spec();
+
+ PasswordForm insecure_form(MakeSimpleForm());
+ insecure_form.username_value = ASCIIToUTF16("compromised_user");
+ insecure_form.password_value = ASCIIToUTF16("C0mpr0m1s3d_P4ss");
+ insecure_form.origin = GURL("http://example.com/home");
+ insecure_form.action = GURL("http://example.com/home");
+ insecure_form.signon_realm = insecure_form.origin.spec();
+
+ EXPECT_CALL(*store_, GetLogins(_, _))
+ .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+
+ EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(Return(true));
+
+ EXPECT_CALL(client_, GetMainFrameURL())
+ .WillRepeatedly(ReturnRef(secure_form.origin));
+
+ // Parse, render and submit the secure form.
+ std::vector<PasswordForm> observed = {secure_form};
+ manager()->OnPasswordFormsParsed(&driver_, observed);
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+ OnPasswordFormSubmitted(secure_form);
+
+ // Make sure |PromptUserToSaveOrUpdatePassword| gets called, and the resulting
+ // form manager is saved.
+ std::unique_ptr<PasswordFormManager> form_manager_to_save;
+ EXPECT_CALL(client_,
+ PromptUserToSaveOrUpdatePasswordPtr(
+ _, CredentialSourceType::CREDENTIAL_SOURCE_PASSWORD_MANAGER))
+ .WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
+
+ EXPECT_CALL(client_, GetMainFrameURL())
+ .WillRepeatedly(ReturnRef(insecure_form.origin));
+
+ // Parse, render and submit the insecure form.
+ observed = {insecure_form};
+ manager()->OnPasswordFormsParsed(&driver_, observed);
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+ OnPasswordFormSubmitted(insecure_form);
+
+ // Expect no further calls to |ProptUserToSaveOrUpdatePassword| due to
+ // insecure origin.
+ EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_, _)).Times(0);
+
+ // Trigger call to |ProvisionalSavePassword| by rendering a page without
+ // forms.
+ observed.clear();
+ manager()->OnPasswordFormsParsed(&driver_, observed);
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+ // Make sure that the form saved by the user is indeed the secure form.
+ ASSERT_TRUE(form_manager_to_save);
+ EXPECT_THAT(form_manager_to_save->pending_credentials(),
+ FormMatches(secure_form));
+}
+
// Create a form with both a new and current password element. Let the current
// password value be non-empty and the new password value be empty and submit
// the form. While normally saving the new password is preferred (on change
@@ -1083,12 +1199,14 @@ TEST_F(PasswordManagerTest, SaveFormFetchedAfterSubmit) {
PasswordForm form(MakeSimpleForm());
observed.push_back(form);
- PasswordStoreConsumer* form_manager = nullptr;
// No call-back from store after GetLogins is called emulates that
// PasswordStore did not fetch a form in time before submission.
- EXPECT_CALL(*store_, GetLogins(_, _)).WillOnce(SaveArg<1>(&form_manager));
+ 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();
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
@@ -1097,8 +1215,8 @@ TEST_F(PasswordManagerTest, SaveFormFetchedAfterSubmit) {
// Emulate fetching password form from PasswordStore after submission but
// before post-navigation load.
ASSERT_TRUE(form_manager);
- form_manager->OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<PasswordForm>>());
+ static_cast<FormFetcherImpl*>(form_manager->form_fetcher())
+ ->OnGetPasswordStoreResults(std::vector<std::unique_ptr<PasswordForm>>());
std::unique_ptr<PasswordFormManager> form_manager_to_save;
EXPECT_CALL(client_,
@@ -1373,13 +1491,13 @@ TEST_F(PasswordManagerTest,
manager()->OnPasswordFormsParsed(&driver_, observed);
manager()->OnPasswordFormsRendered(&driver_, observed, true);
- PasswordStoreConsumer* consumer = nullptr;
- EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(form), _))
- .WillOnce(SaveArg<1>(&consumer));
+ EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(form), _));
manager()->SetGenerationElementAndReasonForForm(&driver_, form,
base::string16(), false);
+ ASSERT_EQ(1u, manager()->pending_login_managers().size());
PasswordFormManager* form_manager =
- static_cast<PasswordFormManager*>(consumer);
+ manager()->pending_login_managers().front().get();
+
EXPECT_FALSE(form_manager->has_generated_password());
}
@@ -1436,19 +1554,20 @@ TEST_F(PasswordManagerTest, UpdateFormManagers) {
// Seeing some forms should result in creating PasswordFormManagers and
// querying PasswordStore. Calling UpdateFormManagers should result in
// querying the store again.
- PasswordStoreConsumer* consumer = nullptr;
- EXPECT_CALL(*store_, GetLogins(_, _)).WillOnce(SaveArg<1>(&consumer));
+ EXPECT_CALL(*store_, GetLogins(_, _));
PasswordForm form;
std::vector<PasswordForm> observed;
observed.push_back(form);
manager()->OnPasswordFormsParsed(&driver_, observed);
+ ASSERT_EQ(1u, manager()->pending_login_managers().size());
+ PasswordFormManager* form_manager =
+ manager()->pending_login_managers().front().get();
// The first GetLogins should have fired, but to unblock the second, we need
// to first send a response from the store (to be ignored).
- ASSERT_TRUE(consumer);
- consumer->OnGetPasswordStoreResults(
- std::vector<std::unique_ptr<PasswordForm>>());
+ static_cast<FormFetcherImpl*>(form_manager->form_fetcher())
+ ->OnGetPasswordStoreResults(std::vector<std::unique_ptr<PasswordForm>>());
EXPECT_CALL(*store_, GetLogins(_, _));
manager()->UpdateFormManagers();
}
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 6383418df27..ae5e3fc3443 100644
--- a/chromium/components/password_manager/core/browser/password_manager_util.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_util.cc
@@ -26,8 +26,8 @@ password_manager::PasswordSyncState GetPasswordSyncState(
}
void FindDuplicates(
- ScopedVector<autofill::PasswordForm>* forms,
- ScopedVector<autofill::PasswordForm>* duplicates,
+ std::vector<std::unique_ptr<autofill::PasswordForm>>* forms,
+ std::vector<std::unique_ptr<autofill::PasswordForm>>* duplicates,
std::vector<std::vector<autofill::PasswordForm*>>* tag_groups) {
if (forms->empty())
return;
@@ -36,27 +36,25 @@ void FindDuplicates(
// duplicates. Therefore, the caller should try to preserve it.
std::stable_sort(forms->begin(), forms->end(), autofill::LessThanUniqueKey());
- ScopedVector<autofill::PasswordForm> unique_forms;
- unique_forms.push_back(forms->front());
- forms->front() = nullptr;
+ std::vector<std::unique_ptr<autofill::PasswordForm>> unique_forms;
+ unique_forms.push_back(std::move(forms->front()));
if (tag_groups) {
tag_groups->clear();
tag_groups->push_back(std::vector<autofill::PasswordForm*>());
- tag_groups->front().push_back(unique_forms.front());
+ tag_groups->front().push_back(unique_forms.front().get());
}
for (auto it = forms->begin() + 1; it != forms->end(); ++it) {
if (ArePasswordFormUniqueKeyEqual(**it, *unique_forms.back())) {
- duplicates->push_back(*it);
if (tag_groups)
- tag_groups->back().push_back(*it);
+ tag_groups->back().push_back(it->get());
+ duplicates->push_back(std::move(*it));
} else {
- unique_forms.push_back(*it);
if (tag_groups)
- tag_groups->push_back(std::vector<autofill::PasswordForm*>(1, *it));
+ tag_groups->push_back(
+ std::vector<autofill::PasswordForm*>(1, it->get()));
+ unique_forms.push_back(std::move(*it));
}
- *it = nullptr;
}
- forms->weak_clear();
forms->swap(unique_forms);
}
@@ -82,17 +80,6 @@ void TrimUsernameOnlyCredentials(
});
}
-std::vector<std::unique_ptr<autofill::PasswordForm>> ConvertScopedVector(
- ScopedVector<autofill::PasswordForm> old_vector) {
- std::vector<std::unique_ptr<autofill::PasswordForm>> new_vector;
- new_vector.reserve(old_vector.size());
- for (auto* form : old_vector) {
- new_vector.push_back(base::WrapUnique(form));
- }
- old_vector.weak_clear(); // All owned by |new_vector| by now.
- return new_vector;
-}
-
bool IsLoggingActive(const password_manager::PasswordManagerClient* client) {
const password_manager::LogManager* log_manager = client->GetLogManager();
return log_manager && log_manager->IsLoggingActive();
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 8a1365c78bf..68fc65b47a1 100644
--- a/chromium/components/password_manager/core/browser/password_manager_util.h
+++ b/chromium/components/password_manager/core/browser/password_manager_util.h
@@ -9,7 +9,6 @@
#include <vector>
#include "base/callback.h"
-#include "base/memory/scoped_vector.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "ui/gfx/native_widget_types.h"
@@ -35,8 +34,8 @@ password_manager::PasswordSyncState GetPasswordSyncState(
// the sync tag. The first element in each group is one from |forms|. It's
// followed by the duplicates.
void FindDuplicates(
- ScopedVector<autofill::PasswordForm>* forms,
- ScopedVector<autofill::PasswordForm>* duplicates,
+ std::vector<std::unique_ptr<autofill::PasswordForm>>* forms,
+ std::vector<std::unique_ptr<autofill::PasswordForm>>* duplicates,
std::vector<std::vector<autofill::PasswordForm*>>* tag_groups);
// Removes Android username-only credentials from |android_credentials|.
@@ -44,11 +43,6 @@ void FindDuplicates(
void TrimUsernameOnlyCredentials(
std::vector<std::unique_ptr<autofill::PasswordForm>>* android_credentials);
-// TODO(crbug.com/555132): Remove this when the migration from ScopedVector is
-// finished for PasswordForm.
-std::vector<std::unique_ptr<autofill::PasswordForm>> ConvertScopedVector(
- ScopedVector<autofill::PasswordForm> old_vector);
-
// A convenience function for testing that |client| has a non-null LogManager
// and that that LogManager returns true for IsLoggingActive. This function can
// be removed once PasswordManagerClient::GetLogManager is implemented on iOS
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
new file mode 100644
index 00000000000..24a7c4f8e5e
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_reuse_detection_manager.cc
@@ -0,0 +1,65 @@
+// 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/core/browser/password_reuse_detection_manager.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"
+
+namespace password_manager {
+
+namespace {
+constexpr size_t kMaxNumberOfCharactersToStore = 30;
+}
+
+PasswordReuseDetectionManager::PasswordReuseDetectionManager(
+ PasswordManagerClient* client)
+ : client_(client) {
+ DCHECK(client_);
+}
+
+PasswordReuseDetectionManager::~PasswordReuseDetectionManager() {}
+
+void PasswordReuseDetectionManager::DidNavigateMainFrame(
+ const GURL& main_frame_url) {
+ main_frame_url_ = main_frame_url;
+ input_characters_.clear();
+}
+
+void PasswordReuseDetectionManager::OnKeyPressed(const base::string16& text) {
+ input_characters_ += text;
+ if (input_characters_.size() > kMaxNumberOfCharactersToStore) {
+ input_characters_.erase(
+ 0, input_characters_.size() - kMaxNumberOfCharactersToStore);
+ }
+
+ PasswordStore* store = client_->GetPasswordStore();
+ if (!store)
+ return;
+ store->CheckReuse(input_characters_, main_frame_url_.GetOrigin().spec(),
+ this);
+}
+
+void PasswordReuseDetectionManager::OnReuseFound(
+ const base::string16& password,
+ const std::string& saved_domain,
+ int saved_passwords,
+ int number_matches) {
+ std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
+ if (password_manager_util::IsLoggingActive(client_)) {
+ logger.reset(
+ new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
+ logger->LogString(BrowserSavePasswordProgressLogger::STRING_REUSE_FOUND,
+ saved_domain);
+ }
+
+ metrics_util::LogPasswordReuse(
+ password.size(), saved_passwords, number_matches,
+ client_->GetPasswordManager()->IsPasswordFieldDetectedOnPage());
+}
+
+} // 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
new file mode 100644
index 00000000000..eaf9384fd99
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_reuse_detection_manager.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_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTION_MANAGER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTION_MANAGER_H_
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/password_manager/core/browser/password_reuse_detector_consumer.h"
+#include "url/gurl.h"
+
+namespace password_manager {
+
+class PasswordManagerClient;
+
+// Class for managing password reuse detection. Now it receives keystrokes and
+// does nothing with them. TODO(crbug.com/657041): write other features of this
+// class when they are implemented. This class is one per-tab.
+class PasswordReuseDetectionManager : public PasswordReuseDetectorConsumer {
+ public:
+ explicit PasswordReuseDetectionManager(PasswordManagerClient* client);
+ ~PasswordReuseDetectionManager() override;
+
+ void DidNavigateMainFrame(const GURL& main_frame_url);
+ void OnKeyPressed(const base::string16& text);
+
+ // PasswordReuseDetectorConsumer
+ void OnReuseFound(const base::string16& password,
+ const std::string& saved_domain,
+ int saved_passwords,
+ int number_matches) override;
+
+ private:
+ PasswordManagerClient* client_;
+ base::string16 input_characters_;
+ GURL main_frame_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordReuseDetectionManager);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTION_MANAGER_H_
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
new file mode 100644
index 00000000000..a2a4557eceb
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc
@@ -0,0 +1,90 @@
+// 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/password_manager/core/browser/password_reuse_detection_manager.h"
+
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.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 "url/gurl.h"
+
+using base::ASCIIToUTF16;
+using testing::AnyNumber;
+using testing::_;
+
+namespace password_manager {
+
+namespace {
+
+constexpr size_t kMaxNumberOfCharactersToStore = 30;
+
+class MockPasswordManagerClient : public StubPasswordManagerClient {
+ public:
+ MockPasswordManagerClient() = default;
+ ~MockPasswordManagerClient() override = default;
+
+ MOCK_CONST_METHOD0(GetPasswordStore, PasswordStore*());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockPasswordManagerClient);
+};
+
+class PasswordReuseDetectionManagerTest : public ::testing::Test {
+ public:
+ PasswordReuseDetectionManagerTest() {}
+ void SetUp() override {
+ store_ = new testing::StrictMock<MockPasswordStore>;
+ CHECK(store_->Init(syncer::SyncableService::StartSyncFlare()));
+ }
+ void TearDown() override {
+ store_->ShutdownOnUIThread();
+ store_ = nullptr;
+ }
+
+ protected:
+ // It's needed for an initialisation of thread runners that are used in
+ // MockPasswordStore.
+ base::MessageLoop message_loop_;
+ MockPasswordManagerClient client_;
+ scoped_refptr<MockPasswordStore> store_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordReuseDetectionManagerTest);
+};
+
+// Verify that CheckReuse is called on each key pressed event with an argument
+// equal to the last 30 keystrokes typed after the last main frame navigaion.
+TEST_F(PasswordReuseDetectionManagerTest, CheckReuseCalled) {
+ const GURL gurls[] = {GURL("https://www.example.com"),
+ GURL("https://www.otherexample.com")};
+ const base::string16 input[] = {
+ base::ASCIIToUTF16(
+ "1234567890abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ"),
+ base::ASCIIToUTF16("?<>:'{}ABCDEF")};
+
+ EXPECT_CALL(client_, GetPasswordStore())
+ .WillRepeatedly(testing::Return(store_.get()));
+ PasswordReuseDetectionManager manager(&client_);
+
+ for (size_t test = 0; test < arraysize(gurls); ++test) {
+ manager.DidNavigateMainFrame(gurls[test]);
+ for (size_t i = 0; i < input[test].size(); ++i) {
+ base::string16 expected_input = input[test].substr(0, i + 1);
+ if (expected_input.size() > kMaxNumberOfCharactersToStore)
+ expected_input = expected_input.substr(expected_input.size() -
+ kMaxNumberOfCharactersToStore);
+ EXPECT_CALL(
+ *store_,
+ CheckReuse(expected_input, gurls[test].GetOrigin().spec(), &manager));
+ manager.OnKeyPressed(input[test].substr(i, 1));
+ testing::Mock::VerifyAndClearExpectations(store_.get());
+ }
+ }
+}
+
+} // 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
new file mode 100644
index 00000000000..dd9b2a94a5a
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector.cc
@@ -0,0 +1,119 @@
+// 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/core/browser/password_reuse_detector.h"
+
+#include <algorithm>
+
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/password_reuse_detector_consumer.h"
+#include "components/password_manager/core/browser/psl_matching_helper.h"
+
+namespace password_manager {
+
+namespace {
+// Minimum number of characters in a password for finding it as password reuse.
+// It does not make sense to consider short strings for password reuse, since it
+// is quite likely that they are parts of common words.
+constexpr size_t kMinPasswordLengthToCheck = 8;
+
+// Returns true iff |suffix_candidate| is a suffix of |str|.
+bool IsSuffix(const base::string16& str,
+ const base::string16& suffix_candidate) {
+ if (str.size() < suffix_candidate.size())
+ return false;
+ return std::equal(suffix_candidate.rbegin(), suffix_candidate.rend(),
+ str.rbegin());
+}
+
+} // namespace
+
+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() {}
+
+PasswordReuseDetector::~PasswordReuseDetector() {}
+
+void PasswordReuseDetector::OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+ for (const auto& form : results)
+ AddPassword(*form);
+}
+
+void PasswordReuseDetector::OnLoginsChanged(
+ const PasswordStoreChangeList& changes) {
+ for (const auto& change : changes) {
+ if (change.type() == PasswordStoreChange::ADD ||
+ change.type() == PasswordStoreChange::UPDATE)
+ AddPassword(change.form());
+ }
+}
+
+void PasswordReuseDetector::CheckReuse(
+ const base::string16& input,
+ const std::string& domain,
+ PasswordReuseDetectorConsumer* consumer) {
+ DCHECK(consumer);
+ if (input.size() < kMinPasswordLengthToCheck)
+ return;
+
+ const std::string registry_controlled_domain =
+ GetRegistryControlledDomain(GURL(domain));
+ auto passwords_iterator = FindSavedPassword(input);
+ if (passwords_iterator == passwords_.end())
+ return;
+
+ const std::set<std::string>& domains = passwords_iterator->second;
+ DCHECK(!domains.empty());
+ if (domains.find(registry_controlled_domain) == domains.end()) {
+ // Return only one domain.
+ const std::string& saved_domain = *domains.begin();
+ consumer->OnReuseFound(passwords_iterator->first, saved_domain,
+ saved_passwords_, domains.size());
+ return;
+ }
+}
+
+void PasswordReuseDetector::AddPassword(const autofill::PasswordForm& form) {
+ if (form.password_value.size() < kMinPasswordLengthToCheck)
+ return;
+ GURL signon_realm(form.signon_realm);
+ const std::string domain = GetRegistryControlledDomain(signon_realm);
+ std::set<std::string>& domains = passwords_[form.password_value];
+ if (domains.find(domain) == domains.end()) {
+ ++saved_passwords_;
+ domains.insert(domain);
+ }
+}
+
+PasswordReuseDetector::passwords_iterator
+PasswordReuseDetector::FindSavedPassword(const base::string16& input) {
+ // Keys in |passwords_| are ordered by lexicographical order of reversed
+ // strings. In order to check a password reuse a key of |passwords_| that is a
+ // suffix of |input| should be found. The longest such key should be the
+ // largest key in the |passwords_| keys order that is equal or smaller to
+ // |input|.
+ if (passwords_.empty())
+ return passwords_.end();
+
+ // lower_bound returns the first key that is bigger or equal to input.
+ auto it = passwords_.lower_bound(input);
+ if (it != passwords_.end() && it->first == input) {
+ // If the key is equal then a saved password is found.
+ return it;
+ }
+
+ // Otherwise the previous key is a candidate for password reuse.
+ if (it == passwords_.begin())
+ return passwords_.end();
+
+ --it;
+ return IsSuffix(input, it->first) ? it : passwords_.end();
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_reuse_detector.h b/chromium/components/password_manager/core/browser/password_reuse_detector.h
new file mode 100644
index 00000000000..967c941a9cd
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector.h
@@ -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.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTOR_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTOR_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/password_manager/core/browser/password_store_change.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+
+namespace password_manager {
+
+class PasswordReuseDetectorConsumer;
+
+// Comparator that compares reversed strings.
+struct ReverseStringLess {
+ bool operator()(const base::string16& lhs, const base::string16& rhs) const;
+};
+
+// 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.
+// It stores passwords in memory and CheckReuse() can be used for finding
+// a password reuse.
+class PasswordReuseDetector : public PasswordStoreConsumer {
+ public:
+ PasswordReuseDetector();
+ ~PasswordReuseDetector() override;
+
+ // PasswordStoreConsumer
+ void OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+
+ // Add new or updated passwords from |changes| to internal password index.
+ void OnLoginsChanged(const PasswordStoreChangeList& changes);
+
+ // Checks that some suffix of |input| equals to a password saved on another
+ // registry controlled domain than |domain|.
+ // If such suffix is found, |consumer|->OnReuseFound() is called on the same
+ // thread on which this method is called.
+ // |consumer| should not be null.
+ void CheckReuse(const base::string16& input,
+ const std::string& domain,
+ PasswordReuseDetectorConsumer* consumer);
+
+ private:
+ using passwords_iterator = std::map<base::string16,
+ std::set<std::string>,
+ ReverseStringLess>::const_iterator;
+
+ // Add password from |form| to |passwords_|.
+ void AddPassword(const autofill::PasswordForm& form);
+
+ // Returns the iterator to |passwords_| that corresponds to the longest key in
+ // |passwords_| that is a suffix of |input|. Returns passwords_.end() in case
+ // when no key in |passwords_| is a prefix of |input|.
+ passwords_iterator FindSavedPassword(const base::string16& input);
+
+ // Contains all passwords.
+ // A key is a password.
+ // A value is a set of registry controlled domains on which the password
+ // saved.
+ std::map<base::string16, std::set<std::string>, ReverseStringLess> passwords_;
+
+ // Number of passwords in |passwords_|, each password is calculated the number
+ // of times how many different sites it's saved on.
+ int saved_passwords_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordReuseDetector);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTOR_H_
diff --git a/chromium/components/password_manager/core/browser/password_reuse_detector_consumer.cc b/chromium/components/password_manager/core/browser/password_reuse_detector_consumer.cc
new file mode 100644
index 00000000000..9f64c86fdb1
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector_consumer.cc
@@ -0,0 +1,13 @@
+// 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/core/browser/password_reuse_detector_consumer.h"
+
+namespace password_manager {
+
+PasswordReuseDetectorConsumer::PasswordReuseDetectorConsumer() {}
+
+PasswordReuseDetectorConsumer::~PasswordReuseDetectorConsumer() {}
+
+} // namespace password_manager
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
new file mode 100644
index 00000000000..254f29f79d9
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector_consumer.h
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTOR_CONSUMER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTOR_CONSUMER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+
+namespace password_manager {
+
+// Callback interface for receiving a password reuse event.
+class PasswordReuseDetectorConsumer
+ : public base::SupportsWeakPtr<PasswordReuseDetectorConsumer> {
+ public:
+ PasswordReuseDetectorConsumer();
+ virtual ~PasswordReuseDetectorConsumer();
+
+ // Called when a password reuse is found.
+ // |saved_domain| is the domain on which |password| is saved.
+ // |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,
+ const std::string& saved_domain,
+ int saved_passwords,
+ int number_matches) = 0;
+};
+
+} // namespace password_manager
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REUSE_DETECTOR_CONSUMER_H_
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
new file mode 100644
index 00000000000..dac4dd694cb
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc
@@ -0,0 +1,189 @@
+// 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/core/browser/password_reuse_detector.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/password_manager_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using autofill::PasswordForm;
+using base::ASCIIToUTF16;
+using testing::_;
+
+namespace password_manager {
+
+namespace {
+
+std::vector<std::pair<std::string, std::string>> GetTestDomainsPasswords() {
+ return {
+ {"https://accounts.google.com", "password"},
+ {"https://facebook.com", "123456789"},
+ {"https://a.appspot.com", "abcdefghi"},
+ {"https://twitter.com", "short"},
+ {"https://example1.com", "secretword"},
+ {"https://example2.com", "secretword"},
+ };
+}
+
+std::unique_ptr<PasswordForm> GetForm(
+ const std::pair<std::string, std::string>& domain_password) {
+ std::unique_ptr<PasswordForm> form(new PasswordForm);
+ form->signon_realm = domain_password.first;
+ form->password_value = ASCIIToUTF16(domain_password.second);
+ return form;
+}
+
+std::vector<std::unique_ptr<PasswordForm>> GetForms(
+ const std::vector<std::pair<std::string, std::string>>& domains_passwords) {
+ std::vector<std::unique_ptr<PasswordForm>> result;
+ for (const auto& domain_password : domains_passwords)
+ result.push_back(GetForm(domain_password));
+ return result;
+}
+
+PasswordStoreChangeList GetChangeList(
+ PasswordStoreChange::Type type,
+ const std::vector<std::unique_ptr<PasswordForm>>& forms) {
+ PasswordStoreChangeList changes;
+ for (const auto& form : forms)
+ changes.push_back(PasswordStoreChange(type, *form));
+
+ return changes;
+}
+
+TEST(PasswordReuseDetectorTest, TypingPasswordOnDifferentSite) {
+ PasswordReuseDetector reuse_detector;
+ reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
+ MockPasswordReuseDetectorConsumer mockConsumer;
+
+ EXPECT_CALL(mockConsumer, OnReuseFound(_, _, _, _)).Times(0);
+ reuse_detector.CheckReuse(ASCIIToUTF16("123passwo"), "https://evil.com",
+ &mockConsumer);
+ reuse_detector.CheckReuse(ASCIIToUTF16("123passwor"), "https://evil.com",
+ &mockConsumer);
+ testing::Mock::VerifyAndClearExpectations(&mockConsumer);
+
+ EXPECT_CALL(mockConsumer,
+ OnReuseFound(ASCIIToUTF16("password"), "google.com", 5, 1));
+ reuse_detector.CheckReuse(ASCIIToUTF16("123password"), "https://evil.com",
+ &mockConsumer);
+ testing::Mock::VerifyAndClearExpectations(&mockConsumer);
+
+ EXPECT_CALL(mockConsumer,
+ OnReuseFound(ASCIIToUTF16("password"), "google.com", 5, 1));
+ reuse_detector.CheckReuse(ASCIIToUTF16("password"), "https://evil.com",
+ &mockConsumer);
+
+ testing::Mock::VerifyAndClearExpectations(&mockConsumer);
+
+ EXPECT_CALL(mockConsumer,
+ OnReuseFound(ASCIIToUTF16("secretword"), "example1.com", 5, 2));
+ reuse_detector.CheckReuse(ASCIIToUTF16("abcdsecretword"), "https://evil.com",
+ &mockConsumer);
+}
+
+TEST(PasswordReuseDetectorTest, PSLMatchNoReuseEvent) {
+ PasswordReuseDetector reuse_detector;
+ reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
+ MockPasswordReuseDetectorConsumer mockConsumer;
+
+ EXPECT_CALL(mockConsumer, OnReuseFound(_, _, _, _)).Times(0);
+ reuse_detector.CheckReuse(ASCIIToUTF16("123456789"), "https://m.facebook.com",
+ &mockConsumer);
+}
+
+TEST(PasswordReuseDetectorTest, NoPSLMatchReuseEvent) {
+ PasswordReuseDetector reuse_detector;
+ reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
+ MockPasswordReuseDetectorConsumer mockConsumer;
+
+ // a.appspot.com and b.appspot.com are not PSL matches. So reuse event should
+ // be raised.
+ EXPECT_CALL(mockConsumer,
+ OnReuseFound(ASCIIToUTF16("abcdefghi"), "a.appspot.com", 5, 1));
+ reuse_detector.CheckReuse(ASCIIToUTF16("abcdefghi"), "https://b.appspot.com",
+ &mockConsumer);
+}
+
+TEST(PasswordReuseDetectorTest, TooShortPasswordNoReuseEvent) {
+ PasswordReuseDetector reuse_detector;
+ reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
+ MockPasswordReuseDetectorConsumer mockConsumer;
+
+ EXPECT_CALL(mockConsumer, OnReuseFound(_, _, _, _)).Times(0);
+ reuse_detector.CheckReuse(ASCIIToUTF16("short"), "evil.com", &mockConsumer);
+}
+
+TEST(PasswordReuseDetectorTest, PasswordNotInputSuffixNoReuseEvent) {
+ PasswordReuseDetector reuse_detector;
+ reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
+ MockPasswordReuseDetectorConsumer mockConsumer;
+
+ EXPECT_CALL(mockConsumer, OnReuseFound(_, _, _, _)).Times(0);
+ reuse_detector.CheckReuse(ASCIIToUTF16("password123"), "https://evil.com",
+ &mockConsumer);
+ reuse_detector.CheckReuse(ASCIIToUTF16("123password456"), "https://evil.com",
+ &mockConsumer);
+}
+
+TEST(PasswordReuseDetectorTest, OnLoginsChanged) {
+ for (PasswordStoreChange::Type type :
+ {PasswordStoreChange::ADD, PasswordStoreChange::UPDATE,
+ PasswordStoreChange::REMOVE}) {
+ PasswordReuseDetector reuse_detector;
+ PasswordStoreChangeList changes =
+ GetChangeList(type, GetForms(GetTestDomainsPasswords()));
+ reuse_detector.OnLoginsChanged(changes);
+ MockPasswordReuseDetectorConsumer mockConsumer;
+
+ if (type == PasswordStoreChange::REMOVE) {
+ EXPECT_CALL(mockConsumer, OnReuseFound(_, _, _, _)).Times(0);
+ } else {
+ EXPECT_CALL(mockConsumer,
+ OnReuseFound(ASCIIToUTF16("password"), "google.com", 5, 1));
+ }
+ reuse_detector.CheckReuse(ASCIIToUTF16("123password"), "https://evil.com",
+ &mockConsumer);
+ }
+}
+
+TEST(PasswordReuseDetectorTest, CheckLongestPasswordMatchReturn) {
+ const std::vector<std::pair<std::string, std::string>> domain_passwords = {
+ {"https://example1.com", "234567890"},
+ {"https://example2.com", "01234567890"},
+ {"https://example3.com", "1234567890"},
+ };
+
+ PasswordReuseDetector reuse_detector;
+ reuse_detector.OnGetPasswordStoreResults(GetForms(domain_passwords));
+
+ MockPasswordReuseDetectorConsumer mockConsumer;
+
+ EXPECT_CALL(mockConsumer,
+ OnReuseFound(ASCIIToUTF16("01234567890"), "example2.com", 3, 1));
+ reuse_detector.CheckReuse(ASCIIToUTF16("abcd01234567890"), "https://evil.com",
+ &mockConsumer);
+ testing::Mock::VerifyAndClearExpectations(&mockConsumer);
+
+ EXPECT_CALL(mockConsumer,
+ OnReuseFound(ASCIIToUTF16("1234567890"), "example3.com", 3, 1));
+ reuse_detector.CheckReuse(ASCIIToUTF16("1234567890"), "https://evil.com",
+ &mockConsumer);
+ testing::Mock::VerifyAndClearExpectations(&mockConsumer);
+
+ EXPECT_CALL(mockConsumer, OnReuseFound(_, _, _, _)).Times(0);
+ reuse_detector.CheckReuse(ASCIIToUTF16("34567890"), "https://evil.com",
+ &mockConsumer);
+}
+
+} // namespace
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_store.cc b/chromium/components/password_manager/core/browser/password_store.cc
index 93ddc39f499..3c48a6a8da1 100644
--- a/chromium/components/password_manager/core/browser/password_store.cc
+++ b/chromium/components/password_manager/core/browser/password_store.cc
@@ -53,12 +53,30 @@ void PasswordStore::GetLoginsRequest::NotifyConsumerWithResults(
}
void PasswordStore::GetLoginsRequest::NotifyWithSiteStatistics(
- std::vector<std::unique_ptr<InteractionsStats>> stats) {
+ std::vector<InteractionsStats> stats) {
origin_task_runner_->PostTask(
FROM_HERE, base::Bind(&PasswordStoreConsumer::OnGetSiteStatistics,
consumer_weak_, base::Passed(&stats)));
}
+PasswordStore::CheckReuseRequest::CheckReuseRequest(
+ PasswordReuseDetectorConsumer* consumer)
+ : origin_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ consumer_weak_(consumer->AsWeakPtr()) {}
+
+PasswordStore::CheckReuseRequest::~CheckReuseRequest() {}
+
+void PasswordStore::CheckReuseRequest::OnReuseFound(
+ const base::string16& password,
+ const std::string& saved_domain,
+ int saved_passwords,
+ int number_matches) {
+ origin_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&PasswordReuseDetectorConsumer::OnReuseFound, consumer_weak_,
+ password, saved_domain, saved_passwords, number_matches));
+}
+
PasswordStore::FormDigest::FormDigest(autofill::PasswordForm::Scheme new_scheme,
const std::string& new_signon_realm,
const GURL& new_origin)
@@ -69,6 +87,16 @@ PasswordStore::FormDigest::FormDigest(const PasswordForm& form)
signon_realm(form.signon_realm),
origin(form.origin) {}
+PasswordStore::FormDigest::FormDigest(const FormDigest& other) = default;
+
+PasswordStore::FormDigest::FormDigest(FormDigest&& other) = default;
+
+PasswordStore::FormDigest& PasswordStore::FormDigest::operator=(
+ const FormDigest& other) = default;
+
+PasswordStore::FormDigest& PasswordStore::FormDigest::operator=(
+ FormDigest&& other) = default;
+
bool PasswordStore::FormDigest::operator==(const FormDigest& other) const {
return scheme == other.scheme && signon_realm == other.signon_realm &&
origin == other.origin;
@@ -81,11 +109,10 @@ PasswordStore::PasswordStore(
db_thread_runner_(db_thread_runner),
observers_(new base::ObserverListThreadSafe<Observer>()),
is_propagating_password_changes_to_web_credentials_enabled_(false),
- shutdown_called_(false) {
-}
+ shutdown_called_(false) {}
bool PasswordStore::Init(const syncer::SyncableService::StartSyncFlare& flare) {
- ScheduleTask(base::Bind(&PasswordStore::InitSyncableService, this, flare));
+ ScheduleTask(base::Bind(&PasswordStore::InitOnBackgroundThread, this, flare));
return true;
}
@@ -262,7 +289,7 @@ bool PasswordStore::ScheduleTask(const base::Closure& task) {
}
void PasswordStore::ShutdownOnUIThread() {
- ScheduleTask(base::Bind(&PasswordStore::DestroySyncableService, this));
+ ScheduleTask(base::Bind(&PasswordStore::DestroyOnBackgroundThread, this));
// The AffiliationService must be destroyed from the main thread.
affiliated_match_helper_.reset();
shutdown_called_ = true;
@@ -275,6 +302,14 @@ PasswordStore::GetPasswordSyncableService() {
return syncable_service_->AsWeakPtr();
}
+void PasswordStore::CheckReuse(const base::string16& input,
+ const std::string& domain,
+ PasswordReuseDetectorConsumer* consumer) {
+ auto check_reuse_request = base::MakeUnique<CheckReuseRequest>(consumer);
+ ScheduleTask(base::Bind(&PasswordStore::CheckReuseImpl, this,
+ base::Passed(&check_reuse_request), input, domain));
+}
+
PasswordStore::~PasswordStore() {
DCHECK(shutdown_called_);
}
@@ -339,9 +374,18 @@ void PasswordStore::NotifyLoginsChanged(
observers_->Notify(FROM_HERE, &Observer::OnLoginsChanged, changes);
if (syncable_service_)
syncable_service_->ActOnPasswordStoreChanges(changes);
+ if (reuse_detector_)
+ reuse_detector_->OnLoginsChanged(changes);
}
}
+void PasswordStore::CheckReuseImpl(std::unique_ptr<CheckReuseRequest> request,
+ const base::string16& input,
+ const std::string& domain) {
+ if (reuse_detector_)
+ reuse_detector_->CheckReuse(input, domain, request.get());
+}
+
void PasswordStore::Schedule(
void (PasswordStore::*func)(std::unique_ptr<GetLoginsRequest>),
PasswordStoreConsumer* consumer) {
@@ -492,12 +536,6 @@ void PasswordStore::GetLoginsWithAffiliationsImpl(
FillMatchingLogins({PasswordForm::SCHEME_HTML, realm, GURL()}));
for (auto& result : more_results)
result->is_affiliation_based_match = true;
- more_results.erase(
- std::remove_if(more_results.begin(), more_results.end(),
- [](const std::unique_ptr<PasswordForm>& result) {
- return !result->federation_origin.unique();
- }),
- more_results.end());
password_manager_util::TrimUsernameOnlyCredentials(&more_results);
const size_t results_count = results.size();
results.resize(results_count + more_results.size());
@@ -649,17 +687,25 @@ void PasswordStore::ScheduleUpdateAffiliatedWebLoginsImpl(
updated_android_form, affiliated_web_realms));
}
-void PasswordStore::InitSyncableService(
+void PasswordStore::InitOnBackgroundThread(
const syncer::SyncableService::StartSyncFlare& flare) {
DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
DCHECK(!syncable_service_);
syncable_service_.reset(new PasswordSyncableService(this));
syncable_service_->InjectStartSyncFlare(flare);
+ reuse_detector_.reset(new PasswordReuseDetector);
+#if !defined(OS_MACOSX)
+ // TODO(crbug.com/668155): For non-migrated keychain users it can lead to
+ // hundreds of requests to unlock keychain.
+ GetAutofillableLoginsImpl(
+ base::MakeUnique<GetLoginsRequest>(reuse_detector_.get()));
+#endif
}
-void PasswordStore::DestroySyncableService() {
+void PasswordStore::DestroyOnBackgroundThread() {
DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
syncable_service_.reset();
+ reuse_detector_.reset();
}
std::ostream& operator<<(std::ostream& os,
diff --git a/chromium/components/password_manager/core/browser/password_store.h b/chromium/components/password_manager/core/browser/password_store.h
index fa757c9f0e9..c6151cc93e1 100644
--- a/chromium/components/password_manager/core/browser/password_store.h
+++ b/chromium/components/password_manager/core/browser/password_store.h
@@ -17,6 +17,8 @@
#include "base/single_thread_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_detector.h"
+#include "components/password_manager/core/browser/password_reuse_detector_consumer.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"
@@ -71,6 +73,10 @@ class PasswordStore : protected PasswordStoreSync,
const std::string& signon_realm,
const GURL& origin);
explicit FormDigest(const autofill::PasswordForm& form);
+ FormDigest(const FormDigest& other);
+ FormDigest(FormDigest&& other);
+ FormDigest& operator=(const FormDigest& other);
+ FormDigest& operator=(FormDigest&& other);
bool operator==(const FormDigest& other) const;
autofill::PasswordForm::Scheme scheme;
@@ -167,9 +173,6 @@ class PasswordStore : protected PasswordStoreSync,
// Searches for a matching PasswordForm, and notifies |consumer| on
// completion. The request will be cancelled if the consumer is destroyed.
- // TODO(engedy): Currently, this will not return federated logins saved from
- // Android applications that are affiliated with the realm of |form|. Need to
- // decide if this is the desired behavior. See: https://crbug.com/539844.
virtual void GetLogins(const FormDigest& form,
PasswordStoreConsumer* consumer);
@@ -220,6 +223,15 @@ class PasswordStore : protected PasswordStoreSync,
base::WeakPtr<syncer::SyncableService> GetPasswordSyncableService();
+ // Checks that some suffix of |input| equals to a password saved on another
+ // registry controlled domain than |domain|.
+ // If such suffix is found, |consumer|->OnReuseFound() is called on the same
+ // thread on which this method is called.
+ // |consumer| must not be null.
+ virtual void CheckReuse(const base::string16& input,
+ const std::string& domain,
+ PasswordReuseDetectorConsumer* consumer);
+
protected:
friend class base::RefCountedThreadSafe<PasswordStore>;
@@ -239,8 +251,7 @@ class PasswordStore : protected PasswordStoreSync,
void NotifyConsumerWithResults(
std::vector<std::unique_ptr<autofill::PasswordForm>> results);
- void NotifyWithSiteStatistics(
- std::vector<std::unique_ptr<InteractionsStats>> stats);
+ void NotifyWithSiteStatistics(std::vector<InteractionsStats> stats);
void set_ignore_logins_cutoff(base::Time cutoff) {
ignore_logins_cutoff_ = cutoff;
@@ -256,6 +267,28 @@ class PasswordStore : protected PasswordStoreSync,
DISALLOW_COPY_AND_ASSIGN(GetLoginsRequest);
};
+ // Represents a single CheckReuse() request. Implements functionality to
+ // listen to reuse events and propagate them to |consumer| on the thread on
+ // which CheckReuseRequest is created.
+ class CheckReuseRequest : public PasswordReuseDetectorConsumer {
+ public:
+ // |consumer| must not be null.
+ explicit CheckReuseRequest(PasswordReuseDetectorConsumer* consumer);
+ ~CheckReuseRequest() override;
+
+ // PasswordReuseDetectorConsumer
+ void OnReuseFound(const base::string16& password,
+ const std::string& saved_domain,
+ int saved_passwords,
+ int number_matches) override;
+
+ private:
+ const scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
+ const base::WeakPtr<PasswordReuseDetectorConsumer> consumer_weak_;
+
+ DISALLOW_COPY_AND_ASSIGN(CheckReuseRequest);
+ };
+
~PasswordStore() override;
// Get the TaskRunner to use for PasswordStore background tasks.
@@ -325,7 +358,7 @@ class PasswordStore : protected PasswordStoreSync,
// Synchronous implementation for manipulating with statistics.
virtual void AddSiteStatsImpl(const InteractionsStats& stats) = 0;
virtual void RemoveSiteStatsImpl(const GURL& origin_domain) = 0;
- virtual std::vector<std::unique_ptr<InteractionsStats>> GetSiteStatsImpl(
+ virtual std::vector<InteractionsStats> GetSiteStatsImpl(
const GURL& origin_domain) = 0;
// Log UMA stats for number of bulk deletions.
@@ -348,6 +381,11 @@ class PasswordStore : protected PasswordStoreSync,
// may have been changed.
void NotifyLoginsChanged(const PasswordStoreChangeList& changes) override;
+ // Synchronous implementation of CheckReuse().
+ void CheckReuseImpl(std::unique_ptr<CheckReuseRequest> request,
+ const base::string16& input,
+ const std::string& domain);
+
// TaskRunner for tasks that run on the main thread (usually the UI thread).
scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner_;
@@ -482,18 +520,20 @@ class PasswordStore : protected PasswordStoreSync,
const autofill::PasswordForm& updated_android_form,
const std::vector<std::string>& affiliated_web_realms);
- // Creates PasswordSyncableService instance on the background thread.
- void InitSyncableService(
+ // Creates PasswordSyncableService and PasswordReuseDetector instances on the
+ // background thread.
+ void InitOnBackgroundThread(
const syncer::SyncableService::StartSyncFlare& flare);
- // Deletes PasswordSyncableService instance on the background thread.
- void DestroySyncableService();
+ // Deletes objest that should be destroyed on the background thread.
+ void DestroyOnBackgroundThread();
// The observers.
scoped_refptr<base::ObserverListThreadSafe<Observer>> observers_;
std::unique_ptr<PasswordSyncableService> syncable_service_;
std::unique_ptr<AffiliatedMatchHelper> affiliated_match_helper_;
+ std::unique_ptr<PasswordReuseDetector> reuse_detector_;
bool is_propagating_password_changes_to_web_credentials_enabled_;
bool shutdown_called_;
diff --git a/chromium/components/password_manager/core/browser/password_store_consumer.cc b/chromium/components/password_manager/core/browser/password_store_consumer.cc
index eef0bf96fe8..1545121507d 100644
--- a/chromium/components/password_manager/core/browser/password_store_consumer.cc
+++ b/chromium/components/password_manager/core/browser/password_store_consumer.cc
@@ -15,6 +15,6 @@ PasswordStoreConsumer::~PasswordStoreConsumer() {
}
void PasswordStoreConsumer::OnGetSiteStatistics(
- std::vector<std::unique_ptr<InteractionsStats>> stats) {}
+ std::vector<InteractionsStats> stats) {}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_store_consumer.h b/chromium/components/password_manager/core/browser/password_store_consumer.h
index f137225be67..89d7975d0ee 100644
--- a/chromium/components/password_manager/core/browser/password_store_consumer.h
+++ b/chromium/components/password_manager/core/browser/password_store_consumer.h
@@ -34,8 +34,7 @@ class PasswordStoreConsumer {
// Called when the GetLogins() request is finished, with the associated site
// statistics.
- virtual void OnGetSiteStatistics(
- std::vector<std::unique_ptr<InteractionsStats>> stats);
+ virtual void OnGetSiteStatistics(std::vector<InteractionsStats> stats);
// The base::CancelableTaskTracker can be used for cancelling the
// tasks associated with the consumer.
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 12b98d53aa6..e01ab227545 100644
--- a/chromium/components/password_manager/core/browser/password_store_default.cc
+++ b/chromium/components/password_manager/core/browser/password_store_default.cc
@@ -84,11 +84,11 @@ PasswordStoreChangeList PasswordStoreDefault::RemoveLoginsByURLAndTimeImpl(
const base::Callback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end) {
- ScopedVector<autofill::PasswordForm> forms;
+ std::vector<std::unique_ptr<PasswordForm>> forms;
PasswordStoreChangeList changes;
if (login_db_ &&
login_db_->GetLoginsCreatedBetween(delete_begin, delete_end, &forms)) {
- for (autofill::PasswordForm* form : forms) {
+ for (const auto& form : forms) {
if (url_filter.Run(form->origin) && login_db_->RemoveLogin(*form))
changes.push_back(
PasswordStoreChange(PasswordStoreChange::REMOVE, *form));
@@ -102,12 +102,12 @@ PasswordStoreChangeList PasswordStoreDefault::RemoveLoginsByURLAndTimeImpl(
PasswordStoreChangeList PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(
base::Time delete_begin,
base::Time delete_end) {
- ScopedVector<autofill::PasswordForm> forms;
+ std::vector<std::unique_ptr<PasswordForm>> forms;
PasswordStoreChangeList changes;
if (login_db_ &&
login_db_->GetLoginsCreatedBetween(delete_begin, delete_end, &forms)) {
if (login_db_->RemoveLoginsCreatedBetween(delete_begin, delete_end)) {
- for (const auto* form : forms) {
+ for (const auto& form : forms) {
changes.push_back(
PasswordStoreChange(PasswordStoreChange::REMOVE, *form));
}
@@ -120,12 +120,12 @@ PasswordStoreChangeList PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(
PasswordStoreChangeList PasswordStoreDefault::RemoveLoginsSyncedBetweenImpl(
base::Time delete_begin,
base::Time delete_end) {
- ScopedVector<autofill::PasswordForm> forms;
+ std::vector<std::unique_ptr<PasswordForm>> forms;
PasswordStoreChangeList changes;
if (login_db_ &&
login_db_->GetLoginsSyncedBetween(delete_begin, delete_end, &forms)) {
if (login_db_->RemoveLoginsSyncedBetween(delete_begin, delete_end)) {
- for (const auto* form : forms) {
+ for (const auto& form : forms) {
changes.push_back(
PasswordStoreChange(PasswordStoreChange::REMOVE, *form));
}
@@ -137,13 +137,13 @@ PasswordStoreChangeList PasswordStoreDefault::RemoveLoginsSyncedBetweenImpl(
PasswordStoreChangeList PasswordStoreDefault::DisableAutoSignInForOriginsImpl(
const base::Callback<bool(const GURL&)>& origin_filter) {
- ScopedVector<autofill::PasswordForm> forms;
+ std::vector<std::unique_ptr<PasswordForm>> forms;
PasswordStoreChangeList changes;
if (!login_db_ || !login_db_->GetAutoSignInLogins(&forms))
return changes;
std::set<GURL> origins_to_update;
- for (const auto* form : forms) {
+ for (const auto& form : forms) {
if (origin_filter.Run(form->origin))
origins_to_update.insert(form->origin);
}
@@ -154,7 +154,7 @@ PasswordStoreChangeList PasswordStoreDefault::DisableAutoSignInForOriginsImpl(
origins_updated.insert(origin);
}
- for (const auto* form : forms) {
+ for (const auto& form : forms) {
if (origins_updated.count(form->origin)) {
changes.push_back(
PasswordStoreChange(PasswordStoreChange::UPDATE, *form));
@@ -205,11 +205,11 @@ void PasswordStoreDefault::RemoveSiteStatsImpl(const GURL& origin_domain) {
login_db_->stats_table().RemoveRow(origin_domain);
}
-std::vector<std::unique_ptr<InteractionsStats>>
-PasswordStoreDefault::GetSiteStatsImpl(const GURL& origin_domain) {
+std::vector<InteractionsStats> PasswordStoreDefault::GetSiteStatsImpl(
+ const GURL& origin_domain) {
DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
return login_db_ ? login_db_->stats_table().GetRows(origin_domain)
- : std::vector<std::unique_ptr<InteractionsStats>>();
+ : std::vector<InteractionsStats>();
}
void PasswordStoreDefault::ResetLoginDB() {
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 9381067b3d4..f7e98b1e11e 100644
--- a/chromium/components/password_manager/core/browser/password_store_default.h
+++ b/chromium/components/password_manager/core/browser/password_store_default.h
@@ -72,7 +72,7 @@ class PasswordStoreDefault : public PasswordStore {
std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
void AddSiteStatsImpl(const InteractionsStats& stats) override;
void RemoveSiteStatsImpl(const GURL& origin_domain) override;
- std::vector<std::unique_ptr<InteractionsStats>> GetSiteStatsImpl(
+ std::vector<InteractionsStats> GetSiteStatsImpl(
const GURL& origin_domain) override;
inline bool DeleteAndRecreateDatabaseFile() {
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 f6b43fe1e00..f8239e5ceba 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
@@ -15,6 +15,7 @@
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_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"
diff --git a/chromium/components/password_manager/core/browser/password_store_sync.h b/chromium/components/password_manager/core/browser/password_store_sync.h
index 4d1b171c02b..fc8b55deeeb 100644
--- a/chromium/components/password_manager/core/browser/password_store_sync.h
+++ b/chromium/components/password_manager/core/browser/password_store_sync.h
@@ -10,7 +10,6 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "components/password_manager/core/browser/password_store_change.h"
namespace autofill {
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 dfa3382c51d..c47fcebe9fd 100644
--- a/chromium/components/password_manager/core/browser/password_store_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_store_unittest.cc
@@ -136,7 +136,7 @@ TEST_F(PasswordStoreTest, IgnoreOldWwwGoogleLogins) {
};
// Build the forms vector and add the forms to the store.
- ScopedVector<PasswordForm> all_forms;
+ std::vector<std::unique_ptr<PasswordForm>> all_forms;
for (size_t i = 0; i < arraysize(form_data); ++i) {
all_forms.push_back(CreatePasswordFormFromDataForTesting(form_data[i]));
store->AddLogin(*all_forms.back());
@@ -398,7 +398,7 @@ TEST_F(PasswordStoreTest, GetLoginsWithoutAffiliations) {
MockAffiliatedMatchHelper* mock_helper = new MockAffiliatedMatchHelper;
store->SetAffiliatedMatchHelper(base::WrapUnique(mock_helper));
- ScopedVector<PasswordForm> all_credentials;
+ std::vector<std::unique_ptr<PasswordForm>> all_credentials;
for (size_t i = 0; i < arraysize(kTestCredentials); ++i) {
all_credentials.push_back(
CreatePasswordFormFromDataForTesting(kTestCredentials[i]));
@@ -480,8 +480,7 @@ TEST_F(PasswordStoreTest, GetLoginsWithAffiliations) {
"", "", L"", L"", L"",
L"username_value_4",
L"", true, 1},
- // Federated credential for this second Android application; this should
- // not be returned.
+ // Federated credential for this second Android application.
{PasswordForm::SCHEME_HTML,
kTestAndroidRealm2,
"", "", L"", L"", L"",
@@ -504,7 +503,7 @@ TEST_F(PasswordStoreTest, GetLoginsWithAffiliations) {
MockAffiliatedMatchHelper* mock_helper = new MockAffiliatedMatchHelper;
store->SetAffiliatedMatchHelper(base::WrapUnique(mock_helper));
- ScopedVector<PasswordForm> all_credentials;
+ std::vector<std::unique_ptr<PasswordForm>> all_credentials;
for (size_t i = 0; i < arraysize(kTestCredentials); ++i) {
all_credentials.push_back(
CreatePasswordFormFromDataForTesting(kTestCredentials[i]));
@@ -527,6 +526,8 @@ TEST_F(PasswordStoreTest, GetLoginsWithAffiliations) {
base::MakeUnique<PasswordForm>(*all_credentials[3]));
expected_results.push_back(
base::MakeUnique<PasswordForm>(*all_credentials[5]));
+ expected_results.push_back(
+ base::MakeUnique<PasswordForm>(*all_credentials[6]));
for (const auto& result : expected_results) {
if (result->signon_realm != observed_form.signon_realm &&
@@ -686,7 +687,7 @@ TEST_F(PasswordStoreTest, MAYBE_UpdatePasswordsStoredForAffiliatedWebsites) {
base::Closure());
// Set up the initial test data set.
- ScopedVector<PasswordForm> all_credentials;
+ std::vector<std::unique_ptr<PasswordForm>> all_credentials;
for (size_t i = 0; i < arraysize(kTestCredentials); ++i) {
all_credentials.push_back(
CreatePasswordFormFromDataForTesting(kTestCredentials[i]));
@@ -794,7 +795,7 @@ TEST_F(PasswordStoreTest, GetLoginsWithAffiliatedRealms) {
store->RemoveLoginsCreatedBetween(base::Time(), base::Time::Max(),
base::Closure());
- ScopedVector<PasswordForm> all_credentials;
+ std::vector<std::unique_ptr<PasswordForm>> all_credentials;
for (size_t i = 0; i < arraysize(kTestCredentials); ++i) {
all_credentials.push_back(
CreatePasswordFormFromDataForTesting(kTestCredentials[i]));
@@ -839,4 +840,56 @@ TEST_F(PasswordStoreTest, GetLoginsWithAffiliatedRealms) {
}
}
+#if !defined(OS_MACOSX)
+// TODO(crbug.com/668155): Enable this test after fixing issues with
+// initialization PasswordStore with MockKeyChain in tests on MacOS.
+TEST_F(PasswordStoreTest, CheckPasswordReuse) {
+ static constexpr PasswordFormData kTestCredentials[] = {
+ {PasswordForm::SCHEME_HTML, "https://www.google.com",
+ "https://www.google.com", "", L"", L"", L"", L"", L"password", true, 1},
+ {PasswordForm::SCHEME_HTML, "https://facebook.com",
+ "https://facebook.com", "", L"", L"", L"", L"", L"topsecret", true, 1}};
+
+ scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
+ base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
+ base::MakeUnique<LoginDatabase>(test_login_db_file_path())));
+ store->Init(syncer::SyncableService::StartSyncFlare());
+
+ for (const auto& test_credentials : kTestCredentials) {
+ auto credentials = CreatePasswordFormFromDataForTesting(test_credentials);
+ store->AddLogin(*credentials);
+ base::RunLoop().RunUntilIdle();
+ }
+
+ static constexpr struct {
+ const wchar_t* input;
+ const char* domain;
+ const wchar_t* reused_password; // Set to nullptr if no reuse is expected.
+ const char* reuse_domain;
+ } kReuseTestData[] = {
+ {L"12345password", "https://evil.com", L"password", "google.com"},
+ {L"1234567890", "https://evil.com", nullptr, nullptr},
+ {L"topsecret", "https://m.facebook.com", nullptr, nullptr},
+ };
+
+ for (const auto& test_data : kReuseTestData) {
+ MockPasswordReuseDetectorConsumer mock_consumer;
+ if (test_data.reused_password) {
+ EXPECT_CALL(mock_consumer,
+ OnReuseFound(base::WideToUTF16(test_data.reused_password),
+ std::string(test_data.reuse_domain), 2, 1));
+ } else {
+ EXPECT_CALL(mock_consumer, OnReuseFound(_, _, _, _)).Times(0);
+ }
+
+ store->CheckReuse(base::WideToUTF16(test_data.input), test_data.domain,
+ &mock_consumer);
+ base::RunLoop().RunUntilIdle();
+ }
+
+ store->ShutdownOnUIThread();
+ base::RunLoop().RunUntilIdle();
+}
+#endif
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/psl_matching_helper.cc b/chromium/components/password_manager/core/browser/psl_matching_helper.cc
index ff01008aee6..02bccbe4b97 100644
--- a/chromium/components/password_manager/core/browser/psl_matching_helper.cc
+++ b/chromium/components/password_manager/core/browser/psl_matching_helper.cc
@@ -5,17 +5,66 @@
#include "components/password_manager/core/browser/psl_matching_helper.h"
#include <memory>
+#include <ostream>
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "components/autofill/core/common/password_form.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/gurl.h"
+#include "url/url_constants.h"
using autofill::PasswordForm;
namespace password_manager {
+std::ostream& operator<<(std::ostream& out, MatchResult result) {
+ switch (result) {
+ case MatchResult::NO_MATCH:
+ return out << "No Match";
+ case MatchResult::EXACT_MATCH:
+ return out << "Exact Match";
+ case MatchResult::PSL_MATCH:
+ return out << "PSL Match";
+ case MatchResult::FEDERATED_MATCH:
+ return out << "Federated Match";
+ case MatchResult::FEDERATED_PSL_MATCH:
+ return out << "Federated PSL Match";
+ }
+ // This should never be reached, it is simply here to suppress compiler
+ // warnings.
+ return out;
+}
+
+MatchResult GetMatchResult(const PasswordForm& form,
+ const PasswordStore::FormDigest& form_digest) {
+ if (form.signon_realm == form_digest.signon_realm)
+ return MatchResult::EXACT_MATCH;
+
+ // PSL and federated matches only apply to HTML forms.
+ if (form_digest.scheme != PasswordForm::SCHEME_HTML ||
+ form.scheme != PasswordForm::SCHEME_HTML)
+ return MatchResult::NO_MATCH;
+
+ const bool allow_psl_match = ShouldPSLDomainMatchingApply(
+ GetRegistryControlledDomain(GURL(form_digest.signon_realm)));
+ const bool allow_federated_match = !form.federation_origin.unique();
+
+ if (allow_psl_match &&
+ IsPublicSuffixDomainMatch(form.signon_realm, form_digest.signon_realm))
+ return MatchResult::PSL_MATCH;
+
+ if (allow_federated_match &&
+ IsFederatedMatch(form.signon_realm, form_digest.origin))
+ return MatchResult::FEDERATED_MATCH;
+
+ if (allow_psl_match && allow_federated_match &&
+ IsFederatedPSLMatch(form.signon_realm, form_digest.origin))
+ return MatchResult::FEDERATED_PSL_MATCH;
+
+ return MatchResult::NO_MATCH;
+}
+
bool ShouldPSLDomainMatchingApply(
const std::string& registry_controlled_domain) {
return !registry_controlled_domain.empty() &&
@@ -57,4 +106,26 @@ bool IsFederatedMatch(const std::string& signon_realm, const GURL& origin) {
base::CompareCase::INSENSITIVE_ASCII);
}
+bool IsFederatedPSLMatch(const std::string& signon_realm, const GURL& origin) {
+ // The format should be "federation://origin.host/federation.host;
+ // Check for presence of "federation://" prefix.
+ static constexpr char federation_prefix[] = "federation://";
+ if (!base::StartsWith(signon_realm, federation_prefix,
+ base::CompareCase::INSENSITIVE_ASCII))
+ return false;
+
+ // Replace federation scheme with HTTPS. This results in correct parsing of
+ // host and path, and forces origin to have a HTTPS scheme in order to return
+ // true.
+ GURL::Replacements replacements;
+ replacements.SetSchemeStr(url::kHttpsScheme);
+ GURL https_signon_realm = GURL(signon_realm).ReplaceComponents(replacements);
+
+ // Check for non-empty federation.host.
+ if (!https_signon_realm.has_path() || https_signon_realm.path_piece() == "/")
+ return false;
+
+ return IsPublicSuffixDomainMatch(https_signon_realm.GetOrigin().spec(),
+ origin.GetOrigin().spec());
+}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/psl_matching_helper.h b/chromium/components/password_manager/core/browser/psl_matching_helper.h
index 22071b6080d..d5839c54205 100644
--- a/chromium/components/password_manager/core/browser/psl_matching_helper.h
+++ b/chromium/components/password_manager/core/browser/psl_matching_helper.h
@@ -5,8 +5,10 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PSL_MATCHING_HELPER_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PSL_MATCHING_HELPER_H_
+#include <iosfwd>
#include <string>
+#include "components/password_manager/core/browser/password_store.h"
class GURL;
@@ -19,9 +21,25 @@ enum PSLDomainMatchMetric {
PSL_DOMAIN_MATCH_NOT_USED = 0,
PSL_DOMAIN_MATCH_NONE,
PSL_DOMAIN_MATCH_FOUND,
+ PSL_DOMAIN_MATCH_FOUND_FEDERATED,
PSL_DOMAIN_MATCH_COUNT
};
+enum class MatchResult {
+ NO_MATCH,
+ EXACT_MATCH,
+ PSL_MATCH,
+ FEDERATED_MATCH,
+ FEDERATED_PSL_MATCH,
+};
+
+// For testing.
+std::ostream& operator<<(std::ostream& out, MatchResult result);
+
+// Returns what type of match applies to |form| and |form_digest|.
+MatchResult GetMatchResult(const autofill::PasswordForm& form,
+ const PasswordStore::FormDigest& form_digest);
+
// Using the public suffix list for matching the origin is only needed for
// websites that do not have a single hostname for entering credentials. It
// would be better for their users if they did, but until then we help them find
@@ -48,6 +66,9 @@ std::string GetRegistryControlledDomain(const GURL& signon_realm);
// |origin|.
bool IsFederatedMatch(const std::string& signon_realm, const GURL& origin);
+// Returns true iff |signon_realm| designates a federated PSL matching
+// credential for the |origin|.
+bool IsFederatedPSLMatch(const std::string& signon_realm, const GURL& origin);
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PSL_MATCHING_HELPER_H_
diff --git a/chromium/components/password_manager/core/browser/psl_matching_helper_unittest.cc b/chromium/components/password_manager/core/browser/psl_matching_helper_unittest.cc
index 68381d2c842..b98dddcd622 100644
--- a/chromium/components/password_manager/core/browser/psl_matching_helper_unittest.cc
+++ b/chromium/components/password_manager/core/browser/psl_matching_helper_unittest.cc
@@ -14,6 +14,154 @@ namespace password_manager {
namespace {
+TEST(PSLMatchingUtilsTest, GetMatchResult) {
+ struct TestData {
+ const char* form_signon_realm;
+ const char* form_federation_origin;
+ autofill::PasswordForm::Scheme digest_scheme;
+ const char* digest_signon_realm;
+ const char* digest_origin;
+ MatchResult match_result;
+ };
+
+ const TestData cases[] = {
+ // Test Exact Matches.
+ {"http://facebook.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "http://facebook.com/", "http://facebook.com", MatchResult::EXACT_MATCH},
+
+ {"http://m.facebook.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "http://m.facebook.com/", "http://m.facebook.com",
+ MatchResult::EXACT_MATCH},
+
+ // Scheme mismatch.
+ {"http://www.example.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "https://www.example.com/", "https://www.example.com",
+ MatchResult::NO_MATCH},
+
+ // Host mismatch.
+ {"http://www.example.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "http://wwwexample.com/", "http://wwwexample.com",
+ MatchResult::NO_MATCH},
+
+ {"http://www.example1.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "http://www.example2.com/", "http://www.example2.com",
+ MatchResult::NO_MATCH},
+
+ // Port mismatch.
+ {"http://www.example.com:123/", "", autofill::PasswordForm::SCHEME_HTML,
+ "http://www.example.com/", "http://www.example.com",
+ MatchResult::NO_MATCH},
+
+ // TLD mismatch.
+ {"http://www.example.org/", "", autofill::PasswordForm::SCHEME_HTML,
+ "http://www.example.com/", "http://www.example.com",
+ MatchResult::NO_MATCH},
+
+ // URLs without registry controlled domains should not match.
+ {"http://localhost/", "", autofill::PasswordForm::SCHEME_HTML,
+ "http://127.0.0.1/", "http://127.0.0.1", MatchResult::NO_MATCH},
+
+ // Invalid URLs don't match.
+ {"http://www.example.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "http://", "", MatchResult::NO_MATCH},
+
+ {"", "", autofill::PasswordForm::SCHEME_HTML, "http://www.example.com/",
+ "http://www.example.com", MatchResult::NO_MATCH},
+
+ {"http://www.example.com", "", autofill::PasswordForm::SCHEME_HTML,
+ "bad url", "", MatchResult::NO_MATCH},
+
+ // Test PSL Matches.
+ {"http://facebook.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "http://m.facebook.com/", "http://m.facebook.com",
+ MatchResult::PSL_MATCH},
+
+ {"https://facebook.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "https://m.facebook.com/", "https://m.facebook.com",
+ MatchResult::PSL_MATCH},
+
+ {"https://www.facebook.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "https://m.facebook.com/", "https://m.facebook.com",
+ MatchResult::PSL_MATCH},
+
+ // Don't apply PSL matching to Google domains.
+ {"https://google.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "https://maps.google.com/", "https://maps.google.com",
+ MatchResult::NO_MATCH},
+
+ {"https://google.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "https://maps.google.com/", "https://maps.google.com",
+ MatchResult::NO_MATCH},
+
+ // Test Federated Matches.
+ {"federation://example.com/google.com", "https://google.com",
+ autofill::PasswordForm::SCHEME_HTML, "https://example.com/",
+ "https://example.com", MatchResult::FEDERATED_MATCH},
+
+ // Empty federation providers don't match.
+ {"federation://example.com/", "", autofill::PasswordForm::SCHEME_HTML,
+ "https://example.com/", "https://example.com", MatchResult::NO_MATCH},
+
+ // Invalid origins don't match.
+ {"federation://example.com/google.com", "https://google.com",
+ autofill::PasswordForm::SCHEME_HTML, "https://example.com",
+ "example.com", MatchResult::NO_MATCH},
+
+ {"federation://example.com/google.com", "https://google.com",
+ autofill::PasswordForm::SCHEME_HTML, "https://example.com", "example",
+ MatchResult::NO_MATCH},
+
+ // Test Federated PSL Matches.
+ {"federation://sub.example.com/google.com", "https://google.com",
+ autofill::PasswordForm::SCHEME_HTML, "https://sub.example.com/",
+ "https://sub.example.com", MatchResult::FEDERATED_MATCH},
+
+ {"federation://sub1.example.com/google.com", "https://google.com",
+ autofill::PasswordForm::SCHEME_HTML, "https://sub2.example.com/",
+ "https://sub2.example.com", MatchResult::FEDERATED_PSL_MATCH},
+
+ {"federation://example.com/google.com", "https://google.com",
+ autofill::PasswordForm::SCHEME_HTML, "https://sub.example.com/",
+ "https://sub.example.com", MatchResult::FEDERATED_PSL_MATCH},
+
+ // Federated PSL matches do not apply to HTTP.
+ {"federation://sub1.example.com/google.com", "https://google.com",
+ autofill::PasswordForm::SCHEME_HTML, "http://sub2.example.com/",
+ "http://sub2.example.com", MatchResult::NO_MATCH},
+
+ {"federation://example.com/google.com", "https://google.com",
+ autofill::PasswordForm::SCHEME_HTML, "http://sub.example.com/",
+ "http://sub.example.com", MatchResult::NO_MATCH},
+
+ // Federated PSL matches do not apply to Google on HTTP or HTTPS.
+ {"federation://accounts.google.com/facebook.com", "https://facebook.com",
+ autofill::PasswordForm::SCHEME_HTML, "https://maps.google.com/",
+ "https://maps.google.com", MatchResult::NO_MATCH},
+
+ {"federation://accounts.google.com/facebook.com", "https://facebook.com",
+ autofill::PasswordForm::SCHEME_HTML, "http://maps.google.com/",
+ "http://maps.google.com", MatchResult::NO_MATCH},
+
+ // TLD Mismatch.
+ {"federation://sub.example.com/google.com", "https://google.com",
+ autofill::PasswordForm::SCHEME_HTML, "https://sub.example.org/",
+ "https://sub.example.org", MatchResult::NO_MATCH},
+ };
+
+ for (const TestData& data : cases) {
+ autofill::PasswordForm form;
+ form.signon_realm = data.form_signon_realm;
+ form.federation_origin = url::Origin(GURL(data.form_federation_origin));
+ PasswordStore::FormDigest digest(
+ data.digest_scheme, data.digest_signon_realm, GURL(data.digest_origin));
+
+ EXPECT_EQ(data.match_result, GetMatchResult(form, digest))
+ << "signon_realm = " << data.form_signon_realm
+ << ", federation_origin = " << data.form_federation_origin
+ << ", digest = " << digest;
+ }
+}
+
TEST(PSLMatchingUtilsTest, IsPublicSuffixDomainMatch) {
struct TestPair {
const char* url1;
@@ -86,6 +234,43 @@ TEST(PSLMatchingUtilsTest, IsFederatedMatch) {
}
}
+TEST(PSLMatchingUtilsTest, IsFederatedPSLMatch) {
+ struct TestPair {
+ const char* signon_realm;
+ const char* origin;
+ bool should_match;
+ };
+
+ const TestPair pairs[] = {
+ {"https://facebook.com", "https://facebook.com", false},
+ {"", "", false},
+ {"", "https://facebook.com/", false},
+ {"https://facebook.com/", "", false},
+
+ {"federation://example.com/google.com", "https://example.com/", true},
+ {"federation://example.com/google.com", "http://example.com/", false},
+ {"federation://example.com/google.com", "example.com", false},
+ {"federation://example.com/", "http://example.com/", false},
+ {"federation://example.com/google.com", "example", false},
+
+ {"federation://sub.example.com/google.com", "https://sub.example.com/",
+ true},
+ {"federation://sub1.example.com/google.com", "https://sub2.example.com/",
+ true},
+ {"federation://example.com/google.com", "https://sub.example.com/", true},
+ {"federation://example.com/google.com", "http://sub.example.com/", false},
+ {"federation://sub.example.com/", "https://sub.example.com/", false},
+ };
+
+ for (const TestPair& pair : pairs) {
+ std::string signon_realm = pair.signon_realm;
+ GURL origin(pair.origin);
+ EXPECT_EQ(pair.should_match, IsFederatedPSLMatch(signon_realm, origin))
+ << "signon_realm = " << pair.signon_realm
+ << ", origin = " << pair.origin;
+ }
+}
+
} // namespace
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/statistics_table.cc b/chromium/components/password_manager/core/browser/statistics_table.cc
index 0b6cc2cc0c4..e03ba5bed9a 100644
--- a/chromium/components/password_manager/core/browser/statistics_table.cc
+++ b/chromium/components/password_manager/core/browser/statistics_table.cc
@@ -37,13 +37,13 @@ bool operator==(const InteractionsStats& lhs, const InteractionsStats& rhs) {
}
const InteractionsStats* FindStatsByUsername(
- const std::vector<const InteractionsStats*>& stats,
+ const std::vector<InteractionsStats>& stats,
const base::string16& username) {
auto it = std::find_if(stats.begin(), stats.end(),
- [&username](const InteractionsStats* element) {
- return username == element->username_value;
+ [&username](const InteractionsStats& element) {
+ return username == element.username_value;
});
- return it == stats.end() ? nullptr : *it;
+ return it == stats.end() ? nullptr : &*it;
}
StatisticsTable::StatisticsTable() : db_(nullptr) {
@@ -106,22 +106,21 @@ bool StatisticsTable::RemoveRow(const GURL& domain) {
return s.Run();
}
-std::vector<std::unique_ptr<InteractionsStats>> StatisticsTable::GetRows(
- const GURL& domain) {
+std::vector<InteractionsStats> StatisticsTable::GetRows(const GURL& domain) {
if (!domain.is_valid())
- return std::vector<std::unique_ptr<InteractionsStats>>();
+ return std::vector<InteractionsStats>();
const char query[] =
"SELECT origin_domain, username_value, "
"dismissal_count, update_time FROM stats WHERE origin_domain == ?";
sql::Statement s(db_->GetCachedStatement(SQL_FROM_HERE, query));
s.BindString(0, domain.spec());
- std::vector<std::unique_ptr<InteractionsStats>> result;
+ std::vector<InteractionsStats> result;
while (s.Step()) {
- result.push_back(base::WrapUnique(new InteractionsStats));
- result.back()->origin_domain = GURL(s.ColumnString(COLUMN_ORIGIN_DOMAIN));
- result.back()->username_value = s.ColumnString16(COLUMN_USERNAME);
- result.back()->dismissal_count = s.ColumnInt(COLUMN_DISMISSALS);
- result.back()->update_time =
+ result.push_back(InteractionsStats());
+ result.back().origin_domain = GURL(s.ColumnString(COLUMN_ORIGIN_DOMAIN));
+ result.back().username_value = s.ColumnString16(COLUMN_USERNAME);
+ result.back().dismissal_count = s.ColumnInt(COLUMN_DISMISSALS);
+ result.back().update_time =
base::Time::FromInternalValue(s.ColumnInt64(COLUMN_DATE));
}
return result;
diff --git a/chromium/components/password_manager/core/browser/statistics_table.h b/chromium/components/password_manager/core/browser/statistics_table.h
index eefa3b45a7d..ea6c8c11658 100644
--- a/chromium/components/password_manager/core/browser/statistics_table.h
+++ b/chromium/components/password_manager/core/browser/statistics_table.h
@@ -10,7 +10,6 @@
#include "base/callback_forward.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "base/time/time.h"
#include "url/gurl.h"
@@ -41,7 +40,7 @@ bool operator==(const InteractionsStats& lhs, const InteractionsStats& rhs);
// Returns an element from |stats| with |username| or nullptr if not found.
const InteractionsStats* FindStatsByUsername(
- const std::vector<const InteractionsStats*>& stats,
+ const std::vector<InteractionsStats>& stats,
const base::string16& username);
// Represents the 'stats' table in the Login Database.
@@ -70,7 +69,7 @@ class StatisticsTable {
bool RemoveRow(const GURL& domain);
// Returns the statistics for |domain| if it exists.
- std::vector<std::unique_ptr<InteractionsStats>> GetRows(const GURL& domain);
+ std::vector<InteractionsStats> GetRows(const GURL& domain);
// Removes the statistics between the dates. If |origin_filter| is not null,
// only statistics for matching origins are removed. Returns true if the SQL
diff --git a/chromium/components/password_manager/core/browser/statistics_table_unittest.cc b/chromium/components/password_manager/core/browser/statistics_table_unittest.cc
index d51aa286f44..e84e78bcccd 100644
--- a/chromium/components/password_manager/core/browser/statistics_table_unittest.cc
+++ b/chromium/components/password_manager/core/browser/statistics_table_unittest.cc
@@ -62,7 +62,7 @@ class StatisticsTableTest : public testing::Test {
TEST_F(StatisticsTableTest, Sanity) {
EXPECT_TRUE(db()->AddRow(test_data()));
EXPECT_THAT(db()->GetRows(test_data().origin_domain),
- ElementsAre(Pointee(test_data())));
+ ElementsAre(test_data()));
EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain));
EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
}
@@ -73,7 +73,7 @@ TEST_F(StatisticsTableTest, Reload) {
ReloadDatabase();
EXPECT_THAT(db()->GetRows(test_data().origin_domain),
- ElementsAre(Pointee(test_data())));
+ ElementsAre(test_data()));
}
TEST_F(StatisticsTableTest, DoubleOperation) {
@@ -82,7 +82,7 @@ TEST_F(StatisticsTableTest, DoubleOperation) {
EXPECT_TRUE(db()->AddRow(test_data()));
EXPECT_THAT(db()->GetRows(test_data().origin_domain),
- ElementsAre(Pointee(test_data())));
+ ElementsAre(test_data()));
EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain));
EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
@@ -97,7 +97,7 @@ TEST_F(StatisticsTableTest, DifferentUsernames) {
EXPECT_TRUE(db()->AddRow(stats1));
EXPECT_TRUE(db()->AddRow(stats2));
EXPECT_THAT(db()->GetRows(test_data().origin_domain),
- UnorderedElementsAre(Pointee(stats1), Pointee(stats2)));
+ UnorderedElementsAre(stats1, stats2));
EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain));
EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
}
@@ -119,26 +119,19 @@ TEST_F(StatisticsTableTest, RemoveStatsByOriginAndTime) {
EXPECT_TRUE(db()->AddRow(stats2));
EXPECT_TRUE(db()->AddRow(stats3));
EXPECT_TRUE(db()->AddRow(stats4));
- EXPECT_THAT(db()->GetRows(stats1.origin_domain),
- ElementsAre(Pointee(stats1)));
- EXPECT_THAT(db()->GetRows(stats2.origin_domain),
- ElementsAre(Pointee(stats2)));
- EXPECT_THAT(db()->GetRows(stats3.origin_domain),
- ElementsAre(Pointee(stats3)));
- EXPECT_THAT(db()->GetRows(stats4.origin_domain),
- ElementsAre(Pointee(stats4)));
+ EXPECT_THAT(db()->GetRows(stats1.origin_domain), ElementsAre(stats1));
+ EXPECT_THAT(db()->GetRows(stats2.origin_domain), ElementsAre(stats2));
+ EXPECT_THAT(db()->GetRows(stats3.origin_domain), ElementsAre(stats3));
+ EXPECT_THAT(db()->GetRows(stats4.origin_domain), ElementsAre(stats4));
// Remove the entry with the timestamp 1 with no origin filter.
EXPECT_TRUE(
db()->RemoveStatsByOriginAndTime(base::Callback<bool(const GURL&)>(),
base::Time(), base::Time::FromTimeT(2)));
EXPECT_THAT(db()->GetRows(stats1.origin_domain), IsEmpty());
- EXPECT_THAT(db()->GetRows(stats2.origin_domain),
- ElementsAre(Pointee(stats2)));
- EXPECT_THAT(db()->GetRows(stats3.origin_domain),
- ElementsAre(Pointee(stats3)));
- EXPECT_THAT(db()->GetRows(stats4.origin_domain),
- ElementsAre(Pointee(stats4)));
+ EXPECT_THAT(db()->GetRows(stats2.origin_domain), ElementsAre(stats2));
+ EXPECT_THAT(db()->GetRows(stats3.origin_domain), ElementsAre(stats3));
+ EXPECT_THAT(db()->GetRows(stats4.origin_domain), ElementsAre(stats4));
// Remove the entries with the timestamp 2 that are NOT matching
// |kTestDomain3|.
@@ -148,8 +141,7 @@ TEST_F(StatisticsTableTest, RemoveStatsByOriginAndTime) {
base::Time::FromTimeT(2), base::Time()));
EXPECT_THAT(db()->GetRows(stats1.origin_domain), IsEmpty());
EXPECT_THAT(db()->GetRows(stats2.origin_domain), IsEmpty());
- EXPECT_THAT(db()->GetRows(stats3.origin_domain),
- ElementsAre(Pointee(stats3)));
+ EXPECT_THAT(db()->GetRows(stats3.origin_domain), ElementsAre(stats3));
EXPECT_THAT(db()->GetRows(stats4.origin_domain), IsEmpty());
// Remove the entries with the timestamp 2 with no origin filter.
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 4d4f823f472..25589e28f9d 100644
--- a/chromium/components/password_manager/core/browser/test_password_store.cc
+++ b/chromium/components/password_manager/core/browser/test_password_store.cc
@@ -86,12 +86,18 @@ std::vector<std::unique_ptr<autofill::PasswordForm>>
TestPasswordStore::FillMatchingLogins(const FormDigest& form) {
std::vector<std::unique_ptr<autofill::PasswordForm>> matched_forms;
for (const auto& elements : stored_passwords_) {
- if (elements.first == form.signon_realm ||
+ const bool realm_matches = elements.first == form.signon_realm;
+ const bool realm_psl_matches =
+ IsPublicSuffixDomainMatch(elements.first, form.signon_realm);
+ if (realm_matches || realm_psl_matches ||
(form.scheme == autofill::PasswordForm::SCHEME_HTML &&
password_manager::IsFederatedMatch(elements.first, form.origin))) {
- for (const auto& stored_form : elements.second)
+ const bool is_psl = !realm_matches && realm_psl_matches;
+ for (const auto& stored_form : elements.second) {
matched_forms.push_back(
base::MakeUnique<autofill::PasswordForm>(stored_form));
+ matched_forms.back()->is_public_suffix_match = is_psl;
+ }
}
}
return matched_forms;
@@ -160,9 +166,9 @@ void TestPasswordStore::AddSiteStatsImpl(const InteractionsStats& stats) {
void TestPasswordStore::RemoveSiteStatsImpl(const GURL& origin_domain) {
}
-std::vector<std::unique_ptr<InteractionsStats>>
-TestPasswordStore::GetSiteStatsImpl(const GURL& origin_domain) {
- return std::vector<std::unique_ptr<InteractionsStats>>();
+std::vector<InteractionsStats> TestPasswordStore::GetSiteStatsImpl(
+ const GURL& origin_domain) {
+ return std::vector<InteractionsStats>();
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/test_password_store.h b/chromium/components/password_manager/core/browser/test_password_store.h
index 891d88c12cb..67bce27930a 100644
--- a/chromium/components/password_manager/core/browser/test_password_store.h
+++ b/chromium/components/password_manager/core/browser/test_password_store.h
@@ -72,7 +72,7 @@ class TestPasswordStore : public PasswordStore {
std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
void AddSiteStatsImpl(const InteractionsStats& stats) override;
void RemoveSiteStatsImpl(const GURL& origin_domain) override;
- std::vector<std::unique_ptr<InteractionsStats>> GetSiteStatsImpl(
+ std::vector<InteractionsStats> GetSiteStatsImpl(
const GURL& origin_domain) override;
private:
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 f06af87102f..f990b37ae13 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
@@ -30,9 +30,6 @@ const char kPasswordManagerSavingEnabled[] = "profile.password_manager_enabled";
const char kWasAutoSignInFirstRunExperienceShown[] =
"profile.was_auto_sign_in_first_run_experience_shown";
-const char kWasSavePrompFirstRunExperienceShown[] =
- "profile.was_save_prompt_first_run_experience_shown";
-
const char kWasSignInPasswordPromoClicked[] =
"profile.was_sign_in_password_promo_clicked";
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 d6b7e63e19f..c0d92f321af 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
@@ -59,10 +59,6 @@ extern const char kPasswordManagerSavingEnabled[];
// prompt was shown or not.
extern const char kWasAutoSignInFirstRunExperienceShown[];
-// Boolean that indicated whether first run experience for the save prompt was
-// shown or not.
-extern const char kWasSavePrompFirstRunExperienceShown[];
-
// Boolean that indicated if user interacted with the Chrome Sign in promo.
extern const char kWasSignInPasswordPromoClicked[];
diff --git a/chromium/components/password_manager/core/common/password_manager_ui.h b/chromium/components/password_manager/core/common/password_manager_ui.h
index 425bf5a04e9..15d8425cd51 100644
--- a/chromium/components/password_manager/core/common/password_manager_ui.h
+++ b/chromium/components/password_manager/core/common/password_manager_ui.h
@@ -39,6 +39,10 @@ enum State {
// The user was prompted to sign in to Chrome after saving a password.
CHROME_SIGN_IN_PROMO_STATE,
+
+ // The user was prompted with the Desktop to Mobile promotion after saving
+ // a password.
+ CHROME_DESKTOP_IOS_PROMO_STATE,
};
} // namespace ui
diff --git a/chromium/components/password_manager/sync/browser/BUILD.gn b/chromium/components/password_manager/sync/browser/BUILD.gn
index 598ca37d1ea..88b029040fe 100644
--- a/chromium/components/password_manager/sync/browser/BUILD.gn
+++ b/chromium/components/password_manager/sync/browser/BUILD.gn
@@ -48,7 +48,6 @@ source_set("unit_tests") {
"//components/autofill/core/common",
"//components/password_manager/core/browser:test_support",
"//components/password_manager/core/common",
- "//components/pref_registry:test_support",
"//components/prefs",
"//components/signin/core/browser:test_support",
"//components/signin/core/common",
diff --git a/chromium/components/password_manager/sync/browser/password_data_type_controller.cc b/chromium/components/password_manager/sync/browser/password_data_type_controller.cc
index 76db1ffce8b..12c35f38c90 100644
--- a/chromium/components/password_manager/sync/browser/password_data_type_controller.cc
+++ b/chromium/components/password_manager/sync/browser/password_data_type_controller.cc
@@ -17,18 +17,18 @@ PasswordDataTypeController::PasswordDataTypeController(
syncer::SyncClient* sync_client,
const base::Closure& state_changed_callback,
const scoped_refptr<password_manager::PasswordStore>& password_store)
- : NonUIDataTypeController(syncer::PASSWORDS, dump_stack, sync_client),
+ : AsyncDirectoryTypeController(syncer::PASSWORDS,
+ dump_stack,
+ sync_client,
+ syncer::GROUP_PASSWORD,
+ nullptr),
sync_client_(sync_client),
state_changed_callback_(state_changed_callback),
password_store_(password_store) {}
-syncer::ModelSafeGroup PasswordDataTypeController::model_safe_group() const {
- return syncer::GROUP_PASSWORD;
-}
-
PasswordDataTypeController::~PasswordDataTypeController() {}
-bool PasswordDataTypeController::PostTaskOnBackendThread(
+bool PasswordDataTypeController::PostTaskOnModelThread(
const tracked_objects::Location& from_here,
const base::Closure& task) {
DCHECK(CalledOnValidThread());
diff --git a/chromium/components/password_manager/sync/browser/password_data_type_controller.h b/chromium/components/password_manager/sync/browser/password_data_type_controller.h
index 732da941c57..79e2c304578 100644
--- a/chromium/components/password_manager/sync/browser/password_data_type_controller.h
+++ b/chromium/components/password_manager/sync/browser/password_data_type_controller.h
@@ -8,7 +8,7 @@
#include <string>
#include "base/macros.h"
-#include "components/sync/driver/non_ui_data_type_controller.h"
+#include "components/sync/driver/async_directory_type_controller.h"
#include "components/sync/driver/sync_service_observer.h"
namespace password_manager {
@@ -22,7 +22,7 @@ class SyncClient;
namespace browser_sync {
// A class that manages the startup and shutdown of password sync.
-class PasswordDataTypeController : public syncer::NonUIDataTypeController,
+class PasswordDataTypeController : public syncer::AsyncDirectoryTypeController,
public syncer::SyncServiceObserver {
public:
// |dump_stack| is called when an unrecoverable error occurs.
@@ -33,13 +33,10 @@ class PasswordDataTypeController : public syncer::NonUIDataTypeController,
const scoped_refptr<password_manager::PasswordStore>& password_store);
~PasswordDataTypeController() override;
- // NonFrontendDataTypeController implementation
- syncer::ModelSafeGroup model_safe_group() const override;
-
protected:
- // NonUIDataTypeController interface.
- bool PostTaskOnBackendThread(const tracked_objects::Location& from_here,
- const base::Closure& task) override;
+ // AsyncDirectoryTypeController interface.
+ bool PostTaskOnModelThread(const tracked_objects::Location& from_here,
+ const base::Closure& task) override;
bool StartModels() override;
void StopModels() override;
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
index b14f21f23ba..fc4199e34ce 100644
--- 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
@@ -11,7 +11,6 @@
#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/pref_registry/testing_pref_service_syncable.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"
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 bb43bdc65df..2830c559af3 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
@@ -14,12 +14,13 @@
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/test/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/user_action_tester.h"
#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/fake_form_fetcher.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
@@ -94,21 +95,30 @@ class CredentialsFilterTest : public SyncUsernameTestBase {
CredentialsFilterTest()
: password_manager_(&client_),
+ pending_(SimpleGaiaForm("user@gmail.com")),
+ form_manager_(&password_manager_,
+ &client_,
+ driver_.AsWeakPtr(),
+ pending_,
+ base::MakeUnique<StubFormSaver>(),
+ &fetcher_),
filter_(&client_,
base::Bind(&SyncUsernameTestBase::sync_service,
base::Unretained(this)),
base::Bind(&SyncUsernameTestBase::signin_manager,
- base::Unretained(this))) {}
+ base::Unretained(this))) {
+ fetcher_.Fetch();
+ }
void CheckFilterResultsTestCase(const TestCase& test_case) {
SetSyncingPasswords(test_case.password_sync == TestCase::SYNCING_PASSWORDS);
FakeSigninAs(test_case.fake_sync_username);
- client()->set_last_committed_entry_url(test_case.last_committed_entry_url);
+ client_.set_last_committed_entry_url(test_case.last_committed_entry_url);
base::HistogramTester tester;
const bool expected_is_form_filtered =
test_case.is_form_filtered == TestCase::FORM_FILTERED;
EXPECT_EQ(expected_is_form_filtered,
- IsFormFiltered(filter(), test_case.form));
+ IsFormFiltered(&filter_, test_case.form));
if (test_case.histogram_reported == TestCase::HISTOGRAM_REPORTED) {
tester.ExpectUniqueSample("PasswordManager.SyncCredentialFiltered",
expected_is_form_filtered, 1);
@@ -118,36 +128,27 @@ class CredentialsFilterTest : public SyncUsernameTestBase {
FakeSignout();
}
- // Creates a PasswordFormManager for the |pending| form and provisionally
- // saves it. Ensures that the created manager's IsNewLogin responds according
- // to |login_state|.
- std::unique_ptr<PasswordFormManager> CreateFormManager(
- LoginState login_state,
- const PasswordForm& pending) {
- auto form_manager = base::MakeUnique<PasswordFormManager>(
- &password_manager_, &client_, driver_.AsWeakPtr(), pending,
- base::MakeUnique<StubFormSaver>());
-
- std::vector<std::unique_ptr<PasswordForm>> saved_forms;
+ // Makes |form_manager_| provisionally save |pending_|. Depending on
+ // |login_state| being NEW or EXISTING, prepares |form_manager_| in a state in
+ // which |pending_| looks like a new or existing credential, respectively.
+ void SavePending(LoginState login_state) {
+ std::vector<const PasswordForm*> matches;
if (login_state == LoginState::EXISTING) {
- saved_forms.push_back(base::MakeUnique<PasswordForm>(pending));
+ matches.push_back(&pending_);
}
- form_manager->OnGetPasswordStoreResults(std::move(saved_forms));
-
- form_manager->ProvisionallySave(
- pending, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+ fetcher_.SetNonFederated(matches, 0u);
- return form_manager;
+ form_manager_.ProvisionallySave(
+ pending_, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
}
- SyncCredentialsFilter* filter() { return &filter_; }
-
- FakePasswordManagerClient* client() { return &client_; }
-
- private:
+ protected:
FakePasswordManagerClient client_;
PasswordManager password_manager_;
StubPasswordManagerDriver driver_;
+ PasswordForm pending_;
+ FakeFormFetcher fetcher_;
+ PasswordFormManager form_manager_;
SyncCredentialsFilter filter_;
};
@@ -280,57 +281,55 @@ TEST_F(CredentialsFilterTest, FilterResults_DisallowSync) {
}
TEST_F(CredentialsFilterTest, ReportFormLoginSuccess_ExistingSyncCredentials) {
- PasswordForm pending = SimpleGaiaForm("user@gmail.com");
FakeSigninAs("user@gmail.com");
SetSyncingPasswords(true);
base::UserActionTester tester;
- auto form_manager = CreateFormManager(LoginState::EXISTING, pending);
- filter()->ReportFormLoginSuccess(*form_manager);
+ SavePending(LoginState::EXISTING);
+ filter_.ReportFormLoginSuccess(form_manager_);
EXPECT_EQ(1, tester.GetActionCount(kFilledAndLoginActionName));
}
TEST_F(CredentialsFilterTest, ReportFormLoginSuccess_NewSyncCredentials) {
- PasswordForm pending = SimpleGaiaForm("user@gmail.com");
FakeSigninAs("user@gmail.com");
SetSyncingPasswords(true);
base::UserActionTester tester;
- auto form_manager = CreateFormManager(LoginState::NEW, pending);
- filter()->ReportFormLoginSuccess(*form_manager);
+ SavePending(LoginState::NEW);
+ filter_.ReportFormLoginSuccess(form_manager_);
EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
}
TEST_F(CredentialsFilterTest, ReportFormLoginSuccess_GAIANotSyncCredentials) {
- PasswordForm pending = SimpleGaiaForm("user@gmail.com");
- FakeSigninAs("other_user@gmail.com");
+ const char kOtherUsername[] = "other_user@gmail.com";
+ FakeSigninAs(kOtherUsername);
+ ASSERT_NE(pending_.username_value, base::ASCIIToUTF16(kOtherUsername));
SetSyncingPasswords(true);
base::UserActionTester tester;
- auto form_manager = CreateFormManager(LoginState::EXISTING, pending);
- filter()->ReportFormLoginSuccess(*form_manager);
+ SavePending(LoginState::EXISTING);
+ filter_.ReportFormLoginSuccess(form_manager_);
EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
}
TEST_F(CredentialsFilterTest, ReportFormLoginSuccess_NotGAIACredentials) {
- PasswordForm pending = SimpleNonGaiaForm("user@gmail.com");
+ pending_ = SimpleNonGaiaForm("user@gmail.com");
FakeSigninAs("user@gmail.com");
SetSyncingPasswords(true);
base::UserActionTester tester;
- auto form_manager = CreateFormManager(LoginState::EXISTING, pending);
- filter()->ReportFormLoginSuccess(*form_manager);
+ SavePending(LoginState::EXISTING);
+ filter_.ReportFormLoginSuccess(form_manager_);
EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
}
TEST_F(CredentialsFilterTest, ReportFormLoginSuccess_NotSyncing) {
- PasswordForm pending = SimpleGaiaForm("user@gmail.com");
FakeSigninAs("user@gmail.com");
SetSyncingPasswords(false);
base::UserActionTester tester;
- auto form_manager = CreateFormManager(LoginState::EXISTING, pending);
- filter()->ReportFormLoginSuccess(*form_manager);
+ SavePending(LoginState::EXISTING);
+ filter_.ReportFormLoginSuccess(form_manager_);
EXPECT_EQ(0, tester.GetActionCount(kFilledAndLoginActionName));
}
@@ -340,7 +339,7 @@ TEST_F(CredentialsFilterTest, ShouldSave_NotSyncCredential) {
ASSERT_NE("user@example.org",
signin_manager()->GetAuthenticatedAccountInfo().email);
SetSyncingPasswords(true);
- EXPECT_TRUE(filter()->ShouldSave(form));
+ EXPECT_TRUE(filter_.ShouldSave(form));
}
TEST_F(CredentialsFilterTest, ShouldSave_SyncCredential) {
@@ -348,7 +347,7 @@ TEST_F(CredentialsFilterTest, ShouldSave_SyncCredential) {
FakeSigninAs("user@example.org");
SetSyncingPasswords(true);
- EXPECT_FALSE(filter()->ShouldSave(form));
+ EXPECT_FALSE(filter_.ShouldSave(form));
}
TEST_F(CredentialsFilterTest, ShouldSave_SyncCredential_NotSyncingPasswords) {
@@ -356,7 +355,7 @@ TEST_F(CredentialsFilterTest, ShouldSave_SyncCredential_NotSyncingPasswords) {
FakeSigninAs("user@example.org");
SetSyncingPasswords(false);
- EXPECT_TRUE(filter()->ShouldSave(form));
+ EXPECT_TRUE(filter_.ShouldSave(form));
}
TEST_F(CredentialsFilterTest, ShouldFilterOneForm) {
@@ -376,7 +375,7 @@ TEST_F(CredentialsFilterTest, ShouldFilterOneForm) {
FakeSigninAs("test1@gmail.com");
- results = filter()->FilterResults(std::move(results));
+ results = filter_.FilterResults(std::move(results));
ASSERT_EQ(1u, results.size());
EXPECT_EQ(SimpleGaiaForm("test2@gmail.com"), *results[0]);
diff --git a/chromium/components/password_manager/sync/browser/sync_username_test_base.h b/chromium/components/password_manager/sync/browser/sync_username_test_base.h
index 6c5835c8f54..77972b54efb 100644
--- a/chromium/components/password_manager/sync/browser/sync_username_test_base.h
+++ b/chromium/components/password_manager/sync/browser/sync_username_test_base.h
@@ -11,12 +11,12 @@
#include <string>
#include "components/autofill/core/common/password_form.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/signin_manager_base.h"
#include "components/signin/core/browser/test_signin_client.h"
#include "components/sync/base/model_type.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"
namespace password_manager {
@@ -68,7 +68,7 @@ class SyncUsernameTestBase : public testing::Test {
using SigninManagerBase::clear_authenticated_user;
};
- user_prefs::TestingPrefServiceSyncable prefs_;
+ sync_preferences::TestingPrefServiceSyncable prefs_;
TestSigninClient signin_client_;
AccountTrackerService account_tracker_;
FakeSigninManagerBase signin_manager_;
diff --git a/chromium/components/payments/BUILD.gn b/chromium/components/payments/BUILD.gn
index 924122beec4..8df0365533d 100644
--- a/chromium/components/payments/BUILD.gn
+++ b/chromium/components/payments/BUILD.gn
@@ -20,12 +20,39 @@ mojom("payment_app") {
]
public_deps = [
+ ":payment_request",
"//mojo/common:common_custom_types",
+ "//url/mojo:url_mojom_gurl",
]
}
+# TODO(crbug.com/679381): Create a layered component to that we can remove the
+# iOS check here (iOS strict DEPS checker doesn't like the //content dependency
+# below).
+if (!is_ios) {
+ static_library("payment_request_impl") {
+ sources = [
+ "payment_request.cc",
+ "payment_request.h",
+ "payment_request_delegate.h",
+ "payment_request_web_contents_manager.cc",
+ "payment_request_web_contents_manager.h",
+ ]
+
+ deps = [
+ ":payment_request",
+ ":payment_validation",
+ "//components/autofill/core/browser",
+ "//content/public/browser",
+ "//mojo/public/cpp/bindings",
+ ]
+ }
+}
+
static_library("payment_validation") {
sources = [
+ "currency_formatter.cc",
+ "currency_formatter.h",
"payment_details_validation.cc",
"payment_details_validation.h",
"payments_validators.cc",
@@ -38,16 +65,23 @@ static_library("payment_validation") {
"//third_party/re2:re2",
"//url:url",
]
+
+ public_deps = [
+ "//third_party/icu:icu",
+ ]
}
-static_library("unit_tests") {
+source_set("unit_tests") {
testonly = true
sources = [
+ "currency_formatter_unittest.cc",
"payments_validators_test.cc",
]
deps = [
":payment_validation",
+ "//base",
"//testing/gtest",
+ "//third_party/icu:icu",
]
}
diff --git a/chromium/components/payments/DEPS b/chromium/components/payments/DEPS
index 0de07bbaf08..151fdf5f8b6 100644
--- a/chromium/components/payments/DEPS
+++ b/chromium/components/payments/DEPS
@@ -1,3 +1,8 @@
include_rules = [
+ "+components/autofill",
+ # TODO(crbug.com/679381): Move this to components/payments/content.
+ "+content/public/browser",
+
+ "+mojo/public/cpp",
"+third_party/re2",
]
diff --git a/chromium/components/payments/android/BUILD.gn b/chromium/components/payments/android/BUILD.gn
index 01ccece0826..a1949e547df 100644
--- a/chromium/components/payments/android/BUILD.gn
+++ b/chromium/components/payments/android/BUILD.gn
@@ -7,10 +7,13 @@ import("//build/config/android/rules.gni")
static_library("payments_jni") {
sources = [
+ "currency_formatter_android.cc",
+ "currency_formatter_android.h",
"payments_jni_registrar.cc",
]
deps = [
":jni_headers",
+ "//base",
"//components/payments:payment_request",
"//components/payments:payment_validation",
]
@@ -18,6 +21,7 @@ static_library("payments_jni") {
generate_jni("jni_headers") {
sources = [
+ "//chrome/android/java/src/org/chromium/chrome/browser/payments/CurrencyFormatter.java",
"//chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentValidator.java",
]
jni_package = "payments"
diff --git a/chromium/components/payments/android/currency_formatter_android.cc b/chromium/components/payments/android/currency_formatter_android.cc
new file mode 100644
index 00000000000..00143f3214e
--- /dev/null
+++ b/chromium/components/payments/android/currency_formatter_android.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/payments/android/currency_formatter_android.h"
+
+#include "base/android/jni_string.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "components/payments/currency_formatter.h"
+#include "jni/CurrencyFormatter_jni.h"
+
+using base::android::JavaParamRef;
+using base::android::ConvertJavaStringToUTF8;
+
+namespace payments {
+
+CurrencyFormatterAndroid::CurrencyFormatterAndroid(
+ JNIEnv* env,
+ jobject unused_obj,
+ const JavaParamRef<jstring>& currency_code,
+ const JavaParamRef<jstring>& currency_system,
+ const JavaParamRef<jstring>& locale_name) {
+ std::string currency_system_str =
+ ConvertJavaStringToUTF8(env, currency_system);
+
+ currency_formatter_.reset(new CurrencyFormatter(
+ ConvertJavaStringToUTF8(env, currency_code),
+ currency_system_str.empty()
+ ? base::Optional<std::string>()
+ : base::Optional<std::string>(currency_system_str),
+ ConvertJavaStringToUTF8(env, locale_name)));
+}
+
+CurrencyFormatterAndroid::~CurrencyFormatterAndroid() {}
+
+void CurrencyFormatterAndroid::Destroy(JNIEnv* env,
+ const JavaParamRef<jobject>& obj) {
+ delete this;
+}
+
+base::android::ScopedJavaLocalRef<jstring> CurrencyFormatterAndroid::Format(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& unused_obj,
+ const JavaParamRef<jstring>& amount) {
+ base::string16 result =
+ currency_formatter_->Format(ConvertJavaStringToUTF8(env, amount));
+ return base::android::ConvertUTF16ToJavaString(env, result);
+}
+
+base::android::ScopedJavaLocalRef<jstring>
+CurrencyFormatterAndroid::GetFormattedCurrencyCode(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& unused_obj) {
+ return base::android::ConvertUTF8ToJavaString(
+ env, currency_formatter_->formatted_currency_code());
+}
+
+// static
+bool CurrencyFormatterAndroid::Register(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+static jlong InitCurrencyFormatterAndroid(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ const JavaParamRef<jstring>& currency_code,
+ const JavaParamRef<jstring>& currency_system,
+ const JavaParamRef<jstring>& locale_name) {
+ CurrencyFormatterAndroid* currency_formatter_android =
+ new CurrencyFormatterAndroid(env, obj, currency_code, currency_system,
+ locale_name);
+ return reinterpret_cast<intptr_t>(currency_formatter_android);
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/android/currency_formatter_android.h b/chromium/components/payments/android/currency_formatter_android.h
new file mode 100644
index 00000000000..f6e6b33e28f
--- /dev/null
+++ b/chromium/components/payments/android/currency_formatter_android.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_PAYMENTS_ANDROID_CURRENCY_FORMATTER_ANDROID_H_
+#define COMPONENTS_PAYMENTS_ANDROID_CURRENCY_FORMATTER_ANDROID_H_
+
+#include <memory>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "components/payments/currency_formatter.h"
+
+namespace payments {
+
+// Forwarding calls to payments::CurrencyFormatter.
+class CurrencyFormatterAndroid {
+ public:
+ CurrencyFormatterAndroid(
+ JNIEnv* env,
+ jobject unused_obj,
+ const base::android::JavaParamRef<jstring>& currency_code,
+ const base::android::JavaParamRef<jstring>& currency_system,
+ const base::android::JavaParamRef<jstring>& locale_name);
+ ~CurrencyFormatterAndroid();
+
+ // Message from Java to destroy this object.
+ void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
+
+ // Refer to CurrencyFormatter::Format documentation.
+ base::android::ScopedJavaLocalRef<jstring> Format(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& unused_obj,
+ const base::android::JavaParamRef<jstring>& amount);
+
+ base::android::ScopedJavaLocalRef<jstring> GetFormattedCurrencyCode(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& unused_obj);
+
+ // Registers the JNI bindings for this class.
+ static bool Register(JNIEnv* env);
+
+ private:
+ std::unique_ptr<CurrencyFormatter> currency_formatter_;
+
+ DISALLOW_COPY_AND_ASSIGN(CurrencyFormatterAndroid);
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_ANDROID_CURRENCY_FORMATTER_ANDROID_H_
diff --git a/chromium/components/payments/currency_formatter.cc b/chromium/components/payments/currency_formatter.cc
new file mode 100644
index 00000000000..d6d4bc04769
--- /dev/null
+++ b/chromium/components/payments/currency_formatter.cc
@@ -0,0 +1,136 @@
+// Copyright 2017 The Chromium Authors. All 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/currency_formatter.h"
+
+#include <memory>
+
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "third_party/icu/source/common/unicode/stringpiece.h"
+#include "third_party/icu/source/common/unicode/uchar.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
+#include "third_party/icu/source/common/unicode/utypes.h"
+
+namespace payments {
+
+const char kIso4217CurrencySystem[] = "urn:iso:std:iso:4217";
+
+namespace {
+
+// Support a maximum of 10 fractional digits, similar to the ISO20022 standard.
+// https://www.iso20022.org/standardsrepository/public/wqt/Description/mx/dico/
+// datatypes/_L8ZcEp0gEeOo48XfssNw8w
+const int kMaximumNumFractionalDigits = 10;
+
+// Max currency code length. Length of currency code can be at most 2048.
+const static size_t kMaxCurrencyCodeLength = 2048;
+
+// Currency codes longer than 6 characters get truncated to 5 + ellipsis.
+const static size_t kMaxCurrencyCodeDisplayedChars = 6;
+
+// Used to truncate long currency codes.
+const char kEllipsis[] = "\xE2\x80\xA6";
+
+// Returns whether the |currency_code| is valid to be used in ICU.
+bool ShouldUseCurrencyCode(const std::string& currency_code,
+ const base::Optional<std::string> currency_system) {
+ return currency_system.value_or(kIso4217CurrencySystem) ==
+ kIso4217CurrencySystem &&
+ !currency_code.empty() &&
+ currency_code.size() <= kMaxCurrencyCodeLength;
+}
+
+std::string FormatCurrencyCode(const std::string& currency_code) {
+ return currency_code.length() < kMaxCurrencyCodeDisplayedChars
+ ? currency_code
+ : currency_code.substr(0, kMaxCurrencyCodeDisplayedChars - 1) +
+ kEllipsis;
+}
+
+} // namespace
+
+CurrencyFormatter::CurrencyFormatter(
+ const std::string& currency_code,
+ const base::Optional<std::string> currency_system,
+ const std::string& locale_name)
+ : locale_(locale_name.c_str()),
+ formatted_currency_code_(FormatCurrencyCode(currency_code)) {
+ UErrorCode error_code = U_ZERO_ERROR;
+ icu_formatter_.reset(
+ icu::NumberFormat::createCurrencyInstance(locale_, error_code));
+ if (U_FAILURE(error_code)) {
+ LOG(ERROR) << "Failed to initialize the currency formatter for "
+ << locale_name;
+ return;
+ }
+
+ if (ShouldUseCurrencyCode(currency_code, currency_system)) {
+ currency_code_.reset(new icu::UnicodeString(
+ currency_code.c_str(),
+ base::checked_cast<int32_t>(currency_code.size())));
+ } else {
+ // For non-ISO4217 currency system/code, we use a dummy code which is not
+ // going to appear in the output (stripped in Format()). This is because ICU
+ // NumberFormat will not accept an empty currency code. Under these
+ // circumstances, the number amount will be formatted according to locale,
+ // which is desirable (e.g. "55.00" -> "55,00" in fr_FR).
+ currency_code_.reset(new icu::UnicodeString("DUM", 3));
+ }
+
+ icu_formatter_->setCurrency(currency_code_->getBuffer(), error_code);
+ if (U_FAILURE(error_code)) {
+ std::string currency_code_str;
+ currency_code_->toUTF8String(currency_code_str);
+ LOG(ERROR) << "Could not set currency code on currency formatter: "
+ << currency_code_str;
+ return;
+ }
+
+ icu_formatter_->setMaximumFractionDigits(kMaximumNumFractionalDigits);
+}
+
+CurrencyFormatter::~CurrencyFormatter() {}
+
+base::string16 CurrencyFormatter::Format(const std::string& amount) {
+ // It's possible that the ICU formatter didn't initialize properly.
+ if (!icu_formatter_ || !icu_formatter_->getCurrency())
+ return base::UTF8ToUTF16(amount);
+
+ icu::UnicodeString output;
+ UErrorCode error_code = U_ZERO_ERROR;
+ icu_formatter_->format(icu::StringPiece(amount.c_str()), output, nullptr,
+ error_code);
+
+ if (output.isEmpty())
+ return base::UTF8ToUTF16(amount);
+
+ // Explicitly removes the currency code (truncated to its 3-letter and
+ // 2-letter versions) from the output, because callers are expected to
+ // display the currency code alongside this result.
+ //
+ // 3+ letters: If currency code is "ABCDEF" or "BTX", this code will
+ // transform "ABC55.00"/"BTX55.00" to "55.00".
+ // 2 letters: If currency code is "CAD", this code will transform "CA$55.00"
+ // to "$55.00" (en_US) or "55,00 $ CA" to "55,00 $" (fr_FR).
+ icu::UnicodeString tmp_currency_code(*currency_code_);
+ tmp_currency_code.truncate(3);
+ output.findAndReplace(tmp_currency_code, "");
+ tmp_currency_code.truncate(2);
+ output.findAndReplace(tmp_currency_code, "");
+ // Trims any unicode whitespace (including non-breaking space).
+ if (u_isUWhiteSpace(output[0])) {
+ output.remove(0, 1);
+ }
+ if (u_isUWhiteSpace(output[output.length() - 1])) {
+ output.remove(output.length() - 1, 1);
+ }
+
+ std::string output_str;
+ output.toUTF8String(output_str);
+ return base::UTF8ToUTF16(output_str);
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/currency_formatter.h b/chromium/components/payments/currency_formatter.h
new file mode 100644
index 00000000000..2794b37dcce
--- /dev/null
+++ b/chromium/components/payments/currency_formatter.h
@@ -0,0 +1,59 @@
+// Copyright 2017 The Chromium Authors. All 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_CURRENCY_FORMATTER_H_
+#define COMPONENTS_PAYMENTS_CURRENCY_FORMATTER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "third_party/icu/source/common/unicode/locid.h"
+#include "third_party/icu/source/i18n/unicode/numfmt.h"
+
+namespace payments {
+
+// URI specifying the ISO4217 currency code specification. See for details:
+// https://w3c.github.io/browser-payment-api/#paymentcurrencyamount-dictionary
+extern const char kIso4217CurrencySystem[];
+
+// Currency formatter for amounts, according to a currency code, which typically
+// adheres to [ISO4217] (for example, "USD" for US Dollars).
+class CurrencyFormatter {
+ public:
+ // Initializes the CurrencyFormatter for a given |currency_code|,
+ // |currency_system| and |locale_name|. Note that |currency_code| and
+ // |currency_system| should have been validated (as part of
+ // payment_details_validation.h) before this is created.
+ CurrencyFormatter(const std::string& currency_code,
+ const base::Optional<std::string> currency_system,
+ const std::string& locale_name);
+ ~CurrencyFormatter();
+
+ // Formats the |amount| according to the currency code that was set. The
+ // result will NOT contain the currency code, nor a subset of it. Rather, the
+ // caller of this function should display the currency code separately. The
+ // return value may contain non-breaking space and is ready for display. In
+ // the case of a failure in initialization of the formatter or during
+ // formatter, this method will return |amount|.
+ base::string16 Format(const std::string& amount);
+
+ // Returns the formatted currency code (<= 6 characters including ellipsis if
+ // applicable).
+ std::string formatted_currency_code() { return formatted_currency_code_; }
+
+ private:
+ const icu::Locale locale_;
+ std::unique_ptr<icu::UnicodeString> currency_code_;
+ std::string formatted_currency_code_;
+ std::unique_ptr<icu::NumberFormat> icu_formatter_;
+
+ DISALLOW_COPY_AND_ASSIGN(CurrencyFormatter);
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CURRENCY_FORMATTER_H_
diff --git a/chromium/components/payments/currency_formatter_unittest.cc b/chromium/components/payments/currency_formatter_unittest.cc
new file mode 100644
index 00000000000..ad679c62460
--- /dev/null
+++ b/chromium/components/payments/currency_formatter_unittest.cc
@@ -0,0 +1,178 @@
+// Copyright 2017 The Chromium Authors. All 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/currency_formatter.h"
+
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace payments {
+
+struct TestCase {
+ TestCase(const char* amount,
+ const char* currency_code,
+ const char* locale_name,
+ const std::string& expected_amount,
+ const char* expected_currency_code,
+ const char* currency_system = kIso4217CurrencySystem)
+ : amount(amount),
+ currency_code(currency_code),
+ locale_name(locale_name),
+ expected_amount(expected_amount),
+ expected_currency_code(expected_currency_code),
+ currency_system(currency_system) {}
+ ~TestCase() {}
+
+ const char* const amount;
+ const char* const currency_code;
+ const char* const locale_name;
+ const std::string expected_amount;
+ const char* const expected_currency_code;
+ const base::Optional<std::string> currency_system;
+};
+
+class PaymentsCurrencyFormatterTest : public testing::TestWithParam<TestCase> {
+};
+
+TEST_P(PaymentsCurrencyFormatterTest, IsValidCurrencyFormat) {
+ CurrencyFormatter formatter(GetParam().currency_code,
+ GetParam().currency_system,
+ GetParam().locale_name);
+ base::string16 output_amount = formatter.Format(GetParam().amount);
+
+ // Convenience so the test cases can use regular spaces.
+ const base::string16 kSpace(base::ASCIIToUTF16(" "));
+ const base::string16 kNonBreakingSpace(base::UTF8ToUTF16("\xC2\xA0"));
+ base::string16 converted;
+ base::ReplaceChars(base::UTF8ToUTF16(GetParam().expected_amount), kSpace,
+ kNonBreakingSpace, &converted);
+
+ EXPECT_EQ(converted, output_amount)
+ << "Failed to convert " << GetParam().amount << " ("
+ << GetParam().currency_code << ") in " << GetParam().locale_name;
+ EXPECT_EQ(GetParam().expected_currency_code,
+ formatter.formatted_currency_code());
+}
+
+INSTANTIATE_TEST_CASE_P(
+ CurrencyAmounts,
+ PaymentsCurrencyFormatterTest,
+ testing::Values(
+ TestCase("55.00", "USD", "en_US", "$55.00", "USD"),
+ TestCase("55.00", "USD", "en_CA", "$55.00", "USD"),
+ TestCase("55.00", "USD", "fr_CA", "55,00 $", "USD"),
+ TestCase("55.00", "USD", "fr_FR", "55,00 $", "USD"),
+ TestCase("1234", "USD", "fr_FR", "1 234,00 $", "USD"),
+
+ TestCase("55.5", "USD", "en_US", "$55.50", "USD"),
+ TestCase("55", "USD", "en_US", "$55.00", "USD"),
+ TestCase("123", "USD", "en_US", "$123.00", "USD"),
+ TestCase("1234", "USD", "en_US", "$1,234.00", "USD"),
+ TestCase("0.1234", "USD", "en_US", "$0.1234", "USD"),
+
+ TestCase("55.00", "EUR", "en_US", "€55.00", "EUR"),
+ TestCase("55.00", "EUR", "fr_CA", "55,00 €", "EUR"),
+ TestCase("55.00", "EUR", "fr_FR", "55,00 €", "EUR"),
+
+ TestCase("55.00", "CAD", "en_US", "$55.00", "CAD"),
+ TestCase("55.00", "CAD", "en_CA", "$55.00", "CAD"),
+ TestCase("55.00", "CAD", "fr_CA", "55,00 $", "CAD"),
+ TestCase("55.00", "CAD", "fr_FR", "55,00 $", "CAD"),
+
+ TestCase("55.00", "BRL", "en_US", "R$55.00", "BRL"),
+ TestCase("55.00", "BRL", "fr_CA", "55,00 R$", "BRL"),
+ TestCase("55.00", "BRL", "pt_BR", "R$55,00", "BRL"),
+
+ TestCase("55.00", "RUB", "en_US", "55.00", "RUB"),
+ TestCase("55.00", "RUB", "fr_CA", "55,00", "RUB"),
+ TestCase("55.00", "RUB", "ru_RU", "55,00 ₽", "RUB"),
+
+ TestCase("55", "JPY", "ja_JP", "ï¿¥55", "JPY"),
+ TestCase("55.0", "JPY", "ja_JP", "ï¿¥55", "JPY"),
+ TestCase("55.00", "JPY", "ja_JP", "ï¿¥55", "JPY"),
+ TestCase("55.12", "JPY", "ja_JP", "ï¿¥55.12", "JPY"),
+ TestCase("55.49", "JPY", "ja_JP", "ï¿¥55.49", "JPY"),
+ TestCase("55.50", "JPY", "ja_JP", "ï¿¥55.5", "JPY"),
+ TestCase("55.9999", "JPY", "ja_JP", "ï¿¥55.9999", "JPY"),
+
+ // Unofficial ISO 4217 currency code.
+ TestCase("55.00", "BTC", "en_US", "55.00", "BTC"),
+ TestCase("-0.0000000001", "BTC", "en_US", "-0.0000000001", "BTC"),
+ TestCase("-55.00", "BTC", "fr_FR", "-55,00", "BTC"),
+
+ // Any string of at most 2048 characters can be a valid currency code.
+ TestCase("55.00", "", "en_US", "55.00", ""),
+ TestCase("55,00", "", "fr_CA", "55,00", ""),
+ TestCase("55,00", "", "fr-CA", "55,00", ""),
+ TestCase("55.00", "ABCDEF", "en_US", "55.00", "ABCDE\xE2\x80\xA6"),
+
+ // Edge cases.
+ TestCase("", "", "", "", ""),
+ TestCase("-1", "", "", "- 1.00", ""),
+ TestCase("-1.1255", "", "", "- 1.1255", ""),
+
+ // Handles big numbers.
+ TestCase(
+ "123456789012345678901234567890.123456789012345678901234567890",
+ "USD",
+ "fr_FR",
+ "123 456 789 012 345 678 901 234 567 890,123456789 $",
+ "USD"),
+
+ // When the currency system is not ISO4217, only the amount is formatted
+ // using the locale (there is no other indication of currency).
+ TestCase("55.00",
+ "USD",
+ "en_CA",
+ "55.00",
+ "USD",
+ "http://currsystem.com"),
+ TestCase("55.00",
+ "USD",
+ "fr_CA",
+ "55,00",
+ "USD",
+ "http://currsystem.com"),
+ TestCase("55.00",
+ "USD",
+ "fr_FR",
+ "55,00",
+ "USD",
+ "http://currsystem.com"),
+ TestCase("1234",
+ "USD",
+ "fr_FR",
+ "1 234,00",
+ "USD",
+ "http://currsystem.com"),
+ TestCase("55.5",
+ "USD",
+ "en_US",
+ "55.50",
+ "USD",
+ "http://currsystem.com"),
+ TestCase("55", "CAD", "en_US", "55.00", "CAD", "http://currsystem.com"),
+ TestCase("123",
+ "BTC",
+ "en_US",
+ "123.00",
+ "BTC",
+ "http://currsystem.com"),
+ TestCase("1234",
+ "JPY",
+ "en_US",
+ "1,234.00",
+ "JPY",
+ "http://currsystem.com"),
+ TestCase("0.1234",
+ "USD",
+ "en_US",
+ "0.1234",
+ "USD",
+ "http://currsystem.com")));
+
+} // namespace payments
diff --git a/chromium/components/payments/payment_app.mojom b/chromium/components/payments/payment_app.mojom
index ca8a97a4d1a..a3e9715b769 100644
--- a/chromium/components/payments/payment_app.mojom
+++ b/chromium/components/payments/payment_app.mojom
@@ -4,25 +4,41 @@
module payments.mojom;
+import "components/payments/payment_request.mojom";
+import "url/mojo/url.mojom";
+
enum PaymentAppManifestError {
NONE,
NOT_IMPLEMENTED,
+ NO_ACTIVE_WORKER,
+ MANIFEST_STORAGE_OPERATION_FAILED,
};
struct PaymentAppOption {
- string label;
+ string name;
string? icon;
string id;
array<string> enabled_methods;
};
struct PaymentAppManifest {
- string label;
+ string name;
string? icon;
array<PaymentAppOption> options;
};
interface PaymentAppManager {
- SetManifest(string service_worker_scope, PaymentAppManifest payment_app_manifest)
+ Init(string service_worker_scope);
+ SetManifest(PaymentAppManifest payment_app_manifest)
=> (PaymentAppManifestError error);
+ GetManifest()
+ => (PaymentAppManifest payment_app_manifest, PaymentAppManifestError error);
+};
+
+struct PaymentAppRequestData {
+ url.mojom.Url origin;
+ array<PaymentMethodData> methodData;
+ PaymentItem total;
+ array<PaymentDetailsModifier> modifiers;
+ string optionId;
};
diff --git a/chromium/components/payments/payment_details_validation.cc b/chromium/components/payments/payment_details_validation.cc
index 08a6fa3dd43..81c42cdbb49 100644
--- a/chromium/components/payments/payment_details_validation.cc
+++ b/chromium/components/payments/payment_details_validation.cc
@@ -44,15 +44,15 @@ bool validateShippingOptionOrPaymentItem(
return false;
}
- if (item->amount->currencySystem.has_value() &&
- item->amount->currencySystem.value().empty()) {
+ if (item->amount->currency_system.has_value() &&
+ item->amount->currency_system.value().empty()) {
*error_message = "Currency system can't be empty";
return false;
}
if (!payments::PaymentsValidators::isValidCurrencyCodeFormat(
- item->amount->currency, item->amount->currencySystem.has_value()
- ? item->amount->currencySystem.value()
+ item->amount->currency, item->amount->currency_system.has_value()
+ ? item->amount->currency_system.value()
: "",
error_message)) {
return false;
@@ -108,19 +108,15 @@ bool validatePaymentDetailsModifiers(
return false;
}
- std::set<mojo::String> uniqueMethods;
for (const auto& modifier : modifiers) {
- if (modifier->supported_methods.empty()) {
- *error_message = "Must specify at least one payment method identifier";
+ if (!modifier->method_data) {
+ *error_message = "Method data required";
return false;
}
- for (const auto& method : modifier->supported_methods) {
- if (uniqueMethods.find(method) != uniqueMethods.end()) {
- *error_message = "Duplicate payment method identifiers are not allowed";
- return false;
- }
- uniqueMethods.insert(method);
+ if (modifier->method_data->supported_methods.empty()) {
+ *error_message = "Must specify at least one payment method identifier";
+ return false;
}
if (modifier->total) {
diff --git a/chromium/components/payments/payment_request.cc b/chromium/components/payments/payment_request.cc
new file mode 100644
index 00000000000..282b3fa38af
--- /dev/null
+++ b/chromium/components/payments/payment_request.cc
@@ -0,0 +1,110 @@
+// 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/payments/payment_request.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/payments/payment_details_validation.h"
+#include "components/payments/payment_request_delegate.h"
+#include "components/payments/payment_request_web_contents_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+
+namespace payments {
+
+PaymentRequest::PaymentRequest(
+ content::WebContents* web_contents,
+ std::unique_ptr<PaymentRequestDelegate> delegate,
+ PaymentRequestWebContentsManager* manager,
+ mojo::InterfaceRequest<payments::mojom::PaymentRequest> request)
+ : web_contents_(web_contents),
+ delegate_(std::move(delegate)),
+ manager_(manager),
+ binding_(this, std::move(request)) {
+ binding_.set_connection_error_handler(
+ base::Bind(&PaymentRequest::OnError, base::Unretained(this)));
+}
+
+PaymentRequest::~PaymentRequest() {}
+
+void PaymentRequest::Init(
+ payments::mojom::PaymentRequestClientPtr client,
+ std::vector<payments::mojom::PaymentMethodDataPtr> methodData,
+ payments::mojom::PaymentDetailsPtr details,
+ payments::mojom::PaymentOptionsPtr options) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ std::string error;
+ if (!payments::validatePaymentDetails(details, &error)) {
+ LOG(ERROR) << error;
+ OnError();
+ client_.reset();
+ return;
+ }
+ client_ = std::move(client);
+ details_ = std::move(details);
+}
+
+void PaymentRequest::Show() {
+ if (!client_.is_bound() || !binding_.is_bound()) {
+ OnError();
+ return;
+ }
+ delegate_->ShowPaymentRequestDialog(this);
+}
+
+void PaymentRequest::Cancel() {
+ client_->OnError(payments::mojom::PaymentErrorReason::USER_CANCEL);
+}
+
+void PaymentRequest::OnError() {
+ binding_.Close();
+ manager_->DestroyRequest(this);
+}
+
+CurrencyFormatter* PaymentRequest::GetOrCreateCurrencyFormatter(
+ const std::string& currency_code,
+ const base::Optional<std::string> currency_system,
+ const std::string& locale_name) {
+ if (!currency_formatter_) {
+ currency_formatter_.reset(
+ new CurrencyFormatter(currency_code, currency_system, locale_name));
+ }
+
+ return currency_formatter_.get();
+}
+
+autofill::AutofillProfile* PaymentRequest::GetCurrentlySelectedProfile() {
+ // TODO(tmartino): Implement more sophisticated algorithm for populating
+ // this when it starts empty.
+ if (!profile_) {
+ autofill::PersonalDataManager* data_manager =
+ delegate_->GetPersonalDataManager();
+ auto profiles = data_manager->GetProfiles();
+ if (!profiles.empty())
+ profile_ = base::MakeUnique<autofill::AutofillProfile>(*profiles[0]);
+ }
+ return profile_ ? profile_.get() : nullptr;
+}
+
+autofill::CreditCard* PaymentRequest::GetCurrentlySelectedCreditCard() {
+ // TODO(anthonyvd): Change this code to prioritize server cards and implement
+ // a way to modify this function's return value.
+ autofill::PersonalDataManager* data_manager =
+ delegate_->GetPersonalDataManager();
+
+ const std::vector<autofill::CreditCard*> cards =
+ data_manager->GetCreditCardsToSuggest();
+
+ auto first_complete_card = std::find_if(
+ cards.begin(),
+ cards.end(),
+ [] (autofill::CreditCard* card) {
+ return card->IsValid();
+ });
+
+ return first_complete_card == cards.end() ? nullptr : *first_complete_card;
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/payment_request.h b/chromium/components/payments/payment_request.h
new file mode 100644
index 00000000000..6c55cca9ae4
--- /dev/null
+++ b/chromium/components/payments/payment_request.h
@@ -0,0 +1,93 @@
+// 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_PAYMENT_REQUEST_H_
+#define COMPONENTS_PAYMENTS_PAYMENT_REQUEST_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/payments/currency_formatter.h"
+#include "components/payments/payment_request.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace autofill {
+class AutofillProfile;
+class CreditCard;
+}
+
+namespace content {
+class WebContents;
+}
+
+namespace payments {
+
+class PaymentRequestDelegate;
+class PaymentRequestWebContentsManager;
+
+class PaymentRequest : payments::mojom::PaymentRequest {
+ public:
+ PaymentRequest(
+ content::WebContents* web_contents,
+ std::unique_ptr<PaymentRequestDelegate> delegate,
+ PaymentRequestWebContentsManager* manager,
+ mojo::InterfaceRequest<payments::mojom::PaymentRequest> request);
+ ~PaymentRequest() override;
+
+ // payments::mojom::PaymentRequest "stub"
+ void Init(payments::mojom::PaymentRequestClientPtr client,
+ std::vector<payments::mojom::PaymentMethodDataPtr> methodData,
+ payments::mojom::PaymentDetailsPtr details,
+ payments::mojom::PaymentOptionsPtr options) override;
+ void Show() override;
+ void UpdateWith(payments::mojom::PaymentDetailsPtr details) override {}
+ void Abort() override {}
+ void Complete(payments::mojom::PaymentComplete result) override {}
+ void CanMakePayment() override {}
+
+ void Cancel();
+ void OnError();
+
+ // Returns the CurrencyFormatter instance for this PaymentRequest.
+ // |locale_name| should be the result of the browser's GetApplicationLocale().
+ // Note: Having multiple currencies per PaymentRequest is not supported; hence
+ // the CurrencyFormatter is cached here.
+ CurrencyFormatter* GetOrCreateCurrencyFormatter(
+ const std::string& currency_code,
+ const base::Optional<std::string> currency_system,
+ const std::string& locale_name);
+
+ // Returns the Autofill Profile, representing the shipping address and contact
+ // information, currently selected for this PaymentRequest flow. If
+ // unpopulated, populates with and returns the 0th profile on record for this
+ // user, if it exists; or nullptr otherwise. Profile is owned by the request
+ // object, not the caller.
+ autofill::AutofillProfile* GetCurrentlySelectedProfile();
+
+ // Returns the currently selected credit card for this PaymentRequest flow.
+ // It's not guaranteed to be complete. Returns nullptr if there is no selected
+ // card.
+ autofill::CreditCard* GetCurrentlySelectedCreditCard();
+
+ payments::mojom::PaymentDetails* details() { return details_.get(); }
+ content::WebContents* web_contents() { return web_contents_; }
+
+ private:
+ content::WebContents* web_contents_;
+ std::unique_ptr<PaymentRequestDelegate> delegate_;
+ // |manager_| owns this PaymentRequest.
+ PaymentRequestWebContentsManager* manager_;
+ mojo::Binding<payments::mojom::PaymentRequest> binding_;
+ payments::mojom::PaymentRequestClientPtr client_;
+ payments::mojom::PaymentDetailsPtr details_;
+ std::unique_ptr<CurrencyFormatter> currency_formatter_;
+ std::unique_ptr<autofill::AutofillProfile> profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(PaymentRequest);
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_PAYMENT_REQUEST_H_
diff --git a/chromium/components/payments/payment_request.mojom b/chromium/components/payments/payment_request.mojom
index 3998dffc8e5..5b1abcdea23 100644
--- a/chromium/components/payments/payment_request.mojom
+++ b/chromium/components/payments/payment_request.mojom
@@ -44,11 +44,11 @@ struct PaymentCurrencyAmount {
// digits. Separated by a dot.
string value;
- // currencySystem is a URL that indicates the currency system that the
+ // 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? currencySystem;
+ string? currency_system;
};
struct PaymentResponse {
@@ -58,8 +58,8 @@ struct PaymentResponse {
// 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 richer types cannot be used. A
- // simple example:
+ // 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;
@@ -106,34 +106,6 @@ struct PaymentShippingOption {
bool selected;
};
-struct PaymentDetailsModifier {
- array<string> supported_methods;
- PaymentItem? total;
- array<PaymentItem> additional_display_items;
-};
-
-struct PaymentDetails {
- PaymentItem total;
- array<PaymentItem> display_items;
- array<PaymentShippingOption> shipping_options;
- array<PaymentDetailsModifier> modifiers;
- string error;
-};
-
-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 AndroidPayEnvironment {
PRODUCTION,
TEST
@@ -157,6 +129,23 @@ struct AndroidPayTokenizationParameter {
string? value;
};
+enum BasicCardNetwork {
+ AMEX,
+ DINERS,
+ DISCOVER,
+ JCB,
+ MASTERCARD,
+ MIR,
+ UNIONPAY,
+ VISA
+};
+
+enum BasicCardType {
+ CREDIT,
+ DEBIT,
+ PREPAID
+};
+
struct PaymentMethodData {
array<string> supported_methods;
@@ -164,8 +153,8 @@ struct PaymentMethodData {
// 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 richer types cannot be used. A simple
- // example:
+ // no one format for this object, so more specific types cannot be used. A
+ // simple example:
//
// {"gateway": "stripe"}
string stringified_data;
@@ -179,6 +168,40 @@ struct PaymentMethodData {
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;
+};
+
+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 {
@@ -189,7 +212,7 @@ enum PaymentComplete {
interface PaymentRequest {
Init(PaymentRequestClient client,
- array<PaymentMethodData> methodData,
+ array<PaymentMethodData> method_data,
PaymentDetails details,
PaymentOptions options);
Show();
diff --git a/chromium/components/payments/payment_request_delegate.h b/chromium/components/payments/payment_request_delegate.h
new file mode 100644
index 00000000000..9e666541661
--- /dev/null
+++ b/chromium/components/payments/payment_request_delegate.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_PAYMENTS_PAYMENT_REQUEST_DELEGATE_H_
+#define COMPONENTS_PAYMENTS_PAYMENT_REQUEST_DELEGATE_H_
+
+namespace autofill {
+class PersonalDataManager;
+}
+
+namespace payments {
+
+class PaymentRequest;
+
+class PaymentRequestDelegate {
+ public:
+ virtual ~PaymentRequestDelegate() {}
+
+ // Shows the Payment Request dialog for the given |request|.
+ virtual void ShowPaymentRequestDialog(PaymentRequest* request) = 0;
+
+ // Gets the PersonalDataManager associated with this PaymentRequest flow.
+ virtual autofill::PersonalDataManager* GetPersonalDataManager() = 0;
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_PAYMENT_REQUEST_DELEGATE_H_
diff --git a/chromium/components/payments/payment_request_web_contents_manager.cc b/chromium/components/payments/payment_request_web_contents_manager.cc
new file mode 100644
index 00000000000..ca893723f6f
--- /dev/null
+++ b/chromium/components/payments/payment_request_web_contents_manager.cc
@@ -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.
+
+#include "components/payments/payment_request_web_contents_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+#include "components/payments/payment_request_delegate.h"
+
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(payments::PaymentRequestWebContentsManager);
+
+namespace payments {
+
+PaymentRequestWebContentsManager::~PaymentRequestWebContentsManager() {}
+
+PaymentRequestWebContentsManager*
+PaymentRequestWebContentsManager::GetOrCreateForWebContents(
+ content::WebContents* web_contents) {
+ DCHECK(web_contents);
+ // CreateForWebContents does nothing if the manager instance already exists.
+ PaymentRequestWebContentsManager::CreateForWebContents(web_contents);
+ return PaymentRequestWebContentsManager::FromWebContents(web_contents);
+}
+
+void PaymentRequestWebContentsManager::CreatePaymentRequest(
+ content::WebContents* web_contents,
+ std::unique_ptr<PaymentRequestDelegate> delegate,
+ mojo::InterfaceRequest<payments::mojom::PaymentRequest> request) {
+ std::unique_ptr<PaymentRequest> new_request(new PaymentRequest(
+ web_contents, std::move(delegate), this, std::move(request)));
+ PaymentRequest* request_ptr = new_request.get();
+ payment_requests_.insert(std::make_pair(request_ptr, std::move(new_request)));
+}
+
+void PaymentRequestWebContentsManager::DestroyRequest(PaymentRequest* request) {
+ payment_requests_.erase(request);
+}
+
+PaymentRequestWebContentsManager::PaymentRequestWebContentsManager(
+ content::WebContents* web_contents) {}
+
+} // namespace payments
diff --git a/chromium/components/payments/payment_request_web_contents_manager.h b/chromium/components/payments/payment_request_web_contents_manager.h
new file mode 100644
index 00000000000..42e26f4a9ce
--- /dev/null
+++ b/chromium/components/payments/payment_request_web_contents_manager.h
@@ -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.
+
+#ifndef COMPONENTS_PAYMENTS_PAYMENT_REQUEST_WEB_CONTENTS_MANAGER_H_
+#define COMPONENTS_PAYMENTS_PAYMENT_REQUEST_WEB_CONTENTS_MANAGER_H_
+
+#include <unordered_map>
+
+#include "components/payments/payment_request.h"
+#include "components/payments/payment_request.mojom.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace payments {
+
+class PaymentRequestDelegate;
+
+// This class owns the PaymentRequest associated with a given WebContents.
+//
+// Responsible for creating PaymentRequest's and retaining ownership. No request
+// pointers are currently available because the request manages its interactions
+// with UI and renderer. The PaymentRequest may call DestroyRequest() to signal
+// it is ready to die. Otherwise it gets destroyed when the WebContents (thus
+// this class) goes away.
+class PaymentRequestWebContentsManager
+ : public content::WebContentsUserData<PaymentRequestWebContentsManager> {
+ public:
+ ~PaymentRequestWebContentsManager() override;
+
+ // Retrieves the instance of PaymentRequestWebContentsManager that was
+ // attached to the specified WebContents. If no instance was attached,
+ // creates one, and attaches it to the specified WebContents.
+ static PaymentRequestWebContentsManager* GetOrCreateForWebContents(
+ content::WebContents* web_contents);
+
+ // Creates the PaymentRequest that will interact with this |web_contents|.
+ void CreatePaymentRequest(
+ content::WebContents* web_contents,
+ std::unique_ptr<PaymentRequestDelegate> delegate,
+ mojo::InterfaceRequest<payments::mojom::PaymentRequest> request);
+
+ // Destroys the given |request|.
+ void DestroyRequest(PaymentRequest* request);
+
+ private:
+ explicit PaymentRequestWebContentsManager(content::WebContents* web_contents);
+ friend class content::WebContentsUserData<PaymentRequestWebContentsManager>;
+ friend class PaymentRequestInteractiveTestBase;
+
+ // Owns all the PaymentRequest for this WebContents. Since the
+ // PaymentRequestWebContentsManager's lifetime is tied to the WebContents,
+ // these requests only get destroyed when the WebContents goes away, or when
+ // the requests themselves call DestroyRequest().
+ std::unordered_map<PaymentRequest*, std::unique_ptr<PaymentRequest>>
+ payment_requests_;
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_PAYMENT_REQUEST_WEB_CONTENTS_MANAGER_H_
diff --git a/chromium/components/pdf/renderer/pdf_accessibility_tree.h b/chromium/components/pdf/renderer/pdf_accessibility_tree.h
index 954b7d928a8..d4b65013548 100644
--- a/chromium/components/pdf/renderer/pdf_accessibility_tree.h
+++ b/chromium/components/pdf/renderer/pdf_accessibility_tree.h
@@ -18,7 +18,6 @@
namespace content {
class RenderAccessibility;
-class RenderFrame;
class RendererPpapiHost;
}
diff --git a/chromium/components/pdf/renderer/pepper_pdf_host.cc b/chromium/components/pdf/renderer/pepper_pdf_host.cc
index c56d78e97ca..af5469363a0 100644
--- a/chromium/components/pdf/renderer/pepper_pdf_host.cc
+++ b/chromium/components/pdf/renderer/pepper_pdf_host.cc
@@ -101,7 +101,7 @@ int32_t PepperPDFHost::OnHostMsgDidStartLoading(
if (!render_frame)
return PP_ERROR_FAILED;
- render_frame->DidStartLoading();
+ render_frame->PluginDidStartLoading();
return PP_OK;
}
@@ -111,7 +111,7 @@ int32_t PepperPDFHost::OnHostMsgDidStopLoading(
if (!render_frame)
return PP_ERROR_FAILED;
- render_frame->DidStopLoading();
+ render_frame->PluginDidStopLoading();
return PP_OK;
}
diff --git a/chromium/components/pdf/renderer/pepper_pdf_host.h b/chromium/components/pdf/renderer/pepper_pdf_host.h
index 5e838c9e34b..94e8cc6c251 100644
--- a/chromium/components/pdf/renderer/pepper_pdf_host.h
+++ b/chromium/components/pdf/renderer/pepper_pdf_host.h
@@ -20,19 +20,12 @@
#include "ppapi/host/resource_host.h"
#include "ppapi/proxy/serialized_structs.h"
-struct PP_ImageDataDesc;
-struct PP_Size;
-class SkBitmap;
-
namespace content {
-class PepperPluginInstance;
class RenderFrame;
class RendererPpapiHost;
}
namespace ppapi {
-class HostResource;
-
namespace host {
struct HostMessageContext;
} // namespace host
diff --git a/chromium/components/physical_web/data_source/BUILD.gn b/chromium/components/physical_web/data_source/BUILD.gn
index a0dffd5201f..5e1c9620310 100644
--- a/chromium/components/physical_web/data_source/BUILD.gn
+++ b/chromium/components/physical_web/data_source/BUILD.gn
@@ -11,8 +11,22 @@ source_set("data_source") {
"physical_web_listener.h",
]
+ public_deps = [
+ "//base",
+ "//url",
+ ]
+}
+
+source_set("test_support") {
+ testonly = true
+ sources = [
+ "fake_physical_web_data_source.cc",
+ "fake_physical_web_data_source.h",
+ ]
deps = [
+ ":data_source",
"//base",
+ "//url",
]
}
@@ -26,5 +40,6 @@ source_set("unit_tests") {
":data_source",
"//base",
"//testing/gtest",
+ "//url",
]
}
diff --git a/chromium/components/physical_web/data_source/fake_physical_web_data_source.cc b/chromium/components/physical_web/data_source/fake_physical_web_data_source.cc
new file mode 100644
index 00000000000..fb04e9f9e41
--- /dev/null
+++ b/chromium/components/physical_web/data_source/fake_physical_web_data_source.cc
@@ -0,0 +1,123 @@
+// 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/physical_web/data_source/fake_physical_web_data_source.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "components/physical_web/data_source/physical_web_listener.h"
+#include "url/gurl.h"
+
+using base::ListValue;
+using base::DictionaryValue;
+
+namespace physical_web {
+
+std::unique_ptr<Metadata> CreatePhysicalWebPage(
+ const std::string& resolved_url,
+ double distance_estimate,
+ const std::string& group_id,
+ int scan_timestamp,
+ const std::string& title,
+ const std::string& description,
+ const std::string& scanned_url) {
+ auto page = base::MakeUnique<Metadata>();
+ page->resolved_url = GURL(resolved_url);
+ page->distance_estimate = distance_estimate;
+ page->group_id = group_id;
+ page->scan_timestamp = base::Time::FromJavaTime(scan_timestamp);
+ page->title = title;
+ page->description = description;
+ page->scanned_url = GURL(scanned_url);
+ return page;
+}
+
+std::unique_ptr<Metadata> CreateDummyPhysicalWebPage(int id,
+ double distance,
+ int timestamp) {
+ const std::string id_string = base::IntToString(id);
+ return CreatePhysicalWebPage("https://resolved_url.com/" + id_string,
+ distance, /*group_id=*/std::string(), timestamp,
+ "title " + id_string, "description " + id_string,
+ "https://scanned_url.com/" + id_string);
+}
+
+std::unique_ptr<MetadataList> CreateDummyPhysicalWebPages(
+ const std::vector<int>& ids) {
+ int distance = 1;
+ int timestamp = static_cast<int>(ids.size());
+ auto list = base::MakeUnique<MetadataList>();
+ for (int id : ids) {
+ std::unique_ptr<Metadata> page =
+ CreateDummyPhysicalWebPage(id, distance, timestamp);
+ list->push_back(*page);
+ ++distance;
+ --timestamp;
+ }
+ return list;
+}
+
+FakePhysicalWebDataSource::FakePhysicalWebDataSource()
+ : metadata_(base::MakeUnique<base::ListValue>()) {}
+
+FakePhysicalWebDataSource::~FakePhysicalWebDataSource() = default;
+
+void FakePhysicalWebDataSource::StartDiscovery(bool network_request_enabled) {
+ // Ignored.
+}
+
+void FakePhysicalWebDataSource::StopDiscovery() {
+ // Ignored.
+}
+
+std::unique_ptr<base::ListValue> FakePhysicalWebDataSource::GetMetadata() {
+ return metadata_->CreateDeepCopy();
+}
+
+std::unique_ptr<MetadataList> FakePhysicalWebDataSource::GetMetadataList() {
+ return base::MakeUnique<MetadataList>(*metadata_list_.get());
+}
+
+bool FakePhysicalWebDataSource::HasUnresolvedDiscoveries() {
+ return false;
+}
+
+void FakePhysicalWebDataSource::RegisterListener(
+ PhysicalWebListener* physical_web_listener) {
+ observer_list_.AddObserver(physical_web_listener);
+}
+
+void FakePhysicalWebDataSource::UnregisterListener(
+ PhysicalWebListener* physical_web_listener) {
+ observer_list_.RemoveObserver(physical_web_listener);
+}
+
+void FakePhysicalWebDataSource::SetMetadata(
+ std::unique_ptr<ListValue> metadata) {
+ metadata_ = std::move(metadata);
+}
+
+void FakePhysicalWebDataSource::SetMetadataList(
+ std::unique_ptr<MetadataList> metadata_list) {
+ metadata_list_ = std::move(metadata_list);
+}
+
+void FakePhysicalWebDataSource::NotifyOnFound(const GURL& url) {
+ for (PhysicalWebListener& observer : observer_list_)
+ observer.OnFound(url);
+}
+
+void FakePhysicalWebDataSource::NotifyOnLost(const GURL& url) {
+ for (PhysicalWebListener& observer : observer_list_)
+ observer.OnLost(url);
+}
+
+void FakePhysicalWebDataSource::NotifyOnDistanceChanged(
+ const GURL& url,
+ double distance_estimate) {
+ for (PhysicalWebListener& observer : observer_list_)
+ observer.OnDistanceChanged(url, distance_estimate);
+}
+
+} // namespace physical_web
diff --git a/chromium/components/physical_web/data_source/fake_physical_web_data_source.h b/chromium/components/physical_web/data_source/fake_physical_web_data_source.h
new file mode 100644
index 00000000000..a84e762cfb6
--- /dev/null
+++ b/chromium/components/physical_web/data_source/fake_physical_web_data_source.h
@@ -0,0 +1,70 @@
+// 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_PHYSICAL_WEB_DATA_SOURCE_FAKE_PHYSICAL_WEB_DATA_SOURCE_H_
+#define COMPONENTS_PHYSICAL_WEB_DATA_SOURCE_FAKE_PHYSICAL_WEB_DATA_SOURCE_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/observer_list.h"
+#include "components/physical_web/data_source/physical_web_data_source.h"
+
+class GURL;
+
+namespace base {
+class ListValue;
+}
+
+namespace physical_web {
+
+std::unique_ptr<Metadata> CreatePhysicalWebPage(
+ const std::string& resolved_url,
+ double distance_estimate,
+ const std::string& group_id,
+ int scan_timestamp,
+ const std::string& title,
+ const std::string& description,
+ const std::string& scanned_url);
+
+std::unique_ptr<Metadata>
+CreateDummyPhysicalWebPage(int id, double distance, int timestamp);
+
+std::unique_ptr<MetadataList> CreateDummyPhysicalWebPages(
+ const std::vector<int>& ids);
+
+class FakePhysicalWebDataSource : public PhysicalWebDataSource {
+ public:
+ FakePhysicalWebDataSource();
+ ~FakePhysicalWebDataSource() override;
+
+ void StartDiscovery(bool network_request_enabled) override;
+ void StopDiscovery() override;
+
+ std::unique_ptr<base::ListValue> GetMetadata() override;
+ std::unique_ptr<MetadataList> GetMetadataList() override;
+
+ bool HasUnresolvedDiscoveries() override;
+
+ void RegisterListener(PhysicalWebListener* physical_web_listener) override;
+ void UnregisterListener(PhysicalWebListener* physical_web_listener) override;
+
+ // for testing
+ void SetMetadata(std::unique_ptr<base::ListValue> metadata);
+ void SetMetadataList(std::unique_ptr<MetadataList> metadata_list);
+ void NotifyOnFound(const GURL& url);
+ void NotifyOnLost(const GURL& url);
+ void NotifyOnDistanceChanged(const GURL& url, double distance_estimate);
+
+ private:
+ std::unique_ptr<base::ListValue> metadata_;
+ std::unique_ptr<MetadataList> metadata_list_;
+ base::ObserverList<PhysicalWebListener> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakePhysicalWebDataSource);
+};
+
+} // namespace physical_web
+
+#endif // COMPONENTS_PHYSICAL_WEB_DATA_SOURCE_FAKE_PHYSICAL_WEB_DATA_SOURCE_H_
diff --git a/chromium/components/physical_web/data_source/physical_web_data_source.cc b/chromium/components/physical_web/data_source/physical_web_data_source.cc
index 31e8349694a..cd2a204f5f7 100644
--- a/chromium/components/physical_web/data_source/physical_web_data_source.cc
+++ b/chromium/components/physical_web/data_source/physical_web_data_source.cc
@@ -4,11 +4,21 @@
#include "components/physical_web/data_source/physical_web_data_source.h"
-const char kPhysicalWebDescriptionKey[] = "description";
-const char kPhysicalWebDistanceEstimateKey[] = "distanceEstimate";
-const char kPhysicalWebGroupIdKey[] = "groupId";
-const char kPhysicalWebIconUrlKey[] = "icon";
-const char kPhysicalWebResolvedUrlKey[] = "resolvedUrl";
-const char kPhysicalWebScanTimestampKey[] = "scanTimestamp";
-const char kPhysicalWebScannedUrlKey[] = "scannedUrl";
-const char kPhysicalWebTitleKey[] = "title";
+namespace physical_web {
+
+const char kDescriptionKey[] = "description";
+const char kDistanceEstimateKey[] = "distanceEstimate";
+const char kGroupIdKey[] = "groupId";
+const char kIconUrlKey[] = "icon";
+const char kResolvedUrlKey[] = "resolvedUrl";
+const char kScanTimestampKey[] = "scanTimestamp";
+const char kScannedUrlKey[] = "scannedUrl";
+const char kTitleKey[] = "title";
+
+Metadata::Metadata() {}
+
+Metadata::Metadata(const Metadata& other) = default;
+
+Metadata::~Metadata() {}
+
+} // namespace physical_web
diff --git a/chromium/components/physical_web/data_source/physical_web_data_source.h b/chromium/components/physical_web/data_source/physical_web_data_source.h
index 927e8933cc9..0e95c5fe361 100644
--- a/chromium/components/physical_web/data_source/physical_web_data_source.h
+++ b/chromium/components/physical_web/data_source/physical_web_data_source.h
@@ -6,22 +6,77 @@
#define COMPONENTS_PHYSICAL_WEB_DATA_SOURCE_PHYSICAL_WEB_DATA_SOURCE_H_
#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "url/gurl.h"
namespace base {
class ListValue;
}
+namespace physical_web {
+
class PhysicalWebListener;
// Dictionary keys for reading Physical Web URL metadata.
-extern const char kPhysicalWebDescriptionKey[];
-extern const char kPhysicalWebDistanceEstimateKey[];
-extern const char kPhysicalWebGroupIdKey[];
-extern const char kPhysicalWebIconUrlKey[];
-extern const char kPhysicalWebResolvedUrlKey[];
-extern const char kPhysicalWebScanTimestampKey[];
-extern const char kPhysicalWebScannedUrlKey[];
-extern const char kPhysicalWebTitleKey[];
+// TODO(cco3): Remove these when we are no longer dependent.
+extern const char kDescriptionKey[];
+extern const char kDistanceEstimateKey[];
+extern const char kGroupIdKey[];
+extern const char kIconUrlKey[];
+extern const char kResolvedUrlKey[];
+extern const char kScanTimestampKey[];
+extern const char kScannedUrlKey[];
+extern const char kTitleKey[];
+
+// Metadata struct for associating data with Physical Web URLs.
+struct Metadata {
+ Metadata();
+ Metadata(const Metadata& other);
+ ~Metadata();
+ // The URL broadcasted by the beacon and scanned by the client device.
+ // REQUIRED
+ GURL scanned_url;
+ // The URL that the scanned_url redirects to.
+ // This is the URL that users should be directed to.
+ // REQUIRED
+ GURL resolved_url;
+ // The favicon URL.
+ // OPTIONAL
+ GURL icon_url;
+ // The title of the web page.
+ // REQUIRED
+ std::string title;
+ // The description of the web page.
+ // OPTIONAL: When the website has not specified a description, the PWS
+ // generates one based on the initial text of the site, but this is not
+ // guaranteed behavior.
+ std::string description;
+ // An identifier that associates multiple resolved URLs. These URLs will
+ // most typically be associated because their metadata is near-identical
+ // (same icon, title, description, URL minus the fragment). e.g.,
+ // https://mymuseum/exhibits#e1
+ // https://mymuseum/exhibits#e2
+ // If two URLs have the same group id, only one should be shown (typically,
+ // the one with the smallest distance estimate).
+ // OPTIONAL: Treat the item as its own unique group if this is empty.
+ std::string group_id;
+ // The estimated distance between the user and the Physical Web device (e.g.,
+ // beacon) in meters.
+ // OPTIONAL: This will be a value <= 0 if no distance estimate has been
+ // calculated. The distance may not be calculated if we aren't able to
+ // receive an estimate from the underlying scanning service in time, or if
+ // (in the future) we begin sourcing Physical Web URLs from a non-BLE
+ // transport (e.g. mDNS).
+ double distance_estimate;
+ // The timestamp corresponding to when this URL was last scanned.
+ // REQUIRED
+ base::Time scan_timestamp;
+};
+
+using MetadataList = std::vector<Metadata>;
// Helper class for accessing Physical Web metadata and controlling the scanner.
class PhysicalWebDataSource {
@@ -39,6 +94,14 @@ class PhysicalWebDataSource {
// requests are disabled or if discovery is not active, the list will be
// empty. The method can be called at any time to receive the current metadata
// list.
+ virtual std::unique_ptr<MetadataList> GetMetadataList() = 0;
+
+ // Returns a list of resolved URLs and associated page metadata. If network
+ // requests are disabled or if discovery is not active, the list will be
+ // empty. The method can be called at any time to receive the current metadata
+ // list.
+ // DEPRECATED
+ // TODO(cco3): Remove this when we are no longer dependent on it.
virtual std::unique_ptr<base::ListValue> GetMetadata() = 0;
// Returns boolean |true| if network requests are disabled and there are one
@@ -56,4 +119,6 @@ class PhysicalWebDataSource {
PhysicalWebListener* physical_web_listener) = 0;
};
+} // namespace physical_web
+
#endif // COMPONENTS_PHYSICAL_WEB_DATA_SOURCE_PHYSICAL_WEB_DATA_SOURCE_H_
diff --git a/chromium/components/physical_web/data_source/physical_web_data_source_impl.cc b/chromium/components/physical_web/data_source/physical_web_data_source_impl.cc
index 600a2b5e61c..86b4c185a14 100644
--- a/chromium/components/physical_web/data_source/physical_web_data_source_impl.cc
+++ b/chromium/components/physical_web/data_source/physical_web_data_source_impl.cc
@@ -7,6 +7,8 @@
#include "components/physical_web/data_source/physical_web_data_source_impl.h"
#include "components/physical_web/data_source/physical_web_listener.h"
+namespace physical_web {
+
PhysicalWebDataSourceImpl::PhysicalWebDataSourceImpl() {}
PhysicalWebDataSourceImpl::~PhysicalWebDataSourceImpl() {}
@@ -21,19 +23,21 @@ void PhysicalWebDataSourceImpl::UnregisterListener(
observer_list_.RemoveObserver(physical_web_listener);
}
-void PhysicalWebDataSourceImpl::NotifyOnFound(const std::string& url) {
+void PhysicalWebDataSourceImpl::NotifyOnFound(const GURL& url) {
for (PhysicalWebListener& observer : observer_list_)
observer.OnFound(url);
}
-void PhysicalWebDataSourceImpl::NotifyOnLost(const std::string& url) {
+void PhysicalWebDataSourceImpl::NotifyOnLost(const GURL& url) {
for (PhysicalWebListener& observer : observer_list_)
observer.OnLost(url);
}
void PhysicalWebDataSourceImpl::NotifyOnDistanceChanged(
- const std::string& url,
+ const GURL& url,
double distance_estimate) {
for (PhysicalWebListener& observer : observer_list_)
observer.OnDistanceChanged(url, distance_estimate);
}
+
+} // namespace physical_web
diff --git a/chromium/components/physical_web/data_source/physical_web_data_source_impl.h b/chromium/components/physical_web/data_source/physical_web_data_source_impl.h
index df23be3c7ec..a9326d2e097 100644
--- a/chromium/components/physical_web/data_source/physical_web_data_source_impl.h
+++ b/chromium/components/physical_web/data_source/physical_web_data_source_impl.h
@@ -7,6 +7,10 @@
#include "base/observer_list.h"
#include "components/physical_web/data_source/physical_web_data_source.h"
+class GURL;
+
+namespace physical_web {
+
class PhysicalWebListener;
class PhysicalWebDataSourceImpl : public PhysicalWebDataSource {
@@ -21,17 +25,19 @@ class PhysicalWebDataSourceImpl : public PhysicalWebDataSource {
void UnregisterListener(PhysicalWebListener* physical_web_listener) override;
// Notify all registered listeners that a URL has been found.
- void NotifyOnFound(const std::string& url);
+ void NotifyOnFound(const GURL& url);
// Notify all registered listeners that a URL has been lost.
- void NotifyOnLost(const std::string& url);
+ void NotifyOnLost(const GURL& url);
// Notify all registered listeners that a distance has changed for a URL.
- void NotifyOnDistanceChanged(const std::string& url,
+ void NotifyOnDistanceChanged(const GURL& url,
double distance_estimate);
private:
base::ObserverList<PhysicalWebListener> observer_list_;
};
+} // namespace physical_web
+
#endif // COMPONENTS_PHYSICAL_WEB_DATA_SOURCE_PHYSICAL_WEB_DATA_SOURCE_IMPL_H_
diff --git a/chromium/components/physical_web/data_source/physical_web_data_source_impl_unittest.cc b/chromium/components/physical_web/data_source/physical_web_data_source_impl_unittest.cc
index 3ba18f0d880..ddf30bc4542 100644
--- a/chromium/components/physical_web/data_source/physical_web_data_source_impl_unittest.cc
+++ b/chromium/components/physical_web/data_source/physical_web_data_source_impl_unittest.cc
@@ -5,13 +5,13 @@
#include "base/values.h"
#include "components/physical_web/data_source/physical_web_data_source_impl.h"
#include "components/physical_web/data_source/physical_web_listener.h"
-
#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
namespace physical_web {
// Test Values ----------------------------------------------------------------
-const char kUrl[] = "https://www.google.com";
+GURL kUrl = GURL("https://www.google.com");
// TestPhysicalWebDataSource --------------------------------------------------
@@ -23,6 +23,7 @@ class TestPhysicalWebDataSource : public PhysicalWebDataSourceImpl {
void StartDiscovery(bool network_request_enabled) override;
void StopDiscovery() override;
std::unique_ptr<base::ListValue> GetMetadata() override;
+ std::unique_ptr<MetadataList> GetMetadataList() override;
bool HasUnresolvedDiscoveries() override;
};
void TestPhysicalWebDataSource::StartDiscovery(bool network_request_enabled) {}
@@ -33,6 +34,10 @@ std::unique_ptr<base::ListValue> TestPhysicalWebDataSource::GetMetadata() {
return NULL;
}
+std::unique_ptr<MetadataList> TestPhysicalWebDataSource::GetMetadataList() {
+ return NULL;
+}
+
bool TestPhysicalWebDataSource::HasUnresolvedDiscoveries() {
return false;
}
@@ -48,18 +53,17 @@ class TestPhysicalWebListener : public PhysicalWebListener {
~TestPhysicalWebListener() {}
- void OnFound(const std::string& url) override {
+ void OnFound(const GURL& url) override {
on_found_notified_ = true;
last_event_url_ = url;
}
- void OnLost(const std::string& url) override {
+ void OnLost(const GURL& url) override {
on_lost_notified_ = true;
last_event_url_ = url;
}
- void OnDistanceChanged(const std::string& url,
- double distance_estimate) override {
+ void OnDistanceChanged(const GURL& url, double distance_estimate) override {
on_distance_changed_notified_ = true;
last_event_url_ = url;
}
@@ -70,13 +74,13 @@ class TestPhysicalWebListener : public PhysicalWebListener {
bool OnDistanceChangedNotified() { return on_distance_changed_notified_; }
- std::string LastEventUrl() { return last_event_url_; }
+ GURL LastEventUrl() { return last_event_url_; }
private:
bool on_found_notified_;
bool on_lost_notified_;
bool on_distance_changed_notified_;
- std::string last_event_url_;
+ GURL last_event_url_;
};
// PhysicalWebDataSourceImplTest ----------------------------------------------
@@ -135,7 +139,7 @@ TEST_F(PhysicalWebDataSourceImplTest, OnFoundNotRegistered) {
EXPECT_FALSE(listener_.OnFoundNotified());
EXPECT_FALSE(listener_.OnLostNotified());
EXPECT_FALSE(listener_.OnDistanceChangedNotified());
- EXPECT_TRUE(listener_.LastEventUrl().empty());
+ EXPECT_TRUE(listener_.LastEventUrl().is_empty());
}
TEST_F(PhysicalWebDataSourceImplTest, OnLostNotRegistered) {
@@ -144,7 +148,7 @@ TEST_F(PhysicalWebDataSourceImplTest, OnLostNotRegistered) {
EXPECT_FALSE(listener_.OnFoundNotified());
EXPECT_FALSE(listener_.OnLostNotified());
EXPECT_FALSE(listener_.OnDistanceChangedNotified());
- EXPECT_TRUE(listener_.LastEventUrl().empty());
+ EXPECT_TRUE(listener_.LastEventUrl().is_empty());
}
TEST_F(PhysicalWebDataSourceImplTest, OnDistanceChangedNotRegistered) {
@@ -153,6 +157,7 @@ TEST_F(PhysicalWebDataSourceImplTest, OnDistanceChangedNotRegistered) {
EXPECT_FALSE(listener_.OnFoundNotified());
EXPECT_FALSE(listener_.OnLostNotified());
EXPECT_FALSE(listener_.OnDistanceChangedNotified());
- EXPECT_TRUE(listener_.LastEventUrl().empty());
+ EXPECT_TRUE(listener_.LastEventUrl().is_empty());
}
+
} // namespace physical_web
diff --git a/chromium/components/physical_web/data_source/physical_web_listener.h b/chromium/components/physical_web/data_source/physical_web_listener.h
index eb338872f9a..3e74bc159e9 100644
--- a/chromium/components/physical_web/data_source/physical_web_listener.h
+++ b/chromium/components/physical_web/data_source/physical_web_listener.h
@@ -5,22 +5,25 @@
#ifndef COMPONENTS_PHYSICAL_WEB_DATA_SOURCE_PHYSICAL_WEB_LISTENER_H_
#define COMPONENTS_PHYSICAL_WEB_DATA_SOURCE_PHYSICAL_WEB_LISTENER_H_
-#include <string>
+class GURL;
+
+namespace physical_web {
// Class for being notified when Physical Web data changes.
class PhysicalWebListener {
public:
// OnFound(url) will be called when a new URL has been found.
- virtual void OnFound(const std::string& url) = 0;
+ virtual void OnFound(const GURL& url) = 0;
// OnLost(url) will be called when a URL can no longer be seen.
- virtual void OnLost(const std::string& url) = 0;
+ virtual void OnLost(const GURL& url) = 0;
// OnDistanceChagned(url, distance_estimate) will be called when the distance
// estimate is changed for the URL.
- virtual void OnDistanceChanged(const std::string& url,
- double distance_estimate) = 0;
+ virtual void OnDistanceChanged(const GURL& url, double distance_estimate) = 0;
};
+} // namespace physical_web
+
#endif // COMPONENTS_PHYSICAL_WEB_DATA_SOURCE_PHYSICAL_WEB_LISTENER_H_
diff --git a/chromium/components/physical_web/webui/BUILD.gn b/chromium/components/physical_web/webui/BUILD.gn
index f3d6432dd2b..a2fc20a4cae 100644
--- a/chromium/components/physical_web/webui/BUILD.gn
+++ b/chromium/components/physical_web/webui/BUILD.gn
@@ -4,11 +4,14 @@
source_set("webui") {
sources = [
+ "physical_web_base_message_handler.cc",
+ "physical_web_base_message_handler.h",
"physical_web_ui_constants.cc",
"physical_web_ui_constants.h",
]
deps = [
"//base",
+ "//components/physical_web/data_source",
]
}
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
new file mode 100644
index 00000000000..fd7d4b61699
--- /dev/null
+++ b/chromium/components/physical_web/webui/physical_web_base_message_handler.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/physical_web/webui/physical_web_base_message_handler.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "components/physical_web/data_source/physical_web_data_source.h"
+#include "components/physical_web/webui/physical_web_ui_constants.h"
+
+namespace physical_web_ui {
+
+PhysicalWebBaseMessageHandler::PhysicalWebBaseMessageHandler() {}
+
+PhysicalWebBaseMessageHandler::~PhysicalWebBaseMessageHandler() = default;
+
+void PhysicalWebBaseMessageHandler::RegisterMessages() {
+ RegisterMessageCallback(
+ kRequestNearbyUrls,
+ base::BindRepeating(
+ &PhysicalWebBaseMessageHandler::HandleRequestNearbyURLs,
+ base::Unretained(this)));
+ RegisterMessageCallback(kPhysicalWebItemClicked,
+ base::BindRepeating(
+ &PhysicalWebBaseMessageHandler::HandlePhysicalWebItemClicked,
+ base::Unretained(this)));
+}
+
+void PhysicalWebBaseMessageHandler::HandleRequestNearbyURLs(
+ const base::ListValue* args) {
+ base::DictionaryValue results;
+
+ std::unique_ptr<physical_web::MetadataList> metadata_list =
+ GetPhysicalWebDataSource()->GetMetadataList();
+
+ auto metadata = base::MakeUnique<base::ListValue>();
+ int index = 0;
+ for (const auto& metadata_list_item : *metadata_list) {
+ auto metadata_item = base::MakeUnique<base::DictionaryValue>();
+ metadata_item->SetString(physical_web_ui::kResolvedUrl,
+ metadata_list_item.resolved_url.spec());
+ metadata_item->SetString(physical_web_ui::kPageInfoIcon,
+ metadata_list_item.icon_url.spec());
+ metadata_item->SetString(physical_web_ui::kPageInfoTitle,
+ metadata_list_item.title);
+ metadata_item->SetString(physical_web_ui::kPageInfoDescription,
+ metadata_list_item.description);
+ // Add the item index so when an item is selected, the index can be recorded
+ // in a UMA histogram.
+ metadata_item->SetInteger(physical_web_ui::kIndex, index);
+ metadata->Append(std::move(metadata_item));
+ ++index;
+ }
+
+ results.Set(physical_web_ui::kMetadata, metadata.release());
+
+ // Pass the list of Physical Web URL metadata to the WebUI. A jstemplate will
+ // create a list view with an item for each URL.
+ CallJavaScriptFunction(physical_web_ui::kReturnNearbyUrls, results);
+}
+
+void PhysicalWebBaseMessageHandler::HandlePhysicalWebItemClicked(
+ const base::ListValue* args) {
+ int index = 0;
+ if (!args->GetInteger(0, &index)) {
+ DLOG(ERROR) << "Invalid selection index";
+ return;
+ }
+
+ // Record the index of the selected item.
+ UMA_HISTOGRAM_EXACT_LINEAR("PhysicalWeb.WebUI.ListViewUrlPosition", index,
+ 50);
+
+ // Count the number of selections.
+ base::RecordAction(
+ base::UserMetricsAction("PhysicalWeb.WebUI.ListViewUrlSelected"));
+}
+
+} // namespace physical_web_ui
diff --git a/chromium/components/physical_web/webui/physical_web_base_message_handler.h b/chromium/components/physical_web/webui/physical_web_base_message_handler.h
new file mode 100644
index 00000000000..190cf294b11
--- /dev/null
+++ b/chromium/components/physical_web/webui/physical_web_base_message_handler.h
@@ -0,0 +1,58 @@
+// 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_PHYSICAL_WEB_WEBUI_PHYSICAL_WEB_BASE_MESSAGE_HANDLER_H_
+#define COMPONENTS_PHYSICAL_WEB_WEBUI_PHYSICAL_WEB_BASE_MESSAGE_HANDLER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/values.h"
+
+namespace physical_web {
+
+class PhysicalWebDataSource;
+
+} // namespace physical_web
+
+namespace physical_web_ui {
+
+// This is the equivalent of content::WebUI::MessageCallback.
+typedef base::Callback<void(const base::ListValue*)> MessageCallback;
+
+// The base handler for Javascript messages for the chrome://physical-web page.
+// This does not implement WebUIMessageHandler or register its methods.
+class PhysicalWebBaseMessageHandler {
+ public:
+ PhysicalWebBaseMessageHandler();
+ virtual ~PhysicalWebBaseMessageHandler();
+
+ // Handles the RequestNearbyURLs message, returning URLs that are currently
+ // being broadcasted by Physical Web transports.
+ void HandleRequestNearbyURLs(const base::ListValue* args);
+
+ // Handles a click on a Physical Web URL, recording the click and
+ // directing the user appropriately.
+ void HandlePhysicalWebItemClicked(const base::ListValue* args);
+
+ // Registers the messages that this MessageHandler can handle.
+ void RegisterMessages();
+
+ protected:
+ // Subclasses should implement these protected methods as a pass through to
+ // the similarly named methods of the appropriate WebUI object (the exact
+ // WebUI class differs per platform).
+ virtual void RegisterMessageCallback(
+ const std::string& message,
+ const MessageCallback& callback) = 0;
+ virtual void CallJavaScriptFunction(const std::string& function,
+ const base::Value& arg) = 0;
+ virtual physical_web::PhysicalWebDataSource* GetPhysicalWebDataSource() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PhysicalWebBaseMessageHandler);
+};
+
+} // namespace physical_web_ui
+
+#endif // COMPONENTS_PHYSICAL_WEB_WEBUI_PHYSICAL_WEB_BASE_MESSAGE_HANDLER_H_
diff --git a/chromium/components/physical_web/webui/physical_web_ui_constants.cc b/chromium/components/physical_web/webui/physical_web_ui_constants.cc
index 47415b72e7a..82d1af39dab 100644
--- a/chromium/components/physical_web/webui/physical_web_ui_constants.cc
+++ b/chromium/components/physical_web/webui/physical_web_ui_constants.cc
@@ -16,12 +16,10 @@ const char kPhysicalWebItemClicked[] = "physicalWebItemClicked";
// Strings.
const char kTitle[] = "title";
-const char kPageInfo[] = "pageInfo";
const char kPageInfoDescription[] = "pageInfoDescription";
const char kPageInfoIcon[] = "pageInfoIcon";
const char kPageInfoTitle[] = "pageInfoTitle";
const char kResolvedUrl[] = "resolvedUrl";
-const char kScannedUrl[] = "scannedUrl";
const char kIndex[] = "index";
const char kReturnNearbyUrls[] = "returnNearbyURLs";
const char kMetadata[] = "metadata";
diff --git a/chromium/components/physical_web/webui/physical_web_ui_constants.h b/chromium/components/physical_web/webui/physical_web_ui_constants.h
index b63d7d44362..2eb85231496 100644
--- a/chromium/components/physical_web/webui/physical_web_ui_constants.h
+++ b/chromium/components/physical_web/webui/physical_web_ui_constants.h
@@ -20,12 +20,10 @@ extern const char kPhysicalWebItemClicked[];
// Strings.
// Must match the constants used in the resource files.
extern const char kTitle[];
-extern const char kPageInfo[];
extern const char kPageInfoDescription[];
extern const char kPageInfoIcon[];
extern const char kPageInfoTitle[];
extern const char kResolvedUrl[];
-extern const char kScannedUrl[];
extern const char kIndex[];
extern const char kReturnNearbyUrls[];
extern const char kMetadata[];
diff --git a/chromium/components/physical_web/webui/resources/physical_web.css b/chromium/components/physical_web/webui/resources/physical_web.css
index 94a671e7014..29298209538 100644
--- a/chromium/components/physical_web/webui/resources/physical_web.css
+++ b/chromium/components/physical_web/webui/resources/physical_web.css
@@ -44,6 +44,7 @@ body {
.physicalWebText .description {
display: block;
height: 2.5em;
+ line-height: 1.25;
font-size: 90%;
color: #444;
overflow: hidden;
diff --git a/chromium/components/physical_web/webui/resources/physical_web.html b/chromium/components/physical_web/webui/resources/physical_web.html
index 434a6b89b96..a5a0ba69884 100644
--- a/chromium/components/physical_web/webui/resources/physical_web.html
+++ b/chromium/components/physical_web/webui/resources/physical_web.html
@@ -27,12 +27,12 @@
<a class="physicalWebTemplate" id="physicalWebTemplate" jsselect="metadata"
jsvalues="href:resolvedUrl;onclick:'physicalWebItemClicked(' + index + ')'">
<div class="physicalWebIcon">
- <img jsvalues="src:icon" />
+ <img jsvalues="src:pageInfoIcon" />
</div>
<div class="physicalWebText">
- <div class="title" jscontent="title"></div>
+ <div class="title" jscontent="pageInfoTitle"></div>
<div class="resolvedUrl" jscontent="resolvedUrl"></div>
- <div class="description" jscontent="description"></div>
+ <div class="description" jscontent="pageInfoDescription"></div>
</div>
</a>
diff --git a/chromium/components/plugins/renderer/BUILD.gn b/chromium/components/plugins/renderer/BUILD.gn
index 6c0081f8c44..8da45b620a9 100644
--- a/chromium/components/plugins/renderer/BUILD.gn
+++ b/chromium/components/plugins/renderer/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/config/features.gni")
+import("//ppapi/features/features.gni")
static_library("renderer") {
sources = [
diff --git a/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc b/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc
index 1f38fdbf87d..3496764e1d5 100644
--- a/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc
+++ b/chromium/components/plugins/renderer/loadable_plugin_placeholder.cc
@@ -129,9 +129,8 @@ void LoadablePluginPlaceholder::ReplacePlugin(blink::WebPlugin* new_plugin) {
container->invalidate();
container->reportGeometry();
- if (plugin()->focused())
- new_plugin->updateFocus(true, blink::WebFocusTypeNone);
container->element().setAttribute("title", plugin()->old_title());
+ plugin()->ReplayReceivedData(new_plugin);
plugin()->destroy();
}
@@ -210,8 +209,7 @@ void LoadablePluginPlaceholder::OnUnobscuredRectUpdate(
: RenderFrame::RECORD_DECISION);
bool plugin_is_tiny_and_blocked =
- is_blocked_for_tinyness_ &&
- status == RenderFrame::CONTENT_STATUS_ESSENTIAL_CROSS_ORIGIN_TINY;
+ is_blocked_for_tinyness_ && status == RenderFrame::CONTENT_STATUS_TINY;
// Early exit for plugins that we've discovered to be essential.
if (!plugin_is_tiny_and_blocked &&
diff --git a/chromium/components/plugins/renderer/webview_plugin.cc b/chromium/components/plugins/renderer/webview_plugin.cc
index 8d45c4201ae..0141ce4f6a5 100644
--- a/chromium/components/plugins/renderer/webview_plugin.cc
+++ b/chromium/components/plugins/renderer/webview_plugin.cc
@@ -53,21 +53,12 @@ WebViewPlugin::WebViewPlugin(content::RenderView* render_view,
: content::RenderViewObserver(render_view),
delegate_(delegate),
container_(nullptr),
- web_view_(WebView::create(this, blink::WebPageVisibilityStateVisible)),
+ finished_loading_(false),
focused_(false),
is_painting_(false),
is_resizing_(false),
- web_frame_client_(this),
+ web_view_helper_(this, preferences),
weak_factory_(this) {
- // 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::Document, &web_frame_client_);
- 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);
}
// static
@@ -84,7 +75,26 @@ WebViewPlugin* WebViewPlugin::Create(content::RenderView* render_view,
WebViewPlugin::~WebViewPlugin() {
DCHECK(!weak_factory_.HasWeakPtrs());
- web_view_->close();
+}
+
+void WebViewPlugin::ReplayReceivedData(WebPlugin* plugin) {
+ if (!response_.isNull()) {
+ plugin->didReceiveResponse(response_);
+ size_t total_bytes = 0;
+ for (std::list<std::string>::iterator it = data_.begin(); it != data_.end();
+ ++it) {
+ plugin->didReceiveData(
+ it->c_str(), base::checked_cast<int>(it->length()));
+ total_bytes += it->length();
+ }
+ }
+ // We need to transfer the |focused_| to new plugin after it loaded.
+ if (focused_)
+ plugin->updateFocus(true, blink::WebFocusTypeNone);
+ if (finished_loading_)
+ plugin->didFinishLoading();
+ if (error_)
+ plugin->didFailLoading(*error_);
}
WebPluginContainer* WebViewPlugin::container() const { return container_; }
@@ -106,8 +116,8 @@ bool WebViewPlugin::initialize(WebPluginContainer* container) {
old_title_ = container_->element().getAttribute("title");
// Propagate device scale and zoom level to inner webview.
- web_view_->setDeviceScaleFactor(container_->deviceScaleFactor());
- web_view_->setZoomLevel(
+ web_view()->setDeviceScaleFactor(container_->deviceScaleFactor());
+ web_view()->setZoomLevel(
blink::WebView::zoomFactorToZoomLevel(container_->pageZoomFactor()));
return true;
@@ -133,7 +143,7 @@ v8::Local<v8::Object> WebViewPlugin::v8ScriptableObject(v8::Isolate* isolate) {
}
void WebViewPlugin::updateAllLifecyclePhases() {
- web_view_->updateAllLifecyclePhases();
+ web_view()->updateAllLifecyclePhases();
}
void WebViewPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
@@ -155,7 +165,7 @@ void WebViewPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
SkFloatToScalar(1.0 / container_->deviceScaleFactor());
canvas->scale(inverse_scale, inverse_scale);
- web_view_->paint(canvas, paint_rect);
+ web_view()->paint(canvas, paint_rect);
canvas->restore();
}
@@ -172,7 +182,7 @@ void WebViewPlugin::updateGeometry(const WebRect& window_rect,
if (static_cast<gfx::Rect>(window_rect) != rect_) {
rect_ = window_rect;
- web_view_->resize(rect_.size());
+ web_view()->resize(rect_.size());
}
// Plugin updates are forbidden during Blink layout. Therefore,
@@ -192,15 +202,15 @@ blink::WebInputEventResult WebViewPlugin::handleInputEvent(
WebCursorInfo& cursor) {
// For tap events, don't handle them. They will be converted to
// mouse events later and passed to here.
- if (event.type == WebInputEvent::GestureTap)
+ if (event.type() == WebInputEvent::GestureTap)
return blink::WebInputEventResult::NotHandled;
// For LongPress events we return false, since otherwise the context menu will
// be suppressed. https://crbug.com/482842
- if (event.type == WebInputEvent::GestureLongPress)
+ if (event.type() == WebInputEvent::GestureLongPress)
return blink::WebInputEventResult::NotHandled;
- if (event.type == WebInputEvent::ContextMenu) {
+ if (event.type() == WebInputEvent::ContextMenu) {
if (delegate_) {
const WebMouseEvent& mouse_event =
reinterpret_cast<const WebMouseEvent&>(event);
@@ -209,77 +219,102 @@ blink::WebInputEventResult WebViewPlugin::handleInputEvent(
return blink::WebInputEventResult::HandledSuppressed;
}
current_cursor_ = cursor;
- blink::WebInputEventResult handled = web_view_->handleInputEvent(event);
+ blink::WebInputEventResult handled = web_view()->handleInputEvent(event);
cursor = current_cursor_;
return handled;
}
void WebViewPlugin::didReceiveResponse(const WebURLResponse& response) {
- NOTREACHED();
+ DCHECK(response_.isNull());
+ response_ = response;
}
void WebViewPlugin::didReceiveData(const char* data, int data_length) {
- NOTREACHED();
+ data_.push_back(std::string(data, data_length));
}
void WebViewPlugin::didFinishLoading() {
- NOTREACHED();
+ DCHECK(!finished_loading_);
+ finished_loading_ = true;
}
void WebViewPlugin::didFailLoading(const WebURLError& error) {
- NOTREACHED();
+ DCHECK(!error_.get());
+ error_.reset(new WebURLError(error));
}
-bool WebViewPlugin::acceptsLoadDrops() { return false; }
+WebViewPlugin::WebViewHelper::WebViewHelper(
+ WebViewPlugin* plugin,
+ const WebPreferences& preferences) : plugin_(plugin) {
+ web_view_ =
+ WebView::create(this, blink::WebPageVisibilityStateVisible);
+ // 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::Document, this);
+ 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);
+}
-void WebViewPlugin::setToolTipText(const WebString& text,
- blink::WebTextDirection hint) {
- if (container_)
- container_->element().setAttribute("title", text);
+WebViewPlugin::WebViewHelper::~WebViewHelper() {
+ web_view_->close();
+}
+
+bool WebViewPlugin::WebViewHelper::acceptsLoadDrops() { return false; }
+
+void WebViewPlugin::WebViewHelper::setToolTipText(
+ const WebString& text,
+ blink::WebTextDirection hint) {
+ if (plugin_->container_)
+ plugin_->container_->element().setAttribute("title", text);
}
-void WebViewPlugin::startDragging(blink::WebReferrerPolicy,
- const WebDragData&,
- WebDragOperationsMask,
- const WebImage&,
- const WebPoint&) {
+void WebViewPlugin::WebViewHelper::startDragging(blink::WebReferrerPolicy,
+ const WebDragData&,
+ WebDragOperationsMask,
+ const WebImage&,
+ const WebPoint&) {
// Immediately stop dragging.
DCHECK(web_view_->mainFrame()->isWebLocalFrame());
web_view_->mainFrame()->toWebLocalFrame()->frameWidget()->
dragSourceSystemDragEnded();
}
-bool WebViewPlugin::allowsBrokenNullLayerTreeView() const {
+bool WebViewPlugin::WebViewHelper::allowsBrokenNullLayerTreeView() const {
return true;
}
-void WebViewPlugin::didInvalidateRect(const WebRect& rect) {
- if (container_)
- container_->invalidateRect(rect);
+void WebViewPlugin::WebViewHelper::didInvalidateRect(const WebRect& rect) {
+ if (plugin_->container_)
+ plugin_->container_->invalidateRect(rect);
}
-void WebViewPlugin::didChangeCursor(const WebCursorInfo& cursor) {
- current_cursor_ = cursor;
+void WebViewPlugin::WebViewHelper::didChangeCursor(
+ const WebCursorInfo& cursor) {
+ plugin_->current_cursor_ = cursor;
}
-void WebViewPlugin::scheduleAnimation() {
+void WebViewPlugin::WebViewHelper::scheduleAnimation() {
// Resizes must be self-contained: any lifecycle updating must
// be triggerd from within the WebView or this WebViewPlugin.
// This is because this WebViewPlugin is contained in another
// Web View which may be in the middle of updating its lifecycle,
// but after layout is done, and it is illegal to dirty earlier
// lifecycle stages during later ones.
- if (is_resizing_)
+ if (plugin_->is_resizing_)
return;
- if (container_) {
+ if (plugin_->container_) {
// This should never happen; see also crbug.com/545039 for context.
- CHECK(!is_painting_);
- container_->scheduleAnimation();
+ DCHECK(!plugin_->is_painting_);
+ plugin_->container_->scheduleAnimation();
}
}
-void WebViewPlugin::PluginWebFrameClient::didClearWindowObject(
+void WebViewPlugin::WebViewHelper::didClearWindowObject(
WebLocalFrame* frame) {
if (!plugin_->delegate_)
return;
@@ -296,11 +331,9 @@ void WebViewPlugin::PluginWebFrameClient::didClearWindowObject(
plugin_->delegate_->GetV8Handle(isolate));
}
-void WebViewPlugin::OnDestruct() {}
-
void WebViewPlugin::OnZoomLevelChanged() {
if (container_) {
- web_view_->setZoomLevel(
+ web_view()->setZoomLevel(
blink::WebView::zoomFactorToZoomLevel(container_->pageZoomFactor()));
}
}
@@ -317,5 +350,5 @@ void WebViewPlugin::UpdatePluginForNewGeometry(
// The delegate may have dirtied style and layout of the WebView.
// See for example the resizePoster function in plugin_poster.html.
// Run the lifecycle now so that it is clean.
- web_view_->updateAllLifecyclePhases();
+ web_view()->updateAllLifecyclePhases();
}
diff --git a/chromium/components/plugins/renderer/webview_plugin.h b/chromium/components/plugins/renderer/webview_plugin.h
index fdb1cf9cbbe..7f4ad1045a8 100644
--- a/chromium/components/plugins/renderer/webview_plugin.h
+++ b/chromium/components/plugins/renderer/webview_plugin.h
@@ -20,7 +20,6 @@
#include "third_party/WebKit/public/web/WebViewClient.h"
namespace blink {
-class WebFrameWidget;
class WebMouseEvent;
}
@@ -29,10 +28,6 @@ class RenderView;
struct WebPreferences;
}
-namespace gfx {
-class Size;
-}
-
// This class implements the WebPlugin interface by forwarding drawing and
// handling input events to a WebView.
// It can be used as a placeholder for an actual plugin, using HTML for the UI.
@@ -41,7 +36,6 @@ class Size;
// chrome:// URL as origin.
class WebViewPlugin : public blink::WebPlugin,
- public blink::WebViewClient,
public content::RenderViewObserver {
public:
class Delegate {
@@ -72,11 +66,15 @@ class WebViewPlugin : public blink::WebPlugin,
const std::string& html_data,
const GURL& url);
- blink::WebView* web_view() { return web_view_; }
+ blink::WebView* web_view() { return web_view_helper_.web_view(); }
- bool focused() const { return focused_; }
const blink::WebString& old_title() const { return old_title_; }
+ // When loading a plugin document (i.e. a full page plugin not embedded in
+ // another page), we save all data that has been received, and replay it with
+ // this method on the actual plugin.
+ void ReplayReceivedData(blink::WebPlugin* plugin);
+
// WebPlugin methods:
blink::WebPluginContainer* container() const override;
// The WebViewPlugin, by design, never fails to initialize. It's used to
@@ -108,27 +106,6 @@ class WebViewPlugin : public blink::WebPlugin,
void didFinishLoading() override;
void didFailLoading(const blink::WebURLError& error) override;
- // WebViewClient methods:
- bool acceptsLoadDrops() override;
-
- void setToolTipText(const blink::WebString&,
- blink::WebTextDirection) override;
-
- void startDragging(blink::WebReferrerPolicy,
- const blink::WebDragData&,
- blink::WebDragOperationsMask,
- const blink::WebImage&,
- const blink::WebPoint&) override;
-
- // TODO(ojan): Remove this override and have this class use a non-null
- // layerTreeView.
- bool allowsBrokenNullLayerTreeView() const override;
-
- // WebWidgetClient methods:
- void didInvalidateRect(const blink::WebRect&) override;
- void didChangeCursor(const blink::WebCursorInfo& cursor) override;
- void scheduleAnimation() override;
-
private:
friend class base::DeleteHelper<WebViewPlugin>;
WebViewPlugin(content::RenderView* render_view,
@@ -137,7 +114,7 @@ class WebViewPlugin : public blink::WebPlugin,
~WebViewPlugin() override;
// content::RenderViewObserver methods:
- void OnDestruct() override;
+ void OnDestruct() override {}
void OnZoomLevelChanged() override;
void UpdatePluginForNewGeometry(const blink::WebRect& window_rect,
@@ -151,27 +128,55 @@ class WebViewPlugin : public blink::WebPlugin,
// Owns us.
blink::WebPluginContainer* container_;
- // Owned by us, deleted via |close()|.
- blink::WebView* web_view_;
-
gfx::Rect rect_;
+ blink::WebURLResponse response_;
+ std::list<std::string> data_;
+ std::unique_ptr<blink::WebURLError> error_;
blink::WebString old_title_;
+ bool finished_loading_;
bool focused_;
bool is_painting_;
bool is_resizing_;
- // A helper needed to create a WebLocalFrame.
- class PluginWebFrameClient : public blink::WebFrameClient {
+ // A helper that handles interaction from WebViewPlugin's internal WebView.
+ class WebViewHelper : public blink::WebViewClient,
+ public blink::WebFrameClient {
public:
- PluginWebFrameClient(WebViewPlugin* plugin) : plugin_(plugin) {}
- ~PluginWebFrameClient() override {}
+ WebViewHelper(WebViewPlugin* plugin,
+ const content::WebPreferences& preferences);
+ ~WebViewHelper() override;
+
+ blink::WebView* web_view() { return web_view_; }
+
+ // WebViewClient methods:
+ bool acceptsLoadDrops() override;
+
+ // WebWidgetClient methods:
+ void setToolTipText(const blink::WebString&,
+ blink::WebTextDirection) override;
+ void startDragging(blink::WebReferrerPolicy,
+ const blink::WebDragData&,
+ blink::WebDragOperationsMask,
+ const blink::WebImage&,
+ const blink::WebPoint&) override;
+ // TODO(ojan): Remove this override and have this class use a non-null
+ // layerTreeView.
+ bool allowsBrokenNullLayerTreeView() const override;
+ void didInvalidateRect(const blink::WebRect&) override;
+ void didChangeCursor(const blink::WebCursorInfo& cursor) override;
+ void scheduleAnimation() override;
+
+ // WebFrameClient methods:
void didClearWindowObject(blink::WebLocalFrame* frame) override;
private:
WebViewPlugin* plugin_;
+
+ // Owned by us, deleted via |close()|.
+ blink::WebView* web_view_;
};
- PluginWebFrameClient web_frame_client_;
+ WebViewHelper web_view_helper_;
// Should be invalidated when destroy() is called.
base::WeakPtrFactory<WebViewPlugin> weak_factory_;
diff --git a/chromium/components/policy/BUILD.gn b/chromium/components/policy/BUILD.gn
index 987142b4dbf..bf7ceca0df5 100644
--- a/chromium/components/policy/BUILD.gn
+++ b/chromium/components/policy/BUILD.gn
@@ -9,6 +9,8 @@ import("//components/policy/resources/policy_templates.gni")
import("//third_party/protobuf/proto_library.gni")
import("//tools/grit/grit_rule.gni")
+assert(!is_ios, "Policy should not be referenced on iOS")
+
# To test policy generation for platforms different than your OS, override and
# enable these flags (but don't check that in!).
gen_policy_templates_common = true
@@ -44,307 +46,348 @@ config("component_implementation") {
defines = [ "POLICY_COMPONENT_IMPLEMENTATION" ]
}
-if (enable_configuration_policy) {
- # This protobuf is equivalent to chrome_settings.proto but shares messages
- # for policies of the same type, so that less classes have to be generated
- # and compiled.
- cloud_policy_proto_path = "$target_gen_dir/proto/cloud_policy.proto"
-
- # This is the "full" protobuf, which defines one protobuf message per
- # policy. It is also the format currently used by the server.
- chrome_settings_proto_path = "$target_gen_dir/proto/chrome_settings.proto"
-
- constants_header_path = "$target_gen_dir/policy_constants.h"
- constants_source_path = "$target_gen_dir/policy_constants.cc"
- protobuf_decoder_path = "$target_gen_dir/cloud_policy_generated.cc"
- app_restrictions_path = "$target_gen_dir/app_restrictions.xml"
- risk_tag_header_path = "$target_gen_dir/risk_tag.h"
-
- action("cloud_policy_code_generate") {
- script = "tools/generate_policy_source.py"
- chrome_version_abspath = "//chrome/VERSION"
- chrome_version_path = rebase_path(chrome_version_abspath, root_build_dir)
-
- if (is_chromeos) {
- chromeos_flag = "1"
- } else {
- chromeos_flag = "0"
- }
+# This protobuf is equivalent to chrome_settings.proto but shares messages
+# for policies of the same type, so that less classes have to be generated
+# and compiled.
+cloud_policy_proto_path = "$target_gen_dir/proto/cloud_policy.proto"
+
+# This is the "full" protobuf, which defines one protobuf message per
+# policy. It is also the format currently used by the server.
+chrome_settings_proto_path = "$target_gen_dir/proto/chrome_settings.proto"
+
+constants_header_path = "$target_gen_dir/policy_constants.h"
+constants_source_path = "$target_gen_dir/policy_constants.cc"
+protobuf_decoder_path = "$target_gen_dir/cloud_policy_generated.cc"
+app_restrictions_path = "$target_gen_dir/app_restrictions.xml"
+risk_tag_header_path = "$target_gen_dir/risk_tag.h"
+
+action("cloud_policy_code_generate") {
+ script = "tools/generate_policy_source.py"
+ chrome_version_abspath = "//chrome/VERSION"
+ chrome_version_path = rebase_path(chrome_version_abspath, root_build_dir)
+
+ if (is_chromeos) {
+ chromeos_flag = "1"
+ } else {
+ chromeos_flag = "0"
+ }
- inputs = [
- chrome_version_abspath,
- "resources/policy_templates.json",
- ]
- outputs = [
- constants_header_path,
- constants_source_path,
- protobuf_decoder_path,
- chrome_settings_proto_path,
- cloud_policy_proto_path,
- app_restrictions_path,
- risk_tag_header_path,
- ]
+ inputs = [
+ chrome_version_abspath,
+ "resources/policy_templates.json",
+ ]
+ outputs = [
+ constants_header_path,
+ constants_source_path,
+ protobuf_decoder_path,
+ chrome_settings_proto_path,
+ cloud_policy_proto_path,
+ app_restrictions_path,
+ risk_tag_header_path,
+ ]
+
+ if (target_os != "android") {
+ outputs -= [ app_restrictions_path ]
+ }
- if (target_os != "android") {
- outputs -= [ app_restrictions_path ]
- }
+ args = [
+ "--policy-constants-header=" +
+ rebase_path(constants_header_path, root_build_dir),
+ "--policy-constants-source=" +
+ rebase_path(constants_source_path, root_build_dir),
+ "--chrome-settings-protobuf=" +
+ rebase_path(chrome_settings_proto_path, root_build_dir),
+ "--cloud-policy-protobuf=" +
+ rebase_path(cloud_policy_proto_path, root_build_dir),
+ "--cloud-policy-decoder=" +
+ rebase_path(protobuf_decoder_path, root_build_dir),
+ "--app-restrictions-definition=" +
+ rebase_path(app_restrictions_path, root_build_dir),
+ "--risk-tag-header=" + rebase_path(risk_tag_header_path, root_build_dir),
+ chrome_version_path,
+ target_os,
+ chromeos_flag,
+ rebase_path("resources/policy_templates.json", root_build_dir),
+ ]
+}
- args = [
- "--policy-constants-header=" +
- rebase_path(constants_header_path, root_build_dir),
- "--policy-constants-source=" +
- rebase_path(constants_source_path, root_build_dir),
- "--chrome-settings-protobuf=" +
- rebase_path(chrome_settings_proto_path, root_build_dir),
- "--cloud-policy-protobuf=" +
- rebase_path(cloud_policy_proto_path, root_build_dir),
- "--cloud-policy-decoder=" +
- rebase_path(protobuf_decoder_path, root_build_dir),
- "--app-restrictions-definition=" +
- rebase_path(app_restrictions_path, root_build_dir),
- "--risk-tag-header=" + rebase_path(risk_tag_header_path, root_build_dir),
- chrome_version_path,
- target_os,
- chromeos_flag,
- rebase_path("resources/policy_templates.json", root_build_dir),
+policy_templates_grd_file = "resources/policy_templates.grd"
+
+grit("grit_policy_templates") {
+ source = policy_templates_grd_file
+ use_qualified_include = true
+ output_dir = "$root_gen_dir/chrome"
+ outputs = []
+ defines = []
+
+ if (gen_policy_templates_common) {
+ outputs += policy_templates_doc_outputs
+ defines += [ "gen_policy_templates_common" ]
+ }
+ if (gen_policy_templates_android) {
+ outputs += policy_templates_android_outputs
+ defines += [ "gen_policy_templates_android" ]
+ }
+ if (gen_policy_templates_linux) {
+ outputs += policy_templates_linux_outputs
+ defines += [ "gen_policy_templates_linux" ]
+ }
+ if (gen_policy_templates_mac) {
+ outputs += policy_templates_mac_outputs
+ defines += [
+ "mac_bundle_id=$chrome_mac_bundle_id",
+ "gen_policy_templates_mac",
]
}
-
- policy_templates_grd_file = "resources/policy_templates.grd"
- grit("policy_templates") {
- source = policy_templates_grd_file
- use_qualified_include = true
- output_dir = "$root_gen_dir/chrome"
- outputs = []
- defines = []
-
- if (gen_policy_templates_common) {
- outputs += policy_templates_doc_outputs
- defines += [ "gen_policy_templates_common" ]
- }
- if (gen_policy_templates_android) {
- outputs += policy_templates_android_outputs
- defines += [ "gen_policy_templates_android" ]
- }
- if (gen_policy_templates_linux) {
- outputs += policy_templates_linux_outputs
- defines += [ "gen_policy_templates_linux" ]
- }
- if (gen_policy_templates_mac) {
- outputs += policy_templates_mac_outputs
- defines += [
- "mac_bundle_id=$chrome_mac_bundle_id",
- "gen_policy_templates_mac",
- ]
- }
- if (gen_policy_templates_win) {
- outputs += policy_templates_windows_outputs
- defines += [ "gen_policy_templates_win" ]
- }
+ if (gen_policy_templates_win) {
+ outputs += policy_templates_windows_outputs
+ defines += [ "gen_policy_templates_win" ]
}
+}
- # Run the proto compiler over the generated file and make it a component.
- component("cloud_policy_proto_generated_compile") {
- public_deps = [
- ":cloud_policy_proto_generated_compile_proto",
+if (gen_policy_templates_win && is_chrome_branded) {
+ # Creates google.admx and google.adml files that define a common 'Google'
+ # category used for Chrome, Chrome OS and possibly external tools, see
+ # crbug.com/665400.
+ action("create_google_admx") {
+ script = "tools/create_google_admx.py"
+ inputs = [
+ policy_templates_grd_file,
+ grit_info_script,
+ ] + policy_templates_windows_outputs
+ outputs = policy_templates_windows_google_outputs
+ deps = [
+ ":grit_policy_templates",
]
+
+ # Don't pass in outputs directly, it would exceed a limit on Windows!
+ args = [
+ "--basedir",
+ rebase_path(policy_templates_base_dir, root_build_dir),
+ "--grd_strip_path_prefix",
+ "app/policy/",
+ "--grd_input",
+ rebase_path(policy_templates_grd_file, root_build_dir),
+ "--grit_info",
+ rebase_path(grit_info_script, root_build_dir),
+ "-D",
+ "gen_policy_templates_win",
+ ] + grit_defines
}
- proto_library("cloud_policy_proto_generated_compile_proto") {
- visibility = [ ":cloud_policy_proto_generated_compile" ]
- sources = [
- cloud_policy_proto_path,
- ]
+}
+
+group("policy_templates") {
+ public_deps = [
+ ":grit_policy_templates",
+ ]
+ if (gen_policy_templates_win && is_chrome_branded) {
+ public_deps += [ ":create_google_admx" ]
+ }
+}
+
+# Run the proto compiler over the generated file and make it a component.
+component("cloud_policy_proto_generated_compile") {
+ public_deps = [
+ ":cloud_policy_proto_generated_compile_proto",
+ ]
+}
+proto_library("cloud_policy_proto_generated_compile_proto") {
+ visibility = [ ":cloud_policy_proto_generated_compile" ]
+ sources = [
+ cloud_policy_proto_path,
+ ]
+
+ proto_out_dir = "components/policy/proto"
+ cc_generator_options = "dllexport_decl=POLICY_PROTO_EXPORT:"
+ cc_include = "components/policy/proto/policy_proto_export.h"
+ component_build_force_source_set = true
+ defines = [ "POLICY_PROTO_COMPILATION" ]
+
+ deps = [
+ ":cloud_policy_code_generate",
+ ]
+}
+
+# This target builds the "full" protobuf, used for tests only.
+proto_library("chrome_settings_proto") {
+ testonly = true
+ sources = [
+ chrome_settings_proto_path,
+ ]
+ proto_out_dir = "components/policy/proto"
+
+ deps = [
+ ":cloud_policy_code_generate",
+ ":cloud_policy_proto_generated_compile",
+ ]
+}
+
+static_library("generated") {
+ sources = [
+ constants_header_path,
+ constants_source_path,
+ protobuf_decoder_path,
+ risk_tag_header_path,
+ ]
+
+ defines = [ "POLICY_COMPONENT_IMPLEMENTATION" ]
+ public_deps = [
+ ":cloud_policy_code_generate",
+ ":cloud_policy_proto_generated_compile",
+ "//base",
+ "//third_party/protobuf:protobuf_lite",
+ ]
+}
- proto_out_dir = "components/policy/proto"
- cc_generator_options = "dllexport_decl=POLICY_PROTO_EXPORT:"
- cc_include = "components/policy/proto/policy_proto_export.h"
- component_build_force_source_set = true
- defines = [ "POLICY_PROTO_COMPILATION" ]
+if (gen_policy_templates_android && is_android) {
+ import("//build/config/android/rules.gni")
+ _generated_resources_dir = "$root_gen_dir/chrome/app/policy/android"
+
+ copy("app_restrictions_resources_copy") {
+ sources = [
+ app_restrictions_path,
+ ]
+ outputs = [
+ "$_generated_resources_dir/xml-v21/app_restrictions.xml",
+ ]
deps = [
":cloud_policy_code_generate",
+ ":policy_templates",
]
}
- # This target builds the "full" protobuf, used for tests only.
- proto_library("chrome_settings_proto") {
- testonly = true
- sources = [
- chrome_settings_proto_path,
+ android_resources("app_restrictions_resources") {
+ resource_dirs = []
+ generated_resource_dirs = [
+ "$policy_templates_base_dir/android",
+ _generated_resources_dir,
]
- proto_out_dir = "components/policy/proto"
-
+ generated_resource_files =
+ policy_templates_android_outputs +
+ [ "$_generated_resources_dir/xml-v21/app_restrictions.xml" ]
deps = [
- ":cloud_policy_code_generate",
- ":cloud_policy_proto_generated_compile",
+ ":app_restrictions_resources_copy",
+ ":grit_policy_templates",
]
}
-
- static_library("generated") {
+} else if (gen_policy_templates_mac && is_mac) {
+ action("convert_mcx_plist") {
+ script = "//build/config/mac/xcrun.py"
sources = [
- constants_header_path,
- constants_source_path,
- protobuf_decoder_path,
- risk_tag_header_path,
+ "$policy_templates_base_dir/mac/app-Manifest.plist",
]
-
- defines = [ "POLICY_COMPONENT_IMPLEMENTATION" ]
- public_deps = [
- ":cloud_policy_code_generate",
- ":cloud_policy_proto_generated_compile",
- "//base",
- "//third_party/protobuf:protobuf_lite",
+ inputs = [
+ script,
+ ]
+ outputs = [
+ "$target_gen_dir/$chrome_mac_bundle_id.manifest",
]
- }
-
- if (gen_policy_templates_android && is_android) {
- import("//build/config/android/rules.gni")
-
- _generated_resources_dir = "$root_gen_dir/chrome/app/policy/android"
-
- copy("app_restrictions_resources_copy") {
- sources = [
- app_restrictions_path,
- ]
- outputs = [
- "$_generated_resources_dir/xml-v21/app_restrictions.xml",
- ]
- deps = [
- ":cloud_policy_code_generate",
- ":policy_templates",
- ]
- }
- android_resources("app_restrictions_resources") {
- resource_dirs = []
- generated_resource_dirs = [
- "$policy_templates_base_dir/android",
- _generated_resources_dir,
- ]
- generated_resource_files =
- policy_templates_android_outputs +
- [ "$_generated_resources_dir/xml-v21/app_restrictions.xml" ]
- deps = [
- ":app_restrictions_resources_copy",
- ":policy_templates_grit",
+ if (use_system_xcode) {
+ args = []
+ } else {
+ args = [
+ "--developer_dir",
+ hermetic_xcode_path,
]
}
- } else if (gen_policy_templates_mac && is_mac) {
- action("convert_mcx_plist") {
- script = "//build/config/mac/xcrun.py"
- sources = [
- "$policy_templates_base_dir/mac/app-Manifest.plist",
- ]
- inputs = [
- script,
- ]
- outputs = [
- "$target_gen_dir/$chrome_mac_bundle_id.manifest",
- ]
+ args += [
+ "plutil",
+ "-convert",
+ "xml1",
+ ] + rebase_path(sources, root_out_dir) + [ "-o" ] +
+ rebase_path(outputs, root_out_dir)
- if (use_system_xcode) {
- args = []
- } else {
- args = [
- "--developer_dir",
- hermetic_xcode_path,
- ]
- }
- args += [
- "plutil",
- "-convert",
- "xml1",
- ] + rebase_path(sources, root_out_dir) + [ "-o" ] +
- rebase_path(outputs, root_out_dir)
-
- deps = [
- ":policy_templates",
- ]
- }
+ deps = [
+ ":policy_templates",
+ ]
+ }
- bundle_data("manifest_bundle_data") {
- sources = get_target_outputs(":convert_mcx_plist")
- outputs = [
- "{{bundle_resources_dir}}/{{source_file_part}}",
- ]
- public_deps = [
- ":convert_mcx_plist",
- ]
- }
+ bundle_data("manifest_bundle_data") {
+ sources = get_target_outputs(":convert_mcx_plist")
+ outputs = [
+ "{{bundle_resources_dir}}/{{source_file_part}}",
+ ]
+ public_deps = [
+ ":convert_mcx_plist",
+ ]
+ }
- # The reason we are not enumerating all the locales is that
- # the translations would eat up 3.5MB disk space in the
- # application bundle.
- bundle_data("manifest_strings_bundle_data") {
- sources = [
- "$policy_templates_base_dir/mac/strings/en.lproj/Localizable.strings",
- ]
- outputs = [
- "{{bundle_resources_dir}}/en.lproj/{{source_file_part}}",
- ]
- public_deps = [
- ":policy_templates",
- ]
- }
+ # The reason we are not enumerating all the locales is that
+ # the translations would eat up 3.5MB disk space in the
+ # application bundle.
+ bundle_data("manifest_strings_bundle_data") {
+ sources = [
+ "$policy_templates_base_dir/mac/strings/en.lproj/Localizable.strings",
+ ]
+ outputs = [
+ "{{bundle_resources_dir}}/en.lproj/{{source_file_part}}",
+ ]
+ public_deps = [
+ ":policy_templates",
+ ]
+ }
- create_bundle("chrome_manifest_bundle") {
- bundle_root_dir = "$root_out_dir/$chrome_mac_bundle_id.manifest/Contents"
- bundle_resources_dir = "$bundle_root_dir/Resources"
+ create_bundle("chrome_manifest_bundle") {
+ bundle_root_dir = "$root_out_dir/$chrome_mac_bundle_id.manifest/Contents"
+ bundle_resources_dir = "$bundle_root_dir/Resources"
- deps = [
- ":manifest_bundle_data",
- ":manifest_strings_bundle_data",
- ]
- }
+ deps = [
+ ":manifest_bundle_data",
+ ":manifest_strings_bundle_data",
+ ]
}
+}
- if (gen_policy_templates_common && gen_policy_templates_win) {
- version_file = "VERSION"
- version_path = "$policy_templates_base_dir/$version_file"
+if (gen_policy_templates_common && gen_policy_templates_win) {
+ version_file = "VERSION"
+ version_path = "$policy_templates_base_dir/$version_file"
- copy("add_version") {
- sources = [
- "//chrome/VERSION",
- ]
- outputs = [
- version_path,
- ]
- }
+ copy("add_version") {
+ sources = [
+ "//chrome/VERSION",
+ ]
+ outputs = [
+ version_path,
+ ]
+ }
- action("pack_policy_templates") {
- output_zip_file = "$root_out_dir/policy_templates.zip"
- script = "tools/make_policy_zip.py"
- inputs =
- [
- version_path,
- policy_templates_grd_file,
- grit_info_script,
- ] + policy_templates_windows_outputs + policy_templates_doc_outputs
- outputs = [
- output_zip_file,
- ]
- args = [
- "--output",
- rebase_path(output_zip_file, root_build_dir),
- "--basedir",
- rebase_path(policy_templates_base_dir, root_build_dir),
- "--grd_input",
- rebase_path(policy_templates_grd_file, root_build_dir),
- "--grd_strip_path_prefix",
- "app/policy",
- "--extra_input",
- version_file,
- "--grit_info",
- rebase_path(grit_info_script, root_build_dir),
- "-D",
- "gen_policy_templates_common",
- "-D",
- "gen_policy_templates_win",
- ] + grit_defines
- deps = [
- ":add_version",
- ":policy_templates",
- ]
+ action("pack_policy_templates") {
+ output_zip_file = "$root_out_dir/policy_templates.zip"
+ script = "tools/make_policy_zip.py"
+ inputs = [
+ version_path,
+ policy_templates_grd_file,
+ grit_info_script,
+ ] + policy_templates_windows_outputs + policy_templates_doc_outputs
+ outputs = [
+ output_zip_file,
+ ]
+ args = [
+ "--output",
+ rebase_path(output_zip_file, root_build_dir),
+ "--basedir",
+ rebase_path(policy_templates_base_dir, root_build_dir),
+ "--grd_input",
+ rebase_path(policy_templates_grd_file, root_build_dir),
+ "--grd_strip_path_prefix",
+ "app/policy",
+ "--extra_input",
+ version_file,
+ "--grit_info",
+ rebase_path(grit_info_script, root_build_dir),
+ "-D",
+ "gen_policy_templates_common",
+ "-D",
+ "gen_policy_templates_win",
+ ] + grit_defines
+ if (is_chrome_branded) {
+ args += [ "--include_google_admx" ]
}
+ deps = [
+ ":add_version",
+ ":policy_templates",
+ ]
}
}
diff --git a/chromium/components/policy/android/BUILD.gn b/chromium/components/policy/android/BUILD.gn
index 38cfe5ee81f..074bc264d67 100644
--- a/chromium/components/policy/android/BUILD.gn
+++ b/chromium/components/policy/android/BUILD.gn
@@ -44,6 +44,7 @@ junit_binary("components_policy_junit_tests") {
java_files = [
"junit/src/org/chromium/policy/AbstractAppRestrictionsProviderTest.java",
"junit/src/org/chromium/policy/CombinedPolicyProviderTest.java",
+ "junit/src/org/chromium/policy/PolicyConverterTest.java",
"junit/src/org/chromium/policy/test/annotations/PoliciesTest.java",
]
deps = [
diff --git a/chromium/components/policy/core/browser/BUILD.gn b/chromium/components/policy/core/browser/BUILD.gn
index 9b98ff86008..c3730e0e1dd 100644
--- a/chromium/components/policy/core/browser/BUILD.gn
+++ b/chromium/components/policy/core/browser/BUILD.gn
@@ -4,6 +4,8 @@
import("//build/config/features.gni")
+assert(!is_ios, "Policy should not be referenced on iOS")
+
group("browser") {
if (is_component_build) {
public_deps = [
@@ -19,11 +21,30 @@ group("browser") {
source_set("internal") {
visibility = [ "//components/policy/*" ]
sources = [
- # Note that these sources are always included, even for builds that disable
- # policy. Most source files should go in the conditional sources list
- # below. url_blacklist_manager.h is used by managed mode.
+ "autofill_policy_handler.cc",
+ "autofill_policy_handler.h",
+ "browser_policy_connector.cc",
+ "browser_policy_connector.h",
+ "browser_policy_connector_base.cc",
+ "browser_policy_connector_base.h",
+ "browser_policy_connector_ios.h",
+ "browser_policy_connector_ios.mm",
+ "cloud/message_util.cc",
+ "cloud/message_util.h",
+ "configuration_policy_handler.cc",
+ "configuration_policy_handler.h",
+ "configuration_policy_handler_list.cc",
+ "configuration_policy_handler_list.h",
+ "configuration_policy_pref_store.cc",
+ "configuration_policy_pref_store.h",
+ "policy_error_map.cc",
+ "policy_error_map.h",
+ "proxy_policy_handler.cc",
+ "proxy_policy_handler.h",
"url_blacklist_manager.cc",
"url_blacklist_manager.h",
+ "url_blacklist_policy_handler.cc",
+ "url_blacklist_policy_handler.h",
]
configs += [ "//components/policy:component_implementation" ]
@@ -56,91 +77,62 @@ source_set("internal") {
deps += [ "//components/policy/android:jni_headers" ]
}
- if (enable_configuration_policy) {
- sources += [
- "autofill_policy_handler.cc",
- "autofill_policy_handler.h",
- "browser_policy_connector.cc",
- "browser_policy_connector.h",
- "browser_policy_connector_base.cc",
- "browser_policy_connector_base.h",
- "browser_policy_connector_ios.h",
- "browser_policy_connector_ios.mm",
- "cloud/message_util.cc",
- "cloud/message_util.h",
- "configuration_policy_handler.cc",
- "configuration_policy_handler.h",
- "configuration_policy_handler_list.cc",
- "configuration_policy_handler_list.h",
- "configuration_policy_pref_store.cc",
- "configuration_policy_pref_store.h",
- "policy_error_map.cc",
- "policy_error_map.h",
- "proxy_policy_handler.cc",
- "proxy_policy_handler.h",
- "url_blacklist_policy_handler.cc",
- "url_blacklist_policy_handler.h",
- ]
-
- public_deps += [ "//components/policy/core/common:internal" ]
- deps += [
- "//components/autofill/core/common",
- "//components/proxy_config",
- "//google_apis",
- "//net",
- "//third_party/icu",
- ]
- }
+ public_deps += [ "//components/policy/core/common:internal" ]
+ deps += [
+ "//components/autofill/core/common",
+ "//components/proxy_config",
+ "//google_apis",
+ "//net",
+ "//third_party/icu",
+ ]
}
-if (enable_configuration_policy) {
- static_library("test_support") {
- testonly = true
- sources = [
- "configuration_policy_pref_store_test.cc",
- "configuration_policy_pref_store_test.h",
- ]
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "configuration_policy_pref_store_test.cc",
+ "configuration_policy_pref_store_test.h",
+ ]
- public_deps = [
- ":browser",
- "//base",
+ public_deps = [
+ ":browser",
+ "//base",
- # Explicitly link in the generated policy target into the test support
- # so it will be linked to dependent targets. Otherwise in component
- # build, it will be hidden inside the policy component.
- "//components/policy:generated",
- "//components/policy/core/common:test_support",
- ]
- deps = [
- "//testing/gtest",
- ]
- }
+ # Explicitly link in the generated policy target into the test support
+ # so it will be linked to dependent targets. Otherwise in component
+ # build, it will be hidden inside the policy component.
+ "//components/policy:generated",
+ "//components/policy/core/common:test_support",
+ ]
+ deps = [
+ "//testing/gtest",
+ ]
+}
- source_set("unit_tests") {
- testonly = true
- sources = [
- "android/android_combined_policy_provider_unittest.cc",
- "android/policy_converter_unittest.cc",
- "autofill_policy_handler_unittest.cc",
- "browser_policy_connector_unittest.cc",
- "configuration_policy_handler_unittest.cc",
- "configuration_policy_pref_store_unittest.cc",
- "proxy_policy_handler_unittest.cc",
- "url_blacklist_manager_unittest.cc",
- "url_blacklist_policy_handler_unittest.cc",
- ]
- deps = [
- ":test_support",
- "//base",
- "//components/autofill/core/common",
- "//components/policy:generated",
- "//components/prefs:test_support",
- "//components/proxy_config",
- "//components/url_formatter",
- "//google_apis",
- "//net",
- "//testing/gmock",
- "//testing/gtest",
- ]
- }
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "android/android_combined_policy_provider_unittest.cc",
+ "android/policy_converter_unittest.cc",
+ "autofill_policy_handler_unittest.cc",
+ "browser_policy_connector_unittest.cc",
+ "configuration_policy_handler_unittest.cc",
+ "configuration_policy_pref_store_unittest.cc",
+ "proxy_policy_handler_unittest.cc",
+ "url_blacklist_manager_unittest.cc",
+ "url_blacklist_policy_handler_unittest.cc",
+ ]
+ deps = [
+ ":test_support",
+ "//base",
+ "//components/autofill/core/common",
+ "//components/policy:generated",
+ "//components/prefs:test_support",
+ "//components/proxy_config",
+ "//components/url_formatter",
+ "//google_apis",
+ "//net",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
}
diff --git a/chromium/components/policy/core/common/BUILD.gn b/chromium/components/policy/core/common/BUILD.gn
index f5015307e74..17a58604882 100644
--- a/chromium/components/policy/core/common/BUILD.gn
+++ b/chromium/components/policy/core/common/BUILD.gn
@@ -21,355 +21,329 @@ source_set("internal") {
configs += [ "//components/policy:component_implementation" ]
- if (enable_configuration_policy) {
- sources = [
- "../../policy_export.h",
- "async_policy_loader.cc",
- "async_policy_loader.h",
- "async_policy_provider.cc",
- "async_policy_provider.h",
- "cloud/cloud_external_data_manager.cc",
- "cloud/cloud_external_data_manager.h",
- "cloud/cloud_policy_client.cc",
- "cloud/cloud_policy_client.h",
- "cloud/cloud_policy_client_registration_helper.cc",
- "cloud/cloud_policy_client_registration_helper.h",
- "cloud/cloud_policy_constants.cc",
- "cloud/cloud_policy_constants.h",
- "cloud/cloud_policy_core.cc",
- "cloud/cloud_policy_core.h",
- "cloud/cloud_policy_manager.cc",
- "cloud/cloud_policy_manager.h",
- "cloud/cloud_policy_refresh_scheduler.cc",
- "cloud/cloud_policy_refresh_scheduler.h",
- "cloud/cloud_policy_service.cc",
- "cloud/cloud_policy_service.h",
- "cloud/cloud_policy_store.cc",
- "cloud/cloud_policy_store.h",
- "cloud/cloud_policy_validator.cc",
- "cloud/cloud_policy_validator.h",
+ sources = [
+ "../../policy_export.h",
+ "async_policy_loader.cc",
+ "async_policy_loader.h",
+ "async_policy_provider.cc",
+ "async_policy_provider.h",
+ "cloud/cloud_external_data_manager.cc",
+ "cloud/cloud_external_data_manager.h",
+ "cloud/cloud_policy_client.cc",
+ "cloud/cloud_policy_client.h",
+ "cloud/cloud_policy_client_registration_helper.cc",
+ "cloud/cloud_policy_client_registration_helper.h",
+ "cloud/cloud_policy_constants.cc",
+ "cloud/cloud_policy_constants.h",
+ "cloud/cloud_policy_core.cc",
+ "cloud/cloud_policy_core.h",
+ "cloud/cloud_policy_manager.cc",
+ "cloud/cloud_policy_manager.h",
+ "cloud/cloud_policy_refresh_scheduler.cc",
+ "cloud/cloud_policy_refresh_scheduler.h",
+ "cloud/cloud_policy_service.cc",
+ "cloud/cloud_policy_service.h",
+ "cloud/cloud_policy_store.cc",
+ "cloud/cloud_policy_store.h",
+ "cloud/cloud_policy_validator.cc",
+ "cloud/cloud_policy_validator.h",
+ "cloud/component_cloud_policy_service.cc",
+ "cloud/component_cloud_policy_service.h",
+ "cloud/component_cloud_policy_store.cc",
+ "cloud/component_cloud_policy_store.h",
+ "cloud/component_cloud_policy_updater.cc",
+ "cloud/component_cloud_policy_updater.h",
+ "cloud/device_management_service.cc",
+ "cloud/device_management_service.h",
+ "cloud/enterprise_metrics.cc",
+ "cloud/enterprise_metrics.h",
+ "cloud/external_policy_data_fetcher.cc",
+ "cloud/external_policy_data_fetcher.h",
+ "cloud/external_policy_data_updater.cc",
+ "cloud/external_policy_data_updater.h",
+ "cloud/policy_header_io_helper.cc",
+ "cloud/policy_header_io_helper.h",
+ "cloud/policy_header_service.cc",
+ "cloud/policy_header_service.h",
+ "cloud/resource_cache.cc",
+ "cloud/resource_cache.h",
+ "cloud/signing_service.h",
+ "cloud/user_cloud_policy_manager.cc",
+ "cloud/user_cloud_policy_manager.h",
+ "cloud/user_cloud_policy_store.cc",
+ "cloud/user_cloud_policy_store.h",
+ "cloud/user_cloud_policy_store_base.cc",
+ "cloud/user_cloud_policy_store_base.h",
+ "cloud/user_info_fetcher.cc",
+ "cloud/user_info_fetcher.h",
+ "config_dir_policy_loader.cc",
+ "config_dir_policy_loader.h",
+ "configuration_policy_provider.cc",
+ "configuration_policy_provider.h",
+ "external_data_fetcher.cc",
+ "external_data_fetcher.h",
+ "external_data_manager.h",
+ "policy_bundle.cc",
+ "policy_bundle.h",
+ "policy_details.h",
+ "policy_load_status.cc",
+ "policy_load_status.h",
+ "policy_loader_ios.h",
+ "policy_loader_ios.mm",
+ "policy_loader_mac.h",
+ "policy_loader_mac.mm",
+ "policy_loader_win.cc",
+ "policy_loader_win.h",
+ "policy_map.cc",
+ "policy_map.h",
+ "policy_namespace.cc",
+ "policy_namespace.h",
+ "policy_pref_names.cc",
+ "policy_pref_names.h",
+ "policy_service.cc",
+ "policy_service.h",
+ "policy_service_impl.cc",
+ "policy_service_impl.h",
+ "policy_statistics_collector.cc",
+ "policy_statistics_collector.h",
+ "policy_switches.cc",
+ "policy_switches.h",
+ "policy_types.h",
+ "preferences_mac.cc",
+ "preferences_mac.h",
+ "remote_commands/remote_command_job.cc",
+ "remote_commands/remote_command_job.h",
+ "remote_commands/remote_commands_factory.cc",
+ "remote_commands/remote_commands_factory.h",
+ "remote_commands/remote_commands_queue.cc",
+ "remote_commands/remote_commands_queue.h",
+ "remote_commands/remote_commands_service.cc",
+ "remote_commands/remote_commands_service.h",
+ "schema.cc",
+ "schema.h",
+ "schema_internal.h",
+ "schema_map.cc",
+ "schema_map.h",
+ "schema_registry.cc",
+ "schema_registry.h",
+ "schema_registry_tracking_policy_provider.cc",
+ "schema_registry_tracking_policy_provider.h",
+ ]
+
+ configs += [ "//build/config:precompiled_headers" ]
+
+ public_deps = [
+ "//components/policy:generated",
+ "//components/policy/proto",
+ ]
+ deps = [
+ "//base:i18n",
+ "//base/third_party/dynamic_annotations",
+ "//components/data_use_measurement/core",
+ "//components/json_schema",
+ "//components/prefs",
+ "//extensions/features",
+ "//google_apis",
+ "//net",
+ "//third_party/re2",
+ "//url",
+ ]
+
+ if (is_win) {
+ libs = [
+ "shlwapi.lib",
+ "userenv.lib",
+ "ntdsapi.lib",
+ ]
+ }
+ if (is_win || is_chromeos) {
+ sources += [
+ "preg_parser.cc",
+ "preg_parser.h",
+ "registry_dict.cc",
+ "registry_dict.h",
+ ]
+ }
+ if (is_android) {
+ sources += [ "cloud/component_cloud_policy_service_stub.cc" ]
+ sources -= [
"cloud/component_cloud_policy_service.cc",
- "cloud/component_cloud_policy_service.h",
"cloud/component_cloud_policy_store.cc",
"cloud/component_cloud_policy_store.h",
"cloud/component_cloud_policy_updater.cc",
"cloud/component_cloud_policy_updater.h",
- "cloud/device_management_service.cc",
- "cloud/device_management_service.h",
- "cloud/enterprise_metrics.cc",
- "cloud/enterprise_metrics.h",
"cloud/external_policy_data_fetcher.cc",
"cloud/external_policy_data_fetcher.h",
"cloud/external_policy_data_updater.cc",
"cloud/external_policy_data_updater.h",
- "cloud/policy_header_io_helper.cc",
- "cloud/policy_header_io_helper.h",
- "cloud/policy_header_service.cc",
- "cloud/policy_header_service.h",
"cloud/resource_cache.cc",
"cloud/resource_cache.h",
- "cloud/signing_service.h",
- "cloud/user_cloud_policy_manager.cc",
- "cloud/user_cloud_policy_manager.h",
- "cloud/user_cloud_policy_store.cc",
- "cloud/user_cloud_policy_store.h",
- "cloud/user_cloud_policy_store_base.cc",
- "cloud/user_cloud_policy_store_base.h",
- "cloud/user_info_fetcher.cc",
- "cloud/user_info_fetcher.h",
"config_dir_policy_loader.cc",
"config_dir_policy_loader.h",
- "configuration_policy_provider.cc",
- "configuration_policy_provider.h",
- "external_data_fetcher.cc",
- "external_data_fetcher.h",
- "external_data_manager.h",
- "policy_bundle.cc",
- "policy_bundle.h",
- "policy_details.h",
"policy_load_status.cc",
"policy_load_status.h",
- "policy_loader_ios.h",
- "policy_loader_ios.mm",
- "policy_loader_mac.h",
- "policy_loader_mac.mm",
- "policy_loader_win.cc",
- "policy_loader_win.h",
- "policy_map.cc",
- "policy_map.h",
- "policy_namespace.cc",
- "policy_namespace.h",
- "policy_pref_names.cc",
- "policy_pref_names.h",
- "policy_service.cc",
- "policy_service.h",
- "policy_service_impl.cc",
- "policy_service_impl.h",
- "policy_statistics_collector.cc",
- "policy_statistics_collector.h",
- "policy_switches.cc",
- "policy_switches.h",
- "policy_types.h",
- "preferences_mac.cc",
- "preferences_mac.h",
- "remote_commands/remote_command_job.cc",
- "remote_commands/remote_command_job.h",
- "remote_commands/remote_commands_factory.cc",
- "remote_commands/remote_commands_factory.h",
- "remote_commands/remote_commands_queue.cc",
- "remote_commands/remote_commands_queue.h",
- "remote_commands/remote_commands_service.cc",
- "remote_commands/remote_commands_service.h",
- "schema.cc",
- "schema.h",
- "schema_internal.h",
- "schema_map.cc",
- "schema_map.h",
- "schema_registry.cc",
- "schema_registry.h",
- "schema_registry_tracking_policy_provider.cc",
- "schema_registry_tracking_policy_provider.h",
- ]
-
- configs += [ "//build/config:precompiled_headers" ]
-
- public_deps = [
- "//components/policy:generated",
- "//components/policy/proto",
]
- deps = [
- "//base:i18n",
- "//base/third_party/dynamic_annotations",
- "//components/data_use_measurement/core",
- "//components/json_schema",
- "//components/prefs",
- "//extensions/features",
- "//google_apis",
- "//net",
- "//third_party/re2",
- "//url",
+ }
+ if (is_chromeos) {
+ sources += [
+ "proxy_policy_provider.cc",
+ "proxy_policy_provider.h",
]
-
- if (is_win) {
- libs = [
- "shlwapi.lib",
- "userenv.lib",
- "ntdsapi.lib",
- ]
- }
- if (is_win || is_chromeos) {
- sources += [
- "preg_parser.cc",
- "preg_parser.h",
- "registry_dict.cc",
- "registry_dict.h",
- ]
- }
- if (is_android) {
- sources += [ "cloud/component_cloud_policy_service_stub.cc" ]
- sources -= [
- "cloud/component_cloud_policy_service.cc",
- "cloud/component_cloud_policy_store.cc",
- "cloud/component_cloud_policy_store.h",
- "cloud/component_cloud_policy_updater.cc",
- "cloud/component_cloud_policy_updater.h",
- "cloud/external_policy_data_fetcher.cc",
- "cloud/external_policy_data_fetcher.h",
- "cloud/external_policy_data_updater.cc",
- "cloud/external_policy_data_updater.h",
- "cloud/resource_cache.cc",
- "cloud/resource_cache.h",
- "config_dir_policy_loader.cc",
- "config_dir_policy_loader.h",
- "policy_load_status.cc",
- "policy_load_status.h",
- ]
- }
- if (is_chromeos) {
- sources += [
- "proxy_policy_provider.cc",
- "proxy_policy_provider.h",
- ]
- sources -= [
- "cloud/cloud_policy_client_registration_helper.cc",
- "cloud/cloud_policy_client_registration_helper.h",
- "cloud/user_cloud_policy_manager.cc",
- "cloud/user_cloud_policy_manager.h",
- "cloud/user_cloud_policy_store.cc",
- "cloud/user_cloud_policy_store.h",
- ]
- }
- if (is_mac) {
- libs = [ "CoreFoundation.framework" ]
- }
- if (is_ios || is_mac) {
- sources += [
- "mac_util.cc",
- "mac_util.h",
- ]
- }
- } else {
- # Some of the policy code is always enabled, so that other parts of Chrome
- # can always interface with the PolicyService without having to #ifdef on
- # ENABLE_CONFIGURATION_POLICY.
- sources = [
- "external_data_fetcher.cc",
- "external_data_fetcher.h",
- "external_data_manager.h",
- "policy_map.cc",
- "policy_map.h",
- "policy_namespace.cc",
- "policy_namespace.h",
- "policy_pref_names.cc",
- "policy_pref_names.h",
- "policy_service.cc",
- "policy_service.h",
- "policy_service_stub.cc",
- "policy_service_stub.h",
+ sources -= [
+ "cloud/cloud_policy_client_registration_helper.cc",
+ "cloud/cloud_policy_client_registration_helper.h",
+ "cloud/user_cloud_policy_manager.cc",
+ "cloud/user_cloud_policy_manager.h",
+ "cloud/user_cloud_policy_store.cc",
+ "cloud/user_cloud_policy_store.h",
]
- deps = [
- "//base",
+ }
+ if (is_mac) {
+ libs = [ "CoreFoundation.framework" ]
+ }
+ if (is_ios || is_mac) {
+ sources += [
+ "mac_util.cc",
+ "mac_util.h",
]
}
}
-if (enable_configuration_policy) {
- static_library("test_support") {
- testonly = true
- sources = [
- "cloud/mock_cloud_external_data_manager.cc",
- "cloud/mock_cloud_external_data_manager.h",
- "cloud/mock_cloud_policy_client.cc",
- "cloud/mock_cloud_policy_client.h",
- "cloud/mock_cloud_policy_store.cc",
- "cloud/mock_cloud_policy_store.h",
- "cloud/mock_device_management_service.cc",
- "cloud/mock_device_management_service.h",
- "cloud/mock_signing_service.cc",
- "cloud/mock_signing_service.h",
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "cloud/mock_cloud_external_data_manager.cc",
+ "cloud/mock_cloud_external_data_manager.h",
+ "cloud/mock_cloud_policy_client.cc",
+ "cloud/mock_cloud_policy_client.h",
+ "cloud/mock_cloud_policy_store.cc",
+ "cloud/mock_cloud_policy_store.h",
+ "cloud/mock_device_management_service.cc",
+ "cloud/mock_device_management_service.h",
+ "cloud/mock_signing_service.cc",
+ "cloud/mock_signing_service.h",
+ "cloud/mock_user_cloud_policy_store.cc",
+ "cloud/mock_user_cloud_policy_store.h",
+ "cloud/policy_builder.cc",
+ "cloud/policy_builder.h",
+ "configuration_policy_provider_test.cc",
+ "configuration_policy_provider_test.h",
+ "fake_async_policy_loader.cc",
+ "fake_async_policy_loader.h",
+ "mock_configuration_policy_provider.cc",
+ "mock_configuration_policy_provider.h",
+ "mock_policy_service.cc",
+ "mock_policy_service.h",
+ "policy_test_utils.cc",
+ "policy_test_utils.h",
+ "preferences_mock_mac.cc",
+ "preferences_mock_mac.h",
+ "remote_commands/test_remote_command_job.cc",
+ "remote_commands/test_remote_command_job.h",
+ "remote_commands/testing_remote_commands_server.cc",
+ "remote_commands/testing_remote_commands_server.h",
+ ]
+
+ if (is_chromeos) {
+ sources -= [
"cloud/mock_user_cloud_policy_store.cc",
"cloud/mock_user_cloud_policy_store.h",
- "cloud/policy_builder.cc",
- "cloud/policy_builder.h",
- "configuration_policy_provider_test.cc",
- "configuration_policy_provider_test.h",
- "fake_async_policy_loader.cc",
- "fake_async_policy_loader.h",
- "mock_configuration_policy_provider.cc",
- "mock_configuration_policy_provider.h",
- "mock_policy_service.cc",
- "mock_policy_service.h",
- "policy_test_utils.cc",
- "policy_test_utils.h",
- "preferences_mock_mac.cc",
- "preferences_mock_mac.h",
- "remote_commands/test_remote_command_job.cc",
- "remote_commands/test_remote_command_job.h",
- "remote_commands/testing_remote_commands_server.cc",
- "remote_commands/testing_remote_commands_server.h",
]
+ }
- if (is_chromeos) {
- sources -= [
- "cloud/mock_user_cloud_policy_store.cc",
- "cloud/mock_user_cloud_policy_store.h",
- ]
- }
+ public_deps = [
+ ":common",
+ "//base",
- public_deps = [
- ":common",
- "//base",
+ # Explicitly link in the generated policy target into the test support
+ # so it will be linked to dependent targets. Otherwise in component
+ # build, it will be hidden inside the policy component.
+ "//components/policy:generated",
+ "//components/policy/proto",
+ "//crypto",
+ "//net",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
- # Explicitly link in the generated policy target into the test support
- # so it will be linked to dependent targets. Otherwise in component
- # build, it will be hidden inside the policy component.
- "//components/policy:generated",
- "//components/policy/proto",
- "//crypto",
- "//net",
- "//testing/gmock",
- "//testing/gtest",
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "//extensions/features",
+ "cloud/cloud_policy_client_unittest.cc",
+ "cloud/cloud_policy_core_unittest.cc",
+ "cloud/cloud_policy_manager_unittest.cc",
+ "cloud/cloud_policy_refresh_scheduler_unittest.cc",
+ "cloud/cloud_policy_service_unittest.cc",
+ "cloud/cloud_policy_validator_unittest.cc",
+ "cloud/device_management_service_unittest.cc",
+ "cloud/policy_header_io_helper_unittest.cc",
+ "cloud/policy_header_service_unittest.cc",
+ "cloud/user_info_fetcher_unittest.cc",
+ "generate_policy_source_unittest.cc",
+ "policy_bundle_unittest.cc",
+ "policy_loader_ios_unittest.mm",
+ "policy_loader_mac_unittest.cc",
+ "policy_loader_win_unittest.cc",
+ "policy_map_unittest.cc",
+ "policy_service_impl_unittest.cc",
+ "policy_statistics_collector_unittest.cc",
+ "remote_commands/remote_commands_queue_unittest.cc",
+ "remote_commands/remote_commands_service_unittest.cc",
+ "schema_map_unittest.cc",
+ "schema_registry_tracking_policy_provider_unittest.cc",
+ "schema_registry_unittest.cc",
+ "schema_unittest.cc",
+ ]
+ if (is_win || is_chromeos) {
+ sources += [
+ "preg_parser_unittest.cc",
+ "registry_dict_unittest.cc",
]
}
-
- source_set("unit_tests") {
- testonly = true
- sources = [
- "//extensions/features",
- "cloud/cloud_policy_client_unittest.cc",
- "cloud/cloud_policy_core_unittest.cc",
- "cloud/cloud_policy_manager_unittest.cc",
- "cloud/cloud_policy_refresh_scheduler_unittest.cc",
- "cloud/cloud_policy_service_unittest.cc",
- "cloud/cloud_policy_validator_unittest.cc",
- "cloud/device_management_service_unittest.cc",
- "cloud/policy_header_io_helper_unittest.cc",
- "cloud/policy_header_service_unittest.cc",
- "cloud/user_info_fetcher_unittest.cc",
- "generate_policy_source_unittest.cc",
- "policy_bundle_unittest.cc",
- "policy_loader_ios_unittest.mm",
- "policy_loader_mac_unittest.cc",
- "policy_loader_win_unittest.cc",
- "policy_map_unittest.cc",
- "policy_service_impl_unittest.cc",
- "policy_statistics_collector_unittest.cc",
- "remote_commands/remote_commands_queue_unittest.cc",
- "remote_commands/remote_commands_service_unittest.cc",
- "schema_map_unittest.cc",
- "schema_registry_tracking_policy_provider_unittest.cc",
- "schema_registry_unittest.cc",
- "schema_unittest.cc",
+ if (is_chromeos) {
+ sources += [ "proxy_policy_provider_unittest.cc" ]
+ } else {
+ sources += [
+ "cloud/user_cloud_policy_manager_unittest.cc",
+ "cloud/user_cloud_policy_store_unittest.cc",
]
- if (is_win || is_chromeos) {
- sources += [
- "preg_parser_unittest.cc",
- "registry_dict_unittest.cc",
- ]
- }
- if (is_chromeos) {
- sources += [ "proxy_policy_provider_unittest.cc" ]
- } else {
- sources += [
- "cloud/user_cloud_policy_manager_unittest.cc",
- "cloud/user_cloud_policy_store_unittest.cc",
- ]
- }
- if (!is_android) {
- sources += [ "async_policy_provider_unittest.cc" ]
- }
- if (!is_android && !is_ios) {
- sources += [
- "cloud/component_cloud_policy_service_unittest.cc",
- "cloud/component_cloud_policy_store_unittest.cc",
- "cloud/component_cloud_policy_updater_unittest.cc",
- "cloud/external_policy_data_fetcher_unittest.cc",
- "cloud/external_policy_data_updater_unittest.cc",
- "cloud/resource_cache_unittest.cc",
- "config_dir_policy_loader_unittest.cc",
- ]
- }
- if (is_mac || is_ios) {
- sources += [ "mac_util_unittest.cc" ]
- }
- if (is_win || is_chromeos) {
- # Needed by policy_loader_win_unittest.cc and preg_parser_unittest.cc
- data = [
- "//chrome/test/data/policy/",
- ]
- }
-
- deps = [
- ":test_support",
- "//base",
- "//base/test:test_support",
- "//components/policy:generated",
- "//components/prefs:test_support",
- "//extensions/features",
- "//google_apis",
- "//net:test_support",
- "//testing/gmock",
- "//testing/gtest",
+ }
+ if (!is_android) {
+ sources += [ "async_policy_provider_unittest.cc" ]
+ }
+ if (!is_android && !is_ios) {
+ sources += [
+ "cloud/component_cloud_policy_service_unittest.cc",
+ "cloud/component_cloud_policy_store_unittest.cc",
+ "cloud/component_cloud_policy_updater_unittest.cc",
+ "cloud/external_policy_data_fetcher_unittest.cc",
+ "cloud/external_policy_data_updater_unittest.cc",
+ "cloud/resource_cache_unittest.cc",
+ "config_dir_policy_loader_unittest.cc",
]
}
+ if (is_mac || is_ios) {
+ sources += [ "mac_util_unittest.cc" ]
+ }
+ if (is_win || is_chromeos) {
+ # Needed by policy_loader_win_unittest.cc and preg_parser_unittest.cc
+ data = [
+ "//chrome/test/data/policy/",
+ ]
+ }
+
+ deps = [
+ ":test_support",
+ "//base",
+ "//base/test:test_support",
+ "//components/policy:generated",
+ "//components/prefs:test_support",
+ "//extensions/features",
+ "//google_apis",
+ "//net:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
}
diff --git a/chromium/components/policy/resources/policy_templates.gni b/chromium/components/policy/resources/policy_templates.gni
index 0c283001350..9f84cab75a2 100644
--- a/chromium/components/policy/resources/policy_templates.gni
+++ b/chromium/components/policy/resources/policy_templates.gni
@@ -239,3 +239,61 @@ policy_templates_windows_outputs = [
"$policy_templates_base_dir/windows/admx/zh-CN/chrome.adml",
"$policy_templates_base_dir/windows/admx/zh-TW/chrome.adml",
]
+
+policy_templates_windows_google_outputs = [
+ "$policy_templates_base_dir/windows/admx/google.admx",
+
+ "$policy_templates_base_dir/windows/admx/am/google.adml",
+ "$policy_templates_base_dir/windows/admx/ar/google.adml",
+ "$policy_templates_base_dir/windows/admx/bg/google.adml",
+ "$policy_templates_base_dir/windows/admx/bn/google.adml",
+ "$policy_templates_base_dir/windows/admx/ca/google.adml",
+ "$policy_templates_base_dir/windows/admx/cs/google.adml",
+ "$policy_templates_base_dir/windows/admx/da/google.adml",
+ "$policy_templates_base_dir/windows/admx/de/google.adml",
+ "$policy_templates_base_dir/windows/admx/el/google.adml",
+ "$policy_templates_base_dir/windows/admx/en-GB/google.adml",
+ "$policy_templates_base_dir/windows/admx/en-US/google.adml",
+ "$policy_templates_base_dir/windows/admx/es-419/google.adml",
+ "$policy_templates_base_dir/windows/admx/es/google.adml",
+ "$policy_templates_base_dir/windows/admx/et/google.adml",
+ "$policy_templates_base_dir/windows/admx/fa/google.adml",
+ "$policy_templates_base_dir/windows/admx/fi/google.adml",
+ "$policy_templates_base_dir/windows/admx/fil/google.adml",
+ "$policy_templates_base_dir/windows/admx/fr/google.adml",
+ "$policy_templates_base_dir/windows/admx/gu/google.adml",
+ "$policy_templates_base_dir/windows/admx/he/google.adml",
+ "$policy_templates_base_dir/windows/admx/hi/google.adml",
+ "$policy_templates_base_dir/windows/admx/hr/google.adml",
+ "$policy_templates_base_dir/windows/admx/hu/google.adml",
+ "$policy_templates_base_dir/windows/admx/id/google.adml",
+ "$policy_templates_base_dir/windows/admx/it/google.adml",
+ "$policy_templates_base_dir/windows/admx/ja/google.adml",
+ "$policy_templates_base_dir/windows/admx/kn/google.adml",
+ "$policy_templates_base_dir/windows/admx/ko/google.adml",
+ "$policy_templates_base_dir/windows/admx/lt/google.adml",
+ "$policy_templates_base_dir/windows/admx/lv/google.adml",
+ "$policy_templates_base_dir/windows/admx/ml/google.adml",
+ "$policy_templates_base_dir/windows/admx/mr/google.adml",
+ "$policy_templates_base_dir/windows/admx/ms/google.adml",
+ "$policy_templates_base_dir/windows/admx/nb/google.adml",
+ "$policy_templates_base_dir/windows/admx/nl/google.adml",
+ "$policy_templates_base_dir/windows/admx/pl/google.adml",
+ "$policy_templates_base_dir/windows/admx/pt-BR/google.adml",
+ "$policy_templates_base_dir/windows/admx/pt-PT/google.adml",
+ "$policy_templates_base_dir/windows/admx/ro/google.adml",
+ "$policy_templates_base_dir/windows/admx/ru/google.adml",
+ "$policy_templates_base_dir/windows/admx/sk/google.adml",
+ "$policy_templates_base_dir/windows/admx/sl/google.adml",
+ "$policy_templates_base_dir/windows/admx/sr/google.adml",
+ "$policy_templates_base_dir/windows/admx/sv/google.adml",
+ "$policy_templates_base_dir/windows/admx/sw/google.adml",
+ "$policy_templates_base_dir/windows/admx/ta/google.adml",
+ "$policy_templates_base_dir/windows/admx/te/google.adml",
+ "$policy_templates_base_dir/windows/admx/th/google.adml",
+ "$policy_templates_base_dir/windows/admx/tr/google.adml",
+ "$policy_templates_base_dir/windows/admx/uk/google.adml",
+ "$policy_templates_base_dir/windows/admx/vi/google.adml",
+ "$policy_templates_base_dir/windows/admx/zh-CN/google.adml",
+ "$policy_templates_base_dir/windows/admx/zh-TW/google.adml",
+]
diff --git a/chromium/components/policy_strings.grdp b/chromium/components/policy_strings.grdp
index 973ff0923aa..bcaef1d2aba 100644
--- a/chromium/components/policy_strings.grdp
+++ b/chromium/components/policy_strings.grdp
@@ -337,6 +337,9 @@
<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>
<message name="IDS_POLICY_SOURCE_PLATFORM" desc="Indicates that the policy is obtained from the local OS.">
Platform
</message>
diff --git a/chromium/components/power/BUILD.gn b/chromium/components/power/BUILD.gn
deleted file mode 100644
index e8c347ec75d..00000000000
--- a/chromium/components/power/BUILD.gn
+++ /dev/null
@@ -1,32 +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("power") {
- sources = [
- "origin_power_map.cc",
- "origin_power_map.h",
- "origin_power_map_factory.cc",
- "origin_power_map_factory.h",
- ]
-
- deps = [
- "//base",
- "//components/keyed_service/content",
- "//components/keyed_service/core",
- "//content/public/common",
- "//url",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
- sources = [
- "origin_power_map_unittest.cc",
- ]
- deps = [
- ":power",
- "//base",
- "//testing/gtest",
- ]
-}
diff --git a/chromium/components/power/DEPS b/chromium/components/power/DEPS
deleted file mode 100644
index df2301d5f96..00000000000
--- a/chromium/components/power/DEPS
+++ /dev/null
@@ -1,4 +0,0 @@
-include_rules = [
- "+components/keyed_service",
- "+content/public/common",
-]
diff --git a/chromium/components/power/OWNERS b/chromium/components/power/OWNERS
deleted file mode 100644
index 76a50a47e6a..00000000000
--- a/chromium/components/power/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-derat@chromium.org
-dhnishi@chromium.org
-sivachandra@chromium.org
diff --git a/chromium/components/power/origin_power_map.cc b/chromium/components/power/origin_power_map.cc
deleted file mode 100644
index 10502e9d6db..00000000000
--- a/chromium/components/power/origin_power_map.cc
+++ /dev/null
@@ -1,87 +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/power/origin_power_map.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "content/public/common/url_constants.h"
-#include "url/gurl.h"
-
-namespace power {
-
-OriginPowerMap::OriginPowerMap() : total_consumed_(0.0) {
-}
-
-OriginPowerMap::~OriginPowerMap() {
-}
-
-int OriginPowerMap::GetPowerForOrigin(const GURL& url) {
- if (!total_consumed_)
- return 0;
-
- OriginMap::const_iterator it = origin_map_.find(url.GetOrigin());
- return it == origin_map_.end() ? 0 :
- static_cast<int>(it->second * 100 / total_consumed_ + 0.5);
-}
-
-void OriginPowerMap::AddPowerForOrigin(const GURL& url, double power) {
- DCHECK_GE(power, 0);
- GURL origin = url.GetOrigin();
- if (!origin.is_valid() || origin.SchemeIs(content::kChromeUIScheme))
- return;
-
- origin_map_[origin] += power;
- total_consumed_ += power;
-}
-
-OriginPowerMap::PercentOriginMap OriginPowerMap::GetPercentOriginMap() {
- OriginPowerMap::PercentOriginMap percent_map;
-
- if (!total_consumed_)
- return percent_map;
-
- for (OriginMap::iterator it = origin_map_.begin(); it != origin_map_.end();
- ++it) {
- percent_map[it->first] =
- static_cast<int>(it->second * 100 / total_consumed_ + 0.5);
- }
- return percent_map;
-}
-
-std::unique_ptr<OriginPowerMap::Subscription>
-OriginPowerMap::AddPowerConsumptionUpdatedCallback(
- const base::Closure& callback) {
- return callback_list_.Add(callback);
-}
-
-void OriginPowerMap::OnAllOriginsUpdated() {
- callback_list_.Notify();
-}
-
-void OriginPowerMap::ClearOriginMap(
- const base::Callback<bool(const GURL&)> url_filter) {
- if (url_filter.is_null()) {
- origin_map_.clear();
- } else {
- for (auto it = origin_map_.begin(); it != origin_map_.end();) {
- auto next_it = std::next(it);
-
- if (url_filter.Run(it->first)) {
- total_consumed_ -= it->second;
- origin_map_.erase(it);
- }
-
- it = next_it;
- }
- }
-
- // Handle the empty case separately to avoid reporting nonzero power usage
- // for zero origins in case of double rounding errors.
- if (origin_map_.empty())
- total_consumed_ = 0;
-}
-
-} // namespace power
diff --git a/chromium/components/power/origin_power_map.h b/chromium/components/power/origin_power_map.h
deleted file mode 100644
index 362c023b239..00000000000
--- a/chromium/components/power/origin_power_map.h
+++ /dev/null
@@ -1,69 +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_POWER_ORIGIN_POWER_MAP_H_
-#define COMPONENTS_POWER_ORIGIN_POWER_MAP_H_
-
-#include <map>
-#include <memory>
-
-#include "base/callback_list.h"
-#include "base/macros.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "url/gurl.h"
-
-namespace power {
-
-// Tracks app and website origins and how much power they are consuming while
-// running.
-class OriginPowerMap : public KeyedService {
- public:
- typedef std::map<GURL, int> PercentOriginMap;
- typedef base::CallbackList<void(void)>::Subscription Subscription;
-
- OriginPowerMap();
- ~OriginPowerMap() override;
-
- // Returns the integer percentage usage of the total power consumed by a
- // given URL's origin.
- int GetPowerForOrigin(const GURL& url);
-
- // Adds a certain amount of power consumption to a given URL's origin.
- // |power| is a platform-specific heuristic estimating power consumption.
- void AddPowerForOrigin(const GURL& url, double power);
-
- // Returns a map of all origins to the integer percentage usage of power
- // consumed.
- PercentOriginMap GetPercentOriginMap();
-
- // Adds a callback for the completion of a round of updates to |origin_map_|.
- std::unique_ptr<Subscription> AddPowerConsumptionUpdatedCallback(
- const base::Closure& callback);
-
- // Notifies observers to let them know that the origin power map has finished
- // updating for all origins this cycle.
- void OnAllOriginsUpdated();
-
- // Clears URLs out of the map. If |url_filter| is not null, only clears those
- // URLs that are matched by it.
- void ClearOriginMap(const base::Callback<bool(const GURL&)> url_filter);
-
- private:
- // OriginMap maps a URL to the amount of power consumed by the URL using the
- // same units as |total_consumed_|.
- typedef std::map<GURL, double> OriginMap;
- OriginMap origin_map_;
-
- // Total amount of power consumed using units determined by
- // the power heuristics available to the platform.
- double total_consumed_;
-
- base::CallbackList<void(void)> callback_list_;
-
- DISALLOW_COPY_AND_ASSIGN(OriginPowerMap);
-};
-
-} // namespace power
-
-#endif // COMPONENTS_POWER_ORIGIN_POWER_MAP_H_
diff --git a/chromium/components/power/origin_power_map_factory.cc b/chromium/components/power/origin_power_map_factory.cc
deleted file mode 100644
index f1df4ff5613..00000000000
--- a/chromium/components/power/origin_power_map_factory.cc
+++ /dev/null
@@ -1,38 +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/power/origin_power_map_factory.h"
-
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/power/origin_power_map.h"
-
-namespace power {
-// static
-OriginPowerMap* OriginPowerMapFactory::GetForBrowserContext(
- content::BrowserContext* context) {
- return static_cast<OriginPowerMap*>(
- GetInstance()->GetServiceForBrowserContext(context, true));
-}
-
-// static
-OriginPowerMapFactory* OriginPowerMapFactory::GetInstance() {
- return base::Singleton<OriginPowerMapFactory>::get();
-}
-
-OriginPowerMapFactory::OriginPowerMapFactory()
- : BrowserContextKeyedServiceFactory(
- "OriginPowerMap",
- BrowserContextDependencyManager::GetInstance()) {
-}
-
-OriginPowerMapFactory::~OriginPowerMapFactory() {
-}
-
-KeyedService* OriginPowerMapFactory::BuildServiceInstanceFor(
- content::BrowserContext* context) const {
- return new OriginPowerMap();
-}
-
-} // namespace power
diff --git a/chromium/components/power/origin_power_map_factory.h b/chromium/components/power/origin_power_map_factory.h
deleted file mode 100644
index a21ee6b4b4e..00000000000
--- a/chromium/components/power/origin_power_map_factory.h
+++ /dev/null
@@ -1,36 +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_POWER_ORIGIN_POWER_MAP_FACTORY_H_
-#define COMPONENTS_POWER_ORIGIN_POWER_MAP_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace power {
-
-class OriginPowerMap;
-
-class OriginPowerMapFactory : public BrowserContextKeyedServiceFactory {
- public:
- static OriginPowerMap* GetForBrowserContext(content::BrowserContext* context);
- static OriginPowerMapFactory* GetInstance();
-
- private:
- friend struct base::DefaultSingletonTraits<OriginPowerMapFactory>;
-
- OriginPowerMapFactory();
- ~OriginPowerMapFactory() override;
-
- // BrowserContextKeyedServiceFactory:
- KeyedService* BuildServiceInstanceFor(
- content::BrowserContext* context) const override;
-
- DISALLOW_COPY_AND_ASSIGN(OriginPowerMapFactory);
-};
-
-} // namespace power
-
-#endif // COMPONENTS_POWER_ORIGIN_POWER_MAP_FACTORY_H_
diff --git a/chromium/components/power/origin_power_map_unittest.cc b/chromium/components/power/origin_power_map_unittest.cc
deleted file mode 100644
index 061f9bdd2f2..00000000000
--- a/chromium/components/power/origin_power_map_unittest.cc
+++ /dev/null
@@ -1,133 +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/power/origin_power_map.h"
-
-#include <stddef.h>
-
-#include "base/bind.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-bool HostFilter(const std::string& host, const GURL& url) {
- return url.host() == host;
-}
-
-} // namespace
-
-namespace power {
-
-TEST(OriginPowerMapTest, StartEmpty) {
- OriginPowerMap origin_power_map;
- EXPECT_EQ(size_t(0), origin_power_map.GetPercentOriginMap().size());
-}
-
-TEST(OriginPowerMapTest, AddOneOriginNotInMap) {
- OriginPowerMap origin_power_map;
- GURL url("http://www.google.com");
- EXPECT_EQ(0, origin_power_map.GetPowerForOrigin(url));
- origin_power_map.AddPowerForOrigin(url, 10);
- EXPECT_EQ(size_t(1), origin_power_map.GetPercentOriginMap().size());
- EXPECT_EQ(100, origin_power_map.GetPowerForOrigin(url));
-}
-
-TEST(OriginPowerMapTest, AddMultiplesOrigins) {
- OriginPowerMap origin_power_map;
- GURL url1("http://www.google.com");
- EXPECT_EQ(0, origin_power_map.GetPowerForOrigin(url1));
- origin_power_map.AddPowerForOrigin(url1, 10);
- EXPECT_EQ(size_t(1), origin_power_map.GetPercentOriginMap().size());
- EXPECT_EQ(100, origin_power_map.GetPowerForOrigin(url1));
-
- GURL url2("http://www.example.com");
- origin_power_map.AddPowerForOrigin(url2, 30);
- EXPECT_EQ(25, origin_power_map.GetPowerForOrigin(url1));
- EXPECT_EQ(75, origin_power_map.GetPowerForOrigin(url2));
- origin_power_map.AddPowerForOrigin(url2, 10);
- EXPECT_EQ(20, origin_power_map.GetPowerForOrigin(url1));
- EXPECT_EQ(80, origin_power_map.GetPowerForOrigin(url2));
-
- GURL url3("https://www.google.com");
- origin_power_map.AddPowerForOrigin(url3, 50);
- EXPECT_EQ(10, origin_power_map.GetPowerForOrigin(url1));
- EXPECT_EQ(40, origin_power_map.GetPowerForOrigin(url2));
- EXPECT_EQ(50, origin_power_map.GetPowerForOrigin(url3));
-}
-
-TEST(OriginPowerMapTest, PercentOriginMap) {
- OriginPowerMap origin_power_map;
- GURL url1("http://www.google.com");
- GURL url2("http://www.example.com");
- origin_power_map.AddPowerForOrigin(url1, 10);
- origin_power_map.AddPowerForOrigin(url2, 40);
- OriginPowerMap::PercentOriginMap origin_map =
- origin_power_map.GetPercentOriginMap();
- EXPECT_EQ(20, origin_map.find(url1)->second);
- EXPECT_EQ(80, origin_map.find(url2)->second);
-}
-
-TEST(OriginPowerMapTest, EmptyPercentOriginMapWhenZeroConsumed) {
- OriginPowerMap origin_power_map;
- EXPECT_EQ(size_t(0), origin_power_map.GetPercentOriginMap().size());
- GURL url("http://www.google.com");
- origin_power_map.AddPowerForOrigin(url, 0);
- EXPECT_EQ(size_t(0), origin_power_map.GetPercentOriginMap().size());
-}
-
-TEST(OriginPowerMapTest, ClearOriginMap) {
- GURL url1("https://www.google.com");
- GURL url2("https://www.chrome.com");
- GURL url3("https://www.example.com");
- GURL url4("http://www.chrome.com");
- GURL url5("http://www.example.com");
-
- // Add all 5 URLs to the map.
- OriginPowerMap origin_power_map;
- origin_power_map.AddPowerForOrigin(url1, 50);
- origin_power_map.AddPowerForOrigin(url2, 20);
- origin_power_map.AddPowerForOrigin(url3, 15);
- origin_power_map.AddPowerForOrigin(url4, 5);
- origin_power_map.AddPowerForOrigin(url5, 10);
- EXPECT_DOUBLE_EQ(50, origin_power_map.GetPowerForOrigin(url1));
- EXPECT_DOUBLE_EQ(20, origin_power_map.GetPowerForOrigin(url2));
- EXPECT_DOUBLE_EQ(15, origin_power_map.GetPowerForOrigin(url3));
- EXPECT_DOUBLE_EQ(5, origin_power_map.GetPowerForOrigin(url4));
- EXPECT_DOUBLE_EQ(10, origin_power_map.GetPowerForOrigin(url5));
-
- // Delete |url1|.
- origin_power_map.ClearOriginMap(base::Bind(
- static_cast<bool (*)(const GURL&, const GURL&)>(operator==), url1));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url1));
- EXPECT_DOUBLE_EQ(40, origin_power_map.GetPowerForOrigin(url2));
- EXPECT_DOUBLE_EQ(30, origin_power_map.GetPowerForOrigin(url3));
- EXPECT_DOUBLE_EQ(10, origin_power_map.GetPowerForOrigin(url4));
- EXPECT_DOUBLE_EQ(20, origin_power_map.GetPowerForOrigin(url5));
-
- // Delete every URL with the host "www.chrome.com", i.e. |url2| and |url4|.
- origin_power_map.ClearOriginMap(base::Bind(&HostFilter, "www.chrome.com"));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url1));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url2));
- EXPECT_DOUBLE_EQ(60, origin_power_map.GetPowerForOrigin(url3));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url4));
- EXPECT_DOUBLE_EQ(40, origin_power_map.GetPowerForOrigin(url5));
-
- // Delete every URL with the host "www.example.org". There should be none.
- origin_power_map.ClearOriginMap(base::Bind(&HostFilter, "www.example.org"));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url1));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url2));
- EXPECT_DOUBLE_EQ(60, origin_power_map.GetPowerForOrigin(url3));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url4));
- EXPECT_DOUBLE_EQ(40, origin_power_map.GetPowerForOrigin(url5));
-
- // Null callback means complete deletion.
- origin_power_map.ClearOriginMap(base::Callback<bool(const GURL&)>());
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url1));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url2));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url3));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url4));
- EXPECT_DOUBLE_EQ(0, origin_power_map.GetPowerForOrigin(url5));
-}
-
-} // namespace power
diff --git a/chromium/components/precache/android/BUILD.gn b/chromium/components/precache/android/BUILD.gn
index d0becc03248..af995624b56 100644
--- a/chromium/components/precache/android/BUILD.gn
+++ b/chromium/components/precache/android/BUILD.gn
@@ -20,6 +20,7 @@ android_library("precache_javatests") {
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",
diff --git a/chromium/components/precache/content/BUILD.gn b/chromium/components/precache/content/BUILD.gn
index 70c76927013..bca9748e511 100644
--- a/chromium/components/precache/content/BUILD.gn
+++ b/chromium/components/precache/content/BUILD.gn
@@ -39,7 +39,7 @@ source_set("unit_tests") {
"//components/history/core/browser",
"//components/precache/core",
"//components/precache/core:proto",
- "//components/variations",
+ "//components/variations:test_support",
"//content/public/browser",
"//content/test:test_support",
"//net:test_support",
diff --git a/chromium/components/precache/content/precache_manager.cc b/chromium/components/precache/content/precache_manager.cc
index 262e30142c9..7732807e8f8 100644
--- a/chromium/components/precache/content/precache_manager.cc
+++ b/chromium/components/precache/content/precache_manager.cc
@@ -119,14 +119,15 @@ PrecacheManager::AllowedType PrecacheManager::PrecachingAllowed() const {
data_reduction_proxy_settings_->IsDataReductionProxyEnabled()))
return AllowedType::DISALLOWED;
- if (!(sync_service_ && sync_service_->IsBackendInitialized()))
+ if (!(sync_service_ && sync_service_->IsEngineInitialized()))
return AllowedType::PENDING;
// SyncService delegates to SyncPrefs, which must be called on the UI thread.
- if (history_service_ &&
+ if (history_service_ && !sync_service_->IsLocalSyncEnabled() &&
sync_service_->GetActiveDataTypes().Has(syncer::SESSIONS) &&
- !sync_service_->GetEncryptedDataTypes().Has(syncer::SESSIONS))
+ !sync_service_->GetEncryptedDataTypes().Has(syncer::SESSIONS)) {
return AllowedType::ALLOWED;
+ }
return AllowedType::DISALLOWED;
}
@@ -308,7 +309,7 @@ void PrecacheManager::OnGetUnfinishedWorkDone(
}
} else {
if (PrecachingAllowed() != AllowedType::PENDING) {
- // We are not waiting on the sync backend to be initialized. The user
+ // 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.
diff --git a/chromium/components/precache/content/precache_manager_unittest.cc b/chromium/components/precache/content/precache_manager_unittest.cc
index 2497911b2db..8fdbe6b9d10 100644
--- a/chromium/components/precache/content/precache_manager_unittest.cc
+++ b/chromium/components/precache/content/precache_manager_unittest.cc
@@ -28,7 +28,7 @@
#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_associated_data.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"
@@ -57,7 +57,6 @@ using ::testing::Invoke;
using ::testing::IsEmpty;
using ::testing::Pair;
using ::testing::SaveArg;
-using variations::testing::VariationParamsManager;
const char kConfigURL[] = "http://config-url.com";
const char kManifestURLPrefix[] = "http://manifest-url-prefix.com/";
@@ -251,6 +250,7 @@ class PrecacheManagerTest : public testing::Test {
testing::NiceMock<MockHistoryService> history_service_;
base::HistogramTester histograms_;
net::HttpResponseInfo info_;
+ variations::testing::VariationParamsManager variation_params_;
};
TEST_F(PrecacheManagerTest, StartAndFinishPrecaching) {
@@ -282,8 +282,8 @@ TEST_F(PrecacheManagerTest, StartAndFinishPrecaching) {
}
TEST_F(PrecacheManagerTest, StartPrecachingWithGoodSizedCache) {
- VariationParamsManager variation_params(kPrecacheFieldTrialName,
- {{kMinCacheSizeParam, "1"}});
+ 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;
@@ -338,8 +338,8 @@ TEST_F(PrecacheManagerTest, StartPrecachingWithGoodSizedCache) {
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.
- VariationParamsManager variation_params(kPrecacheFieldTrialName,
- {{kMinCacheSizeParam, "1"}});
+ variation_params_.SetVariationParams(kPrecacheFieldTrialName,
+ {{kMinCacheSizeParam, "1"}});
EXPECT_FALSE(precache_manager_->IsPrecaching());
precache_manager_->StartPrecaching(precache_callback_.GetCallback());
diff --git a/chromium/components/precache/core/precache_fetcher.cc b/chromium/components/precache/core/precache_fetcher.cc
index fd008c1aa41..0bc2e1b2b40 100644
--- a/chromium/components/precache/core/precache_fetcher.cc
+++ b/chromium/components/precache/core/precache_fetcher.cc
@@ -28,7 +28,6 @@
#include "components/data_use_measurement/core/data_use_user_data.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/quota.pb.h"
#include "components/precache/core/proto/unfinished_work.pb.h"
#include "net/base/completion_callback.h"
@@ -229,12 +228,43 @@ bool IsQuotaTimeExpired(const PrecacheQuota& quota,
start_time + base::TimeDelta::FromDays(1) < time_now;
}
-double ResourceWeight(const PrecacheResource& resource, int64_t host_visits) {
- return resource.weight_ratio() * host_visits;
+// 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,
@@ -723,7 +753,9 @@ void PrecacheFetcher::OnManifestFetchComplete(int64_t host_visits,
manifest.resource(i).has_url()) {
GURL url(manifest.resource(i).url());
if (url.is_valid()) {
- double weight = ResourceWeight(manifest.resource(i), host_visits);
+ 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);
}
diff --git a/chromium/components/precache/core/precache_fetcher.h b/chromium/components/precache/core/precache_fetcher.h
index 87d1c4aa237..9868d1c015a 100644
--- a/chromium/components/precache/core/precache_fetcher.h
+++ b/chromium/components/precache/core/precache_fetcher.h
@@ -20,6 +20,7 @@
#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"
@@ -270,6 +271,12 @@ class PrecacheFetcher : public base::SupportsWeakPtr<PrecacheFetcher> {
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
diff --git a/chromium/components/precache/core/precache_fetcher_unittest.cc b/chromium/components/precache/core/precache_fetcher_unittest.cc
index a5ed3531059..f42c6c39ba1 100644
--- a/chromium/components/precache/core/precache_fetcher_unittest.cc
+++ b/chromium/components/precache/core/precache_fetcher_unittest.cc
@@ -1421,7 +1421,32 @@ TEST_F(PrecacheFetcherTest, SendUsedDownloadedResourceHash) {
}
}
-TEST_F(PrecacheFetcherTest, GloballyRankResources) {
+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;
@@ -1432,6 +1457,7 @@ TEST_F(PrecacheFetcherTest, GloballyRankResources) {
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);
@@ -1465,8 +1491,8 @@ TEST_F(PrecacheFetcherTest, GloballyRankResources) {
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());
+ resources.emplace_back(
+ resource_url, ResourceWeight(GetParam(), weight, top_host->visits()));
}
factory_.SetFakeResponse(GURL(kManifestURLPrefix + top_host_url),
manifest.SerializeAsString(), net::HTTP_OK,
@@ -1494,6 +1520,12 @@ TEST_F(PrecacheFetcherTest, GloballyRankResources) {
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();
diff --git a/chromium/components/precache/core/proto/precache.proto b/chromium/components/precache/core/proto/precache.proto
index 72bcc066029..dd80621a3b6 100644
--- a/chromium/components/precache/core/proto/precache.proto
+++ b/chromium/components/precache/core/proto/precache.proto
@@ -109,4 +109,18 @@ message PrecacheConfigurationSettings {
// 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/pref_registry/BUILD.gn b/chromium/components/pref_registry/BUILD.gn
index 4a32f9a4b22..77ef6cd35dc 100644
--- a/chromium/components/pref_registry/BUILD.gn
+++ b/chromium/components/pref_registry/BUILD.gn
@@ -14,20 +14,3 @@ static_library("pref_registry") {
"//components/prefs",
]
}
-
-static_library("test_support") {
- testonly = true
- sources = [
- "testing_pref_service_syncable.cc",
- "testing_pref_service_syncable.h",
- ]
-
- public_deps = [
- ":pref_registry",
- ]
-
- deps = [
- "//base",
- "//components/prefs:test_support",
- ]
-}
diff --git a/chromium/components/pref_registry/pref_registry_syncable.h b/chromium/components/pref_registry/pref_registry_syncable.h
index cc3e6b05227..17f72434578 100644
--- a/chromium/components/pref_registry/pref_registry_syncable.h
+++ b/chromium/components/pref_registry/pref_registry_syncable.h
@@ -14,9 +14,6 @@
#include "components/prefs/pref_registry_simple.h"
namespace base {
-class DictionaryValue;
-class FilePath;
-class ListValue;
class Value;
}
diff --git a/chromium/components/pref_registry/testing_pref_service_syncable.cc b/chromium/components/pref_registry/testing_pref_service_syncable.cc
deleted file mode 100644
index 7595b2c8b8a..00000000000
--- a/chromium/components/pref_registry/testing_pref_service_syncable.cc
+++ /dev/null
@@ -1,70 +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/pref_registry/testing_pref_service_syncable.h"
-
-#include "base/bind.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_notifier_impl.h"
-#include "components/prefs/pref_value_store.h"
-
-template <>
-TestingPrefServiceBase<PrefService, user_prefs::PrefRegistrySyncable>::
- TestingPrefServiceBase(TestingPrefStore* managed_prefs,
- TestingPrefStore* user_prefs,
- TestingPrefStore* recommended_prefs,
- user_prefs::PrefRegistrySyncable* pref_registry,
- PrefNotifierImpl* pref_notifier)
- : PrefService(pref_notifier,
- new PrefValueStore(managed_prefs,
- NULL, // supervised_user_prefs
- NULL, // extension_prefs
- NULL, // command_line_prefs
- user_prefs,
- recommended_prefs,
- pref_registry->defaults().get(),
- pref_notifier),
- user_prefs,
- pref_registry,
- base::Bind(&TestingPrefServiceBase<
- PrefService,
- user_prefs::PrefRegistrySyncable>::HandleReadError),
- false),
- managed_prefs_(managed_prefs),
- user_prefs_(user_prefs),
- recommended_prefs_(recommended_prefs) {}
-
-namespace user_prefs {
-
-TestingPrefServiceSyncable::TestingPrefServiceSyncable()
- : TestingPrefServiceBase<PrefService, PrefRegistrySyncable>(
- new TestingPrefStore(),
- new TestingPrefStore(),
- new TestingPrefStore(),
- new PrefRegistrySyncable(),
- new PrefNotifierImpl()) {
-}
-
-TestingPrefServiceSyncable::TestingPrefServiceSyncable(
- TestingPrefStore* managed_prefs,
- TestingPrefStore* user_prefs,
- TestingPrefStore* recommended_prefs,
- PrefRegistrySyncable* pref_registry,
- PrefNotifierImpl* pref_notifier)
- : TestingPrefServiceBase<PrefService, PrefRegistrySyncable>(
- managed_prefs,
- user_prefs,
- recommended_prefs,
- pref_registry,
- pref_notifier) {
-}
-
-TestingPrefServiceSyncable::~TestingPrefServiceSyncable() {
-}
-
-PrefRegistrySyncable* TestingPrefServiceSyncable::registry() {
- return static_cast<PrefRegistrySyncable*>(DeprecatedGetPrefRegistry());
-}
-
-} // namespace user_prefs
diff --git a/chromium/components/pref_registry/testing_pref_service_syncable.h b/chromium/components/pref_registry/testing_pref_service_syncable.h
deleted file mode 100644
index 80a11a1f020..00000000000
--- a/chromium/components/pref_registry/testing_pref_service_syncable.h
+++ /dev/null
@@ -1,38 +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_PERF_REGISTRY_TESTING_PREF_SERVICE_SYNCABLE_H_
-#define COMPONENTS_PERF_REGISTRY_TESTING_PREF_SERVICE_SYNCABLE_H_
-
-#include "base/macros.h"
-#include "components/prefs/testing_pref_service.h"
-
-namespace user_prefs {
-
-class PrefRegistrySyncable;
-
-// Test version of PrefServiceSyncable.
-class TestingPrefServiceSyncable
- : public TestingPrefServiceBase<PrefService, PrefRegistrySyncable> {
- public:
- TestingPrefServiceSyncable();
- TestingPrefServiceSyncable(TestingPrefStore* managed_prefs,
- TestingPrefStore* user_prefs,
- TestingPrefStore* recommended_prefs,
- PrefRegistrySyncable* pref_registry,
- PrefNotifierImpl* pref_notifier);
- ~TestingPrefServiceSyncable() override;
-
- // This is provided as a convenience; on a production PrefService
- // you would do all registrations before constructing it, passing it
- // a PrefRegistry via its constructor (or via e.g. PrefServiceFactory).
- PrefRegistrySyncable* registry();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestingPrefServiceSyncable);
-};
-
-} // namespace user_prefs
-
-#endif // COMPONENTS_PERF_REGISTRY_TESTING_PREF_SERVICE_SYNCABLE_H_
diff --git a/chromium/components/prefs/BUILD.gn b/chromium/components/prefs/BUILD.gn
index b2eb8535845..e3462a4a2ba 100644
--- a/chromium/components/prefs/BUILD.gn
+++ b/chromium/components/prefs/BUILD.gn
@@ -4,6 +4,7 @@
component("prefs") {
sources = [
+ "base_prefs_export.h",
"command_line_pref_store.cc",
"command_line_pref_store.h",
"default_pref_store.cc",
@@ -14,12 +15,16 @@ component("prefs") {
"json_pref_store.h",
"overlay_user_pref_store.cc",
"overlay_user_pref_store.h",
+ "persistent_pref_store.h",
"pref_change_registrar.cc",
"pref_change_registrar.h",
+ "pref_filter.h",
"pref_member.cc",
"pref_member.h",
+ "pref_notifier.h",
"pref_notifier_impl.cc",
"pref_notifier_impl.h",
+ "pref_observer.h",
"pref_registry.cc",
"pref_registry.h",
"pref_registry_simple.cc",
@@ -38,17 +43,8 @@ component("prefs") {
"scoped_user_pref_update.h",
"value_map_pref_store.cc",
"value_map_pref_store.h",
+ "writeable_pref_store.h",
]
- if (!is_ios) {
- sources += [
- "base_prefs_export.h",
- "persistent_pref_store.h",
- "pref_filter.h",
- "pref_notifier.h",
- "pref_observer.h",
- "writeable_pref_store.h",
- ]
- }
defines = [ "COMPONENTS_PREFS_IMPLEMENTATION" ]
diff --git a/chromium/components/prefs/json_pref_store.cc b/chromium/components/prefs/json_pref_store.cc
index 4076433dbb8..026606a7b76 100644
--- a/chromium/components/prefs/json_pref_store.cc
+++ b/chromium/components/prefs/json_pref_store.cc
@@ -89,7 +89,7 @@ PersistentPrefStore::PrefReadError HandleReadErrors(
: PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
}
}
- if (!value->IsType(base::Value::TYPE_DICTIONARY))
+ if (!value->IsType(base::Value::Type::DICTIONARY))
return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
return PersistentPrefStore::PREF_READ_ERROR_NONE;
}
diff --git a/chromium/components/prefs/overlay_user_pref_store_unittest.cc b/chromium/components/prefs/overlay_user_pref_store_unittest.cc
index ae320815973..d722aeab6e0 100644
--- a/chromium/components/prefs/overlay_user_pref_store_unittest.cc
+++ b/chromium/components/prefs/overlay_user_pref_store_unittest.cc
@@ -137,19 +137,19 @@ TEST_F(OverlayUserPrefStoreTest, ModifyDictionaries) {
Value* modify = NULL;
EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modify));
ASSERT_TRUE(modify);
- ASSERT_TRUE(modify->IsType(Value::TYPE_DICTIONARY));
+ ASSERT_TRUE(modify->IsType(Value::Type::DICTIONARY));
static_cast<DictionaryValue*>(modify)->SetInteger(overlay_key, 42);
Value* original_in_underlay = NULL;
EXPECT_TRUE(underlay_->GetMutableValue(overlay_key, &original_in_underlay));
ASSERT_TRUE(original_in_underlay);
- ASSERT_TRUE(original_in_underlay->IsType(Value::TYPE_DICTIONARY));
+ ASSERT_TRUE(original_in_underlay->IsType(Value::Type::DICTIONARY));
EXPECT_TRUE(static_cast<DictionaryValue*>(original_in_underlay)->empty());
Value* modified = NULL;
EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modified));
ASSERT_TRUE(modified);
- ASSERT_TRUE(modified->IsType(Value::TYPE_DICTIONARY));
+ ASSERT_TRUE(modified->IsType(Value::Type::DICTIONARY));
EXPECT_TRUE(Value::Equals(modify, static_cast<DictionaryValue*>(modified)));
}
diff --git a/chromium/components/prefs/pref_filter.h b/chromium/components/prefs/pref_filter.h
index 82be63e0aee..9bc2244b412 100644
--- a/chromium/components/prefs/pref_filter.h
+++ b/chromium/components/prefs/pref_filter.h
@@ -14,7 +14,6 @@
namespace base {
class DictionaryValue;
-class Value;
} // namespace base
// Filters preferences as they are loaded from disk or updated at runtime.
diff --git a/chromium/components/prefs/pref_member.cc b/chromium/components/prefs/pref_member.cc
index 53c56d5fc44..8ed13ee5516 100644
--- a/chromium/components/prefs/pref_member.cc
+++ b/chromium/components/prefs/pref_member.cc
@@ -131,7 +131,7 @@ void PrefMemberBase::Internal::MoveToThread(
bool PrefMemberVectorStringUpdate(const base::Value& value,
std::vector<std::string>* string_vector) {
- if (!value.IsType(base::Value::TYPE_LIST))
+ if (!value.IsType(base::Value::Type::LIST))
return false;
const base::ListValue* list = static_cast<const base::ListValue*>(&value);
diff --git a/chromium/components/prefs/pref_registry.cc b/chromium/components/prefs/pref_registry.cc
index c42e77f74aa..63b5e6a823e 100644
--- a/chromium/components/prefs/pref_registry.cc
+++ b/chromium/components/prefs/pref_registry.cc
@@ -54,8 +54,8 @@ void PrefRegistry::RegisterPreference(const std::string& path,
base::Value* default_value,
uint32_t flags) {
base::Value::Type orig_type = default_value->GetType();
- DCHECK(orig_type != base::Value::TYPE_NULL &&
- orig_type != base::Value::TYPE_BINARY) <<
+ DCHECK(orig_type != base::Value::Type::NONE &&
+ orig_type != base::Value::Type::BINARY) <<
"invalid preference type: " << orig_type;
DCHECK(!defaults_->GetValue(path, NULL)) <<
"Trying to register a previously registered pref: " << path;
diff --git a/chromium/components/prefs/pref_service.cc b/chromium/components/prefs/pref_service.cc
index e909d58be7e..d0274b17c8c 100644
--- a/chromium/components/prefs/pref_service.cc
+++ b/chromium/components/prefs/pref_service.cc
@@ -288,7 +288,7 @@ const base::DictionaryValue* PrefService::GetDictionary(
NOTREACHED() << "Trying to read an unregistered pref: " << path;
return NULL;
}
- if (value->GetType() != base::Value::TYPE_DICTIONARY) {
+ if (value->GetType() != base::Value::Type::DICTIONARY) {
NOTREACHED();
return NULL;
}
@@ -345,7 +345,7 @@ const base::ListValue* PrefService::GetList(const std::string& path) const {
NOTREACHED() << "Trying to read an unregistered pref: " << path;
return NULL;
}
- if (value->GetType() != base::Value::TYPE_LIST) {
+ if (value->GetType() != base::Value::Type::LIST) {
NOTREACHED();
return NULL;
}
@@ -453,7 +453,8 @@ uint64_t PrefService::GetUint64(const std::string& path) const {
base::Value* PrefService::GetMutableUserPref(const std::string& path,
base::Value::Type type) {
- CHECK(type == base::Value::TYPE_DICTIONARY || type == base::Value::TYPE_LIST);
+ CHECK(type == base::Value::Type::DICTIONARY ||
+ type == base::Value::Type::LIST);
DCHECK(CalledOnValidThread());
const Preference* pref = FindPreference(path);
@@ -471,9 +472,9 @@ base::Value* PrefService::GetMutableUserPref(const std::string& path,
base::Value* value = NULL;
if (!user_pref_store_->GetMutableValue(path, &value) ||
!value->IsType(type)) {
- if (type == base::Value::TYPE_DICTIONARY) {
+ if (type == base::Value::Type::DICTIONARY) {
value = new base::DictionaryValue;
- } else if (type == base::Value::TYPE_LIST) {
+ } else if (type == base::Value::Type::LIST) {
value = new base::ListValue;
} else {
NOTREACHED();
diff --git a/chromium/components/prefs/pref_service.h b/chromium/components/prefs/pref_service.h
index 82de7c4b79e..d26e118d891 100644
--- a/chromium/components/prefs/pref_service.h
+++ b/chromium/components/prefs/pref_service.h
@@ -222,7 +222,7 @@ class COMPONENTS_PREFS_EXPORT PrefService : public base::NonThreadSafe {
// Int64 helper methods that actually store the given value as a string.
// Note that if obtaining the named value via GetDictionary or GetList, the
- // Value type will be TYPE_STRING.
+ // Value type will be Type::STRING.
void SetInt64(const std::string& path, int64_t value);
int64_t GetInt64(const std::string& path) const;
@@ -365,7 +365,7 @@ class COMPONENTS_PREFS_EXPORT PrefService : public base::NonThreadSafe {
// This will create a dictionary or list if one does not exist in the user
// pref store. This method returns NULL only if you're requesting an
// unregistered pref or a non-dict/non-list pref.
- // |type| may only be Values::TYPE_DICTIONARY or Values::TYPE_LIST and
+ // |type| may only be Values::Type::DICTIONARY or Values::Type::LIST and
// |path| must point to a registered preference of type |type|.
// Ownership of the returned value remains at the user pref store.
base::Value* GetMutableUserPref(const std::string& path,
diff --git a/chromium/components/prefs/pref_service_unittest.cc b/chromium/components/prefs/pref_service_unittest.cc
index 373c01bcb3f..5229b97ee8f 100644
--- a/chromium/components/prefs/pref_service_unittest.cc
+++ b/chromium/components/prefs/pref_service_unittest.cc
@@ -148,7 +148,7 @@ TEST(PrefServiceTest, GetValueChangedType) {
ASSERT_TRUE(pref);
const base::Value* value = pref->GetValue();
ASSERT_TRUE(value);
- EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
int actual_int_value = -1;
EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
EXPECT_EQ(kTestValue, actual_int_value);
@@ -168,7 +168,7 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) {
// Check that GetValue() returns the default value.
const base::Value* value = pref->GetValue();
ASSERT_TRUE(value);
- EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
int actual_int_value = -1;
EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
EXPECT_EQ(kDefaultValue, actual_int_value);
@@ -183,7 +183,7 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) {
// Check that GetValue() returns the user-set value.
value = pref->GetValue();
ASSERT_TRUE(value);
- EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
actual_int_value = -1;
EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
EXPECT_EQ(kUserValue, actual_int_value);
@@ -199,7 +199,7 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) {
// Check that GetValue() returns the user-set value.
value = pref->GetValue();
ASSERT_TRUE(value);
- EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
actual_int_value = -1;
EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
EXPECT_EQ(kUserValue, actual_int_value);
@@ -207,7 +207,7 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) {
// Check that GetRecommendedValue() returns the recommended value.
value = pref->GetRecommendedValue();
ASSERT_TRUE(value);
- EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
actual_int_value = -1;
EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
EXPECT_EQ(kRecommendedValue, actual_int_value);
@@ -218,7 +218,7 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) {
// Check that GetValue() returns the recommended value.
value = pref->GetValue();
ASSERT_TRUE(value);
- EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
actual_int_value = -1;
EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
EXPECT_EQ(kRecommendedValue, actual_int_value);
@@ -226,7 +226,7 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) {
// Check that GetRecommendedValue() returns the recommended value.
value = pref->GetRecommendedValue();
ASSERT_TRUE(value);
- EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
actual_int_value = -1;
EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
EXPECT_EQ(kRecommendedValue, actual_int_value);
@@ -321,7 +321,7 @@ TEST(PrefServiceTest, WriteablePrefStoreFlags) {
SCOPED_TRACE("Currently testing pref with name: " +
std::string(entry.pref_name));
- prefs->GetMutableUserPref(entry.pref_name, base::Value::TYPE_DICTIONARY);
+ prefs->GetMutableUserPref(entry.pref_name, base::Value::Type::DICTIONARY);
EXPECT_TRUE(flag_checker->last_write_flags_set());
EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear());
diff --git a/chromium/components/prefs/pref_value_map.cc b/chromium/components/prefs/pref_value_map.cc
index c146918e238..f0392d25284 100644
--- a/chromium/components/prefs/pref_value_map.cc
+++ b/chromium/components/prefs/pref_value_map.cc
@@ -10,7 +10,6 @@
#include "base/logging.h"
#include "base/memory/ptr_util.h"
-#include "base/stl_util.h"
#include "base/values.h"
PrefValueMap::PrefValueMap() {}
@@ -19,7 +18,11 @@ PrefValueMap::~PrefValueMap() {}
bool PrefValueMap::GetValue(const std::string& key,
const base::Value** value) const {
- const base::Value* got_value = prefs_.get(key);
+ auto it = prefs_.find(key);
+ if (it == prefs_.end())
+ return false;
+
+ const base::Value* got_value = it->second.get();
if (value && got_value)
*value = got_value;
@@ -27,7 +30,11 @@ bool PrefValueMap::GetValue(const std::string& key,
}
bool PrefValueMap::GetValue(const std::string& key, base::Value** value) {
- base::Value* got_value = prefs_.get(key);
+ auto it = prefs_.find(key);
+ if (it == prefs_.end())
+ return false;
+
+ base::Value* got_value = it->second.get();
if (value && got_value)
*value = got_value;
@@ -38,11 +45,11 @@ bool PrefValueMap::SetValue(const std::string& key,
std::unique_ptr<base::Value> value) {
DCHECK(value);
- base::Value* old_value = prefs_.get(key);
- if (old_value && value->Equals(old_value))
+ std::unique_ptr<base::Value>& existing_value = prefs_[key];
+ if (existing_value && value->Equals(existing_value.get()))
return false;
- prefs_.set(key, std::move(value));
+ existing_value = std::move(value);
return true;
}
@@ -118,13 +125,16 @@ void PrefValueMap::GetDifferingKeys(
differing_keys->clear();
// Put everything into ordered maps.
- std::map<std::string, base::Value*> this_prefs(prefs_.begin(), prefs_.end());
- std::map<std::string, base::Value*> other_prefs(other->prefs_.begin(),
- other->prefs_.end());
+ std::map<std::string, base::Value*> this_prefs;
+ std::map<std::string, base::Value*> other_prefs;
+ for (const auto& pair : prefs_)
+ this_prefs[pair.first] = pair.second.get();
+ for (const auto& pair : other->prefs_)
+ other_prefs[pair.first] = pair.second.get();
// Walk over the maps in lockstep, adding everything that is different.
- auto this_pref(this_prefs.begin());
- auto other_pref(other_prefs.begin());
+ auto this_pref = this_prefs.begin();
+ auto other_pref = other_prefs.begin();
while (this_pref != this_prefs.end() && other_pref != other_prefs.end()) {
const int diff = this_pref->first.compare(other_pref->first);
if (diff == 0) {
diff --git a/chromium/components/prefs/pref_value_map.h b/chromium/components/prefs/pref_value_map.h
index 011b2a47268..b0ffbd15fdd 100644
--- a/chromium/components/prefs/pref_value_map.h
+++ b/chromium/components/prefs/pref_value_map.h
@@ -7,9 +7,9 @@
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
-#include "base/containers/scoped_ptr_hash_map.h"
#include "base/macros.h"
#include "components/prefs/base_prefs_export.h"
@@ -20,7 +20,7 @@ class Value;
// A generic string to value map used by the PrefStore implementations.
class COMPONENTS_PREFS_EXPORT PrefValueMap {
public:
- using Map = base::ScopedPtrHashMap<std::string, std::unique_ptr<base::Value>>;
+ using Map = std::unordered_map<std::string, std::unique_ptr<base::Value>>;
using iterator = Map::iterator;
using const_iterator = Map::const_iterator;
diff --git a/chromium/components/prefs/pref_value_store_unittest.cc b/chromium/components/prefs/pref_value_store_unittest.cc
index b1656a427bb..23fc686e17c 100644
--- a/chromium/components/prefs/pref_value_store_unittest.cc
+++ b/chromium/components/prefs/pref_value_store_unittest.cc
@@ -271,7 +271,7 @@ TEST_F(PrefValueStoreTest, GetValue) {
// Test getting a managed value.
value = NULL;
ASSERT_TRUE(pref_value_store_->GetValue(prefs::kManagedPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
std::string actual_str_value;
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(managed_pref::kManagedValue, actual_str_value);
@@ -279,35 +279,35 @@ TEST_F(PrefValueStoreTest, GetValue) {
// Test getting a supervised user value.
value = NULL;
ASSERT_TRUE(pref_value_store_->GetValue(prefs::kSupervisedUserPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(supervised_user_pref::kSupervisedUserValue, actual_str_value);
// Test getting an extension value.
value = NULL;
ASSERT_TRUE(pref_value_store_->GetValue(prefs::kExtensionPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(extension_pref::kExtensionValue, actual_str_value);
// Test getting a command-line value.
value = NULL;
ASSERT_TRUE(pref_value_store_->GetValue(prefs::kCommandLinePref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(command_line_pref::kCommandLineValue, actual_str_value);
// Test getting a user-set value.
value = NULL;
ASSERT_TRUE(pref_value_store_->GetValue(prefs::kUserPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(user_pref::kUserValue, actual_str_value);
// Test getting a user set value overwriting a recommended value.
value = NULL;
ASSERT_TRUE(pref_value_store_->GetValue(prefs::kRecommendedPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(recommended_pref::kRecommendedValue,
actual_str_value);
@@ -315,7 +315,7 @@ TEST_F(PrefValueStoreTest, GetValue) {
// Test getting a default value.
value = NULL;
ASSERT_TRUE(pref_value_store_->GetValue(prefs::kDefaultPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(default_pref::kDefaultValue, actual_str_value);
@@ -324,7 +324,7 @@ TEST_F(PrefValueStoreTest, GetValue) {
base::FundamentalValue tmp_dummy_value(true);
value = &tmp_dummy_value;
ASSERT_FALSE(pref_value_store_->GetValue(prefs::kMissingPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
ASSERT_FALSE(value);
}
@@ -339,7 +339,7 @@ TEST_F(PrefValueStoreTest, GetRecommendedValue) {
value = NULL;
ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
prefs::kManagedPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
std::string actual_str_value;
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(recommended_pref::kManagedValue, actual_str_value);
@@ -348,7 +348,7 @@ TEST_F(PrefValueStoreTest, GetRecommendedValue) {
value = NULL;
ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
prefs::kSupervisedUserPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(recommended_pref::kSupervisedUserValue, actual_str_value);
@@ -356,7 +356,7 @@ TEST_F(PrefValueStoreTest, GetRecommendedValue) {
value = NULL;
ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
prefs::kExtensionPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(recommended_pref::kExtensionValue, actual_str_value);
@@ -364,7 +364,7 @@ TEST_F(PrefValueStoreTest, GetRecommendedValue) {
value = NULL;
ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
prefs::kCommandLinePref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(recommended_pref::kCommandLineValue, actual_str_value);
@@ -372,7 +372,7 @@ TEST_F(PrefValueStoreTest, GetRecommendedValue) {
value = NULL;
ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
prefs::kUserPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(recommended_pref::kUserValue, actual_str_value);
@@ -380,7 +380,7 @@ TEST_F(PrefValueStoreTest, GetRecommendedValue) {
value = NULL;
ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
prefs::kRecommendedPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
EXPECT_TRUE(value->GetAsString(&actual_str_value));
EXPECT_EQ(recommended_pref::kRecommendedValue,
actual_str_value);
@@ -390,7 +390,7 @@ TEST_F(PrefValueStoreTest, GetRecommendedValue) {
value = &tmp_dummy_value;
ASSERT_FALSE(pref_value_store_->GetRecommendedValue(
prefs::kDefaultPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
ASSERT_FALSE(value);
// Test getting a preference value that the |PrefValueStore|
@@ -398,7 +398,7 @@ TEST_F(PrefValueStoreTest, GetRecommendedValue) {
value = &tmp_dummy_value;
ASSERT_FALSE(pref_value_store_->GetRecommendedValue(
prefs::kMissingPref,
- base::Value::TYPE_STRING, &value));
+ base::Value::Type::STRING, &value));
ASSERT_FALSE(value);
}
diff --git a/chromium/components/prefs/scoped_user_pref_update.h b/chromium/components/prefs/scoped_user_pref_update.h
index bfcdb3f5574..29859d65b33 100644
--- a/chromium/components/prefs/scoped_user_pref_update.h
+++ b/chromium/components/prefs/scoped_user_pref_update.h
@@ -101,9 +101,9 @@ class ScopedUserPrefUpdate : public subtle::ScopedUserPrefUpdateBase {
};
typedef ScopedUserPrefUpdate<base::DictionaryValue,
- base::Value::TYPE_DICTIONARY>
+ base::Value::Type::DICTIONARY>
DictionaryPrefUpdate;
-typedef ScopedUserPrefUpdate<base::ListValue, base::Value::TYPE_LIST>
+typedef ScopedUserPrefUpdate<base::ListValue, base::Value::Type::LIST>
ListPrefUpdate;
#endif // COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_
diff --git a/chromium/components/prefs/testing_pref_service.cc b/chromium/components/prefs/testing_pref_service.cc
index 1dd760e72d6..fdaa33b0fa2 100644
--- a/chromium/components/prefs/testing_pref_service.cc
+++ b/chromium/components/prefs/testing_pref_service.cc
@@ -15,6 +15,7 @@
template <>
TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase(
TestingPrefStore* managed_prefs,
+ TestingPrefStore* extension_prefs,
TestingPrefStore* user_prefs,
TestingPrefStore* recommended_prefs,
PrefRegistry* pref_registry,
@@ -23,7 +24,7 @@ TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase(
pref_notifier,
new PrefValueStore(managed_prefs,
NULL,
- NULL,
+ extension_prefs,
NULL,
user_prefs,
recommended_prefs,
@@ -35,6 +36,7 @@ TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase(
PrefRegistry>::HandleReadError),
false),
managed_prefs_(managed_prefs),
+ extension_prefs_(extension_prefs),
user_prefs_(user_prefs),
recommended_prefs_(recommended_prefs) {}
@@ -43,6 +45,7 @@ TestingPrefServiceSimple::TestingPrefServiceSimple()
new TestingPrefStore(),
new TestingPrefStore(),
new TestingPrefStore(),
+ new TestingPrefStore(),
new PrefRegistrySimple(),
new PrefNotifierImpl()) {}
diff --git a/chromium/components/prefs/testing_pref_service.h b/chromium/components/prefs/testing_pref_service.h
index f29e506d853..6f9171740bf 100644
--- a/chromium/components/prefs/testing_pref_service.h
+++ b/chromium/components/prefs/testing_pref_service.h
@@ -40,6 +40,14 @@ class TestingPrefServiceBase : public SuperPrefService {
// preference has been defined previously.
void RemoveManagedPref(const std::string& path);
+ // Similar to the above, but for extension preferences.
+ // Does not really know about extensions and their order of installation.
+ // Useful in tests that only check that a preference is overridden by an
+ // extension.
+ const base::Value* GetExtensionPref(const std::string& path) const;
+ void SetExtensionPref(const std::string& path, base::Value* value);
+ void RemoveExtensionPref(const std::string& path);
+
// Similar to the above, but for user preferences.
const base::Value* GetUserPref(const std::string& path) const;
void SetUserPref(const std::string& path, base::Value* value);
@@ -54,12 +62,12 @@ class TestingPrefServiceBase : public SuperPrefService {
static void HandleReadError(PersistentPrefStore::PrefReadError error) {}
protected:
- TestingPrefServiceBase(
- TestingPrefStore* managed_prefs,
- TestingPrefStore* user_prefs,
- TestingPrefStore* recommended_prefs,
- ConstructionPrefRegistry* pref_registry,
- PrefNotifierImpl* pref_notifier);
+ TestingPrefServiceBase(TestingPrefStore* managed_prefs,
+ TestingPrefStore* extension_prefs,
+ TestingPrefStore* user_prefs,
+ TestingPrefStore* recommended_prefs,
+ ConstructionPrefRegistry* pref_registry,
+ PrefNotifierImpl* pref_notifier);
private:
// Reads the value of the preference indicated by |path| from |pref_store|.
@@ -77,6 +85,7 @@ class TestingPrefServiceBase : public SuperPrefService {
// Pointers to the pref stores our value store uses.
scoped_refptr<TestingPrefStore> managed_prefs_;
+ scoped_refptr<TestingPrefStore> extension_prefs_;
scoped_refptr<TestingPrefStore> user_prefs_;
scoped_refptr<TestingPrefStore> recommended_prefs_;
@@ -104,6 +113,7 @@ class TestingPrefServiceSimple
template<>
TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase(
TestingPrefStore* managed_prefs,
+ TestingPrefStore* extension_prefs,
TestingPrefStore* user_prefs,
TestingPrefStore* recommended_prefs,
PrefRegistry* pref_registry,
@@ -134,6 +144,25 @@ void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
}
template <class SuperPrefService, class ConstructionPrefRegistry>
+const base::Value* TestingPrefServiceBase<
+ SuperPrefService,
+ ConstructionPrefRegistry>::GetExtensionPref(const std::string& path) const {
+ return GetPref(extension_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ SetExtensionPref(const std::string& path, base::Value* value) {
+ SetPref(extension_prefs_.get(), path, value);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ RemoveExtensionPref(const std::string& path) {
+ RemovePref(extension_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
const base::Value*
TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetUserPref(
const std::string& path) const {
diff --git a/chromium/components/previews/core/previews_black_list.h b/chromium/components/previews/core/previews_black_list.h
index e9151da625c..6abac75b2e7 100644
--- a/chromium/components/previews/core/previews_black_list.h
+++ b/chromium/components/previews/core/previews_black_list.h
@@ -49,7 +49,9 @@ enum class PreviewsEligibilityReason {
NETWORK_QUALITY_UNAVAILABLE = 6,
// The network was fast enough to not warrant previews.
NETWORK_NOT_SLOW = 7,
- LAST = 8,
+ // If the page was reloaded, the user should not be shown an offline preview.
+ RELOAD_DISALLOWED_FOR_OFFLINE = 8,
+ LAST = 9,
};
// Manages the state of black listed domains for the previews experiment. Loads
diff --git a/chromium/components/previews/core/previews_experiments.cc b/chromium/components/previews/core/previews_experiments.cc
index fc7922cca8a..3806bc9c8e8 100644
--- a/chromium/components/previews/core/previews_experiments.cc
+++ b/chromium/components/previews/core/previews_experiments.cc
@@ -89,81 +89,72 @@ namespace params {
size_t MaxStoredHistoryLengthForPerHostBlackList() {
std::string param_value = ParamValue(kMaxStoredHistoryLengthPerHost);
size_t history_length;
- if (!base::StringToSizeT(param_value, &history_length)) {
- return 4;
- }
+ if (!base::StringToSizeT(param_value, &history_length))
+ history_length = 4;
return history_length;
}
size_t MaxStoredHistoryLengthForHostIndifferentBlackList() {
std::string param_value = ParamValue(kMaxStoredHistoryLengthHostIndifferent);
size_t history_length;
- if (!base::StringToSizeT(param_value, &history_length)) {
- return 10;
- }
+ if (!base::StringToSizeT(param_value, &history_length))
+ history_length = 10;
return history_length;
}
size_t MaxInMemoryHostsInBlackList() {
std::string param_value = ParamValue(kMaxHostsInBlackList);
size_t max_hosts;
- if (!base::StringToSizeT(param_value, &max_hosts)) {
- return 100;
- }
+ if (!base::StringToSizeT(param_value, &max_hosts))
+ max_hosts = 100;
return max_hosts;
}
int PerHostBlackListOptOutThreshold() {
std::string param_value = ParamValue(kPerHostOptOutThreshold);
int opt_out_threshold;
- if (!base::StringToInt(param_value, &opt_out_threshold)) {
- return 2;
- }
+ if (!base::StringToInt(param_value, &opt_out_threshold))
+ opt_out_threshold = 2;
return opt_out_threshold;
}
int HostIndifferentBlackListOptOutThreshold() {
std::string param_value = ParamValue(kHostIndifferentOptOutThreshold);
int opt_out_threshold;
- if (!base::StringToInt(param_value, &opt_out_threshold)) {
- return 4;
- }
+ if (!base::StringToInt(param_value, &opt_out_threshold))
+ opt_out_threshold = 4;
return opt_out_threshold;
}
base::TimeDelta PerHostBlackListDuration() {
std::string param_value = ParamValue(kPerHostBlackListDurationInDays);
int duration;
- if (!base::StringToInt(param_value, &duration)) {
- return base::TimeDelta::FromDays(30);
- }
+ if (!base::StringToInt(param_value, &duration))
+ duration = 30;
return base::TimeDelta::FromDays(duration);
}
base::TimeDelta HostIndifferentBlackListPerHostDuration() {
std::string param_value = ParamValue(kHostIndifferentBlackListDurationInDays);
int duration;
- if (!base::StringToInt(param_value, &duration)) {
- return base::TimeDelta::FromDays(365 * 100);
- }
+ if (!base::StringToInt(param_value, &duration))
+ duration = 365 * 100;
return base::TimeDelta::FromDays(duration);
}
base::TimeDelta SingleOptOutDuration() {
std::string param_value = ParamValue(kSingleOptOutDurationInSeconds);
int duration;
- if (!base::StringToInt(param_value, &duration)) {
- return base::TimeDelta::FromSeconds(60 * 5);
- }
+ if (!base::StringToInt(param_value, &duration))
+ duration = 60 * 5;
return base::TimeDelta::FromSeconds(duration);
}
base::TimeDelta OfflinePreviewFreshnessDuration() {
std::string param_value = ParamValue(kOfflinePreviewFreshnessDurationInDays);
int duration;
- if (!base::StringToInt(param_value, &duration)) {
- return base::TimeDelta::FromDays(7);
- }
+ if (!base::StringToInt(param_value, &duration))
+ duration = 7;
return base::TimeDelta::FromDays(duration);
}
diff --git a/chromium/components/previews/core/previews_io_data.cc b/chromium/components/previews/core/previews_io_data.cc
index ed9f784d7cb..9c333b2a6f2 100644
--- a/chromium/components/previews/core/previews_io_data.cc
+++ b/chromium/components/previews/core/previews_io_data.cc
@@ -9,12 +9,13 @@
#include "base/files/file_path.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
#include "base/sequenced_task_runner.h"
#include "base/time/default_clock.h"
#include "components/previews/core/previews_black_list.h"
#include "components/previews/core/previews_opt_out_store.h"
#include "components/previews/core/previews_ui_service.h"
+#include "net/base/load_flags.h"
#include "net/nqe/network_quality_estimator.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
@@ -117,6 +118,15 @@ bool PreviewsIOData::ShouldAllowPreview(const net::URLRequest& request,
type);
return false;
}
+ // LOAD_VALIDATE_CACHE or LOAD_BYPASS_CACHE mean the user reloaded the page.
+ // If this is a query for offline previews, reloads should be disallowed.
+ if (type == PreviewsType::OFFLINE &&
+ request.load_flags() &
+ (net::LOAD_VALIDATE_CACHE | net::LOAD_BYPASS_CACHE)) {
+ LogPreviewsEligibilityReason(
+ PreviewsEligibilityReason::RELOAD_DISALLOWED_FOR_OFFLINE, type);
+ return false;
+ }
LogPreviewsEligibilityReason(PreviewsEligibilityReason::ALLOWED, type);
return true;
}
diff --git a/chromium/components/previews/core/previews_io_data_unittest.cc b/chromium/components/previews/core/previews_io_data_unittest.cc
index cb8d3aa1944..39a8925e618 100644
--- a/chromium/components/previews/core/previews_io_data_unittest.cc
+++ b/chromium/components/previews/core/previews_io_data_unittest.cc
@@ -25,6 +25,7 @@
#include "components/previews/core/previews_opt_out_store.h"
#include "components/previews/core/previews_ui_service.h"
#include "components/variations/variations_associated_data.h"
+#include "net/base/load_flags.h"
#include "net/nqe/effective_connection_type.h"
#include "net/nqe/network_quality_estimator_test_util.h"
#include "net/url_request/url_request.h"
@@ -212,12 +213,21 @@ TEST_F(PreviewsIODataTest, TestShouldAllowPreview) {
network_quality_estimator.set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+ request->SetLoadFlags(net::LOAD_BYPASS_CACHE);
+ EXPECT_FALSE(io_data()->ShouldAllowPreview(*request, PreviewsType::OFFLINE));
+ histogram_tester.ExpectBucketCount(
+ "Previews.EligibilityReason.Offline",
+ static_cast<int>(
+ PreviewsEligibilityReason::RELOAD_DISALLOWED_FOR_OFFLINE),
+ 1);
+
+ request->SetLoadFlags(0);
EXPECT_TRUE(io_data()->ShouldAllowPreview(*request, PreviewsType::OFFLINE));
histogram_tester.ExpectBucketCount(
"Previews.EligibilityReason.Offline",
static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
- histogram_tester.ExpectTotalCount("Previews.EligibilityReason.Offline", 6);
+ histogram_tester.ExpectTotalCount("Previews.EligibilityReason.Offline", 7);
variations::testing::ClearAllVariationParams();
}
diff --git a/chromium/components/previews/core/previews_opt_out_store.h b/chromium/components/previews/core/previews_opt_out_store.h
index 7f81e3a50d7..7b352d59aca 100644
--- a/chromium/components/previews/core/previews_opt_out_store.h
+++ b/chromium/components/previews/core/previews_opt_out_store.h
@@ -17,8 +17,6 @@
#include "components/previews/core/previews_black_list_item.h"
#include "components/previews/core/previews_experiments.h"
-class GURL;
-
namespace previews {
typedef std::unordered_map<std::string, std::unique_ptr<PreviewsBlackListItem>>
diff --git a/chromium/components/printing/browser/print_manager_utils.cc b/chromium/components/printing/browser/print_manager_utils.cc
index 78112825b43..ec383a232d2 100644
--- a/chromium/components/printing/browser/print_manager_utils.cc
+++ b/chromium/components/printing/browser/print_manager_utils.cc
@@ -25,6 +25,7 @@ void RenderParamsFromPrintSettings(const PrintSettings& settings,
params->scale_factor = settings.scale_factor();
// Currently hardcoded at 72dpi. See PrintSettings' constructor.
params->desired_dpi = settings.desired_dpi();
+ params->rasterize_pdf = settings.rasterize_pdf();
// Always use an invalid cookie.
params->document_cookie = 0;
params->selection_only = settings.selection_only();
diff --git a/chromium/components/printing/common/print_messages.cc b/chromium/components/printing/common/print_messages.cc
index 5820bb15838..a9291a2b6cd 100644
--- a/chromium/components/printing/common/print_messages.cc
+++ b/chromium/components/printing/common/print_messages.cc
@@ -42,26 +42,27 @@ namespace IPC {
} // namespace IPC
PrintMsg_Print_Params::PrintMsg_Print_Params()
- : page_size(),
- content_size(),
- printable_area(),
- margin_top(0),
- margin_left(0),
- dpi(0),
- scale_factor(1.0f),
- desired_dpi(0),
- document_cookie(0),
- selection_only(false),
- supports_alpha_blend(false),
- preview_ui_id(-1),
- preview_request_id(0),
- is_first_request(false),
- print_scaling_option(blink::WebPrintScalingOptionSourceSize),
- print_to_pdf(false),
- display_header_footer(false),
- title(),
- url(),
- should_print_backgrounds(false) {}
+ : page_size(),
+ content_size(),
+ printable_area(),
+ margin_top(0),
+ margin_left(0),
+ dpi(0),
+ scale_factor(1.0f),
+ desired_dpi(0),
+ rasterize_pdf(false),
+ document_cookie(0),
+ selection_only(false),
+ supports_alpha_blend(false),
+ preview_ui_id(-1),
+ preview_request_id(0),
+ is_first_request(false),
+ print_scaling_option(blink::WebPrintScalingOptionSourceSize),
+ print_to_pdf(false),
+ display_header_footer(false),
+ title(),
+ url(),
+ should_print_backgrounds(false) {}
PrintMsg_Print_Params::PrintMsg_Print_Params(
const PrintMsg_Print_Params& other) = default;
@@ -77,6 +78,7 @@ void PrintMsg_Print_Params::Reset() {
dpi = 0;
scale_factor = 1.0f;
desired_dpi = 0;
+ rasterize_pdf = false;
document_cookie = 0;
selection_only = false;
supports_alpha_blend = false;
diff --git a/chromium/components/printing/common/print_messages.h b/chromium/components/printing/common/print_messages.h
index 3d1e71ae1a0..3679499793d 100644
--- a/chromium/components/printing/common/print_messages.h
+++ b/chromium/components/printing/common/print_messages.h
@@ -47,6 +47,7 @@ struct PrintMsg_Print_Params {
double dpi;
double scale_factor;
int desired_dpi;
+ bool rasterize_pdf;
int document_cookie;
bool selection_only;
bool supports_alpha_blend;
@@ -128,6 +129,9 @@ IPC_STRUCT_TRAITS_BEGIN(PrintMsg_Print_Params)
// Desired apparent dpi on paper.
IPC_STRUCT_TRAITS_MEMBER(desired_dpi)
+ // Whether to rasterize a PDF for printing
+ IPC_STRUCT_TRAITS_MEMBER(rasterize_pdf)
+
// Cookie for the document to ensure correctness.
IPC_STRUCT_TRAITS_MEMBER(document_cookie)
@@ -406,12 +410,12 @@ IPC_SYNC_MESSAGE_ROUTED1_1(PrintHostMsg_ScriptedPrint,
// Asks the browser to create a temporary file for the renderer to fill
// in resulting PdfMetafileSkia in printing.
IPC_SYNC_MESSAGE_CONTROL1_2(PrintHostMsg_AllocateTempFileForPrinting,
- int /* render_view_id */,
+ int /* render_frame_id */,
base::FileDescriptor /* temp file fd */,
- int /* fd in browser*/) // Used only by Chrome OS.
+ int /* fd in browser*/)
IPC_MESSAGE_CONTROL2(PrintHostMsg_TempFileForPrintingWritten,
- int /* render_view_id */,
- int /* fd in browser */) // Used only by Chrome OS.
+ int /* render_frame_id */,
+ int /* fd in browser */)
#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 c72217ae3ca..2ac27ae1814 100644
--- a/chromium/components/printing/renderer/print_web_view_helper.cc
+++ b/chromium/components/printing/renderer/print_web_view_helper.cc
@@ -16,6 +16,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/process/process_handle.h"
#include "base/single_thread_task_runner.h"
@@ -285,6 +286,7 @@ void ComputeWebKitPrintParamsInDesiredDpi(
blink::WebPrintParams* webkit_print_params) {
int dpi = GetDPI(&print_params);
webkit_print_params->printerDPI = dpi;
+ webkit_print_params->rasterizePDF = print_params.rasterize_pdf;
webkit_print_params->printScalingOption = print_params.print_scaling_option;
webkit_print_params->printContentArea.width = ConvertUnit(
@@ -345,6 +347,7 @@ bool PrintingFrameHasPageSizeStyle(blink::WebLocalFrame* frame,
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
+#if BUILDFLAG(ENABLE_PRINTING)
// Disable scaling when either:
// - The PDF specifies disabling scaling.
// - All the pages in the PDF are the same size,
@@ -387,6 +390,7 @@ bool PDFShouldDisableScaling(blink::WebLocalFrame* frame,
return PDFShouldDisableScalingBasedOnPreset(preset_options, params,
ignore_page_size);
}
+#endif
#if BUILDFLAG(ENABLE_BASIC_PRINTING)
MarginType GetMarginsForPdf(blink::WebLocalFrame* frame,
@@ -760,7 +764,7 @@ void PrepareFrameAndViewForPrint::ResizeForPrinting() {
blink::WebView* web_view = frame_.view();
if (blink::WebFrame* web_frame = web_view->mainFrame()) {
if (web_frame->isWebLocalFrame())
- prev_scroll_offset_ = web_frame->scrollOffset();
+ prev_scroll_offset_ = web_frame->getScrollOffset();
}
prev_view_size_ = web_view->size();
@@ -918,8 +922,9 @@ void PrintWebViewHelper::DisablePreview() {
g_is_preview_enabled = false;
}
-bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed(blink::WebFrame* frame,
- bool user_initiated) {
+bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed(
+ blink::WebLocalFrame* frame,
+ bool user_initiated) {
if (!is_printing_enabled_ || !delegate_->IsScriptedPrintEnabled())
return false;
@@ -1847,11 +1852,16 @@ void PrintWebViewHelper::PrintPageInternal(
int dpi = static_cast<int>(params.params.dpi);
// Calculate the actual page size and content area in dpi.
if (page_size_in_dpi) {
+ // Windows uses this for the actual page size. We have scaled page size
+ // to get blink to reflow the page, so scale it back to the real size
+ // before returning it.
*page_size_in_dpi =
gfx::Size(static_cast<int>(ConvertUnitDouble(page_size.width(),
- kPointsPerInch, dpi)),
+ kPointsPerInch, dpi) *
+ css_scale_factor),
static_cast<int>(ConvertUnitDouble(page_size.height(),
- kPointsPerInch, dpi)));
+ kPointsPerInch, dpi) *
+ css_scale_factor));
}
if (content_area_in_dpi) {
@@ -2306,7 +2316,8 @@ void PrintWebViewHelper::SetPrintPagesParams(
PrintWebViewHelper::ScriptingThrottler::ScriptingThrottler() : count_(0) {
}
-bool PrintWebViewHelper::ScriptingThrottler::IsAllowed(blink::WebFrame* frame) {
+bool PrintWebViewHelper::ScriptingThrottler::IsAllowed(
+ blink::WebLocalFrame* frame) {
const int kMinSecondsToIgnoreJavascriptInitiatedPrint = 2;
const int kMaxSecondsToIgnoreJavascriptInitiatedPrint = 32;
bool too_frequent = false;
diff --git a/chromium/components/printing/renderer/print_web_view_helper.h b/chromium/components/printing/renderer/print_web_view_helper.h
index 0730f76303b..e603411a878 100644
--- a/chromium/components/printing/renderer/print_web_view_helper.h
+++ b/chromium/components/printing/renderer/print_web_view_helper.h
@@ -352,7 +352,7 @@ class PrintWebViewHelper
// Return true if script initiated printing is currently
// allowed. |user_initiated| should be true when a user event triggered the
// script, most likely by pressing a print button on the page.
- bool IsScriptInitiatedPrintAllowed(blink::WebFrame* frame,
+ bool IsScriptInitiatedPrintAllowed(blink::WebLocalFrame* frame,
bool user_initiated);
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
@@ -510,7 +510,7 @@ class PrintWebViewHelper
ScriptingThrottler();
// Returns false if script initiated printing occurs too often.
- bool IsAllowed(blink::WebFrame* frame);
+ bool IsAllowed(blink::WebLocalFrame* frame);
// Reset the counter for script initiated printing.
// Scripted printing will be allowed to continue.
diff --git a/chromium/components/proximity_auth/BUILD.gn b/chromium/components/proximity_auth/BUILD.gn
index 2476b344d31..9e19dc5c92f 100644
--- a/chromium/components/proximity_auth/BUILD.gn
+++ b/chromium/components/proximity_auth/BUILD.gn
@@ -6,21 +6,15 @@ import("//testing/test.gni")
static_library("proximity_auth") {
sources = [
+ "authenticator.cc",
"authenticator.h",
"bluetooth_connection.cc",
"bluetooth_connection.h",
"bluetooth_connection_finder.cc",
"bluetooth_connection_finder.h",
- "bluetooth_throttler.h",
- "bluetooth_throttler_impl.cc",
- "bluetooth_throttler_impl.h",
"bluetooth_util.cc",
"bluetooth_util.h",
"bluetooth_util_chromeos.cc",
- "connection.cc",
- "connection.h",
- "connection_finder.h",
- "connection_observer.h",
"cryptauth_enroller_factory_impl.cc",
"cryptauth_enroller_factory_impl.h",
"device_to_device_authenticator.cc",
@@ -46,8 +40,6 @@ static_library("proximity_auth") {
"proximity_monitor_impl.cc",
"proximity_monitor_impl.h",
"proximity_monitor_observer.h",
- "remote_device.cc",
- "remote_device.h",
"remote_device_life_cycle.h",
"remote_device_life_cycle_impl.cc",
"remote_device_life_cycle_impl.h",
@@ -65,15 +57,14 @@ static_library("proximity_auth") {
"throttled_bluetooth_connection_finder.h",
"unlock_manager.cc",
"unlock_manager.h",
- "wire_message.cc",
- "wire_message.h",
]
deps = [
"//base",
+ "//components/cryptauth",
+ "//components/cryptauth/ble",
"//components/prefs",
"//components/proximity_auth/ble",
- "//components/proximity_auth/cryptauth",
"//components/proximity_auth/logging",
"//components/signin/core/account_id:account_id",
"//device/bluetooth",
@@ -83,10 +74,7 @@ static_library("proximity_auth") {
# TODO(https://crbug.com/562683): This whitelists a circular include
# dependency between this target and the following targets which should not
# exist.
- allow_circular_includes_from = [
- "//components/proximity_auth/ble",
- "//components/proximity_auth/cryptauth",
- ]
+ allow_circular_includes_from = [ "//components/proximity_auth/ble" ]
if (is_chromeos) {
deps += [ "//chromeos" ]
@@ -99,14 +87,10 @@ static_library("test_support") {
sources = [
"device_to_device_responder_operations.cc",
"device_to_device_responder_operations.h",
- "fake_connection.cc",
- "fake_connection.h",
"fake_secure_context.cc",
"fake_secure_context.h",
"mock_proximity_auth_client.cc",
"mock_proximity_auth_client.h",
- "proximity_auth_test_util.cc",
- "proximity_auth_test_util.h",
]
public_deps = [
@@ -115,7 +99,7 @@ static_library("test_support") {
deps = [
"//base",
- "//components/proximity_auth/cryptauth:test_support",
+ "//components/cryptauth:test_support",
"//components/proximity_auth/logging",
"//testing/gmock",
]
@@ -126,8 +110,6 @@ source_set("unit_tests") {
sources = [
"bluetooth_connection_finder_unittest.cc",
"bluetooth_connection_unittest.cc",
- "bluetooth_throttler_impl_unittest.cc",
- "connection_unittest.cc",
"device_to_device_authenticator_unittest.cc",
"device_to_device_operations_unittest.cc",
"device_to_device_secure_context_unittest.cc",
@@ -140,7 +122,6 @@ source_set("unit_tests") {
"remote_status_update_unittest.cc",
"throttled_bluetooth_connection_finder_unittest.cc",
"unlock_manager_unittest.cc",
- "wire_message_unittest.cc",
]
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
@@ -150,10 +131,10 @@ source_set("unit_tests") {
":test_support",
"//base",
"//base/test:test_support",
+ "//components/cryptauth:test_support",
+ "//components/cryptauth:unit_tests",
"//components/prefs:test_support",
"//components/proximity_auth/ble:unit_tests",
- "//components/proximity_auth/cryptauth:test_support",
- "//components/proximity_auth/cryptauth:unit_tests",
"//components/proximity_auth/logging",
"//components/proximity_auth/logging:unit_tests",
"//device/bluetooth:mocks",
diff --git a/chromium/components/proximity_auth/ble/BUILD.gn b/chromium/components/proximity_auth/ble/BUILD.gn
index cb96bb9c850..4641f5ab74f 100644
--- a/chromium/components/proximity_auth/ble/BUILD.gn
+++ b/chromium/components/proximity_auth/ble/BUILD.gn
@@ -6,30 +6,20 @@ import("//testing/test.gni")
static_library("ble") {
sources = [
- "bluetooth_low_energy_characteristics_finder.cc",
- "bluetooth_low_energy_characteristics_finder.h",
"bluetooth_low_energy_connection.cc",
"bluetooth_low_energy_connection.h",
"bluetooth_low_energy_connection_finder.cc",
"bluetooth_low_energy_connection_finder.h",
"bluetooth_low_energy_device_whitelist.cc",
"bluetooth_low_energy_device_whitelist.h",
- "bluetooth_low_energy_weave_client_connection.cc",
- "bluetooth_low_energy_weave_client_connection.h",
- "bluetooth_low_energy_weave_defines.h",
- "bluetooth_low_energy_weave_packet_generator.cc",
- "bluetooth_low_energy_weave_packet_generator.h",
- "bluetooth_low_energy_weave_packet_receiver.cc",
- "bluetooth_low_energy_weave_packet_receiver.h",
- "fake_wire_message.cc",
- "fake_wire_message.h",
"pref_names.cc",
"pref_names.h",
- "remote_attribute.h",
]
deps = [
"//base",
+ "//components/cryptauth",
+ "//components/cryptauth/ble",
"//components/prefs",
"//components/proximity_auth/logging",
@@ -42,20 +32,16 @@ static_library("ble") {
]
public_deps = [
- "//components/proximity_auth/cryptauth/proto",
+ "//components/cryptauth/proto",
]
}
source_set("unit_tests") {
testonly = true
sources = [
- "bluetooth_low_energy_characteristics_finder_unittest.cc",
"bluetooth_low_energy_connection_finder_unittest.cc",
"bluetooth_low_energy_connection_unittest.cc",
"bluetooth_low_energy_device_whitelist_unittest.cc",
- "bluetooth_low_energy_weave_client_connection_unittest.cc",
- "bluetooth_low_energy_weave_packet_generator_unittest.cc",
- "bluetooth_low_energy_weave_packet_receiver_unittest.cc",
]
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
@@ -63,6 +49,9 @@ source_set("unit_tests") {
deps = [
":ble",
"//base/test:test_support",
+ "//components/cryptauth",
+ "//components/cryptauth:test_support",
+ "//components/cryptauth/ble:ble",
"//components/prefs:test_support",
"//components/proximity_auth:test_support",
"//device/bluetooth:mocks",
@@ -71,6 +60,6 @@ source_set("unit_tests") {
]
public_deps = [
- "//components/proximity_auth/cryptauth/proto",
+ "//components/cryptauth/proto",
]
}
diff --git a/chromium/components/proximity_auth/webui/BUILD.gn b/chromium/components/proximity_auth/webui/BUILD.gn
index 69b5253a086..54ff59e0879 100644
--- a/chromium/components/proximity_auth/webui/BUILD.gn
+++ b/chromium/components/proximity_auth/webui/BUILD.gn
@@ -17,10 +17,10 @@ static_library("webui") {
deps = [
"//base",
"//base:i18n",
+ "//components/cryptauth",
"//components/prefs",
"//components/proximity_auth",
"//components/proximity_auth/ble",
- "//components/proximity_auth/cryptauth",
"//components/proximity_auth/logging",
"//components/resources",
"//content/public/browser",
diff --git a/chromium/components/rappor/BUILD.gn b/chromium/components/rappor/BUILD.gn
index be595ad2c3b..66b87449b24 100644
--- a/chromium/components/rappor/BUILD.gn
+++ b/chromium/components/rappor/BUILD.gn
@@ -14,25 +14,23 @@ static_library("rappor") {
"rappor_metric.cc",
"rappor_metric.h",
"rappor_parameters.cc",
- "rappor_parameters.h",
"rappor_pref_names.cc",
"rappor_pref_names.h",
"rappor_prefs.cc",
"rappor_prefs.h",
- "rappor_service.cc",
- "rappor_service.h",
+ "rappor_service_impl.cc",
+ "rappor_service_impl.h",
"rappor_utils.cc",
- "rappor_utils.h",
"reports.cc",
"reports.h",
"sample.cc",
- "sample.h",
"sampler.cc",
"sampler.h",
]
public_deps = [
"//components/rappor/proto",
+ "//components/rappor/public",
]
deps = [
diff --git a/chromium/components/rappor/byte_vector_utils.h b/chromium/components/rappor/byte_vector_utils.h
index 71ce88f4943..339b3da10ca 100644
--- a/chromium/components/rappor/byte_vector_utils.h
+++ b/chromium/components/rappor/byte_vector_utils.h
@@ -12,7 +12,7 @@
#include "base/macros.h"
#include "base/strings/string_piece.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
#include "crypto/hmac.h"
namespace rappor {
diff --git a/chromium/components/rappor/log_uploader.h b/chromium/components/rappor/log_uploader.h
index a6dc8bada08..55e53202ef7 100644
--- a/chromium/components/rappor/log_uploader.h
+++ b/chromium/components/rappor/log_uploader.h
@@ -24,9 +24,9 @@ class URLFetcher;
namespace rappor {
-// Uploads logs from RapporService. Logs are passed in via QueueLog(), stored
-// internally, and uploaded one at a time. A queued log will be uploaded at a
-// fixed interval after the successful upload of the previous logs. If an
+// Uploads logs from RapporServiceImpl. Logs are passed in via QueueLog(),
+// stored internally, and uploaded one at a time. A queued log will be uploaded
+// at a fixed interval after the successful upload of the previous logs. If an
// upload fails, the uploader will keep retrying the upload with an exponential
// backoff interval.
class LogUploader : public net::URLFetcherDelegate,
diff --git a/chromium/components/task_scheduler_util/BUILD.gn b/chromium/components/rappor/public/BUILD.gn
index 6c743a5c271..7e766c616ac 100644
--- a/chromium/components/task_scheduler_util/BUILD.gn
+++ b/chromium/components/rappor/public/BUILD.gn
@@ -2,10 +2,12 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-static_library("task_scheduler_util") {
+source_set("public") {
sources = [
- "initialization_util.cc",
- "initialization_util.h",
+ "rappor_parameters.h",
+ "rappor_service.h",
+ "rappor_utils.h",
+ "sample.h",
]
deps = [
diff --git a/chromium/components/rappor/rappor_parameters.h b/chromium/components/rappor/public/rappor_parameters.h
index 1696269f4c0..0497615fba7 100644
--- a/chromium/components/rappor/rappor_parameters.h
+++ b/chromium/components/rappor/public/rappor_parameters.h
@@ -1,9 +1,9 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// 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_RAPPOR_RAPPOR_PARAMETERS_H_
-#define COMPONENTS_RAPPOR_RAPPOR_PARAMETERS_H_
+#ifndef COMPONENTS_RAPPOR_PUBLIC_RAPPOR_PARAMETERS_H_
+#define COMPONENTS_RAPPOR_PUBLIC_RAPPOR_PARAMETERS_H_
#include <string>
@@ -40,16 +40,15 @@ enum RapporType {
};
enum Probability {
- PROBABILITY_100, // 100%
- PROBABILITY_75, // 75%
- PROBABILITY_50, // 50%
- PROBABILITY_25, // 25%
- PROBABILITY_0, // 0%
+ PROBABILITY_100, // 100%
+ PROBABILITY_75, // 75%
+ PROBABILITY_50, // 50%
+ PROBABILITY_25, // 25%
+ PROBABILITY_0, // 0%
};
-
// A metric is reported when its reporting group is in the set of groups
-// passed in to RapporService::Start()
+// passed in to RapporServiceImpl::Start()
enum RecordingGroup {
// Metrics for UMA users.
UMA_RAPPOR_GROUP = 1 << 0,
@@ -57,7 +56,6 @@ enum RecordingGroup {
SAFEBROWSING_RAPPOR_GROUP = 1 << 1,
};
-
// An object describing noise probabilities for a noise level
struct NoiseParameters {
// The probability that a bit will be redacted with fake data. This
@@ -106,68 +104,80 @@ namespace internal {
const NoiseParameters kNoiseParametersForLevel[NUM_NOISE_LEVELS] = {
// NO_NOISE
{
- rappor::PROBABILITY_0 /* Fake data probability */,
- rappor::PROBABILITY_0 /* Fake one probability */,
- rappor::PROBABILITY_100 /* One coin probability */,
- rappor::PROBABILITY_0 /* Zero coin probability */,
+ rappor::PROBABILITY_0 /* Fake data probability */,
+ rappor::PROBABILITY_0 /* Fake one probability */,
+ rappor::PROBABILITY_100 /* One coin probability */,
+ rappor::PROBABILITY_0 /* Zero coin probability */,
},
// NORMAL_NOISE
{
- rappor::PROBABILITY_50 /* Fake data probability */,
- rappor::PROBABILITY_50 /* Fake one probability */,
- rappor::PROBABILITY_75 /* One coin probability */,
- rappor::PROBABILITY_25 /* Zero coin probability */,
+ rappor::PROBABILITY_50 /* Fake data probability */,
+ rappor::PROBABILITY_50 /* Fake one probability */,
+ rappor::PROBABILITY_75 /* One coin probability */,
+ rappor::PROBABILITY_25 /* Zero coin probability */,
},
// SPARSE_NOISE
{
- rappor::PROBABILITY_25 /* Fake data probability */,
- rappor::PROBABILITY_50 /* Fake one probability */,
- rappor::PROBABILITY_75 /* One coin probability */,
- rappor::PROBABILITY_25 /* Zero coin probability */,
+ rappor::PROBABILITY_25 /* Fake data probability */,
+ rappor::PROBABILITY_50 /* Fake one probability */,
+ rappor::PROBABILITY_75 /* One coin probability */,
+ rappor::PROBABILITY_25 /* Zero coin probability */,
},
};
const RapporParameters kRapporParametersForType[NUM_RAPPOR_TYPES] = {
// UMA_RAPPOR_TYPE
- {128 /* Num cohorts */,
- 4 /* Bloom filter size bytes */,
- 2 /* Bloom filter hash count */,
- rappor::NORMAL_NOISE /* Noise level */,
- UMA_RAPPOR_GROUP /* Recording group */},
+ {
+ 128 /* Num cohorts */,
+ 4 /* Bloom filter size bytes */,
+ 2 /* Bloom filter hash count */,
+ rappor::NORMAL_NOISE /* Noise level */,
+ UMA_RAPPOR_GROUP /* Recording group */
+ },
// SAFEBROWSING_RAPPOR_TYPE
- {128 /* Num cohorts */,
- 1 /* Bloom filter size bytes */,
- 2 /* Bloom filter hash count */,
- rappor::NORMAL_NOISE /* Noise level */,
- SAFEBROWSING_RAPPOR_GROUP /* Recording group */},
+ {
+ 128 /* Num cohorts */,
+ 1 /* Bloom filter size bytes */,
+ 2 /* Bloom filter hash count */,
+ rappor::NORMAL_NOISE /* Noise level */,
+ SAFEBROWSING_RAPPOR_GROUP /* Recording group */
+ },
// ETLD_PLUS_ONE_RAPPOR_TYPE
- {128 /* Num cohorts */,
- 16 /* Bloom filter size bytes */,
- 2 /* Bloom filter hash count */,
- rappor::NORMAL_NOISE /* Noise level */,
- UMA_RAPPOR_GROUP /* Recording group */},
+ {
+ 128 /* Num cohorts */,
+ 16 /* Bloom filter size bytes */,
+ 2 /* Bloom filter hash count */,
+ rappor::NORMAL_NOISE /* Noise level */,
+ UMA_RAPPOR_GROUP /* Recording group */
+ },
// LOW_FREQUENCY_UMA_RAPPOR_TYPE
- {128 /* Num cohorts */,
- 4 /* Bloom filter size bytes */,
- 2 /* Bloom filter hash count */,
- rappor::SPARSE_NOISE /* Noise level */,
- UMA_RAPPOR_GROUP /* Recording group */},
+ {
+ 128 /* Num cohorts */,
+ 4 /* Bloom filter size bytes */,
+ 2 /* Bloom filter hash count */,
+ rappor::SPARSE_NOISE /* Noise level */,
+ UMA_RAPPOR_GROUP /* Recording group */
+ },
// LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE
- {128 /* Num cohorts */,
- 1 /* Bloom filter size bytes */,
- 2 /* Bloom filter hash count */,
- rappor::SPARSE_NOISE /* Noise level */,
- SAFEBROWSING_RAPPOR_GROUP /* Recording group */},
+ {
+ 128 /* Num cohorts */,
+ 1 /* Bloom filter size bytes */,
+ 2 /* Bloom filter hash count */,
+ rappor::SPARSE_NOISE /* Noise level */,
+ SAFEBROWSING_RAPPOR_GROUP /* Recording group */
+ },
// LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE
- {128 /* Num cohorts */,
- 16 /* Bloom filter size bytes */,
- 2 /* Bloom filter hash count */,
- rappor::SPARSE_NOISE /* Noise level */,
- UMA_RAPPOR_GROUP /* Recording group */},
+ {
+ 128 /* Num cohorts */,
+ 16 /* Bloom filter size bytes */,
+ 2 /* Bloom filter hash count */,
+ rappor::SPARSE_NOISE /* Noise level */,
+ UMA_RAPPOR_GROUP /* Recording group */
+ },
};
} // namespace internal
} // namespace rappor
-#endif // COMPONENTS_RAPPOR_RAPPOR_PARAMETERS_H_
+#endif // COMPONENTS_RAPPOR_PUBLIC_RAPPOR_PARAMETERS_H_
diff --git a/chromium/components/rappor/public/rappor_service.h b/chromium/components/rappor/public/rappor_service.h
new file mode 100644
index 00000000000..73b7eb5e34c
--- /dev/null
+++ b/chromium/components/rappor/public/rappor_service.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_RAPPOR_PUBLIC_RAPPOR_SERVICE_H_
+#define COMPONENTS_RAPPOR_PUBLIC_RAPPOR_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "components/rappor/public/rappor_parameters.h"
+#include "components/rappor/public/sample.h"
+
+namespace rappor {
+
+// This class provides a public interface for recording samples for rappor
+// metrics, which other components can be depended on.
+class RapporService : public base::SupportsWeakPtr<RapporService> {
+ public:
+ // Constructs a Sample object for the caller to record fields in.
+ virtual std::unique_ptr<Sample> CreateSample(RapporType) = 0;
+
+ // Records a Sample of rappor metric specified by |metric_name|.
+ //
+ // example:
+ // std::unique_ptr<Sample> sample =
+ // rappor_service->CreateSample(MY_METRIC_TYPE);
+ // sample->SetStringField("Field1", "some string");
+ // sample->SetFlagsValue("Field2", SOME|FLAGS);
+ // rappor_service->RecordSample("MyMetric", std::move(sample));
+ virtual void RecordSample(const std::string& metric_name,
+ std::unique_ptr<Sample> sample) = 0;
+
+ // Records a sample of the rappor metric specified by |metric_name|.
+ // Creates and initializes the metric, if it doesn't yet exist.
+ virtual void RecordSampleString(const std::string& metric_name,
+ RapporType type,
+ const std::string& sample) = 0;
+};
+
+} // namespace rappor
+
+#endif // COMPONENTS_RAPPOR_PUBLIC_RAPPOR_SERVICE_H_
diff --git a/chromium/components/rappor/rappor_utils.h b/chromium/components/rappor/public/rappor_utils.h
index 8e425665d8e..77d71429bb8 100644
--- a/chromium/components/rappor/rappor_utils.h
+++ b/chromium/components/rappor/public/rappor_utils.h
@@ -1,20 +1,18 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// 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_RAPPOR_RAPPOR_UTILS_H_
-#define COMPONENTS_RAPPOR_RAPPOR_UTILS_H_
+#ifndef COMPONENTS_RAPPOR_PUBLIC_RAPPOR_UTILS_H_
+#define COMPONENTS_RAPPOR_PUBLIC_RAPPOR_UTILS_H_
#include <string>
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/public/rappor_service.h"
class GURL;
namespace rappor {
-class RapporService;
-
// Records a string to a Rappor metric.
// If |rappor_service| is NULL, this call does nothing.
void SampleString(RapporService* rappor_service,
@@ -33,6 +31,11 @@ void SampleDomainAndRegistryFromGURL(RapporService* rappor_service,
const std::string& metric,
const GURL& gurl);
+// Returns NULL if there is no default service.
+RapporService* GetDefaultService();
+
+void SetDefaultServiceAccessor(RapporService* (*getDefaultService)());
+
} // namespace rappor
-#endif // COMPONENTS_RAPPOR_RAPPOR_UTILS_H_
+#endif // COMPONENTS_RAPPOR_PUBLIC_RAPPOR_UTILS_H_
diff --git a/chromium/components/rappor/sample.h b/chromium/components/rappor/public/sample.h
index 3f143f3f1b0..afdfa14b0a4 100644
--- a/chromium/components/rappor/sample.h
+++ b/chromium/components/rappor/public/sample.h
@@ -1,9 +1,9 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// 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_RAPPOR_SAMPLE_H_
-#define COMPONENTS_RAPPOR_SAMPLE_H_
+#ifndef COMPONENTS_RAPPOR_PUBLIC_SAMPLE_H_
+#define COMPONENTS_RAPPOR_PUBLIC_SAMPLE_H_
#include <stddef.h>
#include <stdint.h>
@@ -12,12 +12,12 @@
#include <string>
#include "base/macros.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
namespace rappor {
class RapporReports;
-class RapporService;
+class RapporServiceImpl;
class TestSamplerFactory;
// Sample is a container for information about a single instance of some event
@@ -60,7 +60,7 @@ class Sample {
private:
friend class TestSamplerFactory;
- friend class RapporService;
+ friend class RapporServiceImpl;
friend class TestSample;
// Constructs a sample. Instead of calling this directly, call
@@ -89,4 +89,4 @@ class Sample {
} // namespace rappor
-#endif // COMPONENTS_RAPPOR_SAMPLE_H_
+#endif // COMPONENTS_RAPPOR_PUBLIC_SAMPLE_H_
diff --git a/chromium/components/rappor/rappor_metric.cc b/chromium/components/rappor/rappor_metric.cc
index c2a3cd153fd..ecc6ae3e35a 100644
--- a/chromium/components/rappor/rappor_metric.cc
+++ b/chromium/components/rappor/rappor_metric.cc
@@ -6,7 +6,7 @@
#include "base/logging.h"
#include "base/rand_util.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
#include "components/rappor/reports.h"
namespace rappor {
diff --git a/chromium/components/rappor/rappor_metric.h b/chromium/components/rappor/rappor_metric.h
index 41d69ec023b..6e17b1db40e 100644
--- a/chromium/components/rappor/rappor_metric.h
+++ b/chromium/components/rappor/rappor_metric.h
@@ -12,7 +12,7 @@
#include "base/macros.h"
#include "components/rappor/bloom_filter.h"
#include "components/rappor/byte_vector_utils.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
namespace rappor {
@@ -20,7 +20,7 @@ namespace rappor {
// and generates randomized reports about the collected data.
//
// This class should not be used directly by metrics clients. Record metrics
-// using RapporService::RecordSample instead.
+// using RapporServiceImpl::RecordSample or RapporService::RecordSample instead.
//
// For a full description of the rappor metrics, see
// http://www.chromium.org/developers/design-documents/rappor
diff --git a/chromium/components/rappor/rappor_parameters.cc b/chromium/components/rappor/rappor_parameters.cc
index ddad0486575..7ab8246eb92 100644
--- a/chromium/components/rappor/rappor_parameters.cc
+++ b/chromium/components/rappor/rappor_parameters.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
#include "base/compiler_specific.h"
#include "base/format_macros.h"
diff --git a/chromium/components/rappor/rappor_prefs.cc b/chromium/components/rappor/rappor_prefs.cc
index 20894fdd459..e16fb50154f 100644
--- a/chromium/components/rappor/rappor_prefs.cc
+++ b/chromium/components/rappor/rappor_prefs.cc
@@ -11,7 +11,7 @@
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/rappor/byte_vector_utils.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
#include "components/rappor/rappor_pref_names.h"
namespace rappor {
diff --git a/chromium/components/rappor/rappor_recorder_impl.cc b/chromium/components/rappor/rappor_recorder_impl.cc
index 101c0677034..805e2fdca9b 100644
--- a/chromium/components/rappor/rappor_recorder_impl.cc
+++ b/chromium/components/rappor/rappor_recorder_impl.cc
@@ -4,19 +4,20 @@
#include "components/rappor/rappor_recorder_impl.h"
-#include "components/rappor/rappor_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "base/memory/ptr_util.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
namespace rappor {
-RapporRecorderImpl::RapporRecorderImpl(RapporService* rappor_service)
+RapporRecorderImpl::RapporRecorderImpl(RapporServiceImpl* rappor_service)
: rappor_service_(rappor_service) {}
RapporRecorderImpl::~RapporRecorderImpl() = default;
// static
-void RapporRecorderImpl::Create(RapporService* rappor_service,
+void RapporRecorderImpl::Create(RapporServiceImpl* rappor_service,
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 d558a274c87..103b4cd0358 100644
--- a/chromium/components/rappor/rappor_recorder_impl.h
+++ b/chromium/components/rappor/rappor_recorder_impl.h
@@ -12,16 +12,16 @@ class GURL;
namespace rappor {
-class RapporService;
+class RapporServiceImpl;
// Records aggregate, privacy-preserving samples from the renderers.
// See https://www.chromium.org/developers/design-documents/rappor
class RapporRecorderImpl : public mojom::RapporRecorder {
public:
- explicit RapporRecorderImpl(RapporService* rappor_service);
+ explicit RapporRecorderImpl(RapporServiceImpl* rappor_service);
~RapporRecorderImpl() override;
- static void Create(RapporService* rappor_service,
+ static void Create(RapporServiceImpl* rappor_service,
mojom::RapporRecorderRequest request);
private:
@@ -30,7 +30,7 @@ class RapporRecorderImpl : public mojom::RapporRecorder {
const std::string& sample) override;
void RecordRapporURL(const std::string& metric, const GURL& sample) override;
- RapporService* rappor_service_;
+ RapporServiceImpl* rappor_service_;
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(RapporRecorderImpl);
diff --git a/chromium/components/rappor/rappor_service.cc b/chromium/components/rappor/rappor_service_impl.cc
index 18d57eea21c..19bfc67f3e6 100644
--- a/chromium/components/rappor/rappor_service.cc
+++ b/chromium/components/rappor/rappor_service_impl.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
#include <utility>
@@ -41,8 +41,7 @@ const char kDefaultServerUrl[] = "https://clients4.google.com/rappor";
GURL GetServerUrl() {
std::string server_url = variations::GetVariationParamValue(
- kRapporRolloutFieldTrialName,
- kRapporRolloutServerUrlParam);
+ kRapporRolloutFieldTrialName, kRapporRolloutServerUrlParam);
if (!server_url.empty())
return GURL(server_url);
else
@@ -51,7 +50,7 @@ GURL GetServerUrl() {
} // namespace
-RapporService::RapporService(
+RapporServiceImpl::RapporServiceImpl(
PrefService* pref_service,
const base::Callback<bool(void)> is_incognito_callback)
: pref_service_(pref_service),
@@ -60,33 +59,32 @@ RapporService::RapporService(
daily_event_(pref_service,
prefs::kRapporLastDailySample,
kRapporDailyEventHistogram),
- recording_groups_(0) {
-}
+ recording_groups_(0) {}
-RapporService::~RapporService() {
-}
+RapporServiceImpl::~RapporServiceImpl() {}
-void RapporService::AddDailyObserver(
+void RapporServiceImpl::AddDailyObserver(
std::unique_ptr<metrics::DailyEvent::Observer> observer) {
daily_event_.AddObserver(std::move(observer));
}
-void RapporService::Initialize(net::URLRequestContextGetter* request_context) {
+void RapporServiceImpl::Initialize(
+ net::URLRequestContextGetter* request_context) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!IsInitialized());
const GURL server_url = GetServerUrl();
if (!server_url.is_valid()) {
DVLOG(1) << server_url.spec() << " is invalid. "
- << "RapporService not started.";
+ << "RapporServiceImpl not started.";
return;
}
- DVLOG(1) << "RapporService reporting to " << server_url.spec();
+ DVLOG(1) << "RapporServiceImpl reporting to " << server_url.spec();
InitializeInternal(
base::MakeUnique<LogUploader>(server_url, kMimeType, request_context),
internal::LoadCohort(pref_service_), internal::LoadSecret(pref_service_));
}
-void RapporService::Update(int recording_groups, bool may_upload) {
+void RapporServiceImpl::Update(int recording_groups, bool may_upload) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(IsInitialized());
if (recording_groups_ != recording_groups) {
@@ -95,18 +93,18 @@ void RapporService::Update(int recording_groups, bool may_upload) {
recording_groups_ = 0;
CancelNextLogRotation();
} else if (recording_groups_ == 0) {
- DVLOG(1) << "RapporService started for groups: "
- << recording_groups;
+ DVLOG(1) << "RapporServiceImpl started for groups: " << recording_groups;
recording_groups_ = recording_groups;
ScheduleNextLogRotation(
base::TimeDelta::FromSeconds(kInitialLogIntervalSeconds));
} else {
- DVLOG(1) << "RapporService recording_groups changed:" << recording_groups;
+ DVLOG(1) << "RapporServiceImpl recording_groups changed:"
+ << recording_groups;
recording_groups_ = recording_groups;
}
}
- DVLOG(1) << "RapporService recording_groups=" << recording_groups_
+ DVLOG(1) << "RapporServiceImpl recording_groups=" << recording_groups_
<< " may_upload=" << may_upload;
if (may_upload) {
uploader_->Start();
@@ -116,11 +114,11 @@ void RapporService::Update(int recording_groups, bool may_upload) {
}
// static
-void RapporService::RegisterPrefs(PrefRegistrySimple* registry) {
+void RapporServiceImpl::RegisterPrefs(PrefRegistrySimple* registry) {
internal::RegisterPrefs(registry);
}
-void RapporService::InitializeInternal(
+void RapporServiceImpl::InitializeInternal(
std::unique_ptr<LogUploaderInterface> uploader,
int32_t cohort,
const std::string& secret) {
@@ -132,37 +130,35 @@ void RapporService::InitializeInternal(
secret_ = secret;
}
-void RapporService::CancelNextLogRotation() {
+void RapporServiceImpl::CancelNextLogRotation() {
DCHECK(thread_checker_.CalledOnValidThread());
metrics_map_.clear();
log_rotation_timer_.Stop();
}
-void RapporService::ScheduleNextLogRotation(base::TimeDelta interval) {
- log_rotation_timer_.Start(FROM_HERE,
- interval,
- this,
- &RapporService::OnLogInterval);
+void RapporServiceImpl::ScheduleNextLogRotation(base::TimeDelta interval) {
+ log_rotation_timer_.Start(FROM_HERE, interval, this,
+ &RapporServiceImpl::OnLogInterval);
}
-void RapporService::OnLogInterval() {
+void RapporServiceImpl::OnLogInterval() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(uploader_);
- DVLOG(2) << "RapporService::OnLogInterval";
+ DVLOG(2) << "RapporServiceImpl::OnLogInterval";
daily_event_.CheckInterval();
RapporReports reports;
if (ExportMetrics(&reports)) {
std::string log_text;
bool success = reports.SerializeToString(&log_text);
DCHECK(success);
- DVLOG(1) << "RapporService sending a report of "
+ DVLOG(1) << "RapporServiceImpl sending a report of "
<< reports.report_size() << " value(s).";
uploader_->QueueLog(log_text);
}
ScheduleNextLogRotation(base::TimeDelta::FromSeconds(kLogIntervalSeconds));
}
-bool RapporService::ExportMetrics(RapporReports* reports) {
+bool RapporServiceImpl::ExportMetrics(RapporReports* reports) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_GE(cohort_, 0);
reports->set_cohort(cohort_);
@@ -184,11 +180,11 @@ bool RapporService::ExportMetrics(RapporReports* reports) {
return reports->report_size() > 0;
}
-bool RapporService::IsInitialized() const {
+bool RapporServiceImpl::IsInitialized() const {
return cohort_ >= 0;
}
-bool RapporService::RecordingAllowed(const RapporParameters& parameters) {
+bool RapporServiceImpl::RecordingAllowed(const RapporParameters& parameters) {
// Skip recording in incognito mode.
if (is_incognito_callback_.Run()) {
DVLOG(2) << "Metric not logged due to incognito mode.";
@@ -196,31 +192,30 @@ bool RapporService::RecordingAllowed(const RapporParameters& parameters) {
}
// Skip this metric if its recording_group is not enabled.
if (!(recording_groups_ & parameters.recording_group)) {
- DVLOG(2) << "Metric not logged due to recording_group "
- << recording_groups_ << " < " << parameters.recording_group;
+ DVLOG(2) << "Metric not logged due to recording_group " << recording_groups_
+ << " < " << parameters.recording_group;
return false;
}
return true;
}
-void RapporService::RecordSample(const std::string& metric_name,
- RapporType type,
- const std::string& sample) {
+void RapporServiceImpl::RecordSampleString(const std::string& metric_name,
+ RapporType type,
+ const std::string& sample) {
DCHECK(thread_checker_.CalledOnValidThread());
// Ignore the sample if the service hasn't started yet.
if (!IsInitialized())
return;
DCHECK_LT(type, NUM_RAPPOR_TYPES);
const RapporParameters& parameters = internal::kRapporParametersForType[type];
- DVLOG(2) << "Recording sample \"" << sample
- << "\" for metric \"" << metric_name
- << "\" of type: " << type;
+ DVLOG(2) << "Recording sample \"" << sample << "\" for metric \""
+ << metric_name << "\" of type: " << type;
RecordSampleInternal(metric_name, parameters, sample);
}
-void RapporService::RecordSampleInternal(const std::string& metric_name,
- const RapporParameters& parameters,
- const std::string& sample) {
+void RapporServiceImpl::RecordSampleInternal(const std::string& metric_name,
+ const RapporParameters& parameters,
+ const std::string& sample) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(IsInitialized());
if (!RecordingAllowed(parameters))
@@ -229,8 +224,9 @@ void RapporService::RecordSampleInternal(const std::string& metric_name,
metric->AddSample(sample);
}
-RapporMetric* RapporService::LookUpMetric(const std::string& metric_name,
- const RapporParameters& parameters) {
+RapporMetric* RapporServiceImpl::LookUpMetric(
+ const std::string& metric_name,
+ const RapporParameters& parameters) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(IsInitialized());
auto it = metrics_map_.find(metric_name);
@@ -245,15 +241,15 @@ RapporMetric* RapporService::LookUpMetric(const std::string& metric_name,
return new_metric;
}
-std::unique_ptr<Sample> RapporService::CreateSample(RapporType type) {
+std::unique_ptr<Sample> RapporServiceImpl::CreateSample(RapporType type) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(IsInitialized());
return base::WrapUnique(
new Sample(cohort_, internal::kRapporParametersForType[type]));
}
-void RapporService::RecordSampleObj(const std::string& metric_name,
- std::unique_ptr<Sample> sample) {
+void RapporServiceImpl::RecordSample(const std::string& metric_name,
+ std::unique_ptr<Sample> sample) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!RecordingAllowed(sample->parameters()))
return;
diff --git a/chromium/components/rappor/rappor_service.h b/chromium/components/rappor/rappor_service_impl.h
index 3bafcad7109..625ff9d06d2 100644
--- a/chromium/components/rappor/rappor_service.h
+++ b/chromium/components/rappor/rappor_service_impl.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_RAPPOR_RAPPOR_SERVICE_H_
-#define COMPONENTS_RAPPOR_RAPPOR_SERVICE_H_
+#ifndef COMPONENTS_RAPPOR_RAPPOR_SERVICE_IMPL_H_
+#define COMPONENTS_RAPPOR_RAPPOR_SERVICE_IMPL_H_
#include <stdint.h>
@@ -13,12 +13,12 @@
#include "base/callback.h"
#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/timer/timer.h"
#include "components/metrics/daily_event.h"
-#include "components/rappor/rappor_parameters.h"
-#include "components/rappor/sample.h"
+#include "components/rappor/public/rappor_parameters.h"
+#include "components/rappor/public/rappor_service.h"
+#include "components/rappor/public/sample.h"
#include "components/rappor/sampler.h"
class PrefRegistrySimple;
@@ -36,15 +36,15 @@ class RapporReports;
// This class provides an interface for recording samples for rappor metrics,
// and periodically generates and uploads reports based on the collected data.
-class RapporService : public base::SupportsWeakPtr<RapporService> {
+class RapporServiceImpl : public RapporService {
public:
- // Constructs a RapporService.
+ // Constructs a RapporServiceImpl.
// Calling code is responsible for ensuring that the lifetime of
- // |pref_service| is longer than the lifetime of RapporService.
+ // |pref_service| is longer than the lifetime of RapporServiceImpl.
// |is_incognito_callback| will be called to test if incognito mode is active.
- RapporService(PrefService* pref_service,
- const base::Callback<bool(void)> is_incognito_callback);
- virtual ~RapporService();
+ RapporServiceImpl(PrefService* pref_service,
+ const base::Callback<bool(void)> is_incognito_callback);
+ virtual ~RapporServiceImpl();
// Add an observer for collecting daily metrics.
void AddDailyObserver(
@@ -55,7 +55,7 @@ class RapporService : public base::SupportsWeakPtr<RapporService> {
void Initialize(net::URLRequestContextGetter* context);
// Updates the settings for metric recording and uploading.
- // The RapporService must be initialized before this method is called.
+ // The RapporServiceImpl must be initialized before this method is called.
// |recording_groups| should be set of flags, e.g.
// UMA_RECORDING_GROUP | SAFEBROWSING_RECORDING_GROUP
// If it contains any enabled groups, periodic reports will be
@@ -64,13 +64,10 @@ class RapporService : public base::SupportsWeakPtr<RapporService> {
void Update(int recording_groups, bool may_upload);
// Constructs a Sample object for the caller to record fields in.
- virtual std::unique_ptr<Sample> CreateSample(RapporType);
+ std::unique_ptr<Sample> CreateSample(RapporType) override;
// Records a Sample of rappor metric specified by |metric_name|.
//
- // TODO(holte): Rename RecordSample to RecordString and then rename this
- // to RecordSample.
- //
// example:
// std::unique_ptr<Sample> sample =
// rappor_service->CreateSample(MY_METRIC_TYPE);
@@ -81,21 +78,21 @@ class RapporService : public base::SupportsWeakPtr<RapporService> {
// This will result in a report setting two metrics "MyMetric.Field1" and
// "MyMetric.Field2", and they will both be generated from the same sample,
// to allow for correlations to be computed.
- virtual void RecordSampleObj(const std::string& metric_name,
- std::unique_ptr<Sample> sample);
+ void RecordSample(const std::string& metric_name,
+ std::unique_ptr<Sample> sample) override;
// Records a sample of the rappor metric specified by |metric_name|.
// Creates and initializes the metric, if it doesn't yet exist.
- virtual void RecordSample(const std::string& metric_name,
- RapporType type,
- const std::string& sample);
+ void RecordSampleString(const std::string& metric_name,
+ RapporType type,
+ const std::string& sample) override;
- // Registers the names of all of the preferences used by RapporService in the
- // provided PrefRegistry. This should be called before calling Start().
+ // Registers the names of all of the preferences used by RapporServiceImpl in
+ // the provided PrefRegistry. This should be called before calling Start().
static void RegisterPrefs(PrefRegistrySimple* registry);
protected:
- // Initializes the state of the RapporService.
+ // Initializes the state of the RapporServiceImpl.
void InitializeInternal(std::unique_ptr<LogUploaderInterface> uploader,
int32_t cohort,
const std::string& secret);
@@ -168,9 +165,9 @@ class RapporService : public base::SupportsWeakPtr<RapporService> {
base::ThreadChecker thread_checker_;
- DISALLOW_COPY_AND_ASSIGN(RapporService);
+ DISALLOW_COPY_AND_ASSIGN(RapporServiceImpl);
};
} // namespace rappor
-#endif // COMPONENTS_RAPPOR_RAPPOR_SERVICE_H_
+#endif // COMPONENTS_RAPPOR_RAPPOR_SERVICE_IMPL_H_
diff --git a/chromium/components/rappor/rappor_service_unittest.cc b/chromium/components/rappor/rappor_service_unittest.cc
index 2c1e8b6ce68..825458a4996 100644
--- a/chromium/components/rappor/rappor_service_unittest.cc
+++ b/chromium/components/rappor/rappor_service_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
#include <stddef.h>
#include <stdint.h>
@@ -15,7 +15,7 @@
#include "components/prefs/testing_pref_service.h"
#include "components/rappor/byte_vector_utils.h"
#include "components/rappor/proto/rappor_metric.pb.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
#include "components/rappor/rappor_pref_names.h"
#include "components/rappor/test_log_uploader.h"
#include "components/rappor/test_rappor_service.h"
@@ -23,9 +23,9 @@
namespace rappor {
-TEST(RapporServiceTest, Update) {
+TEST(RapporServiceImplTest, Update) {
// Test rappor service initially has uploading and reporting enabled.
- TestRapporService rappor_service;
+ TestRapporServiceImpl rappor_service;
EXPECT_LT(base::TimeDelta(), rappor_service.next_rotation());
EXPECT_TRUE(rappor_service.test_uploader()->is_running());
@@ -47,12 +47,14 @@ TEST(RapporServiceTest, Update) {
}
// Check that samples can be recorded and exported.
-TEST(RapporServiceTest, RecordAndExportMetrics) {
- TestRapporService rappor_service;
+TEST(RapporServiceImplTest, RecordAndExportMetrics) {
+ TestRapporServiceImpl rappor_service;
// Multiple samples for the same metric should only generate one report.
- rappor_service.RecordSample("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE, "foo");
- rappor_service.RecordSample("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE, "bar");
+ rappor_service.RecordSampleString("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE,
+ "foo");
+ rappor_service.RecordSampleString("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE,
+ "bar");
RapporReports reports;
rappor_service.GetReports(&reports);
@@ -65,12 +67,12 @@ TEST(RapporServiceTest, RecordAndExportMetrics) {
}
// Check that the reporting groups are respected.
-TEST(RapporServiceTest, UmaRecordingGroup) {
- TestRapporService rappor_service;
+TEST(RapporServiceImplTest, UmaRecordingGroup) {
+ TestRapporServiceImpl rappor_service;
rappor_service.Update(SAFEBROWSING_RAPPOR_GROUP, false);
// Wrong recording group.
- rappor_service.RecordSample("UmaMetric", UMA_RAPPOR_TYPE, "foo");
+ rappor_service.RecordSampleString("UmaMetric", UMA_RAPPOR_TYPE, "foo");
RapporReports reports;
rappor_service.GetReports(&reports);
@@ -78,12 +80,13 @@ TEST(RapporServiceTest, UmaRecordingGroup) {
}
// Check that the reporting groups are respected.
-TEST(RapporServiceTest, SafeBrowsingRecordingGroup) {
- TestRapporService rappor_service;
+TEST(RapporServiceImplTest, SafeBrowsingRecordingGroup) {
+ TestRapporServiceImpl rappor_service;
rappor_service.Update(UMA_RAPPOR_GROUP, false);
// Wrong recording group.
- rappor_service.RecordSample("SbMetric", SAFEBROWSING_RAPPOR_TYPE, "foo");
+ rappor_service.RecordSampleString("SbMetric", SAFEBROWSING_RAPPOR_TYPE,
+ "foo");
RapporReports reports;
rappor_service.GetReports(&reports);
@@ -91,12 +94,14 @@ TEST(RapporServiceTest, SafeBrowsingRecordingGroup) {
}
// Check that GetRecordedSampleForMetric works as expected.
-TEST(RapporServiceTest, GetRecordedSampleForMetric) {
- TestRapporService rappor_service;
+TEST(RapporServiceImplTest, GetRecordedSampleForMetric) {
+ TestRapporServiceImpl rappor_service;
// Multiple samples for the same metric; only the latest is remembered.
- rappor_service.RecordSample("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE, "foo");
- rappor_service.RecordSample("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE, "bar");
+ rappor_service.RecordSampleString("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE,
+ "foo");
+ rappor_service.RecordSampleString("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE,
+ "bar");
std::string sample;
RapporType type;
@@ -109,11 +114,12 @@ TEST(RapporServiceTest, GetRecordedSampleForMetric) {
}
// Check that the incognito is respected.
-TEST(RapporServiceTest, Incognito) {
- TestRapporService rappor_service;
+TEST(RapporServiceImplTest, Incognito) {
+ TestRapporServiceImpl rappor_service;
rappor_service.set_is_incognito(true);
- rappor_service.RecordSample("MyMetric", SAFEBROWSING_RAPPOR_TYPE, "foo");
+ rappor_service.RecordSampleString("MyMetric", SAFEBROWSING_RAPPOR_TYPE,
+ "foo");
RapporReports reports;
rappor_service.GetReports(&reports);
@@ -121,13 +127,13 @@ TEST(RapporServiceTest, Incognito) {
}
// Check that Sample objects record correctly.
-TEST(RapporServiceTest, RecordSample) {
- TestRapporService rappor_service;
+TEST(RapporServiceImplTest, RecordSample) {
+ TestRapporServiceImpl rappor_service;
std::unique_ptr<Sample> sample =
rappor_service.CreateSample(SAFEBROWSING_RAPPOR_TYPE);
sample->SetStringField("Url", "example.com");
sample->SetFlagsField("Flags1", 0xbcd, 12);
- rappor_service.RecordSampleObj("ObjMetric", std::move(sample));
+ rappor_service.RecordSample("ObjMetric", std::move(sample));
uint64_t url_hash = base::HashMetricName("ObjMetric.Url");
uint64_t flags_hash = base::HashMetricName("ObjMetric.Flags1");
RapporReports reports;
diff --git a/chromium/components/rappor/rappor_utils.cc b/chromium/components/rappor/rappor_utils.cc
index 89e4576b7ba..dfc232a888c 100644
--- a/chromium/components/rappor/rappor_utils.cc
+++ b/chromium/components/rappor/rappor_utils.cc
@@ -2,9 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
-#include "components/rappor/rappor_service.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/url_util.h"
#include "url/gurl.h"
@@ -17,7 +16,7 @@ void SampleString(RapporService* rappor_service,
const std::string& sample) {
if (!rappor_service)
return;
- rappor_service->RecordSample(metric, type, sample);
+ rappor_service->RecordSampleString(metric, type, sample);
}
std::string GetDomainAndRegistrySampleFromGURL(const GURL& gurl) {
@@ -39,10 +38,19 @@ void SampleDomainAndRegistryFromGURL(RapporService* rappor_service,
const GURL& gurl) {
if (!rappor_service)
return;
- rappor_service->RecordSample(
- metric,
- rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
- GetDomainAndRegistrySampleFromGURL(gurl));
+ rappor_service->RecordSampleString(metric, rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
+ GetDomainAndRegistrySampleFromGURL(gurl));
+}
+
+RapporService* (*g_GetDefaultService)() = nullptr;
+
+RapporService* GetDefaultService() {
+ return (g_GetDefaultService != nullptr) ? g_GetDefaultService() : nullptr;
+}
+
+void SetDefaultServiceAccessor(RapporService* (*getDefaultService)()) {
+ DCHECK(g_GetDefaultService == nullptr);
+ g_GetDefaultService = getDefaultService;
}
} // namespace rappor
diff --git a/chromium/components/rappor/rappor_utils_unittest.cc b/chromium/components/rappor/rappor_utils_unittest.cc
index 4b1fba5c83e..d502df811bd 100644
--- a/chromium/components/rappor/rappor_utils_unittest.cc
+++ b/chromium/components/rappor/rappor_utils_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
diff --git a/chromium/components/rappor/reports.cc b/chromium/components/rappor/reports.cc
index 2f0f13206d2..b035b5b5b20 100644
--- a/chromium/components/rappor/reports.cc
+++ b/chromium/components/rappor/reports.cc
@@ -8,7 +8,7 @@
#include "base/rand_util.h"
#include "base/strings/string_piece.h"
#include "components/rappor/byte_vector_utils.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
namespace rappor {
diff --git a/chromium/components/rappor/reports.h b/chromium/components/rappor/reports.h
index 51cabd6b1f4..00da4c00b8f 100644
--- a/chromium/components/rappor/reports.h
+++ b/chromium/components/rappor/reports.h
@@ -11,8 +11,6 @@
namespace rappor {
-struct RapporParameters;
-
namespace internal {
// Generate a randomized report for a single metric/field.
diff --git a/chromium/components/rappor/sample.cc b/chromium/components/rappor/sample.cc
index b54d9c42b35..bdc933a92b0 100644
--- a/chromium/components/rappor/sample.cc
+++ b/chromium/components/rappor/sample.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/rappor/sample.h"
+#include "components/rappor/public/sample.h"
#include <map>
#include <string>
diff --git a/chromium/components/rappor/sampler.cc b/chromium/components/rappor/sampler.cc
index b6a809807c8..c334b40ff0d 100644
--- a/chromium/components/rappor/sampler.cc
+++ b/chromium/components/rappor/sampler.cc
@@ -24,7 +24,7 @@ void Sampler::AddSample(const std::string& metric_name,
// Replace the previous sample with a 1 in sample_count_ chance so that each
// sample has equal probability of being reported.
if (base::RandGenerator(sample_counts_[metric_name]) == 0)
- samples_.set(metric_name, std::move(sample));
+ samples_[metric_name] = std::move(sample);
}
void Sampler::ExportMetrics(const std::string& secret, RapporReports* reports) {
diff --git a/chromium/components/rappor/sampler.h b/chromium/components/rappor/sampler.h
index 8059de44c71..acd8a933dad 100644
--- a/chromium/components/rappor/sampler.h
+++ b/chromium/components/rappor/sampler.h
@@ -8,11 +8,11 @@
#include <map>
#include <memory>
#include <string>
+#include <unordered_map>
-#include "base/containers/scoped_ptr_hash_map.h"
#include "base/macros.h"
-#include "components/rappor/rappor_parameters.h"
-#include "components/rappor/sample.h"
+#include "components/rappor/public/rappor_parameters.h"
+#include "components/rappor/public/sample.h"
namespace rappor {
@@ -42,7 +42,7 @@ class Sampler {
std::map<std::string, int> sample_counts_;
// Stores a Sample for each metric, by metric name.
- base::ScopedPtrHashMap<std::string, std::unique_ptr<Sample>> samples_;
+ std::unordered_map<std::string, std::unique_ptr<Sample>> samples_;
DISALLOW_COPY_AND_ASSIGN(Sampler);
};
diff --git a/chromium/components/rappor/test_rappor_service.cc b/chromium/components/rappor/test_rappor_service.cc
index edd51c3f2d9..62da6f9c29b 100644
--- a/chromium/components/rappor/test_rappor_service.cc
+++ b/chromium/components/rappor/test_rappor_service.cc
@@ -10,7 +10,7 @@
#include "base/memory/ptr_util.h"
#include "components/rappor/byte_vector_utils.h"
#include "components/rappor/proto/rappor_metric.pb.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
#include "components/rappor/rappor_prefs.h"
#include "components/rappor/test_log_uploader.h"
@@ -42,18 +42,28 @@ void TestSample::SetFlagsField(const std::string& field_name,
Sample::SetFlagsField(field_name, flags, num_flags);
}
+void TestSample::SetUInt64Field(const std::string& field_name,
+ uint64_t value,
+ NoiseLevel noise_level) {
+ shadow_.uint64_fields[field_name] =
+ std::pair<std::uint64_t, NoiseLevel>(value, noise_level);
+ Sample::SetUInt64Field(field_name, value, noise_level);
+}
+
TestSample::Shadow::Shadow(RapporType type) : type(type) {}
TestSample::Shadow::Shadow(const TestSample::Shadow& other) {
type = other.type;
flag_fields = other.flag_fields;
string_fields = other.string_fields;
+ uint64_fields = other.uint64_fields;
}
TestSample::Shadow::~Shadow() {}
-TestRapporService::TestRapporService()
- : RapporService(&test_prefs_, base::Bind(&MockIsIncognito, &is_incognito_)),
+TestRapporServiceImpl::TestRapporServiceImpl()
+ : RapporServiceImpl(&test_prefs_,
+ base::Bind(&MockIsIncognito, &is_incognito_)),
next_rotation_(base::TimeDelta()),
is_incognito_(false) {
RegisterPrefs(test_prefs_.registry());
@@ -63,48 +73,48 @@ TestRapporService::TestRapporService()
Update(UMA_RAPPOR_GROUP | SAFEBROWSING_RAPPOR_GROUP, true);
}
-TestRapporService::~TestRapporService() {}
+TestRapporServiceImpl::~TestRapporServiceImpl() {}
-std::unique_ptr<Sample> TestRapporService::CreateSample(RapporType type) {
+std::unique_ptr<Sample> TestRapporServiceImpl::CreateSample(RapporType type) {
std::unique_ptr<TestSample> test_sample(new TestSample(type));
return std::move(test_sample);
}
// Intercepts the sample being recorded and saves it in a test structure.
-void TestRapporService::RecordSampleObj(const std::string& metric_name,
- std::unique_ptr<Sample> sample) {
+void TestRapporServiceImpl::RecordSample(const std::string& metric_name,
+ std::unique_ptr<Sample> sample) {
TestSample* test_sample = static_cast<TestSample*>(sample.get());
// Erase the previous sample if we logged one.
shadows_.erase(metric_name);
shadows_.insert(std::pair<std::string, TestSample::Shadow>(
metric_name, test_sample->GetShadow()));
// Original version is still called.
- RapporService::RecordSampleObj(metric_name, std::move(sample));
+ RapporServiceImpl::RecordSample(metric_name, std::move(sample));
}
-void TestRapporService::RecordSample(const std::string& metric_name,
- RapporType type,
- const std::string& sample) {
+void TestRapporServiceImpl::RecordSampleString(const std::string& metric_name,
+ RapporType type,
+ const std::string& sample) {
// Save the recorded sample to the local structure.
RapporSample rappor_sample;
rappor_sample.type = type;
rappor_sample.value = sample;
samples_[metric_name] = rappor_sample;
// Original version is still called.
- RapporService::RecordSample(metric_name, type, sample);
+ RapporServiceImpl::RecordSampleString(metric_name, type, sample);
}
-int TestRapporService::GetReportsCount() {
+int TestRapporServiceImpl::GetReportsCount() {
RapporReports reports;
ExportMetrics(&reports);
return reports.report_size();
}
-void TestRapporService::GetReports(RapporReports* reports) {
+void TestRapporServiceImpl::GetReports(RapporReports* reports) {
ExportMetrics(reports);
}
-TestSample::Shadow* TestRapporService::GetRecordedSampleForMetric(
+TestSample::Shadow* TestRapporServiceImpl::GetRecordedSampleForMetric(
const std::string& metric_name) {
ShadowMap::iterator it = shadows_.find(metric_name);
if (it == shadows_.end())
@@ -112,7 +122,7 @@ TestSample::Shadow* TestRapporService::GetRecordedSampleForMetric(
return &it->second;
}
-bool TestRapporService::GetRecordedSampleForMetric(
+bool TestRapporServiceImpl::GetRecordedSampleForMetric(
const std::string& metric_name,
std::string* sample,
RapporType* type) {
@@ -125,12 +135,12 @@ bool TestRapporService::GetRecordedSampleForMetric(
}
// Cancel the next call to OnLogInterval.
-void TestRapporService::CancelNextLogRotation() {
+void TestRapporServiceImpl::CancelNextLogRotation() {
next_rotation_ = base::TimeDelta();
}
// Schedule the next call to OnLogInterval.
-void TestRapporService::ScheduleNextLogRotation(base::TimeDelta interval) {
+void TestRapporServiceImpl::ScheduleNextLogRotation(base::TimeDelta interval) {
next_rotation_ = interval;
}
diff --git a/chromium/components/rappor/test_rappor_service.h b/chromium/components/rappor/test_rappor_service.h
index 01a850ccd50..9e0552fcaee 100644
--- a/chromium/components/rappor/test_rappor_service.h
+++ b/chromium/components/rappor/test_rappor_service.h
@@ -13,7 +13,7 @@
#include "base/macros.h"
#include "components/prefs/testing_pref_service.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
#include "components/rappor/test_log_uploader.h"
namespace rappor {
@@ -33,6 +33,10 @@ class TestSample : public Sample {
uint64_t flags,
size_t num_flags) override;
+ void SetUInt64Field(const std::string& field_name,
+ uint64_t value,
+ NoiseLevel noise_level) override;
+
struct Shadow {
explicit Shadow(RapporType type);
Shadow(const Shadow& other);
@@ -40,6 +44,7 @@ class TestSample : public Sample {
RapporType type;
std::map<std::string, uint64_t> flag_fields;
std::map<std::string, std::string> string_fields;
+ std::map<std::string, std::pair<std::uint64_t, NoiseLevel>> uint64_fields;
};
Shadow GetShadow() { return shadow_; }
@@ -51,19 +56,19 @@ class TestSample : public Sample {
// This class provides a simple instance that can be instantiated by tests
// and examined to check that metrics were recorded. It assumes the most
// permissive settings so that any metric can be recorded.
-class TestRapporService : public RapporService {
+class TestRapporServiceImpl : public RapporServiceImpl {
public:
- TestRapporService();
+ TestRapporServiceImpl();
- ~TestRapporService() override;
+ ~TestRapporServiceImpl() override;
- // RapporService:
+ // RapporServiceImpl:
std::unique_ptr<Sample> CreateSample(RapporType type) override;
- void RecordSampleObj(const std::string& metric_name,
- std::unique_ptr<Sample> sample) override;
void RecordSample(const std::string& metric_name,
- RapporType type,
- const std::string& sample) override;
+ std::unique_ptr<Sample> sample) override;
+ void RecordSampleString(const std::string& metric_name,
+ RapporType type,
+ const std::string& sample) override;
// Gets the number of reports that would be uploaded by this service.
// This also clears the internal map of metrics as a biproduct, so if
@@ -129,7 +134,7 @@ class TestRapporService : public RapporService {
// Sets this to true to mock incognito state.
bool is_incognito_;
- DISALLOW_COPY_AND_ASSIGN(TestRapporService);
+ DISALLOW_COPY_AND_ASSIGN(TestRapporServiceImpl);
};
} // namespace rappor
diff --git a/chromium/components/reading_list/DEPS b/chromium/components/reading_list/DEPS
new file mode 100644
index 00000000000..0c3028c0a69
--- /dev/null
+++ b/chromium/components/reading_list/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+components/keyed_service",
+ "+components/prefs",
+ "+components/sync",
+ "+net",
+]
diff --git a/chromium/components/reading_list/OWNERS b/chromium/components/reading_list/OWNERS
new file mode 100644
index 00000000000..c6815f792e2
--- /dev/null
+++ b/chromium/components/reading_list/OWNERS
@@ -0,0 +1,2 @@
+noyau@chromium.org
+olivierrobin@chromium.org
diff --git a/chromium/components/reading_list/core/BUILD.gn b/chromium/components/reading_list/core/BUILD.gn
new file mode 100644
index 00000000000..f45b10c0b42
--- /dev/null
+++ b/chromium/components/reading_list/core/BUILD.gn
@@ -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.
+
+import("//build/buildflag_header.gni")
+import("//components/reading_list/core/reading_list.gni")
+
+source_set("core") {
+ sources = [
+ "reading_list_switches.cc",
+ "reading_list_switches.h",
+ ]
+ deps = [
+ ":reading_list_enable_flags",
+ "//base",
+ ]
+}
+
+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.gni b/chromium/components/reading_list/core/reading_list.gni
new file mode 100644
index 00000000000..26c4e1e7cb5
--- /dev/null
+++ b/chromium/components/reading_list/core/reading_list.gni
@@ -0,0 +1,9 @@
+# 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.
+
+declare_args() {
+ # Controls whether reading list support is active or not. Currently only
+ # supported on iOS (on other platforms, the feature is always disabled).
+ enable_reading_list = true
+}
diff --git a/chromium/components/reading_list/core/reading_list_switches.cc b/chromium/components/reading_list/core/reading_list_switches.cc
new file mode 100644
index 00000000000..d6aba34c584
--- /dev/null
+++ b/chromium/components/reading_list/core/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/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
new file mode 100644
index 00000000000..c99231c5def
--- /dev/null
+++ b/chromium/components/reading_list/core/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_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/ios/BUILD.gn b/chromium/components/reading_list/ios/BUILD.gn
new file mode 100644
index 00000000000..683640ef929
--- /dev/null
+++ b/chromium/components/reading_list/ios/BUILD.gn
@@ -0,0 +1,58 @@
+# 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("ios") {
+ sources = [
+ "favicon_web_state_dispatcher.h",
+ "offline_url_utils.cc",
+ "offline_url_utils.h",
+ "reading_list_entry.cc",
+ "reading_list_entry.h",
+ "reading_list_model.cc",
+ "reading_list_model.h",
+ "reading_list_model_bridge_observer.h",
+ "reading_list_model_bridge_observer.mm",
+ "reading_list_model_impl.cc",
+ "reading_list_model_impl.h",
+ "reading_list_model_observer.h",
+ "reading_list_model_storage.cc",
+ "reading_list_model_storage.h",
+ "reading_list_pref_names.cc",
+ "reading_list_pref_names.h",
+ "reading_list_store.cc",
+ "reading_list_store.h",
+ "reading_list_store_delegate.h",
+ ]
+ deps = [
+ "//base",
+ "//components/keyed_service/core",
+ "//components/prefs",
+ "//components/reading_list/core",
+ "//components/sync",
+ "//net",
+ "//url",
+ ]
+ public_deps = [
+ "//components/reading_list/ios/proto",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "offline_url_utils_unittest.cc",
+ "reading_list_entry_unittest.cc",
+ "reading_list_model_unittest.mm",
+ "reading_list_store_unittest.mm",
+ ]
+ deps = [
+ ":ios",
+ "//base",
+ "//base/test:test_support",
+ "//components/sync",
+ "//components/sync:test_support_model",
+ "//testing/gtest",
+ "//url",
+ ]
+}
diff --git a/chromium/components/reading_list/ios/favicon_web_state_dispatcher.h b/chromium/components/reading_list/ios/favicon_web_state_dispatcher.h
new file mode 100644
index 00000000000..4ffcbae2bfd
--- /dev/null
+++ b/chromium/components/reading_list/ios/favicon_web_state_dispatcher.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_READING_LIST_IOS_FAVICON_WEB_STATE_DISPATCHER_H_
+#define COMPONENTS_READING_LIST_IOS_FAVICON_WEB_STATE_DISPATCHER_H_
+
+namespace web {
+class WebState;
+}
+
+namespace reading_list {
+
+// Dispatcher for WebState having a Favicon Driver, with BookmarkModel and
+// HistoryService attached, as observer. After a WebState is returned, the
+// dispatcher keeps it alive long enough for it to download the favicons.
+class FaviconWebStateDispatcher {
+ public:
+ FaviconWebStateDispatcher() {}
+ virtual ~FaviconWebStateDispatcher() {}
+ // Returns a WebState with a Favicon Driver attached.
+ virtual std::unique_ptr<web::WebState> RequestWebState() = 0;
+ // Called to return a WebState. The WebState should not be used after being
+ // returned.
+ virtual void ReturnWebState(std::unique_ptr<web::WebState> web_state) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FaviconWebStateDispatcher);
+};
+
+} // namespace reading_list
+
+#endif // COMPONENTS_READING_LIST_IOS_FAVICON_WEB_STATE_DISPATCHER_H_
diff --git a/chromium/components/reading_list/ios/offline_url_utils.cc b/chromium/components/reading_list/ios/offline_url_utils.cc
new file mode 100644
index 00000000000..25b0561bdf1
--- /dev/null
+++ b/chromium/components/reading_list/ios/offline_url_utils.cc
@@ -0,0 +1,48 @@
+// 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/ios/offline_url_utils.h"
+
+#include "base/md5.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+const char kOfflineDirectory[] = "Offline";
+const char kMainPageFileName[] = "page.html";
+const char kPDFFileName[] = "file.pdf";
+} // namespace
+
+namespace reading_list {
+
+base::FilePath OfflineRootDirectoryPath(const base::FilePath& profile_path) {
+ return profile_path.Append(FILE_PATH_LITERAL(kOfflineDirectory));
+}
+
+std::string OfflineURLDirectoryID(const GURL& url) {
+ return base::MD5String(url.spec());
+}
+
+base::FilePath OfflineURLDirectoryAbsolutePath(
+ const base::FilePath& profile_path,
+ const GURL& url) {
+ return OfflineURLAbsolutePathFromRelativePath(
+ profile_path, base::FilePath(OfflineURLDirectoryID(url)));
+}
+
+base::FilePath OfflinePagePath(const GURL& url, OfflineFileType type) {
+ base::FilePath directory(OfflineURLDirectoryID(url));
+ switch (type) {
+ case OFFLINE_TYPE_HTML:
+ return directory.Append(FILE_PATH_LITERAL(kMainPageFileName));
+ case OFFLINE_TYPE_PDF:
+ return directory.Append(FILE_PATH_LITERAL(kPDFFileName));
+ }
+}
+
+base::FilePath OfflineURLAbsolutePathFromRelativePath(
+ const base::FilePath& profile_path,
+ const base::FilePath& relative_path) {
+ return OfflineRootDirectoryPath(profile_path).Append(relative_path);
+}
+}
diff --git a/chromium/components/reading_list/ios/offline_url_utils.h b/chromium/components/reading_list/ios/offline_url_utils.h
new file mode 100644
index 00000000000..dc9060f61dd
--- /dev/null
+++ b/chromium/components/reading_list/ios/offline_url_utils.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.
+
+#ifndef COMPONENTS_READING_LIST_IOS_OFFLINE_URL_UTILS_H_
+#define COMPONENTS_READING_LIST_IOS_OFFLINE_URL_UTILS_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "url/gurl.h"
+
+namespace reading_list {
+
+// The different types of file that can be stored for offline usage.
+enum OfflineFileType { OFFLINE_TYPE_HTML = 0, OFFLINE_TYPE_PDF = 1 };
+
+// The absolute path of the directory where offline URLs are saved.
+// |profile_path| is the path to the profile directory that contain the offline
+// directory.
+base::FilePath OfflineRootDirectoryPath(const base::FilePath& profile_path);
+
+// The absolute path of |relative_path| where |relative_path| is relative to the
+// offline root folder (OfflineRootDirectoryPath).
+// |profile_path| is the path to the profile directory that contain the offline
+// directory.
+// The file/directory may not exist.
+base::FilePath OfflineURLAbsolutePathFromRelativePath(
+ const base::FilePath& profile_path,
+ const base::FilePath& relative_path);
+
+// The absolute path of the directory where a |url| is saved offline.
+// Contains the page and supporting files (images).
+// |profile_path| is the path to the profile directory that contain the offline
+// directory.
+// The directory may not exist.
+base::FilePath OfflineURLDirectoryAbsolutePath(
+ const base::FilePath& profile_path,
+ const GURL& url);
+
+// The relative path to the offline webpage for the |url|. The result is
+// relative to |OfflineRootDirectoryPath()|. |type| is the type of the file and
+// will determine the extension of the returned value.
+// The file may not exist.
+base::FilePath OfflinePagePath(const GURL& url, OfflineFileType type);
+
+// The name of the directory containing offline data for |url|.
+std::string OfflineURLDirectoryID(const GURL& url);
+
+} // namespace reading_list
+
+#endif // COMPONENTS_READING_LIST_IOS_OFFLINE_URL_UTILS_H_
diff --git a/chromium/components/reading_list/ios/offline_url_utils_unittest.cc b/chromium/components/reading_list/ios/offline_url_utils_unittest.cc
new file mode 100644
index 00000000000..8d9199ab957
--- /dev/null
+++ b/chromium/components/reading_list/ios/offline_url_utils_unittest.cc
@@ -0,0 +1,61 @@
+// 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/ios/offline_url_utils.h"
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+// Checks the root directory of offline pages.
+TEST(OfflineURLUtilsTest, OfflineRootDirectoryPathTest) {
+ base::FilePath profile_path("/profile_path");
+ base::FilePath offline_directory =
+ reading_list::OfflineRootDirectoryPath(profile_path);
+ EXPECT_EQ("/profile_path/Offline", offline_directory.value());
+}
+
+// Checks the offline page directory is the MD5 of the URL
+TEST(OfflineURLUtilsTest, OfflineURLDirectoryIDTest) {
+ GURL url("http://www.google.com/test");
+ // MD5 of "http://www.google.com/test"
+ std::string md5 = "0090071ef710946a1263c276284bb3b8";
+ std::string directory_id = reading_list::OfflineURLDirectoryID(url);
+ EXPECT_EQ(md5, directory_id);
+}
+
+// Checks the offline page directory is
+// |profile_path|/Offline/OfflineURLDirectoryID;
+TEST(OfflineURLUtilsTest, OfflineURLDirectoryAbsolutePathTest) {
+ base::FilePath profile_path("/profile_path");
+ GURL url("http://www.google.com/test");
+ base::FilePath offline_directory =
+ reading_list::OfflineURLDirectoryAbsolutePath(profile_path, url);
+ EXPECT_EQ("/profile_path/Offline/0090071ef710946a1263c276284bb3b8",
+ offline_directory.value());
+}
+
+// Checks the offline page directory is
+// |profile_path|/Offline/OfflineURLDirectoryID;
+TEST(OfflineURLUtilsTest, AbsolutePathForRelativePathTest) {
+ base::FilePath profile_path("/profile_path");
+ base::FilePath relative_path("relative/path");
+ base::FilePath absolute_path =
+ reading_list::OfflineURLAbsolutePathFromRelativePath(profile_path,
+ relative_path);
+ EXPECT_EQ("/profile_path/Offline/relative/path", absolute_path.value());
+}
+
+// Checks the offline page path is OfflineURLDirectoryID/page.html;
+TEST(OfflineURLUtilsTest, OfflinePagePathTest) {
+ GURL url("http://www.google.com/test");
+ base::FilePath offline_page =
+ reading_list::OfflinePagePath(url, reading_list::OFFLINE_TYPE_HTML);
+ EXPECT_EQ("0090071ef710946a1263c276284bb3b8/page.html", offline_page.value());
+ offline_page =
+ reading_list::OfflinePagePath(url, reading_list::OFFLINE_TYPE_PDF);
+ EXPECT_EQ("0090071ef710946a1263c276284bb3b8/file.pdf", offline_page.value());
+}
diff --git a/chromium/components/reading_list/ios/proto/BUILD.gn b/chromium/components/reading_list/ios/proto/BUILD.gn
new file mode 100644
index 00000000000..927a5eea83e
--- /dev/null
+++ b/chromium/components/reading_list/ios/proto/BUILD.gn
@@ -0,0 +1,14 @@
+# 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 = [
+ "reading_list.proto",
+ ]
+ deps = [
+ "//components/sync/protocol",
+ ]
+}
diff --git a/chromium/components/reading_list/ios/proto/reading_list.proto b/chromium/components/reading_list/ios/proto/reading_list.proto
new file mode 100644
index 00000000000..c47f4a6fdff
--- /dev/null
+++ b/chromium/components/reading_list/ios/proto/reading_list.proto
@@ -0,0 +1,45 @@
+// 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.
+//
+// Sync protocol datatype extension for the reading list items.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package reading_list;
+
+// Local Reading list entry. This proto contains the fields stored locally for
+// a reading list entry. It must be kept synced with the
+// sync_pb.ReadingListSpecifics protobuf.
+message ReadingListLocal {
+ optional string entry_id = 1;
+ optional string title = 2;
+ optional string url = 3;
+ optional int64 creation_time_us = 4;
+ optional int64 update_time_us = 5;
+ optional int64 first_read_time_us = 11;
+ optional int64 update_title_time_us = 12;
+
+ enum ReadingListEntryStatus {
+ UNREAD = 0;
+ READ = 1;
+ UNSEEN = 2;
+ }
+ // If the field is not present, it defaults to UNSEEN.
+ optional ReadingListEntryStatus status = 6;
+
+ enum DistillationState {
+ WAITING = 0;
+ PROCESSING = 1;
+ PROCESSED = 2;
+ WILL_RETRY = 3;
+ ERROR = 4;
+ }
+ optional DistillationState distillation_state = 7;
+ optional string distilled_path = 8;
+ optional string distilled_url = 13;
+ optional int64 failed_download_counter = 9;
+ optional string backoff = 10;
+}
diff --git a/chromium/components/reading_list/ios/reading_list_entry.cc b/chromium/components/reading_list/ios/reading_list_entry.cc
new file mode 100644
index 00000000000..faf9d144f14
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_entry.cc
@@ -0,0 +1,551 @@
+// 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/ios/reading_list_entry.h"
+
+#include "base/json/json_string_value_serializer.h"
+#include "base/memory/ptr_util.h"
+#include "components/reading_list/ios/offline_url_utils.h"
+#include "components/reading_list/ios/proto/reading_list.pb.h"
+#include "components/reading_list/ios/reading_list_store.h"
+#include "components/sync/protocol/reading_list_specifics.pb.h"
+#include "net/base/backoff_entry_serializer.h"
+
+// The backoff time is the following: 10min, 10min, 1h, 2h, 2h..., starting
+// after the first failure.
+const net::BackoffEntry::Policy ReadingListEntry::kBackoffPolicy = {
+ // Number of initial errors (in sequence) to ignore before applying
+ // exponential back-off rules.
+ 2,
+
+ // Initial delay for exponential back-off in ms.
+ 10 * 60 * 1000, // 10 minutes.
+
+ // Factor by which the waiting time will be multiplied.
+ 6,
+
+ // Fuzzing percentage. ex: 10% will spread requests randomly
+ // between 90%-100% of the calculated time.
+ 0.1, // 10%.
+
+ // Maximum amount of time we are willing to delay our request in ms.
+ 2 * 3600 * 1000, // 2 hours.
+
+ // Time to keep an entry from being discarded even when it
+ // has no significant state, -1 to never discard.
+ -1,
+
+ true, // Don't use initial delay unless the last request was an error.
+};
+
+ReadingListEntry::ReadingListEntry(const GURL& url, const std::string& title)
+ : ReadingListEntry(url, title, nullptr){};
+
+ReadingListEntry::ReadingListEntry(const GURL& url,
+ const std::string& title,
+ std::unique_ptr<net::BackoffEntry> backoff)
+ : ReadingListEntry(url,
+ title,
+ UNSEEN,
+ 0,
+ 0,
+ 0,
+ 0,
+ WAITING,
+ base::FilePath(),
+ GURL(),
+ 0,
+ std::move(backoff)) {}
+
+ReadingListEntry::ReadingListEntry(
+ const GURL& url,
+ const std::string& title,
+ State state,
+ int64_t creation_time,
+ int64_t first_read_time,
+ int64_t update_time,
+ int64_t update_title_time,
+ ReadingListEntry::DistillationState distilled_state,
+ const base::FilePath& distilled_path,
+ const GURL& distilled_url,
+ int failed_download_counter,
+ std::unique_ptr<net::BackoffEntry> backoff)
+ : url_(url),
+ title_(title),
+ state_(state),
+ distilled_path_(distilled_path),
+ distilled_url_(distilled_url),
+ distilled_state_(distilled_state),
+ failed_download_counter_(failed_download_counter),
+ creation_time_us_(creation_time),
+ first_read_time_us_(first_read_time),
+ update_time_us_(update_time),
+ update_title_time_us_(update_title_time) {
+ if (backoff) {
+ backoff_ = std::move(backoff);
+ } else {
+ backoff_ = base::MakeUnique<net::BackoffEntry>(&kBackoffPolicy);
+ }
+ if (creation_time_us_ == 0) {
+ DCHECK(update_time_us_ == 0);
+ DCHECK(update_title_time_us_ == 0);
+ creation_time_us_ =
+ (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds();
+ update_time_us_ = creation_time_us_;
+ update_title_time_us_ = creation_time_us_;
+ }
+ DCHECK(!url.is_empty());
+ DCHECK(url.is_valid());
+}
+
+ReadingListEntry::ReadingListEntry(ReadingListEntry&& entry)
+ : url_(std::move(entry.url_)),
+ title_(std::move(entry.title_)),
+ state_(std::move(entry.state_)),
+ distilled_path_(std::move(entry.distilled_path_)),
+ distilled_url_(std::move(entry.distilled_url_)),
+ distilled_state_(std::move(entry.distilled_state_)),
+ backoff_(std::move(entry.backoff_)),
+ failed_download_counter_(std::move(entry.failed_download_counter_)),
+ creation_time_us_(std::move(entry.creation_time_us_)),
+ first_read_time_us_(std::move(entry.first_read_time_us_)),
+ update_time_us_(std::move(entry.update_time_us_)),
+ update_title_time_us_(std::move(entry.update_title_time_us_)) {}
+
+ReadingListEntry::~ReadingListEntry() {}
+
+const GURL& ReadingListEntry::URL() const {
+ return url_;
+}
+
+const std::string& ReadingListEntry::Title() const {
+ return title_;
+}
+
+ReadingListEntry::DistillationState ReadingListEntry::DistilledState() const {
+ return distilled_state_;
+}
+
+const base::FilePath& ReadingListEntry::DistilledPath() const {
+ return distilled_path_;
+}
+
+const GURL& ReadingListEntry::DistilledURL() const {
+ return distilled_url_;
+}
+
+base::TimeDelta ReadingListEntry::TimeUntilNextTry() const {
+ return backoff_->GetTimeUntilRelease();
+}
+
+int ReadingListEntry::FailedDownloadCounter() const {
+ return failed_download_counter_;
+}
+
+ReadingListEntry& ReadingListEntry::operator=(ReadingListEntry&& other) {
+ url_ = std::move(other.url_);
+ title_ = std::move(other.title_);
+ distilled_path_ = std::move(other.distilled_path_);
+ distilled_url_ = std::move(other.distilled_url_);
+ distilled_state_ = std::move(other.distilled_state_);
+ backoff_ = std::move(other.backoff_);
+ state_ = std::move(other.state_);
+ failed_download_counter_ = std::move(other.failed_download_counter_);
+ creation_time_us_ = std::move(other.creation_time_us_);
+ first_read_time_us_ = std::move(other.first_read_time_us_);
+ update_time_us_ = std::move(other.update_time_us_);
+ update_title_time_us_ = std::move(other.update_title_time_us_);
+ return *this;
+}
+
+bool ReadingListEntry::operator==(const ReadingListEntry& other) const {
+ return url_ == other.url_;
+}
+
+void ReadingListEntry::SetTitle(const std::string& title) {
+ title_ = title;
+ update_title_time_us_ =
+ (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds();
+}
+
+void ReadingListEntry::SetRead(bool read) {
+ State previous_state = state_;
+ state_ = read ? READ : UNREAD;
+ if (state_ == previous_state) {
+ return;
+ }
+ if (FirstReadTime() == 0 && read) {
+ first_read_time_us_ =
+ (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds();
+ }
+ if (!(previous_state == UNSEEN && state_ == UNREAD)) {
+ // If changing UNSEEN -> UNREAD, entry is not marked updated to preserve
+ // order in Reading List View.
+ MarkEntryUpdated();
+ }
+}
+
+bool ReadingListEntry::IsRead() const {
+ return state_ == READ;
+}
+
+bool ReadingListEntry::HasBeenSeen() const {
+ return state_ != UNSEEN;
+}
+
+void ReadingListEntry::SetDistilledInfo(const base::FilePath& path,
+ const GURL& distilled_url) {
+ DCHECK(!path.empty());
+ DCHECK(distilled_url.is_valid());
+ distilled_path_ = path;
+ distilled_state_ = PROCESSED;
+ distilled_url_ = distilled_url;
+ backoff_->Reset();
+ failed_download_counter_ = 0;
+}
+
+void ReadingListEntry::SetDistilledState(DistillationState distilled_state) {
+ DCHECK(distilled_state != PROCESSED); // use SetDistilledPath instead.
+ DCHECK(distilled_state != WAITING);
+ // Increase time until next retry exponentially if the state change from a
+ // non-error state to an error state.
+ if ((distilled_state == WILL_RETRY || distilled_state == ERROR) &&
+ distilled_state_ != WILL_RETRY && distilled_state_ != ERROR) {
+ backoff_->InformOfRequest(false);
+ failed_download_counter_++;
+ }
+
+ distilled_state_ = distilled_state;
+ distilled_path_ = base::FilePath();
+ distilled_url_ = GURL::EmptyGURL();
+}
+
+int64_t ReadingListEntry::UpdateTime() const {
+ return update_time_us_;
+}
+
+int64_t ReadingListEntry::UpdateTitleTime() const {
+ return update_title_time_us_;
+}
+
+int64_t ReadingListEntry::CreationTime() const {
+ return creation_time_us_;
+}
+
+int64_t ReadingListEntry::FirstReadTime() const {
+ return first_read_time_us_;
+}
+
+void ReadingListEntry::MarkEntryUpdated() {
+ update_time_us_ =
+ (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds();
+}
+
+// static
+std::unique_ptr<ReadingListEntry> ReadingListEntry::FromReadingListLocal(
+ const reading_list::ReadingListLocal& pb_entry) {
+ if (!pb_entry.has_url()) {
+ return nullptr;
+ }
+ GURL url(pb_entry.url());
+ if (url.is_empty() || !url.is_valid()) {
+ return nullptr;
+ }
+ std::string title;
+ if (pb_entry.has_title()) {
+ title = pb_entry.title();
+ }
+
+ int64_t creation_time_us = 0;
+ if (pb_entry.has_creation_time_us()) {
+ creation_time_us = pb_entry.creation_time_us();
+ }
+
+ int64_t first_read_time_us = 0;
+ if (pb_entry.has_first_read_time_us()) {
+ first_read_time_us = pb_entry.first_read_time_us();
+ }
+
+ int64_t update_time_us = 0;
+ if (pb_entry.has_update_time_us()) {
+ update_time_us = pb_entry.update_time_us();
+ }
+
+ int64_t update_title_time_us = 0;
+ if (pb_entry.has_update_title_time_us()) {
+ update_title_time_us = pb_entry.update_title_time_us();
+ }
+
+ State state = UNSEEN;
+ if (pb_entry.has_status()) {
+ switch (pb_entry.status()) {
+ case reading_list::ReadingListLocal::READ:
+ state = READ;
+ break;
+ case reading_list::ReadingListLocal::UNREAD:
+ state = UNREAD;
+ break;
+ case reading_list::ReadingListLocal::UNSEEN:
+ state = UNSEEN;
+ break;
+ }
+ }
+
+ ReadingListEntry::DistillationState distillation_state =
+ ReadingListEntry::WAITING;
+ if (pb_entry.has_distillation_state()) {
+ switch (pb_entry.distillation_state()) {
+ case reading_list::ReadingListLocal::WAITING:
+ distillation_state = ReadingListEntry::WAITING;
+ break;
+ case reading_list::ReadingListLocal::PROCESSING:
+ distillation_state = ReadingListEntry::PROCESSING;
+ break;
+ case reading_list::ReadingListLocal::PROCESSED:
+ distillation_state = ReadingListEntry::PROCESSED;
+ break;
+ case reading_list::ReadingListLocal::WILL_RETRY:
+ distillation_state = ReadingListEntry::WILL_RETRY;
+ break;
+ case reading_list::ReadingListLocal::ERROR:
+ distillation_state = ReadingListEntry::ERROR;
+ break;
+ }
+ }
+
+ base::FilePath distilled_path;
+ if (pb_entry.has_distilled_path()) {
+ distilled_path = base::FilePath(pb_entry.distilled_path());
+ }
+
+ GURL distilled_url;
+ if (pb_entry.has_distilled_url()) {
+ distilled_url = GURL(pb_entry.distilled_url());
+ }
+
+ int64_t failed_download_counter = 0;
+ if (pb_entry.has_failed_download_counter()) {
+ failed_download_counter = pb_entry.failed_download_counter();
+ }
+
+ std::unique_ptr<net::BackoffEntry> backoff;
+ if (pb_entry.has_backoff()) {
+ JSONStringValueDeserializer deserializer(pb_entry.backoff());
+ std::unique_ptr<base::Value> value(
+ deserializer.Deserialize(nullptr, nullptr));
+ if (value) {
+ backoff = net::BackoffEntrySerializer::DeserializeFromValue(
+ *value, &kBackoffPolicy, nullptr, base::Time::Now());
+ }
+ }
+
+ return base::WrapUnique<ReadingListEntry>(new ReadingListEntry(
+ url, title, state, creation_time_us, first_read_time_us, update_time_us,
+ update_title_time_us, distillation_state, distilled_path, distilled_url,
+ failed_download_counter, std::move(backoff)));
+}
+
+// static
+std::unique_ptr<ReadingListEntry> ReadingListEntry::FromReadingListSpecifics(
+ const sync_pb::ReadingListSpecifics& pb_entry) {
+ if (!pb_entry.has_url()) {
+ return nullptr;
+ }
+ GURL url(pb_entry.url());
+ if (url.is_empty() || !url.is_valid()) {
+ return nullptr;
+ }
+ std::string title;
+ if (pb_entry.has_title()) {
+ title = pb_entry.title();
+ }
+
+ int64_t creation_time_us = 0;
+ if (pb_entry.has_creation_time_us()) {
+ creation_time_us = pb_entry.creation_time_us();
+ }
+
+ int64_t first_read_time_us = 0;
+ if (pb_entry.has_first_read_time_us()) {
+ first_read_time_us = pb_entry.first_read_time_us();
+ }
+
+ int64_t update_time_us = 0;
+ if (pb_entry.has_update_time_us()) {
+ update_time_us = pb_entry.update_time_us();
+ }
+
+ int64_t update_title_time_us = 0;
+ if (pb_entry.has_update_title_time_us()) {
+ update_title_time_us = pb_entry.update_title_time_us();
+ }
+
+ State state = UNSEEN;
+ if (pb_entry.has_status()) {
+ switch (pb_entry.status()) {
+ case sync_pb::ReadingListSpecifics::READ:
+ state = READ;
+ break;
+ case sync_pb::ReadingListSpecifics::UNREAD:
+ state = UNREAD;
+ break;
+ case sync_pb::ReadingListSpecifics::UNSEEN:
+ state = UNSEEN;
+ break;
+ }
+ }
+
+ return base::WrapUnique<ReadingListEntry>(new ReadingListEntry(
+ url, title, state, creation_time_us, first_read_time_us, update_time_us,
+ update_title_time_us, WAITING, base::FilePath(), GURL(), 0, nullptr));
+}
+
+void ReadingListEntry::MergeWithEntry(const ReadingListEntry& other) {
+#if !defined(NDEBUG)
+ // Checks that the result entry respects the sync order.
+ std::unique_ptr<sync_pb::ReadingListSpecifics> old_this_pb(
+ AsReadingListSpecifics());
+ std::unique_ptr<sync_pb::ReadingListSpecifics> other_pb(
+ other.AsReadingListSpecifics());
+#endif
+ DCHECK(url_ == other.url_);
+ if (update_title_time_us_ < other.update_title_time_us_) {
+ // Take the most recent title updated.
+ title_ = std::move(other.title_);
+ update_title_time_us_ = std::move(other.update_title_time_us_);
+ } else if (update_title_time_us_ == other.update_title_time_us_) {
+ if (title_.compare(other.title_) < 0) {
+ // Take the last in alphabetical order or the longer one.
+ // This ensure empty string is replaced.
+ title_ = std::move(other.title_);
+ }
+ }
+ if (creation_time_us_ < other.creation_time_us_) {
+ creation_time_us_ = std::move(other.creation_time_us_);
+ first_read_time_us_ = std::move(other.first_read_time_us_);
+ } else if (creation_time_us_ == other.creation_time_us_) {
+ // The first_time_read_us from |other| is used if
+ // - this.first_time_read_us == 0: the entry was never read in this device.
+ // - this.first_time_read_us > other.first_time_read_us: the entry was
+ // first read on another device.
+ if (first_read_time_us_ == 0 ||
+ (other.first_read_time_us_ != 0 &&
+ other.first_read_time_us_ < first_read_time_us_)) {
+ first_read_time_us_ = std::move(other.first_read_time_us_);
+ }
+ }
+ if (update_time_us_ < other.update_time_us_) {
+ update_time_us_ = std::move(other.update_time_us_);
+ state_ = std::move(other.state_);
+ } else if (update_time_us_ == other.update_time_us_) {
+ if (state_ == UNSEEN) {
+ state_ = std::move(other.state_);
+ } else if (other.state_ == READ) {
+ state_ = std::move(other.state_);
+ }
+ }
+#if !defined(NDEBUG)
+ std::unique_ptr<sync_pb::ReadingListSpecifics> new_this_pb(
+ AsReadingListSpecifics());
+ DCHECK(ReadingListStore::CompareEntriesForSync(*old_this_pb, *new_this_pb));
+ DCHECK(ReadingListStore::CompareEntriesForSync(*other_pb, *new_this_pb));
+#endif
+}
+
+std::unique_ptr<reading_list::ReadingListLocal>
+ReadingListEntry::AsReadingListLocal() const {
+ std::unique_ptr<reading_list::ReadingListLocal> pb_entry =
+ base::MakeUnique<reading_list::ReadingListLocal>();
+
+ // URL is used as the key for the database and sync as there is only one entry
+ // per URL.
+ pb_entry->set_entry_id(URL().spec());
+ pb_entry->set_title(Title());
+ pb_entry->set_url(URL().spec());
+ pb_entry->set_creation_time_us(CreationTime());
+ pb_entry->set_first_read_time_us(FirstReadTime());
+ pb_entry->set_update_time_us(UpdateTime());
+ pb_entry->set_update_title_time_us(UpdateTitleTime());
+
+ switch (state_) {
+ case READ:
+ pb_entry->set_status(reading_list::ReadingListLocal::READ);
+ break;
+ case UNREAD:
+ pb_entry->set_status(reading_list::ReadingListLocal::UNREAD);
+ break;
+ case UNSEEN:
+ pb_entry->set_status(reading_list::ReadingListLocal::UNSEEN);
+ break;
+ }
+
+ reading_list::ReadingListLocal::DistillationState distilation_state;
+ switch (DistilledState()) {
+ case ReadingListEntry::WAITING:
+ distilation_state = reading_list::ReadingListLocal::WAITING;
+ break;
+ case ReadingListEntry::PROCESSING:
+ distilation_state = reading_list::ReadingListLocal::PROCESSING;
+ break;
+ case ReadingListEntry::PROCESSED:
+ distilation_state = reading_list::ReadingListLocal::PROCESSED;
+ break;
+ case ReadingListEntry::WILL_RETRY:
+ distilation_state = reading_list::ReadingListLocal::WILL_RETRY;
+ break;
+ case ReadingListEntry::ERROR:
+ distilation_state = reading_list::ReadingListLocal::ERROR;
+ break;
+ }
+ pb_entry->set_distillation_state(distilation_state);
+ if (!DistilledPath().empty()) {
+ pb_entry->set_distilled_path(DistilledPath().value());
+ }
+ if (DistilledURL().is_valid()) {
+ pb_entry->set_distilled_url(DistilledURL().spec());
+ }
+ pb_entry->set_failed_download_counter(failed_download_counter_);
+
+ if (backoff_) {
+ std::unique_ptr<base::Value> backoff =
+ net::BackoffEntrySerializer::SerializeToValue(*backoff_,
+ base::Time::Now());
+
+ std::string output;
+ JSONStringValueSerializer serializer(&output);
+ serializer.Serialize(*backoff);
+ pb_entry->set_backoff(output);
+ }
+ return pb_entry;
+}
+
+std::unique_ptr<sync_pb::ReadingListSpecifics>
+ReadingListEntry::AsReadingListSpecifics() const {
+ std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry =
+ base::MakeUnique<sync_pb::ReadingListSpecifics>();
+
+ // URL is used as the key for the database and sync as there is only one entry
+ // per URL.
+ pb_entry->set_entry_id(URL().spec());
+ pb_entry->set_title(Title());
+ pb_entry->set_url(URL().spec());
+ pb_entry->set_creation_time_us(CreationTime());
+ pb_entry->set_first_read_time_us(FirstReadTime());
+ pb_entry->set_update_time_us(UpdateTime());
+ pb_entry->set_update_title_time_us(UpdateTitleTime());
+
+ switch (state_) {
+ case READ:
+ pb_entry->set_status(sync_pb::ReadingListSpecifics::READ);
+ break;
+ case UNREAD:
+ pb_entry->set_status(sync_pb::ReadingListSpecifics::UNREAD);
+ break;
+ case UNSEEN:
+ pb_entry->set_status(sync_pb::ReadingListSpecifics::UNSEEN);
+ break;
+ }
+
+ return pb_entry;
+}
diff --git a/chromium/components/reading_list/ios/reading_list_entry.h b/chromium/components/reading_list/ios/reading_list_entry.h
new file mode 100644
index 00000000000..6cb2716ebc6
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_entry.h
@@ -0,0 +1,172 @@
+// 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_IOS_READING_LIST_ENTRY_H_
+#define COMPONENTS_READING_LIST_IOS_READING_LIST_ENTRY_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "net/base/backoff_entry.h"
+#include "url/gurl.h"
+
+namespace reading_list {
+class ReadingListLocal;
+
+// The different ways a reading list entry is added.
+// |ADDED_VIA_CURRENT_APP| is when the entry was added by the user from within
+// the current instance of the app.
+// |ADDED_VIA_EXTENSION| is when the entry was added via the share extension.
+// |ADDED_VIA_SYNC| is when the entry was added with sync.
+enum EntrySource { ADDED_VIA_CURRENT_APP, ADDED_VIA_EXTENSION, ADDED_VIA_SYNC };
+}
+
+namespace sync_pb {
+class ReadingListSpecifics;
+}
+
+class ReadingListEntry;
+
+// An entry in the reading list. The URL is a unique identifier for an entry, as
+// such it should not be empty and is the only thing considered when comparing
+// entries.
+class ReadingListEntry {
+ public:
+ ReadingListEntry(const GURL& url, const std::string& title);
+ ReadingListEntry(const GURL& url,
+ const std::string& title,
+ std::unique_ptr<net::BackoffEntry> backoff);
+ ReadingListEntry(ReadingListEntry&& entry);
+ ~ReadingListEntry();
+
+ // Entries are created in WAITING state. At some point they will be PROCESSING
+ // into one of the three state: PROCESSED, the only state a distilled URL
+ // would be set, WILL_RETRY, similar to wait, but with exponential delays or
+ // ERROR where the system will not retry at all.
+ enum DistillationState { WAITING, PROCESSING, PROCESSED, WILL_RETRY, ERROR };
+
+ static const net::BackoffEntry::Policy kBackoffPolicy;
+
+ // The URL of the page the user would like to read later.
+ const GURL& URL() const;
+ // The title of the entry. Might be empty.
+ const std::string& Title() const;
+ // What state this entry is in.
+ DistillationState DistilledState() const;
+ // The local file path for the distilled version of the page. This should only
+ // be called if the state is "PROCESSED".
+ const base::FilePath& DistilledPath() const;
+ // The URL that has been distilled to produce file stored at |DistilledPath|.
+ const GURL& DistilledURL() const;
+ // The time before the next try. This is automatically increased when the
+ // state is set to WILL_RETRY or ERROR from a non-error state.
+ base::TimeDelta TimeUntilNextTry() const;
+ // The number of time chrome failed to download this entry. This is
+ // automatically increased when the state is set to WILL_RETRY or ERROR from a
+ // non-error state.
+ int FailedDownloadCounter() const;
+ // The read status of the entry.
+ bool IsRead() const;
+ // Returns if an entry has ever been seen.
+ bool HasBeenSeen() const;
+
+ // The last update time of the entry. This value may be used to sort the
+ // entries. The value is in microseconds since Jan 1st 1970.
+ int64_t UpdateTime() const;
+
+ // The last update time of the title of the entry. The value is in
+ // microseconds since Jan 1st 1970.
+ int64_t UpdateTitleTime() const;
+
+ // The creation update time of the entry. The value is in microseconds since
+ // Jan 1st 1970.
+ int64_t CreationTime() const;
+
+ // The time when the entry was read for the first time. The value is in
+ // microseconds since Jan 1st 1970.
+ int64_t FirstReadTime() const;
+
+ // Set the update time to now.
+ void MarkEntryUpdated();
+
+ // Returns a protobuf encoding the content of this ReadingListEntry for local
+ // storage.
+ std::unique_ptr<reading_list::ReadingListLocal> AsReadingListLocal() const;
+
+ // Returns a protobuf encoding the content of this ReadingListEntry for sync.
+ std::unique_ptr<sync_pb::ReadingListSpecifics> AsReadingListSpecifics() const;
+
+ // Created a ReadingListEntry from the protobuf format.
+ static std::unique_ptr<ReadingListEntry> FromReadingListLocal(
+ const reading_list::ReadingListLocal& pb_entry);
+
+ // Created a ReadingListEntry from the protobuf format.
+ static std::unique_ptr<ReadingListEntry> FromReadingListSpecifics(
+ const sync_pb::ReadingListSpecifics& pb_entry);
+
+ // Merge |this| and |other| into this.
+ // Local fields are kept from |this|.
+ // Each field is merged individually keeping the highest value as defined by
+ // the |ReadingListStore.CompareEntriesForSync| function.
+ //
+ // After calling |MergeLocalStateFrom|, the result must verify
+ // ReadingListStore.CompareEntriesForSync(old_this.AsReadingListSpecifics(),
+ // new_this.AsReadingListSpecifics())
+ // and
+ // ReadingListStore.CompareEntriesForSync(other.AsReadingListSpecifics(),
+ // new_this.AsReadingListSpecifics()).
+ void MergeWithEntry(const ReadingListEntry& other);
+
+ ReadingListEntry& operator=(ReadingListEntry&& other);
+
+ bool operator==(const ReadingListEntry& other) const;
+
+ // Sets the title.
+ void SetTitle(const std::string& title);
+ // Sets the distilled info (offline path and online URL) about distilled page,
+ // switch the state to PROCESSED and reset the time until the next try.
+ void SetDistilledInfo(const base::FilePath& path, const GURL& distilled_url);
+ // Sets the state to one of PROCESSING, WILL_RETRY or ERROR.
+ void SetDistilledState(DistillationState distilled_state);
+ // Sets the read state of the entry. Will set the UpdateTime of the entry.
+ void SetRead(bool read);
+
+ private:
+ enum State { UNSEEN, UNREAD, READ };
+ ReadingListEntry(const GURL& url,
+ const std::string& title,
+ State state,
+ int64_t creation_time,
+ int64_t first_read_time,
+ int64_t update_time,
+ int64_t update_title_time,
+ ReadingListEntry::DistillationState distilled_state,
+ const base::FilePath& distilled_path,
+ const GURL& distilled_url,
+ int failed_download_counter,
+ std::unique_ptr<net::BackoffEntry> backoff);
+ GURL url_;
+ std::string title_;
+ State state_;
+ base::FilePath distilled_path_;
+ GURL distilled_url_;
+ DistillationState distilled_state_;
+
+ std::unique_ptr<net::BackoffEntry> backoff_;
+ int failed_download_counter_;
+
+ // These value are in microseconds since Jan 1st 1970. They are used for
+ // sorting the entries from the database. They are kept in int64_t to avoid
+ // conversion on each save/read event.
+ int64_t creation_time_us_;
+ int64_t first_read_time_us_;
+ int64_t update_time_us_;
+ int64_t update_title_time_us_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadingListEntry);
+};
+
+#endif // COMPONENTS_READING_LIST_IOS_READING_LIST_ENTRY_H_
diff --git a/chromium/components/reading_list/ios/reading_list_entry_unittest.cc b/chromium/components/reading_list/ios/reading_list_entry_unittest.cc
new file mode 100644
index 00000000000..7afd3aebe03
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_entry_unittest.cc
@@ -0,0 +1,363 @@
+// 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/ios/reading_list_entry.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/test/ios/wait_util.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/reading_list/ios/proto/reading_list.pb.h"
+#include "components/sync/protocol/reading_list_specifics.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const int kFirstBackoff = 10;
+const int kSecondBackoff = 10;
+const int kThirdBackoff = 60;
+const int kFourthBackoff = 120;
+const int kFifthBackoff = 120;
+} // namespace
+
+TEST(ReadingListEntry, CompareIgnoreTitle) {
+ const ReadingListEntry e1(GURL("http://example.com"), "bar");
+ const ReadingListEntry e2(GURL("http://example.com"), "foo");
+
+ EXPECT_EQ(e1, e2);
+}
+
+TEST(ReadingListEntry, CompareFailureIgnoreTitle) {
+ const ReadingListEntry e1(GURL("http://example.com"), "bar");
+ const ReadingListEntry e2(GURL("http://example.org"), "bar");
+
+ EXPECT_FALSE(e1 == e2);
+}
+
+TEST(ReadingListEntry, MovesAreEquals) {
+ ReadingListEntry e1(GURL("http://example.com"), "bar");
+ ReadingListEntry e2(GURL("http://example.com"), "bar");
+ ASSERT_EQ(e1, e2);
+ ASSERT_EQ(e1.Title(), e2.Title());
+
+ ReadingListEntry e3(std::move(e1));
+
+ EXPECT_EQ(e3, e2);
+ EXPECT_EQ(e3.Title(), e2.Title());
+}
+
+TEST(ReadingListEntry, ReadState) {
+ ReadingListEntry e(GURL("http://example.com"), "bar");
+ EXPECT_FALSE(e.HasBeenSeen());
+ EXPECT_FALSE(e.IsRead());
+ e.SetRead(false);
+ EXPECT_TRUE(e.HasBeenSeen());
+ EXPECT_FALSE(e.IsRead());
+ e.SetRead(true);
+ EXPECT_TRUE(e.HasBeenSeen());
+ EXPECT_TRUE(e.IsRead());
+}
+
+TEST(ReadingListEntry, UpdateTitle) {
+ ReadingListEntry e(GURL("http://example.com"), "bar");
+ ASSERT_EQ("bar", e.Title());
+ ASSERT_EQ(e.CreationTime(), e.UpdateTitleTime());
+
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(5));
+ e.SetTitle("foo");
+ EXPECT_GT(e.UpdateTitleTime(), e.CreationTime());
+ EXPECT_EQ("foo", e.Title());
+}
+
+TEST(ReadingListEntry, DistilledPathAndURL) {
+ ReadingListEntry e(GURL("http://example.com"), "bar");
+
+ EXPECT_TRUE(e.DistilledPath().empty());
+
+ const base::FilePath distilled_path("distilled/page.html");
+ const GURL distilled_url("http://example.com/distilled");
+ e.SetDistilledInfo(distilled_path, distilled_url);
+ EXPECT_EQ(distilled_path, e.DistilledPath());
+ EXPECT_EQ(distilled_url, e.DistilledURL());
+}
+
+TEST(ReadingListEntry, DistilledState) {
+ ReadingListEntry e(GURL("http://example.com"), "bar");
+
+ EXPECT_EQ(ReadingListEntry::WAITING, e.DistilledState());
+
+ e.SetDistilledState(ReadingListEntry::ERROR);
+ EXPECT_EQ(ReadingListEntry::ERROR, e.DistilledState());
+
+ const base::FilePath distilled_path("distilled/page.html");
+ const GURL distilled_url("http://example.com/distilled");
+ e.SetDistilledInfo(distilled_path, distilled_url);
+ EXPECT_EQ(ReadingListEntry::PROCESSED, e.DistilledState());
+}
+
+// Tests that the the time until next try increase exponentially when the state
+// changes from non-error to error.
+TEST(ReadingListEntry, TimeUntilNextTry) {
+ base::SimpleTestTickClock clock;
+ std::unique_ptr<net::BackoffEntry> backoff =
+ base::MakeUnique<net::BackoffEntry>(&ReadingListEntry::kBackoffPolicy,
+ &clock);
+
+ ReadingListEntry e(GURL("http://example.com"), "bar", std::move(backoff));
+
+ double fuzzing = ReadingListEntry::kBackoffPolicy.jitter_factor;
+
+ ASSERT_EQ(0, e.TimeUntilNextTry().InSeconds());
+
+ // First error.
+ e.SetDistilledState(ReadingListEntry::ERROR);
+ int nextTry = e.TimeUntilNextTry().InMinutes();
+ EXPECT_NEAR(kFirstBackoff, nextTry, kFirstBackoff * fuzzing);
+ e.SetDistilledState(ReadingListEntry::WILL_RETRY);
+ EXPECT_EQ(nextTry, e.TimeUntilNextTry().InMinutes());
+
+ e.SetDistilledState(ReadingListEntry::PROCESSING);
+ EXPECT_EQ(nextTry, e.TimeUntilNextTry().InMinutes());
+
+ // Second error.
+ e.SetDistilledState(ReadingListEntry::WILL_RETRY);
+ nextTry = e.TimeUntilNextTry().InMinutes();
+ EXPECT_NEAR(kSecondBackoff, nextTry, kSecondBackoff * fuzzing);
+ e.SetDistilledState(ReadingListEntry::ERROR);
+ EXPECT_EQ(nextTry, e.TimeUntilNextTry().InMinutes());
+
+ e.SetDistilledState(ReadingListEntry::PROCESSING);
+ EXPECT_EQ(nextTry, e.TimeUntilNextTry().InMinutes());
+
+ // Third error.
+ e.SetDistilledState(ReadingListEntry::WILL_RETRY);
+ EXPECT_NEAR(kThirdBackoff, e.TimeUntilNextTry().InMinutes(),
+ kThirdBackoff * fuzzing);
+
+ // Fourth error.
+ e.SetDistilledState(ReadingListEntry::PROCESSING);
+ e.SetDistilledState(ReadingListEntry::ERROR);
+ EXPECT_NEAR(kFourthBackoff, e.TimeUntilNextTry().InMinutes(),
+ kFourthBackoff * fuzzing);
+
+ // Fifth error.
+ e.SetDistilledState(ReadingListEntry::PROCESSING);
+ e.SetDistilledState(ReadingListEntry::ERROR);
+ EXPECT_NEAR(kFifthBackoff, e.TimeUntilNextTry().InMinutes(),
+ kFifthBackoff * fuzzing);
+}
+
+// Tests that if the time until next try is in the past, 0 is returned.
+TEST(ReadingListEntry, TimeUntilNextTryInThePast) {
+ // Setup.
+ base::SimpleTestTickClock clock;
+ std::unique_ptr<net::BackoffEntry> backoff =
+ base::MakeUnique<net::BackoffEntry>(&ReadingListEntry::kBackoffPolicy,
+ &clock);
+ ReadingListEntry e(GURL("http://example.com"), "bar", std::move(backoff));
+ double fuzzing = ReadingListEntry::kBackoffPolicy.jitter_factor;
+
+ e.SetDistilledState(ReadingListEntry::ERROR);
+ ASSERT_NEAR(kFirstBackoff, e.TimeUntilNextTry().InMinutes(),
+ kFirstBackoff * fuzzing);
+
+ // Action.
+ clock.Advance(base::TimeDelta::FromMinutes(kFirstBackoff * 2));
+
+ // Test.
+ EXPECT_EQ(0, e.TimeUntilNextTry().InMilliseconds());
+}
+
+// Tests that if the entry gets a distilled URL, 0 is returned.
+TEST(ReadingListEntry, ResetTimeUntilNextTry) {
+ // Setup.
+ base::SimpleTestTickClock clock;
+ std::unique_ptr<net::BackoffEntry> backoff =
+ base::MakeUnique<net::BackoffEntry>(&ReadingListEntry::kBackoffPolicy,
+ &clock);
+ ReadingListEntry e(GURL("http://example.com"), "bar", std::move(backoff));
+ double fuzzing = ReadingListEntry::kBackoffPolicy.jitter_factor;
+
+ e.SetDistilledState(ReadingListEntry::ERROR);
+ ASSERT_NEAR(kFirstBackoff, e.TimeUntilNextTry().InMinutes(),
+ kFirstBackoff * fuzzing);
+
+ // Action.
+ const base::FilePath distilled_path("distilled/page.html");
+ const GURL distilled_url("http://example.com/distilled");
+ e.SetDistilledInfo(distilled_path, distilled_url);
+
+ // Test.
+ EXPECT_EQ(0, e.TimeUntilNextTry().InSeconds());
+ e.SetDistilledState(ReadingListEntry::ERROR);
+ ASSERT_NEAR(kFirstBackoff, e.TimeUntilNextTry().InMinutes(),
+ kFirstBackoff * fuzzing);
+}
+
+// Tests that the failed download counter is incremented when the state change
+// from non-error to error.
+TEST(ReadingListEntry, FailedDownloadCounter) {
+ ReadingListEntry e(GURL("http://example.com"), "bar");
+
+ ASSERT_EQ(0, e.FailedDownloadCounter());
+
+ e.SetDistilledState(ReadingListEntry::ERROR);
+ EXPECT_EQ(1, e.FailedDownloadCounter());
+ e.SetDistilledState(ReadingListEntry::WILL_RETRY);
+ EXPECT_EQ(1, e.FailedDownloadCounter());
+
+ e.SetDistilledState(ReadingListEntry::PROCESSING);
+ EXPECT_EQ(1, e.FailedDownloadCounter());
+
+ e.SetDistilledState(ReadingListEntry::WILL_RETRY);
+ EXPECT_EQ(2, e.FailedDownloadCounter());
+ e.SetDistilledState(ReadingListEntry::ERROR);
+ EXPECT_EQ(2, e.FailedDownloadCounter());
+}
+
+// Tests that the reading list entry is correctly encoded to
+// sync_pb::ReadingListSpecifics.
+TEST(ReadingListEntry, AsReadingListSpecifics) {
+ ReadingListEntry entry(GURL("http://example.com/"), "bar");
+ int64_t creation_time_us = entry.UpdateTime();
+
+ std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry(
+ entry.AsReadingListSpecifics());
+ EXPECT_EQ(pb_entry->entry_id(), "http://example.com/");
+ EXPECT_EQ(pb_entry->url(), "http://example.com/");
+ EXPECT_EQ(pb_entry->title(), "bar");
+ EXPECT_EQ(pb_entry->creation_time_us(), creation_time_us);
+ EXPECT_EQ(pb_entry->update_time_us(), entry.UpdateTime());
+ EXPECT_EQ(pb_entry->status(), sync_pb::ReadingListSpecifics::UNSEEN);
+
+ entry.SetRead(true);
+ EXPECT_NE(entry.UpdateTime(), creation_time_us);
+ std::unique_ptr<sync_pb::ReadingListSpecifics> updated_pb_entry(
+ entry.AsReadingListSpecifics());
+ EXPECT_EQ(updated_pb_entry->creation_time_us(), creation_time_us);
+ EXPECT_EQ(updated_pb_entry->update_time_us(), entry.UpdateTime());
+ EXPECT_EQ(updated_pb_entry->status(), sync_pb::ReadingListSpecifics::READ);
+}
+
+// Tests that the reading list entry is correctly parsed from
+// sync_pb::ReadingListSpecifics.
+TEST(ReadingListEntry, FromReadingListSpecifics) {
+ std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry =
+ base::MakeUnique<sync_pb::ReadingListSpecifics>();
+ pb_entry->set_entry_id("http://example.com/");
+ pb_entry->set_url("http://example.com/");
+ pb_entry->set_title("title");
+ pb_entry->set_creation_time_us(1);
+ pb_entry->set_update_time_us(2);
+ pb_entry->set_status(sync_pb::ReadingListSpecifics::UNREAD);
+
+ std::unique_ptr<ReadingListEntry> entry(
+ ReadingListEntry::FromReadingListSpecifics(*pb_entry));
+ EXPECT_EQ(entry->URL().spec(), "http://example.com/");
+ EXPECT_EQ(entry->Title(), "title");
+ EXPECT_EQ(entry->UpdateTime(), 2);
+ EXPECT_EQ(entry->FailedDownloadCounter(), 0);
+}
+
+// Tests that the reading list entry is correctly encoded to
+// reading_list::ReadingListLocal.
+TEST(ReadingListEntry, AsReadingListLocal) {
+ ReadingListEntry entry(GURL("http://example.com/"), "bar");
+ int64_t creation_time_us = entry.UpdateTime();
+
+ std::unique_ptr<reading_list::ReadingListLocal> pb_entry(
+ entry.AsReadingListLocal());
+ EXPECT_EQ(pb_entry->entry_id(), "http://example.com/");
+ EXPECT_EQ(pb_entry->url(), "http://example.com/");
+ EXPECT_EQ(pb_entry->title(), "bar");
+ EXPECT_EQ(pb_entry->creation_time_us(), creation_time_us);
+ EXPECT_EQ(pb_entry->update_time_us(), entry.UpdateTime());
+ EXPECT_EQ(pb_entry->status(), reading_list::ReadingListLocal::UNSEEN);
+ EXPECT_EQ(pb_entry->distillation_state(),
+ reading_list::ReadingListLocal::WAITING);
+ EXPECT_EQ(pb_entry->distilled_path(), "");
+ EXPECT_EQ(pb_entry->failed_download_counter(), 0);
+ EXPECT_NE(pb_entry->backoff(), "");
+
+ entry.SetDistilledState(ReadingListEntry::WILL_RETRY);
+ std::unique_ptr<reading_list::ReadingListLocal> will_retry_pb_entry(
+ entry.AsReadingListLocal());
+ EXPECT_EQ(will_retry_pb_entry->distillation_state(),
+ reading_list::ReadingListLocal::WILL_RETRY);
+ EXPECT_EQ(will_retry_pb_entry->failed_download_counter(), 1);
+
+ const base::FilePath distilled_path("distilled/page.html");
+ const GURL distilled_url("http://example.com/distilled");
+ entry.SetDistilledInfo(distilled_path, distilled_url);
+ entry.SetRead(true);
+ entry.MarkEntryUpdated();
+ EXPECT_NE(entry.UpdateTime(), creation_time_us);
+ std::unique_ptr<reading_list::ReadingListLocal> distilled_pb_entry(
+ entry.AsReadingListLocal());
+ EXPECT_EQ(distilled_pb_entry->creation_time_us(), creation_time_us);
+ EXPECT_EQ(distilled_pb_entry->update_time_us(), entry.UpdateTime());
+ EXPECT_NE(distilled_pb_entry->backoff(), "");
+ EXPECT_EQ(distilled_pb_entry->status(), reading_list::ReadingListLocal::READ);
+ EXPECT_EQ(distilled_pb_entry->distillation_state(),
+ reading_list::ReadingListLocal::PROCESSED);
+ EXPECT_EQ(distilled_pb_entry->distilled_path(), "distilled/page.html");
+ EXPECT_EQ(distilled_pb_entry->failed_download_counter(), 0);
+}
+
+// Tests that the reading list entry is correctly parsed from
+// sync_pb::ReadingListLocal.
+TEST(ReadingListEntry, FromReadingListLocal) {
+ ReadingListEntry entry(GURL("http://example.com/"), "title");
+ base::Time next_call = base::Time::Now() + entry.TimeUntilNextTry();
+
+ std::unique_ptr<reading_list::ReadingListLocal> pb_entry(
+ entry.AsReadingListLocal());
+
+ pb_entry->set_entry_id("http://example.com/");
+ pb_entry->set_url("http://example.com/");
+ pb_entry->set_title("title");
+ pb_entry->set_creation_time_us(1);
+ pb_entry->set_update_time_us(2);
+ pb_entry->set_status(reading_list::ReadingListLocal::UNREAD);
+ pb_entry->set_distillation_state(reading_list::ReadingListLocal::WAITING);
+ pb_entry->set_failed_download_counter(2);
+
+ std::unique_ptr<ReadingListEntry> waiting_entry(
+ ReadingListEntry::FromReadingListLocal(*pb_entry));
+ EXPECT_EQ(waiting_entry->URL().spec(), "http://example.com/");
+ EXPECT_EQ(waiting_entry->Title(), "title");
+ EXPECT_EQ(waiting_entry->UpdateTime(), 2);
+ EXPECT_EQ(waiting_entry->FailedDownloadCounter(), 2);
+ EXPECT_EQ(waiting_entry->DistilledState(), ReadingListEntry::WAITING);
+ EXPECT_EQ(waiting_entry->DistilledPath(), base::FilePath());
+ base::Time waiting_next_call =
+ base::Time::Now() + waiting_entry->TimeUntilNextTry();
+ base::TimeDelta delta = next_call - waiting_next_call;
+ EXPECT_NEAR(delta.InMillisecondsRoundedUp(), 0, 10);
+}
+
+// Tests the merging of two ReadingListEntry.
+// Additional merging tests are done in
+// ReadingListStoreTest.CompareEntriesForSync
+TEST(ReadingListEntry, MergeWithEntry) {
+ ReadingListEntry local_entry(GURL("http://example.com/"), "title");
+ local_entry.SetDistilledState(ReadingListEntry::ERROR);
+ base::Time next_call = base::Time::Now() + local_entry.TimeUntilNextTry();
+ int64_t local_update_time_us = local_entry.UpdateTime();
+
+ ReadingListEntry sync_entry(GURL("http://example.com/"), "title2");
+ sync_entry.SetDistilledState(ReadingListEntry::ERROR);
+ int64_t sync_update_time_us = sync_entry.UpdateTime();
+ EXPECT_NE(local_update_time_us, sync_update_time_us);
+ local_entry.MergeWithEntry(sync_entry);
+ EXPECT_EQ(local_entry.URL().spec(), "http://example.com/");
+ EXPECT_EQ(local_entry.Title(), "title2");
+ EXPECT_FALSE(local_entry.HasBeenSeen());
+ EXPECT_EQ(local_entry.UpdateTime(), sync_update_time_us);
+ EXPECT_EQ(local_entry.FailedDownloadCounter(), 1);
+ EXPECT_EQ(local_entry.DistilledState(), ReadingListEntry::ERROR);
+ base::Time merge_next_call =
+ base::Time::Now() + local_entry.TimeUntilNextTry();
+ base::TimeDelta delta = merge_next_call - next_call;
+ EXPECT_NEAR(delta.InMillisecondsRoundedUp(), 0, 10);
+}
diff --git a/chromium/components/reading_list/ios/reading_list_model.cc b/chromium/components/reading_list/ios/reading_list_model.cc
new file mode 100644
index 00000000000..ede2f8d7a69
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_model.cc
@@ -0,0 +1,81 @@
+// 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/ios/reading_list_model.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+
+ReadingListModel::ReadingListModel() : current_batch_updates_count_(0) {}
+
+ReadingListModel::~ReadingListModel() {
+ for (auto& observer : observers_) {
+ observer.ReadingListModelBeingDeleted(this);
+ }
+}
+
+// Observer methods.
+void ReadingListModel::AddObserver(ReadingListModelObserver* observer) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(observer);
+ observers_.AddObserver(observer);
+ if (loaded()) {
+ observer->ReadingListModelLoaded(this);
+ }
+}
+
+void ReadingListModel::RemoveObserver(ReadingListModelObserver* observer) {
+ DCHECK(CalledOnValidThread());
+ observers_.RemoveObserver(observer);
+}
+
+// Batch update methods.
+bool ReadingListModel::IsPerformingBatchUpdates() const {
+ DCHECK(CalledOnValidThread());
+ return current_batch_updates_count_ > 0;
+}
+
+std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
+ReadingListModel::CreateBatchToken() {
+ return base::MakeUnique<ReadingListModel::ScopedReadingListBatchUpdate>(this);
+}
+
+std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
+ReadingListModel::BeginBatchUpdates() {
+ DCHECK(CalledOnValidThread());
+ auto token = CreateBatchToken();
+
+ ++current_batch_updates_count_;
+ if (current_batch_updates_count_ == 1) {
+ EnteringBatchUpdates();
+ }
+ return token;
+}
+
+void ReadingListModel::EnteringBatchUpdates() {
+ DCHECK(CalledOnValidThread());
+ for (auto& observer : observers_)
+ observer.ReadingListModelBeganBatchUpdates(this);
+}
+
+void ReadingListModel::EndBatchUpdates() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(IsPerformingBatchUpdates());
+ DCHECK(current_batch_updates_count_ > 0);
+ --current_batch_updates_count_;
+ if (current_batch_updates_count_ == 0) {
+ LeavingBatchUpdates();
+ }
+}
+
+void ReadingListModel::LeavingBatchUpdates() {
+ DCHECK(CalledOnValidThread());
+ for (auto& observer : observers_)
+ observer.ReadingListModelCompletedBatchUpdates(this);
+}
+
+ReadingListModel::ScopedReadingListBatchUpdate::
+ ~ScopedReadingListBatchUpdate() {
+ model_->EndBatchUpdates();
+}
diff --git a/chromium/components/reading_list/ios/reading_list_model.h b/chromium/components/reading_list/ios/reading_list_model.h
new file mode 100644
index 00000000000..cf7f2bb42b8
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_model.h
@@ -0,0 +1,167 @@
+// 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_IOS_READING_LIST_MODEL_H_
+#define COMPONENTS_READING_LIST_IOS_READING_LIST_MODEL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/observer_list.h"
+#include "base/threading/non_thread_safe.h"
+#include "components/reading_list/ios/reading_list_entry.h"
+#include "components/reading_list/ios/reading_list_model_observer.h"
+
+class GURL;
+class ReadingListModel;
+class ScopedReadingListBatchUpdate;
+
+namespace syncer {
+class ModelTypeSyncBridge;
+}
+
+// The reading list model contains two list of entries: one of unread urls, the
+// 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 {
+ public:
+ class ScopedReadingListBatchUpdate;
+
+ // Returns true if the model finished loading. Until this returns true the
+ // reading list is not ready for use.
+ virtual bool loaded() const = 0;
+
+ // Returns true if the model is performing batch updates right now.
+ bool IsPerformingBatchUpdates() const;
+
+ // Returns the ModelTypeSyncBridge responsible for handling sync message.
+ virtual syncer::ModelTypeSyncBridge* GetModelTypeSyncBridge() = 0;
+
+ // Tells model to prepare for batch updates.
+ // This method is reentrant, i.e. several batch updates may take place at the
+ // same time.
+ // Returns a scoped batch update object that should be retained while the
+ // batch update is performed. Deallocating this object will inform model that
+ // the batch update has completed.
+ std::unique_ptr<ScopedReadingListBatchUpdate> BeginBatchUpdates();
+
+ // Creates a batch token that will freeze the model while in scope.
+ virtual std::unique_ptr<ScopedReadingListBatchUpdate> CreateBatchToken();
+
+ // Returns a vector of URLs in the model. The order of the URL is not
+ // specified and can vary on successive calls.
+ virtual const std::vector<GURL> Keys() const = 0;
+
+ // Returns the total number of entries in the model.
+ virtual size_t size() const = 0;
+
+ // Returns the total number of unread entries in the model.
+ virtual size_t unread_size() const = 0;
+
+ // Returns the total number of unseen entries in the model. Note: These
+ // entries are also unread so unseen_size() <= unread_size().
+ virtual size_t unseen_size() const = 0;
+
+ // Mark all unseen entries as unread.
+ virtual void MarkAllSeen() = 0;
+
+ // Returns the flag about unseen entries on the device.
+ // This flag is raised if some unseen items are added on this device.
+ // The flag is reset if |ResetLocalUnseenFlag| is called or if all unseen
+ // entries are removed.
+ // This is a local flag and it can have different values on different devices,
+ // even if they are synced.
+ // (unseen_size() == 0 => GetLocalUnseenFlag() == false)
+ virtual bool GetLocalUnseenFlag() const = 0;
+
+ // Set the unseen flag to false.
+ virtual void ResetLocalUnseenFlag() = 0;
+
+ // Returns a specific entry. Returns null if the entry does not exist.
+ virtual const ReadingListEntry* GetEntryByURL(const GURL& gurl) const = 0;
+
+ // Returns the first unread entry. If |distilled| is true, prioritize the
+ // entries available offline.
+ virtual const ReadingListEntry* GetFirstUnreadEntry(bool distilled) const = 0;
+
+ // Adds |url| at the top of the unread entries, and removes entries with the
+ // same |url| from everywhere else if they exist. The entry title will be a
+ // trimmed copy of |title.
+ // The addition may be asynchronous, and the data will be available only once
+ // the observers are notified.
+ virtual const ReadingListEntry& AddEntry(
+ const GURL& url,
+ const std::string& title,
+ reading_list::EntrySource source) = 0;
+
+ // Removes an entry. The removal may be asynchronous, and not happen
+ // immediately.
+ virtual void RemoveEntryByURL(const GURL& url) = 0;
+
+ // If the |url| is in the reading list and entry(|url|).read != |read|, sets
+ // the read state of the URL to read. This will also update the update time of
+ // the entry.
+ virtual void SetReadStatus(const GURL& url, bool read) = 0;
+
+ // Methods to mutate an entry. Will locate the relevant entry by URL. Does
+ // nothing if the entry is not found.
+ virtual void SetEntryTitle(const GURL& url, const std::string& title) = 0;
+ virtual void SetEntryDistilledState(
+ const GURL& url,
+ ReadingListEntry::DistillationState state) = 0;
+
+ // Sets the Distilled info for the entry |url|. This method sets the
+ // DistillationState of the entry to PROCESSED and sets the |distilled_path|
+ // (path of the file on disk) and the |distilled_url| (url of the page that
+ // was distilled.
+ virtual void SetEntryDistilledInfo(const GURL& url,
+ const base::FilePath& distilled_path,
+ const GURL& distilled_url) = 0;
+
+ // Observer registration methods. The model will remove all observers upon
+ // destruction automatically.
+ void AddObserver(ReadingListModelObserver* observer);
+ void RemoveObserver(ReadingListModelObserver* observer);
+
+ // Helper class that is used to scope batch updates.
+ class ScopedReadingListBatchUpdate {
+ public:
+ explicit ScopedReadingListBatchUpdate(ReadingListModel* model)
+ : model_(model) {}
+
+ virtual ~ScopedReadingListBatchUpdate();
+
+ private:
+ ReadingListModel* model_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedReadingListBatchUpdate);
+ };
+
+ protected:
+ ReadingListModel();
+ virtual ~ReadingListModel();
+
+ // The observers.
+ base::ObserverList<ReadingListModelObserver> observers_;
+
+ // Tells model that batch updates have completed. Called from
+ // ReadingListBatchUpdateToken dtor.
+ virtual void EndBatchUpdates();
+
+ // Called when model is entering batch update mode.
+ virtual void EnteringBatchUpdates();
+
+ // Called when model is leaving batch update mode.
+ virtual void LeavingBatchUpdates();
+
+ private:
+ unsigned int current_batch_updates_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadingListModel);
+};
+
+#endif // COMPONENTS_READING_LIST_IOS_READING_LIST_MODEL_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
new file mode 100644
index 00000000000..f19e09e20ff
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_model_bridge_observer.h
@@ -0,0 +1,81 @@
+
+// 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_IOS_READING_LIST_MODEL_BRIDGE_OBSERVER_H_
+#define COMPONENTS_READING_LIST_IOS_READING_LIST_MODEL_BRIDGE_OBSERVER_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/macros.h"
+#include "components/reading_list/ios/reading_list_model_observer.h"
+
+// Protocol duplicating all Reading List Model Observer methods in Objective-C.
+@protocol ReadingListModelBridgeObserver<NSObject>
+
+@required
+
+- (void)readingListModelLoaded:(const ReadingListModel*)model;
+- (void)readingListModelDidApplyChanges:(const ReadingListModel*)model;
+
+@optional
+- (void)readingListModel:(const ReadingListModel*)model
+ willRemoveEntry:(const GURL&)url;
+
+- (void)readingListModel:(const ReadingListModel*)model
+ willMoveEntry:(const GURL&)url;
+
+- (void)readingListModel:(const ReadingListModel*)model
+ willAddEntry:(const ReadingListEntry&)entry;
+
+- (void)readingListModel:(const ReadingListModel*)model
+ didAddEntry:(const GURL&)url
+ entrySource:(reading_list::EntrySource)source;
+
+- (void)readingListModelBeganBatchUpdates:(const ReadingListModel*)model;
+- (void)readingListModelCompletedBatchUpdates:(const ReadingListModel*)model;
+
+- (void)readingListModelBeingDeleted:(const ReadingListModel*)model;
+
+- (void)readingListModel:(const ReadingListModel*)model
+ willUpdateEntry:(const GURL&)url;
+
+@end
+
+// Observer for the Reading List model that translates all the callbacks to
+// Objective-C calls.
+class ReadingListModelBridge : public ReadingListModelObserver {
+ public:
+ explicit ReadingListModelBridge(id<ReadingListModelBridgeObserver> observer,
+ ReadingListModel* model);
+ ~ReadingListModelBridge() override;
+
+ private:
+ void ReadingListModelBeganBatchUpdates(
+ const ReadingListModel* model) override;
+
+ void ReadingListModelCompletedBatchUpdates(
+ const ReadingListModel* model) override;
+ void ReadingListModelLoaded(const ReadingListModel* model) override;
+ void ReadingListModelBeingDeleted(const ReadingListModel* model) override;
+ void ReadingListWillRemoveEntry(const ReadingListModel* model,
+ const GURL& url) override;
+ void ReadingListWillMoveEntry(const ReadingListModel* model,
+ const GURL& url) override;
+ void ReadingListWillAddEntry(const ReadingListModel* model,
+ const ReadingListEntry& entry) override;
+ void ReadingListDidAddEntry(const ReadingListModel* model,
+ const GURL& url,
+ reading_list::EntrySource source) override;
+ void ReadingListDidApplyChanges(ReadingListModel* model) override;
+ void ReadingListWillUpdateEntry(const ReadingListModel* model,
+ const GURL& url) override;
+
+ __unsafe_unretained id<ReadingListModelBridgeObserver> observer_;
+ ReadingListModel* model_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(ReadingListModelBridge);
+};
+
+#endif // COMPONENTS_READING_LIST_IOS_READING_LIST_MODEL_BRIDGE_OBSERVER_H_
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
new file mode 100644
index 00000000000..3595d3890c1
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_model_bridge_observer.mm
@@ -0,0 +1,104 @@
+// 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 "components/reading_list/ios/reading_list_model_bridge_observer.h"
+
+#include "components/reading_list/ios/reading_list_entry.h"
+#include "components/reading_list/ios/reading_list_model.h"
+
+ReadingListModelBridge::ReadingListModelBridge(
+ id<ReadingListModelBridgeObserver> observer,
+ ReadingListModel* model)
+ : observer_(observer), model_(model) {
+ DCHECK(model);
+ model_->AddObserver(this);
+}
+
+ReadingListModelBridge::~ReadingListModelBridge() {
+ if (model_) {
+ model_->RemoveObserver(this);
+ }
+}
+
+void ReadingListModelBridge::ReadingListModelLoaded(
+ const ReadingListModel* model) {
+ [observer_ readingListModelLoaded:model];
+}
+
+void ReadingListModelBridge::ReadingListModelBeingDeleted(
+ const ReadingListModel* model) {
+ if ([observer_ respondsToSelector:@selector(readingListModelBeingDeleted:)]) {
+ [observer_ readingListModelBeingDeleted:model];
+ }
+ model_ = nullptr;
+}
+
+void ReadingListModelBridge::ReadingListWillRemoveEntry(
+ const ReadingListModel* model,
+ const GURL& url) {
+ if ([observer_
+ respondsToSelector:@selector(readingListModel:willRemoveEntry:)]) {
+ [observer_ readingListModel:model willRemoveEntry:url];
+ }
+}
+
+void ReadingListModelBridge::ReadingListWillAddEntry(
+ const ReadingListModel* model,
+ const ReadingListEntry& entry) {
+ if ([observer_
+ respondsToSelector:@selector(readingListModel:willAddEntry:)]) {
+ [observer_ readingListModel:model willAddEntry:entry];
+ }
+}
+
+void ReadingListModelBridge::ReadingListDidAddEntry(
+ const ReadingListModel* model,
+ const GURL& url,
+ reading_list::EntrySource source) {
+ if ([observer_ respondsToSelector:@selector(readingListModel:
+ didAddEntry:
+ entrySource:)]) {
+ [observer_ readingListModel:model didAddEntry:url entrySource:source];
+ }
+}
+
+void ReadingListModelBridge::ReadingListDidApplyChanges(
+ ReadingListModel* model) {
+ [observer_ readingListModelDidApplyChanges:model];
+}
+
+void ReadingListModelBridge::ReadingListModelBeganBatchUpdates(
+ const ReadingListModel* model) {
+ if ([observer_
+ respondsToSelector:@selector(readingListModelBeganBatchUpdates:)]) {
+ [observer_ readingListModelBeganBatchUpdates:model];
+ }
+}
+
+void ReadingListModelBridge::ReadingListModelCompletedBatchUpdates(
+ const ReadingListModel* model) {
+ if ([observer_
+ respondsToSelector:@selector(
+ readingListModelCompletedBatchUpdates:)]) {
+ [observer_ readingListModelCompletedBatchUpdates:model];
+ }
+}
+
+void ReadingListModelBridge::ReadingListWillMoveEntry(
+ const ReadingListModel* model,
+ const GURL& url) {
+ if ([observer_
+ respondsToSelector:@selector(readingListModel:willMoveEntry:)]) {
+ [observer_ readingListModel:model willMoveEntry:url];
+ }
+}
+
+void ReadingListModelBridge::ReadingListWillUpdateEntry(
+ const ReadingListModel* model,
+ const GURL& url) {
+ if ([observer_
+ respondsToSelector:@selector(readingListModel:willUpdateEntry:)]) {
+ [observer_ readingListModel:model willUpdateEntry:url];
+ }
+}
diff --git a/chromium/components/reading_list/ios/reading_list_model_impl.cc b/chromium/components/reading_list/ios/reading_list_model_impl.cc
new file mode 100644
index 00000000000..015bcc44970
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_model_impl.cc
@@ -0,0 +1,505 @@
+// 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/ios/reading_list_model_impl.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "components/prefs/pref_service.h"
+#include "components/reading_list/ios/reading_list_model_storage.h"
+#include "components/reading_list/ios/reading_list_pref_names.h"
+#include "url/gurl.h"
+
+ReadingListModelImpl::ReadingListModelImpl()
+ : ReadingListModelImpl(nullptr, nullptr) {}
+
+ReadingListModelImpl::ReadingListModelImpl(
+ std::unique_ptr<ReadingListModelStorage> storage,
+ PrefService* pref_service)
+ : unread_entry_count_(0),
+ read_entry_count_(0),
+ unseen_entry_count_(0),
+ pref_service_(pref_service),
+ has_unseen_(false),
+ loaded_(false),
+ weak_ptr_factory_(this) {
+ DCHECK(CalledOnValidThread());
+ if (storage) {
+ storage_layer_ = std::move(storage);
+ storage_layer_->SetReadingListModel(this, this);
+ } else {
+ loaded_ = true;
+ entries_ = base::MakeUnique<ReadingListEntries>();
+ }
+ has_unseen_ = GetPersistentHasUnseen();
+}
+
+ReadingListModelImpl::~ReadingListModelImpl() {}
+
+void ReadingListModelImpl::StoreLoaded(
+ std::unique_ptr<ReadingListEntries> entries) {
+ DCHECK(CalledOnValidThread());
+ entries_ = std::move(entries);
+ for (auto& iterator : *entries_) {
+ UpdateEntryStateCountersOnEntryInsertion(iterator.second);
+ }
+ DCHECK(read_entry_count_ + unread_entry_count_ == entries_->size());
+ loaded_ = true;
+ for (auto& observer : observers_)
+ observer.ReadingListModelLoaded(this);
+}
+
+void ReadingListModelImpl::Shutdown() {
+ DCHECK(CalledOnValidThread());
+ for (auto& observer : observers_)
+ observer.ReadingListModelBeingDeleted(this);
+ loaded_ = false;
+}
+
+bool ReadingListModelImpl::loaded() const {
+ DCHECK(CalledOnValidThread());
+ return loaded_;
+}
+
+size_t ReadingListModelImpl::size() const {
+ DCHECK(CalledOnValidThread());
+ DCHECK(read_entry_count_ + unread_entry_count_ == entries_->size());
+ if (!loaded())
+ return 0;
+ return entries_->size();
+}
+
+size_t ReadingListModelImpl::unread_size() const {
+ DCHECK(CalledOnValidThread());
+ DCHECK(read_entry_count_ + unread_entry_count_ == entries_->size());
+ if (!loaded())
+ return 0;
+ return unread_entry_count_;
+}
+
+size_t ReadingListModelImpl::unseen_size() const {
+ DCHECK(CalledOnValidThread());
+ if (!loaded())
+ return 0;
+ return unseen_entry_count_;
+}
+
+void ReadingListModelImpl::SetUnseenFlag() {
+ if (!has_unseen_) {
+ has_unseen_ = true;
+ if (!IsPerformingBatchUpdates()) {
+ SetPersistentHasUnseen(true);
+ }
+ }
+}
+
+bool ReadingListModelImpl::GetLocalUnseenFlag() const {
+ DCHECK(CalledOnValidThread());
+ if (!loaded())
+ return false;
+ // If there are currently no unseen entries, return false even if has_unseen_
+ // is true.
+ // This is possible if the last unseen entry has be removed via sync.
+ return has_unseen_ && unseen_entry_count_;
+}
+
+void ReadingListModelImpl::ResetLocalUnseenFlag() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ has_unseen_ = false;
+ if (!IsPerformingBatchUpdates())
+ SetPersistentHasUnseen(false);
+}
+
+void ReadingListModelImpl::MarkAllSeen() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ if (unseen_entry_count_ == 0) {
+ return;
+ }
+ std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
+ model_batch_updates = BeginBatchUpdates();
+ for (auto& iterator : *entries_) {
+ ReadingListEntry& entry = iterator.second;
+ if (entry.HasBeenSeen()) {
+ continue;
+ }
+ for (auto& observer : observers_) {
+ observer.ReadingListWillUpdateEntry(this, iterator.first);
+ }
+ UpdateEntryStateCountersOnEntryRemoval(entry);
+ entry.SetRead(false);
+ UpdateEntryStateCountersOnEntryInsertion(entry);
+ if (storage_layer_) {
+ storage_layer_->SaveEntry(entry);
+ }
+ for (auto& observer : observers_) {
+ observer.ReadingListDidApplyChanges(this);
+ }
+ }
+ DCHECK(unseen_entry_count_ == 0);
+}
+
+void ReadingListModelImpl::UpdateEntryStateCountersOnEntryRemoval(
+ const ReadingListEntry& entry) {
+ if (!entry.HasBeenSeen()) {
+ unseen_entry_count_--;
+ }
+ if (entry.IsRead()) {
+ read_entry_count_--;
+ } else {
+ unread_entry_count_--;
+ }
+}
+
+void ReadingListModelImpl::UpdateEntryStateCountersOnEntryInsertion(
+ const ReadingListEntry& entry) {
+ if (!entry.HasBeenSeen()) {
+ unseen_entry_count_++;
+ }
+ if (entry.IsRead()) {
+ read_entry_count_++;
+ } else {
+ unread_entry_count_++;
+ }
+}
+
+const std::vector<GURL> ReadingListModelImpl::Keys() const {
+ std::vector<GURL> keys;
+ for (const auto& iterator : *entries_) {
+ keys.push_back(iterator.first);
+ }
+ return keys;
+}
+
+const ReadingListEntry* ReadingListModelImpl::GetEntryByURL(
+ const GURL& gurl) const {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ return GetMutableEntryFromURL(gurl);
+}
+
+const ReadingListEntry* ReadingListModelImpl::GetFirstUnreadEntry(
+ bool distilled) const {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ if (unread_entry_count_ == 0) {
+ return nullptr;
+ }
+ int64_t update_time_all = 0;
+ const ReadingListEntry* first_entry_all = nullptr;
+ int64_t update_time_distilled = 0;
+ const ReadingListEntry* first_entry_distilled = nullptr;
+ for (auto& iterator : *entries_) {
+ ReadingListEntry& entry = iterator.second;
+ if (entry.IsRead()) {
+ continue;
+ }
+ if (entry.UpdateTime() > update_time_all) {
+ update_time_all = entry.UpdateTime();
+ first_entry_all = &entry;
+ }
+ if (entry.DistilledState() == ReadingListEntry::PROCESSED &&
+ entry.UpdateTime() > update_time_distilled) {
+ update_time_distilled = entry.UpdateTime();
+ first_entry_distilled = &entry;
+ }
+ }
+ DCHECK(first_entry_all);
+ DCHECK_GT(update_time_all, 0);
+ if (distilled && first_entry_distilled) {
+ return first_entry_distilled;
+ }
+ return first_entry_all;
+}
+
+ReadingListEntry* ReadingListModelImpl::GetMutableEntryFromURL(
+ const GURL& url) const {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ auto iterator = entries_->find(url);
+ if (iterator == entries_->end()) {
+ return nullptr;
+ }
+ return &(iterator->second);
+}
+
+void ReadingListModelImpl::SyncAddEntry(
+ std::unique_ptr<ReadingListEntry> entry) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ // entry must not already exist.
+ DCHECK(GetMutableEntryFromURL(entry->URL()) == nullptr);
+ for (auto& observer : observers_)
+ observer.ReadingListWillAddEntry(this, *entry);
+ UpdateEntryStateCountersOnEntryInsertion(*entry);
+ if (!entry->HasBeenSeen()) {
+ SetUnseenFlag();
+ }
+ GURL url = entry->URL();
+ entries_->insert(std::make_pair(url, std::move(*entry)));
+ for (auto& observer : observers_) {
+ observer.ReadingListDidAddEntry(this, url, reading_list::ADDED_VIA_SYNC);
+ observer.ReadingListDidApplyChanges(this);
+ }
+}
+
+ReadingListEntry* ReadingListModelImpl::SyncMergeEntry(
+ std::unique_ptr<ReadingListEntry> entry) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ ReadingListEntry* existing_entry = GetMutableEntryFromURL(entry->URL());
+ DCHECK(existing_entry);
+ GURL url = entry->URL();
+
+ for (auto& observer : observers_)
+ observer.ReadingListWillMoveEntry(this, url);
+
+ bool was_seen = existing_entry->HasBeenSeen();
+ UpdateEntryStateCountersOnEntryRemoval(*existing_entry);
+ existing_entry->MergeWithEntry(*entry);
+ existing_entry = GetMutableEntryFromURL(url);
+ UpdateEntryStateCountersOnEntryInsertion(*existing_entry);
+ if (was_seen && !existing_entry->HasBeenSeen()) {
+ // Only set the flag if a new unseen entry is added.
+ SetUnseenFlag();
+ }
+ for (auto& observer : observers_) {
+ observer.ReadingListDidMoveEntry(this, url);
+ observer.ReadingListDidApplyChanges(this);
+ }
+
+ return existing_entry;
+}
+
+void ReadingListModelImpl::SyncRemoveEntry(const GURL& url) {
+ RemoveEntryByURLImpl(url, true);
+}
+
+void ReadingListModelImpl::RemoveEntryByURL(const GURL& url) {
+ RemoveEntryByURLImpl(url, false);
+}
+
+void ReadingListModelImpl::RemoveEntryByURLImpl(const GURL& url,
+ bool from_sync) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ const ReadingListEntry* entry = GetEntryByURL(url);
+ if (!entry)
+ return;
+
+ for (auto& observer : observers_)
+ observer.ReadingListWillRemoveEntry(this, url);
+
+ if (storage_layer_ && !from_sync) {
+ storage_layer_->RemoveEntry(*entry);
+ }
+ UpdateEntryStateCountersOnEntryRemoval(*entry);
+
+ entries_->erase(url);
+ for (auto& observer : observers_)
+ observer.ReadingListDidApplyChanges(this);
+}
+
+const ReadingListEntry& ReadingListModelImpl::AddEntry(
+ const GURL& url,
+ const std::string& title,
+ reading_list::EntrySource source) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ DCHECK(url.SchemeIsHTTPOrHTTPS());
+ RemoveEntryByURL(url);
+
+ std::string trimmedTitle(title);
+ base::TrimWhitespaceASCII(trimmedTitle, base::TRIM_ALL, &trimmedTitle);
+
+ ReadingListEntry entry(url, trimmedTitle);
+ for (auto& observer : observers_)
+ observer.ReadingListWillAddEntry(this, entry);
+ UpdateEntryStateCountersOnEntryInsertion(entry);
+ SetUnseenFlag();
+ entries_->insert(std::make_pair(url, std::move(entry)));
+
+ if (storage_layer_) {
+ storage_layer_->SaveEntry(*GetEntryByURL(url));
+ }
+
+ for (auto& observer : observers_) {
+ observer.ReadingListDidAddEntry(this, url, source);
+ observer.ReadingListDidApplyChanges(this);
+ }
+
+ return entries_->at(url);
+}
+
+void ReadingListModelImpl::SetReadStatus(const GURL& url, bool read) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ auto iterator = entries_->find(url);
+ if (iterator == entries_->end()) {
+ return;
+ }
+ ReadingListEntry& entry = iterator->second;
+ if (entry.IsRead() == read) {
+ return;
+ }
+ for (ReadingListModelObserver& observer : observers_) {
+ observer.ReadingListWillMoveEntry(this, url);
+ }
+ UpdateEntryStateCountersOnEntryRemoval(entry);
+ entry.SetRead(read);
+ entry.MarkEntryUpdated();
+ UpdateEntryStateCountersOnEntryInsertion(entry);
+
+ if (storage_layer_) {
+ storage_layer_->SaveEntry(entry);
+ }
+ for (ReadingListModelObserver& observer : observers_) {
+ observer.ReadingListDidMoveEntry(this, url);
+ observer.ReadingListDidApplyChanges(this);
+ }
+}
+
+void ReadingListModelImpl::SetEntryTitle(const GURL& url,
+ const std::string& title) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ auto iterator = entries_->find(url);
+ if (iterator == entries_->end()) {
+ return;
+ }
+ ReadingListEntry& entry = iterator->second;
+ if (entry.Title() == title) {
+ return;
+ }
+
+ for (ReadingListModelObserver& observer : observers_) {
+ observer.ReadingListWillUpdateEntry(this, url);
+ }
+ entry.SetTitle(title);
+ if (storage_layer_) {
+ storage_layer_->SaveEntry(entry);
+ }
+ for (ReadingListModelObserver& observer : observers_) {
+ observer.ReadingListDidApplyChanges(this);
+ }
+}
+
+void ReadingListModelImpl::SetEntryDistilledInfo(
+ const GURL& url,
+ const base::FilePath& distilled_path,
+ const GURL& distilled_url) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ auto iterator = entries_->find(url);
+ if (iterator == entries_->end()) {
+ return;
+ }
+ ReadingListEntry& entry = iterator->second;
+ if (entry.DistilledState() == ReadingListEntry::PROCESSED &&
+ entry.DistilledPath() == distilled_path) {
+ return;
+ }
+
+ for (ReadingListModelObserver& observer : observers_) {
+ observer.ReadingListWillUpdateEntry(this, url);
+ }
+ entry.SetDistilledInfo(distilled_path, distilled_url);
+ if (storage_layer_) {
+ storage_layer_->SaveEntry(entry);
+ }
+ for (ReadingListModelObserver& observer : observers_) {
+ observer.ReadingListDidApplyChanges(this);
+ }
+}
+
+void ReadingListModelImpl::SetEntryDistilledState(
+ const GURL& url,
+ ReadingListEntry::DistillationState state) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loaded());
+ auto iterator = entries_->find(url);
+ if (iterator == entries_->end()) {
+ return;
+ }
+ ReadingListEntry& entry = iterator->second;
+ if (entry.DistilledState() == state) {
+ return;
+ }
+
+ for (ReadingListModelObserver& observer : observers_) {
+ observer.ReadingListWillUpdateEntry(this, url);
+ }
+ entry.SetDistilledState(state);
+ if (storage_layer_) {
+ storage_layer_->SaveEntry(entry);
+ }
+ for (ReadingListModelObserver& observer : observers_) {
+ observer.ReadingListDidApplyChanges(this);
+ }
+}
+
+std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
+ReadingListModelImpl::CreateBatchToken() {
+ return base::MakeUnique<ReadingListModelImpl::ScopedReadingListBatchUpdate>(
+ this);
+}
+
+ReadingListModelImpl::ScopedReadingListBatchUpdate::
+ ScopedReadingListBatchUpdate(ReadingListModelImpl* model)
+ : ReadingListModel::ScopedReadingListBatchUpdate::
+ ScopedReadingListBatchUpdate(model) {
+ if (model->StorageLayer()) {
+ storage_token_ = model->StorageLayer()->EnsureBatchCreated();
+ }
+}
+
+ReadingListModelImpl::ScopedReadingListBatchUpdate::
+ ~ScopedReadingListBatchUpdate() {
+ storage_token_.reset();
+}
+
+void ReadingListModelImpl::LeavingBatchUpdates() {
+ DCHECK(CalledOnValidThread());
+ if (storage_layer_) {
+ SetPersistentHasUnseen(has_unseen_);
+ }
+ ReadingListModel::LeavingBatchUpdates();
+}
+
+void ReadingListModelImpl::EnteringBatchUpdates() {
+ DCHECK(CalledOnValidThread());
+ ReadingListModel::EnteringBatchUpdates();
+}
+
+void ReadingListModelImpl::SetPersistentHasUnseen(bool has_unseen) {
+ DCHECK(CalledOnValidThread());
+ if (!pref_service_) {
+ return;
+ }
+ pref_service_->SetBoolean(reading_list::prefs::kReadingListHasUnseenEntries,
+ has_unseen);
+}
+
+bool ReadingListModelImpl::GetPersistentHasUnseen() {
+ DCHECK(CalledOnValidThread());
+ if (!pref_service_) {
+ return false;
+ }
+ return pref_service_->GetBoolean(
+ reading_list::prefs::kReadingListHasUnseenEntries);
+}
+
+syncer::ModelTypeSyncBridge* ReadingListModelImpl::GetModelTypeSyncBridge() {
+ DCHECK(loaded());
+ if (!storage_layer_)
+ return nullptr;
+ return storage_layer_.get();
+}
+
+ReadingListModelStorage* ReadingListModelImpl::StorageLayer() {
+ return storage_layer_.get();
+}
diff --git a/chromium/components/reading_list/ios/reading_list_model_impl.h b/chromium/components/reading_list/ios/reading_list_model_impl.h
new file mode 100644
index 00000000000..72258860816
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_model_impl.h
@@ -0,0 +1,140 @@
+// 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_IOS_READING_LIST_MODEL_IMPL_H_
+#define COMPONENTS_READING_LIST_IOS_READING_LIST_MODEL_IMPL_H_
+
+#include <map>
+#include <memory>
+
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/reading_list/ios/reading_list_entry.h"
+#include "components/reading_list/ios/reading_list_model.h"
+#include "components/reading_list/ios/reading_list_model_storage.h"
+#include "components/reading_list/ios/reading_list_store_delegate.h"
+
+class PrefService;
+
+// Concrete implementation of a reading list model using in memory lists.
+class ReadingListModelImpl : public ReadingListModel,
+ public ReadingListStoreDelegate,
+ public KeyedService {
+ public:
+ using ReadingListEntries = std::map<GURL, ReadingListEntry>;
+
+ // Initialize a ReadingListModelImpl to load and save data in
+ // |persistence_layer|.
+ ReadingListModelImpl(std::unique_ptr<ReadingListModelStorage> storage_layer,
+ PrefService* pref_service);
+
+ // Initialize a ReadingListModelImpl without persistence. Data will not be
+ // persistent across sessions.
+ ReadingListModelImpl();
+
+ syncer::ModelTypeSyncBridge* GetModelTypeSyncBridge() override;
+
+ ~ReadingListModelImpl() override;
+
+ void StoreLoaded(std::unique_ptr<ReadingListEntries> entries) override;
+
+ // KeyedService implementation.
+ void Shutdown() override;
+
+ // ReadingListModel implementation.
+ bool loaded() const override;
+
+ size_t size() const override;
+ size_t unread_size() const override;
+ size_t unseen_size() const override;
+
+ void MarkAllSeen() override;
+ bool GetLocalUnseenFlag() const override;
+ void ResetLocalUnseenFlag() override;
+
+ const std::vector<GURL> Keys() const override;
+
+ const ReadingListEntry* GetEntryByURL(const GURL& gurl) const override;
+ const ReadingListEntry* GetFirstUnreadEntry(bool distilled) const override;
+
+ void RemoveEntryByURL(const GURL& url) override;
+
+ const ReadingListEntry& AddEntry(const GURL& url,
+ const std::string& title,
+ reading_list::EntrySource source) override;
+
+ void SetReadStatus(const GURL& url, bool read) override;
+
+ void SetEntryTitle(const GURL& url, const std::string& title) override;
+ void SetEntryDistilledState(
+ const GURL& url,
+ ReadingListEntry::DistillationState state) override;
+ void SetEntryDistilledInfo(const GURL& url,
+ const base::FilePath& distilled_path,
+ const GURL& distilled_url) override;
+
+ void SyncAddEntry(std::unique_ptr<ReadingListEntry> entry) override;
+ ReadingListEntry* SyncMergeEntry(
+ std::unique_ptr<ReadingListEntry> entry) override;
+ void SyncRemoveEntry(const GURL& url) override;
+
+ std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
+ CreateBatchToken() override;
+
+ // Helper class that is used to scope batch updates.
+ class ScopedReadingListBatchUpdate
+ : public ReadingListModel::ScopedReadingListBatchUpdate {
+ public:
+ explicit ScopedReadingListBatchUpdate(ReadingListModelImpl* model);
+
+ ~ScopedReadingListBatchUpdate() override;
+
+ private:
+ std::unique_ptr<ReadingListModelStorage::ScopedBatchUpdate> storage_token_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedReadingListBatchUpdate);
+ };
+
+ protected:
+ void EnteringBatchUpdates() override;
+ void LeavingBatchUpdates() override;
+
+ private:
+ // Sets/Loads the pref flag that indicate if some entries have never been seen
+ // since being added to the store.
+ void SetPersistentHasUnseen(bool has_unseen);
+ bool GetPersistentHasUnseen();
+
+ // Returns a mutable pointer to the entry with URL |url|. Return nullptr if
+ // no entry is found.
+ ReadingListEntry* GetMutableEntryFromURL(const GURL& url) const;
+
+ // Returns the |storage_layer_| of the model.
+ ReadingListModelStorage* StorageLayer();
+
+ // Remove entry |url| and propagate to store if |from_sync| is false.
+ void RemoveEntryByURLImpl(const GURL& url, bool from_sync);
+
+ void RebuildIndex() const;
+
+ std::unique_ptr<ReadingListEntries> entries_;
+ size_t unread_entry_count_;
+ size_t read_entry_count_;
+ size_t unseen_entry_count_;
+
+ // Update the 3 counts above considering addition/removal of |entry|.
+ void UpdateEntryStateCountersOnEntryRemoval(const ReadingListEntry& entry);
+ void UpdateEntryStateCountersOnEntryInsertion(const ReadingListEntry& entry);
+
+ // Set the unseen flag to true.
+ void SetUnseenFlag();
+
+ std::unique_ptr<ReadingListModelStorage> storage_layer_;
+ PrefService* pref_service_;
+ bool has_unseen_;
+ bool loaded_;
+ base::WeakPtrFactory<ReadingListModelImpl> weak_ptr_factory_;
+ DISALLOW_COPY_AND_ASSIGN(ReadingListModelImpl);
+};
+
+#endif // COMPONENTS_READING_LIST_IOS_READING_LIST_MODEL_IMPL_H_
diff --git a/chromium/components/reading_list/ios/reading_list_model_observer.h b/chromium/components/reading_list/ios/reading_list_model_observer.h
new file mode 100644
index 00000000000..ffaaed806e0
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_model_observer.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_READING_LIST_IOS_READING_LIST_MODEL_OBSERVER_H_
+#define COMPONENTS_READING_LIST_IOS_READING_LIST_MODEL_OBSERVER_H_
+
+#include <set>
+#include <vector>
+
+#include "components/reading_list/ios/reading_list_entry.h"
+
+class GURL;
+class ReadingListModel;
+
+// Observer for the Reading List model. In the observer methods care should be
+// taken to not modify the model.
+class ReadingListModelObserver {
+ public:
+ // Invoked when the model has finished loading. Until this method is called it
+ // is unsafe to use the model.
+ virtual void ReadingListModelLoaded(const ReadingListModel* model) = 0;
+
+ // Invoked when the batch updates are about to start. It will only be called
+ // once before ReadingListModelCompletedBatchUpdates, even if several updates
+ // are taking place at the same time.
+ virtual void ReadingListModelBeganBatchUpdates(
+ const ReadingListModel* model) {}
+
+ // Invoked when the batch updates have completed. This is called once all
+ // batch updates are completed.
+ virtual void ReadingListModelCompletedBatchUpdates(
+ const ReadingListModel* model) {}
+
+ // Invoked from the destructor of the model. The model is no longer valid
+ // after this call. There is no need to call RemoveObserver on the model from
+ // here, as the observers are automatically deleted.
+ virtual void ReadingListModelBeingDeleted(const ReadingListModel* model) {}
+
+ // Invoked when elements are about to be removed from the read or unread list.
+ virtual void ReadingListWillRemoveEntry(const ReadingListModel* model,
+ const GURL& url) {}
+ // Invoked when elements |MarkEntryUpdated| is called on an entry. This means
+ // that the order of the entry may change and read/unread list may change
+ // too.
+ virtual void ReadingListWillMoveEntry(const ReadingListModel* model,
+ const GURL& url) {}
+
+ // Invoked when elements |MarkEntryUpdated| has been called on an entry. This
+ // means that the order of the entry may have changed and read/unread list may
+ // have changed too.
+ virtual void ReadingListDidMoveEntry(const ReadingListModel* model,
+ const GURL& url) {}
+
+ // Invoked when elements are added.
+ virtual void ReadingListWillAddEntry(const ReadingListModel* model,
+ const ReadingListEntry& entry) {}
+
+ // Invoked when elements have been added. This method is called after the
+ // the entry has been added to the model and the entry can now be retrieved
+ // from the model.
+ // |source| says where the entry came from.
+ virtual void ReadingListDidAddEntry(const ReadingListModel* model,
+ const GURL& url,
+ reading_list::EntrySource source) {}
+
+ // Invoked when an entry is about to change.
+ virtual void ReadingListWillUpdateEntry(const ReadingListModel* model,
+ const GURL& url) {}
+
+ // Called after all the changes signaled by calls to the "Will" methods are
+ // done. All the "Will" methods are called as necessary, then the changes
+ // are applied and then this method is called.
+ virtual void ReadingListDidApplyChanges(ReadingListModel* model) {}
+
+ protected:
+ ReadingListModelObserver() {}
+ virtual ~ReadingListModelObserver() {}
+
+ DISALLOW_COPY_AND_ASSIGN(ReadingListModelObserver);
+};
+
+#endif // COMPONENTS_READING_LIST_IOS_READING_LIST_MODEL_OBSERVER_H_
diff --git a/chromium/components/reading_list/ios/reading_list_model_storage.cc b/chromium/components/reading_list/ios/reading_list_model_storage.cc
new file mode 100644
index 00000000000..c1dbd05ca47
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_model_storage.cc
@@ -0,0 +1,12 @@
+// 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/ios/reading_list_model_storage.h"
+
+ReadingListModelStorage::ReadingListModelStorage(
+ const ChangeProcessorFactory& change_processor_factory,
+ syncer::ModelType type)
+ : ModelTypeSyncBridge(change_processor_factory, type) {}
+
+ReadingListModelStorage::~ReadingListModelStorage() {}
diff --git a/chromium/components/reading_list/ios/reading_list_model_storage.h b/chromium/components/reading_list/ios/reading_list_model_storage.h
new file mode 100644
index 00000000000..f0ac31a9a33
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_model_storage.h
@@ -0,0 +1,67 @@
+// 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_IOS_READING_LIST_MODEL_STORAGE_H_
+#define COMPONENTS_READING_LIST_IOS_READING_LIST_MODEL_STORAGE_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "components/reading_list/ios/reading_list_entry.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+
+class ReadingListModel;
+class ReadingListStoreDelegate;
+
+namespace syncer {
+class ModelTypeSyncBridge;
+}
+
+// Interface for a persistence layer for reading list.
+// All interface methods have to be called on main thread.
+class ReadingListModelStorage : public syncer::ModelTypeSyncBridge {
+ public:
+ class ScopedBatchUpdate;
+
+ ReadingListModelStorage(
+ const ChangeProcessorFactory& change_processor_factory,
+ syncer::ModelType type);
+ ~ReadingListModelStorage() override;
+
+ // Sets the model the Storage is backing.
+ // This will trigger store initalization and load persistent entries.
+ virtual void SetReadingListModel(ReadingListModel* model,
+ ReadingListStoreDelegate* delegate) = 0;
+
+ // Starts a transaction. All Save/Remove entry will be delayed until the
+ // transaction is commited.
+ // Multiple transaction can be started at the same time. Commit will happen
+ // when the last transaction is commited.
+ // Returns a scoped batch update object that should be retained while the
+ // batch update is performed. Deallocating this object will inform model that
+ // the batch update has completed.
+ virtual std::unique_ptr<ScopedBatchUpdate> EnsureBatchCreated() = 0;
+
+ // Saves or updates an entry. If the entry is not yet in the database, it is
+ // created.
+ virtual void SaveEntry(const ReadingListEntry& entry) = 0;
+
+ // Removed an entry from the storage.
+ virtual void RemoveEntry(const ReadingListEntry& entry) = 0;
+
+ class ScopedBatchUpdate {
+ public:
+ ScopedBatchUpdate() {}
+ virtual ~ScopedBatchUpdate() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedBatchUpdate);
+ };
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReadingListModelStorage);
+};
+
+#endif // COMPONENTS_READING_LIST_IOS_READING_LIST_MODEL_STORAGE_H_
diff --git a/chromium/components/reading_list/ios/reading_list_model_unittest.mm b/chromium/components/reading_list/ios/reading_list_model_unittest.mm
new file mode 100644
index 00000000000..d92b042518d
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_model_unittest.mm
@@ -0,0 +1,686 @@
+// 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/ios/reading_list_model.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#import "base/test/ios/wait_util.h"
+#include "components/reading_list/ios/reading_list_model_impl.h"
+#include "components/reading_list/ios/reading_list_model_storage.h"
+#include "components/reading_list/ios/reading_list_store_delegate.h"
+#include "components/sync/model/metadata_change_list.h"
+#include "components/sync/model/model_error.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const GURL callback_url("http://example.com");
+const std::string callback_title("test title");
+
+class TestReadingListStorageObserver {
+ public:
+ virtual void ReadingListDidSaveEntry() = 0;
+ virtual void ReadingListDidRemoveEntry() = 0;
+};
+
+class TestReadingListStorage : public ReadingListModelStorage {
+ public:
+ TestReadingListStorage(TestReadingListStorageObserver* observer)
+ : ReadingListModelStorage(
+ base::Bind(&syncer::ModelTypeChangeProcessor::Create,
+ base::RepeatingClosure()),
+ syncer::READING_LIST),
+ entries_(new ReadingListStoreDelegate::ReadingListEntries()),
+ observer_(observer) {}
+
+ void AddSampleEntries() {
+ // Adds timer and interlace read/unread entry creation to avoid having two
+ // entries with the same creation timestamp.
+ ReadingListEntry unread_a(GURL("http://unread_a.com"), "unread_a");
+ entries_->insert(
+ std::make_pair(GURL("http://unread_a.com"), std::move(unread_a)));
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(5));
+
+ ReadingListEntry read_a(GURL("http://read_a.com"), "read_a");
+ read_a.SetRead(true);
+ entries_->insert(
+ std::make_pair(GURL("http://read_a.com"), std::move(read_a)));
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(5));
+
+ ReadingListEntry unread_b(GURL("http://unread_b.com"), "unread_b");
+ entries_->insert(
+ std::make_pair(GURL("http://unread_b.com"), std::move(unread_b)));
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(5));
+
+ ReadingListEntry read_b(GURL("http://read_b.com"), "read_b");
+ read_b.SetRead(true);
+ entries_->insert(
+ std::make_pair(GURL("http://read_b.com"), std::move(read_b)));
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(5));
+
+ ReadingListEntry unread_c(GURL("http://unread_c.com"), "unread_c");
+ entries_->insert(
+ std::make_pair(GURL("http://unread_c.com"), std::move(unread_c)));
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(5));
+
+ ReadingListEntry read_c(GURL("http://read_c.com"), "read_c");
+ read_c.SetRead(true);
+ entries_->insert(
+ std::make_pair(GURL("http://read_c.com"), std::move(read_c)));
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(5));
+
+ ReadingListEntry unread_d(GURL("http://unread_d.com"), "unread_d");
+ entries_->insert(
+ std::make_pair(GURL("http://unread_d.com"), std::move(unread_d)));
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(5));
+ }
+
+ void SetReadingListModel(ReadingListModel* model,
+ ReadingListStoreDelegate* delegate) override {
+ delegate->StoreLoaded(std::move(entries_));
+ }
+
+ // Saves or updates an entry. If the entry is not yet in the database, it is
+ // created.
+ void SaveEntry(const ReadingListEntry& entry) override {
+ observer_->ReadingListDidSaveEntry();
+ }
+
+ // Removes an entry from the storage.
+ void RemoveEntry(const ReadingListEntry& entry) override {
+ observer_->ReadingListDidRemoveEntry();
+ }
+
+ std::unique_ptr<ScopedBatchUpdate> EnsureBatchCreated() override {
+ return std::unique_ptr<ScopedBatchUpdate>();
+ }
+
+ // Syncing is not used in this test class.
+ std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+ override {
+ NOTREACHED();
+ return std::unique_ptr<syncer::MetadataChangeList>();
+ }
+
+ base::Optional<syncer::ModelError> MergeSyncData(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityDataMap entity_data_map) override {
+ NOTREACHED();
+ return {};
+ }
+
+ base::Optional<syncer::ModelError> ApplySyncChanges(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_changes) override {
+ NOTREACHED();
+ return {};
+ }
+
+ void GetData(StorageKeyList storage_keys, DataCallback callback) override {
+ NOTREACHED();
+ return;
+ }
+
+ void GetAllData(DataCallback callback) override {
+ NOTREACHED();
+ return;
+ }
+
+ std::string GetClientTag(const syncer::EntityData& entity_data) override {
+ NOTREACHED();
+ return "";
+ }
+
+ std::string GetStorageKey(const syncer::EntityData& entity_data) override {
+ NOTREACHED();
+ return "";
+ }
+
+ private:
+ std::unique_ptr<ReadingListStoreDelegate::ReadingListEntries> entries_;
+ TestReadingListStorageObserver* observer_;
+};
+
+class ReadingListModelTest : public ReadingListModelObserver,
+ public TestReadingListStorageObserver,
+ public testing::Test {
+ public:
+ ReadingListModelTest()
+ : callback_called_(false), model_(new ReadingListModelImpl()) {
+ ClearCounts();
+ model_->AddObserver(this);
+ }
+ ~ReadingListModelTest() override {}
+
+ void SetStorage(std::unique_ptr<TestReadingListStorage> storage) {
+ model_ =
+ base::MakeUnique<ReadingListModelImpl>(std::move(storage), nullptr);
+ ClearCounts();
+ model_->AddObserver(this);
+ }
+
+ void ClearCounts() {
+ observer_loaded_ = observer_started_batch_update_ =
+ observer_completed_batch_update_ = observer_deleted_ =
+ observer_remove_ = observer_move_ = observer_add_ =
+ observer_did_add_ = observer_update_ = observer_did_apply_ =
+ storage_saved_ = storage_removed_ = 0;
+ }
+
+ void AssertObserverCount(int observer_loaded,
+ int observer_started_batch_update,
+ int observer_completed_batch_update,
+ int observer_deleted,
+ int observer_remove,
+ int observer_move,
+ int observer_add,
+ int observer_update,
+ int observer_did_apply) {
+ ASSERT_EQ(observer_loaded, observer_loaded_);
+ ASSERT_EQ(observer_started_batch_update, observer_started_batch_update_);
+ ASSERT_EQ(observer_completed_batch_update,
+ observer_completed_batch_update_);
+ ASSERT_EQ(observer_deleted, observer_deleted_);
+ ASSERT_EQ(observer_remove, observer_remove_);
+ ASSERT_EQ(observer_move, observer_move_);
+ // Add and did_add should be the same.
+ ASSERT_EQ(observer_add, observer_add_);
+ ASSERT_EQ(observer_add, observer_did_add_);
+ ASSERT_EQ(observer_update, observer_update_);
+ ASSERT_EQ(observer_did_apply, observer_did_apply_);
+ }
+
+ void AssertStorageCount(int storage_saved, int storage_removed) {
+ ASSERT_EQ(storage_saved, storage_saved_);
+ ASSERT_EQ(storage_removed, storage_removed_);
+ }
+
+ // ReadingListModelObserver
+ void ReadingListModelLoaded(const ReadingListModel* model) override {
+ observer_loaded_ += 1;
+ }
+ void ReadingListModelBeganBatchUpdates(
+ const ReadingListModel* model) override {
+ observer_started_batch_update_ += 1;
+ }
+ void ReadingListModelCompletedBatchUpdates(
+ const ReadingListModel* model) override {
+ observer_completed_batch_update_ += 1;
+ }
+ void ReadingListModelBeingDeleted(const ReadingListModel* model) override {
+ observer_deleted_ += 1;
+ }
+ void ReadingListWillRemoveEntry(const ReadingListModel* model,
+ const GURL& url) override {
+ observer_remove_ += 1;
+ }
+ void ReadingListWillMoveEntry(const ReadingListModel* model,
+ const GURL& url) override {
+ observer_move_ += 1;
+ }
+ void ReadingListWillAddEntry(const ReadingListModel* model,
+ const ReadingListEntry& entry) override {
+ observer_add_ += 1;
+ }
+ void ReadingListDidAddEntry(const ReadingListModel* model,
+ const GURL& url,
+ reading_list::EntrySource entry_source) override {
+ observer_did_add_ += 1;
+ }
+ void ReadingListWillUpdateEntry(const ReadingListModel* model,
+ const GURL& url) override {
+ observer_update_ += 1;
+ }
+ void ReadingListDidApplyChanges(ReadingListModel* model) override {
+ observer_did_apply_ += 1;
+ }
+
+ void ReadingListDidSaveEntry() override { storage_saved_ += 1; }
+ void ReadingListDidRemoveEntry() override { storage_removed_ += 1; }
+
+ size_t UnreadSize() {
+ size_t size = 0;
+ for (const auto& url : model_->Keys()) {
+ const ReadingListEntry* entry = model_->GetEntryByURL(url);
+ if (!entry->IsRead()) {
+ size++;
+ }
+ }
+ DCHECK_EQ(size, model_->unread_size());
+ return size;
+ }
+
+ size_t ReadSize() {
+ size_t size = 0;
+ for (const auto& url : model_->Keys()) {
+ const ReadingListEntry* entry = model_->GetEntryByURL(url);
+ if (entry->IsRead()) {
+ size++;
+ }
+ }
+ return size;
+ }
+
+ void Callback(const ReadingListEntry& entry) {
+ EXPECT_EQ(callback_url, entry.URL());
+ EXPECT_EQ(callback_title, entry.Title());
+ callback_called_ = true;
+ }
+
+ bool CallbackCalled() { return callback_called_; }
+
+ protected:
+ int observer_loaded_;
+ int observer_started_batch_update_;
+ int observer_completed_batch_update_;
+ int observer_deleted_;
+ int observer_remove_;
+ int observer_move_;
+ int observer_add_;
+ int observer_did_add_;
+ int observer_update_;
+ int observer_did_apply_;
+ int storage_saved_;
+ int storage_removed_;
+ bool callback_called_;
+
+ std::unique_ptr<ReadingListModelImpl> model_;
+};
+
+// Tests creating an empty model.
+TEST_F(ReadingListModelTest, EmptyLoaded) {
+ EXPECT_TRUE(model_->loaded());
+ AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ model_->Shutdown();
+ EXPECT_FALSE(model_->loaded());
+ AssertObserverCount(1, 0, 0, 1, 0, 0, 0, 0, 0);
+}
+
+// Tests load model.
+TEST_F(ReadingListModelTest, ModelLoaded) {
+ ClearCounts();
+ auto storage = base::MakeUnique<TestReadingListStorage>(this);
+ storage->AddSampleEntries();
+ SetStorage(std::move(storage));
+
+ AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
+ std::map<GURL, std::string> loaded_entries;
+ int size = 0;
+ for (const auto& url : model_->Keys()) {
+ size++;
+ const ReadingListEntry* entry = model_->GetEntryByURL(url);
+ loaded_entries[url] = entry->Title();
+ }
+ EXPECT_EQ(size, 7);
+ EXPECT_EQ(loaded_entries[GURL("http://unread_a.com")], "unread_a");
+ EXPECT_EQ(loaded_entries[GURL("http://unread_b.com")], "unread_b");
+ EXPECT_EQ(loaded_entries[GURL("http://unread_c.com")], "unread_c");
+ EXPECT_EQ(loaded_entries[GURL("http://unread_d.com")], "unread_d");
+ EXPECT_EQ(loaded_entries[GURL("http://read_a.com")], "read_a");
+ EXPECT_EQ(loaded_entries[GURL("http://read_b.com")], "read_b");
+ EXPECT_EQ(loaded_entries[GURL("http://read_c.com")], "read_c");
+}
+
+// Tests adding entry.
+TEST_F(ReadingListModelTest, AddEntry) {
+ auto storage = base::MakeUnique<TestReadingListStorage>(this);
+ SetStorage(std::move(storage));
+ ClearCounts();
+
+ const ReadingListEntry& entry =
+ model_->AddEntry(GURL("http://example.com"), "\n \tsample Test ",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ EXPECT_EQ(GURL("http://example.com"), entry.URL());
+ EXPECT_EQ("sample Test", entry.Title());
+
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 1, 0, 1);
+ AssertStorageCount(1, 0);
+ EXPECT_EQ(1ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_TRUE(model_->GetLocalUnseenFlag());
+
+ const ReadingListEntry* other_entry =
+ model_->GetEntryByURL(GURL("http://example.com"));
+ EXPECT_NE(other_entry, nullptr);
+ EXPECT_FALSE(other_entry->IsRead());
+ EXPECT_EQ(GURL("http://example.com"), other_entry->URL());
+ EXPECT_EQ("sample Test", other_entry->Title());
+}
+
+// Tests addin entry from sync.
+TEST_F(ReadingListModelTest, SyncAddEntry) {
+ auto storage = base::MakeUnique<TestReadingListStorage>(this);
+ SetStorage(std::move(storage));
+ auto entry =
+ base::MakeUnique<ReadingListEntry>(GURL("http://example.com"), "sample");
+ entry->SetRead(true);
+ ClearCounts();
+
+ model_->SyncAddEntry(std::move(entry));
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 1, 0, 1);
+ AssertStorageCount(0, 0);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ ClearCounts();
+}
+
+// Tests updating entry from sync.
+TEST_F(ReadingListModelTest, SyncMergeEntry) {
+ auto storage = base::MakeUnique<TestReadingListStorage>(this);
+ SetStorage(std::move(storage));
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ const base::FilePath distilled_path("distilled/page.html");
+ const GURL distilled_url("http://example.com/distilled");
+ model_->SetEntryDistilledInfo(GURL("http://example.com"), distilled_path,
+ distilled_url);
+ const ReadingListEntry* local_entry =
+ model_->GetEntryByURL(GURL("http://example.com"));
+ int64_t local_update_time = local_entry->UpdateTime();
+
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(10));
+ auto sync_entry =
+ base::MakeUnique<ReadingListEntry>(GURL("http://example.com"), "sample");
+ sync_entry->SetRead(true);
+ ASSERT_GT(sync_entry->UpdateTime(), local_update_time);
+ int64_t sync_update_time = sync_entry->UpdateTime();
+ EXPECT_TRUE(sync_entry->DistilledPath().empty());
+
+ EXPECT_EQ(1ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+
+ ReadingListEntry* merged_entry =
+ model_->SyncMergeEntry(std::move(sync_entry));
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ EXPECT_EQ(merged_entry->DistilledPath(),
+ base::FilePath("distilled/page.html"));
+ EXPECT_EQ(merged_entry->UpdateTime(), sync_update_time);
+}
+
+// Tests deleting entry.
+TEST_F(ReadingListModelTest, RemoveEntryByUrl) {
+ auto storage = base::MakeUnique<TestReadingListStorage>(this);
+ SetStorage(std::move(storage));
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ ClearCounts();
+ EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+ EXPECT_EQ(1ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ model_->RemoveEntryByURL(GURL("http://example.com"));
+ AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1);
+ AssertStorageCount(0, 1);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(GURL("http://example.com"), true);
+ ClearCounts();
+ EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ model_->RemoveEntryByURL(GURL("http://example.com"));
+ AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1);
+ AssertStorageCount(0, 1);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+}
+
+// Tests deleting entry from sync.
+TEST_F(ReadingListModelTest, RemoveSyncEntryByUrl) {
+ auto storage = base::MakeUnique<TestReadingListStorage>(this);
+ SetStorage(std::move(storage));
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ ClearCounts();
+ EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+ EXPECT_EQ(1ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ model_->SyncRemoveEntry(GURL("http://example.com"));
+ AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1);
+ AssertStorageCount(0, 0);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(GURL("http://example.com"), true);
+ ClearCounts();
+ EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ model_->SyncRemoveEntry(GURL("http://example.com"));
+ AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1);
+ AssertStorageCount(0, 0);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+}
+
+// Tests marking entry read.
+TEST_F(ReadingListModelTest, ReadEntry) {
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+
+ ClearCounts();
+ model_->SetReadStatus(GURL("http://example.com"), true);
+ AssertObserverCount(0, 0, 0, 0, 0, 1, 0, 0, 1);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ EXPECT_EQ(0ul, model_->unseen_size());
+
+ const ReadingListEntry* other_entry =
+ model_->GetEntryByURL(GURL("http://example.com"));
+ EXPECT_NE(other_entry, nullptr);
+ EXPECT_TRUE(other_entry->IsRead());
+ EXPECT_EQ(GURL("http://example.com"), other_entry->URL());
+ EXPECT_EQ("sample", other_entry->Title());
+}
+
+// Tests accessing existing entry.
+TEST_F(ReadingListModelTest, EntryFromURL) {
+ GURL url1("http://example.com");
+ GURL url2("http://example2.com");
+ std::string entry1_title = "foo bar qux";
+ model_->AddEntry(url1, entry1_title, reading_list::ADDED_VIA_CURRENT_APP);
+
+ // Check call with nullptr |read| parameter.
+ const ReadingListEntry* entry1 = model_->GetEntryByURL(url1);
+ EXPECT_NE(nullptr, entry1);
+ EXPECT_EQ(entry1_title, entry1->Title());
+
+ entry1 = model_->GetEntryByURL(url1);
+ EXPECT_NE(nullptr, entry1);
+ EXPECT_EQ(entry1_title, entry1->Title());
+ EXPECT_EQ(entry1->IsRead(), false);
+ model_->SetReadStatus(url1, true);
+ entry1 = model_->GetEntryByURL(url1);
+ EXPECT_NE(nullptr, entry1);
+ EXPECT_EQ(entry1_title, entry1->Title());
+ EXPECT_EQ(entry1->IsRead(), true);
+
+ const ReadingListEntry* entry2 = model_->GetEntryByURL(url2);
+ EXPECT_EQ(nullptr, entry2);
+}
+
+// Tests mark entry unread.
+TEST_F(ReadingListModelTest, UnreadEntry) {
+ // Setup.
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ EXPECT_TRUE(model_->GetLocalUnseenFlag());
+ model_->SetReadStatus(GURL("http://example.com"), true);
+ ClearCounts();
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ EXPECT_FALSE(model_->GetLocalUnseenFlag());
+
+ // Action.
+ model_->SetReadStatus(GURL("http://example.com"), false);
+
+ // Tests.
+ AssertObserverCount(0, 0, 0, 0, 0, 1, 0, 0, 1);
+ EXPECT_EQ(1ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_FALSE(model_->GetLocalUnseenFlag());
+
+ const ReadingListEntry* other_entry =
+ model_->GetEntryByURL(GURL("http://example.com"));
+ EXPECT_NE(other_entry, nullptr);
+ EXPECT_FALSE(other_entry->IsRead());
+ EXPECT_EQ(GURL("http://example.com"), other_entry->URL());
+ EXPECT_EQ("sample", other_entry->Title());
+}
+
+// Tests batch updates observers are called.
+TEST_F(ReadingListModelTest, BatchUpdates) {
+ auto token = model_->BeginBatchUpdates();
+ AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(model_->IsPerformingBatchUpdates());
+
+ delete token.release();
+ AssertObserverCount(1, 1, 1, 0, 0, 0, 0, 0, 0);
+ EXPECT_FALSE(model_->IsPerformingBatchUpdates());
+}
+
+// Tests batch updates are reentrant.
+TEST_F(ReadingListModelTest, BatchUpdatesReentrant) {
+ // When two updates happen at the same time, the notification is only sent
+ // for beginning of first update and completion of last update.
+ EXPECT_FALSE(model_->IsPerformingBatchUpdates());
+
+ auto token = model_->BeginBatchUpdates();
+ AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(model_->IsPerformingBatchUpdates());
+
+ auto second_token = model_->BeginBatchUpdates();
+ AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(model_->IsPerformingBatchUpdates());
+
+ delete token.release();
+ AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(model_->IsPerformingBatchUpdates());
+
+ delete second_token.release();
+ AssertObserverCount(1, 1, 1, 0, 0, 0, 0, 0, 0);
+ EXPECT_FALSE(model_->IsPerformingBatchUpdates());
+
+ // Consequent updates send notifications.
+ auto third_token = model_->BeginBatchUpdates();
+ AssertObserverCount(1, 2, 1, 0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(model_->IsPerformingBatchUpdates());
+
+ delete third_token.release();
+ AssertObserverCount(1, 2, 2, 0, 0, 0, 0, 0, 0);
+ EXPECT_FALSE(model_->IsPerformingBatchUpdates());
+}
+
+// Tests setting title on unread entry.
+TEST_F(ReadingListModelTest, UpdateEntryTitle) {
+ const GURL gurl("http://example.com");
+ const ReadingListEntry& entry =
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ ClearCounts();
+
+ model_->SetEntryTitle(gurl, "ping");
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ("ping", entry.Title());
+}
+// Tests setting distillation state on unread entry.
+TEST_F(ReadingListModelTest, UpdateEntryDistilledState) {
+ const GURL gurl("http://example.com");
+ const ReadingListEntry& entry =
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ ClearCounts();
+
+ model_->SetEntryDistilledState(gurl, ReadingListEntry::PROCESSING);
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ(ReadingListEntry::PROCESSING, entry.DistilledState());
+}
+
+// Tests setting distillation info on unread entry.
+TEST_F(ReadingListModelTest, UpdateDistilledInfo) {
+ const GURL gurl("http://example.com");
+ const ReadingListEntry& entry =
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ ClearCounts();
+
+ const base::FilePath distilled_path("distilled/page.html");
+ const GURL distilled_url("http://example.com/distilled");
+ model_->SetEntryDistilledInfo(GURL("http://example.com"), distilled_path,
+ distilled_url);
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ(ReadingListEntry::PROCESSED, entry.DistilledState());
+ EXPECT_EQ(distilled_path, entry.DistilledPath());
+ EXPECT_EQ(distilled_url, entry.DistilledURL());
+}
+
+// Tests setting title on read entry.
+TEST_F(ReadingListModelTest, UpdateReadEntryTitle) {
+ const GURL gurl("http://example.com");
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(gurl, true);
+ const ReadingListEntry* entry = model_->GetEntryByURL(gurl);
+ ClearCounts();
+
+ model_->SetEntryTitle(gurl, "ping");
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ("ping", entry->Title());
+}
+
+// Tests setting distillation state on read entry.
+TEST_F(ReadingListModelTest, UpdateReadEntryState) {
+ const GURL gurl("http://example.com");
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(gurl, true);
+ const ReadingListEntry* entry = model_->GetEntryByURL(gurl);
+ ClearCounts();
+
+ model_->SetEntryDistilledState(gurl, ReadingListEntry::PROCESSING);
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ(ReadingListEntry::PROCESSING, entry->DistilledState());
+}
+
+// Tests setting distillation info on read entry.
+TEST_F(ReadingListModelTest, UpdateReadDistilledInfo) {
+ const GURL gurl("http://example.com");
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(gurl, true);
+ const ReadingListEntry* entry = model_->GetEntryByURL(gurl);
+ ClearCounts();
+
+ const base::FilePath distilled_path("distilled/page.html");
+ const GURL distilled_url("http://example.com/distilled");
+ model_->SetEntryDistilledInfo(GURL("http://example.com"), distilled_path,
+ distilled_url);
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ(ReadingListEntry::PROCESSED, entry->DistilledState());
+ EXPECT_EQ(distilled_path, entry->DistilledPath());
+ EXPECT_EQ(distilled_url, entry->DistilledURL());
+}
+
+// Tests that ReadingListModel calls CallbackModelBeingDeleted when destroyed.
+TEST_F(ReadingListModelTest, CallbackModelBeingDeleted) {
+ AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
+ model_.reset();
+ AssertObserverCount(1, 0, 0, 1, 0, 0, 0, 0, 0);
+}
+
+} // namespace
diff --git a/chromium/components/reading_list/ios/reading_list_pref_names.cc b/chromium/components/reading_list/ios/reading_list_pref_names.cc
new file mode 100644
index 00000000000..0c160945a86
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_pref_names.cc
@@ -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.
+
+#include "components/reading_list/ios/reading_list_pref_names.h"
+
+namespace reading_list {
+namespace prefs {
+
+// Boolean to track if some reading list entries have never been seen on this
+// device. Not synced.
+const char kReadingListHasUnseenEntries[] = "reading_list.has_unseen_entries";
+
+} // namespace prefs
+} // namespace reading_list
diff --git a/chromium/components/reading_list/ios/reading_list_pref_names.h b/chromium/components/reading_list/ios/reading_list_pref_names.h
new file mode 100644
index 00000000000..c9afb6013b5
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_pref_names.h
@@ -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.
+
+// Constants for the names of various reading list preferences.
+
+#ifndef COMPONENTS_READING_LIST_IOS_READING_LIST_PREF_NAMES_H_
+#define COMPONENTS_READING_LIST_IOS_READING_LIST_PREF_NAMES_H_
+
+namespace reading_list {
+namespace prefs {
+
+extern const char kReadingListHasUnseenEntries[];
+
+} // namespace prefs
+} // namespace reading_list
+
+#endif // COMPONENTS_READING_LIST_IOS_READING_LIST_PREF_NAMES_H_
diff --git a/chromium/components/reading_list/ios/reading_list_store.cc b/chromium/components/reading_list/ios/reading_list_store.cc
new file mode 100644
index 00000000000..087ad621f0c
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_store.cc
@@ -0,0 +1,461 @@
+// 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/ios/reading_list_store.h"
+
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "components/reading_list/ios/proto/reading_list.pb.h"
+#include "components/reading_list/ios/reading_list_model_impl.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/metadata_change_list.h"
+#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "components/sync/model_impl/accumulating_metadata_change_list.h"
+#include "components/sync/protocol/model_type_state.pb.h"
+
+ReadingListStore::ReadingListStore(
+ StoreFactoryFunction create_store_callback,
+ const ChangeProcessorFactory& change_processor_factory)
+ : ReadingListModelStorage(change_processor_factory, syncer::READING_LIST),
+ create_store_callback_(create_store_callback),
+ pending_transaction_count_(0) {}
+
+ReadingListStore::~ReadingListStore() {
+ DCHECK_EQ(0, pending_transaction_count_);
+}
+
+void ReadingListStore::SetReadingListModel(ReadingListModel* model,
+ ReadingListStoreDelegate* delegate) {
+ DCHECK(CalledOnValidThread());
+ model_ = model;
+ delegate_ = delegate;
+ create_store_callback_.Run(
+ base::Bind(&ReadingListStore::OnStoreCreated, base::AsWeakPtr(this)));
+}
+
+std::unique_ptr<ReadingListModelStorage::ScopedBatchUpdate>
+ReadingListStore::EnsureBatchCreated() {
+ return base::WrapUnique<ReadingListModelStorage::ScopedBatchUpdate>(
+ new ScopedBatchUpdate(this));
+}
+
+ReadingListStore::ScopedBatchUpdate::ScopedBatchUpdate(ReadingListStore* store)
+ : store_(store) {
+ store_->BeginTransaction();
+}
+
+ReadingListStore::ScopedBatchUpdate::~ScopedBatchUpdate() {
+ store_->CommitTransaction();
+}
+
+void ReadingListStore::BeginTransaction() {
+ DCHECK(CalledOnValidThread());
+ pending_transaction_count_++;
+ if (pending_transaction_count_ == 1) {
+ batch_ = store_->CreateWriteBatch();
+ }
+}
+
+void ReadingListStore::CommitTransaction() {
+ DCHECK(CalledOnValidThread());
+ pending_transaction_count_--;
+ if (pending_transaction_count_ == 0) {
+ store_->CommitWriteBatch(
+ std::move(batch_),
+ base::Bind(&ReadingListStore::OnDatabaseSave, base::AsWeakPtr(this)));
+ batch_.reset();
+ }
+}
+
+void ReadingListStore::SaveEntry(const ReadingListEntry& entry) {
+ DCHECK(CalledOnValidThread());
+ auto token = EnsureBatchCreated();
+
+ std::unique_ptr<reading_list::ReadingListLocal> pb_entry =
+ entry.AsReadingListLocal();
+
+ batch_->WriteData(entry.URL().spec(), pb_entry->SerializeAsString());
+
+ if (!change_processor()->IsTrackingMetadata()) {
+ return;
+ }
+ std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry_sync =
+ entry.AsReadingListSpecifics();
+
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
+ CreateMetadataChangeList();
+
+ std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
+ *entity_data->specifics.mutable_reading_list() = *pb_entry_sync;
+ entity_data->non_unique_name = pb_entry_sync->entry_id();
+
+ change_processor()->Put(entry.URL().spec(), std::move(entity_data),
+ metadata_change_list.get());
+ batch_->TransferMetadataChanges(std::move(metadata_change_list));
+}
+
+void ReadingListStore::RemoveEntry(const ReadingListEntry& entry) {
+ DCHECK(CalledOnValidThread());
+ auto token = EnsureBatchCreated();
+
+ batch_->DeleteData(entry.URL().spec());
+ if (!change_processor()->IsTrackingMetadata()) {
+ return;
+ }
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
+ CreateMetadataChangeList();
+
+ change_processor()->Delete(entry.URL().spec(), metadata_change_list.get());
+ batch_->TransferMetadataChanges(std::move(metadata_change_list));
+}
+
+void ReadingListStore::OnDatabaseLoad(
+ syncer::ModelTypeStore::Result result,
+ std::unique_ptr<syncer::ModelTypeStore::RecordList> entries) {
+ DCHECK(CalledOnValidThread());
+ if (result != syncer::ModelTypeStore::Result::SUCCESS) {
+ change_processor()->ReportError(FROM_HERE,
+ "Cannot load Reading List Database.");
+ return;
+ }
+ auto loaded_entries =
+ base::MakeUnique<ReadingListStoreDelegate::ReadingListEntries>();
+
+ for (const syncer::ModelTypeStore::Record& r : *entries.get()) {
+ reading_list::ReadingListLocal proto;
+ if (!proto.ParseFromString(r.value)) {
+ continue;
+ // TODO(skym, crbug.com/582460): Handle unrecoverable initialization
+ // failure.
+ }
+
+ std::unique_ptr<ReadingListEntry> entry(
+ ReadingListEntry::FromReadingListLocal(proto));
+ if (!entry) {
+ continue;
+ }
+ GURL url = entry->URL();
+ DCHECK(!loaded_entries->count(url));
+ loaded_entries->insert(std::make_pair(url, std::move(*entry)));
+ }
+
+ delegate_->StoreLoaded(std::move(loaded_entries));
+
+ store_->ReadAllMetadata(
+ base::Bind(&ReadingListStore::OnReadAllMetadata, base::AsWeakPtr(this)));
+}
+
+void ReadingListStore::OnReadAllMetadata(
+ base::Optional<syncer::ModelError> error,
+ std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
+ DCHECK(CalledOnValidThread());
+ if (error) {
+ change_processor()->ReportError(FROM_HERE, "Failed to read metadata.");
+ } else {
+ change_processor()->ModelReadyToSync(std::move(metadata_batch));
+ }
+}
+
+void ReadingListStore::OnDatabaseSave(syncer::ModelTypeStore::Result result) {
+ return;
+}
+
+void ReadingListStore::OnStoreCreated(
+ syncer::ModelTypeStore::Result result,
+ std::unique_ptr<syncer::ModelTypeStore> store) {
+ DCHECK(CalledOnValidThread());
+ if (result != syncer::ModelTypeStore::Result::SUCCESS) {
+ // TODO(crbug.com/664926): handle store creation error.
+ return;
+ }
+ store_ = std::move(store);
+ store_->ReadAllData(
+ base::Bind(&ReadingListStore::OnDatabaseLoad, base::AsWeakPtr(this)));
+ return;
+}
+
+// Creates an object used to communicate changes in the sync metadata to the
+// model type store.
+std::unique_ptr<syncer::MetadataChangeList>
+ReadingListStore::CreateMetadataChangeList() {
+ return syncer::ModelTypeStore::WriteBatch::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.
+base::Optional<syncer::ModelError> ReadingListStore::MergeSyncData(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityDataMap entity_data_map) {
+ DCHECK(CalledOnValidThread());
+ auto token = EnsureBatchCreated();
+ // Keep track of the last update of each item.
+ std::set<std::string> synced_entries;
+ std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
+ model_batch_updates = model_->BeginBatchUpdates();
+
+ // Merge sync to local data.
+ for (const auto& kv : entity_data_map) {
+ synced_entries.insert(kv.first);
+ const sync_pb::ReadingListSpecifics& specifics =
+ kv.second.value().specifics.reading_list();
+ // Deserialize entry.
+ std::unique_ptr<ReadingListEntry> entry(
+ ReadingListEntry::FromReadingListSpecifics(specifics));
+
+ const ReadingListEntry* existing_entry =
+ model_->GetEntryByURL(entry->URL());
+
+ if (!existing_entry) {
+ // This entry is new. Add it to the store and model.
+ // Convert to local store format and write to store.
+ std::unique_ptr<reading_list::ReadingListLocal> entry_pb =
+ entry->AsReadingListLocal();
+ batch_->WriteData(entry->URL().spec(), entry_pb->SerializeAsString());
+
+ // Notify model about updated entry.
+ delegate_->SyncAddEntry(std::move(entry));
+ } else {
+ // Merge the local data and the sync data and store the result.
+ ReadingListEntry* merged_entry =
+ delegate_->SyncMergeEntry(std::move(entry));
+
+ // Write to the store.
+ std::unique_ptr<reading_list::ReadingListLocal> entry_local_pb =
+ merged_entry->AsReadingListLocal();
+ batch_->WriteData(merged_entry->URL().spec(),
+ entry_local_pb->SerializeAsString());
+
+ // Send to sync
+ std::unique_ptr<sync_pb::ReadingListSpecifics> entry_sync_pb =
+ merged_entry->AsReadingListSpecifics();
+ DCHECK(CompareEntriesForSync(specifics, *entry_sync_pb));
+ auto entity_data = base::MakeUnique<syncer::EntityData>();
+ *(entity_data->specifics.mutable_reading_list()) = *entry_sync_pb;
+ entity_data->non_unique_name = entry_sync_pb->entry_id();
+
+ // TODO(crbug.com/666232): Investigate if there is a risk of sync
+ // ping-pong.
+ change_processor()->Put(entry_sync_pb->entry_id(), std::move(entity_data),
+ metadata_change_list.get());
+ }
+ }
+
+ // Commit local only entries to server.
+ for (const auto& url : model_->Keys()) {
+ const ReadingListEntry* entry = model_->GetEntryByURL(url);
+ if (synced_entries.count(url.spec())) {
+ // Entry already exists and has been merged above.
+ continue;
+ }
+
+ // Local entry has later timestamp. It should be committed to server.
+ std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
+ entry->AsReadingListSpecifics();
+
+ auto entity_data = base::MakeUnique<syncer::EntityData>();
+ *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
+ entity_data->non_unique_name = entry_pb->entry_id();
+
+ change_processor()->Put(entry_pb->entry_id(), std::move(entity_data),
+ metadata_change_list.get());
+ }
+ batch_->TransferMetadataChanges(std::move(metadata_change_list));
+
+ return {};
+}
+
+// Apply changes from the sync server locally.
+// Please note that |entity_changes| might have fewer entries than
+// |metadata_change_list| in case when some of the data changes are filtered
+// out, or even be empty in case when a commit confirmation is processed and
+// only the metadata needs to persisted.
+base::Optional<syncer::ModelError> ReadingListStore::ApplySyncChanges(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_changes) {
+ DCHECK(CalledOnValidThread());
+ std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> batch =
+ model_->BeginBatchUpdates();
+ auto token = EnsureBatchCreated();
+
+ for (syncer::EntityChange& change : entity_changes) {
+ if (change.type() == syncer::EntityChange::ACTION_DELETE) {
+ batch_->DeleteData(change.storage_key());
+ // Need to notify model that entry is deleted.
+ delegate_->SyncRemoveEntry(GURL(change.storage_key()));
+ } else {
+ // Deserialize entry.
+ const sync_pb::ReadingListSpecifics& specifics =
+ change.data().specifics.reading_list();
+ std::unique_ptr<ReadingListEntry> entry(
+ ReadingListEntry::FromReadingListSpecifics(specifics));
+
+ const ReadingListEntry* existing_entry =
+ model_->GetEntryByURL(entry->URL());
+
+ if (!existing_entry) {
+ // This entry is new. Add it to the store and model.
+ // Convert to local store format and write to store.
+ std::unique_ptr<reading_list::ReadingListLocal> entry_pb =
+ entry->AsReadingListLocal();
+ batch_->WriteData(entry->URL().spec(), entry_pb->SerializeAsString());
+
+ // Notify model about updated entry.
+ delegate_->SyncAddEntry(std::move(entry));
+ } else {
+ // Merge the local data and the sync data and store the result.
+ ReadingListEntry* merged_entry =
+ delegate_->SyncMergeEntry(std::move(entry));
+
+ // Write to the store.
+ std::unique_ptr<reading_list::ReadingListLocal> entry_local_pb =
+ merged_entry->AsReadingListLocal();
+ batch_->WriteData(merged_entry->URL().spec(),
+ entry_local_pb->SerializeAsString());
+
+ // Send to sync
+ std::unique_ptr<sync_pb::ReadingListSpecifics> entry_sync_pb =
+ merged_entry->AsReadingListSpecifics();
+ DCHECK(CompareEntriesForSync(specifics, *entry_sync_pb));
+ auto entity_data = base::MakeUnique<syncer::EntityData>();
+ *(entity_data->specifics.mutable_reading_list()) = *entry_sync_pb;
+ entity_data->non_unique_name = entry_sync_pb->entry_id();
+
+ // TODO(crbug.com/666232): Investigate if there is a risk of sync
+ // ping-pong.
+ change_processor()->Put(entry_sync_pb->entry_id(),
+ std::move(entity_data),
+ metadata_change_list.get());
+ }
+ }
+ }
+
+ batch_->TransferMetadataChanges(std::move(metadata_change_list));
+ return {};
+}
+
+void ReadingListStore::GetData(StorageKeyList storage_keys,
+ DataCallback callback) {
+ DCHECK(CalledOnValidThread());
+ auto batch = base::MakeUnique<syncer::MutableDataBatch>();
+ for (const std::string& url_string : storage_keys) {
+ const ReadingListEntry* entry = model_->GetEntryByURL(GURL(url_string));
+ if (entry) {
+ AddEntryToBatch(batch.get(), *entry);
+ }
+ }
+
+ callback.Run(std::move(batch));
+}
+
+void ReadingListStore::GetAllData(DataCallback callback) {
+ DCHECK(CalledOnValidThread());
+ auto batch = base::MakeUnique<syncer::MutableDataBatch>();
+
+ for (const auto& url : model_->Keys()) {
+ const ReadingListEntry* entry = model_->GetEntryByURL(GURL(url));
+ AddEntryToBatch(batch.get(), *entry);
+ }
+
+ callback.Run(std::move(batch));
+}
+
+void ReadingListStore::AddEntryToBatch(syncer::MutableDataBatch* batch,
+ const ReadingListEntry& entry) {
+ DCHECK(CalledOnValidThread());
+ std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
+ entry.AsReadingListSpecifics();
+
+ std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
+ *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
+ entity_data->non_unique_name = entry_pb->entry_id();
+
+ batch->Put(entry_pb->entry_id(), std::move(entity_data));
+}
+
+// Get or generate a client tag for |entity_data|. This must be the same tag
+// that was/would have been generated in the SyncableService/Directory world
+// for backward compatibility with pre-USS clients. The only time this
+// theoretically needs to be called is on the creation of local data, however
+// it is also used to verify the hash of remote data. If a data type was never
+// launched pre-USS, then method does not need to be different from
+// GetStorageKey().
+std::string ReadingListStore::GetClientTag(
+ const syncer::EntityData& entity_data) {
+ DCHECK(CalledOnValidThread());
+ return GetStorageKey(entity_data);
+}
+
+// Get or generate a storage key for |entity_data|. This will only ever be
+// called once when first encountering a remote entity. Local changes will
+// provide their storage keys directly to Put instead of using this method.
+// Theoretically this function doesn't need to be stable across multiple calls
+// on the same or different clients, but to keep things simple, it probably
+// should be.
+std::string ReadingListStore::GetStorageKey(
+ const syncer::EntityData& entity_data) {
+ DCHECK(CalledOnValidThread());
+ return entity_data.specifics.reading_list().entry_id();
+}
+
+bool ReadingListStore::CompareEntriesForSync(
+ const sync_pb::ReadingListSpecifics& lhs,
+ const sync_pb::ReadingListSpecifics& rhs) {
+ DCHECK(lhs.entry_id() == rhs.entry_id());
+ DCHECK(lhs.has_update_time_us());
+ DCHECK(rhs.has_update_time_us());
+ DCHECK(lhs.has_update_title_time_us());
+ DCHECK(rhs.has_update_title_time_us());
+ DCHECK(lhs.has_creation_time_us());
+ DCHECK(rhs.has_creation_time_us());
+ DCHECK(lhs.has_url());
+ DCHECK(rhs.has_url());
+ DCHECK(lhs.has_title());
+ DCHECK(rhs.has_title());
+ DCHECK(lhs.has_status());
+ DCHECK(rhs.has_status());
+ if (rhs.url() != lhs.url() ||
+ rhs.update_title_time_us() < lhs.update_title_time_us() ||
+ rhs.creation_time_us() < lhs.creation_time_us() ||
+ rhs.update_time_us() < lhs.update_time_us()) {
+ return false;
+ }
+ if (rhs.update_time_us() == lhs.update_time_us()) {
+ if ((rhs.status() == sync_pb::ReadingListSpecifics::UNSEEN &&
+ lhs.status() != sync_pb::ReadingListSpecifics::UNSEEN) ||
+ (rhs.status() == sync_pb::ReadingListSpecifics::UNREAD &&
+ lhs.status() == sync_pb::ReadingListSpecifics::READ))
+ return false;
+ }
+ if (rhs.update_title_time_us() == lhs.update_title_time_us()) {
+ if (rhs.title().compare(lhs.title()) < 0)
+ return false;
+ }
+ if (rhs.creation_time_us() == lhs.creation_time_us()) {
+ if (rhs.first_read_time_us() == 0 && lhs.first_read_time_us() != 0) {
+ return false;
+ }
+ if (rhs.first_read_time_us() > lhs.first_read_time_us() &&
+ lhs.first_read_time_us() != 0) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/chromium/components/reading_list/ios/reading_list_store.h b/chromium/components/reading_list/ios/reading_list_store.h
new file mode 100644
index 00000000000..2b780073749
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_store.h
@@ -0,0 +1,169 @@
+// 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_IOS_READING_LIST_STORE_H_
+#define COMPONENTS_READING_LIST_IOS_READING_LIST_STORE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/threading/non_thread_safe.h"
+#include "components/reading_list/ios/reading_list_model_storage.h"
+#include "components/reading_list/ios/reading_list_store_delegate.h"
+#include "components/sync/model/model_error.h"
+#include "components/sync/model/model_type_store.h"
+
+namespace syncer {
+class MutableDataBatch;
+}
+
+class ReadingListModel;
+
+// A ReadingListModelStorage storing and syncing data in protobufs.
+class ReadingListStore : public ReadingListModelStorage,
+ public base::NonThreadSafe {
+ using StoreFactoryFunction = base::Callback<void(
+ const syncer::ModelTypeStore::InitCallback& callback)>;
+
+ public:
+ ReadingListStore(StoreFactoryFunction create_store_callback,
+ const ChangeProcessorFactory& change_processor_factory);
+ ~ReadingListStore() override;
+
+ std::unique_ptr<ScopedBatchUpdate> EnsureBatchCreated() override;
+
+ // ReadingListModelStorage implementation
+ void SetReadingListModel(ReadingListModel* model,
+ ReadingListStoreDelegate* delegate) override;
+
+ void SaveEntry(const ReadingListEntry& entry) override;
+ void RemoveEntry(const ReadingListEntry& entry) override;
+
+ // Creates an object used to communicate changes in the sync metadata to the
+ // model type store.
+ std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+ override;
+
+ // 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.
+ base::Optional<syncer::ModelError> MergeSyncData(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityDataMap entity_data_map) override;
+
+ // Apply changes from the sync server locally.
+ // Please note that |entity_changes| might have fewer entries than
+ // |metadata_change_list| in case when some of the data changes are filtered
+ // out, or even be empty in case when a commit confirmation is processed and
+ // only the metadata needs to persisted.
+ base::Optional<syncer::ModelError> ApplySyncChanges(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_changes) override;
+
+ // Returns whether entries respect a strict order for sync and if |rhs| can be
+ // submitted to sync after |lhs| has been received.
+ // The order should ensure that there is no sync loop in sync and should be
+ // submitted to sync in strictly increasing order.
+ // Entries are in increasing order if all the fields respect increasing order.
+ // - URL must be the same.
+ // - update_title_time_us:
+ // rhs.update_title_time_us >= lhs.update_title_time_us
+ // - title:
+ // if rhs.update_title_time_us > lhs.update_title_time_us
+ // title can be anything
+ // if rhs.update_title_time_us == lhs.update_title_time_us
+ // title must verify rhs.title.compare(lhs.title) >= 0
+ // - creation_time_us:
+ // rhs.creation_time_us >= lhs.creation_time_us
+ // - rhs.first_read_time_us:
+ // if rhs.creation_time_us > lhs.creation_time_us,
+ // rhs.first_read_time_us can be anything.
+ // if rhs.creation_time_us == lhs.creation_time_us
+ // and rhs.first_read_time_us == 0
+ // rhs.first_read_time_us can be anything.
+ // if rhs.creation_time_us == lhs.creation_time_us,
+ // rhs.first_read_time_us <= lhs.first_read_time_us
+ // - update_time_us:
+ // rhs.update_time_us >= lhs.update_time_us
+ // - state:
+ // if rhs.update_time_us > lhs.update_time_us
+ // rhs.state can be anything.
+ // if rhs.update_time_us == lhs.update_time_us
+ // rhs.state >= lhs.state in the order UNSEEN, UNREAD, READ.
+ static bool CompareEntriesForSync(const sync_pb::ReadingListSpecifics& lhs,
+ const sync_pb::ReadingListSpecifics& rhs);
+
+ // Asynchronously retrieve the corresponding sync data for |storage_keys|.
+ void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+
+ // Asynchronously retrieve all of the local sync data.
+ void GetAllData(DataCallback callback) override;
+
+ // Get or generate a client tag for |entity_data|. This must be the same tag
+ // that was/would have been generated in the SyncableService/Directory world
+ // for backward compatibility with pre-USS clients. The only time this
+ // theoretically needs to be called is on the creation of local data, however
+ // it is also used to verify the hash of remote data. If a data type was never
+ // launched pre-USS, then method does not need to be different from
+ // GetStorageKey().
+ std::string GetClientTag(const syncer::EntityData& entity_data) override;
+
+ // Get or generate a storage key for |entity_data|. This will only ever be
+ // called once when first encountering a remote entity. Local changes will
+ // provide their storage keys directly to Put instead of using this method.
+ // Theoretically this function doesn't need to be stable across multiple calls
+ // on the same or different clients, but to keep things simple, it probably
+ // should be.
+ std::string GetStorageKey(const syncer::EntityData& entity_data) override;
+
+ // Methods used as callbacks given to DataTypeStore.
+ void OnStoreCreated(syncer::ModelTypeStore::Result result,
+ std::unique_ptr<syncer::ModelTypeStore> store);
+
+ class ScopedBatchUpdate : public ReadingListModelStorage::ScopedBatchUpdate {
+ public:
+ explicit ScopedBatchUpdate(ReadingListStore* store);
+
+ ~ScopedBatchUpdate() override;
+
+ private:
+ ReadingListStore* store_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedBatchUpdate);
+ };
+
+ private:
+ void BeginTransaction();
+ void CommitTransaction();
+ // Callbacks needed for the database handling.
+ void OnDatabaseLoad(
+ syncer::ModelTypeStore::Result result,
+ std::unique_ptr<syncer::ModelTypeStore::RecordList> entries);
+ void OnDatabaseSave(syncer::ModelTypeStore::Result result);
+ void OnReadAllMetadata(base::Optional<syncer::ModelError> error,
+ std::unique_ptr<syncer::MetadataBatch> metadata_batch);
+
+ void AddEntryToBatch(syncer::MutableDataBatch* batch,
+ const ReadingListEntry& entry);
+
+ std::unique_ptr<syncer::ModelTypeStore> store_;
+ ReadingListModel* model_;
+ ReadingListStoreDelegate* delegate_;
+ StoreFactoryFunction create_store_callback_;
+
+ int pending_transaction_count_;
+ std::unique_ptr<syncer::ModelTypeStore::WriteBatch> batch_;
+};
+
+#endif // COMPONENTS_READING_LIST_IOS_READING_LIST_STORE_H_
diff --git a/chromium/components/reading_list/ios/reading_list_store_delegate.h b/chromium/components/reading_list/ios/reading_list_store_delegate.h
new file mode 100644
index 00000000000..d95100c353f
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_store_delegate.h
@@ -0,0 +1,45 @@
+// 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_IOS_READING_LIST_STORE_DELEGATE_H_
+#define COMPONENTS_READING_LIST_IOS_READING_LIST_STORE_DELEGATE_H_
+
+#include <map>
+
+#include "base/macros.h"
+
+class ReadingListEntry;
+
+// The delegate to handle callbacks from the ReadingListStore.
+class ReadingListStoreDelegate {
+ public:
+ using ReadingListEntries = std::map<GURL, ReadingListEntry>;
+ // These three methods handle callbacks from a ReadingListStore.
+ // This method is called when the local store is loaded. |entries| contains
+ // the ReadingListEntry present on the device before sync starts.
+ virtual void StoreLoaded(std::unique_ptr<ReadingListEntries> entries) = 0;
+ // Handle sync events.
+ // Called to add a new entry to the model.
+ // |entry| must not already exist in the model.
+ virtual void SyncAddEntry(std::unique_ptr<ReadingListEntry> entry) = 0;
+
+ // Called to merge a sync entry with a local entry in the model.
+ // A local entry with the same URL must exist in the local store and have an
+ // older UpdateTime.
+ // Return a pointer to the merged entry.
+ virtual ReadingListEntry* SyncMergeEntry(
+ std::unique_ptr<ReadingListEntry> entry) = 0;
+
+ // Called to remove an entry to the model.
+ virtual void SyncRemoveEntry(const GURL& url) = 0;
+
+ protected:
+ ReadingListStoreDelegate() {}
+ virtual ~ReadingListStoreDelegate() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReadingListStoreDelegate);
+};
+
+#endif // COMPONENTS_READING_LIST_IOS_READING_LIST_STORE_DELEGATE_H_
diff --git a/chromium/components/reading_list/ios/reading_list_store_unittest.mm b/chromium/components/reading_list/ios/reading_list_store_unittest.mm
new file mode 100644
index 00000000000..9ba369d41bd
--- /dev/null
+++ b/chromium/components/reading_list/ios/reading_list_store_unittest.mm
@@ -0,0 +1,440 @@
+// 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/ios/reading_list_store.h"
+
+#include <map>
+#include <set>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#import "base/test/ios/wait_util.h"
+#include "components/reading_list/ios/reading_list_model_impl.h"
+#include "components/sync/model/fake_model_type_change_processor.h"
+#include "components/sync/model/model_type_store_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Tests that the transition from |entryA| to |entryB| is possible (|possible|
+// is true) or not.
+void ExpectAB(const sync_pb::ReadingListSpecifics& entryA,
+ const sync_pb::ReadingListSpecifics& entryB,
+ bool possible) {
+ EXPECT_EQ(ReadingListStore::CompareEntriesForSync(entryA, entryB), possible);
+ std::unique_ptr<ReadingListEntry> a =
+ ReadingListEntry::FromReadingListSpecifics(entryA);
+ std::unique_ptr<ReadingListEntry> b =
+ ReadingListEntry::FromReadingListSpecifics(entryB);
+ a->MergeWithEntry(*b);
+ std::unique_ptr<sync_pb::ReadingListSpecifics> mergedEntry =
+ a->AsReadingListSpecifics();
+ if (possible) {
+ // If transition is possible, the merge should be B.
+ EXPECT_EQ(entryB.SerializeAsString(), mergedEntry->SerializeAsString());
+ } else {
+ // If transition is not possible, the transition shold be possible to the
+ // merged state.
+ EXPECT_TRUE(ReadingListStore::CompareEntriesForSync(entryA, *mergedEntry));
+ EXPECT_TRUE(ReadingListStore::CompareEntriesForSync(entryB, *mergedEntry));
+ }
+}
+
+class FakeModelTypeChangeProcessorObserver {
+ public:
+ virtual void Put(const std::string& client_tag,
+ std::unique_ptr<syncer::EntityData> entity_data,
+ syncer::MetadataChangeList* metadata_change_list) = 0;
+
+ virtual void Delete(const std::string& client_tag,
+ syncer::MetadataChangeList* metadata_change_list) = 0;
+};
+
+class TestModelTypeChangeProcessor
+ : public syncer::FakeModelTypeChangeProcessor {
+ public:
+ void SetObserver(FakeModelTypeChangeProcessorObserver* observer) {
+ observer_ = observer;
+ }
+
+ void Put(const std::string& client_tag,
+ std::unique_ptr<syncer::EntityData> entity_data,
+ syncer::MetadataChangeList* metadata_change_list) override {
+ observer_->Put(client_tag, std::move(entity_data), metadata_change_list);
+ }
+
+ void Delete(const std::string& client_tag,
+ syncer::MetadataChangeList* metadata_change_list) override {
+ observer_->Delete(client_tag, metadata_change_list);
+ }
+
+ private:
+ FakeModelTypeChangeProcessorObserver* observer_;
+};
+
+class ReadingListStoreTest : public testing::Test,
+ public FakeModelTypeChangeProcessorObserver,
+ public ReadingListStoreDelegate {
+ protected:
+ ReadingListStoreTest()
+ : store_(syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {
+ ClearState();
+ reading_list_store_ = base::MakeUnique<ReadingListStore>(
+ base::Bind(&syncer::ModelTypeStoreTestUtil::MoveStoreToCallback,
+ base::Passed(&store_)),
+ base::Bind(&ReadingListStoreTest::CreateModelTypeChangeProcessor,
+ base::Unretained(this)));
+ model_ = base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr);
+ reading_list_store_->SetReadingListModel(model_.get(), this);
+
+ base::RunLoop().RunUntilIdle();
+ }
+
+ std::unique_ptr<syncer::ModelTypeChangeProcessor>
+ CreateModelTypeChangeProcessor(syncer::ModelType type,
+ syncer::ModelTypeSyncBridge* service) {
+ auto processor = base::MakeUnique<TestModelTypeChangeProcessor>();
+ processor->SetObserver(this);
+ return std::move(processor);
+ }
+
+ void Put(const std::string& storage_key,
+ std::unique_ptr<syncer::EntityData> entity_data,
+ syncer::MetadataChangeList* metadata_changes) override {
+ put_multimap_.insert(std::make_pair(storage_key, std::move(entity_data)));
+ put_called_++;
+ }
+
+ void Delete(const std::string& storage_key,
+ syncer::MetadataChangeList* metadata_changes) override {
+ delete_set_.insert(storage_key);
+ delete_called_++;
+ }
+
+ void AssertCounts(int put_called,
+ int delete_called,
+ int sync_add_called,
+ int sync_remove_called,
+ int sync_merge_called) {
+ EXPECT_EQ(put_called, put_called_);
+ EXPECT_EQ(delete_called, delete_called_);
+ EXPECT_EQ(sync_add_called, sync_add_called_);
+ EXPECT_EQ(sync_remove_called, sync_remove_called_);
+ EXPECT_EQ(sync_merge_called, sync_merge_called_);
+ }
+
+ void ClearState() {
+ delete_called_ = 0;
+ put_called_ = 0;
+ delete_set_.clear();
+ put_multimap_.clear();
+ sync_add_called_ = 0;
+ sync_remove_called_ = 0;
+ sync_merge_called_ = 0;
+ sync_added_.clear();
+ sync_removed_.clear();
+ sync_merged_.clear();
+ }
+
+ // These three mathods handle callbacks from a ReadingListStore.
+ void StoreLoaded(std::unique_ptr<ReadingListEntries> entries) override {}
+
+ // Handle sync events.
+ void SyncAddEntry(std::unique_ptr<ReadingListEntry> entry) override {
+ sync_add_called_++;
+ sync_added_[entry->URL().spec()] = entry->IsRead();
+ }
+
+ void SyncRemoveEntry(const GURL& gurl) override {
+ sync_remove_called_++;
+ sync_removed_.insert(gurl.spec());
+ }
+
+ ReadingListEntry* SyncMergeEntry(
+ std::unique_ptr<ReadingListEntry> entry) override {
+ sync_merge_called_++;
+ sync_merged_[entry->URL().spec()] = entry->IsRead();
+ return model_->SyncMergeEntry(std::move(entry));
+ }
+
+ // In memory model type store needs a MessageLoop.
+ base::MessageLoop message_loop_;
+
+ std::unique_ptr<syncer::ModelTypeStore> store_;
+ std::unique_ptr<ReadingListModelImpl> model_;
+ std::unique_ptr<ReadingListStore> reading_list_store_;
+ int put_called_;
+ int delete_called_;
+ int sync_add_called_;
+ int sync_remove_called_;
+ int sync_merge_called_;
+ std::map<std::string, std::unique_ptr<syncer::EntityData>> put_multimap_;
+ std::set<std::string> delete_set_;
+ std::map<std::string, bool> sync_added_;
+ std::set<std::string> sync_removed_;
+ std::map<std::string, bool> sync_merged_;
+};
+
+TEST_F(ReadingListStoreTest, CheckEmpties) {
+ EXPECT_EQ(0ul, model_->size());
+}
+
+TEST_F(ReadingListStoreTest, SaveOneRead) {
+ ReadingListEntry entry(GURL("http://read.example.com/"), "read title");
+ entry.SetRead(true);
+ reading_list_store_->SaveEntry(entry);
+ AssertCounts(1, 0, 0, 0, 0);
+ syncer::EntityData* data = put_multimap_["http://read.example.com/"].get();
+ const sync_pb::ReadingListSpecifics& specifics =
+ data->specifics.reading_list();
+ EXPECT_EQ(specifics.title(), "read title");
+ EXPECT_EQ(specifics.url(), "http://read.example.com/");
+ EXPECT_EQ(specifics.status(), sync_pb::ReadingListSpecifics::READ);
+}
+
+TEST_F(ReadingListStoreTest, SaveOneUnread) {
+ ReadingListEntry entry(GURL("http://unread.example.com/"), "unread title");
+ reading_list_store_->SaveEntry(entry);
+ AssertCounts(1, 0, 0, 0, 0);
+ syncer::EntityData* data = put_multimap_["http://unread.example.com/"].get();
+ const sync_pb::ReadingListSpecifics& specifics =
+ data->specifics.reading_list();
+ EXPECT_EQ(specifics.title(), "unread title");
+ EXPECT_EQ(specifics.url(), "http://unread.example.com/");
+ EXPECT_EQ(specifics.status(), sync_pb::ReadingListSpecifics::UNSEEN);
+}
+
+TEST_F(ReadingListStoreTest, SyncMergeOneEntry) {
+ syncer::EntityDataMap remote_input;
+ ReadingListEntry entry(GURL("http://read.example.com/"), "read title");
+ entry.SetRead(true);
+ std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
+ entry.AsReadingListSpecifics();
+
+ syncer::EntityData data;
+ data.client_tag_hash = "http://read.example.com/";
+ *data.specifics.mutable_reading_list() = *specifics;
+
+ remote_input["http://read.example.com/"] = data.PassToPtr();
+
+ std::unique_ptr<syncer::MetadataChangeList> metadata_changes(
+ reading_list_store_->CreateMetadataChangeList());
+ auto error = reading_list_store_->MergeSyncData(std::move(metadata_changes),
+ remote_input);
+ AssertCounts(0, 0, 1, 0, 0);
+ EXPECT_EQ(sync_added_.size(), 1u);
+ EXPECT_EQ(sync_added_.count("http://read.example.com/"), 1u);
+ EXPECT_EQ(sync_added_["http://read.example.com/"], true);
+}
+
+TEST_F(ReadingListStoreTest, ApplySyncChangesOneAdd) {
+ syncer::EntityDataMap remote_input;
+ ReadingListEntry entry(GURL("http://read.example.com/"), "read title");
+ entry.SetRead(true);
+ std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
+ entry.AsReadingListSpecifics();
+ syncer::EntityData data;
+ data.client_tag_hash = "http://read.example.com/";
+ *data.specifics.mutable_reading_list() = *specifics;
+
+ syncer::EntityChangeList add_changes;
+
+ add_changes.push_back(syncer::EntityChange::CreateAdd(
+ "http://read.example.com/", data.PassToPtr()));
+ auto error = reading_list_store_->ApplySyncChanges(
+ reading_list_store_->CreateMetadataChangeList(), add_changes);
+ AssertCounts(0, 0, 1, 0, 0);
+ EXPECT_EQ(sync_added_.size(), 1u);
+ EXPECT_EQ(sync_added_.count("http://read.example.com/"), 1u);
+ EXPECT_EQ(sync_added_["http://read.example.com/"], true);
+}
+
+TEST_F(ReadingListStoreTest, ApplySyncChangesOneMerge) {
+ syncer::EntityDataMap remote_input;
+ model_->AddEntry(GURL("http://unread.example.com/"), "unread title",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(10));
+
+ ReadingListEntry new_entry(GURL("http://unread.example.com/"),
+ "unread title");
+ new_entry.SetRead(true);
+ std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
+ new_entry.AsReadingListSpecifics();
+ syncer::EntityData data;
+ data.client_tag_hash = "http://unread.example.com/";
+ *data.specifics.mutable_reading_list() = *specifics;
+
+ syncer::EntityChangeList add_changes;
+ add_changes.push_back(syncer::EntityChange::CreateAdd(
+ "http://unread.example.com/", data.PassToPtr()));
+ auto error = reading_list_store_->ApplySyncChanges(
+ reading_list_store_->CreateMetadataChangeList(), add_changes);
+ AssertCounts(1, 0, 0, 0, 1);
+ EXPECT_EQ(sync_merged_.size(), 1u);
+ EXPECT_EQ(sync_merged_.count("http://unread.example.com/"), 1u);
+ EXPECT_EQ(sync_merged_["http://unread.example.com/"], true);
+}
+
+TEST_F(ReadingListStoreTest, ApplySyncChangesOneIgnored) {
+ // Read entry but with unread URL as it must update the other one.
+ ReadingListEntry old_entry(GURL("http://unread.example.com/"),
+ "old unread title");
+ old_entry.SetRead(true);
+
+ base::test::ios::SpinRunLoopWithMinDelay(
+ base::TimeDelta::FromMilliseconds(10));
+ syncer::EntityDataMap remote_input;
+ model_->AddEntry(GURL("http://unread.example.com/"), "new unread title",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ AssertCounts(0, 0, 0, 0, 0);
+
+ std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
+ old_entry.AsReadingListSpecifics();
+ syncer::EntityData data;
+ data.client_tag_hash = "http://unread.example.com/";
+ *data.specifics.mutable_reading_list() = *specifics;
+
+ syncer::EntityChangeList add_changes;
+ add_changes.push_back(syncer::EntityChange::CreateAdd(
+ "http://unread.example.com/", data.PassToPtr()));
+ auto error = reading_list_store_->ApplySyncChanges(
+ reading_list_store_->CreateMetadataChangeList(), add_changes);
+ AssertCounts(1, 0, 0, 0, 1);
+ EXPECT_EQ(sync_merged_.size(), 1u);
+}
+
+TEST_F(ReadingListStoreTest, ApplySyncChangesOneRemove) {
+ syncer::EntityChangeList delete_changes;
+ delete_changes.push_back(
+ syncer::EntityChange::CreateDelete("http://read.example.com/"));
+ auto error = reading_list_store_->ApplySyncChanges(
+ reading_list_store_->CreateMetadataChangeList(), delete_changes);
+ AssertCounts(0, 0, 0, 1, 0);
+ EXPECT_EQ(sync_removed_.size(), 1u);
+ EXPECT_EQ(sync_removed_.count("http://read.example.com/"), 1u);
+}
+
+TEST_F(ReadingListStoreTest, CompareEntriesForSync) {
+ sync_pb::ReadingListSpecifics entryA;
+ sync_pb::ReadingListSpecifics entryB;
+ entryA.set_entry_id("http://foo.bar/");
+ entryB.set_entry_id("http://foo.bar/");
+ entryA.set_url("http://foo.bar/");
+ entryB.set_url("http://foo.bar/");
+ entryA.set_title("Foo Bar");
+ entryB.set_title("Foo Bar");
+ entryA.set_status(sync_pb::ReadingListSpecifics::UNREAD);
+ entryB.set_status(sync_pb::ReadingListSpecifics::UNREAD);
+ entryA.set_creation_time_us(10);
+ entryB.set_creation_time_us(10);
+ entryA.set_first_read_time_us(50);
+ entryB.set_first_read_time_us(50);
+ entryA.set_update_time_us(100);
+ entryB.set_update_time_us(100);
+ entryA.set_update_title_time_us(110);
+ entryB.set_update_title_time_us(110);
+ // Equal entries can be submitted.
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, true);
+
+ // Try to update each field.
+
+ // You cannot change the URL of an entry.
+ entryA.set_url("http://foo.foo/");
+ EXPECT_FALSE(ReadingListStore::CompareEntriesForSync(entryA, entryB));
+ EXPECT_FALSE(ReadingListStore::CompareEntriesForSync(entryB, entryA));
+ entryA.set_url("http://foo.bar/");
+
+ // You can set a title to a title later in alphabetical order if the
+ // update_title_time is the same. If a title has been more recently updated,
+ // the only possible transition is to this one.
+ entryA.set_title("");
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_update_title_time_us(109);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_update_title_time_us(110);
+
+ entryA.set_title("Foo Aar");
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_update_title_time_us(109);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_update_title_time_us(110);
+
+ entryA.set_title("Foo Ba");
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_update_title_time_us(109);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_update_title_time_us(110);
+
+ entryA.set_title("Foo Bas");
+ ExpectAB(entryA, entryB, false);
+ ExpectAB(entryB, entryA, true);
+ entryA.set_update_title_time_us(109);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_update_title_time_us(110);
+ entryA.set_title("Foo Bar");
+
+ // Update times.
+ entryA.set_creation_time_us(9);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_first_read_time_us(51);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_first_read_time_us(49);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_first_read_time_us(0);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_first_read_time_us(50);
+ entryB.set_first_read_time_us(0);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryB.set_first_read_time_us(50);
+ entryA.set_creation_time_us(10);
+ entryA.set_first_read_time_us(51);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_first_read_time_us(0);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ entryA.set_first_read_time_us(50);
+
+ entryA.set_update_time_us(99);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ sync_pb::ReadingListSpecifics::ReadingListEntryStatus status_oder[3] = {
+ sync_pb::ReadingListSpecifics::UNSEEN,
+ sync_pb::ReadingListSpecifics::UNREAD,
+ sync_pb::ReadingListSpecifics::READ};
+ for (int index_a = 0; index_a < 3; index_a++) {
+ entryA.set_status(status_oder[index_a]);
+ for (int index_b = 0; index_b < 3; index_b++) {
+ entryB.set_status(status_oder[index_b]);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ }
+ }
+ entryA.set_update_time_us(100);
+ for (int index_a = 0; index_a < 3; index_a++) {
+ entryA.set_status(status_oder[index_a]);
+ entryB.set_status(status_oder[index_a]);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, true);
+ for (int index_b = index_a + 1; index_b < 3; index_b++) {
+ entryB.set_status(status_oder[index_b]);
+ ExpectAB(entryA, entryB, true);
+ ExpectAB(entryB, entryA, false);
+ }
+ }
+}
diff --git a/chromium/components/renderer_context_menu/BUILD.gn b/chromium/components/renderer_context_menu/BUILD.gn
index 0a21d2773df..7f98c556fa5 100644
--- a/chromium/components/renderer_context_menu/BUILD.gn
+++ b/chromium/components/renderer_context_menu/BUILD.gn
@@ -22,6 +22,7 @@ static_library("renderer_context_menu") {
"//components/search_engines",
"//content/public/browser",
"//content/public/common",
+ "//ppapi/features",
"//printing/features",
"//third_party/WebKit/public:blink_headers",
"//ui/base",
diff --git a/chromium/components/renderer_context_menu/DEPS b/chromium/components/renderer_context_menu/DEPS
index 302f35645e2..a8b1208bda8 100644
--- a/chromium/components/renderer_context_menu/DEPS
+++ b/chromium/components/renderer_context_menu/DEPS
@@ -5,5 +5,7 @@ include_rules = [
"+ui/base",
"+ui/gfx",
"+ui/views",
+ # This directory contains build flags and does not pull all of PPAPI in.
+ "+ppapi/features",
"+third_party/WebKit/public/web",
]
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 63d8df500f6..7f979cf7937 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
@@ -16,6 +16,7 @@
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/menu_item.h"
+#include "ppapi/features/features.h"
#include "third_party/WebKit/public/web/WebContextMenuData.h"
using blink::WebContextMenuData;
@@ -309,7 +310,7 @@ void RenderViewContextMenuBase::ExecuteCommand(int id, int event_flags) {
if (IsContentCustomCommandId(id)) {
unsigned action = id - content_context_custom_first;
const content::CustomContextMenuContext& context = params_.custom_context;
-#if defined(ENABLE_PLUGINS)
+#if BUILDFLAG(ENABLE_PLUGINS)
if (context.request_id && !context.is_pepper_menu)
HandleAuthorizeAllPlugins();
#endif
diff --git a/chromium/components/renderer_context_menu/render_view_context_menu_base.h b/chromium/components/renderer_context_menu/render_view_context_menu_base.h
index 5ea257c9976..765ee5f4bd5 100644
--- a/chromium/components/renderer_context_menu/render_view_context_menu_base.h
+++ b/chromium/components/renderer_context_menu/render_view_context_menu_base.h
@@ -20,6 +20,7 @@
#include "components/renderer_context_menu/render_view_context_menu_observer.h"
#include "components/renderer_context_menu/render_view_context_menu_proxy.h"
#include "content/public/common/context_menu_params.h"
+#include "ppapi/features/features.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/window_open_disposition.h"
@@ -29,15 +30,6 @@ class RenderFrameHost;
class WebContents;
}
-namespace gfx {
-class Point;
-}
-
-namespace blink {
-struct WebMediaPlayerAction;
-struct WebPluginAction;
-}
-
class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate,
public RenderViewContextMenuProxy {
public:
@@ -144,7 +136,7 @@ class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate,
// Increments histogram value for visible context menu item specified by |id|.
virtual void RecordShownItem(int id) = 0;
-#if defined(ENABLE_PLUGINS)
+#if BUILDFLAG(ENABLE_PLUGINS)
virtual void HandleAuthorizeAllPlugins() = 0;
#endif
diff --git a/chromium/components/resources/OWNERS b/chromium/components/resources/OWNERS
index a573f6c2cd2..eb2ef926c60 100644
--- a/chromium/components/resources/OWNERS
+++ b/chromium/components/resources/OWNERS
@@ -1,16 +1,12 @@
per-file autofill_scaled_resources.grdp=estade@chromium.org
+per-file content_suggestions*=file://components/ntp_snippets/OWNERS
per-file crash_*=cpu@chromium.org
per-file crash_*=jochen@chromium.org
per-file crash_*=mark@chromium.org
per-file crash_*=rsesek@chromium.org
per-file crash_*=scottmg@chromium.org
per-file crash_*=thestig@chromium.org
-per-file data_reduction_proxy*=bengr@chromium.org
-per-file data_reduction_proxy*=kundaji@chromium.org
-per-file data_reduction_proxy*=megjablon@chromium.org
-per-file data_reduction_proxy*=ryansturm@chromium.org
-per-file data_reduction_proxy*=sclittle@chromium.org
-per-file data_reduction_proxy*=tbansal@chromium.org
+per-file data_reduction_proxy*=file://components/data_reduction_proxy/OWNERS
per-file dom_distiller*=mdjones@chromium.org
per-file dom_distiller*=nyquist@chromium.org
per-file dom_distiller*=wychen@chromium.org
@@ -19,10 +15,9 @@ per-file gcm_driver_resources.grdp=fgorski@chromium.org
per-file gcm_driver_resources.grdp=jianli@chromium.org
per-file gcm_driver_resources.grdp=peter@chromium.org
per-file gcm_driver_resources.grdp=zea@chromium.org
-per-file neterror*=mmenke@chromium.org
per-file neterror*=juliatuttle@chromium.org
-per-file ntp_tiles.grdp=file://components/ntp_tiles/OWNERS
-per-file proximity_auth*=isherman@chromium.org
+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 supervised_user_error_page.grpd=aberent@chromium.org
per-file supervised_user_error_page.grpd=bauerb@chromium.org
diff --git a/chromium/components/resources/autofill_scaled_resources.grdp b/chromium/components/resources/autofill_scaled_resources.grdp
index c07f1d5ba93..40e9adccec4 100644
--- a/chromium/components/resources/autofill_scaled_resources.grdp
+++ b/chromium/components/resources/autofill_scaled_resources.grdp
@@ -1,9 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<grit-part>
<structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_AMEX" file="autofill/amex.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_DINERS" file="autofill/diners.png" />
<structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_DISCOVER" file="autofill/discover.png" />
<structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_GENERIC" file="autofill/cc-generic.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_JCB" file="autofill/jcb.png" />
<structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_MASTERCARD" file="autofill/mastercard.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_MIR" file="autofill/mir.png" />
<structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_VISA" file="autofill/visa.png" />
<!-- These are not used on desktop, only Android, so use a placeholder file.
@@ -13,20 +16,20 @@
<structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_SCAN_NEW" file="autofill/cc-generic.png" />
<structure type="chrome_scaled_image" name="IDR_AUTOFILL_CC_SCAN_NEW_KEYBOARD_ACCESSORY" file="autofill/cc-generic.png" />
<structure type="chrome_scaled_image" name="IDR_AUTOFILL_SETTINGS" file="autofill/cc-generic.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_HTTP_WARNING" file="autofill/cc-generic.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_HTTPS_INVALID_WARNING" file="autofill/cc-generic.png" />
</if>
- <!-- PaymentRequest image variants.
- TODO(crbug.com/602666): Move the credit card icons into
- components/resources and refer to them here instead of the placeholder
- files so they can be used on platforms other than Android. -->
- <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_AMEX" file="autofill/cc-generic.png" />
- <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_DINERS" file="autofill/cc-generic.png" />
- <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_DISCOVER" file="autofill/cc-generic.png" />
- <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_GENERIC" file="autofill/cc-generic.png" />
- <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_JCB" file="autofill/cc-generic.png" />
- <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_MASTERCARD" file="autofill/cc-generic.png" />
- <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_UNIONPAY" file="autofill/cc-generic.png" />
- <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_VISA" file="autofill/cc-generic.png" />
+ <!-- PaymentRequest image variants. -->
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_AMEX" file="autofill/pr_amex.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_DINERS" file="autofill/pr_dinersclub.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_DISCOVER" file="autofill/pr_discover.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_GENERIC" file="autofill/pr_generic.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_JCB" file="autofill/pr_jcb.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_MASTERCARD" file="autofill/pr_mc.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_MIR" file="autofill/pr_mir.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_UNIONPAY" file="autofill/pr_unionpay.png" />
+ <structure type="chrome_scaled_image" name="IDR_AUTOFILL_PR_VISA" file="autofill/pr_visa.png" />
<structure type="chrome_scaled_image" name="IDR_CREDIT_CARD_CVC_HINT" file="autofill/credit_card_cvc_hint.png" />
<structure type="chrome_scaled_image" name="IDR_CREDIT_CARD_CVC_HINT_AMEX" file="autofill/credit_card_cvc_hint_amex.png" />
diff --git a/chromium/components/resources/components_scaled_resources.grd b/chromium/components/resources/components_scaled_resources.grd
index 240326772de..511ec1968f3 100644
--- a/chromium/components/resources/components_scaled_resources.grd
+++ b/chromium/components/resources/components_scaled_resources.grd
@@ -13,6 +13,7 @@
<release seq="1">
<structures fallback_to_low_resolution="true">
<part file="autofill_scaled_resources.grdp" />
+ <part file="content_suggestions_scaled_resources.grdp" />
<part file="crash_scaled_resources.grdp" />
<part file="flags_ui_scaled_resources.grdp" />
<part file="neterror_scaled_resources.grdp" />
diff --git a/chromium/components/resources/content_suggestions_scaled_resources.grdp b/chromium/components/resources/content_suggestions_scaled_resources.grdp
new file mode 100644
index 00000000000..12b382dcfe0
--- /dev/null
+++ b/chromium/components/resources/content_suggestions_scaled_resources.grdp
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit-part>
+ <structure type="chrome_scaled_image" name="IDR_PHYSICAL_WEB_LOGO_WITH_PADDING" file="content_suggestions/physical_web_logo_with_padding.png" />
+</grit-part>
diff --git a/chromium/components/resources/default_100_percent/autofill/amex.png b/chromium/components/resources/default_100_percent/autofill/amex.png
index b7be59cd8f5..c441aff0f44 100644
--- a/chromium/components/resources/default_100_percent/autofill/amex.png
+++ b/chromium/components/resources/default_100_percent/autofill/amex.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/diners.png b/chromium/components/resources/default_100_percent/autofill/diners.png
new file mode 100644
index 00000000000..3bd2ec49b8e
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/diners.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/discover.png b/chromium/components/resources/default_100_percent/autofill/discover.png
index 06e42c59dc7..9ec9baab1f9 100644
--- a/chromium/components/resources/default_100_percent/autofill/discover.png
+++ b/chromium/components/resources/default_100_percent/autofill/discover.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/jcb.png b/chromium/components/resources/default_100_percent/autofill/jcb.png
new file mode 100644
index 00000000000..0b580b33222
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/jcb.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/mastercard.png b/chromium/components/resources/default_100_percent/autofill/mastercard.png
index 079be0024ce..bac5d8c69b2 100644
--- a/chromium/components/resources/default_100_percent/autofill/mastercard.png
+++ b/chromium/components/resources/default_100_percent/autofill/mastercard.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/mir.png b/chromium/components/resources/default_100_percent/autofill/mir.png
new file mode 100644
index 00000000000..c9b3e79a841
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/mir.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/pr_amex.png b/chromium/components/resources/default_100_percent/autofill/pr_amex.png
new file mode 100644
index 00000000000..67dfb96f3f8
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/pr_amex.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/pr_dinersclub.png b/chromium/components/resources/default_100_percent/autofill/pr_dinersclub.png
new file mode 100644
index 00000000000..a3446c40d75
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/pr_dinersclub.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/pr_discover.png b/chromium/components/resources/default_100_percent/autofill/pr_discover.png
new file mode 100644
index 00000000000..c6e04e9929e
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/pr_discover.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/pr_generic.png b/chromium/components/resources/default_100_percent/autofill/pr_generic.png
new file mode 100644
index 00000000000..a93ff32d21e
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/pr_generic.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/pr_jcb.png b/chromium/components/resources/default_100_percent/autofill/pr_jcb.png
new file mode 100644
index 00000000000..66495ce5106
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/pr_jcb.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/pr_mc.png b/chromium/components/resources/default_100_percent/autofill/pr_mc.png
new file mode 100644
index 00000000000..bcdf48f0272
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/pr_mc.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/pr_mir.png b/chromium/components/resources/default_100_percent/autofill/pr_mir.png
new file mode 100644
index 00000000000..fc97556f02f
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/pr_mir.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/pr_unionpay.png b/chromium/components/resources/default_100_percent/autofill/pr_unionpay.png
new file mode 100644
index 00000000000..0e11c0f97f3
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/pr_unionpay.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/pr_visa.png b/chromium/components/resources/default_100_percent/autofill/pr_visa.png
new file mode 100644
index 00000000000..c0c8c08201e
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/autofill/pr_visa.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/visa.png b/chromium/components/resources/default_100_percent/autofill/visa.png
index 3505d913d65..c7a9d8ee931 100644
--- a/chromium/components/resources/default_100_percent/autofill/visa.png
+++ b/chromium/components/resources/default_100_percent/autofill/visa.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/content_suggestions/physical_web_logo_with_padding.png b/chromium/components/resources/default_100_percent/content_suggestions/physical_web_logo_with_padding.png
new file mode 100644
index 00000000000..271b580f9f8
--- /dev/null
+++ b/chromium/components/resources/default_100_percent/content_suggestions/physical_web_logo_with_padding.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/amex.png b/chromium/components/resources/default_200_percent/autofill/amex.png
index 72a6091b884..7a417d5a26a 100644
--- a/chromium/components/resources/default_200_percent/autofill/amex.png
+++ b/chromium/components/resources/default_200_percent/autofill/amex.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/diners.png b/chromium/components/resources/default_200_percent/autofill/diners.png
new file mode 100644
index 00000000000..522f0104cf8
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/diners.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/discover.png b/chromium/components/resources/default_200_percent/autofill/discover.png
index c10718b0b9f..0012ff0890d 100644
--- a/chromium/components/resources/default_200_percent/autofill/discover.png
+++ b/chromium/components/resources/default_200_percent/autofill/discover.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/jcb.png b/chromium/components/resources/default_200_percent/autofill/jcb.png
new file mode 100644
index 00000000000..381c938934f
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/jcb.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/mastercard.png b/chromium/components/resources/default_200_percent/autofill/mastercard.png
index 43f4d1e0316..e496711ae62 100644
--- a/chromium/components/resources/default_200_percent/autofill/mastercard.png
+++ b/chromium/components/resources/default_200_percent/autofill/mastercard.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/mir.png b/chromium/components/resources/default_200_percent/autofill/mir.png
new file mode 100644
index 00000000000..9aaa05def04
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/mir.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/pr_amex.png b/chromium/components/resources/default_200_percent/autofill/pr_amex.png
new file mode 100644
index 00000000000..f29dcdf57e1
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/pr_amex.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/pr_dinersclub.png b/chromium/components/resources/default_200_percent/autofill/pr_dinersclub.png
new file mode 100644
index 00000000000..c61096b0c69
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/pr_dinersclub.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/pr_discover.png b/chromium/components/resources/default_200_percent/autofill/pr_discover.png
new file mode 100644
index 00000000000..6d8f2ff285b
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/pr_discover.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/pr_generic.png b/chromium/components/resources/default_200_percent/autofill/pr_generic.png
new file mode 100644
index 00000000000..8dd4609eb86
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/pr_generic.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/pr_jcb.png b/chromium/components/resources/default_200_percent/autofill/pr_jcb.png
new file mode 100644
index 00000000000..2a18c032176
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/pr_jcb.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/pr_mc.png b/chromium/components/resources/default_200_percent/autofill/pr_mc.png
new file mode 100644
index 00000000000..ade774b868f
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/pr_mc.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/pr_mir.png b/chromium/components/resources/default_200_percent/autofill/pr_mir.png
new file mode 100644
index 00000000000..1b9e53300b2
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/pr_mir.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/pr_unionpay.png b/chromium/components/resources/default_200_percent/autofill/pr_unionpay.png
new file mode 100644
index 00000000000..33f62de69f6
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/pr_unionpay.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/pr_visa.png b/chromium/components/resources/default_200_percent/autofill/pr_visa.png
new file mode 100644
index 00000000000..493c2ab6edb
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/autofill/pr_visa.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/visa.png b/chromium/components/resources/default_200_percent/autofill/visa.png
index 8057ab97f62..08e401570b3 100644
--- a/chromium/components/resources/default_200_percent/autofill/visa.png
+++ b/chromium/components/resources/default_200_percent/autofill/visa.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/content_suggestions/physical_web_logo_with_padding.png b/chromium/components/resources/default_200_percent/content_suggestions/physical_web_logo_with_padding.png
new file mode 100644
index 00000000000..96aba766942
--- /dev/null
+++ b/chromium/components/resources/default_200_percent/content_suggestions/physical_web_logo_with_padding.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/pr_amex.png b/chromium/components/resources/default_300_percent/autofill/pr_amex.png
new file mode 100644
index 00000000000..318ec6584c8
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/pr_amex.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/pr_dinersclub.png b/chromium/components/resources/default_300_percent/autofill/pr_dinersclub.png
new file mode 100644
index 00000000000..87fa3c42834
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/pr_dinersclub.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/pr_discover.png b/chromium/components/resources/default_300_percent/autofill/pr_discover.png
new file mode 100644
index 00000000000..b8109ff2dd9
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/pr_discover.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/pr_generic.png b/chromium/components/resources/default_300_percent/autofill/pr_generic.png
new file mode 100644
index 00000000000..bda5b34dec9
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/pr_generic.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/pr_jcb.png b/chromium/components/resources/default_300_percent/autofill/pr_jcb.png
new file mode 100644
index 00000000000..d9919ef04f1
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/pr_jcb.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/pr_mc.png b/chromium/components/resources/default_300_percent/autofill/pr_mc.png
new file mode 100644
index 00000000000..4cdd3a52986
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/pr_mc.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/pr_mir.png b/chromium/components/resources/default_300_percent/autofill/pr_mir.png
new file mode 100644
index 00000000000..56934a40d76
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/pr_mir.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/pr_unionpay.png b/chromium/components/resources/default_300_percent/autofill/pr_unionpay.png
new file mode 100644
index 00000000000..fa1bd84cb1b
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/pr_unionpay.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/pr_visa.png b/chromium/components/resources/default_300_percent/autofill/pr_visa.png
new file mode 100644
index 00000000000..ef6aa8566ba
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/pr_visa.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/content_suggestions/physical_web_logo_with_padding.png b/chromium/components/resources/default_300_percent/content_suggestions/physical_web_logo_with_padding.png
new file mode 100644
index 00000000000..e1ebaed84f4
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/content_suggestions/physical_web_logo_with_padding.png
Binary files differ
diff --git a/chromium/components/resources/ntp_tiles_resources.grdp b/chromium/components/resources/ntp_tiles_resources.grdp
index dadacfabb41..37999984ffc 100644
--- a/chromium/components/resources/ntp_tiles_resources.grdp
+++ b/chromium/components/resources/ntp_tiles_resources.grdp
@@ -5,4 +5,7 @@
<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>
+ <include name="IDR_NTP_TILES_INTERNALS_HTML" file="../ntp_tiles/webui/resources/ntp_tiles_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+ <include name="IDR_NTP_TILES_INTERNALS_JS" file="../ntp_tiles/webui/resources/ntp_tiles_internals.js" type="BINDATA" />
+ <include name="IDR_NTP_TILES_INTERNALS_CSS" file="../ntp_tiles/webui/resources/ntp_tiles_internals.css" type="BINDATA" />
</grit-part>
diff --git a/chromium/components/safe_browsing/BUILD.gn b/chromium/components/safe_browsing/BUILD.gn
new file mode 100644
index 00000000000..f8d05496a00
--- /dev/null
+++ b/chromium/components/safe_browsing/BUILD.gn
@@ -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.
+
+source_set("safe_browsing") {
+ sources = [
+ "base_blocking_page.cc",
+ "base_blocking_page.h",
+ "base_resource_throttle.cc",
+ "base_resource_throttle.h",
+ "base_ui_manager.cc",
+ "base_ui_manager.h",
+ ]
+
+ deps = [
+ "//base:base",
+ "//base:i18n",
+ "//components/safe_browsing_db:database_manager",
+ "//components/safe_browsing_db:safe_browsing_prefs",
+ "//components/security_interstitials/content:security_interstitial_page",
+ "//components/security_interstitials/core:core",
+ "//components/subresource_filter/content/browser:browser",
+ "//content/public/browser:browser",
+ "//content/public/common:common",
+ "//net:net",
+ ]
+}
diff --git a/chromium/components/safe_browsing/DEPS b/chromium/components/safe_browsing/DEPS
new file mode 100644
index 00000000000..75760451179
--- /dev/null
+++ b/chromium/components/safe_browsing/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+ "+components/safe_browsing_db",
+ "+components/security_interstitials/content",
+ "+components/security_interstitials/core",
+ "+components/subresource_filter/content/browser",
+ "+content/public/browser",
+ "+content/public/common",
+ "+net/base",
+ "+net/log",
+ "+net/url_request",
+]
diff --git a/chromium/components/safe_browsing/OWNERS b/chromium/components/safe_browsing/OWNERS
new file mode 100644
index 00000000000..41a80e21e18
--- /dev/null
+++ b/chromium/components/safe_browsing/OWNERS
@@ -0,0 +1,7 @@
+jialiul@chromium.org
+mattm@chromium.org
+nparker@chromium.org
+shess@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
new file mode 100644
index 00000000000..6c57b34a1a8
--- /dev/null
+++ b/chromium/components/safe_browsing/base_blocking_page.cc
@@ -0,0 +1,327 @@
+// Copyright 2017 The Chromium Authors. All 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/base_blocking_page.h"
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "components/safe_browsing_db/safe_browsing_prefs.h"
+#include "components/security_interstitials/content/security_interstitial_controller_client.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+#include "content/public/browser/interstitial_page.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/user_metrics.h"
+#include "content/public/browser/web_contents.h"
+
+using base::UserMetricsAction;
+using content::InterstitialPage;
+using content::WebContents;
+using security_interstitials::SafeBrowsingErrorUI;
+using security_interstitials::SecurityInterstitialControllerClient;
+
+namespace safe_browsing {
+
+namespace {
+
+base::LazyInstance<BaseBlockingPage::UnsafeResourceMap>::Leaky
+ g_unsafe_resource_map = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+BaseBlockingPage::BaseBlockingPage(
+ BaseUIManager* ui_manager,
+ WebContents* web_contents,
+ const GURL& main_frame_url,
+ const UnsafeResourceList& unsafe_resources,
+ std::unique_ptr<SecurityInterstitialControllerClient> controller_client,
+ const SafeBrowsingErrorUI::SBErrorDisplayOptions& display_options)
+ : SecurityInterstitialPage(web_contents,
+ unsafe_resources[0].url,
+ std::move(controller_client)),
+ ui_manager_(ui_manager),
+ main_frame_url_(main_frame_url),
+ navigation_entry_index_to_remove_(
+ IsMainPageLoadBlocked(unsafe_resources) ?
+ -1 :
+ web_contents->GetController().GetLastCommittedEntryIndex()),
+ unsafe_resources_(unsafe_resources),
+ sb_error_ui_(base::MakeUnique<SafeBrowsingErrorUI>(
+ unsafe_resources_[0].url, main_frame_url_,
+ GetInterstitialReason(unsafe_resources_),
+ display_options,
+ ui_manager->app_locale(),
+ base::Time::NowFromSystemTime(),
+ controller())),
+ proceeded_(false) {}
+
+BaseBlockingPage::~BaseBlockingPage() {}
+
+// static
+const SafeBrowsingErrorUI::SBErrorDisplayOptions
+BaseBlockingPage::CreateDefaultDisplayOptions(
+ const UnsafeResourceList& unsafe_resources) {
+ return SafeBrowsingErrorUI::SBErrorDisplayOptions(
+ IsMainPageLoadBlocked(unsafe_resources),
+ true, // kSafeBrowsingExtendedReportingOptInAllowed
+ false, // is_off_the_record
+ false, // is_extended_reporting
+ false, // is_scout
+ false); // kSafeBrowsingProceedAnywayDisabled
+}
+
+// static
+void BaseBlockingPage::ShowBlockingPage(
+ BaseUIManager* ui_manager,
+ const UnsafeResource& unsafe_resource) {
+ WebContents* web_contents = unsafe_resource.web_contents_getter.Run();
+
+ if (!InterstitialPage::GetInterstitialPage(web_contents) ||
+ !unsafe_resource.is_subresource) {
+ // There is no interstitial currently showing in that tab, or we are about
+ // to display a new one for the main frame. If there is already an
+ // interstitial, showing the new one will automatically hide the old one.
+ content::NavigationEntry* entry =
+ unsafe_resource.GetNavigationEntryForResource();
+ const UnsafeResourceList resources{unsafe_resource};
+ BaseBlockingPage* blocking_page =
+ new BaseBlockingPage(
+ ui_manager, web_contents,
+ entry ? entry->GetURL() : GURL(),
+ resources,
+ CreateControllerClient(
+ web_contents, resources,
+ ui_manager->history_service(web_contents),
+ ui_manager->app_locale(),
+ ui_manager->default_safe_page()),
+ CreateDefaultDisplayOptions(resources));
+ blocking_page->Show();
+ return;
+ }
+
+ // This is an interstitial for a page's resource, let's queue it.
+ UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
+ (*unsafe_resource_map)[web_contents].push_back(unsafe_resource);
+}
+
+// static
+bool BaseBlockingPage::IsMainPageLoadBlocked(
+ const UnsafeResourceList& unsafe_resources) {
+ // If there is more than one unsafe resource, the main page load must not be
+ // blocked. Otherwise, check if the one resource is.
+ return unsafe_resources.size() == 1 &&
+ unsafe_resources[0].IsMainPageLoadBlocked();
+}
+
+void BaseBlockingPage::OnProceed() {
+ proceeded_ = true;
+
+ ui_manager_->OnBlockingPageDone(unsafe_resources_, true /* proceed */,
+ web_contents(), main_frame_url_);
+}
+
+void BaseBlockingPage::OnDontProceed() {
+ // We could have already called Proceed(), in which case we must not notify
+ // the SafeBrowsingUIManager again, as the client has been deleted.
+ if (proceeded_)
+ return;
+
+ UpdateMetricsAfterSecurityInterstitial();
+ if (!sb_error_ui_->is_proceed_anyway_disabled()) {
+ controller()->metrics_helper()->RecordUserDecision(
+ security_interstitials::MetricsHelper::DONT_PROCEED);
+ }
+
+ // Send the malware details, if we opted to.
+ FinishThreatDetails(base::TimeDelta(), false /* did_proceed */,
+ controller()->metrics_helper()->NumVisits()); // No delay
+
+ ui_manager_->OnBlockingPageDone(unsafe_resources_, false /* proceed */,
+ web_contents(), main_frame_url_);
+
+ // The user does not want to proceed, clear the queued unsafe resources
+ // notifications we received while the interstitial was showing.
+ UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
+ UnsafeResourceMap::iterator iter = unsafe_resource_map->find(web_contents());
+ if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
+ ui_manager_->OnBlockingPageDone(iter->second, false, web_contents(),
+ main_frame_url_);
+ unsafe_resource_map->erase(iter);
+ }
+
+ // We don't remove the navigation entry if the tab is being destroyed as this
+ // would trigger a navigation that would cause trouble as the render view host
+ // for the tab has by then already been destroyed. We also don't delete the
+ // current entry if it has been committed again, which is possible on a page
+ // that had a subresource warning.
+ const int last_committed_index =
+ web_contents()->GetController().GetLastCommittedEntryIndex();
+ if (navigation_entry_index_to_remove_ != -1 &&
+ navigation_entry_index_to_remove_ != last_committed_index &&
+ !web_contents()->IsBeingDestroyed()) {
+ CHECK(web_contents()->GetController().RemoveEntryAtIndex(
+ navigation_entry_index_to_remove_));
+ }
+}
+
+void BaseBlockingPage::CommandReceived(
+ const std::string& page_cmd) {
+ if (page_cmd == "\"pageLoadComplete\"") {
+ // content::WaitForRenderFrameReady sends this message when the page
+ // load completes. Ignore it.
+ return;
+ }
+
+ int command = 0;
+ bool retval = base::StringToInt(page_cmd, &command);
+ DCHECK(retval) << page_cmd;
+
+ sb_error_ui_->HandleCommand(
+ static_cast<security_interstitials::SecurityInterstitialCommands>(
+ command));
+}
+
+bool BaseBlockingPage::ShouldCreateNewNavigation() const {
+ return sb_error_ui_->is_main_frame_load_blocked();
+}
+
+void BaseBlockingPage::PopulateInterstitialStrings(
+ base::DictionaryValue* load_time_data) {
+ sb_error_ui_->PopulateStringsForHTML(load_time_data);
+}
+
+void BaseBlockingPage::FinishThreatDetails(const base::TimeDelta& delay,
+ bool did_proceed,
+ int num_visits) {}
+
+// static
+BaseBlockingPage::UnsafeResourceMap*
+BaseBlockingPage::GetUnsafeResourcesMap() {
+ return g_unsafe_resource_map.Pointer();
+}
+
+// static
+std::string BaseBlockingPage::GetMetricPrefix(
+ const UnsafeResourceList& unsafe_resources,
+ SafeBrowsingErrorUI::SBInterstitialReason interstitial_reason) {
+ bool primary_subresource = unsafe_resources[0].is_subresource;
+ switch (interstitial_reason) {
+ case SafeBrowsingErrorUI::SB_REASON_MALWARE:
+ return primary_subresource ? "malware_subresource" : "malware";
+ case SafeBrowsingErrorUI::SB_REASON_HARMFUL:
+ return primary_subresource ? "harmful_subresource" : "harmful";
+ case SafeBrowsingErrorUI::SB_REASON_PHISHING:
+ ThreatPatternType threat_pattern_type =
+ unsafe_resources[0].threat_metadata.threat_pattern_type;
+ if (threat_pattern_type == ThreatPatternType::PHISHING ||
+ threat_pattern_type == ThreatPatternType::NONE)
+ return primary_subresource ? "phishing_subresource" : "phishing";
+ else if (threat_pattern_type == ThreatPatternType::SOCIAL_ENGINEERING_ADS)
+ return primary_subresource ? "social_engineering_ads_subresource"
+ : "social_engineering_ads";
+ else if (threat_pattern_type ==
+ ThreatPatternType::SOCIAL_ENGINEERING_LANDING)
+ return primary_subresource ? "social_engineering_landing_subresource"
+ : "social_engineering_landing";
+ }
+ NOTREACHED();
+ return "unkown_metric_prefix";
+}
+
+// We populate a parallel set of metrics to differentiate some threat sources.
+// static
+std::string BaseBlockingPage::GetExtraMetricsSuffix(
+ const UnsafeResourceList& unsafe_resources) {
+ switch (unsafe_resources[0].threat_source) {
+ case safe_browsing::ThreatSource::DATA_SAVER:
+ return "from_data_saver";
+ case safe_browsing::ThreatSource::REMOTE:
+ case safe_browsing::ThreatSource::LOCAL_PVER3:
+ // REMOTE and LOCAL_PVER3 can be distinguished in the logs
+ // by platform type: Remote is mobile, local_pver3 is desktop.
+ return "from_device";
+ case safe_browsing::ThreatSource::LOCAL_PVER4:
+ return "from_device_v4";
+ case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION:
+ return "from_client_side_detection";
+ case safe_browsing::ThreatSource::UNKNOWN:
+ break;
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+// static
+SafeBrowsingErrorUI::SBInterstitialReason
+BaseBlockingPage::GetInterstitialReason(
+ const UnsafeResourceList& unsafe_resources) {
+ bool harmful = false;
+ for (UnsafeResourceList::const_iterator iter = unsafe_resources.begin();
+ iter != unsafe_resources.end(); ++iter) {
+ 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) {
+ return SafeBrowsingErrorUI::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);
+ }
+ }
+
+ if (harmful)
+ return SafeBrowsingErrorUI::SB_REASON_HARMFUL;
+ return SafeBrowsingErrorUI::SB_REASON_PHISHING;
+}
+
+BaseUIManager* BaseBlockingPage::ui_manager() const {
+ return ui_manager_;
+}
+
+const GURL BaseBlockingPage::main_frame_url() const {
+ return main_frame_url_;
+}
+
+BaseBlockingPage::UnsafeResourceList
+BaseBlockingPage::unsafe_resources() const {
+ return unsafe_resources_;
+}
+
+SafeBrowsingErrorUI* BaseBlockingPage::sb_error_ui() const {
+ return sb_error_ui_.get();
+}
+
+void BaseBlockingPage::set_proceeded(bool proceeded) {
+ proceeded_ = proceeded;
+}
+
+// static
+std::unique_ptr<SecurityInterstitialControllerClient>
+BaseBlockingPage::CreateControllerClient(
+ content::WebContents* web_contents,
+ const UnsafeResourceList& unsafe_resources,
+ history::HistoryService* history_service,
+ const std::string& app_locale,
+ const GURL& default_safe_page) {
+ SafeBrowsingErrorUI::SBInterstitialReason interstitial_reason =
+ GetInterstitialReason(unsafe_resources);
+ security_interstitials::MetricsHelper::ReportDetails reporting_info;
+ reporting_info.metric_prefix =
+ GetMetricPrefix(unsafe_resources, interstitial_reason);
+ reporting_info.extra_suffix = GetExtraMetricsSuffix(unsafe_resources);
+
+ std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper =
+ base::MakeUnique<security_interstitials::MetricsHelper>(
+ unsafe_resources[0].url, reporting_info, history_service);
+
+ return base::MakeUnique<SecurityInterstitialControllerClient>(
+ web_contents, std::move(metrics_helper), nullptr, app_locale,
+ default_safe_page);
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/base_blocking_page.h b/chromium/components/safe_browsing/base_blocking_page.h
new file mode 100644
index 00000000000..3e031bba876
--- /dev/null
+++ b/chromium/components/safe_browsing/base_blocking_page.h
@@ -0,0 +1,140 @@
+// Copyright 2017 The Chromium Authors. All 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_BASE_BLOCKING_PAGE_H_
+#define COMPONENTS_SAFE_BROWSING_BASE_BLOCKING_PAGE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/safe_browsing/base_ui_manager.h"
+#include "components/security_interstitials/content/security_interstitial_page.h"
+#include "components/security_interstitials/core/safe_browsing_error_ui.h"
+#include "content/public/browser/interstitial_page_delegate.h"
+#include "url/gurl.h"
+
+namespace history {
+class HistoryService;
+}
+
+namespace safe_browsing {
+
+// Base class for managing the SafeBrowsing interstitial pages.
+class BaseBlockingPage
+ : public security_interstitials::SecurityInterstitialPage {
+ public:
+ typedef security_interstitials::UnsafeResource UnsafeResource;
+ typedef security_interstitials::SafeBrowsingErrorUI SafeBrowsingErrorUI;
+ typedef std::vector<UnsafeResource> UnsafeResourceList;
+ typedef std::unordered_map<content::WebContents*, UnsafeResourceList>
+ UnsafeResourceMap;
+
+ ~BaseBlockingPage() override;
+
+ static const SafeBrowsingErrorUI::SBErrorDisplayOptions
+ CreateDefaultDisplayOptions(const UnsafeResourceList& unsafe_resources);
+
+ // Shows a blocking page warning the user about phishing/malware for a
+ // specific resource.
+ // This can be called several times. If an interstitial is already showing
+ // and the user decides to proceed, it will be discarded and a new one will be
+ // displayed.
+ static void ShowBlockingPage(BaseUIManager* ui_manager,
+ const UnsafeResource& resource);
+
+ // Returns true if the passed |unsafe_resources| is blocking the load of
+ // the main page.
+ static bool IsMainPageLoadBlocked(const UnsafeResourceList& unsafe_resources);
+
+ // InterstitialPageDelegate methods:
+ void OnProceed() override;
+ void OnDontProceed() override;
+ void CommandReceived(const std::string& command) override;
+
+ protected:
+ // Don't instantiate this class directly, use ShowBlockingPage instead.
+ BaseBlockingPage(
+ BaseUIManager* ui_manager,
+ content::WebContents* web_contents,
+ const GURL& main_frame_url,
+ const UnsafeResourceList& unsafe_resources,
+ std::unique_ptr<
+ security_interstitials::SecurityInterstitialControllerClient>
+ controller_client,
+ const SafeBrowsingErrorUI::SBErrorDisplayOptions& display_options);
+
+ // SecurityInterstitialPage methods:
+ bool ShouldCreateNewNavigation() const override;
+ void PopulateInterstitialStrings(
+ base::DictionaryValue* load_time_data) override;
+
+ // Called when the interstitial is going away. Intentionally do nothing in
+ // this base class.
+ virtual void FinishThreatDetails(const base::TimeDelta& delay,
+ bool did_proceed,
+ int num_visits);
+
+ // A list of SafeBrowsingUIManager::UnsafeResource for a tab that the user
+ // should be warned about. They are queued when displaying more than one
+ // interstitial at a time.
+ static UnsafeResourceMap* GetUnsafeResourcesMap();
+
+ static std::string GetMetricPrefix(
+ const UnsafeResourceList& unsafe_resources,
+ SafeBrowsingErrorUI::SBInterstitialReason interstitial_reason);
+
+ static std::string GetExtraMetricsSuffix(
+ const UnsafeResourceList& unsafe_resources);
+
+ // Return the most severe interstitial reason from a list of unsafe resources.
+ // Severity ranking: malware > UwS (harmful) > phishing.
+ static SafeBrowsingErrorUI::SBInterstitialReason GetInterstitialReason(
+ const UnsafeResourceList& unsafe_resources);
+
+ BaseUIManager* ui_manager() const;
+
+ const GURL main_frame_url() const;
+
+ UnsafeResourceList unsafe_resources() const;
+
+ SafeBrowsingErrorUI* sb_error_ui() const;
+
+ void set_proceeded(bool proceeded);
+
+ private:
+ static std::unique_ptr<
+ security_interstitials::SecurityInterstitialControllerClient>
+ CreateControllerClient(content::WebContents* web_contents,
+ const UnsafeResourceList& unsafe_resources,
+ history::HistoryService* history_service,
+ const std::string& app_locale,
+ const GURL& default_safe_page);
+
+ // For reporting back user actions.
+ BaseUIManager* ui_manager_;
+
+ // The URL of the main frame that caused the warning.
+ GURL main_frame_url_;
+
+ // The index of a navigation entry that should be removed when DontProceed()
+ // is invoked, -1 if entry should not be removed.
+ const int navigation_entry_index_to_remove_;
+
+ // The list of unsafe resources this page is warning about.
+ UnsafeResourceList unsafe_resources_;
+
+ // For displaying safe browsing interstitial.
+ std::unique_ptr<SafeBrowsingErrorUI> sb_error_ui_;
+
+ // Indicate whether user has proceeded this blocking page.
+ bool proceeded_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseBlockingPage);
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_BASE_BLOCKING_PAGE_H_
diff --git a/chromium/components/safe_browsing/base_resource_throttle.cc b/chromium/components/safe_browsing/base_resource_throttle.cc
new file mode 100644
index 00000000000..744ad4c4930
--- /dev/null
+++ b/chromium/components/safe_browsing/base_resource_throttle.cc
@@ -0,0 +1,455 @@
+// 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/base_resource_throttle.h"
+
+#include <iterator>
+#include <utility>
+
+#include "base/debug/alias.h"
+#include "base/logging.h"
+#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_db/util.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/resource_request_info.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/load_flags.h"
+#include "net/log/net_log_capture_mode.h"
+#include "net/log/net_log_source.h"
+#include "net/log/net_log_source_type.h"
+#include "net/url_request/redirect_info.h"
+#include "net/url_request/url_request.h"
+
+using net::NetLogEventType;
+using net::NetLogSourceType;
+
+namespace safe_browsing {
+
+namespace {
+
+// Maximum time in milliseconds to wait for the safe browsing service to
+// verify a URL. After this amount of time the outstanding check will be
+// aborted, and the URL will be treated as if it were safe.
+const int kCheckUrlTimeoutMs = 5000;
+
+// Return a dictionary with "url"=|url-spec| and optionally
+// |name|=|value| (if not null), for netlogging.
+// This will also add a reference to the original request's net_log ID.
+std::unique_ptr<base::Value> NetLogUrlCallback(
+ const net::URLRequest* request,
+ const GURL& url,
+ const char* name,
+ const char* value,
+ net::NetLogCaptureMode /* capture_mode */) {
+ std::unique_ptr<base::DictionaryValue> event_params(
+ new base::DictionaryValue());
+ event_params->SetString("url", url.spec());
+ if (name && value)
+ event_params->SetString(name, value);
+ request->net_log().source().AddToEventParameters(event_params.get());
+ return std::move(event_params);
+}
+
+// Return a dictionary with |name|=|value|, for netlogging.
+std::unique_ptr<base::Value> NetLogStringCallback(const char* name,
+ const char* value,
+ net::NetLogCaptureMode) {
+ std::unique_ptr<base::DictionaryValue> event_params(
+ new base::DictionaryValue());
+ if (name && value)
+ event_params->SetString(name, value);
+ return std::move(event_params);
+}
+
+} // namespace
+
+// TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more
+// unit test coverage.
+
+BaseResourceThrottle::BaseResourceThrottle(
+ const net::URLRequest* request,
+ content::ResourceType resource_type,
+ scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+ scoped_refptr<BaseUIManager> ui_manager)
+ : ui_manager_(ui_manager),
+ threat_type_(SB_THREAT_TYPE_SAFE),
+ database_manager_(database_manager),
+ request_(request),
+ state_(STATE_NONE),
+ defer_state_(DEFERRED_NONE),
+ resource_type_(resource_type),
+ net_log_with_source_(
+ 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);
+ }
+
+ if (state_ == STATE_CHECKING_URL) {
+ database_manager_->CancelCheck(this);
+ EndNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result",
+ "request_canceled");
+ }
+}
+
+// Note on net_log calls: SAFE_BROWSING_DEFERRED events must be wholly
+// nested within SAFE_BROWSING_CHECKING_URL events. Synchronous checks
+// are not logged at all.
+void BaseResourceThrottle::BeginNetLogEvent(NetLogEventType type,
+ const GURL& url,
+ const char* name,
+ const char* value) {
+ net_log_with_source_.BeginEvent(
+ type, base::Bind(&NetLogUrlCallback, request_, url, name, value));
+ request_->net_log().AddEvent(
+ type, net_log_with_source_.source().ToEventParametersCallback());
+}
+
+void BaseResourceThrottle::EndNetLogEvent(NetLogEventType type,
+ const char* name,
+ const char* value) {
+ net_log_with_source_.EndEvent(type,
+ base::Bind(&NetLogStringCallback, name, value));
+ request_->net_log().AddEvent(
+ type, net_log_with_source_.source().ToEventParametersCallback());
+}
+
+void BaseResourceThrottle::WillStartRequest(bool* defer) {
+ // We need to check the new URL before starting the request.
+ if (CheckUrl(request_->url()))
+ return;
+
+ // We let the check run in parallel with resource load only if this
+ // db_manager only supports asynchronous checks, like on mobile.
+ // Otherwise, we defer now.
+ if (database_manager_->ChecksAreAlwaysAsync())
+ return;
+
+ // If the URL couldn't be verified synchronously, defer starting the
+ // request until the check has completed.
+ defer_state_ = DEFERRED_START;
+ defer_start_time_ = base::TimeTicks::Now();
+ *defer = true;
+ BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(),
+ "defer_reason", "at_start");
+}
+
+void BaseResourceThrottle::WillProcessResponse(bool* defer) {
+ CHECK_EQ(defer_state_, DEFERRED_NONE);
+ // TODO(nparker): Maybe remove this check, since it should have no effect.
+ if (!database_manager_->ChecksAreAlwaysAsync())
+ return;
+
+ if (state_ == STATE_CHECKING_URL ||
+ state_ == STATE_DISPLAYING_BLOCKING_PAGE) {
+ defer_state_ = DEFERRED_PROCESSING;
+ defer_start_time_ = base::TimeTicks::Now();
+ *defer = true;
+ BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, request_->url(),
+ "defer_reason", "at_response");
+ }
+}
+
+bool BaseResourceThrottle::MustProcessResponseBeforeReadingBody() {
+ // On Android, SafeBrowsing may only decide to cancel the request when the
+ // response has been received. Therefore, no part of it should be cached
+ // until this ResourceThrottle has been able to check the response. This
+ // prevents the following scenario:
+ // 1) A request is made for foo.com which has been hacked.
+ // 2) The request is only canceled at WillProcessResponse stage, but part of
+ // it has been cached.
+ // 3) foo.com is no longer hacked and removed from the SafeBrowsing list.
+ // 4) The user requests foo.com, which is not on the SafeBrowsing list. This
+ // is deemed safe. However, the resource is actually served from cache,
+ // using the version that was previously stored.
+ // 5) This results in the user accessing an unsafe resource without being
+ // notified that it's dangerous.
+ // TODO(clamy): Add a browser test that checks this specific scenario.
+ return true;
+}
+
+void BaseResourceThrottle::WillRedirectRequest(
+ const net::RedirectInfo& redirect_info,
+ bool* defer) {
+ CHECK_EQ(defer_state_, DEFERRED_NONE);
+
+ // Prev check completed and was safe.
+ if (state_ == STATE_NONE) {
+ // Save the redirect urls for possible malware detail reporting later.
+ redirect_urls_.push_back(redirect_info.new_url);
+
+ // We need to check the new URL before following the redirect.
+ if (CheckUrl(redirect_info.new_url))
+ return;
+ defer_state_ = DEFERRED_REDIRECT;
+ } else {
+ CHECK(state_ == STATE_CHECKING_URL ||
+ state_ == STATE_DISPLAYING_BLOCKING_PAGE);
+ // We can't check this new URL until we have finished checking
+ // the prev one, or resumed from the blocking page.
+ unchecked_redirect_url_ = redirect_info.new_url;
+ defer_state_ = DEFERRED_UNCHECKED_REDIRECT;
+ }
+
+ defer_start_time_ = base::TimeTicks::Now();
+ *defer = true;
+ BeginNetLogEvent(
+ NetLogEventType::SAFE_BROWSING_DEFERRED, redirect_info.new_url,
+ "defer_reason",
+ defer_state_ == DEFERRED_REDIRECT ? "redirect" : "unchecked_redirect");
+}
+
+const char* BaseResourceThrottle::GetNameForLogging() const {
+ return "BaseResourceThrottle";
+}
+
+void BaseResourceThrottle::MaybeDestroyPrerenderContents(
+ const content::ResourceRequestInfo* info) {}
+
+// SafeBrowsingService::Client implementation, called on the IO thread once
+// the URL has been classified.
+void BaseResourceThrottle::OnCheckBrowseUrlResult(
+ const GURL& url,
+ SBThreatType threat_type,
+ const ThreatMetadata& metadata) {
+ CHECK_EQ(state_, STATE_CHECKING_URL);
+ // TODO(vakh): The following base::debug::Alias() and CHECK calls should be
+ // removed after http://crbug.com/660293 is fixed.
+ CHECK(url.is_valid());
+ CHECK(url_being_checked_.is_valid());
+ if (url != url_being_checked_) {
+ bool url_had_timed_out = timed_out_urls_.count(url) > 0;
+ char buf[1000];
+ snprintf(buf, sizeof(buf), "sbtr::ocbur:%d:%s -- %s\n", url_had_timed_out,
+ url.spec().c_str(), url_being_checked_.spec().c_str());
+ base::debug::Alias(buf);
+ CHECK(false) << "buf: " << buf;
+ }
+
+ timer_.Stop(); // Cancel the timeout timer.
+ threat_type_ = threat_type;
+ state_ = STATE_NONE;
+
+ if (defer_state_ != DEFERRED_NONE) {
+ EndNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED, nullptr, nullptr);
+ }
+ EndNetLogEvent(
+ NetLogEventType::SAFE_BROWSING_CHECKING_URL, "result",
+ threat_type_ == SB_THREAT_TYPE_SAFE ? "safe" : "unsafe");
+
+ if (threat_type == SB_THREAT_TYPE_SAFE) {
+ if (defer_state_ != DEFERRED_NONE) {
+ // Log how much time the safe browsing check cost us.
+ ui_manager_->LogPauseDelay(base::TimeTicks::Now() - defer_start_time_);
+ ResumeRequest();
+ } else {
+ ui_manager_->LogPauseDelay(base::TimeDelta());
+ }
+ return;
+ }
+
+ const content::ResourceRequestInfo* info =
+ content::ResourceRequestInfo::ForRequest(request_);
+
+ if (request_->load_flags() & net::LOAD_PREFETCH) {
+ // Destroy the prefetch with FINAL_STATUS_SAFEBROSWING.
+ if (resource_type_ == content::RESOURCE_TYPE_MAIN_FRAME) {
+ MaybeDestroyPrerenderContents(info);
+ }
+ // Don't prefetch resources that fail safe browsing, disallow them.
+ Cancel();
+ UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.UnsafePrefetchCanceled",
+ resource_type_, content::RESOURCE_TYPE_LAST_TYPE);
+ return;
+ }
+
+ UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Unsafe", resource_type_,
+ content::RESOURCE_TYPE_LAST_TYPE);
+
+ security_interstitials::UnsafeResource resource;
+ resource.url = url;
+ resource.original_url = request_->original_url();
+ resource.redirect_urls = redirect_urls_;
+ 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(
+ &BaseResourceThrottle::OnBlockingPageComplete, AsWeakPtr());
+ resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::IO);
+ resource.web_contents_getter = info->GetWebContentsGetterForRequest();
+ resource.threat_source = database_manager_->GetThreatSource();
+
+ state_ = STATE_DISPLAYING_BLOCKING_PAGE;
+
+ StartDisplayingBlockingPageHelper(resource);
+}
+
+void BaseResourceThrottle::StartDisplayingBlockingPageHelper(
+ security_interstitials::UnsafeResource resource) {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&BaseResourceThrottle::StartDisplayingBlockingPage,
+ AsWeakPtr(), ui_manager_, resource));
+}
+
+// Static
+void BaseResourceThrottle::StartDisplayingBlockingPage(
+ const base::WeakPtr<BaseResourceThrottle>& throttle,
+ scoped_refptr<BaseUIManager> ui_manager,
+ const security_interstitials::UnsafeResource& resource) {
+ content::WebContents* web_contents = resource.web_contents_getter.Run();
+ if (web_contents) {
+ // Once activated, the subresource filter will filter subresources, but is
+ // triggered when the main frame document matches Safe Browsing blacklists.
+ if (!resource.is_subresource) {
+ using subresource_filter::ContentSubresourceFilterDriverFactory;
+ ContentSubresourceFilterDriverFactory* driver_factory =
+ ContentSubresourceFilterDriverFactory::FromWebContents(web_contents);
+ // Content embedders (such as Android Webview) does not have a
+ // driver_factory.
+ if (driver_factory) {
+ // For a redirect chain of A -> B -> C, the subresource filter expects C
+ // as the resource URL and [A, B] as redirect URLs.
+ std::vector<GURL> redirect_parent_urls;
+ if (!resource.redirect_urls.empty()) {
+ redirect_parent_urls.push_back(resource.original_url);
+ redirect_parent_urls.insert(redirect_parent_urls.end(),
+ resource.redirect_urls.begin(),
+ std::prev(resource.redirect_urls.end()));
+ }
+ driver_factory->OnMainResourceMatchedSafeBrowsingBlacklist(
+ resource.url, redirect_parent_urls, resource.threat_type,
+ resource.threat_metadata.threat_pattern_type);
+ }
+ }
+
+ ui_manager->DisplayBlockingPage(resource);
+ return;
+ }
+
+ // Tab is gone or it's being prerendered.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&BaseResourceThrottle::Cancel, throttle));
+}
+
+void BaseResourceThrottle::OnBlockingPageComplete(bool proceed) {
+ CHECK_EQ(state_, STATE_DISPLAYING_BLOCKING_PAGE);
+ state_ = STATE_NONE;
+
+ if (proceed) {
+ threat_type_ = SB_THREAT_TYPE_SAFE;
+ if (defer_state_ != DEFERRED_NONE) {
+ ResumeRequest();
+ }
+ } else {
+ CancelResourceLoad();
+ }
+}
+
+void BaseResourceThrottle::CancelResourceLoad() {
+ Cancel();
+}
+
+scoped_refptr<BaseUIManager> BaseResourceThrottle::ui_manager() {
+ return ui_manager_;
+}
+
+bool BaseResourceThrottle::CheckUrl(const GURL& url) {
+ TRACE_EVENT1("loader", "BaseResourceThrottle::CheckUrl", "url",
+ url.spec());
+ CHECK_EQ(state_, STATE_NONE);
+ // To reduce aggregate latency on mobile, check only the most dangerous
+ // resource types.
+ if (!database_manager_->CanCheckResourceType(resource_type_)) {
+ // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType
+ // to be consistent with the other PVer4 metrics.
+ UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Skipped", resource_type_,
+ content::RESOURCE_TYPE_LAST_TYPE);
+ return true;
+ }
+
+ // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType to
+ // be consistent with the other PVer4 metrics.
+ UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Checked", resource_type_,
+ content::RESOURCE_TYPE_LAST_TYPE);
+
+ if (database_manager_->CheckBrowseUrl(url, this)) {
+ threat_type_ = SB_THREAT_TYPE_SAFE;
+ ui_manager_->LogPauseDelay(base::TimeDelta()); // No delay.
+ return true;
+ }
+
+ state_ = STATE_CHECKING_URL;
+ url_being_checked_ = url;
+ BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_CHECKING_URL, url, nullptr,
+ nullptr);
+
+ // Start a timer to abort the check if it takes too long.
+ // TODO(nparker): Set this only when we defer, based on remaining time,
+ // so we don't cancel earlier than necessary.
+ timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs),
+ this, &BaseResourceThrottle::OnCheckUrlTimeout);
+
+ return false;
+}
+
+void BaseResourceThrottle::OnCheckUrlTimeout() {
+ CHECK_EQ(state_, STATE_CHECKING_URL);
+
+ database_manager_->CancelCheck(this);
+
+ OnCheckBrowseUrlResult(url_being_checked_, safe_browsing::SB_THREAT_TYPE_SAFE,
+ ThreatMetadata());
+
+ timed_out_urls_.insert(url_being_checked_);
+}
+
+void BaseResourceThrottle::ResumeRequest() {
+ CHECK_EQ(state_, STATE_NONE);
+ CHECK_NE(defer_state_, DEFERRED_NONE);
+
+ bool resume = true;
+ if (defer_state_ == DEFERRED_UNCHECKED_REDIRECT) {
+ // Save the redirect urls for possible malware detail reporting later.
+ redirect_urls_.push_back(unchecked_redirect_url_);
+ if (!CheckUrl(unchecked_redirect_url_)) {
+ // We're now waiting for the unchecked_redirect_url_.
+ defer_state_ = DEFERRED_REDIRECT;
+ resume = false;
+ BeginNetLogEvent(NetLogEventType::SAFE_BROWSING_DEFERRED,
+ unchecked_redirect_url_, "defer_reason",
+ "resumed_redirect");
+ }
+ }
+
+ if (resume) {
+ defer_state_ = DEFERRED_NONE;
+ Resume();
+ }
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/base_resource_throttle.h b/chromium/components/safe_browsing/base_resource_throttle.h
new file mode 100644
index 00000000000..a14fb17dc76
--- /dev/null
+++ b/chromium/components/safe_browsing/base_resource_throttle.h
@@ -0,0 +1,195 @@
+// 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_BASE_RESOURCE_THROTTLE_H_
+#define COMPONENTS_SAFE_BROWSING_BASE_RESOURCE_THROTTLE_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing_db/database_manager.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "content/public/browser/resource_throttle.h"
+#include "content/public/common/resource_type.h"
+#include "net/log/net_log_event_type.h"
+#include "net/log/net_log_with_source.h"
+#include "url/gurl.h"
+
+namespace content {
+class ResourceRequestInfo;
+}
+
+namespace net {
+class URLRequest;
+}
+
+namespace safe_browsing {
+
+// BaseResourceThrottle checks that URLs are "safe" before
+// navigating to them. To be considered "safe", a URL must not appear in the
+// malware/phishing blacklists (see SafeBrowsingService for details).
+//
+// Note that the safe browsing check takes at most kCheckUrlTimeoutMs
+// milliseconds. If it takes longer than this, then the system defaults to
+// treating the URL as safe.
+//
+// If the URL is classified as dangerous, a warning page is thrown up and
+// the request remains suspended. If the user clicks "proceed" on warning
+// page, we resume the request.
+//
+// Note: The ResourceThrottle interface is called in this order:
+// WillStartRequest once, WillRedirectRequest zero or more times, and then
+// WillProcessReponse once.
+class BaseResourceThrottle
+ : public content::ResourceThrottle,
+ 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,
+ bool* defer) override;
+ void WillProcessResponse(bool* defer) override;
+ bool MustProcessResponseBeforeReadingBody() override;
+
+ const char* GetNameForLogging() const override;
+
+ // SafeBrowsingDatabaseManager::Client implementation (called on IO thread):
+ void OnCheckBrowseUrlResult(
+ const GURL& url,
+ SBThreatType threat_type,
+ const ThreatMetadata& metadata) override;
+
+ protected:
+ BaseResourceThrottle(
+ const net::URLRequest* request,
+ content::ResourceType resource_type,
+ scoped_refptr<SafeBrowsingDatabaseManager>
+ database_manager,
+ scoped_refptr<BaseUIManager> ui_manager);
+
+ ~BaseResourceThrottle() override;
+
+ // Does nothing in the base class. Override this to destroy prerender contents
+ // in chrome.
+ virtual void MaybeDestroyPrerenderContents(
+ const content::ResourceRequestInfo* info);
+
+ // Posts a task for StartDisplayingBlockingPage
+ virtual void StartDisplayingBlockingPageHelper(
+ security_interstitials::UnsafeResource resource);
+
+ // Called by OnBlockingPageComplete when proceed == false. This removes the
+ // blocking page. This calls ResourceThrottle::Cancel() to show the previous
+ // page, but may be overridden in a subclass.
+ virtual void CancelResourceLoad();
+
+ scoped_refptr<BaseUIManager> ui_manager();
+
+ private:
+ // Describes what phase of the check a throttle is in.
+ enum State {
+ // Haven't started checking or checking is complete. Not deferred.
+ STATE_NONE,
+ // We have one outstanding URL-check. Could be deferred.
+ STATE_CHECKING_URL,
+ // We're displaying a blocking page. Could be deferred.
+ STATE_DISPLAYING_BLOCKING_PAGE,
+ };
+
+ // Describes what stage of the request got paused by the check.
+ enum DeferState {
+ DEFERRED_NONE,
+ DEFERRED_START,
+ DEFERRED_REDIRECT,
+ DEFERRED_UNCHECKED_REDIRECT, // unchecked_redirect_url_ is populated.
+ DEFERRED_PROCESSING,
+ };
+
+ scoped_refptr<BaseUIManager> ui_manager_;
+
+ // 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);
+
+ // 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.
+ void BeginNetLogEvent(net::NetLogEventType type,
+ const GURL& url,
+ const char* name,
+ const char* value);
+ void EndNetLogEvent(net::NetLogEventType type,
+ const char* name,
+ const char* value);
+
+ // The result of the most recent safe browsing check. Only valid to read this
+ // when state_ != STATE_CHECKING_URL.
+ safe_browsing::SBThreatType threat_type_;
+
+ // The time when we started deferring the request.
+ base::TimeTicks defer_start_time_;
+
+ // Timer to abort the safe browsing check if it takes too long.
+ base::OneShotTimer timer_;
+
+ // The redirect chain for this resource
+ std::vector<GURL> redirect_urls_;
+
+ // If in DEFERRED_UNCHECKED_REDIRECT state, this is the
+ // URL we still need to check before resuming.
+ GURL unchecked_redirect_url_;
+ GURL url_being_checked_;
+
+ scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
+ const net::URLRequest* request_;
+
+ State state_;
+ DeferState defer_state_;
+
+ const content::ResourceType resource_type_;
+ net::NetLogWithSource net_log_with_source_;
+
+ // TODO(vakh): The following set should be removed after fixing
+ // http://crbug.com/660293
+ // URLs that timed out waiting for a SafeBrowsing reputation check.
+ std::set<GURL> timed_out_urls_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseResourceThrottle);
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_BASE_RESOURCE_THROTTLE_H_
diff --git a/chromium/components/safe_browsing/base_ui_manager.cc b/chromium/components/safe_browsing/base_ui_manager.cc
new file mode 100644
index 00000000000..bc50cb22347
--- /dev/null
+++ b/chromium/components/safe_browsing/base_ui_manager.cc
@@ -0,0 +1,351 @@
+// 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/safe_browsing/base_ui_manager.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/i18n/rtl.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/supports_user_data.h"
+#include "components/safe_browsing/base_blocking_page.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/web_contents.h"
+
+using content::BrowserThread;
+using content::NavigationEntry;
+using content::WebContents;
+using safe_browsing::HitReport;
+using safe_browsing::SBThreatType;
+
+namespace {
+
+const void* const kWhitelistKey = &kWhitelistKey;
+
+// A WhitelistUrlSet holds the set of URLs that have been whitelisted
+// for a specific WebContents, along with pending entries that are still
+// undecided. Each URL is associated with the first SBThreatType that
+// was seen for that URL. The URLs in this set should come from
+// GetWhitelistUrl() or GetMainFrameWhitelistUrlForResource() (in
+// SafeBrowsingUIManager)
+class WhitelistUrlSet : public base::SupportsUserData::Data {
+ public:
+ WhitelistUrlSet() {}
+ bool Contains(const GURL url, SBThreatType* threat_type) {
+ auto found = map_.find(url);
+ if (found == map_.end())
+ return false;
+ if (threat_type)
+ *threat_type = found->second;
+ return true;
+ }
+ void RemovePending(const GURL& url) { pending_.erase(url); }
+ void Insert(const GURL url, SBThreatType threat_type) {
+ if (Contains(url, nullptr))
+ return;
+ map_[url] = threat_type;
+ RemovePending(url);
+ }
+ bool ContainsPending(const GURL& url, SBThreatType* threat_type) {
+ auto found = pending_.find(url);
+ if (found == pending_.end())
+ return false;
+ if (threat_type)
+ *threat_type = found->second;
+ return true;
+ }
+ void InsertPending(const GURL url, SBThreatType threat_type) {
+ if (ContainsPending(url, nullptr))
+ return;
+ pending_[url] = threat_type;
+ }
+
+ private:
+ std::map<GURL, SBThreatType> map_;
+ std::map<GURL, SBThreatType> pending_;
+ DISALLOW_COPY_AND_ASSIGN(WhitelistUrlSet);
+};
+
+// Returns the URL that should be used in a WhitelistUrlSet for the
+// resource loaded from |url| on a navigation |entry|.
+GURL GetWhitelistUrl(const GURL& url,
+ bool is_subresource,
+ NavigationEntry* entry) {
+ if (is_subresource) {
+ if (!entry)
+ return GURL();
+ return entry->GetURL().GetWithEmptyPath();
+ }
+ return url.GetWithEmptyPath();
+}
+
+WhitelistUrlSet* GetOrCreateWhitelist(WebContents* web_contents) {
+ WhitelistUrlSet* site_list =
+ static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey));
+ if (!site_list) {
+ site_list = new WhitelistUrlSet;
+ web_contents->SetUserData(kWhitelistKey, site_list);
+ }
+ return site_list;
+}
+
+} // namespace
+
+namespace safe_browsing {
+
+BaseUIManager::BaseUIManager() {}
+
+void BaseUIManager::StopOnIOThread(bool shutdown) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ return;
+}
+
+BaseUIManager::~BaseUIManager() {}
+
+bool BaseUIManager::IsWhitelisted(const UnsafeResource& resource) {
+ NavigationEntry* entry = nullptr;
+ if (resource.is_subresource) {
+ entry = resource.GetNavigationEntryForResource();
+ }
+ SBThreatType unused_threat_type;
+ return IsUrlWhitelistedOrPendingForWebContents(
+ resource.url, resource.is_subresource, entry,
+ resource.web_contents_getter.Run(), true, &unused_threat_type);
+}
+
+// Check if the user has already seen and/or ignored a SB warning for this
+// WebContents and top-level domain.
+bool BaseUIManager::IsUrlWhitelistedOrPendingForWebContents(
+ const GURL& url,
+ bool is_subresource,
+ NavigationEntry* entry,
+ WebContents* web_contents,
+ bool whitelist_only,
+ SBThreatType* threat_type) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ GURL lookup_url = GetWhitelistUrl(url, is_subresource, entry);
+ if (lookup_url.is_empty())
+ return false;
+
+ WhitelistUrlSet* site_list =
+ static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey));
+ if (!site_list)
+ return false;
+
+ bool whitelisted = site_list->Contains(lookup_url, threat_type);
+ if (whitelist_only) {
+ return whitelisted;
+ } else {
+ return whitelisted || site_list->ContainsPending(lookup_url, threat_type);
+ }
+}
+
+void BaseUIManager::OnBlockingPageDone(
+ const std::vector<UnsafeResource>& resources,
+ bool proceed,
+ WebContents* web_contents,
+ const GURL& main_frame_url) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ for (const auto& resource : resources) {
+ if (!resource.callback.is_null()) {
+ DCHECK(resource.callback_thread);
+ resource.callback_thread->PostTask(
+ FROM_HERE, base::Bind(resource.callback, proceed));
+ }
+
+ GURL whitelist_url = GetWhitelistUrl(
+ main_frame_url, false /* is subresource */,
+ nullptr /* no navigation entry needed for main resource */);
+ if (proceed) {
+ AddToWhitelistUrlSet(whitelist_url, web_contents,
+ false /* Pending -> permanent */,
+ resource.threat_type);
+ } else if (web_contents) {
+ // |web_contents| doesn't exist if the tab has been closed.
+ RemoveFromPendingWhitelistUrlSet(whitelist_url, web_contents);
+ }
+ }
+}
+
+void BaseUIManager::DisplayBlockingPage(
+ const UnsafeResource& resource) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (resource.is_subresource && !resource.is_subframe) {
+ // Sites tagged as serving Unwanted Software should only show a warning for
+ // main-frame or sub-frame resource. Similar warning restrictions should be
+ // applied to malware sites tagged as "landing sites" (see "Types of
+ // Malware sites" under
+ // https://developers.google.com/safe-browsing/developers_guide_v3#UserWarnings).
+ if (resource.threat_type == SB_THREAT_TYPE_URL_UNWANTED ||
+ (resource.threat_type == SB_THREAT_TYPE_URL_MALWARE &&
+ resource.threat_metadata.threat_pattern_type ==
+ ThreatPatternType::MALWARE_LANDING)) {
+ if (!resource.callback.is_null()) {
+ DCHECK(resource.callback_thread);
+ resource.callback_thread->PostTask(FROM_HERE,
+ base::Bind(resource.callback, true));
+ }
+
+ return;
+ }
+ }
+
+ // The tab might have been closed. If it was closed, just act as if "Don't
+ // Proceed" had been chosen.
+ WebContents* web_contents = resource.web_contents_getter.Run();
+ if (!web_contents) {
+ OnBlockingPageDone(std::vector<UnsafeResource>{resource},
+ false /* proceed */,
+ web_contents,
+ GetMainFrameWhitelistUrlForResource(resource));
+ return;
+ }
+
+ // Check if the user has already ignored a SB warning for the same WebContents
+ // and top-level domain.
+ if (IsWhitelisted(resource)) {
+ if (!resource.callback.is_null()) {
+ DCHECK(resource.callback_thread);
+ resource.callback_thread->PostTask(FROM_HERE,
+ base::Bind(resource.callback, true));
+ }
+ return;
+ }
+
+ // BaseUIManager does not send SafeBrowsingHitReport. Subclasses should
+ // implement the reporting logic themselves if needed.
+ AddToWhitelistUrlSet(GetMainFrameWhitelistUrlForResource(resource),
+ resource.web_contents_getter.Run(),
+ true /* A decision is now pending */,
+ resource.threat_type);
+ BaseBlockingPage::ShowBlockingPage(this, resource);
+}
+
+void BaseUIManager::EnsureWhitelistCreated(
+ WebContents* web_contents) {
+ GetOrCreateWhitelist(web_contents);
+}
+
+void BaseUIManager::LogPauseDelay(base::TimeDelta time) {
+ UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
+ return;
+}
+
+// A safebrowsing hit is sent after a blocking page for malware/phishing
+// or after the warning dialog for download urls, only for
+// UMA || extended_reporting users.
+void BaseUIManager::MaybeReportSafeBrowsingHit(
+ const HitReport& hit_report) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ return;
+}
+
+void BaseUIManager::ReportSafeBrowsingHitOnIOThread(
+ const HitReport& hit_report) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ return;
+}
+
+// If the user had opted-in to send ThreatDetails, this gets called
+// when the report is ready.
+void BaseUIManager::SendSerializedThreatDetails(
+ const std::string& serialized) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ return;
+}
+
+// Record this domain in the given WebContents as either whitelisted or
+// pending whitelisting (if an interstitial is currently displayed). If an
+// existing WhitelistUrlSet does not yet exist, create a new WhitelistUrlSet.
+void BaseUIManager::AddToWhitelistUrlSet(const GURL& whitelist_url,
+ WebContents* web_contents,
+ bool pending,
+ SBThreatType threat_type) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // A WebContents might not exist if the tab has been closed.
+ if (!web_contents)
+ return;
+
+ WhitelistUrlSet* site_list = GetOrCreateWhitelist(web_contents);
+
+ if (whitelist_url.is_empty())
+ return;
+
+ if (pending) {
+ site_list->InsertPending(whitelist_url, threat_type);
+ } else {
+ site_list->Insert(whitelist_url, threat_type);
+ }
+
+ // Notify security UI that security state has changed.
+ web_contents->DidChangeVisibleSecurityState();
+}
+
+const std::string BaseUIManager::app_locale() const {
+ return base::i18n::GetConfiguredLocale();
+}
+
+history::HistoryService* BaseUIManager::history_service(
+ content::WebContents* web_contents) {
+ // TODO(jialiul): figure out how to get HistoryService from webview.
+ return nullptr;
+}
+
+const GURL BaseUIManager::default_safe_page() const {
+ return GURL(url::kAboutBlankURL);
+}
+
+void BaseUIManager::RemoveFromPendingWhitelistUrlSet(
+ const GURL& whitelist_url,
+ WebContents* web_contents) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // A WebContents might not exist if the tab has been closed.
+ if (!web_contents)
+ return;
+
+ // Use |web_contents| rather than |resource.web_contents_getter|
+ // here. By this point, a "Back" navigation could have already been
+ // committed, so the page loading |resource| might be gone and
+ // |web_contents_getter| may no longer be valid.
+ WhitelistUrlSet* site_list =
+ static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey));
+
+ if (whitelist_url.is_empty())
+ return;
+
+ // Note that this function does not DCHECK that |whitelist_url|
+ // appears in the pending whitelist. In the common case, it's expected
+ // that a URL is in the pending whitelist when it is removed, but it's
+ // not always the case. For example, if there are several blocking
+ // pages queued up for different resources on the same page, and the
+ // user goes back to dimiss the first one, the subsequent blocking
+ // pages get dismissed as well (as if the user had clicked "Back to
+ // safety" on each of them). In this case, the first dismissal will
+ // remove the main-frame URL from the pending whitelist, so the
+ // main-frame URL will have already been removed when the subsequent
+ // blocking pages are dismissed.
+ if (site_list->ContainsPending(whitelist_url, nullptr))
+ site_list->RemovePending(whitelist_url);
+
+ // Notify security UI that security state has changed.
+ web_contents->DidChangeVisibleSecurityState();
+}
+
+// static
+GURL BaseUIManager::GetMainFrameWhitelistUrlForResource(
+ const security_interstitials::UnsafeResource& resource) {
+ if (resource.is_subresource) {
+ NavigationEntry* entry = resource.GetNavigationEntryForResource();
+ if (!entry)
+ return GURL();
+ return entry->GetURL().GetWithEmptyPath();
+ }
+ return resource.url.GetWithEmptyPath();
+}
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/base_ui_manager.h b/chromium/components/safe_browsing/base_ui_manager.h
new file mode 100644
index 00000000000..638e0cd4cd1
--- /dev/null
+++ b/chromium/components/safe_browsing/base_ui_manager.h
@@ -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.
+
+#ifndef COMPONENTS_SAFE_BROWSING_BASE_UI_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_BASE_UI_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+
+class GURL;
+
+namespace content {
+class NavigationEntry;
+class WebContents;
+} // namespace content
+
+namespace history {
+class HistoryService;
+} // namespace history
+
+namespace safe_browsing {
+
+// Construction needs to happen on the main thread.
+class BaseUIManager
+ : public base::RefCountedThreadSafe<BaseUIManager> {
+ public:
+ typedef security_interstitials::UnsafeResource UnsafeResource;
+
+ BaseUIManager();
+
+ // Called to stop or shutdown operations on the io_thread. This may be called
+ // multiple times during the life of the UIManager. Should be called
+ // on IO thread. If shutdown is true, the manager is disabled permanently.
+ // This currently is a no-op in the base class.
+ virtual void StopOnIOThread(bool shutdown);
+
+ // Called on the UI thread to display an interstitial page.
+ // |url| is the url of the resource that matches a safe browsing list.
+ // If the request contained a chain of redirects, |url| is the last url
+ // in the chain, and |original_url| is the first one (the root of the
+ // chain). Otherwise, |original_url| = |url|.
+ virtual void DisplayBlockingPage(const UnsafeResource& resource);
+
+ // Log the user perceived delay caused by SafeBrowsing. This delay is the time
+ // delta starting from when we would have started reading data from the
+ // network, and ending when the SafeBrowsing check completes indicating that
+ // the current page is 'safe'.
+ virtual void LogPauseDelay(base::TimeDelta time);
+
+ // This is a no-op in the base class, but should be overridden to send threat
+ // details. Called on the IO thread by the ThreatDetails with the serialized
+ // protocol buffer.
+ virtual void SendSerializedThreatDetails(const std::string& serialized);
+
+ // This is a no-op in the base class, but should be overridden to report hits
+ // to the unsafe contents (malware, phishing, unsafe download URL)
+ // to the server. Can only be called on UI thread.
+ virtual void MaybeReportSafeBrowsingHit(
+ const safe_browsing::HitReport& hit_report);
+
+ // A convenience wrapper method for IsUrlWhitelistedOrPendingForWebContents.
+ virtual bool IsWhitelisted(const UnsafeResource& resource);
+
+ // Checks if we already displayed or are displaying an interstitial
+ // for the top-level site |url| in a given WebContents. If
+ // |whitelist_only|, it returns true only if the user chose to ignore
+ // the interstitial. Otherwise, it returns true if an interstitial for
+ // |url| is already displaying *or* if the user has seen an
+ // interstitial for |url| before in this WebContents and proceeded
+ // through it. Called on the UI thread.
+ //
+ // If the resource was found in the whitelist or pending for the
+ // whitelist, |threat_type| will be set to the SBThreatType for which
+ // the URL was first whitelisted.
+ virtual bool IsUrlWhitelistedOrPendingForWebContents(
+ const GURL& url,
+ bool is_subresource,
+ content::NavigationEntry* entry,
+ content::WebContents* web_contents,
+ bool whitelist_only,
+ SBThreatType* threat_type);
+
+ // The blocking page for |web_contents| on the UI thread has
+ // completed, with |proceed| set to true if the user has chosen to
+ // proceed through the blocking page and false
+ // otherwise. |web_contents| is the WebContents that was displaying
+ // the blocking page. |main_frame_url| is the top-level URL on which
+ // the blocking page was displayed. If |proceed| is true,
+ // |main_frame_url| is whitelisted so that the user will not see
+ // another warning for that URL in this WebContents.
+ virtual void OnBlockingPageDone(const std::vector<UnsafeResource>& resources,
+ bool proceed,
+ content::WebContents* web_contents,
+ const GURL& main_frame_url);
+
+ virtual const std::string app_locale() const;
+
+ virtual history::HistoryService* history_service(
+ content::WebContents* web_contents);
+
+ // The default safe page when there is no entry in the history to go back to.
+ // e.g. about::blank page, or chrome's new tab page.
+ virtual const GURL default_safe_page() const;
+
+ protected:
+ virtual ~BaseUIManager();
+
+ // Updates the whitelist URL set for |web_contents|. Called on the UI thread.
+ void AddToWhitelistUrlSet(const GURL& whitelist_url,
+ content::WebContents* web_contents,
+ bool is_pending,
+ SBThreatType threat_type);
+
+ // This is a no-op that should be overridden to call protocol manager on IO
+ // thread to report hits of unsafe contents.
+ virtual void ReportSafeBrowsingHitOnIOThread(
+ const safe_browsing::HitReport& hit_report);
+
+ // Removes |whitelist_url| from the pending whitelist for
+ // |web_contents|. Called on the UI thread.
+ void RemoveFromPendingWhitelistUrlSet(const GURL& whitelist_url,
+ content::WebContents* web_contents);
+
+ // Ensures that |web_contents| has its whitelist set in its userdata
+ static void EnsureWhitelistCreated(content::WebContents* web_contents);
+
+ // Returns the URL that should be used in a WhitelistUrlSet for the given
+ // |resource|.
+ static GURL GetMainFrameWhitelistUrlForResource(
+ const security_interstitials::UnsafeResource& resource);
+
+ private:
+ friend class base::RefCountedThreadSafe<BaseUIManager>;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseUIManager);
+};
+
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_BASE_UI_MANAGER_H_
diff --git a/chromium/components/safe_browsing/common/BUILD.gn b/chromium/components/safe_browsing/common/BUILD.gn
new file mode 100644
index 00000000000..47587637f13
--- /dev/null
+++ b/chromium/components/safe_browsing/common/BUILD.gn
@@ -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.
+
+source_set("common") {
+ sources = [
+ "safebrowsing_constants.cc",
+ "safebrowsing_constants.h",
+ "safebrowsing_message_generator.cc",
+ "safebrowsing_message_generator.h",
+ "safebrowsing_messages.h",
+ "safebrowsing_switches.cc",
+ "safebrowsing_switches.h",
+ ]
+
+ deps = [
+ "//base",
+ "//ipc",
+ "//url/ipc:url_ipc",
+ ]
+}
diff --git a/chromium/components/safe_browsing/common/DEPS b/chromium/components/safe_browsing/common/DEPS
new file mode 100644
index 00000000000..acf92ab14c9
--- /dev/null
+++ b/chromium/components/safe_browsing/common/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+ipc",
+ "+url"
+]
diff --git a/chromium/components/safe_browsing/common/OWNERS b/chromium/components/safe_browsing/common/OWNERS
new file mode 100644
index 00000000000..6734395b373
--- /dev/null
+++ b/chromium/components/safe_browsing/common/OWNERS
@@ -0,0 +1,4 @@
+jialiul@chromium.org
+
+per-file *_messages*.h=set noparent
+per-file *_messages*.h=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/safe_browsing/common/safebrowsing_constants.cc b/chromium/components/safe_browsing/common/safebrowsing_constants.cc
new file mode 100644
index 00000000000..543f725e964
--- /dev/null
+++ b/chromium/components/safe_browsing/common/safebrowsing_constants.cc
@@ -0,0 +1,12 @@
+// 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/safe_browsing/common/safebrowsing_constants.h"
+
+namespace safe_browsing {
+
+const base::FilePath::CharType kSafeBrowsingBaseFilename[] =
+ FILE_PATH_LITERAL("Safe Browsing");
+
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/common/safebrowsing_constants.h b/chromium/components/safe_browsing/common/safebrowsing_constants.h
new file mode 100644
index 00000000000..17173ccf7c0
--- /dev/null
+++ b/chromium/components/safe_browsing/common/safebrowsing_constants.h
@@ -0,0 +1,16 @@
+// 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_SAFE_BROWSING_COMMON_SAFEBROWSING_CONSTANTS_H_
+#define COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_CONSTANTS_H_
+
+#include "base/files/file_path.h"
+
+namespace safe_browsing {
+
+extern const base::FilePath::CharType kSafeBrowsingBaseFilename[];
+
+}
+
+#endif // COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_CONSTANTS_H_
diff --git a/chromium/components/safe_browsing/common/safebrowsing_message_generator.cc b/chromium/components/safe_browsing/common/safebrowsing_message_generator.cc
new file mode 100644
index 00000000000..8629de28c52
--- /dev/null
+++ b/chromium/components/safe_browsing/common/safebrowsing_message_generator.cc
@@ -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.
+
+// Get basic type definitions.
+#define IPC_MESSAGE_IMPL
+#include "components/safe_browsing/common/safebrowsing_message_generator.h"
+
+// Generate constructors.
+#include "ipc/struct_constructor_macros.h"
+#include "components/safe_browsing/common/safebrowsing_message_generator.h"
+
+// Generate destructors.
+#include "ipc/struct_destructor_macros.h"
+#include "components/safe_browsing/common/safebrowsing_message_generator.h"
+
+// Generate param traits size methods.
+#include "ipc/param_traits_size_macros.h"
+namespace IPC {
+#include "components/safe_browsing/common/safebrowsing_message_generator.h"
+} // namespace IPC
+
+// Generate param traits write methods.
+#include "ipc/param_traits_write_macros.h"
+namespace IPC {
+#include "components/safe_browsing/common/safebrowsing_message_generator.h"
+} // namespace IPC
+
+// Generate param traits read methods.
+#include "ipc/param_traits_read_macros.h"
+namespace IPC {
+#include "components/safe_browsing/common/safebrowsing_message_generator.h"
+} // namespace IPC
+
+// Generate param traits log methods.
+#include "ipc/param_traits_log_macros.h"
+namespace IPC {
+#include "components/safe_browsing/common/safebrowsing_message_generator.h"
+} // namespace IPC
diff --git a/chromium/components/safe_browsing/common/safebrowsing_message_generator.h b/chromium/components/safe_browsing/common/safebrowsing_message_generator.h
new file mode 100644
index 00000000000..8141e6d9cff
--- /dev/null
+++ b/chromium/components/safe_browsing/common/safebrowsing_message_generator.h
@@ -0,0 +1,7 @@
+// 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.
+
+// Multiply-included file, hence no include guard.
+
+#include "components/safe_browsing/common/safebrowsing_messages.h"
diff --git a/chromium/components/safe_browsing/common/safebrowsing_messages.h b/chromium/components/safe_browsing/common/safebrowsing_messages.h
new file mode 100644
index 00000000000..1432398d81d
--- /dev/null
+++ b/chromium/components/safe_browsing/common/safebrowsing_messages.h
@@ -0,0 +1,66 @@
+// 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.
+
+// Multiply-included message file, so no include guard.
+
+#include <string>
+#include <vector>
+
+#include "ipc/ipc_message_macros.h"
+#include "url/gurl.h"
+#include "url/ipc/url_param_traits.h"
+
+#define IPC_MESSAGE_START SafeBrowsingMsgStart
+
+// A node is essentially a frame.
+IPC_STRUCT_BEGIN(SafeBrowsingHostMsg_ThreatDOMDetails_Node)
+ // URL of this resource. Can be empty.
+ IPC_STRUCT_MEMBER(GURL, url)
+
+ // If this resource was in the "src" attribute of a tag, this is the tagname
+ // (eg "IFRAME"). Can be empty.
+ IPC_STRUCT_MEMBER(std::string, tag_name)
+
+ // URL of the parent node. Can be empty.
+ IPC_STRUCT_MEMBER(GURL, parent)
+
+ // children of this node. Can be emtpy.
+ IPC_STRUCT_MEMBER(std::vector<GURL>, children)
+IPC_STRUCT_END()
+
+// SafeBrowsing client-side detection messages sent from the renderer to the
+// browser.
+
+// Send part of the DOM to the browser, to be used in a threat report.
+IPC_MESSAGE_ROUTED1(SafeBrowsingHostMsg_ThreatDOMDetails,
+ std::vector<SafeBrowsingHostMsg_ThreatDOMDetails_Node>)
+
+#if defined(FULL_SAFE_BROWSING)
+// Inform the browser that the client-side phishing detector running in the
+// renderer is done classifying the current URL. If the URL is phishing
+// the request proto will have |is_phishing()| set to true.
+// TODO(noelutz): we may want to create custom ParamTraits for MessageLite to
+// have a generic way to send protocol messages over IPC.
+IPC_MESSAGE_ROUTED1(SafeBrowsingHostMsg_PhishingDetectionDone,
+ std::string /* encoded ClientPhishingRequest proto */)
+#endif
+
+// SafeBrowsing client-side detection messages sent from the browser to the
+// renderer.
+
+// Request a DOM tree when a safe browsing interstitial is shown.
+IPC_MESSAGE_ROUTED0(SafeBrowsingMsg_GetThreatDOMDetails)
+
+#if defined(FULL_SAFE_BROWSING)
+// A classification model for client-side phishing detection.
+// The string is an encoded safe_browsing::ClientSideModel protocol buffer, or
+// empty to disable client-side phishing detection for this renderer.
+IPC_MESSAGE_CONTROL1(SafeBrowsingMsg_SetPhishingModel,
+ std::string /* encoded ClientSideModel proto */)
+
+// Tells the renderer to begin phishing detection for the given toplevel URL
+// which it has started loading.
+IPC_MESSAGE_ROUTED1(SafeBrowsingMsg_StartPhishingDetection,
+ GURL)
+#endif
diff --git a/chromium/components/safe_browsing/common/safebrowsing_switches.cc b/chromium/components/safe_browsing/common/safebrowsing_switches.cc
new file mode 100644
index 00000000000..04fb912f832
--- /dev/null
+++ b/chromium/components/safe_browsing/common/safebrowsing_switches.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/safe_browsing/common/safebrowsing_switches.h"
+
+namespace safe_browsing {
+namespace switches {
+
+// If present, safebrowsing only performs update when
+// SafeBrowsingProtocolManager::ForceScheduleNextUpdate() is explicitly called.
+// This is used for testing only.
+const char kSbDisableAutoUpdate[] = "safebrowsing-disable-auto-update";
+
+// TODO(lzheng): Remove this flag once the feature works fine
+// (http://crbug.com/74848).
+//
+// Disables safebrowsing feature that checks download url and downloads
+// content's hash to make sure the content are not malicious.
+const char kSbDisableDownloadProtection[] =
+ "safebrowsing-disable-download-protection";
+
+// Disables safebrowsing feature that checks for blacklisted extensions.
+const char kSbDisableExtensionBlacklist[] =
+ "safebrowsing-disable-extension-blacklist";
+
+// List of comma-separated sha256 hashes of executable files which the
+// download-protection service should treat as "dangerous." For a file to
+// show a warning, it also must be considered a dangerous filetype and not
+// be whitelisted otherwise (by signature or URL) and must be on a supported
+// OS. Hashes are in hex. This is used for manual testing when looking
+// for ways to by-pass download protection.
+const char kSbManualDownloadBlacklist[] =
+ "safebrowsing-manual-download-blacklist";
+
+} // namespace switches
+} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/common/safebrowsing_switches.h b/chromium/components/safe_browsing/common/safebrowsing_switches.h
new file mode 100644
index 00000000000..126795bbcf7
--- /dev/null
+++ b/chromium/components/safe_browsing/common/safebrowsing_switches.h
@@ -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.
+
+#ifndef COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_SWITCHES_H_
+#define COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_SWITCHES_H_
+
+namespace safe_browsing {
+namespace switches {
+
+extern const char kSbDisableAutoUpdate[];
+extern const char kSbDisableDownloadProtection[];
+extern const char kSbDisableExtensionBlacklist[];
+extern const char kSbManualDownloadBlacklist[];
+
+} // namespace switches
+} // namespace safe_browsing
+
+#endif // COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_SWITCHES_H_
diff --git a/chromium/components/safe_browsing_db/BUILD.gn b/chromium/components/safe_browsing_db/BUILD.gn
index e7e788ceec9..0c3632b1c39 100644
--- a/chromium/components/safe_browsing_db/BUILD.gn
+++ b/chromium/components/safe_browsing_db/BUILD.gn
@@ -78,9 +78,11 @@ static_library("hit_report") {
"hit_report.cc",
"hit_report.h",
]
+ public_deps = [
+ ":util",
+ ]
deps = [
":safe_browsing_prefs",
- ":util",
"//components/metrics",
"//url",
]
@@ -116,14 +118,26 @@ static_library("remote_database_manager") {
source_set("safe_browsing_api_handler") {
sources = [
+ "android/jni_registrar.cc",
+ "android/jni_registrar.h",
"safe_browsing_api_handler.cc",
"safe_browsing_api_handler.h",
]
deps = [
+ ":safe_browsing_api_handler_util",
":util",
"//base",
+ "//content/public/browser:browser",
"//url",
]
+
+ if (is_android) {
+ deps += [ "//components/safe_browsing_db/android:jni_headers" ]
+ sources += [
+ "android/safe_browsing_api_handler_bridge.cc",
+ "android/safe_browsing_api_handler_bridge.h",
+ ]
+ }
}
static_library("safe_browsing_api_handler_util") {
@@ -307,6 +321,7 @@ static_library("v4_update_protocol_manager") {
"v4_update_protocol_manager.h",
]
deps = [
+ ":safe_browsing_prefs",
":safebrowsing_proto",
":util",
":v4_protocol_manager_util",
diff --git a/chromium/components/safe_browsing_db/DEPS b/chromium/components/safe_browsing_db/DEPS
index 91ea0ff090f..e6985f92ef2 100644
--- a/chromium/components/safe_browsing_db/DEPS
+++ b/chromium/components/safe_browsing_db/DEPS
@@ -6,6 +6,7 @@ include_rules = [
"+content/public/common",
"+content/public/test",
"+crypto",
+ "+jni",
"+third_party/protobuf/src/google",
"+net",
]
diff --git a/chromium/components/safe_browsing_db/android/BUILD.gn b/chromium/components/safe_browsing_db/android/BUILD.gn
new file mode 100644
index 00000000000..32b2ff2f402
--- /dev/null
+++ b/chromium/components/safe_browsing_db/android/BUILD.gn
@@ -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.
+
+import("//build/config/android/config.gni")
+import("//build/config/android/rules.gni")
+android_library("safe_browsing_java") {
+ deps = [
+ "//base:base_java",
+ "//third_party/android_tools:android_support_annotations_java",
+ ]
+ java_files = [
+ "java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java",
+ "java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java",
+ ]
+}
+
+generate_jni("jni_headers") {
+ sources = [
+ "java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java",
+ ]
+ jni_package = "components/safe_browsing_db/android"
+}
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
new file mode 100644
index 00000000000..c51337ccc97
--- /dev/null
+++ b/chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.cc
@@ -0,0 +1,196 @@
+// 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/safe_browsing_db/android/safe_browsing_api_handler_bridge.h"
+
+#include <memory>
+#include <string>
+
+#include "base/android/context_utils.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/metrics/histogram_macros.h"
+#include "components/safe_browsing_db/safe_browsing_api_handler_util.h"
+#include "content/public/browser/browser_thread.h"
+#include "jni/SafeBrowsingApiBridge_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::GetApplicationContext;
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+using base::android::ToJavaIntArray;
+using content::BrowserThread;
+
+namespace safe_browsing {
+
+namespace {
+// Takes ownership of callback ptr.
+void RunCallbackOnIOThread(
+ SafeBrowsingApiHandler::URLCheckCallbackMeta* callback,
+ SBThreatType threat_type,
+ const ThreatMetadata& metadata) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&SafeBrowsingApiHandler::URLCheckCallbackMeta::Run,
+ base::Owned(callback), threat_type, metadata));
+}
+
+void ReportUmaResult(safe_browsing::UmaRemoteCallResult result) {
+ UMA_HISTOGRAM_ENUMERATION("SB2.RemoteCall.Result", result,
+ safe_browsing::UMA_STATUS_MAX_VALUE);
+}
+
+// Convert a SBThreatType to a Java threat type. We only support a few.
+int SBThreatTypeToJavaThreatType(const SBThreatType& sb_threat_type) {
+ switch (sb_threat_type) {
+ case SB_THREAT_TYPE_URL_PHISHING:
+ return safe_browsing::JAVA_THREAT_TYPE_SOCIAL_ENGINEERING;
+ case SB_THREAT_TYPE_URL_MALWARE:
+ return safe_browsing::JAVA_THREAT_TYPE_POTENTIALLY_HARMFUL_APPLICATION;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
+// Convert a vector of SBThreatTypes to JavaIntArray of Java threat types.
+ScopedJavaLocalRef<jintArray> SBThreatTypesToJavaArray(
+ JNIEnv* env,
+ const std::vector<SBThreatType>& threat_types) {
+ DCHECK(threat_types.size() > 0);
+ int int_threat_types[threat_types.size()];
+ int* itr = &int_threat_types[0];
+ for (auto threat_type : threat_types) {
+ *itr++ = SBThreatTypeToJavaThreatType(threat_type);
+ }
+ return ToJavaIntArray(env, int_threat_types, threat_types.size());
+}
+
+} // namespace
+
+bool RegisterSafeBrowsingApiBridge(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+// Java->Native call, invoked when a check is done.
+// |callback_id| is an int form of pointer to a URLCheckCallbackMeta
+// that will be called and then deleted here.
+// |result_status| is one of those from SafeBrowsingApiHandler.java
+// |metadata| is a JSON string classifying the threat if there is one.
+void OnUrlCheckDone(JNIEnv* env,
+ const JavaParamRef<jclass>& context,
+ jlong callback_id,
+ jint result_status,
+ const JavaParamRef<jstring>& metadata) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(callback_id);
+
+ const std::string metadata_str =
+ (metadata ? ConvertJavaStringToUTF8(env, metadata) : "");
+
+ DVLOG(1) << "OnURLCheckDone invoked for check " << callback_id
+ << " with status=" << result_status << " and metadata=["
+ << metadata_str << "]";
+
+ // Convert java long long int to c++ pointer, take ownership.
+ std::unique_ptr<SafeBrowsingApiHandler::URLCheckCallbackMeta> callback(
+ reinterpret_cast<SafeBrowsingApiHandlerBridge::URLCheckCallbackMeta*>(
+ callback_id));
+
+ if (result_status != RESULT_STATUS_SUCCESS) {
+ // TODO(nparker): If the API is consistently failing, we might want to
+ // turn it off altogether and retest periodically. This would
+ // alleviate a bad experience if GMSCore is somehow busted.
+ if (result_status == RESULT_STATUS_TIMEOUT) {
+ ReportUmaResult(UMA_STATUS_TIMEOUT);
+ VLOG(1) << "Safe browsing API call timed-out";
+ } else {
+ DCHECK_EQ(result_status, RESULT_STATUS_INTERNAL_ERROR);
+ ReportUmaResult(UMA_STATUS_INTERNAL_ERROR);
+ LOG(WARNING) << "Safe browsing API had internal error";
+ }
+ RunCallbackOnIOThread(callback.release(), SB_THREAT_TYPE_SAFE,
+ ThreatMetadata());
+ return;
+ }
+
+ // Shortcut for safe, so we don't have to parse JSON.
+ if (metadata_str == "{}") {
+ ReportUmaResult(UMA_STATUS_SAFE);
+ RunCallbackOnIOThread(callback.release(), SB_THREAT_TYPE_SAFE,
+ ThreatMetadata());
+ } else {
+ // Unsafe, assuming we can parse the JSON.
+ SBThreatType worst_threat;
+ ThreatMetadata threat_metadata;
+ ReportUmaResult(
+ ParseJsonFromGMSCore(metadata_str, &worst_threat, &threat_metadata));
+ if (worst_threat != SB_THREAT_TYPE_SAFE) {
+ DVLOG(1) << "Check " << callback_id << " marked as UNSAFE";
+ }
+
+ RunCallbackOnIOThread(callback.release(), worst_threat, threat_metadata);
+ }
+}
+
+//
+// SafeBrowsingApiHandlerBridge
+//
+SafeBrowsingApiHandlerBridge::SafeBrowsingApiHandlerBridge()
+ : checked_api_support_(false) {}
+
+SafeBrowsingApiHandlerBridge::~SafeBrowsingApiHandlerBridge() {}
+
+bool SafeBrowsingApiHandlerBridge::CheckApiIsSupported() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (!checked_api_support_) {
+ DVLOG(1) << "Checking API support.";
+ j_api_handler_ = Java_SafeBrowsingApiBridge_create(AttachCurrentThread(),
+ GetApplicationContext());
+ checked_api_support_ = true;
+ }
+ return j_api_handler_.obj() != nullptr;
+}
+
+void SafeBrowsingApiHandlerBridge::StartURLCheck(
+ const SafeBrowsingApiHandler::URLCheckCallbackMeta& callback,
+ const GURL& url,
+ const std::vector<SBThreatType>& threat_types) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ if (!CheckApiIsSupported()) {
+ // Mark all requests as safe. Only users who have an old, broken GMSCore or
+ // have sideloaded Chrome w/o PlayStore should land here.
+ RunCallbackOnIOThread(new URLCheckCallbackMeta(callback),
+ SB_THREAT_TYPE_SAFE, ThreatMetadata());
+ ReportUmaResult(UMA_STATUS_UNSUPPORTED);
+ return;
+ }
+
+ // Make copy on the heap so we can pass the pointer through JNI.
+ intptr_t callback_id =
+ reinterpret_cast<intptr_t>(new URLCheckCallbackMeta(callback));
+
+ 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);
+ if (local_threat_types.empty()) {
+ local_threat_types.push_back(SB_THREAT_TYPE_URL_PHISHING);
+ local_threat_types.push_back(SB_THREAT_TYPE_URL_MALWARE);
+ }
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, url.spec());
+ ScopedJavaLocalRef<jintArray> j_threat_types =
+ SBThreatTypesToJavaArray(env, local_threat_types);
+
+ Java_SafeBrowsingApiBridge_startUriLookup(env, j_api_handler_, callback_id,
+ j_url, j_threat_types);
+}
+
+} // namespace safe_browsing
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
new file mode 100644
index 00000000000..678437affbe
--- /dev/null
+++ b/chromium/components/safe_browsing_db/android/safe_browsing_api_handler_bridge.h
@@ -0,0 +1,48 @@
+// 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.
+//
+// Glue to pass Safe Browsing API requests between Chrome and GMSCore
+
+#ifndef COMPONENTS_SAFE_BROWSING_DB_SAFE_BROWSING_API_HANDLER_BRIDGE_H_
+#define COMPONENTS_SAFE_BROWSING_DB_SAFE_BROWSING_API_HANDLER_BRIDGE_H_
+
+#include <jni.h>
+
+#include <string>
+#include <vector>
+
+#include "base/android/jni_android.h"
+#include "base/macros.h"
+#include "components/safe_browsing_db/safe_browsing_api_handler.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+bool RegisterSafeBrowsingApiBridge(JNIEnv* env);
+
+class SafeBrowsingApiHandlerBridge : public SafeBrowsingApiHandler {
+ public:
+ SafeBrowsingApiHandlerBridge();
+ ~SafeBrowsingApiHandlerBridge() override;
+
+ // 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;
+
+ private:
+ // Creates the j_api_handler_ if it hasn't been already. If the API is not
+ // supported, this will return false and j_api_handler_ will remain NULL.
+ bool CheckApiIsSupported();
+
+ // The Java-side SafeBrowsingApiHandler. Must call CheckApiIsSupported first.
+ base::android::ScopedJavaLocalRef<jobject> j_api_handler_;
+
+ // True if we've once tried to create the above object.
+ bool checked_api_support_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingApiHandlerBridge);
+};
+
+} // namespace safe_browsing
+#endif // COMPONENTS_SAFE_BROWSING_DB_SAFE_BROWSING_API_HANDLER_BRIDGE_H_
diff --git a/chromium/components/safe_browsing_db/database_manager.h b/chromium/components/safe_browsing_db/database_manager.h
index 0d24b8bf3f4..018bf15ba54 100644
--- a/chromium/components/safe_browsing_db/database_manager.h
+++ b/chromium/components/safe_browsing_db/database_manager.h
@@ -27,7 +27,6 @@ class URLRequestContextGetter;
namespace safe_browsing {
-struct ListIdentifier;
struct V4ProtocolConfig;
class V4GetHashProtocolManager;
diff --git a/chromium/components/safe_browsing_db/safe_browsing_api_handler_util.cc b/chromium/components/safe_browsing_db/safe_browsing_api_handler_util.cc
index e4474effec1..4e894f03ee5 100644
--- a/chromium/components/safe_browsing_db/safe_browsing_api_handler_util.cc
+++ b/chromium/components/safe_browsing_db/safe_browsing_api_handler_util.cc
@@ -157,7 +157,7 @@ UmaRemoteCallResult ParseJsonFromGMSCore(const std::string& metadata_str,
// Pick out the "matches" list.
std::unique_ptr<base::Value> value = base::JSONReader::Read(metadata_str);
const base::ListValue* matches = nullptr;
- if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY) ||
+ if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY) ||
!(static_cast<base::DictionaryValue*>(value.get()))
->GetList(kJsonKeyMatches, &matches) ||
!matches) {
diff --git a/chromium/components/safe_browsing_db/safe_browsing_prefs.cc b/chromium/components/safe_browsing_db/safe_browsing_prefs.cc
index c5a110aabdb..66c1dbb6e47 100644
--- a/chromium/components/safe_browsing_db/safe_browsing_prefs.cc
+++ b/chromium/components/safe_browsing_db/safe_browsing_prefs.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/command_line.h"
+#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing_db/safe_browsing_prefs.h"
@@ -45,6 +46,76 @@ enum ScoutTransitionReason {
// New reasons must be added BEFORE MAX_REASONS
MAX_REASONS
};
+
+// The Extended Reporting pref that is currently active, used for UMA metrics.
+// These values are written to logs. New enum values can be added, but
+// existing enums must never be renumbered or deleted and reused.
+enum ActiveExtendedReportingPref {
+ SBER1_PREF = 0,
+ SBER2_PREF = 1,
+ // New prefs must be added before MAX_SBER_PREF
+ MAX_SBER_PREF
+};
+
+// Update the correct UMA metric based on which pref was changed and which UI
+// the change was made on.
+void RecordExtendedReportingPrefChanged(
+ const PrefService& prefs,
+ safe_browsing::ExtendedReportingOptInLocation location) {
+ bool pref_value = safe_browsing::IsExtendedReportingEnabled(prefs);
+
+ if (safe_browsing::IsScout(prefs)) {
+ switch (location) {
+ case safe_browsing::SBER_OPTIN_SITE_CHROME_SETTINGS:
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.ChromeSettings",
+ pref_value);
+ break;
+ case safe_browsing::SBER_OPTIN_SITE_ANDROID_SETTINGS:
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.AndroidSettings",
+ pref_value);
+ break;
+ case safe_browsing::SBER_OPTIN_SITE_DOWNLOAD_FEEDBACK_POPUP:
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.DownloadPopup",
+ pref_value);
+ break;
+ case safe_browsing::SBER_OPTIN_SITE_SECURITY_INTERSTITIAL:
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.SecurityInterstitial",
+ pref_value);
+ break;
+ default:
+ NOTREACHED();
+ }
+ } else {
+ switch (location) {
+ case safe_browsing::SBER_OPTIN_SITE_CHROME_SETTINGS:
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Scout.SetPref.SBER1Pref.ChromeSettings",
+ pref_value);
+ break;
+ case safe_browsing::SBER_OPTIN_SITE_ANDROID_SETTINGS:
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Scout.SetPref.SBER1Pref.AndroidSettings",
+ pref_value);
+ break;
+ case safe_browsing::SBER_OPTIN_SITE_DOWNLOAD_FEEDBACK_POPUP:
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Scout.SetPref.SBER1Pref.DownloadPopup",
+ pref_value);
+ break;
+ case safe_browsing::SBER_OPTIN_SITE_SECURITY_INTERSTITIAL:
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Scout.SetPref.SBER1Pref.SecurityInterstitial",
+ pref_value);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+}
} // namespace
namespace prefs {
@@ -228,10 +299,81 @@ void RecordExtendedReportingMetrics(const PrefService& prefs) {
}
}
+void SetExtendedReportingPrefAndMetric(
+ PrefService* prefs,
+ bool value,
+ ExtendedReportingOptInLocation location) {
+ prefs->SetBoolean(GetExtendedReportingPrefName(*prefs), value);
+ RecordExtendedReportingPrefChanged(*prefs, location);
+}
+
void SetExtendedReportingPref(PrefService* prefs, bool value) {
prefs->SetBoolean(GetExtendedReportingPrefName(*prefs), value);
}
+void UpdateMetricsAfterSecurityInterstitial(const PrefService& prefs,
+ bool on_show_pref_existed,
+ bool on_show_pref_value) {
+ const ActiveExtendedReportingPref active_pref =
+ IsScout(prefs) ? SBER2_PREF : SBER1_PREF;
+ const bool cur_pref_value = IsExtendedReportingEnabled(prefs);
+
+ if (!on_show_pref_existed) {
+ if (!ExtendedReportingPrefExists(prefs)) {
+ // User seeing pref for the first time, didn't touch the checkbox (left it
+ // unchecked).
+ UMA_HISTOGRAM_ENUMERATION(
+ "SafeBrowsing.Pref.Scout.Decision.First_LeftUnchecked", active_pref,
+ MAX_SBER_PREF);
+ return;
+ }
+
+ // Pref currently exists so user did something to the checkbox
+ if (cur_pref_value) {
+ // User turned the pref on.
+ UMA_HISTOGRAM_ENUMERATION(
+ "SafeBrowsing.Pref.Scout.Decision.First_Enabled", active_pref,
+ MAX_SBER_PREF);
+ return;
+ }
+
+ // Otherwise, user turned the pref off, but because it didn't exist when
+ // the interstitial was first shown, they must have turned it on and then
+ // off before the interstitial was closed.
+ UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.Pref.Scout.Decision.First_Disabled",
+ active_pref, MAX_SBER_PREF);
+ return;
+ }
+
+ // At this point, the pref existed when the interstitial was shown so this is
+ // a repeat appearance of the opt-in. Existence can't be removed during an
+ // interstitial so no need to check whether the pref currently exists.
+ if (on_show_pref_value && cur_pref_value) {
+ // User left the pref on.
+ UMA_HISTOGRAM_ENUMERATION(
+ "SafeBrowsing.Pref.Scout.Decision.Repeat_LeftEnabled", active_pref,
+ MAX_SBER_PREF);
+ return;
+ } else if (on_show_pref_value && !cur_pref_value) {
+ // User turned the pref off.
+ UMA_HISTOGRAM_ENUMERATION(
+ "SafeBrowsing.Pref.Scout.Decision.Repeat_Disabled", active_pref,
+ MAX_SBER_PREF);
+ return;
+ } else if (!on_show_pref_value && cur_pref_value) {
+ // User turned the pref on.
+ UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.Pref.Scout.Decision.Repeat_Enabled",
+ active_pref, MAX_SBER_PREF);
+ return;
+ } else {
+ // Both on_show and cur values are false - user left the pref off.
+ UMA_HISTOGRAM_ENUMERATION(
+ "SafeBrowsing.Pref.Scout.Decision.Repeat_LeftDisabled", active_pref,
+ MAX_SBER_PREF);
+ return;
+ }
+}
+
void UpdatePrefsBeforeSecurityInterstitial(PrefService* prefs) {
// Move the user into the Scout Group if the CanShowScoutOptIn experiment is
// enabled and they're not in the group already.
diff --git a/chromium/components/safe_browsing_db/safe_browsing_prefs.h b/chromium/components/safe_browsing_db/safe_browsing_prefs.h
index c8856b40c1f..da25461697e 100644
--- a/chromium/components/safe_browsing_db/safe_browsing_prefs.h
+++ b/chromium/components/safe_browsing_db/safe_browsing_prefs.h
@@ -55,6 +55,23 @@ enum ExtendedReportingLevel {
SBER_LEVEL_SCOUT = 2,
};
+// Enumerates all the places where the Safe Browsing Extended Reporting
+// preference can be changed.
+// These values are written to logs. New enum values can be added, but
+// existing enums must never be renumbered or deleted and reused.
+enum ExtendedReportingOptInLocation {
+ // The chrome://settings UI (also shared with chrome://md-settings).
+ SBER_OPTIN_SITE_CHROME_SETTINGS = 0,
+ // The Android settings UI.
+ SBER_OPTIN_SITE_ANDROID_SETTINGS = 1,
+ // The Download Feedback popup.
+ SBER_OPTIN_SITE_DOWNLOAD_FEEDBACK_POPUP = 2,
+ // Any security interstitial (malware, SSL, etc).
+ SBER_OPTIN_SITE_SECURITY_INTERSTITIAL = 3,
+ // New sites must be added before SBER_OPTIN_SITE_MAX.
+ SBER_OPTIN_SITE_MAX
+};
+
// Determines which opt-in text should be used based on the currently active
// preference. Will return either |extended_reporting_pref| if the legacy
// Extended Reporting pref is active, or |scout_pref| if the Scout pref is
@@ -100,9 +117,22 @@ bool IsScout(const PrefService& prefs);
void RecordExtendedReportingMetrics(const PrefService& prefs);
// Sets the currently active Safe Browsing Extended Reporting preference to the
-// specified value.
+// specified value. The |location| indicates the UI where the change was
+// made.
+void SetExtendedReportingPrefAndMetric(PrefService* prefs,
+ bool value,
+ ExtendedReportingOptInLocation location);
+// This variant is used to simplify test code by omitting the location.
void SetExtendedReportingPref(PrefService* prefs, bool value);
+// Called when a security interstitial is closed by the user.
+// |on_show_pref_existed| indicates whether the pref existed when the
+// interstitial was shown. |on_show_pref_value| contains the pref value when the
+// interstitial was shown.
+void UpdateMetricsAfterSecurityInterstitial(const PrefService& prefs,
+ bool on_show_pref_existed,
+ bool on_show_pref_value);
+
// Called to indicate that a security interstitial is about to be shown to the
// user. This may trigger the user to begin seeing the Scout opt-in text
// depending on their experiment state.
diff --git a/chromium/components/safe_browsing_db/safebrowsing.proto b/chromium/components/safe_browsing_db/safebrowsing.proto
index fb5785d28d7..42f41d640cf 100644
--- a/chromium/components/safe_browsing_db/safebrowsing.proto
+++ b/chromium/components/safe_browsing_db/safebrowsing.proto
@@ -111,6 +111,9 @@ message FetchThreatListUpdatesRequest {
// The requested threat list updates.
repeated ListUpdateRequest list_update_requests = 3;
+
+ // Chrome-specific client information.
+ optional ChromeClientInfo chrome_client_info = 4;
}
// Response type for threat list update requests.
@@ -323,6 +326,28 @@ message ClientInfo {
optional string client_version = 2;
}
+// The client metadata associated with Safe Browsing API requests specific to
+// users of Chrome.
+message ChromeClientInfo {
+ // Safe Browsing reporting populations in Chrome.
+ enum SafeBrowsingReportingPopulation {
+ // Unspecified reporting verbosity.
+ UNSPECIFIED = 0;
+
+ // Client is opted out of reporting.
+ OPT_OUT = 1;
+
+ // Legacy extended reporting population.
+ EXTENDED = 2;
+
+ // Scout reporting population.
+ SCOUT = 3;
+ }
+
+ // The reporting population of the user.
+ optional SafeBrowsingReportingPopulation reporting_population = 1;
+}
+
// The expected state of a client's local database.
message Checksum {
// The SHA256 hash of the client state; that is, of the sorted list of all
diff --git a/chromium/components/safe_browsing_db/test_database_manager.h b/chromium/components/safe_browsing_db/test_database_manager.h
index 22239d94539..3dbf27512aa 100644
--- a/chromium/components/safe_browsing_db/test_database_manager.h
+++ b/chromium/components/safe_browsing_db/test_database_manager.h
@@ -11,14 +11,8 @@
#include "components/safe_browsing_db/database_manager.h"
-namespace net {
-class URLRequestContextGetter;
-}
-
namespace safe_browsing {
-struct V4ProtocolConfig;
-
// This is a non-pure-virtual implementation of the SafeBrowsingDatabaseManager
// interface. It's used in tests by overriding only the functions that get
// called, and it'll complain if you call one that isn't overriden. The
diff --git a/chromium/components/safe_browsing_db/v4_database.cc b/chromium/components/safe_browsing_db/v4_database.cc
index 5edeb3e9422..8b20a88b344 100644
--- a/chromium/components/safe_browsing_db/v4_database.cc
+++ b/chromium/components/safe_browsing_db/v4_database.cc
@@ -91,7 +91,8 @@ V4Database::V4Database(
std::unique_ptr<StoreMap> store_map)
: db_task_runner_(db_task_runner),
store_map_(std::move(store_map)),
- pending_store_updates_(0) {
+ pending_store_updates_(0),
+ weak_factory_on_io_(this) {
DCHECK(db_task_runner->RunsTasksOnCurrentThread());
}
@@ -100,6 +101,7 @@ void V4Database::Destroy(std::unique_ptr<V4Database> v4_database) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
V4Database* v4_database_raw = v4_database.release();
if (v4_database_raw) {
+ v4_database_raw->weak_factory_on_io_.InvalidateWeakPtrs();
v4_database_raw->db_task_runner_->DeleteSoon(FROM_HERE, v4_database_raw);
}
}
@@ -130,8 +132,9 @@ void V4Database::ApplyUpdate(
if (old_store->state() != response->new_client_state()) {
// A different state implies there are updates to process.
pending_store_updates_++;
- UpdatedStoreReadyCallback store_ready_callback = base::Bind(
- &V4Database::UpdatedStoreReady, base::Unretained(this), identifier);
+ UpdatedStoreReadyCallback store_ready_callback =
+ base::Bind(&V4Database::UpdatedStoreReady,
+ weak_factory_on_io_.GetWeakPtr(), identifier);
db_task_runner_->PostTask(
FROM_HERE,
base::Bind(&V4Store::ApplyUpdate, base::Unretained(old_store.get()),
@@ -220,9 +223,10 @@ void V4Database::VerifyChecksum(
const scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner =
base::ThreadTaskRunnerHandle::Get();
db_task_runner_->PostTask(
- FROM_HERE, base::Bind(&V4Database::VerifyChecksumOnTaskRunner,
- base::Unretained(this), callback_task_runner,
- db_ready_for_updates_callback));
+ FROM_HERE,
+ base::Bind(&V4Database::VerifyChecksumOnTaskRunner,
+ weak_factory_on_io_.GetWeakPtr(), callback_task_runner,
+ db_ready_for_updates_callback));
}
void V4Database::VerifyChecksumOnTaskRunner(
diff --git a/chromium/components/safe_browsing_db/v4_database.h b/chromium/components/safe_browsing_db/v4_database.h
index 19fee93a53b..cc07d4e308a 100644
--- a/chromium/components/safe_browsing_db/v4_database.h
+++ b/chromium/components/safe_browsing_db/v4_database.h
@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "components/safe_browsing_db/v4_protocol_manager_util.h"
@@ -205,6 +206,10 @@ class V4Database {
// accessed on the IO thread.
int pending_store_updates_;
+ // Only meant to be dereferenced and invalidated on the IO thread and hence
+ // named. For details, see the comment at the top of weak_ptr.h
+ base::WeakPtrFactory<V4Database> weak_factory_on_io_;
+
DISALLOW_COPY_AND_ASSIGN(V4Database);
};
diff --git a/chromium/components/safe_browsing_db/v4_database_unittest.cc b/chromium/components/safe_browsing_db/v4_database_unittest.cc
index a9133d74f2b..b8e0786aedc 100644
--- a/chromium/components/safe_browsing_db/v4_database_unittest.cc
+++ b/chromium/components/safe_browsing_db/v4_database_unittest.cc
@@ -110,6 +110,7 @@ class V4DatabaseTest : public PlatformTest {
}
void DatabaseUpdated() {}
+
void NewDatabaseReadyWithExpectedStorePathsAndIds(
std::unique_ptr<V4Database> v4_database) {
ASSERT_TRUE(v4_database);
@@ -476,4 +477,42 @@ TEST_F(V4DatabaseTest, TestStoresAvailable) {
EXPECT_FALSE(v4_database_->AreStoresAvailable(StoresToCheck({bogus_id})));
}
+// Test to ensure that the callback to the database is dropped when the database
+// gets destroyed. See http://crbug.com/683147#c5 for more details.
+TEST_F(V4DatabaseTest, UsingWeakPtrDropsCallback) {
+ RegisterFactory();
+
+ // Step 1: Create the database.
+ V4Database::Create(task_runner_, database_dirname_, list_infos_,
+ callback_db_ready_);
+ created_but_not_called_back_ = true;
+ WaitForTasksOnTaskRunner();
+
+ // Step 2: Try to update the database. This posts V4Store::ApplyUpdate on the
+ // task runner.
+ std::unique_ptr<ParsedServerResponse> parsed_server_response(
+ new ParsedServerResponse);
+ auto lur = base::MakeUnique<ListUpdateResponse>();
+ lur->set_platform_type(linux_malware_id_.platform_type());
+ lur->set_threat_entry_type(linux_malware_id_.threat_entry_type());
+ lur->set_threat_type(linux_malware_id_.threat_type());
+ lur->set_new_client_state("new_state");
+ lur->set_response_type(ListUpdateResponse::FULL_UPDATE);
+ parsed_server_response->push_back(std::move(lur));
+
+ // We pass |null_callback| as the second argument to |ApplyUpdate| since we
+ // expect it to not get called. This callback method is called from
+ // V4Database::UpdatedStoreReady but we expect that call to get dropped.
+ base::Callback<void()> null_callback;
+ v4_database_->ApplyUpdate(std::move(parsed_server_response), null_callback);
+
+ // Step 3: Before V4Store::ApplyUpdate gets executed on the task runner,
+ // destroy the database. This posts ~V4Database() on the task runner.
+ V4Database::Destroy(std::move(v4_database_));
+
+ // Step 4: Wait for the task runner to go to completion. The test should
+ // finish to completion and the |null_callback| should not get called.
+ WaitForTasksOnTaskRunner();
+}
+
} // namespace safe_browsing
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 ffef84ef025..b21f2bd1d59 100644
--- a/chromium/components/safe_browsing_db/v4_local_database_manager.cc
+++ b/chromium/components/safe_browsing_db/v4_local_database_manager.cc
@@ -87,6 +87,7 @@ ThreatSeverity GetThreatSeverity(const ListIdentifier& list_id) {
case UNWANTED_SOFTWARE:
return 1;
case API_ABUSE:
+ case CLIENT_INCIDENT:
return 2;
default:
NOTREACHED() << "Unexpected ThreatType encountered: "
@@ -128,17 +129,22 @@ V4LocalDatabaseManager::PendingCheck::~PendingCheck() {}
// static
scoped_refptr<V4LocalDatabaseManager> V4LocalDatabaseManager::Create(
- const base::FilePath& base_path) {
+ const base::FilePath& base_path,
+ ExtendedReportingLevelCallback extended_reporting_level_callback) {
if (!V4FeatureList::IsLocalDatabaseManagerEnabled()) {
return nullptr;
}
- return make_scoped_refptr(new V4LocalDatabaseManager(base_path));
+ return make_scoped_refptr(
+ new V4LocalDatabaseManager(base_path, extended_reporting_level_callback));
}
-V4LocalDatabaseManager::V4LocalDatabaseManager(const base::FilePath& base_path)
+V4LocalDatabaseManager::V4LocalDatabaseManager(
+ const base::FilePath& base_path,
+ ExtendedReportingLevelCallback extended_reporting_level_callback)
: base_path_(base_path),
enabled_(false),
+ extended_reporting_level_callback_(extended_reporting_level_callback),
list_infos_(GetListInfos()),
weak_factory_(this) {
DCHECK(!base_path_.empty());
@@ -708,12 +714,13 @@ void V4LocalDatabaseManager::SetupDatabase() {
void V4LocalDatabaseManager::SetupUpdateProtocolManager(
net::URLRequestContextGetter* request_context_getter,
const V4ProtocolConfig& config) {
- V4UpdateCallback callback =
+ V4UpdateCallback update_callback =
base::Bind(&V4LocalDatabaseManager::UpdateRequestCompleted,
weak_factory_.GetWeakPtr());
- v4_update_protocol_manager_ =
- V4UpdateProtocolManager::Create(request_context_getter, config, callback);
+ v4_update_protocol_manager_ = V4UpdateProtocolManager::Create(
+ request_context_getter, config, update_callback,
+ extended_reporting_level_callback_);
}
void V4LocalDatabaseManager::UpdateRequestCompleted(
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 41598243228..74d19a2b287 100644
--- a/chromium/components/safe_browsing_db/v4_local_database_manager.h
+++ b/chromium/components/safe_browsing_db/v4_local_database_manager.h
@@ -20,8 +20,6 @@
#include "components/safe_browsing_db/v4_update_protocol_manager.h"
#include "url/gurl.h"
-using content::ResourceType;
-
namespace safe_browsing {
typedef unsigned ThreatSeverity;
@@ -33,7 +31,8 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
// Create and return an instance of V4LocalDatabaseManager, if Finch trial
// allows it; nullptr otherwise.
static scoped_refptr<V4LocalDatabaseManager> Create(
- const base::FilePath& base_path);
+ const base::FilePath& base_path,
+ ExtendedReportingLevelCallback extended_reporting_level_callback);
//
// SafeBrowsingDatabaseManager implementation
@@ -74,7 +73,9 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
protected:
// Construct V4LocalDatabaseManager.
// Must be initialized by calling StartOnIOThread() before using.
- V4LocalDatabaseManager(const base::FilePath& base_path);
+ V4LocalDatabaseManager(
+ const base::FilePath& base_path,
+ ExtendedReportingLevelCallback extended_reporting_level_callback);
~V4LocalDatabaseManager() override;
@@ -270,6 +271,10 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
// Whether the service is running.
bool enabled_;
+ // Callback to get the current extended reporting level. Needed by the update
+ // manager.
+ ExtendedReportingLevelCallback extended_reporting_level_callback_;
+
// The list of stores to manage (for hash prefixes and full hashes). Each
// element contains the identifier for the store, the corresponding
// SBThreatType, whether to fetch hash prefixes for that store, and the
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 85df2f85adb..948c77aee7a 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
@@ -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/safe_browsing_db/v4_local_database_manager.h"
+#include "base/base64.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
@@ -9,7 +11,6 @@
#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_local_database_manager.h"
#include "components/safe_browsing_db/v4_test_util.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "crypto/sha2.h"
@@ -20,6 +21,8 @@ namespace safe_browsing {
namespace {
+typedef std::vector<FullHashInfo> FullHashInfos;
+
// Utility function for populating hashes.
FullHash HashForUrl(const GURL& url) {
std::vector<FullHash> full_hashes;
@@ -34,39 +37,49 @@ class FakeGetHashProtocolManager : public V4GetHashProtocolManager {
FakeGetHashProtocolManager(
net::URLRequestContextGetter* request_context_getter,
const StoresToCheck& stores_to_check,
- const V4ProtocolConfig& config)
+ const V4ProtocolConfig& config,
+ const FullHashInfos& full_hash_infos)
: V4GetHashProtocolManager(request_context_getter,
stores_to_check,
- config) {}
+ config),
+ full_hash_infos_(full_hash_infos) {}
void GetFullHashes(const FullHashToStoreAndHashPrefixesMap&,
FullHashCallback callback) override {
- std::vector<FullHashInfo> full_hash_infos;
-
// Async, since the real manager might use a fetcher.
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(callback, full_hash_infos));
+ FROM_HERE, base::Bind(callback, full_hash_infos_));
}
+
+ private:
+ FullHashInfos full_hash_infos_;
};
class FakeGetHashProtocolManagerFactory
: public V4GetHashProtocolManagerFactory {
public:
+ FakeGetHashProtocolManagerFactory(const FullHashInfos& full_hash_infos)
+ : full_hash_infos_(full_hash_infos) {}
+
std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager(
net::URLRequestContextGetter* request_context_getter,
const StoresToCheck& stores_to_check,
const V4ProtocolConfig& config) override {
return base::MakeUnique<FakeGetHashProtocolManager>(
- request_context_getter, stores_to_check, config);
+ request_context_getter, stores_to_check, config, full_hash_infos_);
}
+
+ private:
+ FullHashInfos full_hash_infos_;
};
// Use FakeGetHashProtocolManagerFactory in scope, then reset.
class ScopedFakeGetHashProtocolManagerFactory {
public:
- ScopedFakeGetHashProtocolManagerFactory() {
+ ScopedFakeGetHashProtocolManagerFactory(
+ const FullHashInfos& full_hash_infos) {
V4GetHashProtocolManager::RegisterFactory(
- base::MakeUnique<FakeGetHashProtocolManagerFactory>());
+ base::MakeUnique<FakeGetHashProtocolManagerFactory>(full_hash_infos));
}
~ScopedFakeGetHashProtocolManagerFactory() {
V4GetHashProtocolManager::RegisterFactory(nullptr);
@@ -167,9 +180,7 @@ class TestClient : public SafeBrowsingDatabaseManager::Client {
const std::string& threat_hash) override {
DCHECK_EQ(expected_url, url);
DCHECK_EQ(expected_sb_threat_type, threat_type);
- // |threat_hash| is empty because GetFullHashes calls back with empty
- // |full_hash_infos|.
- DCHECK(threat_hash.empty());
+ DCHECK_EQ(threat_type == SB_THREAT_TYPE_SAFE, threat_hash.empty());
on_check_resource_url_result_called_ = true;
}
@@ -188,8 +199,10 @@ class FakeV4LocalDatabaseManager : public V4LocalDatabaseManager {
perform_full_hash_check_called_ = true;
}
- FakeV4LocalDatabaseManager(const base::FilePath& base_path)
- : V4LocalDatabaseManager(base_path),
+ FakeV4LocalDatabaseManager(
+ const base::FilePath& base_path,
+ ExtendedReportingLevelCallback extended_reporting_level_callback)
+ : V4LocalDatabaseManager(base_path, extended_reporting_level_callback),
perform_full_hash_check_called_(false) {}
static bool PerformFullHashCheckCalled(
@@ -215,8 +228,13 @@ class V4LocalDatabaseManagerTest : public PlatformTest {
ASSERT_TRUE(base_dir_.CreateUniqueTempDir());
DVLOG(1) << "base_dir_: " << base_dir_.GetPath().value();
- v4_local_database_manager_ =
- make_scoped_refptr(new V4LocalDatabaseManager(base_dir_.GetPath()));
+ extended_reporting_level_ = SBER_LEVEL_OFF;
+ erl_callback_ =
+ base::Bind(&V4LocalDatabaseManagerTest::GetExtendedReportingLevel,
+ base::Unretained(this));
+
+ v4_local_database_manager_ = make_scoped_refptr(
+ new V4LocalDatabaseManager(base_dir_.GetPath(), erl_callback_));
SetTaskRunnerForTest();
StartLocalDatabaseManager();
@@ -240,6 +258,10 @@ class V4LocalDatabaseManagerTest : public PlatformTest {
return v4_local_database_manager_->queued_checks_;
}
+ ExtendedReportingLevel GetExtendedReportingLevel() {
+ return extended_reporting_level_;
+ }
+
void ReplaceV4Database(const StoreAndHashPrefixes& store_and_hash_prefixes,
bool stores_available = false) {
// Disable the V4LocalDatabaseManager first so that if the callback to
@@ -264,8 +286,8 @@ class V4LocalDatabaseManagerTest : public PlatformTest {
void ResetLocalDatabaseManager() {
StopLocalDatabaseManager();
- v4_local_database_manager_ =
- make_scoped_refptr(new V4LocalDatabaseManager(base_dir_.GetPath()));
+ v4_local_database_manager_ = make_scoped_refptr(
+ new V4LocalDatabaseManager(base_dir_.GetPath(), erl_callback_));
SetTaskRunnerForTest();
StartLocalDatabaseManager();
}
@@ -304,14 +326,16 @@ class V4LocalDatabaseManagerTest : public PlatformTest {
// StopLocalDatabaseManager before resetting it because that's what
// ~V4LocalDatabaseManager expects.
StopLocalDatabaseManager();
- v4_local_database_manager_ =
- make_scoped_refptr(new FakeV4LocalDatabaseManager(base_dir_.GetPath()));
+ v4_local_database_manager_ = make_scoped_refptr(
+ new FakeV4LocalDatabaseManager(base_dir_.GetPath(), erl_callback_));
SetTaskRunnerForTest();
StartLocalDatabaseManager();
WaitForTasksOnTaskRunner();
}
base::ScopedTempDir base_dir_;
+ ExtendedReportingLevel extended_reporting_level_;
+ ExtendedReportingLevelCallback erl_callback_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
content::TestBrowserThreadBundle thread_bundle_;
scoped_refptr<V4LocalDatabaseManager> v4_local_database_manager_;
@@ -432,7 +456,7 @@ TEST_F(V4LocalDatabaseManagerTest, TestChecksAreQueued) {
// Verify that a window where checks cannot be cancelled is closed.
TEST_F(V4LocalDatabaseManagerTest, CancelPending) {
// Setup to receive full-hash misses.
- ScopedFakeGetHashProtocolManagerFactory pin;
+ ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
// Reset the database manager so it picks up the replacement protocol manager.
ResetLocalDatabaseManager();
@@ -659,7 +683,7 @@ TEST_F(V4LocalDatabaseManagerTest, TestMatchModuleWhitelist) {
// This verifies the fix for race in http://crbug.com/660293
TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithSameClientAndCancel) {
- ScopedFakeGetHashProtocolManagerFactory pin;
+ ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
// Reset the database manager so it picks up the replacement protocol manager.
ResetLocalDatabaseManager();
WaitForTasksOnTaskRunner();
@@ -694,7 +718,7 @@ TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithSameClientAndCancel) {
TEST_F(V4LocalDatabaseManagerTest, TestCheckResourceUrl) {
// Setup to receive full-hash misses.
- ScopedFakeGetHashProtocolManagerFactory pin;
+ ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
// Reset the database manager so it picks up the replacement protocol manager.
ResetLocalDatabaseManager();
@@ -717,6 +741,37 @@ TEST_F(V4LocalDatabaseManagerTest, TestCheckResourceUrl) {
EXPECT_TRUE(client.on_check_resource_url_result_called_);
}
+TEST_F(V4LocalDatabaseManagerTest, TestCheckResourceUrlReturnsBad) {
+ std::string base64_encoded = "ZVcaD6lke9GaaZEf07X3CpuEgMAqbpAyPw3sX/7eK9M=";
+ std::string base64_decoded;
+ base::Base64Decode(base64_encoded, &base64_decoded);
+ FullHashInfo fhi(base64_decoded, GetChromeUrlClientIncidentId(),
+ base::Time());
+
+ // Setup to receive full-hash hit.
+ ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({fhi}));
+
+ // Reset the database manager so it picks up the replacement protocol manager.
+ ResetLocalDatabaseManager();
+ WaitForTasksOnTaskRunner();
+
+ // An URL and matching prefix.
+ const GURL url("http://example.com/a/");
+ const HashPrefix hash_prefix("eW\x1A\xF\xA9");
+
+ // Put a match in the db that will cause a protocol-manager request.
+ StoreAndHashPrefixes store_and_hash_prefixes;
+ store_and_hash_prefixes.emplace_back(GetChromeUrlClientIncidentId(),
+ hash_prefix);
+ ReplaceV4Database(store_and_hash_prefixes, true /* stores_available */);
+
+ TestClient client(SB_THREAT_TYPE_BLACKLISTED_RESOURCE, url);
+ EXPECT_FALSE(v4_local_database_manager_->CheckResourceUrl(url, &client));
+ EXPECT_FALSE(client.on_check_resource_url_result_called_);
+ WaitForTasksOnTaskRunner();
+ EXPECT_TRUE(client.on_check_resource_url_result_called_);
+}
+
// TODO(nparker): Add tests for
// CheckDownloadUrl()
// CheckExtensionIDs()
diff --git a/chromium/components/safe_browsing_db/v4_store.cc b/chromium/components/safe_browsing_db/v4_store.cc
index 889c55c4b66..c46dc584097 100644
--- a/chromium/components/safe_browsing_db/v4_store.cc
+++ b/chromium/components/safe_browsing_db/v4_store.cc
@@ -745,7 +745,10 @@ StoreWriteResult V4Store::WriteToDisk(const Checksum& checksum) {
file_format.SerializeToString(&file_format_string);
size_t written = base::WriteFile(new_filename, file_format_string.data(),
file_format_string.size());
- DCHECK_EQ(file_format_string.size(), written);
+
+ if (file_format_string.size() != written) {
+ return UNEXPECTED_BYTES_WRITTEN_FAILURE;
+ }
if (!base::Move(new_filename, store_path_)) {
return UNABLE_TO_RENAME_FAILURE;
diff --git a/chromium/components/safe_browsing_db/v4_store_unittest.cc b/chromium/components/safe_browsing_db/v4_store_unittest.cc
index 2d2e3d76720..575b340cc56 100644
--- a/chromium/components/safe_browsing_db/v4_store_unittest.cc
+++ b/chromium/components/safe_browsing_db/v4_store_unittest.cc
@@ -850,6 +850,14 @@ TEST_F(V4StoreTest, WriteToDiskFails) {
// the temp store file to |store_path_| it fails.
EXPECT_EQ(UNABLE_TO_RENAME_FAILURE,
V4Store(task_runner_, temp_dir_.GetPath()).WriteToDisk(Checksum()));
+
+ // Give a location that isn't writable, even for the tmp file.
+ base::FilePath non_writable_dir =
+ temp_dir_.GetPath()
+ .Append(FILE_PATH_LITERAL("nonexistent_dir"))
+ .Append(FILE_PATH_LITERAL("some.store"));
+ EXPECT_EQ(UNEXPECTED_BYTES_WRITTEN_FAILURE,
+ V4Store(task_runner_, non_writable_dir).WriteToDisk(Checksum()));
}
TEST_F(V4StoreTest, FullUpdateFailsChecksumSynchronously) {
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 47736aa73bf..e72eb44c029 100644
--- a/chromium/components/safe_browsing_db/v4_update_protocol_manager.cc
+++ b/chromium/components/safe_browsing_db/v4_update_protocol_manager.cc
@@ -73,6 +73,21 @@ static const int kV4TimerStartIntervalSecMax = 300;
// Maximum time, in seconds, to wait for a response to an update request.
static const int kV4TimerUpdateWaitSecMax = 30;
+ChromeClientInfo::SafeBrowsingReportingPopulation GetReportingLevelProtoValue(
+ ExtendedReportingLevel reporting_level) {
+ switch (reporting_level) {
+ case SBER_LEVEL_OFF:
+ return ChromeClientInfo::OPT_OUT;
+ case SBER_LEVEL_LEGACY:
+ return ChromeClientInfo::EXTENDED;
+ case SBER_LEVEL_SCOUT:
+ return ChromeClientInfo::SCOUT;
+ default:
+ NOTREACHED() << "Unexpected reporting_level!";
+ return ChromeClientInfo::UNSPECIFIED;
+ }
+}
+
// The default V4UpdateProtocolManagerFactory.
class V4UpdateProtocolManagerFactoryImpl
: public V4UpdateProtocolManagerFactory {
@@ -82,9 +97,12 @@ class V4UpdateProtocolManagerFactoryImpl
std::unique_ptr<V4UpdateProtocolManager> CreateProtocolManager(
net::URLRequestContextGetter* request_context_getter,
const V4ProtocolConfig& config,
- V4UpdateCallback callback) override {
- return std::unique_ptr<V4UpdateProtocolManager>(
- new V4UpdateProtocolManager(request_context_getter, config, callback));
+ V4UpdateCallback update_callback,
+ ExtendedReportingLevelCallback extended_reporting_level_callback)
+ override {
+ return std::unique_ptr<V4UpdateProtocolManager>(new V4UpdateProtocolManager(
+ request_context_getter, config, update_callback,
+ extended_reporting_level_callback));
}
private:
@@ -100,12 +118,14 @@ V4UpdateProtocolManagerFactory* V4UpdateProtocolManager::factory_ = NULL;
std::unique_ptr<V4UpdateProtocolManager> V4UpdateProtocolManager::Create(
net::URLRequestContextGetter* request_context_getter,
const V4ProtocolConfig& config,
- V4UpdateCallback callback) {
+ V4UpdateCallback update_callback,
+ ExtendedReportingLevelCallback extended_reporting_level_callback) {
if (!factory_) {
factory_ = new V4UpdateProtocolManagerFactoryImpl();
}
return factory_->CreateProtocolManager(request_context_getter, config,
- callback);
+ update_callback,
+ extended_reporting_level_callback);
}
void V4UpdateProtocolManager::ResetUpdateErrors() {
@@ -116,7 +136,8 @@ void V4UpdateProtocolManager::ResetUpdateErrors() {
V4UpdateProtocolManager::V4UpdateProtocolManager(
net::URLRequestContextGetter* request_context_getter,
const V4ProtocolConfig& config,
- V4UpdateCallback update_callback)
+ V4UpdateCallback update_callback,
+ ExtendedReportingLevelCallback extended_reporting_level_callback)
: update_error_count_(0),
update_back_off_mult_(1),
next_update_interval_(base::TimeDelta::FromSeconds(
@@ -125,7 +146,8 @@ V4UpdateProtocolManager::V4UpdateProtocolManager(
config_(config),
request_context_getter_(request_context_getter),
url_fetcher_id_(0),
- update_callback_(update_callback) {
+ update_callback_(update_callback),
+ extended_reporting_level_callback_(extended_reporting_level_callback) {
// Do not auto-schedule updates. Let the owner (V4LocalDatabaseManager) do it
// when it is ready to process updates.
}
@@ -217,6 +239,11 @@ std::string V4UpdateProtocolManager::GetBase64SerializedUpdateRequestProto() {
RICE);
}
+ if (!extended_reporting_level_callback_.is_null()) {
+ request.mutable_chrome_client_info()->set_reporting_population(
+ GetReportingLevelProtoValue(extended_reporting_level_callback_.Run()));
+ }
+
V4ProtocolManagerUtil::SetClientInfoFromConfig(request.mutable_client(),
config_);
@@ -290,7 +317,7 @@ void V4UpdateProtocolManager::IssueUpdateRequest() {
data_use_measurement::DataUseUserData::AttachToFetcher(
fetcher.get(), data_use_measurement::DataUseUserData::SAFE_BROWSING);
- request_.reset(fetcher.release());
+ request_ = std::move(fetcher);
request_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
request_->SetRequestContext(request_context_getter_.get());
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 a3b4cd538e0..4405742938c 100644
--- a/chromium/components/safe_browsing_db/v4_update_protocol_manager.h
+++ b/chromium/components/safe_browsing_db/v4_update_protocol_manager.h
@@ -21,6 +21,7 @@
#include "base/threading/non_thread_safe.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
+#include "components/safe_browsing_db/safe_browsing_prefs.h"
#include "components/safe_browsing_db/safebrowsing.pb.h"
#include "components/safe_browsing_db/util.h"
#include "components/safe_browsing_db/v4_protocol_manager_util.h"
@@ -44,6 +45,8 @@ class V4UpdateProtocolManagerFactory;
typedef base::Callback<void(std::unique_ptr<ParsedServerResponse>)>
V4UpdateCallback;
+typedef base::Callback<ExtendedReportingLevel()> ExtendedReportingLevelCallback;
+
class V4UpdateProtocolManager : public net::URLFetcherDelegate,
public base::NonThreadSafe {
public:
@@ -59,7 +62,8 @@ class V4UpdateProtocolManager : public net::URLFetcherDelegate,
static std::unique_ptr<V4UpdateProtocolManager> Create(
net::URLRequestContextGetter* request_context_getter,
const V4ProtocolConfig& config,
- V4UpdateCallback callback);
+ V4UpdateCallback update_callback,
+ ExtendedReportingLevelCallback extended_reporting_level_callback);
// net::URLFetcherDelegate interface.
void OnURLFetchComplete(const net::URLFetcher* source) override;
@@ -75,7 +79,8 @@ class V4UpdateProtocolManager : public net::URLFetcherDelegate,
V4UpdateProtocolManager(
net::URLRequestContextGetter* request_context_getter,
const V4ProtocolConfig& config,
- V4UpdateCallback callback);
+ V4UpdateCallback update_callback,
+ ExtendedReportingLevelCallback extended_reporting_level_callback);
private:
FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
@@ -90,6 +95,8 @@ class V4UpdateProtocolManager : public net::URLFetcherDelegate,
FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest, TestDisableAutoUpdates);
FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
TestGetUpdatesHasTimeout);
+ FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
+ TestExtendedReportingLevelIncluded);
friend class V4UpdateProtocolManagerFactoryImpl;
// Fills a FetchThreatListUpdatesRequest protocol buffer for a request.
@@ -183,6 +190,8 @@ class V4UpdateProtocolManager : public net::URLFetcherDelegate,
// complete.
base::OneShotTimer timeout_timer_;
+ ExtendedReportingLevelCallback extended_reporting_level_callback_;
+
DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManager);
};
@@ -194,7 +203,8 @@ class V4UpdateProtocolManagerFactory {
virtual std::unique_ptr<V4UpdateProtocolManager> CreateProtocolManager(
net::URLRequestContextGetter* request_context_getter,
const V4ProtocolConfig& config,
- V4UpdateCallback callback) = 0;
+ V4UpdateCallback update_callback,
+ ExtendedReportingLevelCallback extended_reporting_level_callback) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManagerFactory);
diff --git a/chromium/components/safe_browsing_db/v4_update_protocol_manager_unittest.cc b/chromium/components/safe_browsing_db/v4_update_protocol_manager_unittest.cc
index cdef7382501..e76e249b826 100644
--- a/chromium/components/safe_browsing_db/v4_update_protocol_manager_unittest.cc
+++ b/chromium/components/safe_browsing_db/v4_update_protocol_manager_unittest.cc
@@ -58,13 +58,20 @@ class V4UpdateProtocolManagerTest : public PlatformTest {
}
}
+ ExtendedReportingLevel GetExtendedReportingLevel(ExtendedReportingLevel erl) {
+ return erl;
+ }
+
std::unique_ptr<V4UpdateProtocolManager> CreateProtocolManager(
const std::vector<ListUpdateResponse>& expected_lurs,
- bool disable_auto_update = false) {
+ bool disable_auto_update = false,
+ ExtendedReportingLevel erl = SBER_LEVEL_OFF) {
return V4UpdateProtocolManager::Create(
NULL, GetTestV4ProtocolConfig(disable_auto_update),
base::Bind(&V4UpdateProtocolManagerTest::ValidateGetUpdatesResults,
- base::Unretained(this), expected_lurs));
+ base::Unretained(this), expected_lurs),
+ base::Bind(&V4UpdateProtocolManagerTest::GetExtendedReportingLevel,
+ base::Unretained(this), erl));
}
void SetupStoreStates() {
@@ -291,7 +298,7 @@ TEST_F(V4UpdateProtocolManagerTest, TestBase64EncodingUsesUrlEncoding) {
std::string encoded_request_with_minus =
pm->GetBase64SerializedUpdateRequestProto();
- EXPECT_EQ("Cg8KCHVuaXR0ZXN0EgMxLjAaGAgBEAIaCmg4eGZZcVk-OlIiBCABIAIoAQ==",
+ EXPECT_EQ("Cg8KCHVuaXR0ZXN0EgMxLjAaGAgBEAIaCmg4eGZZcVk-OlIiBCABIAIoASICCAE=",
encoded_request_with_minus);
// TODO(vakh): Add a similar test for underscore for completeness, although
@@ -358,4 +365,27 @@ TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesHasTimeout) {
EXPECT_EQ(1ul, pm->update_back_off_mult_);
}
+TEST_F(V4UpdateProtocolManagerTest, TestExtendedReportingLevelIncluded) {
+ store_state_map_->clear();
+ (*store_state_map_)[ListIdentifier(LINUX_PLATFORM, URL, MALWARE_THREAT)] =
+ "state";
+ std::string base = "Cg8KCHVuaXR0ZXN0EgMxLjAaEwgBEAIaBXN0YXRlIgQgASACKAEiAgg";
+
+ std::unique_ptr<V4UpdateProtocolManager> pm_with_off(CreateProtocolManager(
+ std::vector<ListUpdateResponse>({}), false, SBER_LEVEL_OFF));
+ pm_with_off->store_state_map_ = std::move(store_state_map_);
+ EXPECT_EQ(base + "B", pm_with_off->GetBase64SerializedUpdateRequestProto());
+
+ std::unique_ptr<V4UpdateProtocolManager> pm_with_legacy(CreateProtocolManager(
+ std::vector<ListUpdateResponse>({}), false, SBER_LEVEL_LEGACY));
+ pm_with_legacy->store_state_map_ = std::move(pm_with_off->store_state_map_);
+ EXPECT_EQ(base + "C",
+ pm_with_legacy->GetBase64SerializedUpdateRequestProto());
+
+ std::unique_ptr<V4UpdateProtocolManager> pm_with_scout(CreateProtocolManager(
+ std::vector<ListUpdateResponse>({}), false, SBER_LEVEL_SCOUT));
+ pm_with_scout->store_state_map_ = std::move(pm_with_legacy->store_state_map_);
+ EXPECT_EQ(base + "D", pm_with_scout->GetBase64SerializedUpdateRequestProto());
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_json/json_sanitizer.cc b/chromium/components/safe_json/json_sanitizer.cc
index 3ad634d1dfb..c88dd883269 100644
--- a/chromium/components/safe_json/json_sanitizer.cc
+++ b/chromium/components/safe_json/json_sanitizer.cc
@@ -61,7 +61,8 @@ void OopJsonSanitizer::OnParseSuccess(std::unique_ptr<base::Value> value) {
// A valid JSON document may only have a dictionary or list as its top-level
// type, but the JSON parser also accepts other types, so we filter them out.
base::Value::Type type = value->GetType();
- if (type != base::Value::TYPE_DICTIONARY && type != base::Value::TYPE_LIST) {
+ if (type != base::Value::Type::DICTIONARY &&
+ type != base::Value::Type::LIST) {
error_callback_.Run("Invalid top-level type");
return;
}
diff --git a/chromium/components/safe_json/json_sanitizer_unittest.cc b/chromium/components/safe_json/json_sanitizer_unittest.cc
index 7caa0992e2b..aac3a494020 100644
--- a/chromium/components/safe_json/json_sanitizer_unittest.cc
+++ b/chromium/components/safe_json/json_sanitizer_unittest.cc
@@ -133,13 +133,13 @@ TEST_F(JsonSanitizerTest, Json) {
}
TEST_F(JsonSanitizerTest, Nesting) {
- // 99 nested arrays are fine.
- std::string nested(99u, '[');
- nested.append(99u, ']');
+ // 199 nested arrays are fine.
+ std::string nested(199u, '[');
+ nested.append(199u, ']');
CheckSuccess(nested);
- // 100 nested arrays is too much.
- CheckError(std::string(100u, '[') + std::string(100u, ']'));
+ // 200 nested arrays is too much.
+ CheckError(std::string(200u, '[') + std::string(200u, ']'));
}
TEST_F(JsonSanitizerTest, Unicode) {
diff --git a/chromium/components/safe_json/public/interfaces/BUILD.gn b/chromium/components/safe_json/public/interfaces/BUILD.gn
index 6e8b6de5e09..bd150aab319 100644
--- a/chromium/components/safe_json/public/interfaces/BUILD.gn
+++ b/chromium/components/safe_json/public/interfaces/BUILD.gn
@@ -8,4 +8,8 @@ mojom("interfaces") {
sources = [
"safe_json.mojom",
]
+
+ public_deps = [
+ "//mojo/common:common_custom_types",
+ ]
}
diff --git a/chromium/components/safe_json/public/interfaces/safe_json.mojom b/chromium/components/safe_json/public/interfaces/safe_json.mojom
index 599e6c45f29..c5c5d74b6e9 100644
--- a/chromium/components/safe_json/public/interfaces/safe_json.mojom
+++ b/chromium/components/safe_json/public/interfaces/safe_json.mojom
@@ -4,9 +4,8 @@
module safe_json.mojom;
-[Native]
-struct ListValue;
+import "mojo/common/values.mojom";
interface SafeJsonParser {
- Parse(string json) => (ListValue result, string? error);
+ Parse(string json) => (mojo.common.mojom.Value? result, string? error);
};
diff --git a/chromium/components/safe_json/public/interfaces/safe_json.typemap b/chromium/components/safe_json/public/interfaces/safe_json.typemap
deleted file mode 100644
index adca3e2a9e4..00000000000
--- a/chromium/components/safe_json/public/interfaces/safe_json.typemap
+++ /dev/null
@@ -1,9 +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/safe_json/public/interfaces/safe_json.mojom"
-public_headers = [ "//base/values.h" ]
-traits_headers = [ "//ipc/ipc_message_utils.h" ]
-deps = [ "//ipc" ]
-type_mappings = [ "safe_json.mojom.ListValue=base::ListValue" ]
diff --git a/chromium/components/safe_json/safe_json_parser_android.h b/chromium/components/safe_json/safe_json_parser_android.h
index 3dc34b1ae5d..520a2db0ad4 100644
--- a/chromium/components/safe_json/safe_json_parser_android.h
+++ b/chromium/components/safe_json/safe_json_parser_android.h
@@ -12,8 +12,6 @@
namespace safe_json {
-class JsonSanitizer;
-
class SafeJsonParserAndroid : public SafeJsonParser {
public:
SafeJsonParserAndroid(const std::string& unsafe_json,
diff --git a/chromium/components/safe_json/safe_json_parser_impl.cc b/chromium/components/safe_json/safe_json_parser_impl.cc
index 52bd0a3d4c2..9538c501d29 100644
--- a/chromium/components/safe_json/safe_json_parser_impl.cc
+++ b/chromium/components/safe_json/safe_json_parser_impl.cc
@@ -56,32 +56,23 @@ void SafeJsonParserImpl::OnConnectionError() {
mojo_json_parser_.reset();
caller_task_runner_->PostTask(
- FROM_HERE, base::Bind(&SafeJsonParserImpl::ReportResults,
- base::Unretained(this), nullptr, "Json error"));
+ FROM_HERE,
+ base::Bind(&SafeJsonParserImpl::ReportResults, base::Unretained(this),
+ nullptr, "Connection error with the json parser process."));
}
-void SafeJsonParserImpl::OnParseDone(const base::ListValue& wrapper,
+void SafeJsonParserImpl::OnParseDone(std::unique_ptr<base::Value> result,
const base::Optional<std::string>& error) {
DCHECK(io_thread_checker_.CalledOnValidThread());
// Shut down the utility process.
mojo_json_parser_.reset();
- std::unique_ptr<base::Value> parsed_json;
- std::string error_message;
- if (!wrapper.empty()) {
- const base::Value* value = nullptr;
- CHECK(wrapper.Get(0, &value));
- parsed_json.reset(value->DeepCopy());
- } else if (error.has_value()) {
- error_message = error.value();
- }
-
// Call ReportResults() on caller's thread.
caller_task_runner_->PostTask(
FROM_HERE,
base::Bind(&SafeJsonParserImpl::ReportResults, base::Unretained(this),
- base::Passed(&parsed_json), error_message));
+ base::Passed(&result), error.value_or("")));
}
void SafeJsonParserImpl::ReportResults(std::unique_ptr<base::Value> 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 9fa583c953a..03ac99563f5 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 ListValue;
class SequencedTaskRunner;
class Value;
}
@@ -40,7 +39,7 @@ class SafeJsonParserImpl : public SafeJsonParser {
void OnConnectionError();
// mojom::SafeJsonParser::Parse callback.
- void OnParseDone(const base::ListValue& wrapper,
+ void OnParseDone(std::unique_ptr<base::Value> result,
const base::Optional<std::string>& error);
// Reports the result on the calling task runner via the |success_callback_|
diff --git a/chromium/components/safe_json/testing_json_parser_unittest.cc b/chromium/components/safe_json/testing_json_parser_unittest.cc
index 1e944a98fa2..63f638fa094 100644
--- a/chromium/components/safe_json/testing_json_parser_unittest.cc
+++ b/chromium/components/safe_json/testing_json_parser_unittest.cc
@@ -40,7 +40,7 @@ class TestingJsonParserTest : public testing::Test {
test->did_success_ = true;
quit_closure.Run();
- ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+ ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
base::DictionaryValue* dict;
ASSERT_TRUE(value->GetAsDictionary(&dict));
int key_value = 0;
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 7d7458c7996..1595192008e 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
@@ -8,6 +8,7 @@
#include <utility>
#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
@@ -30,12 +31,10 @@ void SafeJsonParserMojoImpl::Parse(const std::string& json,
std::string error;
std::unique_ptr<base::Value> value = base::JSONReader::ReadAndReturnError(
json, base::JSON_PARSE_RFC, &error_code, &error);
- base::ListValue wrapper;
if (value) {
- wrapper.Append(std::move(value));
- callback.Run(wrapper, base::nullopt);
+ callback.Run(std::move(value), base::nullopt);
} else {
- callback.Run(wrapper, base::make_optional(std::move(error)));
+ callback.Run(nullptr, base::make_optional(std::move(error)));
}
}
diff --git a/chromium/components/search/BUILD.gn b/chromium/components/search/BUILD.gn
index cc2f790d51f..b3896ba7d1a 100644
--- a/chromium/components/search/BUILD.gn
+++ b/chromium/components/search/BUILD.gn
@@ -6,8 +6,6 @@ static_library("search") {
sources = [
"search.cc",
"search.h",
- "search_switches.cc",
- "search_switches.h",
]
deps = [
diff --git a/chromium/components/search/search.cc b/chromium/components/search/search.cc
index 97cdacb9856..213f2dcc4cf 100644
--- a/chromium/components/search/search.cc
+++ b/chromium/components/search/search.cc
@@ -6,15 +6,11 @@
#include <stddef.h>
-#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/google/core/browser/google_util.h"
-#include "components/search/search_switches.h"
#include "components/search_engines/template_url.h"
#include "url/gurl.h"
@@ -31,12 +27,8 @@ namespace {
// space-delimited list of key:value pairs which correspond to these flags:
const char kEmbeddedPageVersionFlagName[] = "espv";
-#if defined(OS_IOS)
+#if defined(OS_IOS) || defined(OS_ANDROID)
const uint64_t kEmbeddedPageVersionDefault = 1;
-#elif defined(OS_ANDROID)
-const uint64_t kEmbeddedPageVersionDefault = 1;
-// Use this variant to enable EmbeddedSearch SearchBox API in the results page.
-const uint64_t kEmbeddedSearchEnabledVersion = 2;
#else
const uint64_t kEmbeddedPageVersionDefault = 2;
#endif
@@ -55,36 +47,19 @@ const char kEmbeddedSearchFieldTrialName[] = "EmbeddedSearch";
// be ignored and Instant Extended will not be enabled by default.
const char kDisablingSuffix[] = "DISABLED";
-#if defined(OS_ANDROID)
-const char kPrefetchSearchResultsFlagName[] = "prefetch_results";
-
-// Controls whether to reuse prerendered Instant Search base page to commit any
-// search query.
-const char kReuseInstantSearchBasePage[] = "reuse_instant_search_base_page";
-#endif
-
} // namespace
bool IsInstantExtendedAPIEnabled() {
-#if defined(OS_IOS)
+#if defined(OS_IOS) || defined(OS_ANDROID)
return false;
-#elif defined(OS_ANDROID)
- return EmbeddedSearchPageVersion() == kEmbeddedSearchEnabledVersion;
#else
return true;
-#endif // defined(OS_IOS)
+#endif
}
// Determine what embedded search page version to request from the user's
// default search provider. If 0, the embedded search UI should not be enabled.
uint64_t EmbeddedSearchPageVersion() {
-#if defined(OS_ANDROID)
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableEmbeddedSearchAPI)) {
- return kEmbeddedSearchEnabledVersion;
- }
-#endif
-
FieldTrialFlags flags;
if (GetFieldTrialInfo(&flags)) {
return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
@@ -158,10 +133,7 @@ bool GetBoolValueForFlagWithDefault(const std::string& flag,
return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags);
}
-std::string InstantExtendedEnabledParam(bool for_search) {
- // TODO(treib): Remove |for_search| and update callers that set it to true.
- if (for_search)
- return std::string();
+std::string InstantExtendedEnabledParam() {
return std::string(google_util::kInstantExtendedAPIParam) + "=" +
base::Uint64ToString(EmbeddedSearchPageVersion()) + "&";
}
@@ -172,36 +144,11 @@ std::string ForceInstantResultsParam(bool for_prerender) {
}
bool ShouldPrefetchSearchResults() {
- if (!IsInstantExtendedAPIEnabled())
- return false;
-
-#if defined(OS_ANDROID)
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kPrefetchSearchResults)) {
- return true;
- }
-
- FieldTrialFlags flags;
- return GetFieldTrialInfo(&flags) &&
- GetBoolValueForFlagWithDefault(kPrefetchSearchResultsFlagName, false,
- flags);
-#else
- return true;
-#endif
+ return IsInstantExtendedAPIEnabled();
}
bool ShouldReuseInstantSearchBasePage() {
- if (!ShouldPrefetchSearchResults())
- return false;
-
-#if defined(OS_ANDROID)
- FieldTrialFlags flags;
- return GetFieldTrialInfo(&flags) &&
- GetBoolValueForFlagWithDefault(kReuseInstantSearchBasePage, false,
- flags);
-#else
- return true;
-#endif
+ return IsInstantExtendedAPIEnabled();
}
// |url| should either have a secure scheme or have a non-HTTPS base URL that
diff --git a/chromium/components/search/search.h b/chromium/components/search/search.h
index a8b21a472bd..0f0d5e364cb 100644
--- a/chromium/components/search/search.h
+++ b/chromium/components/search/search.h
@@ -8,10 +8,7 @@
#include <stdint.h>
#include <string>
-#include <utility>
-#include <vector>
-#include "base/strings/string16.h"
#include "base/strings/string_split.h"
class GURL;
@@ -19,7 +16,8 @@ class TemplateURL;
namespace search {
-// Returns whether the Instant Extended API is enabled.
+// Returns whether the Instant Extended API is enabled. This is always true on
+// desktop and false on mobile.
bool IsInstantExtendedAPIEnabled();
// Returns the value to pass to the &espv CGI parameter when loading the
@@ -61,11 +59,7 @@ bool GetBoolValueForFlagWithDefault(const std::string& flag,
// Returns a string indicating whether InstantExtended is enabled, suitable
// for adding as a query string param to the homepage or search requests.
-// Returns an empty string otherwise.
-//
-// |for_search| should be set to true for search requests, in which case this
-// returns a non-empty string only if query extraction is enabled.
-std::string InstantExtendedEnabledParam(bool for_search);
+std::string InstantExtendedEnabledParam();
// Returns a string that will cause the search results page to update
// incrementally. Currently, Instant Extended passes a different param to
@@ -77,12 +71,12 @@ std::string InstantExtendedEnabledParam(bool for_search);
// the returned string to be non-empty.
std::string ForceInstantResultsParam(bool for_prerender);
-// Returns true if 'prefetch_results' flag is set to true in field trials to
-// prefetch high-confidence search suggestions.
+// Returns whether to prefetch high-confidence search suggestions. True iff
+// the Instant Extended API is enabled.
bool ShouldPrefetchSearchResults();
-// Returns true if 'reuse_instant_search_base_page' flag is set to true in field
-// trials to reuse the prerendered page to commit any search query.
+// Returns whether to reuse the prerendered page to commit any search query.
+// True iff the Instant Extended API is enabled.
bool ShouldReuseInstantSearchBasePage();
// |url| should either have a secure scheme or have a non-HTTPS base URL that
diff --git a/chromium/components/search/search_android_unittest.cc b/chromium/components/search/search_android_unittest.cc
index 387975f3014..08fd73ce0d2 100644
--- a/chromium/components/search/search_android_unittest.cc
+++ b/chromium/components/search/search_android_unittest.cc
@@ -6,7 +6,6 @@
#include "base/command_line.h"
#include "components/search/search.h"
-#include "components/search/search_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace search {
@@ -16,10 +15,6 @@ namespace {
TEST(SearchTest, EmbeddedSearchAPIEnabled) {
EXPECT_EQ(1ul, EmbeddedSearchPageVersion());
EXPECT_FALSE(IsInstantExtendedAPIEnabled());
- base::CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kEnableEmbeddedSearchAPI);
- EXPECT_EQ(2ul, EmbeddedSearchPageVersion());
- EXPECT_TRUE(IsInstantExtendedAPIEnabled());
}
} // namespace
diff --git a/chromium/components/search/search_switches.cc b/chromium/components/search/search_switches.cc
deleted file mode 100644
index b8026033f9f..00000000000
--- a/chromium/components/search/search_switches.cc
+++ /dev/null
@@ -1,21 +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 "build/build_config.h"
-#include "components/search/search_switches.h"
-
-namespace switches {
-
-#if defined(OS_ANDROID)
-
-// Enables EmbeddedSearch API in the search results page.
-const char kEnableEmbeddedSearchAPI[] = "enable-embeddedsearch-api";
-
-// Triggers prerendering of search base page to prefetch results for the typed
-// omnibox query. Only has an effect when prerender is enabled.
-const char kPrefetchSearchResults[] = "prefetch-search-results";
-
-#endif
-
-} // namespace switches
diff --git a/chromium/components/search/search_switches.h b/chromium/components/search/search_switches.h
deleted file mode 100644
index ada2e1727b2..00000000000
--- a/chromium/components/search/search_switches.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SEARCH_SEARCH_SWITCHES_H_
-#define COMPONENTS_SEARCH_SEARCH_SWITCHES_H_
-
-#include "build/build_config.h"
-
-namespace switches {
-
-#if defined(OS_ANDROID)
-extern const char kEnableEmbeddedSearchAPI[];
-extern const char kPrefetchSearchResults[];
-#endif
-
-} // namespace switches
-
-#endif // COMPONENTS_SEARCH_SEARCH_SWITCHES_H_
diff --git a/chromium/components/search/search_unittest.cc b/chromium/components/search/search_unittest.cc
index d5adaa47b14..07db3fb8f42 100644
--- a/chromium/components/search/search_unittest.cc
+++ b/chromium/components/search/search_unittest.cc
@@ -165,17 +165,13 @@ typedef EmbeddedSearchFieldTrialTest InstantExtendedEnabledParamTest;
TEST_F(InstantExtendedEnabledParamTest, QueryExtractionDisabled) {
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("EmbeddedSearch",
"Group1 espv:12"));
- // Make sure InstantExtendedEnabledParam() returns an empty string for search
- // requests.
- EXPECT_EQ("", InstantExtendedEnabledParam(true));
- EXPECT_EQ("espv=12&", InstantExtendedEnabledParam(false));
+ EXPECT_EQ("espv=12&", InstantExtendedEnabledParam());
}
TEST_F(InstantExtendedEnabledParamTest, UseDefaultEmbeddedSearchPageVersion) {
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
"EmbeddedSearch", "Group1 espv:-1"));
- EXPECT_EQ("", InstantExtendedEnabledParam(true));
- EXPECT_EQ("espv=2&", InstantExtendedEnabledParam(false));
+ EXPECT_EQ("espv=2&", InstantExtendedEnabledParam());
}
#endif // !defined(OS_IOS) && !defined(OS_ANDROID)
diff --git a/chromium/components/search_engines/BUILD.gn b/chromium/components/search_engines/BUILD.gn
index 1d14a800820..2ca2e4ae1c7 100644
--- a/chromium/components/search_engines/BUILD.gn
+++ b/chromium/components/search_engines/BUILD.gn
@@ -9,8 +9,6 @@ static_library("search_engines") {
sources = [
"default_search_manager.cc",
"default_search_manager.h",
- "default_search_pref_migration.cc",
- "default_search_pref_migration.h",
"keyword_table.cc",
"keyword_table.h",
"keyword_web_data_service.cc",
@@ -30,6 +28,8 @@ static_library("search_engines") {
"template_url.h",
"template_url_data.cc",
"template_url_data.h",
+ "template_url_data_util.cc",
+ "template_url_data_util.h",
"template_url_fetcher.cc",
"template_url_fetcher.h",
"template_url_id.h",
@@ -81,7 +81,7 @@ static_library("search_engines") {
"//url",
]
- if (enable_configuration_policy) {
+ if (!is_ios) {
sources += [
"default_search_policy_handler.cc",
"default_search_policy_handler.h",
@@ -96,8 +96,6 @@ static_library("search_engines") {
static_library("test_support") {
testonly = true
sources = [
- "default_search_pref_test_util.cc",
- "default_search_pref_test_util.h",
"testing_search_terms_data.cc",
"testing_search_terms_data.h",
]
@@ -113,7 +111,6 @@ source_set("unit_tests") {
testonly = true
sources = [
"default_search_manager_unittest.cc",
- "default_search_pref_migration_unittest.cc",
"keyword_table_unittest.cc",
"search_engine_data_type_controller_unittest.cc",
"search_host_to_urls_map_unittest.cc",
@@ -128,7 +125,7 @@ source_set("unit_tests") {
"//base",
"//base/test:test_support",
"//components/google/core/browser",
- "//components/pref_registry:test_support",
+ "//components/pref_registry:pref_registry",
"//components/prefs",
"//components/sync:test_support_driver",
"//components/sync:test_support_model",
@@ -140,7 +137,7 @@ source_set("unit_tests") {
"//url",
]
- if (enable_configuration_policy) {
+ if (!is_ios) {
sources += [ "default_search_policy_handler_unittest.cc" ]
deps += [
diff --git a/chromium/components/search_engines/OWNERS b/chromium/components/search_engines/OWNERS
index c19e61b74ea..9608b235c80 100644
--- a/chromium/components/search_engines/OWNERS
+++ b/chromium/components/search_engines/OWNERS
@@ -1,2 +1,2 @@
pkasting@chromium.org
-stevet@chromium.org
+vasilii@chromium.org
diff --git a/chromium/components/search_engines/default_search_manager.cc b/chromium/components/search_engines/default_search_manager.cc
index 42b29d29ee7..869ce645584 100644
--- a/chromium/components/search_engines/default_search_manager.cc
+++ b/chromium/components/search_engines/default_search_manager.cc
@@ -25,6 +25,7 @@
#include "components/prefs/pref_value_map.h"
#include "components/search_engines/search_engines_pref_names.h"
#include "components/search_engines/template_url_data.h"
+#include "components/search_engines/template_url_data_util.h"
#include "components/search_engines/template_url_prepopulate_data.h"
namespace {
@@ -50,6 +51,8 @@ const char DefaultSearchManager::kSuggestionsURL[] = "suggestions_url";
const char DefaultSearchManager::kInstantURL[] = "instant_url";
const char DefaultSearchManager::kImageURL[] = "image_url";
const char DefaultSearchManager::kNewTabURL[] = "new_tab_url";
+const char DefaultSearchManager::kContextualSearchURL[] =
+ "contextual_search_url";
const char DefaultSearchManager::kFaviconURL[] = "favicon_url";
const char DefaultSearchManager::kOriginatingURL[] = "originating_url";
@@ -67,6 +70,7 @@ const char DefaultSearchManager::kInputEncodings[] = "input_encodings";
const char DefaultSearchManager::kDateCreated[] = "date_created";
const char DefaultSearchManager::kLastModified[] = "last_modified";
+const char DefaultSearchManager::kLastVisited[] = "last_visited";
const char DefaultSearchManager::kUsageCount[] = "usage_count";
const char DefaultSearchManager::kAlternateURLs[] = "alternate_urls";
@@ -159,57 +163,8 @@ void DefaultSearchManager::SetUserSelectedDefaultSearchEngine(
return;
}
- base::DictionaryValue url_dict;
- url_dict.SetString(kID, base::Int64ToString(data.id));
- url_dict.SetString(kShortName, data.short_name());
- url_dict.SetString(kKeyword, data.keyword());
- url_dict.SetInteger(kPrepopulateID, data.prepopulate_id);
- url_dict.SetString(kSyncGUID, data.sync_guid);
-
- url_dict.SetString(kURL, data.url());
- url_dict.SetString(kSuggestionsURL, data.suggestions_url);
- url_dict.SetString(kInstantURL, data.instant_url);
- url_dict.SetString(kImageURL, data.image_url);
- url_dict.SetString(kNewTabURL, data.new_tab_url);
- url_dict.SetString(kFaviconURL, data.favicon_url.spec());
- url_dict.SetString(kOriginatingURL, data.originating_url.spec());
-
- url_dict.SetString(kSearchURLPostParams, data.search_url_post_params);
- url_dict.SetString(kSuggestionsURLPostParams,
- data.suggestions_url_post_params);
- url_dict.SetString(kInstantURLPostParams, data.instant_url_post_params);
- url_dict.SetString(kImageURLPostParams, data.image_url_post_params);
-
- url_dict.SetBoolean(kSafeForAutoReplace, data.safe_for_autoreplace);
-
- url_dict.SetString(kDateCreated,
- base::Int64ToString(data.date_created.ToInternalValue()));
- url_dict.SetString(kLastModified,
- base::Int64ToString(data.last_modified.ToInternalValue()));
- url_dict.SetInteger(kUsageCount, data.usage_count);
-
- std::unique_ptr<base::ListValue> alternate_urls(new base::ListValue);
- for (std::vector<std::string>::const_iterator it =
- data.alternate_urls.begin();
- it != data.alternate_urls.end(); ++it) {
- alternate_urls->AppendString(*it);
- }
- url_dict.Set(kAlternateURLs, alternate_urls.release());
-
- std::unique_ptr<base::ListValue> encodings(new base::ListValue);
- for (std::vector<std::string>::const_iterator it =
- data.input_encodings.begin();
- it != data.input_encodings.end(); ++it) {
- encodings->AppendString(*it);
- }
- url_dict.Set(kInputEncodings, encodings.release());
-
- url_dict.SetString(kSearchTermsReplacementKey,
- data.search_terms_replacement_key);
-
- url_dict.SetBoolean(kCreatedByPolicy, data.created_by_policy);
-
- pref_service_->Set(kDefaultSearchProviderDataPrefName, url_dict);
+ pref_service_->Set(kDefaultSearchProviderDataPrefName,
+ *TemplateURLDataToDictionary(data));
}
void DefaultSearchManager::SetExtensionControlledDefaultSearchEngine(
@@ -261,10 +216,9 @@ void DefaultSearchManager::MergePrefsDataWithPrepopulated() {
if (!prefs_default_search_ || !prefs_default_search_->prepopulate_id)
return;
- size_t default_search_index;
std::vector<std::unique_ptr<TemplateURLData>> prepopulated_urls =
TemplateURLPrepopulateData::GetPrepopulatedEngines(pref_service_,
- &default_search_index);
+ nullptr);
for (auto& engine : prepopulated_urls) {
if (engine->prepopulate_id != prefs_default_search_->prepopulate_id)
@@ -279,6 +233,7 @@ void DefaultSearchManager::MergePrefsDataWithPrepopulated() {
engine->sync_guid = prefs_default_search_->sync_guid;
engine->date_created = prefs_default_search_->date_created;
engine->last_modified = prefs_default_search_->last_modified;
+ engine->last_visited = prefs_default_search_->last_visited;
prefs_default_search_ = std::move(engine);
return;
@@ -307,98 +262,11 @@ void DefaultSearchManager::LoadDefaultSearchEngineFromPrefs() {
return;
}
- std::string search_url;
- base::string16 keyword;
- url_dict->GetString(kURL, &search_url);
- url_dict->GetString(kKeyword, &keyword);
- if (search_url.empty() || keyword.empty())
+ auto turl_data = TemplateURLDataFromDictionary(*url_dict);
+ if (!turl_data)
return;
- prefs_default_search_.reset(new TemplateURLData);
- prefs_default_search_->SetKeyword(keyword);
- prefs_default_search_->SetURL(search_url);
-
- std::string id;
- url_dict->GetString(kID, &id);
- base::StringToInt64(id, &prefs_default_search_->id);
- base::string16 short_name;
- url_dict->GetString(kShortName, &short_name);
- prefs_default_search_->SetShortName(short_name);
- url_dict->GetInteger(kPrepopulateID, &prefs_default_search_->prepopulate_id);
- url_dict->GetString(kSyncGUID, &prefs_default_search_->sync_guid);
-
- url_dict->GetString(kSuggestionsURL, &prefs_default_search_->suggestions_url);
- url_dict->GetString(kInstantURL, &prefs_default_search_->instant_url);
- url_dict->GetString(kImageURL, &prefs_default_search_->image_url);
- url_dict->GetString(kNewTabURL, &prefs_default_search_->new_tab_url);
-
- std::string favicon_url;
- std::string originating_url;
- url_dict->GetString(kFaviconURL, &favicon_url);
- url_dict->GetString(kOriginatingURL, &originating_url);
- prefs_default_search_->favicon_url = GURL(favicon_url);
- prefs_default_search_->originating_url = GURL(originating_url);
-
- url_dict->GetString(kSearchURLPostParams,
- &prefs_default_search_->search_url_post_params);
- url_dict->GetString(kSuggestionsURLPostParams,
- &prefs_default_search_->suggestions_url_post_params);
- url_dict->GetString(kInstantURLPostParams,
- &prefs_default_search_->instant_url_post_params);
- url_dict->GetString(kImageURLPostParams,
- &prefs_default_search_->image_url_post_params);
-
- url_dict->GetBoolean(kSafeForAutoReplace,
- &prefs_default_search_->safe_for_autoreplace);
-
- std::string date_created_str;
- std::string last_modified_str;
- url_dict->GetString(kDateCreated, &date_created_str);
- url_dict->GetString(kLastModified, &last_modified_str);
-
- int64_t date_created = 0;
- if (base::StringToInt64(date_created_str, &date_created)) {
- prefs_default_search_->date_created =
- base::Time::FromInternalValue(date_created);
- }
-
- int64_t last_modified = 0;
- if (base::StringToInt64(date_created_str, &last_modified)) {
- prefs_default_search_->last_modified =
- base::Time::FromInternalValue(last_modified);
- }
-
- url_dict->GetInteger(kUsageCount, &prefs_default_search_->usage_count);
-
- const base::ListValue* alternate_urls = NULL;
- if (url_dict->GetList(kAlternateURLs, &alternate_urls)) {
- for (base::ListValue::const_iterator it = alternate_urls->begin();
- it != alternate_urls->end();
- ++it) {
- std::string alternate_url;
- if ((*it)->GetAsString(&alternate_url))
- prefs_default_search_->alternate_urls.push_back(alternate_url);
- }
- }
-
- const base::ListValue* encodings = NULL;
- if (url_dict->GetList(kInputEncodings, &encodings)) {
- for (base::ListValue::const_iterator it = encodings->begin();
- it != encodings->end();
- ++it) {
- std::string encoding;
- if ((*it)->GetAsString(&encoding))
- prefs_default_search_->input_encodings.push_back(encoding);
- }
- }
-
- url_dict->GetString(kSearchTermsReplacementKey,
- &prefs_default_search_->search_terms_replacement_key);
-
- url_dict->GetBoolean(kCreatedByPolicy,
- &prefs_default_search_->created_by_policy);
-
- prefs_default_search_->show_in_default_list = true;
+ prefs_default_search_ = std::move(turl_data);
MergePrefsDataWithPrepopulated();
}
diff --git a/chromium/components/search_engines/default_search_manager.h b/chromium/components/search_engines/default_search_manager.h
index 3cf6960340e..aadc6a46489 100644
--- a/chromium/components/search_engines/default_search_manager.h
+++ b/chromium/components/search_engines/default_search_manager.h
@@ -40,6 +40,7 @@ class DefaultSearchManager {
static const char kInstantURL[];
static const char kImageURL[];
static const char kNewTabURL[];
+ static const char kContextualSearchURL[];
static const char kFaviconURL[];
static const char kOriginatingURL[];
@@ -53,6 +54,7 @@ class DefaultSearchManager {
static const char kDateCreated[];
static const char kLastModified[];
+ static const char kLastVisited[];
static const char kUsageCount[];
static const char kAlternateURLs[];
diff --git a/chromium/components/search_engines/default_search_manager_unittest.cc b/chromium/components/search_engines/default_search_manager_unittest.cc
index a8df14e94ba..a8611d775e2 100644
--- a/chromium/components/search_engines/default_search_manager_unittest.cc
+++ b/chromium/components/search_engines/default_search_manager_unittest.cc
@@ -15,10 +15,11 @@
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
#include "components/search_engines/search_engines_pref_names.h"
#include "components/search_engines/template_url_data.h"
+#include "components/search_engines/template_url_data_util.h"
#include "components/search_engines/template_url_prepopulate_data.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -28,8 +29,8 @@ namespace {
const char kDefaultSearchProviderData[] =
"default_search_provider_data.template_url_data";
-// Checks that the two TemplateURLs are similar. Does not check the id, the
-// date_created or the last_modified time. Neither pointer should be NULL.
+// Checks that the two TemplateURLs are similar. Does not check the id or
+// any time-related fields. Neither pointer should be null.
void ExpectSimilar(const TemplateURLData* expected,
const TemplateURLData* actual) {
ASSERT_TRUE(expected != NULL);
@@ -41,7 +42,6 @@ void ExpectSimilar(const TemplateURLData* expected,
EXPECT_EQ(expected->suggestions_url, actual->suggestions_url);
EXPECT_EQ(expected->favicon_url, actual->favicon_url);
EXPECT_EQ(expected->alternate_urls, actual->alternate_urls);
- EXPECT_EQ(expected->show_in_default_list, actual->show_in_default_list);
EXPECT_EQ(expected->safe_for_autoreplace, actual->safe_for_autoreplace);
EXPECT_EQ(expected->input_encodings, actual->input_encodings);
EXPECT_EQ(expected->search_terms_replacement_key,
@@ -49,7 +49,8 @@ void ExpectSimilar(const TemplateURLData* expected,
}
// TODO(caitkp): TemplateURLData-ify this.
-void SetOverrides(user_prefs::TestingPrefServiceSyncable* prefs, bool update) {
+void SetOverrides(sync_preferences::TestingPrefServiceSyncable* prefs,
+ bool update) {
prefs->SetUserPref(prefs::kSearchProviderOverridesVersion,
new base::FundamentalValue(1));
base::ListValue* overrides = new base::ListValue;
@@ -83,42 +84,15 @@ void SetOverrides(user_prefs::TestingPrefServiceSyncable* prefs, bool update) {
prefs->SetUserPref(prefs::kSearchProviderOverrides, overrides);
}
-void SetPolicy(user_prefs::TestingPrefServiceSyncable* prefs,
+void SetPolicy(sync_preferences::TestingPrefServiceSyncable* prefs,
bool enabled,
TemplateURLData* data) {
if (enabled) {
EXPECT_FALSE(data->keyword().empty());
EXPECT_FALSE(data->url().empty());
}
- std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue);
- entry->SetString(DefaultSearchManager::kShortName, data->short_name());
- entry->SetString(DefaultSearchManager::kKeyword, data->keyword());
- entry->SetString(DefaultSearchManager::kURL, data->url());
- entry->SetString(DefaultSearchManager::kFaviconURL, data->favicon_url.spec());
- entry->SetString(DefaultSearchManager::kSuggestionsURL,
- data->suggestions_url);
- entry->SetBoolean(DefaultSearchManager::kSafeForAutoReplace,
- data->safe_for_autoreplace);
- std::unique_ptr<base::ListValue> alternate_urls(new base::ListValue);
- for (std::vector<std::string>::const_iterator it =
- data->alternate_urls.begin();
- it != data->alternate_urls.end();
- ++it) {
- alternate_urls->AppendString(*it);
- }
- entry->Set(DefaultSearchManager::kAlternateURLs, alternate_urls.release());
-
- std::unique_ptr<base::ListValue> encodings(new base::ListValue);
- for (std::vector<std::string>::const_iterator it =
- data->input_encodings.begin();
- it != data->input_encodings.end();
- ++it) {
- encodings->AppendString(*it);
- }
- entry->Set(DefaultSearchManager::kInputEncodings, encodings.release());
-
- entry->SetString(DefaultSearchManager::kSearchTermsReplacementKey,
- data->search_terms_replacement_key);
+ std::unique_ptr<base::DictionaryValue> entry(
+ TemplateURLDataToDictionary(*data));
entry->SetBoolean(DefaultSearchManager::kDisabledByPolicy, !enabled);
prefs->SetManagedPref(kDefaultSearchProviderData, entry.release());
}
@@ -134,11 +108,11 @@ std::unique_ptr<TemplateURLData> GenerateDummyTemplateURLData(
std::string("http://").append(type).append("foo/alt"));
data->favicon_url = GURL("http://icon1");
data->safe_for_autoreplace = true;
- data->show_in_default_list = true;
data->input_encodings = base::SplitString(
"UTF-8;UTF-16", ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
data->date_created = base::Time();
data->last_modified = base::Time();
+ data->last_visited = base::Time();
return data;
}
@@ -149,17 +123,17 @@ class DefaultSearchManagerTest : public testing::Test {
DefaultSearchManagerTest() {};
void SetUp() override {
- pref_service_.reset(new user_prefs::TestingPrefServiceSyncable);
+ pref_service_.reset(new sync_preferences::TestingPrefServiceSyncable);
DefaultSearchManager::RegisterProfilePrefs(pref_service_->registry());
TemplateURLPrepopulateData::RegisterProfilePrefs(pref_service_->registry());
}
- user_prefs::TestingPrefServiceSyncable* pref_service() {
+ sync_preferences::TestingPrefServiceSyncable* pref_service() {
return pref_service_.get();
}
private:
- std::unique_ptr<user_prefs::TestingPrefServiceSyncable> pref_service_;
+ std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_;
DISALLOW_COPY_AND_ASSIGN(DefaultSearchManagerTest);
};
@@ -176,11 +150,11 @@ TEST_F(DefaultSearchManagerTest, ReadAndWritePref) {
data.alternate_urls.push_back("http://foo1/alt");
data.favicon_url = GURL("http://icon1");
data.safe_for_autoreplace = true;
- data.show_in_default_list = true;
data.input_encodings = base::SplitString(
"UTF-8;UTF-16", ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
data.date_created = base::Time();
data.last_modified = base::Time();
+ data.last_modified = base::Time();
manager.SetUserSelectedDefaultSearchEngine(data);
TemplateURLData* read_data = manager.GetDefaultSearchEngine(NULL);
diff --git a/chromium/components/search_engines/default_search_policy_handler.cc b/chromium/components/search_engines/default_search_policy_handler.cc
index 7c742da5903..35f85cc0bbd 100644
--- a/chromium/components/search_engines/default_search_policy_handler.cc
+++ b/chromium/components/search_engines/default_search_policy_handler.cc
@@ -11,7 +11,6 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
#include "components/policy/core/browser/policy_error_map.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
@@ -59,141 +58,46 @@ void SetStringInPref(const PolicyMap& policies,
// List of policy types to preference names, for policies affecting the default
// search provider.
-const PolicyToPreferenceMapEntry kDefaultSearchPolicyMap[] = {
- { key::kDefaultSearchProviderEnabled,
- prefs::kDefaultSearchProviderEnabled,
- base::Value::TYPE_BOOLEAN },
- { key::kDefaultSearchProviderName,
- prefs::kDefaultSearchProviderName,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderKeyword,
- prefs::kDefaultSearchProviderKeyword,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderSearchURL,
- prefs::kDefaultSearchProviderSearchURL,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderSuggestURL,
- prefs::kDefaultSearchProviderSuggestURL,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderInstantURL,
- prefs::kDefaultSearchProviderInstantURL,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderIconURL,
- prefs::kDefaultSearchProviderIconURL,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderEncodings,
- prefs::kDefaultSearchProviderEncodings,
- base::Value::TYPE_LIST },
- { key::kDefaultSearchProviderAlternateURLs,
- prefs::kDefaultSearchProviderAlternateURLs,
- base::Value::TYPE_LIST },
- { key::kDefaultSearchProviderSearchTermsReplacementKey,
- prefs::kDefaultSearchProviderSearchTermsReplacementKey,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderImageURL,
- prefs::kDefaultSearchProviderImageURL,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderNewTabURL,
- prefs::kDefaultSearchProviderNewTabURL,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderSearchURLPostParams,
- prefs::kDefaultSearchProviderSearchURLPostParams,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderSuggestURLPostParams,
- prefs::kDefaultSearchProviderSuggestURLPostParams,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderInstantURLPostParams,
- prefs::kDefaultSearchProviderInstantURLPostParams,
- base::Value::TYPE_STRING },
- { key::kDefaultSearchProviderImageURLPostParams,
- prefs::kDefaultSearchProviderImageURLPostParams,
- base::Value::TYPE_STRING },
-};
-
-// List of policy types to preference names, for policies affecting the default
-// search provider.
const PolicyToPreferenceMapEntry kDefaultSearchPolicyDataMap[] = {
+ {key::kDefaultSearchProviderEnabled, prefs::kDefaultSearchProviderEnabled,
+ base::Value::Type::BOOLEAN},
{key::kDefaultSearchProviderName, DefaultSearchManager::kShortName,
- base::Value::TYPE_STRING},
+ base::Value::Type::STRING},
{key::kDefaultSearchProviderKeyword, DefaultSearchManager::kKeyword,
- base::Value::TYPE_STRING},
+ base::Value::Type::STRING},
{key::kDefaultSearchProviderSearchURL, DefaultSearchManager::kURL,
- base::Value::TYPE_STRING},
+ base::Value::Type::STRING},
{key::kDefaultSearchProviderSuggestURL,
- DefaultSearchManager::kSuggestionsURL, base::Value::TYPE_STRING},
+ DefaultSearchManager::kSuggestionsURL, base::Value::Type::STRING},
{key::kDefaultSearchProviderInstantURL, DefaultSearchManager::kInstantURL,
- base::Value::TYPE_STRING},
+ base::Value::Type::STRING},
{key::kDefaultSearchProviderIconURL, DefaultSearchManager::kFaviconURL,
- base::Value::TYPE_STRING},
+ base::Value::Type::STRING},
{key::kDefaultSearchProviderEncodings,
- DefaultSearchManager::kInputEncodings, base::Value::TYPE_LIST},
+ DefaultSearchManager::kInputEncodings, base::Value::Type::LIST},
{key::kDefaultSearchProviderAlternateURLs,
- DefaultSearchManager::kAlternateURLs, base::Value::TYPE_LIST},
+ DefaultSearchManager::kAlternateURLs, base::Value::Type::LIST},
{key::kDefaultSearchProviderSearchTermsReplacementKey,
DefaultSearchManager::kSearchTermsReplacementKey,
- base::Value::TYPE_STRING},
+ base::Value::Type::STRING},
{key::kDefaultSearchProviderImageURL, DefaultSearchManager::kImageURL,
- base::Value::TYPE_STRING},
+ base::Value::Type::STRING},
{key::kDefaultSearchProviderNewTabURL, DefaultSearchManager::kNewTabURL,
- base::Value::TYPE_STRING},
+ base::Value::Type::STRING},
{key::kDefaultSearchProviderSearchURLPostParams,
- DefaultSearchManager::kSearchURLPostParams, base::Value::TYPE_STRING},
+ DefaultSearchManager::kSearchURLPostParams, base::Value::Type::STRING},
{key::kDefaultSearchProviderSuggestURLPostParams,
- DefaultSearchManager::kSuggestionsURLPostParams, base::Value::TYPE_STRING},
+ DefaultSearchManager::kSuggestionsURLPostParams,
+ base::Value::Type::STRING},
{key::kDefaultSearchProviderInstantURLPostParams,
- DefaultSearchManager::kInstantURLPostParams, base::Value::TYPE_STRING},
+ DefaultSearchManager::kInstantURLPostParams, base::Value::Type::STRING},
{key::kDefaultSearchProviderImageURLPostParams,
- DefaultSearchManager::kImageURLPostParams, base::Value::TYPE_STRING},
+ DefaultSearchManager::kImageURLPostParams, base::Value::Type::STRING},
};
-// DefaultSearchEncodingsPolicyHandler implementation --------------------------
-
-DefaultSearchEncodingsPolicyHandler::DefaultSearchEncodingsPolicyHandler()
- : TypeCheckingPolicyHandler(key::kDefaultSearchProviderEncodings,
- base::Value::TYPE_LIST) {}
-
-DefaultSearchEncodingsPolicyHandler::~DefaultSearchEncodingsPolicyHandler() {
-}
-
-void DefaultSearchEncodingsPolicyHandler::ApplyPolicySettings(
- const PolicyMap& policies, PrefValueMap* prefs) {
- // The DefaultSearchProviderEncodings policy has type list, but the related
- // preference has type string. Convert one into the other here, using
- // ';' as a separator.
- const base::Value* value = policies.GetValue(policy_name());
- const base::ListValue* list;
- if (!value || !value->GetAsList(&list))
- return;
-
- base::ListValue::const_iterator iter(list->begin());
- base::ListValue::const_iterator end(list->end());
- std::vector<std::string> string_parts;
- for (; iter != end; ++iter) {
- std::string s;
- if ((*iter)->GetAsString(&s)) {
- string_parts.push_back(s);
- }
- }
- std::string encodings = base::JoinString(string_parts, ";");
- prefs->SetString(prefs::kDefaultSearchProviderEncodings, encodings);
-}
-
-
// DefaultSearchPolicyHandler implementation -----------------------------------
-DefaultSearchPolicyHandler::DefaultSearchPolicyHandler() {
- for (size_t i = 0; i < arraysize(kDefaultSearchPolicyMap); ++i) {
- const char* policy_name = kDefaultSearchPolicyMap[i].policy_name;
- if (policy_name == key::kDefaultSearchProviderEncodings) {
- handlers_.push_back(
- base::MakeUnique<DefaultSearchEncodingsPolicyHandler>());
- } else {
- handlers_.push_back(base::MakeUnique<SimplePolicyHandler>(
- policy_name, kDefaultSearchPolicyMap[i].preference_path,
- kDefaultSearchPolicyMap[i].value_type));
- }
- }
-}
+DefaultSearchPolicyHandler::DefaultSearchPolicyHandler() {}
DefaultSearchPolicyHandler::~DefaultSearchPolicyHandler() {}
@@ -206,8 +110,8 @@ bool DefaultSearchPolicyHandler::CheckPolicySettings(const PolicyMap& policies,
// Add an error for all specified default search policies except
// DefaultSearchProviderEnabled.
- for (const auto& handler : handlers_) {
- const char* policy_name = handler->policy_name();
+ for (const auto& policy_map_entry : kDefaultSearchPolicyDataMap) {
+ const char* policy_name = policy_map_entry.policy_name;
if (policy_name != key::kDefaultSearchProviderEnabled &&
HasDefaultSearchPolicy(policies, policy_name)) {
errors->AddError(policy_name, IDS_POLICY_DEFAULT_SEARCH_DISABLED);
@@ -246,14 +150,18 @@ void DefaultSearchPolicyHandler::ApplyPolicySettings(const PolicyMap& policies,
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
for (size_t i = 0; i < arraysize(kDefaultSearchPolicyDataMap); ++i) {
const char* policy_name = kDefaultSearchPolicyDataMap[i].policy_name;
+ // kDefaultSearchProviderEnabled has already been handled.
+ if (policy_name == key::kDefaultSearchProviderEnabled)
+ continue;
+
switch (kDefaultSearchPolicyDataMap[i].value_type) {
- case base::Value::TYPE_STRING:
+ case base::Value::Type::STRING:
SetStringInPref(policies,
policy_name,
kDefaultSearchPolicyDataMap[i].preference_path,
dict.get());
break;
- case base::Value::TYPE_LIST:
+ case base::Value::Type::LIST:
SetListInPref(policies,
policy_name,
kDefaultSearchPolicyDataMap[i].preference_path,
@@ -302,10 +210,15 @@ bool DefaultSearchPolicyHandler::CheckIndividualPolicies(
const PolicyMap& policies,
PolicyErrorMap* errors) {
bool all_ok = true;
- for (const auto& handler : handlers_) {
- // It's important to call CheckPolicySettings() on all handlers and not just
- // exit on the first error, so we report all policy errors.
- all_ok &= handler->CheckPolicySettings(policies, errors);
+ for (const auto& policy_map_entry : kDefaultSearchPolicyDataMap) {
+ // It's important to check policy type for all policies and not just exit on
+ // the first error, so we report all policy errors.
+ const base::Value* value = policies.GetValue(policy_map_entry.policy_name);
+ if (value && !value->IsType(policy_map_entry.value_type)) {
+ errors->AddError(policy_map_entry.policy_name, IDS_POLICY_TYPE_ERROR,
+ base::Value::GetTypeName(policy_map_entry.value_type));
+ all_ok = false;
+ }
}
return all_ok;
}
@@ -318,8 +231,8 @@ bool DefaultSearchPolicyHandler::HasDefaultSearchPolicy(
bool DefaultSearchPolicyHandler::AnyDefaultSearchPoliciesSpecified(
const PolicyMap& policies) {
- for (const auto& handler : handlers_) {
- if (policies.Get(handler->policy_name()))
+ for (const auto& policy_map_entry : kDefaultSearchPolicyDataMap) {
+ if (policies.Get(policy_map_entry.policy_name))
return true;
}
return false;
diff --git a/chromium/components/search_engines/default_search_policy_handler.h b/chromium/components/search_engines/default_search_policy_handler.h
index 499d5a4f0bc..9eea0cd26a0 100644
--- a/chromium/components/search_engines/default_search_policy_handler.h
+++ b/chromium/components/search_engines/default_search_policy_handler.h
@@ -13,21 +13,6 @@
namespace policy {
-// ConfigurationPolicyHandler for the DefaultSearchEncodings policy.
-class DefaultSearchEncodingsPolicyHandler
- : public TypeCheckingPolicyHandler {
- public:
- DefaultSearchEncodingsPolicyHandler();
- ~DefaultSearchEncodingsPolicyHandler() override;
-
- // ConfigurationPolicyHandler methods:
- void ApplyPolicySettings(const PolicyMap& policies,
- PrefValueMap* prefs) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DefaultSearchEncodingsPolicyHandler);
-};
-
// ConfigurationPolicyHandler for the default search policies.
class DefaultSearchPolicyHandler : public ConfigurationPolicyHandler {
public:
@@ -41,8 +26,8 @@ class DefaultSearchPolicyHandler : public ConfigurationPolicyHandler {
PrefValueMap* prefs) override;
private:
- // Calls |CheckPolicySettings()| on each of the handlers in |handlers_|
- // and returns whether all of the calls succeeded.
+ // Checks that value type is valid for each policy and returns whether all of
+ // the policies are valid.
bool CheckIndividualPolicies(const PolicyMap& policies,
PolicyErrorMap* errors);
@@ -70,9 +55,6 @@ class DefaultSearchPolicyHandler : public ConfigurationPolicyHandler {
// not, set it to an empty list.
void EnsureListPrefExists(PrefValueMap* prefs, const std::string& path);
- // The ConfigurationPolicyHandler handlers for each default search policy.
- std::vector<std::unique_ptr<TypeCheckingPolicyHandler>> handlers_;
-
DISALLOW_COPY_AND_ASSIGN(DefaultSearchPolicyHandler);
};
diff --git a/chromium/components/search_engines/default_search_policy_handler_unittest.cc b/chromium/components/search_engines/default_search_policy_handler_unittest.cc
index fece4e7a298..93f7eaff2ff 100644
--- a/chromium/components/search_engines/default_search_policy_handler_unittest.cc
+++ b/chromium/components/search_engines/default_search_policy_handler_unittest.cc
@@ -14,13 +14,6 @@
#include "components/search_engines/default_search_manager.h"
#include "components/search_engines/search_engines_pref_names.h"
-namespace {
-// TODO(caitkp): Should we find a way to route this through DefaultSearchManager
-// to avoid hardcoding this here?
-const char kDefaultSearchProviderData[] =
- "default_search_provider_data.template_url_data";
-} // namespace
-
namespace policy {
class DefaultSearchPolicyHandlerTest
@@ -133,7 +126,8 @@ TEST_F(DefaultSearchPolicyHandlerTest, MissingUrl) {
UpdateProviderPolicy(policy);
const base::Value* temp = nullptr;
- EXPECT_FALSE(store_->GetValue(kDefaultSearchProviderData, &temp));
+ EXPECT_FALSE(store_->GetValue(
+ DefaultSearchManager::kDefaultSearchProviderDataPrefName, &temp));
}
// Checks that if the default search policy is invalid, that no elements of the
@@ -148,7 +142,54 @@ TEST_F(DefaultSearchPolicyHandlerTest, Invalid) {
UpdateProviderPolicy(policy);
const base::Value* temp = nullptr;
- EXPECT_FALSE(store_->GetValue(kDefaultSearchProviderData, &temp));
+ EXPECT_FALSE(store_->GetValue(
+ DefaultSearchManager::kDefaultSearchProviderDataPrefName, &temp));
+}
+
+// Checks that if the default search policy has invalid type for elements,
+// that no elements of the default search policy will be present in prefs.
+TEST_F(DefaultSearchPolicyHandlerTest, InvalidType) {
+ // List of policies defined in test policy.
+ const char* kPolicyNamesToCheck[] = {
+ key::kDefaultSearchProviderEnabled,
+ key::kDefaultSearchProviderName,
+ key::kDefaultSearchProviderKeyword,
+ key::kDefaultSearchProviderSearchURL,
+ key::kDefaultSearchProviderSuggestURL,
+ key::kDefaultSearchProviderIconURL,
+ key::kDefaultSearchProviderEncodings,
+ key::kDefaultSearchProviderAlternateURLs,
+ key::kDefaultSearchProviderSearchTermsReplacementKey,
+ key::kDefaultSearchProviderImageURL,
+ key::kDefaultSearchProviderNewTabURL,
+ key::kDefaultSearchProviderImageURLPostParams};
+
+ PolicyMap policy;
+ BuildDefaultSearchPolicy(&policy);
+
+ for (auto policy_name : kPolicyNamesToCheck) {
+ // Check that policy can be successfully applied first.
+ UpdateProviderPolicy(policy);
+ const base::Value* temp = nullptr;
+ EXPECT_TRUE(store_->GetValue(
+ DefaultSearchManager::kDefaultSearchProviderDataPrefName, &temp));
+
+ auto old_value = base::WrapUnique(policy.GetValue(policy_name)->DeepCopy());
+ // BinaryValue is not supported in any current default search policy params.
+ // Try changing policy param to BinaryValue and check that policy becomes
+ // invalid.
+ policy.Set(policy_name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+ POLICY_SOURCE_CLOUD, base::WrapUnique(new base::BinaryValue()),
+ nullptr);
+ UpdateProviderPolicy(policy);
+
+ EXPECT_FALSE(store_->GetValue(
+ DefaultSearchManager::kDefaultSearchProviderDataPrefName, &temp))
+ << "Policy type check failed " << policy_name;
+ // Return old value to policy map.
+ policy.Set(policy_name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+ POLICY_SOURCE_CLOUD, std::move(old_value), nullptr);
+ }
}
// Checks that for a fully defined search policy, all elements have been
@@ -162,7 +203,8 @@ TEST_F(DefaultSearchPolicyHandlerTest, FullyDefined) {
const base::DictionaryValue* dictionary;
std::string value;
const base::ListValue* list_value;
- EXPECT_TRUE(store_->GetValue(kDefaultSearchProviderData, &temp));
+ EXPECT_TRUE(store_->GetValue(
+ DefaultSearchManager::kDefaultSearchProviderDataPrefName, &temp));
temp->GetAsDictionary(&dictionary);
EXPECT_TRUE(dictionary->GetString(DefaultSearchManager::kURL, &value));
@@ -224,7 +266,8 @@ TEST_F(DefaultSearchPolicyHandlerTest, Disabled) {
UpdateProviderPolicy(policy);
const base::Value* temp = NULL;
const base::DictionaryValue* dictionary;
- EXPECT_TRUE(store_->GetValue(kDefaultSearchProviderData, &temp));
+ EXPECT_TRUE(store_->GetValue(
+ DefaultSearchManager::kDefaultSearchProviderDataPrefName, &temp));
temp->GetAsDictionary(&dictionary);
bool disabled = false;
EXPECT_TRUE(dictionary->GetBoolean(DefaultSearchManager::kDisabledByPolicy,
@@ -248,7 +291,8 @@ TEST_F(DefaultSearchPolicyHandlerTest, MinimallyDefined) {
const base::DictionaryValue* dictionary;
std::string value;
const base::ListValue* list_value;
- EXPECT_TRUE(store_->GetValue(kDefaultSearchProviderData, &temp));
+ EXPECT_TRUE(store_->GetValue(
+ DefaultSearchManager::kDefaultSearchProviderDataPrefName, &temp));
temp->GetAsDictionary(&dictionary);
// Name and keyword should be derived from host.
@@ -306,7 +350,8 @@ TEST_F(DefaultSearchPolicyHandlerTest, FileURL) {
const base::DictionaryValue* dictionary;
std::string value;
- EXPECT_TRUE(store_->GetValue(kDefaultSearchProviderData, &temp));
+ EXPECT_TRUE(store_->GetValue(
+ DefaultSearchManager::kDefaultSearchProviderDataPrefName, &temp));
temp->GetAsDictionary(&dictionary);
EXPECT_TRUE(dictionary->GetString(DefaultSearchManager::kURL, &value));
@@ -316,4 +361,5 @@ TEST_F(DefaultSearchPolicyHandlerTest, FileURL) {
EXPECT_TRUE(dictionary->GetString(DefaultSearchManager::kKeyword, &value));
EXPECT_EQ("_", value);
}
+
} // namespace policy
diff --git a/chromium/components/search_engines/default_search_pref_migration.cc b/chromium/components/search_engines/default_search_pref_migration.cc
deleted file mode 100644
index 5912275f3b7..00000000000
--- a/chromium/components/search_engines/default_search_pref_migration.cc
+++ /dev/null
@@ -1,170 +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/search_engines/default_search_pref_migration.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/prefs/pref_service.h"
-#include "components/search_engines/default_search_manager.h"
-#include "components/search_engines/search_engines_pref_names.h"
-#include "components/search_engines/template_url_data.h"
-#include "components/search_engines/template_url_service.h"
-#include "url/gurl.h"
-
-namespace {
-
-// Loads the user-selected DSE (if there is one) from legacy preferences.
-std::unique_ptr<TemplateURLData> LoadDefaultSearchProviderFromLegacyPrefs(
- PrefService* prefs) {
- if (!prefs->HasPrefPath(prefs::kDefaultSearchProviderSearchURL) ||
- !prefs->HasPrefPath(prefs::kDefaultSearchProviderKeyword))
- return std::unique_ptr<TemplateURLData>();
-
- const PrefService::Preference* pref =
- prefs->FindPreference(prefs::kDefaultSearchProviderSearchURL);
- DCHECK(pref);
- if (pref->IsManaged())
- return std::unique_ptr<TemplateURLData>();
-
- base::string16 keyword =
- base::UTF8ToUTF16(prefs->GetString(prefs::kDefaultSearchProviderKeyword));
- std::string search_url =
- prefs->GetString(prefs::kDefaultSearchProviderSearchURL);
- if (keyword.empty() || search_url.empty())
- return std::unique_ptr<TemplateURLData>();
-
- std::unique_ptr<TemplateURLData> default_provider_data(new TemplateURLData);
- default_provider_data->SetShortName(
- base::UTF8ToUTF16(prefs->GetString(prefs::kDefaultSearchProviderName)));
- default_provider_data->SetKeyword(keyword);
- default_provider_data->SetURL(search_url);
- default_provider_data->suggestions_url =
- prefs->GetString(prefs::kDefaultSearchProviderSuggestURL);
- default_provider_data->instant_url =
- prefs->GetString(prefs::kDefaultSearchProviderInstantURL);
- default_provider_data->image_url =
- prefs->GetString(prefs::kDefaultSearchProviderImageURL);
- default_provider_data->new_tab_url =
- prefs->GetString(prefs::kDefaultSearchProviderNewTabURL);
- default_provider_data->search_url_post_params =
- prefs->GetString(prefs::kDefaultSearchProviderSearchURLPostParams);
- default_provider_data->suggestions_url_post_params =
- prefs->GetString(prefs::kDefaultSearchProviderSuggestURLPostParams);
- default_provider_data->instant_url_post_params =
- prefs->GetString(prefs::kDefaultSearchProviderInstantURLPostParams);
- default_provider_data->image_url_post_params =
- prefs->GetString(prefs::kDefaultSearchProviderImageURLPostParams);
- default_provider_data->favicon_url =
- GURL(prefs->GetString(prefs::kDefaultSearchProviderIconURL));
- default_provider_data->show_in_default_list = true;
- default_provider_data->search_terms_replacement_key =
- prefs->GetString(prefs::kDefaultSearchProviderSearchTermsReplacementKey);
- default_provider_data->input_encodings = base::SplitString(
- prefs->GetString(prefs::kDefaultSearchProviderEncodings),
- ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
- default_provider_data->alternate_urls.clear();
- const base::ListValue* alternate_urls =
- prefs->GetList(prefs::kDefaultSearchProviderAlternateURLs);
- for (size_t i = 0; i < alternate_urls->GetSize(); ++i) {
- std::string alternate_url;
- if (alternate_urls->GetString(i, &alternate_url))
- default_provider_data->alternate_urls.push_back(alternate_url);
- }
-
- std::string id_string = prefs->GetString(prefs::kDefaultSearchProviderID);
- if (!id_string.empty()) {
- int64_t value;
- base::StringToInt64(id_string, &value);
- default_provider_data->id = value;
- }
-
- std::string prepopulate_id =
- prefs->GetString(prefs::kDefaultSearchProviderPrepopulateID);
- if (!prepopulate_id.empty()) {
- int value;
- base::StringToInt(prepopulate_id, &value);
- default_provider_data->prepopulate_id = value;
- }
-
- return default_provider_data;
-}
-
-void ClearDefaultSearchProviderFromLegacyPrefs(PrefService* prefs) {
- prefs->ClearPref(prefs::kDefaultSearchProviderName);
- prefs->ClearPref(prefs::kDefaultSearchProviderKeyword);
- prefs->ClearPref(prefs::kDefaultSearchProviderSearchURL);
- prefs->ClearPref(prefs::kDefaultSearchProviderSuggestURL);
- prefs->ClearPref(prefs::kDefaultSearchProviderInstantURL);
- prefs->ClearPref(prefs::kDefaultSearchProviderImageURL);
- prefs->ClearPref(prefs::kDefaultSearchProviderNewTabURL);
- prefs->ClearPref(prefs::kDefaultSearchProviderSearchURLPostParams);
- prefs->ClearPref(prefs::kDefaultSearchProviderSuggestURLPostParams);
- prefs->ClearPref(prefs::kDefaultSearchProviderInstantURLPostParams);
- prefs->ClearPref(prefs::kDefaultSearchProviderImageURLPostParams);
- prefs->ClearPref(prefs::kDefaultSearchProviderIconURL);
- prefs->ClearPref(prefs::kDefaultSearchProviderEncodings);
- prefs->ClearPref(prefs::kDefaultSearchProviderPrepopulateID);
- prefs->ClearPref(prefs::kDefaultSearchProviderAlternateURLs);
- prefs->ClearPref(prefs::kDefaultSearchProviderSearchTermsReplacementKey);
-}
-
-void MigrateDefaultSearchPref(PrefService* pref_service) {
- DCHECK(pref_service);
-
- std::unique_ptr<TemplateURLData> legacy_dse_from_prefs =
- LoadDefaultSearchProviderFromLegacyPrefs(pref_service);
- if (!legacy_dse_from_prefs)
- return;
-
- DefaultSearchManager default_search_manager(
- pref_service, DefaultSearchManager::ObserverCallback());
- DefaultSearchManager::Source modern_source;
- TemplateURLData* modern_value =
- default_search_manager.GetDefaultSearchEngine(&modern_source);
- if (modern_source == DefaultSearchManager::FROM_FALLBACK) {
- // |modern_value| is the prepopulated default. If it matches the legacy DSE
- // we assume it is not a user-selected value.
- if (!modern_value ||
- legacy_dse_from_prefs->prepopulate_id != modern_value->prepopulate_id) {
- // This looks like a user-selected value, so let's migrate it.
- // TODO(erikwright): Remove this migration logic when this stat approaches
- // zero.
- UMA_HISTOGRAM_BOOLEAN("Search.MigratedPrefToDictionaryValue", true);
- default_search_manager.SetUserSelectedDefaultSearchEngine(
- *legacy_dse_from_prefs);
- }
- }
-
- ClearDefaultSearchProviderFromLegacyPrefs(pref_service);
-}
-
-void OnPrefsInitialized(PrefService* pref_service,
- bool pref_service_initialization_success) {
- MigrateDefaultSearchPref(pref_service);
-}
-
-} // namespace
-
-void ConfigureDefaultSearchPrefMigrationToDictionaryValue(
- PrefService* pref_service) {
- if (pref_service->GetInitializationStatus() ==
- PrefService::INITIALIZATION_STATUS_WAITING) {
- pref_service->AddPrefInitObserver(
- base::Bind(&OnPrefsInitialized, base::Unretained(pref_service)));
- } else {
- MigrateDefaultSearchPref(pref_service);
- }
-}
diff --git a/chromium/components/search_engines/default_search_pref_migration.h b/chromium/components/search_engines/default_search_pref_migration.h
deleted file mode 100644
index b2fd0c443fc..00000000000
--- a/chromium/components/search_engines/default_search_pref_migration.h
+++ /dev/null
@@ -1,17 +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_SEARCH_ENGINES_DEFAULT_SEARCH_PREF_MIGRATION_H_
-#define COMPONENTS_SEARCH_ENGINES_DEFAULT_SEARCH_PREF_MIGRATION_H_
-
-class PrefService;
-
-// Migrates a DSE value stored in separate String/List/..Value preferences by
-// M35 or earlier to the new single DictionaryValue used in M36.
-// Operates immediately if |pref_service| is fully initialized. Otherwise, waits
-// for the PrefService to load using an observer.
-void ConfigureDefaultSearchPrefMigrationToDictionaryValue(
- PrefService* pref_service);
-
-#endif // COMPONENTS_SEARCH_ENGINES_DEFAULT_SEARCH_PREF_MIGRATION_H_
diff --git a/chromium/components/search_engines/default_search_pref_migration_unittest.cc b/chromium/components/search_engines/default_search_pref_migration_unittest.cc
deleted file mode 100644
index 5de070b2910..00000000000
--- a/chromium/components/search_engines/default_search_pref_migration_unittest.cc
+++ /dev/null
@@ -1,234 +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/search_engines/default_search_pref_migration.h"
-
-#include <stddef.h>
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
-#include "components/search_engines/default_search_manager.h"
-#include "components/search_engines/search_engines_pref_names.h"
-#include "components/search_engines/template_url.h"
-#include "components/search_engines/template_url_prepopulate_data.h"
-#include "components/search_engines/template_url_service.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-class DefaultSearchPrefMigrationTest : public testing::Test {
- public:
- DefaultSearchPrefMigrationTest();
-
- void SaveDefaultSearchProviderToLegacyPrefs(const TemplateURL* t_url);
-
- std::unique_ptr<TemplateURL> CreateKeyword(const std::string& short_name,
- const std::string& keyword,
- const std::string& url);
-
- DefaultSearchManager* default_search_manager() {
- return default_search_manager_.get();
- }
-
- PrefService* pref_service() { return &prefs_; }
-
- private:
- user_prefs::TestingPrefServiceSyncable prefs_;
- std::unique_ptr<DefaultSearchManager> default_search_manager_;
-
- DISALLOW_COPY_AND_ASSIGN(DefaultSearchPrefMigrationTest);
-};
-
-DefaultSearchPrefMigrationTest::DefaultSearchPrefMigrationTest() {
- DefaultSearchManager::RegisterProfilePrefs(prefs_.registry());
- TemplateURLPrepopulateData::RegisterProfilePrefs(prefs_.registry());
- TemplateURLService::RegisterProfilePrefs(prefs_.registry());
- default_search_manager_.reset(new DefaultSearchManager(
- pref_service(), DefaultSearchManager::ObserverCallback()));
-}
-
-void DefaultSearchPrefMigrationTest::SaveDefaultSearchProviderToLegacyPrefs(
- const TemplateURL* t_url) {
- PrefService* prefs = pref_service();
-
- bool enabled = false;
- std::string search_url;
- std::string suggest_url;
- std::string instant_url;
- std::string image_url;
- std::string new_tab_url;
- std::string search_url_post_params;
- std::string suggest_url_post_params;
- std::string instant_url_post_params;
- std::string image_url_post_params;
- std::string icon_url;
- std::string encodings;
- std::string short_name;
- std::string keyword;
- std::string id_string;
- std::string prepopulate_id;
- base::ListValue alternate_urls;
- std::string search_terms_replacement_key;
- if (t_url) {
- DCHECK_EQ(TemplateURL::NORMAL, t_url->type());
- enabled = true;
- search_url = t_url->url();
- suggest_url = t_url->suggestions_url();
- instant_url = t_url->instant_url();
- image_url = t_url->image_url();
- new_tab_url = t_url->new_tab_url();
- search_url_post_params = t_url->search_url_post_params();
- suggest_url_post_params = t_url->suggestions_url_post_params();
- instant_url_post_params = t_url->instant_url_post_params();
- image_url_post_params = t_url->image_url_post_params();
- GURL icon_gurl = t_url->favicon_url();
- if (!icon_gurl.is_empty())
- icon_url = icon_gurl.spec();
- encodings = base::JoinString(t_url->input_encodings(), ";");
- short_name = base::UTF16ToUTF8(t_url->short_name());
- keyword = base::UTF16ToUTF8(t_url->keyword());
- id_string = base::Int64ToString(t_url->id());
- prepopulate_id = base::Int64ToString(t_url->prepopulate_id());
- for (size_t i = 0; i < t_url->alternate_urls().size(); ++i)
- alternate_urls.AppendString(t_url->alternate_urls()[i]);
- search_terms_replacement_key = t_url->search_terms_replacement_key();
- }
- prefs->SetBoolean(prefs::kDefaultSearchProviderEnabled, enabled);
- prefs->SetString(prefs::kDefaultSearchProviderSearchURL, search_url);
- prefs->SetString(prefs::kDefaultSearchProviderSuggestURL, suggest_url);
- prefs->SetString(prefs::kDefaultSearchProviderInstantURL, instant_url);
- prefs->SetString(prefs::kDefaultSearchProviderImageURL, image_url);
- prefs->SetString(prefs::kDefaultSearchProviderNewTabURL, new_tab_url);
- prefs->SetString(prefs::kDefaultSearchProviderSearchURLPostParams,
- search_url_post_params);
- prefs->SetString(prefs::kDefaultSearchProviderSuggestURLPostParams,
- suggest_url_post_params);
- prefs->SetString(prefs::kDefaultSearchProviderInstantURLPostParams,
- instant_url_post_params);
- prefs->SetString(prefs::kDefaultSearchProviderImageURLPostParams,
- image_url_post_params);
- prefs->SetString(prefs::kDefaultSearchProviderIconURL, icon_url);
- prefs->SetString(prefs::kDefaultSearchProviderEncodings, encodings);
- prefs->SetString(prefs::kDefaultSearchProviderName, short_name);
- prefs->SetString(prefs::kDefaultSearchProviderKeyword, keyword);
- prefs->SetString(prefs::kDefaultSearchProviderID, id_string);
- prefs->SetString(prefs::kDefaultSearchProviderPrepopulateID, prepopulate_id);
- prefs->Set(prefs::kDefaultSearchProviderAlternateURLs, alternate_urls);
- prefs->SetString(prefs::kDefaultSearchProviderSearchTermsReplacementKey,
- search_terms_replacement_key);
-}
-
-std::unique_ptr<TemplateURL> DefaultSearchPrefMigrationTest::CreateKeyword(
- const std::string& short_name,
- const std::string& keyword,
- const std::string& url) {
- TemplateURLData data;
- data.SetShortName(base::ASCIIToUTF16(short_name));
- data.SetKeyword(base::ASCIIToUTF16(keyword));
- data.SetURL(url);
- std::unique_ptr<TemplateURL> t_url(new TemplateURL(data));
- return t_url;
-}
-
-TEST_F(DefaultSearchPrefMigrationTest, MigrateUserSelectedValue) {
- std::unique_ptr<TemplateURL> t_url(
- CreateKeyword("name1", "key1", "http://foo1/{searchTerms}"));
- // Store a value in the legacy location.
- SaveDefaultSearchProviderToLegacyPrefs(t_url.get());
-
- // Run the migration.
- ConfigureDefaultSearchPrefMigrationToDictionaryValue(pref_service());
-
- // Test that it was migrated.
- DefaultSearchManager::Source source;
- const TemplateURLData* modern_default =
- default_search_manager()->GetDefaultSearchEngine(&source);
- ASSERT_TRUE(modern_default);
- EXPECT_EQ(DefaultSearchManager::FROM_USER, source);
- EXPECT_EQ(t_url->short_name(), modern_default->short_name());
- EXPECT_EQ(t_url->keyword(), modern_default->keyword());
- EXPECT_EQ(t_url->url(), modern_default->url());
-}
-
-TEST_F(DefaultSearchPrefMigrationTest, MigrateOnlyOnce) {
- std::unique_ptr<TemplateURL> t_url(
- CreateKeyword("name1", "key1", "http://foo1/{searchTerms}"));
- // Store a value in the legacy location.
- SaveDefaultSearchProviderToLegacyPrefs(t_url.get());
-
- // Run the migration.
- ConfigureDefaultSearchPrefMigrationToDictionaryValue(pref_service());
-
- // Test that it was migrated.
- DefaultSearchManager::Source source;
- const TemplateURLData* modern_default =
- default_search_manager()->GetDefaultSearchEngine(&source);
- ASSERT_TRUE(modern_default);
- EXPECT_EQ(DefaultSearchManager::FROM_USER, source);
- EXPECT_EQ(t_url->short_name(), modern_default->short_name());
- EXPECT_EQ(t_url->keyword(), modern_default->keyword());
- EXPECT_EQ(t_url->url(), modern_default->url());
- default_search_manager()->ClearUserSelectedDefaultSearchEngine();
-
- // Run the migration.
- ConfigureDefaultSearchPrefMigrationToDictionaryValue(pref_service());
-
- // Test that it was NOT migrated.
- modern_default = default_search_manager()->GetDefaultSearchEngine(&source);
- ASSERT_TRUE(modern_default);
- EXPECT_EQ(DefaultSearchManager::FROM_FALLBACK, source);
-}
-
-TEST_F(DefaultSearchPrefMigrationTest, ModernValuePresent) {
- std::unique_ptr<TemplateURL> t_url(
- CreateKeyword("name1", "key1", "http://foo1/{searchTerms}"));
- std::unique_ptr<TemplateURL> t_url2(
- CreateKeyword("name2", "key2", "http://foo2/{searchTerms}"));
- // Store a value in the legacy location.
- SaveDefaultSearchProviderToLegacyPrefs(t_url.get());
-
- // Store another value in the modern location.
- default_search_manager()->SetUserSelectedDefaultSearchEngine(t_url2->data());
-
- // Run the migration.
- ConfigureDefaultSearchPrefMigrationToDictionaryValue(pref_service());
-
- // Test that no migration occurred. The modern value is left intact.
- DefaultSearchManager::Source source;
- const TemplateURLData* modern_default =
- default_search_manager()->GetDefaultSearchEngine(&source);
- ASSERT_TRUE(modern_default);
- EXPECT_EQ(DefaultSearchManager::FROM_USER, source);
- EXPECT_EQ(t_url2->short_name(), modern_default->short_name());
- EXPECT_EQ(t_url2->keyword(), modern_default->keyword());
- EXPECT_EQ(t_url2->url(), modern_default->url());
-}
-
-TEST_F(DefaultSearchPrefMigrationTest,
- AutomaticallySelectedValueIsNotMigrated) {
- DefaultSearchManager::Source source;
- TemplateURLData prepopulated_default(
- *default_search_manager()->GetDefaultSearchEngine(&source));
- EXPECT_EQ(DefaultSearchManager::FROM_FALLBACK, source);
-
- TemplateURL prepopulated_turl(prepopulated_default);
-
- // Store a value in the legacy location.
- SaveDefaultSearchProviderToLegacyPrefs(&prepopulated_turl);
-
- // Run the migration.
- ConfigureDefaultSearchPrefMigrationToDictionaryValue(pref_service());
-
- // Test that the legacy value is not migrated, as it is not user-selected.
- default_search_manager()->GetDefaultSearchEngine(&source);
- EXPECT_EQ(DefaultSearchManager::FROM_FALLBACK, source);
-}
diff --git a/chromium/components/search_engines/default_search_pref_test_util.cc b/chromium/components/search_engines/default_search_pref_test_util.cc
deleted file mode 100644
index 193ab8f1452..00000000000
--- a/chromium/components/search_engines/default_search_pref_test_util.cc
+++ /dev/null
@@ -1,56 +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/search_engines/default_search_pref_test_util.h"
-
-#include "base/strings/string_split.h"
-#include "components/search_engines/default_search_manager.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-// static
-std::unique_ptr<base::DictionaryValue>
-DefaultSearchPrefTestUtil::CreateDefaultSearchPreferenceValue(
- bool enabled,
- const std::string& name,
- const std::string& keyword,
- const std::string& search_url,
- const std::string& suggest_url,
- const std::string& icon_url,
- const std::string& encodings,
- const std::string& alternate_url,
- const std::string& search_terms_replacement_key) {
- std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
- if (!enabled) {
- value->SetBoolean(DefaultSearchManager::kDisabledByPolicy, true);
- return value;
- }
-
- EXPECT_FALSE(keyword.empty());
- EXPECT_FALSE(search_url.empty());
- value->Set(DefaultSearchManager::kShortName,
- new base::StringValue(name));
- value->Set(DefaultSearchManager::kKeyword,
- new base::StringValue(keyword));
- value->Set(DefaultSearchManager::kURL,
- new base::StringValue(search_url));
- value->Set(DefaultSearchManager::kSuggestionsURL,
- new base::StringValue(suggest_url));
- value->Set(DefaultSearchManager::kFaviconURL,
- new base::StringValue(icon_url));
- value->Set(DefaultSearchManager::kSearchTermsReplacementKey,
- new base::StringValue(search_terms_replacement_key));
-
- std::unique_ptr<base::ListValue> encodings_list(new base::ListValue);
- for (const std::string& term : base::SplitString(
- encodings, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL))
- encodings_list->AppendString(term);
- value->Set(DefaultSearchManager::kInputEncodings, encodings_list.release());
-
- std::unique_ptr<base::ListValue> alternate_url_list(new base::ListValue());
- if (!alternate_url.empty())
- alternate_url_list->AppendString(alternate_url);
- value->Set(DefaultSearchManager::kAlternateURLs,
- alternate_url_list.release());
- return value;
-}
diff --git a/chromium/components/search_engines/default_search_pref_test_util.h b/chromium/components/search_engines/default_search_pref_test_util.h
deleted file mode 100644
index 1732119b3bb..00000000000
--- a/chromium/components/search_engines/default_search_pref_test_util.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SEARCH_ENGINES_DEFAULT_SEARCH_PREF_TEST_UTIL_H_
-#define COMPONENTS_SEARCH_ENGINES_DEFAULT_SEARCH_PREF_TEST_UTIL_H_
-
-#include <memory>
-#include <string>
-
-#include "base/values.h"
-#include "components/search_engines/default_search_manager.h"
-
-class DefaultSearchPrefTestUtil {
- public:
- // Creates a DictionaryValue which can be used as a
- // kDefaultSearchProviderDataPrefName preference value.
- static std::unique_ptr<base::DictionaryValue>
- CreateDefaultSearchPreferenceValue(
- bool enabled,
- const std::string& name,
- const std::string& keyword,
- const std::string& search_url,
- const std::string& suggest_url,
- const std::string& icon_url,
- const std::string& encodings,
- const std::string& alternate_url,
- const std::string& search_terms_replacement_key);
-
- // Set the managed preferences for the default search provider and trigger
- // notification. If |alternate_url| is empty, uses an empty list of alternate
- // URLs, otherwise use a list containing a single entry.
- template<typename TestingPrefService>
- static void SetManagedPref(TestingPrefService* pref_service,
- bool enabled,
- const std::string& name,
- const std::string& keyword,
- const std::string& search_url,
- const std::string& suggest_url,
- const std::string& icon_url,
- const std::string& encodings,
- const std::string& alternate_url,
- const std::string& search_terms_replacement_key) {
- pref_service->SetManagedPref(
- DefaultSearchManager::kDefaultSearchProviderDataPrefName,
- CreateDefaultSearchPreferenceValue(
- enabled, name, keyword, search_url, suggest_url, icon_url,
- encodings, alternate_url, search_terms_replacement_key).release());
- }
-
- // Remove all the managed preferences for the default search provider and
- // trigger notification.
- template<typename TestingPrefService>
- static void RemoveManagedPref(TestingPrefService* pref_service) {
- pref_service->RemoveManagedPref(
- DefaultSearchManager::kDefaultSearchProviderDataPrefName);
- }
-};
-
-#endif // COMPONENTS_SEARCH_ENGINES_DEFAULT_SEARCH_PREF_TEST_UTIL_H_
diff --git a/chromium/components/search_engines/keyword_table.cc b/chromium/components/search_engines/keyword_table.cc
index eee2ac8273e..8ca529c1fb1 100644
--- a/chromium/components/search_engines/keyword_table.cc
+++ b/chromium/components/search_engines/keyword_table.cc
@@ -49,7 +49,10 @@ const std::string ColumnsForVersion(int version, bool concatenated) {
columns.push_back("date_created");
columns.push_back("usage_count");
columns.push_back("input_encodings");
- columns.push_back("show_in_default_list");
+ if (version <= 67) {
+ // Column removed after version 67.
+ columns.push_back("show_in_default_list");
+ }
columns.push_back("suggest_url");
columns.push_back("prepopulate_id");
if (version <= 44) {
@@ -81,6 +84,10 @@ const std::string ColumnsForVersion(int version, bool concatenated) {
// Column added in version 53.
columns.push_back("new_tab_url");
}
+ if (version >= 69) {
+ // Column added in version 69.
+ columns.push_back("last_visited");
+ }
return base::JoinString(columns, std::string(concatenated ? " || " : ", "));
}
@@ -119,21 +126,21 @@ void BindURLToStatement(const TemplateURLData& data,
s->BindInt(starting_column + 7, data.usage_count);
s->BindString(starting_column + 8,
base::JoinString(data.input_encodings, ";"));
- s->BindBool(starting_column + 9, data.show_in_default_list);
- s->BindString(starting_column + 10, data.suggestions_url);
- s->BindInt(starting_column + 11, data.prepopulate_id);
- s->BindBool(starting_column + 12, data.created_by_policy);
- s->BindString(starting_column + 13, data.instant_url);
- s->BindInt64(starting_column + 14, data.last_modified.ToTimeT());
- s->BindString(starting_column + 15, data.sync_guid);
- s->BindString(starting_column + 16, alternate_urls);
- s->BindString(starting_column + 17, data.search_terms_replacement_key);
- s->BindString(starting_column + 18, data.image_url);
- s->BindString(starting_column + 19, data.search_url_post_params);
- s->BindString(starting_column + 20, data.suggestions_url_post_params);
- s->BindString(starting_column + 21, data.instant_url_post_params);
- s->BindString(starting_column + 22, data.image_url_post_params);
- s->BindString(starting_column + 23, data.new_tab_url);
+ s->BindString(starting_column + 9, data.suggestions_url);
+ s->BindInt(starting_column + 10, data.prepopulate_id);
+ s->BindBool(starting_column + 11, data.created_by_policy);
+ s->BindString(starting_column + 12, data.instant_url);
+ s->BindInt64(starting_column + 13, data.last_modified.ToTimeT());
+ s->BindString(starting_column + 14, data.sync_guid);
+ s->BindString(starting_column + 15, alternate_urls);
+ s->BindString(starting_column + 16, data.search_terms_replacement_key);
+ s->BindString(starting_column + 17, data.image_url);
+ s->BindString(starting_column + 18, data.search_url_post_params);
+ s->BindString(starting_column + 19, data.suggestions_url_post_params);
+ s->BindString(starting_column + 20, data.instant_url_post_params);
+ s->BindString(starting_column + 21, data.image_url_post_params);
+ s->BindString(starting_column + 22, data.new_tab_url);
+ s->BindInt64(starting_column + 23, data.last_visited.ToTimeT());
}
WebDatabaseTable::TypeKey GetKey() {
@@ -161,31 +168,31 @@ WebDatabaseTable::TypeKey KeywordTable::GetTypeKey() const {
bool KeywordTable::CreateTablesIfNecessary() {
return db_->DoesTableExist("keywords") ||
db_->Execute("CREATE TABLE keywords ("
- "id INTEGER PRIMARY KEY,"
- "short_name VARCHAR NOT NULL,"
- "keyword VARCHAR NOT NULL,"
- "favicon_url VARCHAR NOT NULL,"
- "url VARCHAR NOT NULL,"
- "safe_for_autoreplace INTEGER,"
- "originating_url VARCHAR,"
- "date_created INTEGER DEFAULT 0,"
- "usage_count INTEGER DEFAULT 0,"
- "input_encodings VARCHAR,"
- "show_in_default_list INTEGER,"
- "suggest_url VARCHAR,"
- "prepopulate_id INTEGER DEFAULT 0,"
- "created_by_policy INTEGER DEFAULT 0,"
- "instant_url VARCHAR,"
- "last_modified INTEGER DEFAULT 0,"
- "sync_guid VARCHAR,"
- "alternate_urls VARCHAR,"
- "search_terms_replacement_key VARCHAR,"
- "image_url VARCHAR,"
- "search_url_post_params VARCHAR,"
- "suggest_url_post_params VARCHAR,"
- "instant_url_post_params VARCHAR,"
- "image_url_post_params VARCHAR,"
- "new_tab_url VARCHAR)");
+ "id INTEGER PRIMARY KEY,"
+ "short_name VARCHAR NOT NULL,"
+ "keyword VARCHAR NOT NULL,"
+ "favicon_url VARCHAR NOT NULL,"
+ "url VARCHAR NOT NULL,"
+ "safe_for_autoreplace INTEGER,"
+ "originating_url VARCHAR,"
+ "date_created INTEGER DEFAULT 0,"
+ "usage_count INTEGER DEFAULT 0,"
+ "input_encodings VARCHAR,"
+ "suggest_url VARCHAR,"
+ "prepopulate_id INTEGER DEFAULT 0,"
+ "created_by_policy INTEGER DEFAULT 0,"
+ "instant_url VARCHAR,"
+ "last_modified INTEGER DEFAULT 0,"
+ "sync_guid VARCHAR,"
+ "alternate_urls VARCHAR,"
+ "search_terms_replacement_key VARCHAR,"
+ "image_url VARCHAR,"
+ "search_url_post_params VARCHAR,"
+ "suggest_url_post_params VARCHAR,"
+ "instant_url_post_params VARCHAR,"
+ "image_url_post_params VARCHAR,"
+ "new_tab_url VARCHAR,"
+ " last_visited INTEGER DEFAULT 0)");
}
bool KeywordTable::IsSyncable() {
@@ -202,6 +209,11 @@ bool KeywordTable::MigrateToVersion(int version,
case 59:
*update_compatible_version = true;
return MigrateToVersion59RemoveExtensionKeywords();
+ case 68:
+ *update_compatible_version = true;
+ return MigrateToVersion68RemoveShowInDefaultListColumn();
+ case 69:
+ return MigrateToVersion69AddLastVisitedColumn();
}
return true;
@@ -289,6 +301,53 @@ bool KeywordTable::MigrateToVersion59RemoveExtensionKeywords() {
"WHERE url LIKE 'chrome-extension://%'");
}
+// SQLite does not support DROP COLUMN operation. So A new table is created
+// without the show_in_default_list column. Data from all but the dropped column
+// of the old table is copied into it. After that, the old table is dropped and
+// the new table is renamed to it.
+bool KeywordTable::MigrateToVersion68RemoveShowInDefaultListColumn() {
+ sql::Transaction transaction(db_);
+ std::string query_str =
+ std::string("INSERT INTO temp_keywords SELECT " +
+ ColumnsForVersion(68, false) + " FROM keywords");
+ const char* clone_query = query_str.c_str();
+ return transaction.Begin() &&
+ db_->Execute(
+ "CREATE TABLE temp_keywords ("
+ "id INTEGER PRIMARY KEY,"
+ "short_name VARCHAR NOT NULL,"
+ "keyword VARCHAR NOT NULL,"
+ "favicon_url VARCHAR NOT NULL,"
+ "url VARCHAR NOT NULL,"
+ "safe_for_autoreplace INTEGER,"
+ "originating_url VARCHAR,"
+ "date_created INTEGER DEFAULT 0,"
+ "usage_count INTEGER DEFAULT 0,"
+ "input_encodings VARCHAR,"
+ "suggest_url VARCHAR,"
+ "prepopulate_id INTEGER DEFAULT 0,"
+ "created_by_policy INTEGER DEFAULT 0,"
+ "instant_url VARCHAR,"
+ "last_modified INTEGER DEFAULT 0,"
+ "sync_guid VARCHAR,"
+ "alternate_urls VARCHAR,"
+ "search_terms_replacement_key VARCHAR,"
+ "image_url VARCHAR,"
+ "search_url_post_params VARCHAR,"
+ "suggest_url_post_params VARCHAR,"
+ "instant_url_post_params VARCHAR,"
+ "image_url_post_params VARCHAR,"
+ "new_tab_url VARCHAR)") &&
+ db_->Execute(clone_query) && db_->Execute("DROP TABLE keywords") &&
+ db_->Execute("ALTER TABLE temp_keywords RENAME TO keywords") &&
+ transaction.Commit();
+}
+
+bool KeywordTable::MigrateToVersion69AddLastVisitedColumn() {
+ return db_->Execute("ALTER TABLE keywords ADD COLUMN last_visited "
+ "INTEGER DEFAULT 0");
+}
+
// static
bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s,
TemplateURLData* data) {
@@ -303,32 +362,31 @@ bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s,
if (s.ColumnString(4).empty())
return false;
data->SetURL(s.ColumnString(4));
- data->suggestions_url = s.ColumnString(11);
- data->instant_url = s.ColumnString(14);
- data->image_url = s.ColumnString(19);
- data->new_tab_url = s.ColumnString(24);
- data->search_url_post_params = s.ColumnString(20);
- data->suggestions_url_post_params = s.ColumnString(21);
- data->instant_url_post_params = s.ColumnString(22);
- data->image_url_post_params = s.ColumnString(23);
+ data->suggestions_url = s.ColumnString(10);
+ data->instant_url = s.ColumnString(13);
+ data->image_url = s.ColumnString(18);
+ data->new_tab_url = s.ColumnString(23);
+ data->search_url_post_params = s.ColumnString(19);
+ data->suggestions_url_post_params = s.ColumnString(20);
+ data->instant_url_post_params = s.ColumnString(21);
+ data->image_url_post_params = s.ColumnString(22);
data->favicon_url = GURL(s.ColumnString(3));
data->originating_url = GURL(s.ColumnString(6));
- data->show_in_default_list = s.ColumnBool(10);
data->safe_for_autoreplace = s.ColumnBool(5);
data->input_encodings = base::SplitString(
s.ColumnString(9), ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
data->id = s.ColumnInt64(0);
data->date_created = Time::FromTimeT(s.ColumnInt64(7));
- data->last_modified = Time::FromTimeT(s.ColumnInt64(15));
- data->created_by_policy = s.ColumnBool(13);
+ data->last_modified = Time::FromTimeT(s.ColumnInt64(14));
+ data->created_by_policy = s.ColumnBool(12);
data->usage_count = s.ColumnInt(8);
- data->prepopulate_id = s.ColumnInt(12);
- data->sync_guid = s.ColumnString(16);
+ data->prepopulate_id = s.ColumnInt(11);
+ data->sync_guid = s.ColumnString(15);
data->alternate_urls.clear();
base::JSONReader json_reader;
std::unique_ptr<base::Value> value(
- json_reader.ReadToValue(s.ColumnString(17)));
+ json_reader.ReadToValue(s.ColumnString(16)));
base::ListValue* alternate_urls_value;
if (value.get() && value->GetAsList(&alternate_urls_value)) {
std::string alternate_url;
@@ -338,7 +396,8 @@ bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s,
}
}
- data->search_terms_replacement_key = s.ColumnString(18);
+ data->search_terms_replacement_key = s.ColumnString(17);
+ data->last_visited = Time::FromTimeT(s.ColumnInt64(24));
return true;
}
@@ -346,8 +405,8 @@ bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s,
bool KeywordTable::AddKeyword(const TemplateURLData& data) {
DCHECK(data.id);
std::string query("INSERT INTO keywords (" + GetKeywordColumns() + ") "
- "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
- " ?)");
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+ " ?,?)");
sql::Statement s(db_->GetCachedStatement(SQL_FROM_HERE, query.c_str()));
BindURLToStatement(data, &s, 0, 1);
@@ -369,12 +428,12 @@ bool KeywordTable::UpdateKeyword(const TemplateURLData& data) {
SQL_FROM_HERE,
"UPDATE keywords SET short_name=?, keyword=?, favicon_url=?, url=?, "
"safe_for_autoreplace=?, originating_url=?, date_created=?, "
- "usage_count=?, input_encodings=?, show_in_default_list=?, "
- "suggest_url=?, prepopulate_id=?, created_by_policy=?, instant_url=?, "
+ "usage_count=?, input_encodings=?, suggest_url=?, "
+ "prepopulate_id=?, created_by_policy=?, instant_url=?, "
"last_modified=?, sync_guid=?, alternate_urls=?, "
"search_terms_replacement_key=?, image_url=?, search_url_post_params=?, "
"suggest_url_post_params=?, instant_url_post_params=?, "
- "image_url_post_params=?, new_tab_url=? WHERE id=?"));
+ "image_url_post_params=?, new_tab_url=?, last_visited=? WHERE id=?"));
BindURLToStatement(data, &s, 24, 0); // "24" binds id() as the last item.
return s.Run();
@@ -401,87 +460,3 @@ bool KeywordTable::GetKeywordAsString(TemplateURLID id,
*result = s.ColumnString(0);
return true;
}
-
-bool KeywordTable::MigrateKeywordsTableForVersion45(const std::string& name) {
- // Create a new table without the columns we're dropping.
- if (!db_->Execute("CREATE TABLE keywords_temp ("
- "id INTEGER PRIMARY KEY,"
- "short_name VARCHAR NOT NULL,"
- "keyword VARCHAR NOT NULL,"
- "favicon_url VARCHAR NOT NULL,"
- "url VARCHAR NOT NULL,"
- "safe_for_autoreplace INTEGER,"
- "originating_url VARCHAR,"
- "date_created INTEGER DEFAULT 0,"
- "usage_count INTEGER DEFAULT 0,"
- "input_encodings VARCHAR,"
- "show_in_default_list INTEGER,"
- "suggest_url VARCHAR,"
- "prepopulate_id INTEGER DEFAULT 0,"
- "created_by_policy INTEGER DEFAULT 0,"
- "instant_url VARCHAR,"
- "last_modified INTEGER DEFAULT 0,"
- "sync_guid VARCHAR)"))
- return false;
- std::string sql("INSERT INTO keywords_temp SELECT " +
- ColumnsForVersion(46, false) + " FROM " + name);
- if (!db_->Execute(sql.c_str()))
- return false;
-
- // NOTE: The ORDER BY here ensures that the uniquing process for keywords will
- // happen identically on both the normal and backup tables.
- sql = "SELECT id, keyword, url, autogenerate_keyword FROM " + name +
- " ORDER BY id ASC";
- sql::Statement s(db_->GetUniqueStatement(sql.c_str()));
- base::string16 placeholder_keyword(base::ASCIIToUTF16("dummy"));
- std::set<base::string16> keywords;
- while (s.Step()) {
- base::string16 keyword(s.ColumnString16(1));
- bool generate_keyword = keyword.empty() || s.ColumnBool(3);
- if (generate_keyword)
- keyword = placeholder_keyword;
- TemplateURLData data;
- data.SetKeyword(keyword);
- data.SetURL(s.ColumnString(2));
- TemplateURL turl(data);
- // Don't persist extension keywords to disk. These will get added to the
- // TemplateURLService as the extensions are loaded.
- bool delete_entry = turl.type() == TemplateURL::OMNIBOX_API_EXTENSION;
- if (!delete_entry && generate_keyword) {
- // Explicitly generate keywords for all rows with the autogenerate bit set
- // or where the keyword is empty.
- SearchTermsData terms_data;
- GURL url(turl.GenerateSearchURL(terms_data));
- if (!url.is_valid()) {
- delete_entry = true;
- } else {
- // Ensure autogenerated keywords are unique.
- keyword = TemplateURL::GenerateKeyword(url);
- while (keywords.count(keyword))
- keyword.append(base::ASCIIToUTF16("_"));
- sql::Statement u(db_->GetUniqueStatement(
- "UPDATE keywords_temp SET keyword=? WHERE id=?"));
- u.BindString16(0, keyword);
- u.BindInt64(1, s.ColumnInt64(0));
- if (!u.Run())
- return false;
- }
- }
- if (delete_entry) {
- sql::Statement u(db_->GetUniqueStatement(
- "DELETE FROM keywords_temp WHERE id=?"));
- u.BindInt64(0, s.ColumnInt64(0));
- if (!u.Run())
- return false;
- } else {
- keywords.insert(keyword);
- }
- }
-
- // Replace the old table with the new one.
- sql = "DROP TABLE " + name;
- if (!db_->Execute(sql.c_str()))
- return false;
- sql = "ALTER TABLE keywords_temp RENAME TO " + name;
- return db_->Execute(sql.c_str());
-}
diff --git a/chromium/components/search_engines/keyword_table.h b/chromium/components/search_engines/keyword_table.h
index 2ac8695fca5..90a42573fa9 100644
--- a/chromium/components/search_engines/keyword_table.h
+++ b/chromium/components/search_engines/keyword_table.h
@@ -36,7 +36,6 @@ class Statement;
// keyword
// favicon_url
// url
-// show_in_default_list
// safe_for_autoreplace
// originating_url
// date_created This column was added after we allowed keywords.
@@ -131,6 +130,8 @@ class KeywordTable : public WebDatabaseTable {
// Table migration functions.
bool MigrateToVersion53AddNewTabURLColumn();
bool MigrateToVersion59RemoveExtensionKeywords();
+ bool MigrateToVersion68RemoveShowInDefaultListColumn();
+ bool MigrateToVersion69AddLastVisitedColumn();
private:
friend class KeywordTableTest;
@@ -165,10 +166,6 @@ class KeywordTable : public WebDatabaseTable {
const std::string& table_name,
std::string* result);
- // Migrates table |name| (which should be either "keywords" or
- // "keywords_backup") from version 44 to version 45.
- bool MigrateKeywordsTableForVersion45(const std::string& name);
-
DISALLOW_COPY_AND_ASSIGN(KeywordTable);
};
diff --git a/chromium/components/search_engines/keyword_table_unittest.cc b/chromium/components/search_engines/keyword_table_unittest.cc
index 0e4503aaee8..e0de66ab705 100644
--- a/chromium/components/search_engines/keyword_table_unittest.cc
+++ b/chromium/components/search_engines/keyword_table_unittest.cc
@@ -53,13 +53,13 @@ class KeywordTableTest : public testing::Test {
keyword.image_url_post_params = "name=1,value=2";
keyword.favicon_url = GURL("http://favicon.url/");
keyword.originating_url = GURL("http://google.com/");
- keyword.show_in_default_list = true;
keyword.safe_for_autoreplace = true;
keyword.input_encodings.push_back("UTF-8");
keyword.input_encodings.push_back("UTF-16");
keyword.id = 1;
keyword.date_created = base::Time::UnixEpoch();
keyword.last_modified = base::Time::UnixEpoch();
+ keyword.last_visited = base::Time::UnixEpoch();
keyword.created_by_policy = true;
keyword.usage_count = 32;
keyword.prepopulate_id = 10;
@@ -124,8 +124,6 @@ TEST_F(KeywordTableTest, Keywords) {
EXPECT_EQ(keyword.instant_url, restored_keyword.instant_url);
EXPECT_EQ(keyword.favicon_url, restored_keyword.favicon_url);
EXPECT_EQ(keyword.originating_url, restored_keyword.originating_url);
- EXPECT_EQ(keyword.show_in_default_list,
- restored_keyword.show_in_default_list);
EXPECT_EQ(keyword.safe_for_autoreplace,
restored_keyword.safe_for_autoreplace);
EXPECT_EQ(keyword.input_encodings, restored_keyword.input_encodings);
@@ -135,6 +133,8 @@ TEST_F(KeywordTableTest, Keywords) {
restored_keyword.date_created.ToTimeT());
EXPECT_EQ(keyword.last_modified.ToTimeT(),
restored_keyword.last_modified.ToTimeT());
+ EXPECT_EQ(keyword.last_visited.ToTimeT(),
+ restored_keyword.last_visited.ToTimeT());
EXPECT_EQ(keyword.created_by_policy, restored_keyword.created_by_policy);
EXPECT_EQ(keyword.usage_count, restored_keyword.usage_count);
EXPECT_EQ(keyword.prepopulate_id, restored_keyword.prepopulate_id);
@@ -168,8 +168,6 @@ TEST_F(KeywordTableTest, UpdateKeyword) {
EXPECT_EQ(keyword.instant_url, restored_keyword.instant_url);
EXPECT_EQ(keyword.favicon_url, restored_keyword.favicon_url);
EXPECT_EQ(keyword.originating_url, restored_keyword.originating_url);
- EXPECT_EQ(keyword.show_in_default_list,
- restored_keyword.show_in_default_list);
EXPECT_EQ(keyword.safe_for_autoreplace,
restored_keyword.safe_for_autoreplace);
EXPECT_EQ(keyword.input_encodings, restored_keyword.input_encodings);
diff --git a/chromium/components/search_engines/prepopulated_engines.json b/chromium/components/search_engines/prepopulated_engines.json
index 97dad3e51dc..3409ecc4a77 100644
--- a/chromium/components/search_engines/prepopulated_engines.json
+++ b/chromium/components/search_engines/prepopulated_engines.json
@@ -30,7 +30,7 @@
// Increment this if you change the data in ways that mean users with
// existing data should get a new version.
- "kCurrentDataVersion": 96
+ "kCurrentDataVersion": 97
},
// The following engines are included in country lists and are added to the
@@ -164,7 +164,7 @@
"name": "\ub124\uc774\ubc84",
"keyword": "naver.com",
"favicon_url": "https://ssl.pstatic.net/sstatic/search/favicon/favicon_140327.ico",
- "search_url": "https://search.naver.com/search.naver?ie={inputEncoding}&query={searchTerms}",
+ "search_url": "https://search.naver.com/search.naver?ie={inputEncoding}&query={searchTerms}&sm=chr_hty",
"suggest_url": "https://ac.search.naver.com/nx/ac?of=os&ie={inputEncoding}&q={searchTerms}&oe={outputEncoding}",
"type": "SEARCH_ENGINE_NAVER",
"id": 67
diff --git a/chromium/components/search_engines/search_engine_data_type_controller.cc b/chromium/components/search_engines/search_engine_data_type_controller.cc
index 536f38703ac..1796368fb89 100644
--- a/chromium/components/search_engines/search_engine_data_type_controller.cc
+++ b/chromium/components/search_engines/search_engine_data_type_controller.cc
@@ -4,13 +4,19 @@
#include "components/search_engines/search_engine_data_type_controller.h"
+#include "base/threading/thread_task_runner_handle.h"
+
namespace browser_sync {
SearchEngineDataTypeController::SearchEngineDataTypeController(
const base::Closure& dump_stack,
syncer::SyncClient* sync_client,
TemplateURLService* template_url_service)
- : UIDataTypeController(syncer::SEARCH_ENGINES, dump_stack, sync_client),
+ : AsyncDirectoryTypeController(syncer::SEARCH_ENGINES,
+ dump_stack,
+ sync_client,
+ syncer::GROUP_UI,
+ base::ThreadTaskRunnerHandle::Get()),
template_url_service_(template_url_service) {}
TemplateURLService::Subscription*
@@ -47,7 +53,7 @@ void SearchEngineDataTypeController::StopModels() {
void SearchEngineDataTypeController::OnTemplateURLServiceLoaded() {
DCHECK(CalledOnValidThread());
- DCHECK_EQ(MODEL_STARTING, state_);
+ DCHECK_EQ(MODEL_STARTING, state());
template_url_subscription_.reset();
OnModelLoaded();
}
diff --git a/chromium/components/search_engines/search_engine_data_type_controller.h b/chromium/components/search_engines/search_engine_data_type_controller.h
index 0a6e1dc1ca4..a7925b5c893 100644
--- a/chromium/components/search_engines/search_engine_data_type_controller.h
+++ b/chromium/components/search_engines/search_engine_data_type_controller.h
@@ -10,16 +10,15 @@
#include "base/macros.h"
#include "components/search_engines/template_url_service.h"
-#include "components/sync/driver/ui_data_type_controller.h"
-
-class Profile;
+#include "components/sync/driver/async_directory_type_controller.h"
namespace browser_sync {
// Controller for the SEARCH_ENGINES sync data type. This class tells sync
// how to load the model for this data type, and the superclasses manage
// controlling the rest of the state of the datatype with regards to sync.
-class SearchEngineDataTypeController : public syncer::UIDataTypeController {
+class SearchEngineDataTypeController
+ : public syncer::AsyncDirectoryTypeController {
public:
// |dump_stack| is called when an unrecoverable error occurs.
SearchEngineDataTypeController(const base::Closure& dump_stack,
@@ -30,7 +29,7 @@ class SearchEngineDataTypeController : public syncer::UIDataTypeController {
TemplateURLService::Subscription* GetSubscriptionForTesting();
private:
- // FrontendDataTypeController:
+ // AsyncDirectoryTypeController:
bool StartModels() override;
void StopModels() override;
diff --git a/chromium/components/search_engines/search_engine_data_type_controller_unittest.cc b/chromium/components/search_engines/search_engine_data_type_controller_unittest.cc
index 4f308855c21..a9c07218644 100644
--- a/chromium/components/search_engines/search_engine_data_type_controller_unittest.cc
+++ b/chromium/components/search_engines/search_engine_data_type_controller_unittest.cc
@@ -157,6 +157,9 @@ TEST_F(SyncSearchEngineDataTypeControllerTest, Stop) {
search_engine_dtc_.Stop();
EXPECT_EQ(syncer::DataTypeController::NOT_RUNNING,
search_engine_dtc_.state());
+ // AsyncDirectoryTypeController::Stop posts call to StopLocalService to model
+ // thread. We run message loop for this call to take effect.
+ base::RunLoop().RunUntilIdle();
EXPECT_FALSE(syncable_service_.syncing());
}
diff --git a/chromium/components/search_engines/search_engines_pref_names.cc b/chromium/components/search_engines/search_engines_pref_names.cc
index 0a0d51f392b..a65cff338a8 100644
--- a/chromium/components/search_engines/search_engines_pref_names.cc
+++ b/chromium/components/search_engines/search_engines_pref_names.cc
@@ -18,82 +18,6 @@ const char kSyncedDefaultSearchProviderGUID[] =
const char kDefaultSearchProviderEnabled[] =
"default_search_provider.enabled";
-// The URL (as understood by TemplateURLRef) the default search provider uses
-// for searches.
-const char kDefaultSearchProviderSearchURL[] =
- "default_search_provider.search_url";
-
-// The URL (as understood by TemplateURLRef) the default search provider uses
-// for suggestions.
-const char kDefaultSearchProviderSuggestURL[] =
- "default_search_provider.suggest_url";
-
-// The URL (as understood by TemplateURLRef) the default search provider uses
-// for instant results.
-const char kDefaultSearchProviderInstantURL[] =
- "default_search_provider.instant_url";
-
-// The URL (as understood by TemplateURLRef) the default search provider uses
-// for image search results.
-const char kDefaultSearchProviderImageURL[] =
- "default_search_provider.image_url";
-
-// The URL (as understood by TemplateURLRef) the default search provider uses
-// for the new tab page.
-const char kDefaultSearchProviderNewTabURL[] =
- "default_search_provider.new_tab_url";
-
-// The string of post parameters (as understood by TemplateURLRef) the default
-// search provider uses for searches by using POST.
-const char kDefaultSearchProviderSearchURLPostParams[] =
- "default_search_provider.search_url_post_params";
-
-// The string of post parameters (as understood by TemplateURLRef) the default
-// search provider uses for suggestions by using POST.
-const char kDefaultSearchProviderSuggestURLPostParams[] =
- "default_search_provider.suggest_url_post_params";
-
-// The string of post parameters (as understood by TemplateURLRef) the default
-// search provider uses for instant results by using POST.
-const char kDefaultSearchProviderInstantURLPostParams[] =
- "default_search_provider.instant_url_post_params";
-
-// The string of post parameters (as understood by TemplateURLRef) the default
-// search provider uses for image search results by using POST.
-const char kDefaultSearchProviderImageURLPostParams[] =
- "default_search_provider.image_url_post_params";
-
-// The Favicon URL (as understood by TemplateURLRef) of the default search
-// provider.
-const char kDefaultSearchProviderIconURL[] =
- "default_search_provider.icon_url";
-
-// The input encoding (as understood by TemplateURLRef) supported by the default
-// search provider. The various encodings are separated by ';'
-const char kDefaultSearchProviderEncodings[] =
- "default_search_provider.encodings";
-
-// The name of the default search provider.
-const char kDefaultSearchProviderName[] = "default_search_provider.name";
-
-// The keyword of the default search provider.
-const char kDefaultSearchProviderKeyword[] = "default_search_provider.keyword";
-
-// The id of the default search provider.
-const char kDefaultSearchProviderID[] = "default_search_provider.id";
-
-// The prepopulate id of the default search provider.
-const char kDefaultSearchProviderPrepopulateID[] =
- "default_search_provider.prepopulate_id";
-
-// The alternate urls of the default search provider.
-const char kDefaultSearchProviderAlternateURLs[] =
- "default_search_provider.alternate_urls";
-
-// Search term placement query parameter for the default search provider.
-const char kDefaultSearchProviderSearchTermsReplacementKey[] =
- "default_search_provider.search_terms_replacement_key";
-
// The dictionary key used when the default search providers are given
// in the preferences file. Normally they are copied from the master
// preferences file.
diff --git a/chromium/components/search_engines/search_engines_pref_names.h b/chromium/components/search_engines/search_engines_pref_names.h
index 22f7e81bfad..0f07d015814 100644
--- a/chromium/components/search_engines/search_engines_pref_names.h
+++ b/chromium/components/search_engines/search_engines_pref_names.h
@@ -9,23 +9,6 @@ namespace prefs {
extern const char kSyncedDefaultSearchProviderGUID[];
extern const char kDefaultSearchProviderEnabled[];
-extern const char kDefaultSearchProviderSearchURL[];
-extern const char kDefaultSearchProviderSuggestURL[];
-extern const char kDefaultSearchProviderInstantURL[];
-extern const char kDefaultSearchProviderImageURL[];
-extern const char kDefaultSearchProviderNewTabURL[];
-extern const char kDefaultSearchProviderSearchURLPostParams[];
-extern const char kDefaultSearchProviderSuggestURLPostParams[];
-extern const char kDefaultSearchProviderInstantURLPostParams[];
-extern const char kDefaultSearchProviderImageURLPostParams[];
-extern const char kDefaultSearchProviderIconURL[];
-extern const char kDefaultSearchProviderEncodings[];
-extern const char kDefaultSearchProviderName[];
-extern const char kDefaultSearchProviderKeyword[];
-extern const char kDefaultSearchProviderID[];
-extern const char kDefaultSearchProviderPrepopulateID[];
-extern const char kDefaultSearchProviderAlternateURLs[];
-extern const char kDefaultSearchProviderSearchTermsReplacementKey[];
extern const char kSearchProviderOverrides[];
extern const char kSearchProviderOverridesVersion[];
extern const char kCountryIDAtInstall[];
diff --git a/chromium/components/search_engines/search_terms_data.cc b/chromium/components/search_engines/search_terms_data.cc
index 6de540664d0..f0ef783c6ec 100644
--- a/chromium/components/search_engines/search_terms_data.cc
+++ b/chromium/components/search_engines/search_terms_data.cc
@@ -54,8 +54,7 @@ std::string SearchTermsData::GetSuggestRequestIdentifier() const {
return std::string();
}
-std::string SearchTermsData::InstantExtendedEnabledParam(
- bool for_search) const {
+std::string SearchTermsData::InstantExtendedEnabledParam() const {
return std::string();
}
diff --git a/chromium/components/search_engines/search_terms_data.h b/chromium/components/search_engines/search_terms_data.h
index c91745e8a50..50f7ed2eb93 100644
--- a/chromium/components/search_engines/search_terms_data.h
+++ b/chromium/components/search_engines/search_terms_data.h
@@ -49,8 +49,9 @@ class SearchTermsData {
// This implementation returns the empty string.
virtual std::string GetSuggestRequestIdentifier() const;
- // Returns a string indicating whether InstantExtended is enabled.
- virtual std::string InstantExtendedEnabledParam(bool for_search) const;
+ // Returns a string indicating whether InstantExtended is enabled, suitable
+ // for adding as a query string param to the homepage or search requests.
+ virtual std::string InstantExtendedEnabledParam() const;
// Returns a string that will cause the search results page to update
// incrementally.
diff --git a/chromium/components/search_engines/template_url.cc b/chromium/components/search_engines/template_url.cc
index 89029652922..0a457d1632c 100644
--- a/chromium/components/search_engines/template_url.cc
+++ b/chromium/components/search_engines/template_url.cc
@@ -186,39 +186,15 @@ TemplateURLRef::SearchTermsArgs::~SearchTermsArgs() {
TemplateURLRef::SearchTermsArgs::ContextualSearchParams::
ContextualSearchParams()
: version(-1),
- start(base::string16::npos),
- end(base::string16::npos),
contextual_cards_version(0) {}
TemplateURLRef::SearchTermsArgs::ContextualSearchParams::ContextualSearchParams(
int version,
- const std::string& selection,
- const std::string& base_page_url,
- int contextual_cards_version)
+ int contextual_cards_version,
+ const std::string& home_country)
: version(version),
- start(base::string16::npos),
- end(base::string16::npos),
- selection(selection),
- base_page_url(base_page_url),
- contextual_cards_version(contextual_cards_version) {}
-
-TemplateURLRef::SearchTermsArgs::ContextualSearchParams::ContextualSearchParams(
- int version,
- size_t start,
- size_t end,
- const std::string& selection,
- const std::string& content,
- const std::string& base_page_url,
- const std::string& encoding,
- int contextual_cards_version)
- : version(version),
- start(start),
- end(end),
- selection(selection),
- content(content),
- base_page_url(base_page_url),
- encoding(encoding),
- contextual_cards_version(contextual_cards_version) {}
+ contextual_cards_version(contextual_cards_version),
+ home_country(home_country) {}
TemplateURLRef::SearchTermsArgs::ContextualSearchParams::ContextualSearchParams(
const ContextualSearchParams& other) = default;
@@ -967,11 +943,13 @@ std::string TemplateURLRef::HandleReplacements(
case GOOGLE_INSTANT_EXTENDED_ENABLED:
DCHECK(!i->is_post_param);
+ // Regular search requests don't use Instant, so only add the param for
+ // other types.
HandleReplacement(std::string(),
- search_terms_data.InstantExtendedEnabledParam(
- type_ == SEARCH),
- *i,
- &url);
+ type_ == SEARCH
+ ? std::string()
+ : search_terms_data.InstantExtendedEnabledParam(),
+ *i, &url);
break;
case GOOGLE_CONTEXTUAL_SEARCH_VERSION:
@@ -992,24 +970,12 @@ std::string TemplateURLRef::HandleReplacements(
search_terms_args.contextual_search_params;
std::vector<std::string> args;
- if (params.start != std::string::npos)
- args.push_back("ctxs_start=" + base::SizeTToString(params.start));
- if (params.end != std::string::npos)
- args.push_back("ctxs_end=" + base::SizeTToString(params.end));
-
- if (!params.selection.empty())
- args.push_back("q=" + params.selection);
- if (!params.content.empty())
- args.push_back("ctxs_content=" + params.content);
- if (!params.base_page_url.empty())
- args.push_back("ctxsl_url=" + params.base_page_url);
- if (!params.encoding.empty())
- args.push_back("ctxs_encoding=" + params.encoding);
-
if (params.contextual_cards_version > 0) {
args.push_back("ctxsl_coca=" +
base::IntToString(params.contextual_cards_version));
}
+ if (!params.home_country.empty())
+ args.push_back("ctxs_hc=" + params.home_country);
HandleReplacement(std::string(), base::JoinString(args, "&"), *i, &url);
break;
@@ -1253,7 +1219,6 @@ bool TemplateURL::MatchesData(const TemplateURL* t_url,
(t_url->image_url_post_params() == data->image_url_post_params) &&
(t_url->favicon_url() == data->favicon_url) &&
(t_url->safe_for_autoreplace() == data->safe_for_autoreplace) &&
- (t_url->show_in_default_list() == data->show_in_default_list) &&
(t_url->input_encodings() == data->input_encodings) &&
(t_url->alternate_urls() == data->alternate_urls) &&
(t_url->search_terms_replacement_key() ==
@@ -1266,12 +1231,6 @@ base::string16 TemplateURL::AdjustedShortNameForLocaleDirection() const {
return bidi_safe_short_name;
}
-bool TemplateURL::ShowInDefaultList(
- const SearchTermsData& search_terms_data) const {
- return data_.show_in_default_list &&
- url_ref_->SupportsReplacement(search_terms_data);
-}
-
bool TemplateURL::SupportsReplacement(
const SearchTermsData& search_terms_data) const {
return url_ref_->SupportsReplacement(search_terms_data);
diff --git a/chromium/components/search_engines/template_url.h b/chromium/components/search_engines/template_url.h
index e931a0ae6b1..d57aed22627 100644
--- a/chromium/components/search_engines/template_url.h
+++ b/chromium/components/search_engines/template_url.h
@@ -79,51 +79,28 @@ class TemplateURLRef {
struct ContextualSearchParams {
ContextualSearchParams();
- // Used when the content is sent in the HTTP header instead of as CGI
- // parameters.
- // TODO(donnd): Remove base_page_url and selection parameters once
- // they are logged from the HTTP header.
+ // Modern constructor, used when the content is sent in the HTTP header
+ // instead of as CGI parameters.
+ // The |home_country| is an ISO country code for the country that the user
+ // considers their permanent home (which may be different from the country
+ // they are currently visiting). Pass an empty string if none available.
ContextualSearchParams(int version,
- const std::string& selection,
- const std::string& base_page_url,
- int contextual_cards_version);
- // TODO(donnd): Delete constructor once Clank, iOS, and tests no
- // longer depend on it.
- ContextualSearchParams(int version,
- size_t start,
- size_t end,
- const std::string& selection,
- const std::string& content,
- const std::string& base_page_url,
- const std::string& encoding,
- int contextual_cards_version);
+ int contextual_cards_version,
+ const std::string& home_country);
ContextualSearchParams(const ContextualSearchParams& other);
~ContextualSearchParams();
// The version of contextual search.
int version;
- // Offset into the page content of the start of the user selection.
- size_t start;
-
- // Offset into the page content of the end of the user selection.
- size_t end;
-
- // The user selection.
- std::string selection;
-
- // The text including and surrounding the user selection.
- std::string content;
-
- // The URL of the page containing the user selection.
- std::string base_page_url;
-
- // The encoding of content.
- std::string encoding;
-
// The version of Contextual Cards data to request.
// A value of 0 indicates no data needed.
int contextual_cards_version;
+
+ // The locale of the user's home country in an ISO country code format,
+ // or an empty string if not available. This indicates where the user
+ // resides, not where they currently are.
+ std::string home_country;
};
// The search terms (query).
@@ -571,11 +548,6 @@ class TemplateURL {
const GURL& originating_url() const { return data_.originating_url; }
- bool show_in_default_list() const { return data_.show_in_default_list; }
- // Returns true if show_in_default_list() is true and this TemplateURL has a
- // TemplateURLRef that supports replacement.
- bool ShowInDefaultList(const SearchTermsData& search_terms_data) const;
-
bool safe_for_autoreplace() const { return data_.safe_for_autoreplace; }
const std::vector<std::string>& input_encodings() const {
@@ -586,6 +558,7 @@ class TemplateURL {
base::Time date_created() const { return data_.date_created; }
base::Time last_modified() const { return data_.last_modified; }
+ base::Time last_visited() const { return data_.last_visited; }
bool created_by_policy() const { return data_.created_by_policy; }
diff --git a/chromium/components/search_engines/template_url_data.cc b/chromium/components/search_engines/template_url_data.cc
index f415c7719fc..187d0a277c6 100644
--- a/chromium/components/search_engines/template_url_data.cc
+++ b/chromium/components/search_engines/template_url_data.cc
@@ -9,23 +9,71 @@
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
TemplateURLData::TemplateURLData()
- : show_in_default_list(false),
- safe_for_autoreplace(false),
+ : safe_for_autoreplace(false),
id(0),
date_created(base::Time::Now()),
last_modified(base::Time::Now()),
+ last_visited(base::Time()),
created_by_policy(false),
usage_count(0),
prepopulate_id(0),
sync_guid(base::GenerateGUID()),
keyword_(base::ASCIIToUTF16("dummy")),
- url_("x") {
-}
+ url_("x") {}
TemplateURLData::TemplateURLData(const TemplateURLData& other) = default;
+TemplateURLData::TemplateURLData(const base::string16& name,
+ const base::string16& keyword,
+ base::StringPiece search_url,
+ base::StringPiece suggest_url,
+ base::StringPiece instant_url,
+ base::StringPiece image_url,
+ base::StringPiece new_tab_url,
+ base::StringPiece contextual_search_url,
+ base::StringPiece search_url_post_params,
+ base::StringPiece suggest_url_post_params,
+ base::StringPiece instant_url_post_params,
+ base::StringPiece image_url_post_params,
+ base::StringPiece favicon_url,
+ base::StringPiece encoding,
+ const base::ListValue& alternate_urls_list,
+ base::StringPiece search_terms_replacement_key,
+ int prepopulate_id)
+ : suggestions_url(suggest_url.as_string()),
+ instant_url(instant_url.as_string()),
+ image_url(image_url.as_string()),
+ new_tab_url(new_tab_url.as_string()),
+ contextual_search_url(contextual_search_url.as_string()),
+ 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()),
+ image_url_post_params(image_url_post_params.as_string()),
+ favicon_url(GURL(favicon_url)),
+ safe_for_autoreplace(true),
+ id(0),
+ date_created(base::Time()),
+ last_modified(base::Time()),
+ created_by_policy(false),
+ usage_count(0),
+ prepopulate_id(prepopulate_id),
+ sync_guid(base::GenerateGUID()),
+ search_terms_replacement_key(search_terms_replacement_key.as_string()) {
+ SetShortName(name);
+ SetKeyword(keyword);
+ SetURL(search_url.as_string());
+ input_encodings.push_back(encoding.as_string());
+ for (size_t i = 0; i < alternate_urls_list.GetSize(); ++i) {
+ std::string alternate_url;
+ alternate_urls_list.GetString(i, &alternate_url);
+ DCHECK(!alternate_url.empty());
+ alternate_urls.push_back(alternate_url);
+ }
+}
+
TemplateURLData::~TemplateURLData() {
}
diff --git a/chromium/components/search_engines/template_url_data.h b/chromium/components/search_engines/template_url_data.h
index 11943b0ce67..93088a8dbdf 100644
--- a/chromium/components/search_engines/template_url_data.h
+++ b/chromium/components/search_engines/template_url_data.h
@@ -13,12 +13,40 @@
#include "components/search_engines/template_url_id.h"
#include "url/gurl.h"
+namespace base {
+class ListValue;
+}
+
// The data for the TemplateURL. Separating this into its own class allows most
// users to do SSA-style usage of TemplateURL: construct a TemplateURLData with
// whatever fields are desired, then create an immutable TemplateURL from it.
struct TemplateURLData {
TemplateURLData();
TemplateURLData(const TemplateURLData& other);
+ // Creates a TemplateURLData suitable for prepopulated engines.
+ // Note that unlike in the default constructor, |safe_for_autoreplace| will
+ // be set to true. date_created and last_modified will be set to null time
+ // value, instead of current time.
+ // StringPiece in arguments is used to pass const char* pointer members
+ // of PrepopulatedEngine structure which can be nullptr.
+ TemplateURLData(const base::string16& name,
+ const base::string16& keyword,
+ base::StringPiece search_url,
+ base::StringPiece suggest_url,
+ base::StringPiece instant_url,
+ base::StringPiece image_url,
+ base::StringPiece new_tab_url,
+ base::StringPiece contextual_search_url,
+ base::StringPiece search_url_post_params,
+ base::StringPiece suggest_url_post_params,
+ base::StringPiece instant_url_post_params,
+ base::StringPiece image_url_post_params,
+ base::StringPiece favicon_url,
+ base::StringPiece encoding,
+ const base::ListValue& alternate_urls_list,
+ base::StringPiece search_terms_replacement_key,
+ int prepopulate_id);
+
~TemplateURLData();
// A short description of the template. This is the name we show to the user
@@ -56,12 +84,6 @@ struct TemplateURLData {
// URL to the OSD file this came from. May be empty.
GURL originating_url;
- // Whether this TemplateURL is shown in the default list of search providers.
- // This is just a property and does not indicate whether the TemplateURL has a
- // TemplateURLRef that supports replacement. Use
- // TemplateURL::ShowInDefaultList() to test both.
- bool show_in_default_list;
-
// Whether it's safe for auto-modification code (the autogenerator and the
// code that imports data from other browsers) to replace the TemplateURL.
// This should be set to false for any TemplateURL the user edits, or any
@@ -88,6 +110,11 @@ struct TemplateURLData {
// NOTE: Like date_created above, this may be 0.
base::Time last_modified;
+ // Date when this TemplateURL was last visited.
+ //
+ // NOTE: This might be 0 if the TemplateURL has never been visited.
+ base::Time last_visited;
+
// True if this TemplateURL was automatically created by the administrator via
// group policy.
bool created_by_policy;
diff --git a/chromium/components/search_engines/template_url_data_util.cc b/chromium/components/search_engines/template_url_data_util.cc
new file mode 100644
index 00000000000..ab76c92ab57
--- /dev/null
+++ b/chromium/components/search_engines/template_url_data_util.cc
@@ -0,0 +1,244 @@
+// 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/search_engines/template_url_data_util.h"
+
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/search_engines/default_search_manager.h"
+#include "components/search_engines/prepopulated_engines.h"
+#include "components/search_engines/template_url_data.h"
+#include "url/gurl.h"
+
+std::unique_ptr<TemplateURLData> TemplateURLDataFromDictionary(
+ const base::DictionaryValue& dict) {
+ std::string search_url;
+ base::string16 keyword;
+ base::string16 short_name;
+ dict.GetString(DefaultSearchManager::kURL, &search_url);
+ dict.GetString(DefaultSearchManager::kKeyword, &keyword);
+ dict.GetString(DefaultSearchManager::kShortName, &short_name);
+ // Check required TemplateURLData fields first.
+ if (search_url.empty() || keyword.empty() || short_name.empty())
+ return std::unique_ptr<TemplateURLData>();
+
+ auto result = base::MakeUnique<TemplateURLData>();
+ result->SetKeyword(keyword);
+ result->SetURL(search_url);
+
+ std::string id;
+ dict.GetString(DefaultSearchManager::kID, &id);
+ base::StringToInt64(id, &result->id);
+
+ result->SetShortName(short_name);
+ dict.GetInteger(DefaultSearchManager::kPrepopulateID,
+ &result->prepopulate_id);
+ dict.GetString(DefaultSearchManager::kSyncGUID, &result->sync_guid);
+ dict.GetString(DefaultSearchManager::kSuggestionsURL,
+ &result->suggestions_url);
+
+ dict.GetString(DefaultSearchManager::kInstantURL, &result->instant_url);
+ dict.GetString(DefaultSearchManager::kImageURL, &result->image_url);
+ dict.GetString(DefaultSearchManager::kNewTabURL, &result->new_tab_url);
+ dict.GetString(DefaultSearchManager::kContextualSearchURL,
+ &result->contextual_search_url);
+ std::string favicon_url;
+ std::string originating_url;
+ dict.GetString(DefaultSearchManager::kFaviconURL, &favicon_url);
+ dict.GetString(DefaultSearchManager::kOriginatingURL, &originating_url);
+ result->favicon_url = GURL(favicon_url);
+ result->originating_url = GURL(originating_url);
+
+ dict.GetString(DefaultSearchManager::kSearchURLPostParams,
+ &result->search_url_post_params);
+ dict.GetString(DefaultSearchManager::kSuggestionsURLPostParams,
+ &result->suggestions_url_post_params);
+ dict.GetString(DefaultSearchManager::kInstantURLPostParams,
+ &result->instant_url_post_params);
+ dict.GetString(DefaultSearchManager::kImageURLPostParams,
+ &result->image_url_post_params);
+ dict.GetBoolean(DefaultSearchManager::kSafeForAutoReplace,
+ &result->safe_for_autoreplace);
+
+ std::string date_created_str;
+ std::string last_modified_str;
+ std::string last_visited_str;
+ dict.GetString(DefaultSearchManager::kDateCreated, &date_created_str);
+ dict.GetString(DefaultSearchManager::kLastModified, &last_modified_str);
+ dict.GetString(DefaultSearchManager::kLastVisited, &last_visited_str);
+
+ int64_t date_created = 0;
+ if (base::StringToInt64(date_created_str, &date_created))
+ result->date_created = base::Time::FromInternalValue(date_created);
+
+ int64_t last_modified = 0;
+ if (base::StringToInt64(last_modified_str, &last_modified))
+ result->last_modified = base::Time::FromInternalValue(last_modified);
+
+ int64_t last_visited = 0;
+ if (base::StringToInt64(last_visited_str, &last_visited))
+ result->last_visited = base::Time::FromInternalValue(last_visited);
+
+ dict.GetInteger(DefaultSearchManager::kUsageCount, &result->usage_count);
+
+ const base::ListValue* alternate_urls = nullptr;
+ if (dict.GetList(DefaultSearchManager::kAlternateURLs, &alternate_urls)) {
+ for (const auto& it : *alternate_urls) {
+ std::string alternate_url;
+ if (it->GetAsString(&alternate_url))
+ result->alternate_urls.push_back(std::move(alternate_url));
+ }
+ }
+
+ const base::ListValue* encodings = nullptr;
+ if (dict.GetList(DefaultSearchManager::kInputEncodings, &encodings)) {
+ for (const auto& it : *encodings) {
+ std::string encoding;
+ if (it->GetAsString(&encoding))
+ result->input_encodings.push_back(std::move(encoding));
+ }
+ }
+
+ dict.GetString(DefaultSearchManager::kSearchTermsReplacementKey,
+ &result->search_terms_replacement_key);
+ dict.GetBoolean(DefaultSearchManager::kCreatedByPolicy,
+ &result->created_by_policy);
+ return result;
+}
+
+std::unique_ptr<base::DictionaryValue> TemplateURLDataToDictionary(
+ const TemplateURLData& data) {
+ auto url_dict = base::MakeUnique<base::DictionaryValue>();
+ url_dict->SetString(DefaultSearchManager::kID, base::Int64ToString(data.id));
+ url_dict->SetString(DefaultSearchManager::kShortName, data.short_name());
+ url_dict->SetString(DefaultSearchManager::kKeyword, data.keyword());
+ url_dict->SetInteger(DefaultSearchManager::kPrepopulateID,
+ data.prepopulate_id);
+ url_dict->SetString(DefaultSearchManager::kSyncGUID, data.sync_guid);
+
+ url_dict->SetString(DefaultSearchManager::kURL, data.url());
+ url_dict->SetString(DefaultSearchManager::kSuggestionsURL,
+ data.suggestions_url);
+ url_dict->SetString(DefaultSearchManager::kInstantURL, data.instant_url);
+ url_dict->SetString(DefaultSearchManager::kImageURL, data.image_url);
+ url_dict->SetString(DefaultSearchManager::kNewTabURL, data.new_tab_url);
+ url_dict->SetString(DefaultSearchManager::kContextualSearchURL,
+ data.contextual_search_url);
+ url_dict->SetString(DefaultSearchManager::kFaviconURL,
+ data.favicon_url.spec());
+ url_dict->SetString(DefaultSearchManager::kOriginatingURL,
+ data.originating_url.spec());
+
+ url_dict->SetString(DefaultSearchManager::kSearchURLPostParams,
+ data.search_url_post_params);
+ url_dict->SetString(DefaultSearchManager::kSuggestionsURLPostParams,
+ data.suggestions_url_post_params);
+ url_dict->SetString(DefaultSearchManager::kInstantURLPostParams,
+ data.instant_url_post_params);
+ url_dict->SetString(DefaultSearchManager::kImageURLPostParams,
+ data.image_url_post_params);
+
+ url_dict->SetBoolean(DefaultSearchManager::kSafeForAutoReplace,
+ data.safe_for_autoreplace);
+
+ url_dict->SetString(DefaultSearchManager::kDateCreated,
+ base::Int64ToString(data.date_created.ToInternalValue()));
+ url_dict->SetString(
+ DefaultSearchManager::kLastModified,
+ base::Int64ToString(data.last_modified.ToInternalValue()));
+ url_dict->SetString(
+ DefaultSearchManager::kLastVisited,
+ base::Int64ToString(data.last_visited.ToInternalValue()));
+ url_dict->SetInteger(DefaultSearchManager::kUsageCount, data.usage_count);
+
+ auto alternate_urls = base::MakeUnique<base::ListValue>();
+ for (const auto& alternate_url : data.alternate_urls)
+ alternate_urls->AppendString(alternate_url);
+
+ url_dict->Set(DefaultSearchManager::kAlternateURLs,
+ std::move(alternate_urls));
+
+ auto encodings = base::MakeUnique<base::ListValue>();
+ for (const auto& input_encoding : data.input_encodings)
+ encodings->AppendString(input_encoding);
+ url_dict->Set(DefaultSearchManager::kInputEncodings, std::move(encodings));
+
+ url_dict->SetString(DefaultSearchManager::kSearchTermsReplacementKey,
+ data.search_terms_replacement_key);
+ url_dict->SetBoolean(DefaultSearchManager::kCreatedByPolicy,
+ data.created_by_policy);
+ return url_dict;
+}
+
+std::unique_ptr<TemplateURLData> TemplateURLDataFromPrepopulatedEngine(
+ const TemplateURLPrepopulateData::PrepopulatedEngine& engine) {
+ base::ListValue alternate_urls;
+ if (engine.alternate_urls) {
+ for (size_t i = 0; i < engine.alternate_urls_size; ++i)
+ alternate_urls.AppendString(std::string(engine.alternate_urls[i]));
+ }
+
+ return base::MakeUnique<TemplateURLData>(
+ 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);
+}
+
+std::unique_ptr<TemplateURLData> TemplateURLDataFromOverrideDictionary(
+ const base::DictionaryValue& engine) {
+ base::string16 name;
+ base::string16 keyword;
+ std::string search_url;
+ std::string favicon_url;
+ std::string encoding;
+ int id = -1;
+ // The following fields are required for each search engine configuration.
+ if (engine.GetString("name", &name) && !name.empty() &&
+ engine.GetString("keyword", &keyword) && !keyword.empty() &&
+ engine.GetString("search_url", &search_url) && !search_url.empty() &&
+ engine.GetString("favicon_url", &favicon_url) && !favicon_url.empty() &&
+ engine.GetString("encoding", &encoding) && !encoding.empty() &&
+ engine.GetInteger("id", &id)) {
+ // These fields are optional.
+ std::string suggest_url;
+ std::string instant_url;
+ std::string image_url;
+ std::string new_tab_url;
+ std::string contextual_search_url;
+ std::string search_url_post_params;
+ std::string suggest_url_post_params;
+ std::string instant_url_post_params;
+ std::string image_url_post_params;
+ base::ListValue empty_list;
+ const base::ListValue* alternate_urls = &empty_list;
+ std::string search_terms_replacement_key;
+ engine.GetString("suggest_url", &suggest_url);
+ engine.GetString("instant_url", &instant_url);
+ engine.GetString("image_url", &image_url);
+ engine.GetString("new_tab_url", &new_tab_url);
+ engine.GetString("contextual_search_url", &contextual_search_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);
+ engine.GetString("image_url_post_params", &image_url_post_params);
+ engine.GetList("alternate_urls", &alternate_urls);
+ engine.GetString("search_terms_replacement_key",
+ &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,
+ suggest_url_post_params, instant_url_post_params, image_url_post_params,
+ favicon_url, encoding, *alternate_urls, search_terms_replacement_key,
+ id);
+ }
+ return std::unique_ptr<TemplateURLData>();
+}
diff --git a/chromium/components/search_engines/template_url_data_util.h b/chromium/components/search_engines/template_url_data_util.h
new file mode 100644
index 00000000000..f99ae052cd9
--- /dev/null
+++ b/chromium/components/search_engines/template_url_data_util.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_SEARCH_ENGINES_TEMPLATE_URL_DATA_UTIL_H_
+#define COMPONENTS_SEARCH_ENGINES_TEMPLATE_URL_DATA_UTIL_H_
+
+#include <memory>
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace TemplateURLPrepopulateData {
+struct PrepopulatedEngine;
+}
+
+struct TemplateURLData;
+
+// Deserializes a TemplateURLData from |dict|.
+std::unique_ptr<TemplateURLData> TemplateURLDataFromDictionary(
+ const base::DictionaryValue& dict);
+
+// Serializes a TemplateURLData to |dict|.
+std::unique_ptr<base::DictionaryValue> TemplateURLDataToDictionary(
+ const TemplateURLData& turl_data);
+
+// Create TemplateURLData structure from PrepopulatedEngine structure.
+std::unique_ptr<TemplateURLData> TemplateURLDataFromPrepopulatedEngine(
+ const TemplateURLPrepopulateData::PrepopulatedEngine& engine);
+
+// Deserializes a TemplateURLData from |dict| as stored in
+// kSearchProviderOverrides pref. The field names in |dict| differ from those
+// used in the To/FromDictionary functions above for historical reasons.
+// TODO(a-v-y) Migrate to single TemplateURLData serialization format.
+std::unique_ptr<TemplateURLData> TemplateURLDataFromOverrideDictionary(
+ const base::DictionaryValue& engine);
+
+#endif // COMPONENTS_SEARCH_ENGINES_TEMPLATE_URL_DATA_UTIL_H_
diff --git a/chromium/components/search_engines/template_url_fetcher.cc b/chromium/components/search_engines/template_url_fetcher.cc
index 6274531e2c5..cf972d0eeb9 100644
--- a/chromium/components/search_engines/template_url_fetcher.cc
+++ b/chromium/components/search_engines/template_url_fetcher.cc
@@ -112,7 +112,7 @@ void TemplateURLFetcher::RequestDelegate::OnURLFetchComplete(
}
template_url_ = TemplateURLParser::Parse(
- fetcher_->template_url_service_->search_terms_data(), false, data.data(),
+ fetcher_->template_url_service_->search_terms_data(), data.data(),
data.length(), nullptr);
if (!template_url_.get() ||
!template_url_->url_ref().SupportsReplacement(
diff --git a/chromium/components/search_engines/template_url_parser.cc b/chromium/components/search_engines/template_url_parser.cc
index e3eec92f67d..9d051056be5 100644
--- a/chromium/components/search_engines/template_url_parser.cc
+++ b/chromium/components/search_engines/template_url_parser.cc
@@ -146,8 +146,7 @@ class TemplateURLParsingContext {
// the resulting URL was not HTTP[S], a name wasn't supplied, a resulting
// TemplateURLRef was invalid, etc.).
std::unique_ptr<TemplateURL> GetTemplateURL(
- const SearchTermsData& search_terms_data,
- bool show_in_default_list);
+ const SearchTermsData& search_terms_data);
private:
// Key is UTF8 encoded.
@@ -302,8 +301,7 @@ void TemplateURLParsingContext::CharactersImpl(void* ctx,
}
std::unique_ptr<TemplateURL> TemplateURLParsingContext::GetTemplateURL(
- const SearchTermsData& search_terms_data,
- bool show_in_default_list) {
+ 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()) ||
@@ -323,8 +321,6 @@ std::unique_ptr<TemplateURL> TemplateURLParsingContext::GetTemplateURL(
if (!has_custom_keyword_)
data_.SetKeyword(TemplateURL::GenerateKeyword(search_url));
- data_.show_in_default_list = show_in_default_list;
-
// Bail if the search URL is empty or if either TemplateURLRef is invalid.
std::unique_ptr<TemplateURL> template_url =
base::MakeUnique<TemplateURL>(data_);
@@ -495,7 +491,6 @@ TemplateURLParsingContext::ElementType
// static
std::unique_ptr<TemplateURL> TemplateURLParser::Parse(
const SearchTermsData& search_terms_data,
- bool show_in_default_list,
const char* data,
size_t length,
TemplateURLParser::ParameterFilter* param_filter) {
@@ -514,6 +509,5 @@ std::unique_ptr<TemplateURL> TemplateURLParser::Parse(
static_cast<int>(length));
xmlSubstituteEntitiesDefault(last_sub_entities_value);
- return error ? nullptr : context.GetTemplateURL(search_terms_data,
- show_in_default_list);
+ return error ? nullptr : context.GetTemplateURL(search_terms_data);
}
diff --git a/chromium/components/search_engines/template_url_parser.h b/chromium/components/search_engines/template_url_parser.h
index a878cc95dd3..c381f7a02cd 100644
--- a/chromium/components/search_engines/template_url_parser.h
+++ b/chromium/components/search_engines/template_url_parser.h
@@ -40,7 +40,6 @@ class TemplateURLParser {
// the URL is not modified.
static std::unique_ptr<TemplateURL> Parse(
const SearchTermsData& search_terms_data,
- bool show_in_default_list,
const char* data,
size_t length,
ParameterFilter* parameter_filter);
diff --git a/chromium/components/search_engines/template_url_prepopulate_data.cc b/chromium/components/search_engines/template_url_prepopulate_data.cc
index 72574064b75..40604f5f8a4 100644
--- a/chromium/components/search_engines/template_url_prepopulate_data.cc
+++ b/chromium/components/search_engines/template_url_prepopulate_data.cc
@@ -21,6 +21,7 @@
#include "components/search_engines/prepopulated_engines.h"
#include "components/search_engines/search_engines_pref_names.h"
#include "components/search_engines/template_url_data.h"
+#include "components/search_engines/template_url_data_util.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/gurl.h"
@@ -536,12 +537,16 @@ int CountryCharsToCountryIDWithUpdate(char c1, char c2) {
return CountryCharsToCountryID(c1, c2);
}
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+
int CountryStringToCountryID(const std::string& country) {
return (country.length() == 2)
? CountryCharsToCountryIDWithUpdate(country[0], country[1])
: kCountryIDUnknown;
}
+#endif
+
#if defined(OS_WIN)
// For reference, a list of GeoIDs can be found at
@@ -972,59 +977,10 @@ std::vector<std::unique_ptr<TemplateURLData>> GetPrepopulationSetFromCountryID(
std::vector<std::unique_ptr<TemplateURLData>> t_urls;
for (size_t i = 0; i < num_engines; ++i)
- t_urls.push_back(MakeTemplateURLDataFromPrepopulatedEngine(*engines[i]));
+ t_urls.push_back(TemplateURLDataFromPrepopulatedEngine(*engines[i]));
return t_urls;
}
-std::unique_ptr<TemplateURLData> MakePrepopulatedTemplateURLData(
- const base::string16& name,
- const base::string16& keyword,
- const base::StringPiece& search_url,
- const base::StringPiece& suggest_url,
- const base::StringPiece& instant_url,
- const base::StringPiece& image_url,
- const base::StringPiece& new_tab_url,
- const base::StringPiece& contextual_search_url,
- const base::StringPiece& search_url_post_params,
- const base::StringPiece& suggest_url_post_params,
- const base::StringPiece& instant_url_post_params,
- const base::StringPiece& image_url_post_params,
- const base::StringPiece& favicon_url,
- const base::StringPiece& encoding,
- const base::ListValue& alternate_urls,
- const base::StringPiece& search_terms_replacement_key,
- int id) {
- std::unique_ptr<TemplateURLData> data(new TemplateURLData);
-
- data->SetShortName(name);
- data->SetKeyword(keyword);
- data->SetURL(search_url.as_string());
- data->suggestions_url = suggest_url.as_string();
- data->instant_url = instant_url.as_string();
- data->image_url = image_url.as_string();
- data->new_tab_url = new_tab_url.as_string();
- data->contextual_search_url = contextual_search_url.as_string();
- data->search_url_post_params = search_url_post_params.as_string();
- data->suggestions_url_post_params = suggest_url_post_params.as_string();
- data->instant_url_post_params = instant_url_post_params.as_string();
- data->image_url_post_params = image_url_post_params.as_string();
- data->favicon_url = GURL(favicon_url);
- data->show_in_default_list = true;
- data->safe_for_autoreplace = true;
- data->input_encodings.push_back(encoding.as_string());
- data->date_created = base::Time();
- data->last_modified = base::Time();
- data->prepopulate_id = id;
- for (size_t i = 0; i < alternate_urls.GetSize(); ++i) {
- std::string alternate_url;
- alternate_urls.GetString(i, &alternate_url);
- DCHECK(!alternate_url.empty());
- data->alternate_urls.push_back(alternate_url);
- }
- data->search_terms_replacement_key = search_terms_replacement_key.as_string();
- return data;
-}
-
std::vector<std::unique_ptr<TemplateURLData>> GetPrepopulatedTemplateURLData(
PrefService* prefs) {
std::vector<std::unique_ptr<TemplateURLData>> t_urls;
@@ -1038,52 +994,10 @@ std::vector<std::unique_ptr<TemplateURLData>> GetPrepopulatedTemplateURLData(
size_t num_engines = list->GetSize();
for (size_t i = 0; i != num_engines; ++i) {
const base::DictionaryValue* engine;
- base::string16 name;
- base::string16 keyword;
- std::string search_url;
- std::string favicon_url;
- std::string encoding;
- int id = -1;
- // The following fields are required for each search engine configuration.
- if (list->GetDictionary(i, &engine) &&
- engine->GetString("name", &name) && !name.empty() &&
- engine->GetString("keyword", &keyword) && !keyword.empty() &&
- engine->GetString("search_url", &search_url) && !search_url.empty() &&
- engine->GetString("favicon_url", &favicon_url) &&
- !favicon_url.empty() &&
- engine->GetString("encoding", &encoding) && !encoding.empty() &&
- engine->GetInteger("id", &id)) {
- // These fields are optional.
- std::string suggest_url;
- std::string instant_url;
- std::string image_url;
- std::string new_tab_url;
- std::string contextual_search_url;
- std::string search_url_post_params;
- std::string suggest_url_post_params;
- std::string instant_url_post_params;
- std::string image_url_post_params;
- base::ListValue empty_list;
- const base::ListValue* alternate_urls = &empty_list;
- std::string search_terms_replacement_key;
- engine->GetString("suggest_url", &suggest_url);
- engine->GetString("instant_url", &instant_url);
- engine->GetString("image_url", &image_url);
- engine->GetString("new_tab_url", &new_tab_url);
- engine->GetString("contextual_search_url", &contextual_search_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);
- engine->GetString("image_url_post_params", &image_url_post_params);
- engine->GetList("alternate_urls", &alternate_urls);
- engine->GetString("search_terms_replacement_key",
- &search_terms_replacement_key);
- t_urls.push_back(MakePrepopulatedTemplateURLData(
- name, keyword, search_url, suggest_url, instant_url, image_url,
- new_tab_url, contextual_search_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));
+ if (list->GetDictionary(i, &engine)) {
+ auto t_url = TemplateURLDataFromOverrideDictionary(*engine);
+ if (t_url)
+ t_urls.push_back(std::move(t_url));
}
}
return t_urls;
@@ -1118,7 +1032,8 @@ std::vector<std::unique_ptr<TemplateURLData>> GetPrepopulatedEngines(
size_t* default_search_provider_index) {
// If there is a set of search engines in the preferences file, it overrides
// the built-in set.
- *default_search_provider_index = 0;
+ if (default_search_provider_index)
+ *default_search_provider_index = 0;
std::vector<std::unique_ptr<TemplateURLData>> t_urls =
GetPrepopulatedTemplateURLData(prefs);
if (!t_urls.empty())
@@ -1128,6 +1043,7 @@ std::vector<std::unique_ptr<TemplateURLData>> GetPrepopulatedEngines(
}
#if defined(OS_ANDROID)
+
std::vector<std::unique_ptr<TemplateURLData>> GetLocalPrepopulatedEngines(
const std::string& locale,
PrefService* prefs) {
@@ -1139,6 +1055,7 @@ std::vector<std::unique_ptr<TemplateURLData>> GetLocalPrepopulatedEngines(
return GetPrepopulationSetFromCountryID(country_id);
}
+
#endif
std::vector<const PrepopulatedEngine*> GetAllPrepopulatedEngines() {
@@ -1146,24 +1063,6 @@ std::vector<const PrepopulatedEngine*> GetAllPrepopulatedEngines() {
std::end(kAllEngines));
}
-std::unique_ptr<TemplateURLData> MakeTemplateURLDataFromPrepopulatedEngine(
- const PrepopulatedEngine& engine) {
- base::ListValue alternate_urls;
- if (engine.alternate_urls) {
- for (size_t i = 0; i < engine.alternate_urls_size; ++i)
- alternate_urls.AppendString(std::string(engine.alternate_urls[i]));
- }
-
- return MakePrepopulatedTemplateURLData(
- 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);
-}
-
void ClearPrepopulatedEnginesInPrefs(PrefService* prefs) {
if (!prefs)
return;
diff --git a/chromium/components/search_engines/template_url_prepopulate_data.h b/chromium/components/search_engines/template_url_prepopulate_data.h
index 4a218f73f1f..17c81cd9ff4 100644
--- a/chromium/components/search_engines/template_url_prepopulate_data.h
+++ b/chromium/components/search_engines/template_url_prepopulate_data.h
@@ -36,8 +36,8 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
int GetDataVersion(PrefService* prefs);
// Returns the prepopulated URLs for the current country.
-// |default_search_provider_index| is set to the index of the default search
-// provider within the returned vector.
+// If |default_search_provider_index| is non-null, it is set to the index of the
+// default search provider within the returned vector.
std::vector<std::unique_ptr<TemplateURLData>> GetPrepopulatedEngines(
PrefService* prefs,
size_t* default_search_provider_index);
@@ -55,10 +55,6 @@ std::vector<std::unique_ptr<TemplateURLData>> GetLocalPrepopulatedEngines(
// Returns all prepopulated engines for all locales. Used only by tests.
std::vector<const PrepopulatedEngine*> GetAllPrepopulatedEngines();
-// Returns a TemplateURLData for the specified prepopulated engine.
-std::unique_ptr<TemplateURLData> MakeTemplateURLDataFromPrepopulatedEngine(
- const PrepopulatedEngine& engine);
-
// Removes prepopulated engines and their version stored in user prefs.
void ClearPrepopulatedEnginesInPrefs(PrefService* prefs);
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 e57a940cf89..de612204469 100644
--- a/chromium/components/search_engines/template_url_prepopulate_data_unittest.cc
+++ b/chromium/components/search_engines/template_url_prepopulate_data_unittest.cc
@@ -14,12 +14,13 @@
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "components/google/core/browser/google_switches.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
#include "components/search_engines/prepopulated_engines.h"
#include "components/search_engines/search_engines_pref_names.h"
#include "components/search_engines/search_terms_data.h"
#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/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::ASCIIToUTF16;
@@ -45,7 +46,7 @@ class TemplateURLPrepopulateDataTest : public testing::Test {
}
protected:
- user_prefs::TestingPrefServiceSyncable prefs_;
+ sync_preferences::TestingPrefServiceSyncable prefs_;
};
// Verifies the set of prepopulate data doesn't contain entries with duplicate
@@ -104,10 +105,8 @@ TEST_F(TemplateURLPrepopulateDataTest, UniqueIDs) {
for (size_t i = 0; i < arraysize(kCountryIds); ++i) {
prefs_.SetInteger(prefs::kCountryIDAtInstall, kCountryIds[i]);
- size_t default_index;
std::vector<std::unique_ptr<TemplateURLData>> urls =
- TemplateURLPrepopulateData::GetPrepopulatedEngines(&prefs_,
- &default_index);
+ TemplateURLPrepopulateData::GetPrepopulatedEngines(&prefs_, nullptr);
std::set<int> unique_ids;
for (size_t turl_i = 0; turl_i < urls.size(); ++turl_i) {
ASSERT_TRUE(unique_ids.find(urls[turl_i]->prepopulate_id) ==
@@ -153,6 +152,9 @@ TEST_F(TemplateURLPrepopulateDataTest, ProvidersFromPrefs) {
EXPECT_TRUE(t_urls[0]->instant_url.empty());
EXPECT_EQ(0u, t_urls[0]->alternate_urls.size());
EXPECT_TRUE(t_urls[0]->search_terms_replacement_key.empty());
+ EXPECT_TRUE(t_urls[0]->safe_for_autoreplace);
+ EXPECT_TRUE(t_urls[0]->date_created.is_null());
+ EXPECT_TRUE(t_urls[0]->last_modified.is_null());
// Test the optional settings too.
entry->SetString("suggest_url", "http://foo.com/suggest?q={searchTerms}");
@@ -272,6 +274,9 @@ TEST_F(TemplateURLPrepopulateDataTest, ProvidersFromPrepopulated) {
ASSERT_FALSE(GetHostFromTemplateURLData(*t_urls[i]).empty());
ASSERT_FALSE(t_urls[i]->input_encodings.empty());
EXPECT_GT(t_urls[i]->prepopulate_id, 0);
+ EXPECT_TRUE(t_urls[0]->safe_for_autoreplace);
+ EXPECT_TRUE(t_urls[0]->date_created.is_null());
+ EXPECT_TRUE(t_urls[0]->last_modified.is_null());
}
// Ensures the default URL is Google and has the optional fields filled.
@@ -357,8 +362,7 @@ TEST_F(TemplateURLPrepopulateDataTest, GetEngineTypeForAllPrepopulatedEngines) {
TemplateURLPrepopulateData::GetAllPrepopulatedEngines();
for (const PrepopulatedEngine* engine : all_engines) {
std::unique_ptr<TemplateURLData> data =
- TemplateURLPrepopulateData::MakeTemplateURLDataFromPrepopulatedEngine(
- *engine);
+ TemplateURLDataFromPrepopulatedEngine(*engine);
EXPECT_EQ(engine->type,
TemplateURL(*data).GetEngineType(SearchTermsData()));
}
diff --git a/chromium/components/search_engines/template_url_service.cc b/chromium/components/search_engines/template_url_service.cc
index e3bc412c2b4..0e77d27379f 100644
--- a/chromium/components/search_engines/template_url_service.cc
+++ b/chromium/components/search_engines/template_url_service.cc
@@ -24,7 +24,7 @@
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
#include "components/search_engines/search_engines_pref_names.h"
#include "components/search_engines/search_host_to_urls_map.h"
#include "components/search_engines/search_terms_data.h"
@@ -230,7 +230,7 @@ TemplateURLService::TemplateURLService(
const scoped_refptr<KeywordWebDataService>& web_data_service,
std::unique_ptr<TemplateURLServiceClient> client,
GoogleURLTracker* google_url_tracker,
- rappor::RapporService* rappor_service,
+ rappor::RapporServiceImpl* rappor_service,
const base::Closure& dsp_change_callback)
: prefs_(prefs),
search_terms_data_(std::move(search_terms_data)),
@@ -300,38 +300,6 @@ void TemplateURLService::RegisterProfilePrefs(
std::string(),
flags);
registry->RegisterBooleanPref(prefs::kDefaultSearchProviderEnabled, true);
- registry->RegisterStringPref(prefs::kDefaultSearchProviderName,
- std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderID, std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderPrepopulateID,
- std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderSuggestURL,
- std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderSearchURL,
- std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderInstantURL,
- std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderImageURL,
- std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderNewTabURL,
- std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderSearchURLPostParams,
- std::string());
- registry->RegisterStringPref(
- prefs::kDefaultSearchProviderSuggestURLPostParams, std::string());
- registry->RegisterStringPref(
- prefs::kDefaultSearchProviderInstantURLPostParams, std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderImageURLPostParams,
- std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderKeyword,
- std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderIconURL,
- std::string());
- registry->RegisterStringPref(prefs::kDefaultSearchProviderEncodings,
- std::string());
- registry->RegisterListPref(prefs::kDefaultSearchProviderAlternateURLs);
- registry->RegisterStringPref(
- prefs::kDefaultSearchProviderSearchTermsReplacementKey, std::string());
}
// static
@@ -395,6 +363,17 @@ bool TemplateURLService::CanAddAutogeneratedKeyword(
CanAddAutogeneratedKeywordForHost(url.host());
}
+bool TemplateURLService::IsPrepopulatedOrCreatedByPolicy(
+ const TemplateURL* t_url) {
+ return (t_url->prepopulate_id() > 0 || t_url->created_by_policy()) &&
+ t_url->SupportsReplacement(search_terms_data());
+}
+
+bool TemplateURLService::ShowInDefaultList(const TemplateURL* t_url) {
+ return t_url == default_search_provider_ ||
+ IsPrepopulatedOrCreatedByPolicy(t_url);
+}
+
void TemplateURLService::AddMatchingKeywords(
const base::string16& prefix,
bool supports_replacement_only,
@@ -478,8 +457,6 @@ TemplateURL* TemplateURLService::AddExtensionControlledTURL(
DCHECK_EQ(kInvalidTemplateURLID, template_url->id());
DCHECK(info);
DCHECK_NE(TemplateURL::NORMAL, template_url->type());
- DCHECK_EQ(info->wants_to_be_default_engine,
- template_url->show_in_default_list());
DCHECK(
!FindTemplateURLForExtension(info->extension_id, template_url->type()));
template_url->extension_info_.swap(info);
@@ -670,10 +647,8 @@ void TemplateURLService::RepairPrepopulatedSearchEngines() {
default_search_provider_ = nullptr;
}
- size_t default_search_provider_index = 0;
std::vector<std::unique_ptr<TemplateURLData>> prepopulated_urls =
- TemplateURLPrepopulateData::GetPrepopulatedEngines(
- prefs_, &default_search_provider_index);
+ TemplateURLPrepopulateData::GetPrepopulatedEngines(prefs_, nullptr);
DCHECK(!prepopulated_urls.empty());
ActionsFromPrepopulateData actions(CreateActionsFromCurrentPrepopulateData(
&prepopulated_urls, template_urls_, default_search_provider_));
@@ -855,9 +830,8 @@ void TemplateURLService::OnWebDataServiceRequestDone(
SEARCH_ENGINE_MAX);
if (rappor_service_) {
- rappor_service_->RecordSample(
- "Search.DefaultSearchProvider",
- rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
+ rappor_service_->RecordSampleString(
+ "Search.DefaultSearchProvider", rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
net::registry_controlled_domains::GetDomainAndRegistry(
default_search_provider_->url_ref().GetHost(search_terms_data()),
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
@@ -990,8 +964,7 @@ syncer::SyncError TemplateURLService::ProcessSyncChanges(
TemplateURLData data(existing_turl->data());
data.SetKeyword(updated_keyword);
TemplateURL new_turl(data);
- if (UpdateNoNotify(existing_turl, new_turl))
- NotifyObservers();
+ Update(existing_turl, new_turl);
syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(new_turl);
new_changes.push_back(syncer::SyncChange(FROM_HERE,
@@ -1039,10 +1012,8 @@ syncer::SyncError TemplateURLService::ProcessSyncChanges(
ResolveSyncKeywordConflict(turl.get(), existing_keyword_turl,
&new_changes);
}
- if (UpdateNoNotify(existing_turl, *turl)) {
- NotifyObservers();
+ if (Update(existing_turl, *turl))
MaybeUpdateDSEAfterSync(existing_turl);
- }
} else {
// We've unexpectedly received an ACTION_INVALID.
error = sync_error_factory_->CreateAndUploadError(
@@ -1139,8 +1110,7 @@ syncer::SyncMergeResult TemplateURLService::MergeDataAndStartSyncing(
// TemplateURLID and the TemplateURL may have to be reparsed. This
// also makes the local data's last_modified timestamp equal to Sync's,
// avoiding an Update on the next MergeData call.
- if (UpdateNoNotify(local_turl, *sync_turl))
- NotifyObservers();
+ Update(local_turl, *sync_turl);
merge_result.set_num_items_modified(
merge_result.num_items_modified() + 1);
} else if (sync_turl->last_modified() < local_turl->last_modified()) {
@@ -1245,7 +1215,6 @@ syncer::SyncData TemplateURLService::CreateSyncDataFromTemplateURL(
se_specifics->set_date_created(turl.date_created().ToInternalValue());
se_specifics->set_input_encodings(
base::JoinString(turl.input_encodings(), ";"));
- se_specifics->set_show_in_default_list(turl.show_in_default_list());
se_specifics->set_suggestions_url(turl.suggestions_url());
se_specifics->set_prepopulate_id(turl.prepopulate_id());
se_specifics->set_instant_url(turl.instant_url());
@@ -1325,7 +1294,6 @@ TemplateURLService::CreateTemplateURLFromTemplateURLAndSyncData(
data.instant_url_post_params = specifics.instant_url_post_params();
data.image_url_post_params = specifics.image_url_post_params();
data.favicon_url = GURL(specifics.favicon_url());
- data.show_in_default_list = specifics.show_in_default_list();
data.safe_for_autoreplace = specifics.safe_for_autoreplace();
data.input_encodings = base::SplitString(
specifics.input_encodings(), ";",
@@ -1642,8 +1610,7 @@ bool TemplateURLService::CanAddAutogeneratedKeywordForHost(
}
bool TemplateURLService::CanReplace(const TemplateURL* t_url) {
- return (t_url != default_search_provider_ && !t_url->show_in_default_list() &&
- t_url->safe_for_autoreplace());
+ return !ShowInDefaultList(t_url) && t_url->safe_for_autoreplace();
}
TemplateURL* TemplateURLService::FindNonExtensionTemplateURLForKeyword(
@@ -1736,6 +1703,14 @@ bool TemplateURLService::UpdateNoNotify(TemplateURL* existing_turl,
return true;
}
+bool TemplateURLService::Update(TemplateURL* existing_turl,
+ const TemplateURL& new_values) {
+ const bool updated = UpdateNoNotify(existing_turl, new_values);
+ if (updated)
+ NotifyObservers();
+ return updated;
+}
+
// static
void TemplateURLService::UpdateTemplateURLIfPrepopulated(
TemplateURL* template_url,
@@ -1744,10 +1719,8 @@ void TemplateURLService::UpdateTemplateURLIfPrepopulated(
if (template_url->prepopulate_id() == 0)
return;
- size_t default_search_index;
std::vector<std::unique_ptr<TemplateURLData>> prepopulated_urls =
- TemplateURLPrepopulateData::GetPrepopulatedEngines(prefs,
- &default_search_index);
+ TemplateURLPrepopulateData::GetPrepopulatedEngines(prefs, nullptr);
for (const auto& url : prepopulated_urls) {
if (url->prepopulate_id == prepopulate_id) {
@@ -1776,6 +1749,7 @@ void TemplateURLService::UpdateKeywordSearchTermsForURL(
if (!urls_for_host)
return;
+ TemplateURL* visited_url = nullptr;
for (TemplateURLSet::const_iterator i = urls_for_host->begin();
i != urls_for_host->end(); ++i) {
base::string16 search_terms;
@@ -1792,8 +1766,21 @@ void TemplateURLService::UpdateKeywordSearchTermsForURL(
client_->SetKeywordSearchTermsForURL(
details.url, (*i)->id(), search_terms);
}
+ // Caches the matched TemplateURL so its last_visited could be updated
+ // later after iteration.
+ // Note: UpdateNoNotify() will replace the entry from the container of
+ // this iterator, so update here directly will cause an error about it.
+ visited_url = *i;
}
}
+ if (visited_url)
+ UpdateTemplateURLVisitTime(visited_url);
+}
+
+void TemplateURLService::UpdateTemplateURLVisitTime(TemplateURL* url) {
+ TemplateURLData data(url->data());
+ data.last_visited = clock_->Now();
+ Update(url, TemplateURL(data));
}
void TemplateURLService::AddTabToSearchVisit(const TemplateURL& t_url) {
@@ -1986,7 +1973,6 @@ bool TemplateURLService::ApplyDefaultSearchChangeNoMetrics(
FindPrepopulatedTemplateURL(data->prepopulate_id);
}
TemplateURLData new_data(*data);
- new_data.show_in_default_list = true;
if (default_search_provider_) {
UpdateNoNotify(default_search_provider_, TemplateURL(new_data));
} else {
@@ -2268,8 +2254,7 @@ void TemplateURLService::ResolveSyncKeywordConflict(
// Update |applied_sync_turl| in the local model with the new keyword.
TemplateURLData data(applied_sync_turl->data());
data.SetKeyword(new_keyword);
- if (UpdateNoNotify(applied_sync_turl, TemplateURL(data)))
- NotifyObservers();
+ Update(applied_sync_turl, TemplateURL(data));
}
// The losing TemplateURL should have their keyword updated. Send a change to
// the server to reflect this change.
diff --git a/chromium/components/search_engines/template_url_service.h b/chromium/components/search_engines/template_url_service.h
index da7c5169a0f..a0368a31078 100644
--- a/chromium/components/search_engines/template_url_service.h
+++ b/chromium/components/search_engines/template_url_service.h
@@ -41,7 +41,7 @@ class TemplateURLServiceObserver;
struct TemplateURLData;
namespace rappor {
-class RapporService;
+class RapporServiceImpl;
}
namespace syncer {
@@ -109,7 +109,7 @@ class TemplateURLService : public WebDataServiceConsumer,
const scoped_refptr<KeywordWebDataService>& web_data_service,
std::unique_ptr<TemplateURLServiceClient> client,
GoogleURLTracker* google_url_tracker,
- rappor::RapporService* rappor_service,
+ rappor::RapporServiceImpl* rappor_service,
const base::Closure& dsp_change_callback);
// The following is for testing.
TemplateURLService(const Initializer* initializers, const int count);
@@ -133,6 +133,17 @@ class TemplateURLService : public WebDataServiceConsumer,
const GURL& url,
TemplateURL** template_url_to_replace);
+ // Returns whether the engine is a "pre-existing" engine, either from the
+ // prepopulate list or created by policy.
+ bool IsPrepopulatedOrCreatedByPolicy(const TemplateURL* template_url);
+
+ // Returns whether |template_url| should be shown in the list of engines
+ // most likely to be selected as a default engine. This is meant to highlight
+ // the current default, as well as the other most likely choices of default
+ // engine, separately from a full list of all TemplateURLs (which might be
+ // very long).
+ bool ShowInDefaultList(const TemplateURL* template_url);
+
// Adds to |matches| all TemplateURLs whose keywords begin with |prefix|,
// sorted shortest-keyword-first. If |supports_replacement_only| is true, only
// TemplateURLs that support replacement are returned.
@@ -403,6 +414,7 @@ class TemplateURLService : public WebDataServiceConsumer,
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, ChangeGoogleBaseValue);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, MergeDeletesUnusedProviders);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, AddExtensionKeyword);
+ FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceTest, LastVisitedTimeUpdate);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceSyncTest, UniquifyKeyword);
FRIEND_TEST_ALL_PREFIXES(TemplateURLServiceSyncTest,
IsLocalTemplateURLBetter);
@@ -414,6 +426,7 @@ class TemplateURLService : public WebDataServiceConsumer,
friend class InstantUnitTestBase;
friend class TemplateURLServiceTestUtil;
+ friend class TemplateUrlServiceAndroid;
using GUIDToTURL = std::map<std::string, TemplateURL*>;
@@ -537,6 +550,10 @@ class TemplateURLService : public WebDataServiceConsumer,
bool UpdateNoNotify(TemplateURL* existing_turl,
const TemplateURL& new_values);
+ // Calls UpdateNoNotify() and NotifyObservers() if update succeeds.
+ // Returns the result of UpdateNoNotify().
+ bool Update(TemplateURL* existing_turl, const TemplateURL& new_values);
+
// If the TemplateURL comes from a prepopulated URL available in the current
// country, update all its fields save for the keyword, short name and id so
// that they match the internal prepopulated URL. TemplateURLs not coming from
@@ -554,6 +571,9 @@ class TemplateURLService : public WebDataServiceConsumer,
// SetKeywordSearchTermsForURL is invoked.
void UpdateKeywordSearchTermsForURL(const URLVisitedDetails& details);
+ // Updates the last_visited time of |url| to the current time.
+ void UpdateTemplateURLVisitTime(TemplateURL* url);
+
// If necessary, generates a visit for the site http:// + t_url.keyword().
void AddTabToSearchVisit(const TemplateURL& t_url);
@@ -710,7 +730,7 @@ class TemplateURLService : public WebDataServiceConsumer,
GoogleURLTracker* google_url_tracker_;
// ---------- Metrics related members ---------------------------------------
- rappor::RapporService* rappor_service_;
+ rappor::RapporServiceImpl* rappor_service_;
// This closure is run when the default search provider is set to Google.
base::Closure dsp_change_callback_;
diff --git a/chromium/components/search_engines/template_url_unittest.cc b/chromium/components/search_engines/template_url_unittest.cc
index de0a54a263f..47630f34892 100644
--- a/chromium/components/search_engines/template_url_unittest.cc
+++ b/chromium/components/search_engines/template_url_unittest.cc
@@ -54,7 +54,6 @@ void TemplateURLTest::ExpectPostParamIs(const TemplateURLRef::PostParam& param,
TEST_F(TemplateURLTest, Defaults) {
TemplateURLData data;
- EXPECT_FALSE(data.show_in_default_list);
EXPECT_FALSE(data.safe_for_autoreplace);
EXPECT_EQ(0, data.prepopulate_id);
}
@@ -1692,34 +1691,29 @@ TEST_F(TemplateURLTest, ContextualSearchParameters) {
search_terms_data_);
EXPECT_EQ("http://bar/_/contextualsearch?", result);
- TemplateURLRef::SearchTermsArgs::ContextualSearchParams params(
- 1, 6, 11, "allen", "woody+allen+movies", "www.wikipedia.org", "utf-8", 1);
+ // Test the current common case, which uses no home country.
+ TemplateURLRef::SearchTermsArgs::ContextualSearchParams params(2, 1,
+ std::string());
search_terms_args.contextual_search_params = params;
result = url.url_ref().ReplaceSearchTerms(search_terms_args,
search_terms_data_);
EXPECT_EQ(
"http://bar/_/contextualsearch?"
- "ctxs=1&"
- "ctxs_start=6&"
- "ctxs_end=11&"
- "q=allen&"
- "ctxs_content=woody+allen+movies&"
- "ctxsl_url=www.wikipedia.org&"
- "ctxs_encoding=utf-8&"
+ "ctxs=2&"
"ctxsl_coca=1",
result);
- // Test the current common case, which uses the shorter constructor.
+ // Test the home country case.
search_terms_args.contextual_search_params =
- TemplateURLRef::SearchTermsArgs::ContextualSearchParams(2, "allen",
- std::string(), 0);
+ TemplateURLRef::SearchTermsArgs::ContextualSearchParams(2, 2, "CH");
result =
url.url_ref().ReplaceSearchTerms(search_terms_args, search_terms_data_);
EXPECT_EQ(
"http://bar/_/contextualsearch?"
"ctxs=2&"
- "q=allen",
+ "ctxsl_coca=2&"
+ "ctxs_hc=CH",
result);
}
diff --git a/chromium/components/search_provider_logos/logo_cache_unittest.cc b/chromium/components/search_provider_logos/logo_cache_unittest.cc
index 6a7dcbae646..0de7e3897d3 100644
--- a/chromium/components/search_provider_logos/logo_cache_unittest.cc
+++ b/chromium/components/search_provider_logos/logo_cache_unittest.cc
@@ -94,7 +94,7 @@ void ExpectLogosEqual(const EncodedLogo& expected_logo,
void ShortenFile(base::FilePath path) {
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
int64_t file_length = file.GetLength();
- ASSERT_NE(file_length, 0);
+ ASSERT_GT(file_length, 0);
file.SetLength(file_length - 1);
}
diff --git a/chromium/components/security_interstitials/OWNERS b/chromium/components/security_interstitials/OWNERS
index d46fc3fe61a..f8671b11b0f 100644
--- a/chromium/components/security_interstitials/OWNERS
+++ b/chromium/components/security_interstitials/OWNERS
@@ -3,4 +3,7 @@ felt@chromium.org
mattm@chromium.org
meacer@chromium.org
nparker@chromium.org
-palmer@chromium.org \ No newline at end of file
+palmer@chromium.org
+
+# For componentization purpose
+jialiul@chromium.org
diff --git a/chromium/components/security_interstitials/content/BUILD.gn b/chromium/components/security_interstitials/content/BUILD.gn
new file mode 100644
index 00000000000..d2252c2708b
--- /dev/null
+++ b/chromium/components/security_interstitials/content/BUILD.gn
@@ -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.
+
+static_library("security_interstitial_page") {
+ sources = [
+ "security_interstitial_controller_client.cc",
+ "security_interstitial_controller_client.h",
+ "security_interstitial_page.cc",
+ "security_interstitial_page.h",
+ "unsafe_resource.cc",
+ "unsafe_resource.h",
+ ]
+
+ public_deps = [
+ "//components/safe_browsing_db:hit_report",
+ ]
+
+ deps = [
+ "//base",
+ "//components/prefs:prefs",
+ "//components/resources",
+ "//components/safe_browsing_db:hit_report",
+ "//components/safe_browsing_db:safe_browsing_prefs",
+ "//components/safe_browsing_db:util",
+ "//components/security_interstitials/core:core",
+ "//content/public/browser",
+ "//content/public/common",
+ ]
+}
diff --git a/chromium/components/security_interstitials/content/DEPS b/chromium/components/security_interstitials/content/DEPS
new file mode 100644
index 00000000000..a35d23399fe
--- /dev/null
+++ b/chromium/components/security_interstitials/content/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+components/prefs",
+ "+components/safe_browsing_db",
+ "+components/security_interstitials/core",
+ "+content/public/browser",
+ "+content/public/common",
+ "+grit/components_resources.h",
+]
diff --git a/chromium/components/security_interstitials/content/security_interstitial_controller_client.cc b/chromium/components/security_interstitials/content/security_interstitial_controller_client.cc
new file mode 100644
index 00000000000..150505e5f5a
--- /dev/null
+++ b/chromium/components/security_interstitials/content/security_interstitial_controller_client.cc
@@ -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.
+
+#include "components/security_interstitials/content/security_interstitial_controller_client.h"
+
+#include <utility>
+
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing_db/safe_browsing_prefs.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+#include "content/public/browser/interstitial_page.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/referrer.h"
+
+using content::Referrer;
+
+namespace security_interstitials {
+
+SecurityInterstitialControllerClient::SecurityInterstitialControllerClient(
+ content::WebContents* web_contents,
+ std::unique_ptr<MetricsHelper> metrics_helper,
+ PrefService* prefs,
+ const std::string& app_locale,
+ const GURL& default_safe_page)
+ : ControllerClient(std::move(metrics_helper)),
+ web_contents_(web_contents),
+ interstitial_page_(nullptr),
+ prefs_(prefs),
+ app_locale_(app_locale),
+ default_safe_page_(default_safe_page) {}
+
+SecurityInterstitialControllerClient::~SecurityInterstitialControllerClient() {}
+
+void SecurityInterstitialControllerClient::set_interstitial_page(
+ content::InterstitialPage* interstitial_page) {
+ interstitial_page_ = interstitial_page;
+}
+
+content::InterstitialPage*
+SecurityInterstitialControllerClient::interstitial_page() {
+ return interstitial_page_;
+}
+
+void SecurityInterstitialControllerClient::GoBack() {
+ interstitial_page_->DontProceed();
+}
+
+void SecurityInterstitialControllerClient::GoBackAfterNavigationCommitted() {
+ // If the offending entry has committed, go back or to a safe page without
+ // closing the error page. This error page will be closed when the new page
+ // commits.
+ if (web_contents_->GetController().CanGoBack()) {
+ web_contents_->GetController().GoBack();
+ } else {
+ web_contents_->GetController().LoadURL(
+ default_safe_page_, content::Referrer(),
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
+ }
+}
+
+void SecurityInterstitialControllerClient::Proceed() {
+ interstitial_page_->Proceed();
+}
+
+void SecurityInterstitialControllerClient::Reload() {
+ web_contents_->GetController().Reload(content::ReloadType::NORMAL, true);
+}
+
+void SecurityInterstitialControllerClient::OpenUrlInCurrentTab(
+ const GURL& url) {
+ content::OpenURLParams params(url, Referrer(),
+ WindowOpenDisposition::CURRENT_TAB,
+ ui::PAGE_TRANSITION_LINK, false);
+ web_contents_->OpenURL(params);
+}
+
+const std::string&
+SecurityInterstitialControllerClient::GetApplicationLocale() const {
+ return app_locale_;
+}
+
+PrefService*
+SecurityInterstitialControllerClient::GetPrefService() {
+ return prefs_;
+}
+
+const std::string
+SecurityInterstitialControllerClient::GetExtendedReportingPrefName() const {
+ return safe_browsing::GetExtendedReportingPrefName(*prefs_);
+}
+
+bool SecurityInterstitialControllerClient::CanLaunchDateAndTimeSettings() {
+ NOTREACHED();
+ return false;
+}
+
+void SecurityInterstitialControllerClient::LaunchDateAndTimeSettings() {
+ NOTREACHED();
+}
+
+} // namespace security_interstitials
diff --git a/chromium/components/security_interstitials/content/security_interstitial_controller_client.h b/chromium/components/security_interstitials/content/security_interstitial_controller_client.h
new file mode 100644
index 00000000000..1d70720718c
--- /dev/null
+++ b/chromium/components/security_interstitials/content/security_interstitial_controller_client.h
@@ -0,0 +1,70 @@
+// 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_SECURITY_INTERSTITIALS_CONTENT_SECURITY_INTERSTITIAL_CONTROLLER_CLIENT_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SECURITY_INTERSTITIAL_CONTROLLER_CLIENT_H_
+
+#include "base/macros.h"
+#include "components/security_interstitials/core/controller_client.h"
+#include "url/gurl.h"
+
+namespace content {
+ class InterstitialPage;
+ class WebContents;
+}
+
+namespace security_interstitials {
+
+class MetricsHelper;
+
+// Handle commands from security interstitial pages. This class should only be
+// instantiated by SafeBrowsingBlockingPage for the time being.
+// TODO(jialiul): After we've done componentizing SafeBrowsingBlockingPage, we
+// should revisit this class to decide if other interstitials can use
+// SecurityInterstitialControllerClient.
+class SecurityInterstitialControllerClient
+ : public security_interstitials::ControllerClient {
+ public:
+ SecurityInterstitialControllerClient(
+ content::WebContents* web_contents,
+ std::unique_ptr<MetricsHelper> metrics_helper,
+ PrefService* prefs,
+ const std::string& app_locale,
+ const GURL& default_safe_page);
+
+ ~SecurityInterstitialControllerClient() override;
+
+ void set_interstitial_page(content::InterstitialPage* interstitial_page);
+ content::InterstitialPage* interstitial_page();
+
+ // security_interstitials::ControllerClient overrides.
+ void GoBack() override;
+ void GoBackAfterNavigationCommitted() override;
+ void Proceed() override;
+ void Reload() override;
+ void OpenUrlInCurrentTab(const GURL& url) override;
+ PrefService* GetPrefService() override;
+ const std::string& GetApplicationLocale() const override;
+ bool CanLaunchDateAndTimeSettings() override;
+ void LaunchDateAndTimeSettings() override;
+
+protected:
+ // security_interstitials::ControllerClient overrides.
+ const std::string GetExtendedReportingPrefName() const override;
+ content::WebContents* web_contents_;
+
+ private:
+ content::InterstitialPage* interstitial_page_;
+ PrefService* prefs_;
+ const std::string app_locale_;
+ // The default safe page we should go to if there is no previous page to go
+ // back to, e.g. chrome:kChromeUINewTabURL.
+ const GURL default_safe_page_;
+
+ DISALLOW_COPY_AND_ASSIGN(SecurityInterstitialControllerClient);
+};
+
+} // namespace security_interstitials
+
+#endif // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SECURITY_INTERSTITIAL_CONTROLLER_CLIENT_H_
diff --git a/chromium/components/security_interstitials/content/security_interstitial_page.cc b/chromium/components/security_interstitials/content/security_interstitial_page.cc
new file mode 100644
index 00000000000..0fd75a0baf1
--- /dev/null
+++ b/chromium/components/security_interstitials/content/security_interstitial_page.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 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/security_interstitials/content/security_interstitial_page.h"
+
+#include <utility>
+
+#include "base/i18n/rtl.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/values.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing_db/safe_browsing_prefs.h"
+#include "components/security_interstitials/content/security_interstitial_controller_client.h"
+#include "components/security_interstitials/core/common_string_util.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+#include "content/public/browser/interstitial_page.h"
+#include "content/public/browser/page_navigator.h"
+#include "content/public/browser/web_contents.h"
+#include "grit/components_resources.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace security_interstitials {
+
+SecurityInterstitialPage::SecurityInterstitialPage(
+ content::WebContents* web_contents,
+ const GURL& request_url,
+ std::unique_ptr<SecurityInterstitialControllerClient> controller)
+ : web_contents_(web_contents),
+ request_url_(request_url),
+ interstitial_page_(NULL),
+ create_view_(true),
+ on_show_extended_reporting_pref_exists_(false),
+ on_show_extended_reporting_pref_value_(false),
+ controller_(std::move(controller)) {
+ // Determine if any prefs need to be updated prior to showing the security
+ // interstitial. Note that some content embedders (such as Android WebView)
+ // uses security interstitials without a prefservice.
+ if (controller_->GetPrefService()) {
+ safe_browsing::UpdatePrefsBeforeSecurityInterstitial(
+ controller_->GetPrefService());
+ }
+
+ // Creating interstitial_page_ without showing it leaks memory, so don't
+ // create it here.
+}
+
+SecurityInterstitialPage::~SecurityInterstitialPage() {
+}
+
+content::InterstitialPage* SecurityInterstitialPage::interstitial_page() const {
+ return interstitial_page_;
+}
+
+content::WebContents* SecurityInterstitialPage::web_contents() const {
+ return web_contents_;
+}
+
+GURL SecurityInterstitialPage::request_url() const {
+ return request_url_;
+}
+
+void SecurityInterstitialPage::DontCreateViewForTesting() {
+ create_view_ = false;
+}
+
+void SecurityInterstitialPage::Show() {
+ DCHECK(!interstitial_page_);
+ interstitial_page_ = content::InterstitialPage::Create(
+ web_contents_, ShouldCreateNewNavigation(), request_url_, this);
+ if (!create_view_)
+ interstitial_page_->DontCreateViewForTesting();
+
+ interstitial_page_->Show();
+
+ // Remember the initial state of the extended reporting pref, to be compared
+ // to the same data when the interstitial is closed.
+ PrefService* prefs = controller_->GetPrefService();
+ if (prefs) {
+ on_show_extended_reporting_pref_exists_ =
+ safe_browsing::ExtendedReportingPrefExists(*prefs);
+ on_show_extended_reporting_pref_value_ =
+ safe_browsing::IsExtendedReportingEnabled(*prefs);
+ }
+
+ controller_->set_interstitial_page(interstitial_page_);
+ AfterShow();
+}
+
+SecurityInterstitialControllerClient* SecurityInterstitialPage::controller() {
+ return controller_.get();
+}
+
+security_interstitials::MetricsHelper*
+SecurityInterstitialPage::metrics_helper() {
+ return controller_->metrics_helper();
+}
+
+void SecurityInterstitialPage::UpdateMetricsAfterSecurityInterstitial() {
+ if (controller_->GetPrefService()) {
+ safe_browsing::UpdateMetricsAfterSecurityInterstitial(
+ *controller_->GetPrefService(), on_show_extended_reporting_pref_exists_,
+ on_show_extended_reporting_pref_value_);
+ }
+}
+
+base::string16 SecurityInterstitialPage::GetFormattedHostName() const {
+ return security_interstitials::common_string_util::GetFormattedHostName(
+ request_url_);
+}
+
+std::string SecurityInterstitialPage::GetHTMLContents() {
+ base::DictionaryValue load_time_data;
+ PopulateInterstitialStrings(&load_time_data);
+ webui::SetLoadTimeDataDefaults(
+ controller()->GetApplicationLocale(), &load_time_data);
+ std::string html = ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(IDR_SECURITY_INTERSTITIAL_HTML)
+ .as_string();
+ webui::AppendWebUiCssTextDefaults(&html);
+ return webui::GetI18nTemplateHtml(html, &load_time_data);
+}
+
+} // security_interstitials
diff --git a/chromium/components/security_interstitials/content/security_interstitial_page.h b/chromium/components/security_interstitials/content/security_interstitial_page.h
new file mode 100644
index 00000000000..1b24add7e1c
--- /dev/null
+++ b/chromium/components/security_interstitials/content/security_interstitial_page.h
@@ -0,0 +1,100 @@
+// 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_SECURITY_INTERSTITIALS_CONTENT_SECURITY_INTERSTITIAL_PAGE_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SECURITY_INTERSTITIAL_PAGE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "content/public/browser/interstitial_page_delegate.h"
+#include "url/gurl.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace content {
+class InterstitialPage;
+class WebContents;
+}
+
+namespace security_interstitials {
+class MetricsHelper;
+class SecurityInterstitialControllerClient;
+
+class SecurityInterstitialPage : public content::InterstitialPageDelegate {
+ public:
+ // |request_url| is the URL which triggered the interstitial page. For
+ // SafeBrowsing interstitials, it can be a main frame or a subresource URL.
+ // For SSL interstitials, it's always the main frame URL.
+ SecurityInterstitialPage(
+ content::WebContents* web_contents,
+ const GURL& request_url,
+ std::unique_ptr<SecurityInterstitialControllerClient> controller);
+ ~SecurityInterstitialPage() override;
+
+ // Creates an interstitial and shows it.
+ virtual void Show();
+
+ // Prevents creating the actual interstitial view for testing.
+ void DontCreateViewForTesting();
+
+ protected:
+ // Returns true if the interstitial should create a new navigation entry.
+ virtual bool ShouldCreateNewNavigation() const = 0;
+
+ // Populates the strings used to generate the HTML from the template.
+ virtual void PopulateInterstitialStrings(
+ base::DictionaryValue* load_time_data) = 0;
+
+ // Gives an opportunity for child classes to react to Show() having run. The
+ // interstitial_page_ will now have a value.
+ virtual void AfterShow() {}
+
+ // InterstitialPageDelegate method:
+ std::string GetHTMLContents() override;
+
+ // Returns the formatted host name for the request url.
+ base::string16 GetFormattedHostName() const;
+
+ content::InterstitialPage* interstitial_page() const;
+ content::WebContents* web_contents() const;
+ GURL request_url() const;
+
+ SecurityInterstitialControllerClient* controller();
+
+ MetricsHelper* metrics_helper();
+
+ // Update metrics when the interstitial is closed.
+ void UpdateMetricsAfterSecurityInterstitial();
+
+ private:
+ // The WebContents with which this interstitial page is
+ // associated. Not available in ~SecurityInterstitialPage, since it
+ // can be destroyed before this class is destroyed.
+ content::WebContents* web_contents_;
+ const GURL request_url_;
+ // Once shown, |interstitial_page| takes ownership of this
+ // SecurityInterstitialPage instance.
+ content::InterstitialPage* interstitial_page_;
+ // Whether the interstitial should create a view.
+ bool create_view_;
+
+ // Store some data about the initial state of extended reporting opt-in.
+ bool on_show_extended_reporting_pref_exists_;
+ bool on_show_extended_reporting_pref_value_;
+
+ // For subclasses that don't have their own ControllerClients yet.
+ std::unique_ptr<SecurityInterstitialControllerClient> controller_;
+
+ std::unique_ptr<MetricsHelper> metrics_helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(SecurityInterstitialPage);
+};
+
+} // security_interstitials
+
+#endif // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_SECURITY_INTERSTITIAL_PAGE_H_
diff --git a/chromium/components/security_interstitials/content/unsafe_resource.cc b/chromium/components/security_interstitials/content/unsafe_resource.cc
new file mode 100644
index 00000000000..c6204cb9652
--- /dev/null
+++ b/chromium/components/security_interstitials/content/unsafe_resource.cc
@@ -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.
+
+#include "components/security_interstitials/content/unsafe_resource.h"
+
+#include "base/bind.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+
+namespace security_interstitials {
+
+namespace {
+
+content::WebContents* GetWebContentsByFrameID(int render_process_id,
+ int render_frame_id) {
+ content::RenderFrameHost* render_frame_host =
+ content::RenderFrameHost::FromID(render_process_id, render_frame_id);
+ if (!render_frame_host)
+ return NULL;
+ return content::WebContents::FromRenderFrameHost(render_frame_host);
+}
+
+}; // namespace
+
+UnsafeResource::UnsafeResource()
+ : is_subresource(false),
+ is_subframe(false),
+ threat_type(safe_browsing::SB_THREAT_TYPE_SAFE),
+ threat_source(safe_browsing::ThreatSource::UNKNOWN) {}
+
+UnsafeResource::UnsafeResource(
+ const UnsafeResource& other) = default;
+
+UnsafeResource::~UnsafeResource() {}
+
+bool UnsafeResource::IsMainPageLoadBlocked() const {
+ // Subresource hits cannot happen until after main page load is committed.
+ 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) {
+ return false;
+ }
+
+ return true;
+}
+
+content::NavigationEntry*
+UnsafeResource::GetNavigationEntryForResource() const {
+ content::WebContents* web_contents = web_contents_getter.Run();
+ if (!web_contents)
+ return nullptr;
+ // If a safebrowsing hit occurs during main frame navigation, the navigation
+ // will not be committed, and the pending navigation entry refers to the hit.
+ if (IsMainPageLoadBlocked())
+ return web_contents->GetController().GetPendingEntry();
+ // If a safebrowsing hit occurs on a subresource load, or on a main frame
+ // after the navigation is committed, the last committed navigation entry
+ // refers to the page with the hit. Note that there may concurrently be an
+ // unrelated pending navigation to another site, so GetActiveEntry() would be
+ // wrong.
+ return web_contents->GetController().GetLastCommittedEntry();
+}
+
+// static
+base::Callback<content::WebContents*(void)>
+UnsafeResource::GetWebContentsGetter(
+ int render_process_host_id,
+ int render_frame_id) {
+ return base::Bind(&GetWebContentsByFrameID, render_process_host_id,
+ render_frame_id);
+}
+
+} // security_interstitials
diff --git a/chromium/components/security_interstitials/content/unsafe_resource.h b/chromium/components/security_interstitials/content/unsafe_resource.h
new file mode 100644
index 00000000000..b26da33d121
--- /dev/null
+++ b/chromium/components/security_interstitials/content/unsafe_resource.h
@@ -0,0 +1,74 @@
+// 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_SECURITY_INTERSTITIALS_CONTENT_UNSAFE_RESOURCE_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_UNSAFE_RESOURCE_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "components/safe_browsing_db/hit_report.h"
+#include "components/safe_browsing_db/util.h"
+#include "url/gurl.h"
+
+namespace content {
+class NavigationEntry;
+class WebContents;
+} // namespace content
+
+namespace security_interstitials {
+
+// Structure that passes parameters between the IO and UI thread when
+// interacting with the safe browsing blocking page.
+struct UnsafeResource {
+ // Passed a boolean indicating whether or not it is OK to proceed with
+ // loading an URL.
+ typedef base::Callback<void(bool /*proceed*/)> UrlCheckCallback;
+
+ UnsafeResource();
+ UnsafeResource(const UnsafeResource& other);
+ ~UnsafeResource();
+
+ // Returns true if this UnsafeResource is a main frame load that was blocked
+ // while the navigation is still pending. Note that a main frame hit may not
+ // be blocking, eg. client side detection happens after the load is
+ // committed.
+ bool IsMainPageLoadBlocked() const;
+
+ // Returns the NavigationEntry for this resource (for a main frame hit) or
+ // for the page which contains this resource (for a subresource hit).
+ // This method must only be called while the UnsafeResource is still
+ // "valid".
+ // I.e,
+ // For MainPageLoadBlocked resources, it must not be called if the load
+ // was aborted (going back or replaced with a different navigation),
+ // or resumed (proceeded through warning or matched whitelist).
+ // For non-MainPageLoadBlocked resources, it must not be called if any
+ // other navigation has committed (whether by going back or unrelated
+ // navigations), though a pending navigation is okay.
+ content::NavigationEntry* GetNavigationEntryForResource() const;
+
+ // Helper to build a getter for WebContents* from render frame id.
+ static base::Callback<content::WebContents*(void)> GetWebContentsGetter(
+ int render_process_host_id,
+ int render_frame_id);
+
+ GURL url;
+ GURL original_url;
+ std::vector<GURL> redirect_urls;
+ bool is_subresource;
+ bool is_subframe;
+ safe_browsing::SBThreatType threat_type;
+ safe_browsing::ThreatMetadata threat_metadata;
+ UrlCheckCallback callback; // This is called back on |callback_thread|.
+ scoped_refptr<base::SingleThreadTaskRunner> callback_thread;
+ base::Callback<content::WebContents*(void)> web_contents_getter;
+ safe_browsing::ThreatSource threat_source;
+};
+
+} // security_interstitials
+
+#endif // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_UNSAFE_RESOURCE_H_
diff --git a/chromium/components/security_interstitials/core/BUILD.gn b/chromium/components/security_interstitials/core/BUILD.gn
index dbd5bccad12..6970a43949d 100644
--- a/chromium/components/security_interstitials/core/BUILD.gn
+++ b/chromium/components/security_interstitials/core/BUILD.gn
@@ -12,6 +12,8 @@ static_library("core") {
"controller_client.h",
"metrics_helper.cc",
"metrics_helper.h",
+ "safe_browsing_error_ui.cc",
+ "safe_browsing_error_ui.h",
"ssl_error_ui.cc",
"ssl_error_ui.h",
]
@@ -23,7 +25,6 @@ static_library("core") {
"//components/history/core/browser",
"//components/metrics",
"//components/prefs",
- "//components/rappor",
"//components/ssl_errors",
"//components/strings",
"//components/url_formatter",
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.css b/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.css
index d67982a7edd..34d2c9d089f 100644
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.css
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.css
@@ -370,7 +370,7 @@ input[type=checkbox]:checked ~ .checkbox::before {
padding-left: 24px;
padding-right: 24px;
position: fixed;
- z-index: 1;
+ z-index: 2;
}
body.safe-browsing .nav-wrapper {
@@ -402,6 +402,7 @@ input[type=checkbox]:checked ~ .checkbox::before {
font-weight: 600;
margin: 6px 0;
text-transform: uppercase;
+ transform: translatez(0);
}
.nav-wrapper {
diff --git a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.js b/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.js
index d4359be4f81..e46279d279e 100644
--- a/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.js
+++ b/chromium/components/security_interstitials/core/browser/resources/interstitial_v2.js
@@ -32,17 +32,17 @@ var CMD_REPORT_PHISHING_ERROR = 12;
* @param {string} cmd The command to send.
*/
function sendCommand(cmd) {
-<if expr="not is_ios">
+// <if expr="not is_ios">
window.domAutomationController.setAutomationId(1);
window.domAutomationController.send(cmd);
-</if>
-<if expr="is_ios">
+// </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>
+// </if>
}
/**
diff --git a/chromium/components/security_interstitials/core/controller_client.cc b/chromium/components/security_interstitials/core/controller_client.cc
index e38fc18dd79..22a3dce765e 100644
--- a/chromium/components/security_interstitials/core/controller_client.cc
+++ b/chromium/components/security_interstitials/core/controller_client.cc
@@ -19,7 +19,7 @@ const char kBoxChecked[] = "boxchecked";
const char kDisplayCheckBox[] = "displaycheckbox";
const char kOptInLink[] = "optInLink";
const char kPrivacyLinkHtml[] =
- "<a id=\"privacy-link\" href=\"\" onclick=\"sendCommand(%d); "
+ "<a id=\"privacy-link\" href=\"#\" onclick=\"sendCommand(%d); "
"return false;\" onmousedown=\"return false;\">%s</a>";
ControllerClient::ControllerClient(
diff --git a/chromium/components/security_interstitials/core/controller_client.h b/chromium/components/security_interstitials/core/controller_client.h
index 42f9990ee2f..33d9cece494 100644
--- a/chromium/components/security_interstitials/core/controller_client.h
+++ b/chromium/components/security_interstitials/core/controller_client.h
@@ -68,9 +68,15 @@ class ControllerClient {
virtual bool CanLaunchDateAndTimeSettings() = 0;
virtual void LaunchDateAndTimeSettings() = 0;
- // Close the error and go back to the previous page.
+ // Close the error and go back to the previous page. This applies to
+ // situations where navigation is blocked before committing.
virtual void GoBack() = 0;
+ // If the offending entry has committed, go back or to a safe page without
+ // closing the error page. This error page will be closed when the new page
+ // commits.
+ virtual void GoBackAfterNavigationCommitted() = 0;
+
// Close the error and proceed to the blocked page.
virtual void Proceed() = 0;
@@ -81,10 +87,12 @@ class ControllerClient {
virtual void OpenUrlInCurrentTab(const GURL& url) = 0;
- protected:
- virtual const std::string& GetApplicationLocale() = 0;
virtual PrefService* GetPrefService() = 0;
- virtual const std::string GetExtendedReportingPrefName() = 0;
+
+ virtual const std::string& GetApplicationLocale() const = 0;
+
+ protected:
+ virtual const std::string GetExtendedReportingPrefName() const = 0;
private:
std::unique_ptr<MetricsHelper> metrics_helper_;
diff --git a/chromium/components/security_interstitials/core/metrics_helper.cc b/chromium/components/security_interstitials/core/metrics_helper.cc
index 17073b3c6d1..5a061fca4c1 100644
--- a/chromium/components/security_interstitials/core/metrics_helper.cc
+++ b/chromium/components/security_interstitials/core/metrics_helper.cc
@@ -11,8 +11,6 @@
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "components/history/core/browser/history_service.h"
-#include "components/rappor/rappor_service.h"
-#include "components/rappor/rappor_utils.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
using base::RecordAction;
@@ -22,13 +20,6 @@ namespace security_interstitials {
namespace {
-// Used for setting bits in Rappor's "interstitial.*.flags"
-enum InterstitialFlagBits {
- DID_PROCEED = 0,
- IS_REPEAT_VISIT = 1,
- HIGHEST_USED_BIT = 1
-};
-
// Directly adds to the UMA histograms, using the same properties as
// UMA_HISTOGRAM_ENUMERATION, because the macro doesn't allow non-constant
// histogram names.
@@ -108,27 +99,18 @@ void MaybeRecordInteractionAsAction(MetricsHelper::Interaction interaction,
MetricsHelper::~MetricsHelper() {}
-MetricsHelper::ReportDetails::ReportDetails()
- : rappor_report_type(rappor::NUM_RAPPOR_TYPES) {}
+MetricsHelper::ReportDetails::ReportDetails() {}
MetricsHelper::ReportDetails::ReportDetails(const ReportDetails& other) =
default;
MetricsHelper::ReportDetails::~ReportDetails() {}
-MetricsHelper::MetricsHelper(
- const GURL& request_url,
- const ReportDetails settings,
- history::HistoryService* history_service,
- const base::WeakPtr<rappor::RapporService>& rappor_service)
- : request_url_(request_url),
- settings_(settings),
- rappor_service_(rappor_service),
- num_visits_(-1) {
+MetricsHelper::MetricsHelper(const GURL& request_url,
+ const ReportDetails settings,
+ history::HistoryService* history_service)
+ : request_url_(request_url), settings_(settings), num_visits_(-1) {
DCHECK(!settings_.metric_prefix.empty());
- if (settings_.rappor_report_type == rappor::NUM_RAPPOR_TYPES) // Default.
- rappor_service_.reset();
- DCHECK(!rappor_service_ || !settings_.rappor_prefix.empty());
if (history_service) {
history_service->GetVisibleVisitCountToHost(
request_url_,
@@ -150,10 +132,6 @@ void MetricsHelper::RecordUserDecision(Decision decision) {
}
MaybeRecordDecisionAsAction(decision, settings_.metric_prefix);
- RecordUserDecisionToRappor(decision, settings_.rappor_report_type,
- settings_.rappor_prefix);
- RecordUserDecisionToRappor(decision, settings_.deprecated_rappor_report_type,
- settings_.deprecated_rappor_prefix);
RecordExtraUserDecisionMetrics(decision);
}
@@ -168,36 +146,6 @@ void MetricsHelper::RecordUserDecisionToMetrics(
}
}
-void MetricsHelper::RecordUserDecisionToRappor(
- Decision decision,
- const rappor::RapporType rappor_report_type,
- const std::string& rappor_prefix) {
- if (!rappor_service_ || (decision != PROCEED && decision != DONT_PROCEED))
- return;
-
- std::unique_ptr<rappor::Sample> sample =
- rappor_service_->CreateSample(rappor_report_type);
-
- // This will populate, for example, "intersitial.malware2.domain" or
- // "interstitial.ssl3.domain". The domain will be empty for hosts w/o TLDs.
- sample->SetStringField(
- "domain", rappor::GetDomainAndRegistrySampleFromGURL(request_url_));
-
- // Only report history and decision if we have history data.
- if (num_visits_ >= 0) {
- int flags = 0;
- if (decision == PROCEED)
- flags |= 1 << InterstitialFlagBits::DID_PROCEED;
- if (num_visits_ > 0)
- flags |= 1 << InterstitialFlagBits::IS_REPEAT_VISIT;
- // e.g. "interstitial.malware.flags"
- sample->SetFlagsField("flags", flags,
- InterstitialFlagBits::HIGHEST_USED_BIT + 1);
- }
- rappor_service_->RecordSampleObj("interstitial." + rappor_prefix,
- std::move(sample));
-}
-
void MetricsHelper::RecordUserInteraction(Interaction interaction) {
const std::string histogram_name(
"interstitial." + settings_.metric_prefix + ".interaction");
@@ -219,6 +167,13 @@ int MetricsHelper::NumVisits() {
return num_visits_;
}
+void MetricsHelper::RecordExtraUserDecisionMetrics(Decision decision) {}
+
+void MetricsHelper::RecordExtraUserInteractionMetrics(Interaction interaction) {
+}
+
+void MetricsHelper::RecordExtraShutdownMetrics() {}
+
void MetricsHelper::OnGotHistoryCount(bool success,
int num_visits,
base::Time /*first_visit*/) {
diff --git a/chromium/components/security_interstitials/core/metrics_helper.h b/chromium/components/security_interstitials/core/metrics_helper.h
index 66a3d6dd874..af8602eb149 100644
--- a/chromium/components/security_interstitials/core/metrics_helper.h
+++ b/chromium/components/security_interstitials/core/metrics_helper.h
@@ -11,7 +11,6 @@
#include "base/memory/weak_ptr.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/time/time.h"
-#include "components/rappor/rappor_service.h"
#include "url/gurl.h"
namespace history {
@@ -21,13 +20,10 @@ class HistoryService;
namespace security_interstitials {
// MetricsHelper records user warning interactions in a common way via METRICS
-// histograms and, optionally, RAPPOR metrics. The class will generate the
-// following histograms:
+// histograms. The class will generate the following histograms:
// METRICS: interstitial.<metric_prefix>.decision[.repeat_visit]
-// METRICS: interstitial.<metric_prefix>.interaction[.repeat_visi]
-// RAPPOR: interstitial.<rappor_prefix> (SafeBrowsing parameters)
-// RAPPOR: interstitial.<rappor_prefix>2 (Low frequency parameters)
-// wherein |metric_prefix| and |rappor_prefix| are specified via ReportDetails.
+// METRICS: interstitial.<metric_prefix>.interaction[.repeat_visit]
+// wherein |metric_prefix| is specified via ReportDetails.
// repeat_visit is also generated if the user has seen the page before.
//
// If |extra_suffix| is not empty, MetricsHelper will append ".<extra_suffix>"
@@ -64,45 +60,28 @@ class MetricsHelper {
// extra_suffix: If not-empty, will generate second set of metrics by
// placing at the end of the metric name. Examples:
// "from_datasaver", "from_device"
- // rappor_prefix: Metric prefix for Rappor.
- // examples: "phishing2", "ssl3"
- // rappor_report_type: Specifies the low-frequency RAPPOR configuration to use
- // (i.e. UMA or Safe Browsing).
- // deprecated_rappor_report_type: Specifies the deprecated RAPPOR
- // configuration to use for comparison with the
- // low-frequency metric.
- // The rappor preferences can be left blank if rappor_service is not set.
- // TODO(dominickn): remove deprecated_rappor_report_type once sufficient
- // comparison data has been collected and analysed - crbug.com/605836.
struct ReportDetails {
ReportDetails();
ReportDetails(const ReportDetails& other);
~ReportDetails();
std::string metric_prefix;
std::string extra_suffix;
- std::string rappor_prefix;
- std::string deprecated_rappor_prefix;
- rappor::RapporType rappor_report_type;
- rappor::RapporType deprecated_rappor_report_type;
};
// Args:
// url: URL of page that triggered the interstitial. Only origin is used.
// history_service: Set this to record metrics based on whether the user
// has visited this hostname before.
- // rappor_service: If you want RAPPOR statistics, provide a service,
- // settings.rappor_prefix, and settings.rappor_report_type.
// settings: Specify reporting details (prefixes and report types).
// sampling_event_name: Event name for Experience Sampling.
// e.g. "phishing_interstitial_"
MetricsHelper(const GURL& url,
const ReportDetails settings,
- history::HistoryService* history_service,
- const base::WeakPtr<rappor::RapporService>& rappor_service);
+ history::HistoryService* history_service);
virtual ~MetricsHelper();
// Records a user decision or interaction to the appropriate UMA metrics
- // histogram and potentially in a RAPPOR metric.
+ // histogram.
void RecordUserDecision(Decision decision);
void RecordUserInteraction(Interaction interaction);
void RecordShutdownMetrics();
@@ -113,9 +92,9 @@ class MetricsHelper {
protected:
// Subclasses should implement any embedder-specific recording logic in these
// methods. They'll be invoked from the matching Record methods.
- virtual void RecordExtraUserDecisionMetrics(Decision decision) = 0;
- virtual void RecordExtraUserInteractionMetrics(Interaction interaction) = 0;
- virtual void RecordExtraShutdownMetrics() = 0;
+ virtual void RecordExtraUserDecisionMetrics(Decision decision);
+ virtual void RecordExtraUserInteractionMetrics(Interaction interaction);
+ virtual void RecordExtraShutdownMetrics();
private:
// Used to query the HistoryService to see if the URL is in history. It will
@@ -124,12 +103,8 @@ class MetricsHelper {
void RecordUserDecisionToMetrics(Decision decision,
const std::string& histogram_name);
- void RecordUserDecisionToRappor(Decision decision,
- const rappor::RapporType rappor_report_type,
- const std::string& rappor_prefix);
const GURL request_url_;
const ReportDetails settings_;
- base::WeakPtr<rappor::RapporService> rappor_service_;
int num_visits_;
base::CancelableTaskTracker request_tracker_;
diff --git a/chromium/components/security_interstitials/core/safe_browsing_error_ui.cc b/chromium/components/security_interstitials/core/safe_browsing_error_ui.cc
new file mode 100644
index 00000000000..986892c45aa
--- /dev/null
+++ b/chromium/components/security_interstitials/core/safe_browsing_error_ui.cc
@@ -0,0 +1,311 @@
+// 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/security_interstitials/core/safe_browsing_error_ui.h"
+
+#include "base/i18n/time_formatting.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/security_interstitials/core/common_string_util.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+#include "grit/components_strings.h"
+#include "net/base/escape.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace security_interstitials {
+namespace {
+
+// URL for the Help Center article on Safe Browsing warnings.
+const char kLearnMore[] = "https://support.google.com/chrome/answer/99020";
+
+// For malware interstitial pages, we link the problematic URL to Google's
+// diagnostic page.
+#if defined(GOOGLE_CHROME_BUILD)
+const char kSbDiagnosticUrl[] =
+ "https://www.google.com/safebrowsing/"
+ "diagnostic?site=%s&client=googlechrome";
+#else
+const char kSbDiagnosticUrl[] =
+ "https://www.google.com/safebrowsing/diagnostic?site=%s&client=chromium";
+#endif
+
+// Constants for the V4 phishing string upgrades.
+const char kReportPhishingErrorUrl[] =
+ "https://www.google.com/safebrowsing/report_error/";
+
+void RecordExtendedReportingPrefChanged(bool report, bool is_scout) {
+ if (is_scout) {
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.SecurityInterstitial",
+ report);
+ } else {
+ UMA_HISTOGRAM_BOOLEAN(
+ "SafeBrowsing.Pref.Scout.SetPref.SBER1Pref.SecurityInterstitial",
+ report);
+ }
+}
+
+} // namespace
+
+SafeBrowsingErrorUI::SafeBrowsingErrorUI(
+ const GURL& request_url,
+ const GURL& main_frame_url,
+ SBInterstitialReason reason,
+ const SBErrorDisplayOptions& display_options,
+ const std::string& app_locale,
+ const base::Time& time_triggered,
+ ControllerClient* controller)
+ : request_url_(request_url),
+ main_frame_url_(main_frame_url),
+ interstitial_reason_(reason),
+ display_options_(display_options),
+ app_locale_(app_locale),
+ time_triggered_(time_triggered),
+ controller_(controller) {
+ controller_->metrics_helper()->RecordUserDecision(MetricsHelper::SHOW);
+ controller_->metrics_helper()->RecordUserInteraction(
+ MetricsHelper::TOTAL_VISITS);
+ if (display_options_.is_proceed_anyway_disabled)
+ controller_->metrics_helper()->RecordUserDecision(
+ security_interstitials::MetricsHelper::PROCEEDING_DISABLED);
+}
+
+SafeBrowsingErrorUI::~SafeBrowsingErrorUI() {
+ controller_->metrics_helper()->RecordShutdownMetrics();
+}
+
+void SafeBrowsingErrorUI::PopulateStringsForHTML(
+ base::DictionaryValue* load_time_data) {
+ DCHECK(load_time_data);
+
+ load_time_data->SetString("type", "SAFEBROWSING");
+ load_time_data->SetString(
+ "tabTitle", l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_TITLE));
+ load_time_data->SetString(
+ "openDetails",
+ l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_OPEN_DETAILS_BUTTON));
+ load_time_data->SetString(
+ "closeDetails",
+ l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_CLOSE_DETAILS_BUTTON));
+ load_time_data->SetString(
+ "primaryButtonText",
+ l10n_util::GetStringUTF16(IDS_SAFEBROWSING_OVERRIDABLE_SAFETY_BUTTON));
+ load_time_data->SetBoolean("overridable",
+ !display_options_.is_proceed_anyway_disabled);
+ security_interstitials::common_string_util::PopulateNewIconStrings(
+ load_time_data);
+
+ switch (interstitial_reason_) {
+ case SB_REASON_MALWARE:
+ PopulateMalwareLoadTimeData(load_time_data);
+ break;
+ case SB_REASON_HARMFUL:
+ PopulateHarmfulLoadTimeData(load_time_data);
+ break;
+ case SB_REASON_PHISHING:
+ PopulatePhishingLoadTimeData(load_time_data);
+ break;
+ }
+
+ PopulateExtendedReportingOption(load_time_data);
+}
+
+void SafeBrowsingErrorUI::HandleCommand(SecurityInterstitialCommands command) {
+ switch (command) {
+ case CMD_PROCEED: {
+ // User pressed on the button to proceed.
+ if (!display_options_.is_proceed_anyway_disabled) {
+ controller_->metrics_helper()->RecordUserDecision(
+ MetricsHelper::PROCEED);
+ controller_->Proceed();
+ break;
+ }
+ }
+ // If the user can't proceed, fall through to CMD_DONT_PROCEED.
+ case CMD_DONT_PROCEED: {
+ // User pressed on the button to return to safety.
+ // Don't record the user action here because there are other ways of
+ // triggering DontProceed, like clicking the back button.
+ if (display_options_.is_main_frame_load_blocked) {
+ // If the load is blocked, we want to close the interstitial and discard
+ // the pending entry.
+ controller_->GoBack();
+ } else {
+ // Otherwise the offending entry has committed, and we need to go back
+ // or to a safe page. We will close the interstitial when that page
+ // commits.
+ controller_->GoBackAfterNavigationCommitted();
+ }
+ break;
+ }
+ case CMD_DO_REPORT: {
+ // User enabled SB Extended Reporting via the checkbox.
+ display_options_.is_extended_reporting_enabled = true;
+ controller_->SetReportingPreference(true);
+ RecordExtendedReportingPrefChanged(
+ true, display_options_.is_scout_reporting_enabled);
+ break;
+ }
+ case CMD_DONT_REPORT: {
+ // User disabled SB Extended Reporting via the checkbox.
+ display_options_.is_extended_reporting_enabled = false;
+ controller_->SetReportingPreference(false);
+ RecordExtendedReportingPrefChanged(
+ false, display_options_.is_scout_reporting_enabled);
+ break;
+ }
+ case CMD_SHOW_MORE_SECTION: {
+ controller_->metrics_helper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::SHOW_ADVANCED);
+ break;
+ }
+ case CMD_OPEN_HELP_CENTER: {
+ // User pressed "Learn more".
+ controller_->metrics_helper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::SHOW_LEARN_MORE);
+ GURL learn_more_url(kLearnMore);
+ learn_more_url =
+ google_util::AppendGoogleLocaleParam(learn_more_url, app_locale_);
+ controller_->OpenUrlInCurrentTab(learn_more_url);
+ break;
+ }
+ case CMD_RELOAD: {
+ controller_->metrics_helper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::RELOAD);
+ controller_->Reload();
+ break;
+ }
+ case CMD_OPEN_REPORTING_PRIVACY: {
+ // User pressed on the SB Extended Reporting "privacy policy" link.
+ controller_->OpenExtendedReportingPrivacyPolicy();
+ break;
+ }
+ case CMD_OPEN_WHITEPAPER: {
+ controller_->OpenExtendedReportingWhitepaper();
+ break;
+ }
+ case CMD_OPEN_DIAGNOSTIC: {
+ controller_->metrics_helper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::SHOW_DIAGNOSTIC);
+ std::string diagnostic = base::StringPrintf(
+ kSbDiagnosticUrl,
+ net::EscapeQueryParamValue(request_url_.spec(), true).c_str());
+ GURL diagnostic_url(diagnostic);
+ diagnostic_url =
+ google_util::AppendGoogleLocaleParam(diagnostic_url, app_locale_);
+ controller_->OpenUrlInCurrentTab(diagnostic_url);
+ break;
+ }
+ case CMD_REPORT_PHISHING_ERROR: {
+ controller_->metrics_helper()->RecordUserInteraction(
+ security_interstitials::MetricsHelper::REPORT_PHISHING_ERROR);
+ GURL phishing_error_url(kReportPhishingErrorUrl);
+ phishing_error_url =
+ google_util::AppendGoogleLocaleParam(phishing_error_url, app_locale_);
+ controller_->OpenUrlInCurrentTab(phishing_error_url);
+ break;
+ }
+ case CMD_OPEN_DATE_SETTINGS:
+ case CMD_OPEN_LOGIN:
+ case CMD_ERROR:
+ case CMD_TEXT_FOUND:
+ case CMD_TEXT_NOT_FOUND:
+ break;
+ }
+}
+
+bool SafeBrowsingErrorUI::CanShowExtendedReportingOption() {
+ return !is_off_the_record() && is_extended_reporting_opt_in_allowed();
+}
+
+void SafeBrowsingErrorUI::PopulateMalwareLoadTimeData(
+ base::DictionaryValue* load_time_data) {
+ load_time_data->SetBoolean("phishing", false);
+ load_time_data->SetString("heading",
+ l10n_util::GetStringUTF16(IDS_MALWARE_V3_HEADING));
+ load_time_data->SetString(
+ "primaryParagraph",
+ l10n_util::GetStringFUTF16(
+ IDS_MALWARE_V3_PRIMARY_PARAGRAPH,
+ common_string_util::GetFormattedHostName(request_url_)));
+ load_time_data->SetString(
+ "explanationParagraph",
+ display_options_.is_main_frame_load_blocked
+ ? l10n_util::GetStringFUTF16(
+ IDS_MALWARE_V3_EXPLANATION_PARAGRAPH,
+ common_string_util::GetFormattedHostName(request_url_))
+ : l10n_util::GetStringFUTF16(
+ IDS_MALWARE_V3_EXPLANATION_PARAGRAPH_SUBRESOURCE,
+ base::UTF8ToUTF16(main_frame_url_.host()),
+ common_string_util::GetFormattedHostName(request_url_)));
+ load_time_data->SetString(
+ "finalParagraph",
+ l10n_util::GetStringUTF16(IDS_MALWARE_V3_PROCEED_PARAGRAPH));
+}
+
+void SafeBrowsingErrorUI::PopulateHarmfulLoadTimeData(
+ base::DictionaryValue* load_time_data) {
+ load_time_data->SetBoolean("phishing", false);
+ load_time_data->SetString("heading",
+ l10n_util::GetStringUTF16(IDS_HARMFUL_V3_HEADING));
+ load_time_data->SetString(
+ "primaryParagraph",
+ l10n_util::GetStringFUTF16(
+ IDS_HARMFUL_V3_PRIMARY_PARAGRAPH,
+ common_string_util::GetFormattedHostName(request_url_)));
+ load_time_data->SetString(
+ "explanationParagraph",
+ l10n_util::GetStringFUTF16(
+ IDS_HARMFUL_V3_EXPLANATION_PARAGRAPH,
+ common_string_util::GetFormattedHostName(request_url_)));
+ load_time_data->SetString(
+ "finalParagraph",
+ l10n_util::GetStringUTF16(IDS_HARMFUL_V3_PROCEED_PARAGRAPH));
+}
+
+void SafeBrowsingErrorUI::PopulatePhishingLoadTimeData(
+ base::DictionaryValue* load_time_data) {
+ load_time_data->SetBoolean("phishing", true);
+ load_time_data->SetString("heading",
+ l10n_util::GetStringUTF16(IDS_PHISHING_V4_HEADING));
+ load_time_data->SetString(
+ "primaryParagraph",
+ l10n_util::GetStringFUTF16(
+ IDS_PHISHING_V4_PRIMARY_PARAGRAPH,
+ common_string_util::GetFormattedHostName(request_url_)));
+ load_time_data->SetString(
+ "explanationParagraph",
+ l10n_util::GetStringFUTF16(
+ IDS_PHISHING_V4_EXPLANATION_PARAGRAPH,
+ common_string_util::GetFormattedHostName(request_url_)));
+ load_time_data->SetString(
+ "finalParagraph",
+ l10n_util::GetStringUTF16(IDS_PHISHING_V4_PROCEED_AND_REPORT_PARAGRAPH));
+}
+
+void SafeBrowsingErrorUI::PopulateExtendedReportingOption(
+ base::DictionaryValue* load_time_data) {
+ 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)
+ return;
+
+ const std::string privacy_link = base::StringPrintf(
+ security_interstitials::kPrivacyLinkHtml,
+ security_interstitials::CMD_OPEN_REPORTING_PRIVACY,
+ l10n_util::GetStringUTF8(IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE).c_str());
+ load_time_data->SetString(security_interstitials::kOptInLink,
+ l10n_util::GetStringFUTF16(
+ display_options_.is_scout_reporting_enabled
+ ? IDS_SAFE_BROWSING_SCOUT_REPORTING_AGREE
+ : IDS_SAFE_BROWSING_MALWARE_REPORTING_AGREE,
+ base::UTF8ToUTF16(privacy_link)));
+ load_time_data->SetBoolean(security_interstitials::kBoxChecked,
+ display_options_.is_extended_reporting_enabled);
+}
+
+} // security_interstitials
diff --git a/chromium/components/security_interstitials/core/safe_browsing_error_ui.h b/chromium/components/security_interstitials/core/safe_browsing_error_ui.h
new file mode 100644
index 00000000000..3124ee3bd4d
--- /dev/null
+++ b/chromium/components/security_interstitials/core/safe_browsing_error_ui.h
@@ -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.
+
+#ifndef COMPONENTS_SECURITY_INTERSTITIALS_CORE_SAFE_BROWSING_ERROR_UI_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CORE_SAFE_BROWSING_ERROR_UI_H_
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/security_interstitials/core/controller_client.h"
+#include "url/gurl.h"
+
+namespace security_interstitials {
+
+// This class displays UI for Safe Browsing errors that block page loads. This
+// class is purely about visual display; it does not do any error-handling logic
+// to determine what type of error should be displayed when.
+class SafeBrowsingErrorUI {
+ public:
+ enum SBInterstitialReason {
+ SB_REASON_MALWARE,
+ SB_REASON_HARMFUL,
+ SB_REASON_PHISHING,
+ };
+
+ struct SBErrorDisplayOptions {
+ SBErrorDisplayOptions(bool is_main_frame_load_blocked,
+ bool is_extended_reporting_opt_in_allowed,
+ bool is_off_the_record,
+ bool is_extended_reporting_enabled,
+ bool is_scout_reporting_enabled,
+ bool is_proceed_anyway_disabled)
+ : is_main_frame_load_blocked(is_main_frame_load_blocked),
+ is_extended_reporting_opt_in_allowed(
+ is_extended_reporting_opt_in_allowed),
+ is_off_the_record(is_off_the_record),
+ is_extended_reporting_enabled(is_extended_reporting_enabled),
+ is_scout_reporting_enabled(is_scout_reporting_enabled),
+ is_proceed_anyway_disabled(is_proceed_anyway_disabled) {}
+
+ // Indicates if this SB interstitial is blocking main frame load.
+ bool is_main_frame_load_blocked;
+
+ // Indicates if user is allowed to opt-in extended reporting preference.
+ bool is_extended_reporting_opt_in_allowed;
+
+ // Indicates if user is in incognito mode.
+ bool is_off_the_record;
+
+ // Indicates if user opted in for SB extended reporting.
+ bool is_extended_reporting_enabled;
+
+ // Indicates if user opted in for Scout extended reporting.
+ bool is_scout_reporting_enabled;
+
+ // Indicates if kSafeBrowsingProceedAnywayDisabled preference is set.
+ bool is_proceed_anyway_disabled;
+ };
+
+ SafeBrowsingErrorUI(const GURL& request_url,
+ const GURL& main_frame_url,
+ SBInterstitialReason reason,
+ const SBErrorDisplayOptions& display_options,
+ const std::string& app_locale,
+ const base::Time& time_triggered,
+ ControllerClient* controller);
+ ~SafeBrowsingErrorUI();
+
+ void PopulateStringsForHTML(base::DictionaryValue* load_time_data);
+ void HandleCommand(SecurityInterstitialCommands command);
+
+ // Checks if we should even show the extended reporting option. We don't show
+ // it in incognito mode or if kSafeBrowsingExtendedReportingOptInAllowed
+ // preference is disabled.
+ bool CanShowExtendedReportingOption();
+
+ bool is_main_frame_load_blocked() const {
+ return display_options_.is_main_frame_load_blocked;
+ }
+
+ bool is_extended_reporting_opt_in_allowed() const {
+ return display_options_.is_extended_reporting_opt_in_allowed;
+ }
+
+ bool is_off_the_record() const {
+ return display_options_.is_off_the_record;
+ }
+
+ bool is_extended_reporting_enabled() const {
+ return display_options_.is_extended_reporting_enabled;
+ }
+
+ bool is_proceed_anyway_disabled() const {
+ return display_options_.is_proceed_anyway_disabled;
+ }
+
+ const std::string app_locale() const {
+ return app_locale_;
+ }
+
+ private:
+ // Fills the passed dictionary with the values to be passed to the template
+ // when creating the HTML.
+ void PopulateExtendedReportingOption(base::DictionaryValue* load_time_data);
+ void PopulateMalwareLoadTimeData(base::DictionaryValue* load_time_data);
+ void PopulateHarmfulLoadTimeData(base::DictionaryValue* load_time_data);
+ void PopulatePhishingLoadTimeData(base::DictionaryValue* load_time_data);
+
+ const GURL request_url_;
+ const GURL main_frame_url_;
+ const SBInterstitialReason interstitial_reason_;
+ SBErrorDisplayOptions display_options_;
+ const std::string app_locale_;
+ const base::Time time_triggered_;
+
+ ControllerClient* controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingErrorUI);
+};
+
+} // security_interstitials
+
+#endif // COMPONENTS_SECURITY_INTERSTITIALS_CORE_SAFE_BROWSING_ERROR_UI_H_
diff --git a/chromium/components/security_interstitials_strings.grdp b/chromium/components/security_interstitials_strings.grdp
index e4e70f5097c..780a4e6b766 100644
--- a/chromium/components/security_interstitials_strings.grdp
+++ b/chromium/components/security_interstitials_strings.grdp
@@ -106,4 +106,84 @@
</message>
</if>
+ <!-- Safe Browsing interstitials V3 (shared phishing & malware) -->
+ <message name="IDS_SAFEBROWSING_V3_TITLE" desc="The tab title for the Safe Browsing interstitials.">
+ Security error
+ </message>
+ <message name="IDS_SAFEBROWSING_V3_OPEN_DETAILS_BUTTON" desc="The text for the button that expands the details.">
+ Details
+ </message>
+ <message name="IDS_SAFEBROWSING_V3_CLOSE_DETAILS_BUTTON" desc="The text for the button that hides the details.">
+ Hide details
+ </message>
+ <message name="IDS_SAFEBROWSING_OVERRIDABLE_SAFETY_BUTTON" desc="The text for the button that takes the user back to the previous page.">
+ Back to safety
+ </message>
+
+ <!-- Malware interstitial V3 -->
+ <message name="IDS_MALWARE_V3_HEADING" desc="The large heading at the top of the malware interstitial.">
+ The site ahead contains malware
+ </message>
+ <if expr="is_android">
+ <message name="IDS_MALWARE_V3_PRIMARY_PARAGRAPH" desc="Mobile: The primary explanatory paragraph for the malware interstitial.">
+ Attackers currently on <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="SITE">$1<ex>example.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> might attempt to install dangerous apps on your device that steal or delete your information (for example, photos, passwords, messages, and credit cards).
+ </message>
+ </if>
+ <if expr="is_macosx">
+ <message name="IDS_MALWARE_V3_PRIMARY_PARAGRAPH" desc="Mac: The primary explanatory paragraph for the malware interstitial.">
+ Attackers currently on <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="SITE">$1<ex>example.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> might attempt to install dangerous programs on your Mac that steal or delete your information (for example, photos, passwords, messages, and credit cards).
+ </message>
+ </if>
+ <if expr="not is_android and not is_macosx">
+ <message name="IDS_MALWARE_V3_PRIMARY_PARAGRAPH" desc="The primary explanatory paragraph for the malware interstitial.">
+ Attackers currently on <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="SITE">$1<ex>example.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> might attempt to install dangerous programs on your computer that steal or delete your information (for example, photos, passwords, messages, and credit cards).
+ </message>
+ </if>
+ <message name="IDS_MALWARE_V3_EXPLANATION_PARAGRAPH" desc="The explanation of why Safe Browsing has blocked the page.">
+ Google Safe Browsing recently <ph name="BEGIN_LINK">&lt;a href="#" id="diagnostic-link"&gt;</ph>detected malware<ph name="END_LINK">&lt;/a&gt;</ph> on <ph name="SITE">$1<ex>example.com</ex></ph>. Websites that are normally safe are sometimes infected with malware. <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>
+ <message name="IDS_MALWARE_V3_EXPLANATION_PARAGRAPH_SUBRESOURCE" desc="The explanation of why Safe Browsing has blocked the page.">
+ Google Safe Browsing recently <ph name="BEGIN_LINK">&lt;a href="#" id="diagnostic-link"&gt;</ph>detected malware<ph name="END_LINK">&lt;/a&gt;</ph> on <ph name="SITE">$1<ex>example.com</ex></ph>. Websites that are normally safe are sometimes infected with malware. The malicious content comes from <ph name="SUBRESOURCE_HOST">$2<ex>evil.com</ex></ph>, a known malware distributor. <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>
+ <message name="IDS_MALWARE_V3_PROCEED_PARAGRAPH" desc="The paragraph that lets the user skip the warning.">
+ If you understand the risks to your security, you may <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>visit this unsafe site<ph name="END_LINK">&lt;/a&gt;</ph> before the dangerous programs have been removed.
+ </message>
+ <message name="IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE" desc="Label for the link to SafeBrowsing Privacy policy page">
+ Privacy policy
+ </message>
+ <message name="IDS_SAFE_BROWSING_MALWARE_REPORTING_AGREE" desc="SafeBrowsing Malware v2 Details label next to checkbox">
+ <ph name="BEGIN_WHITEPAPER_LINK">&lt;a href="#" id="whitepaper-link"&gt;</ph>Automatically report<ph name="END_WHITEPAPER_LINK">&lt;/a&gt;</ph> details of possible security incidents to Google. <ph name="PRIVACY_PAGE_LINK">$1</ph>
+ </message>
+ <message name="IDS_SAFE_BROWSING_SCOUT_REPORTING_AGREE" desc="SafeBrowsing Scout label next to checkbox">
+ Automatically send some <ph name="BEGIN_WHITEPAPER_LINK">&lt;a href="#" id="whitepaper-link"&gt;</ph>system information and page content<ph name="END_WHITEPAPER_LINK">&lt;/a&gt;</ph> to Google to help detect dangerous apps and sites. <ph name="PRIVACY_PAGE_LINK">$1</ph>
+ </message>
+
+ <!-- Harmful download interstitial V3 -->
+ <message name="IDS_HARMFUL_V3_HEADING" desc="The large heading at the top of the social engineering interstitial.">
+ The site ahead contains harmful programs
+ </message>
+ <message name="IDS_HARMFUL_V3_PRIMARY_PARAGRAPH" desc="Mobile: The primary explanatory paragraph for the social engineering interstitial.">
+ Attackers on <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="SITE">$1<ex>example.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> might attempt to trick you into installing programs that harm your browsing experience (for example, by changing your homepage or showing extra ads on sites you visit).
+ </message>
+ <message name="IDS_HARMFUL_V3_EXPLANATION_PARAGRAPH" desc="The explanation of why Safe Browsing has blocked the page.">
+ Google Safe Browsing recently <ph name="BEGIN_LINK">&lt;a href="#" id="diagnostic-link"&gt;</ph>found harmful programs<ph name="END_LINK">&lt;/a&gt;</ph> on <ph name="SITE">$1<ex>example.com</ex></ph>. <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>
+ <message name="IDS_HARMFUL_V3_PROCEED_PARAGRAPH" desc="The paragraph that lets the user skip the warning.">
+ If you understand the risks to your security, you may <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>visit this site<ph name="END_LINK">&lt;/a&gt;</ph> before the harmful programs have been removed.
+ </message>
+
+ <!-- Phishing interstitial -->
+ <message name="IDS_PHISHING_V4_HEADING" desc="The large heading at the top of the phishing interstitial.">
+ Deceptive site ahead
+ </message>
+ <message name="IDS_PHISHING_V4_PRIMARY_PARAGRAPH" desc="The primary explanatory paragraph for the malware interstitial.">
+ Attackers on <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="SITE">$1<ex>example.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> may trick you into doing something dangerous like installing software or revealing your personal information (for example, passwords, phone numbers, or credit cards).
+ </message>
+ <message name="IDS_PHISHING_V4_EXPLANATION_PARAGRAPH" desc="The explanation of why Safe Browsing has blocked the page.">
+ Google Safe Browsing recently detected phishing on <ph name="SITE">$1<ex>example.com</ex></ph>. Phishing sites pretend to be other websites to trick you. <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>
+ <message name="IDS_PHISHING_V4_PROCEED_AND_REPORT_PARAGRAPH" desc="The paragraph that lets the user skip the warning.">
+ You can <ph name="BEGIN_ERROR_LINK">&lt;a href="#" id="report-error-link"&gt;</ph>report a detection problem<ph name="END_ERROR_LINK">&lt;/a&gt;</ph> or, if you understand the risks to your security, <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>visit this unsafe site<ph name="END_LINK">&lt;/a&gt;</ph>.
+ </message>
+
</grit-part>
diff --git a/chromium/components/security_state/content/content_utils.cc b/chromium/components/security_state/content/content_utils.cc
index 62b3a33d1a7..71b9d1c5fe9 100644
--- a/chromium/components/security_state/content/content_utils.cc
+++ b/chromium/components/security_state/content/content_utils.cc
@@ -211,6 +211,12 @@ blink::WebSecurityStyle GetSecurityStyle(
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);
+ }
+
// Check if the page is HTTP; if so, no more explanations are needed. Note
// that SecurityStyleUnauthenticated does not necessarily mean that
// the page is loaded over HTTP, because the security style merely
@@ -224,19 +230,11 @@ blink::WebSecurityStyle GetSecurityStyle(
return security_style;
}
- if (security_info.sha1_deprecation_status ==
- security_state::DEPRECATED_SHA1_MAJOR) {
- security_style_explanations->broken_explanations.push_back(
- content::SecurityStyleExplanation(
- l10n_util::GetStringUTF8(IDS_MAJOR_SHA1),
- l10n_util::GetStringUTF8(IDS_MAJOR_SHA1_DESCRIPTION),
- !!security_info.certificate));
- } else if (security_info.sha1_deprecation_status ==
- security_state::DEPRECATED_SHA1_MINOR) {
+ if (security_info.sha1_in_chain) {
security_style_explanations->unauthenticated_explanations.push_back(
content::SecurityStyleExplanation(
- l10n_util::GetStringUTF8(IDS_MINOR_SHA1),
- l10n_util::GetStringUTF8(IDS_MINOR_SHA1_DESCRIPTION),
+ l10n_util::GetStringUTF8(IDS_SHA1),
+ l10n_util::GetStringUTF8(IDS_SHA1_DESCRIPTION),
!!security_info.certificate));
}
@@ -293,11 +291,9 @@ blink::WebSecurityStyle GetSecurityStyle(
security_style_explanations->broken_explanations.push_back(explanation);
}
} else {
- // If the certificate does not have errors and is not using
- // deprecated SHA1, then add an explanation that the certificate is
- // valid.
- if (security_info.sha1_deprecation_status ==
- security_state::NO_DEPRECATED_SHA1) {
+ // 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),
diff --git a/chromium/components/security_state/core/BUILD.gn b/chromium/components/security_state/core/BUILD.gn
index 3ce60b70593..bb28c1f81b4 100644
--- a/chromium/components/security_state/core/BUILD.gn
+++ b/chromium/components/security_state/core/BUILD.gn
@@ -11,6 +11,8 @@ static_library("core") {
sources = [
"security_state.cc",
"security_state.h",
+ "security_state_ui.cc",
+ "security_state_ui.h",
"switches.cc",
"switches.h",
]
diff --git a/chromium/components/security_state/core/security_state.cc b/chromium/components/security_state/core/security_state.cc
index 42f2a577232..b818c8aee80 100644
--- a/chromium/components/security_state/core/security_state.cc
+++ b/chromium/components/security_state/core/security_state.cc
@@ -42,9 +42,7 @@ bool GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
}
if (switch_or_field_trial_group ==
- switches::kMarkHttpWithPasswordsOrCcWithChip ||
- switch_or_field_trial_group ==
- switches::kMarkHttpWithPasswordsOrCcWithChipAndFormWarning) {
+ switches::kMarkHttpWithPasswordsOrCcWithChip) {
if (displayed_sensitive_input_on_http) {
*level = security_state::HTTP_SHOW_WARNING;
} else {
@@ -86,27 +84,6 @@ SecurityLevel GetSecurityLevelForNonSecureFieldTrial(
return level;
}
-SHA1DeprecationStatus GetSHA1DeprecationStatus(
- const VisibleSecurityState& visible_security_state) {
- if (!visible_security_state.certificate ||
- !(visible_security_state.cert_status &
- net::CERT_STATUS_SHA1_SIGNATURE_PRESENT))
- return NO_DEPRECATED_SHA1;
-
- // The internal representation of the dates for UI treatment of SHA-1.
- // See http://crbug.com/401365 for details.
- static const int64_t kJanuary2017 = INT64_C(13127702400000000);
- if (visible_security_state.certificate->valid_expiry() >=
- base::Time::FromInternalValue(kJanuary2017))
- return DEPRECATED_SHA1_MAJOR;
- static const int64_t kJanuary2016 = INT64_C(13096080000000000);
- if (visible_security_state.certificate->valid_expiry() >=
- base::Time::FromInternalValue(kJanuary2016))
- return DEPRECATED_SHA1_MINOR;
-
- return NO_DEPRECATED_SHA1;
-}
-
ContentStatus GetContentStatus(bool displayed, bool ran) {
if (ran && displayed)
return CONTENT_STATUS_DISPLAYED_AND_RAN;
@@ -121,7 +98,7 @@ SecurityLevel GetSecurityLevelForRequest(
const VisibleSecurityState& visible_security_state,
bool used_policy_installed_certificate,
const IsOriginSecureCallback& is_origin_secure_callback,
- SHA1DeprecationStatus sha1_status,
+ bool sha1_in_chain,
ContentStatus mixed_content_status,
ContentStatus content_with_cert_errors_status) {
DCHECK(visible_security_state.connection_info_initialized ||
@@ -180,14 +157,10 @@ SecurityLevel GetSecurityLevelForRequest(
return SECURE_WITH_POLICY_INSTALLED_CERT;
// In most cases, SHA1 use is treated as a certificate error, in which case
- // DANGEROUS will have been returned above. If SHA1 is permitted, we downgrade
- // the security level to Neutral or Dangerous depending on policy.
- if (sha1_status == DEPRECATED_SHA1_MAJOR ||
- sha1_status == DEPRECATED_SHA1_MINOR) {
- return (visible_security_state.display_sha1_from_local_anchors_as_neutral)
- ? NONE
- : DANGEROUS;
- }
+ // DANGEROUS will have been returned above. If SHA1 was permitted by policy,
+ // downgrade the security level to Neutral.
+ if (sha1_in_chain)
+ return NONE;
// Active mixed content is handled above.
DCHECK_NE(CONTENT_STATUS_RAN, mixed_content_status);
@@ -224,14 +197,16 @@ void SecurityInfoForRequest(
MALICIOUS_CONTENT_STATUS_NONE) {
security_info->security_level = GetSecurityLevelForRequest(
visible_security_state, used_policy_installed_certificate,
- is_origin_secure_callback, UNKNOWN_SHA1, CONTENT_STATUS_UNKNOWN,
+ is_origin_secure_callback, false, CONTENT_STATUS_UNKNOWN,
CONTENT_STATUS_UNKNOWN);
}
return;
}
security_info->certificate = visible_security_state.certificate;
- security_info->sha1_deprecation_status =
- GetSHA1DeprecationStatus(visible_security_state);
+
+ security_info->sha1_in_chain = visible_security_state.certificate &&
+ (visible_security_state.cert_status &
+ net::CERT_STATUS_SHA1_SIGNATURE_PRESENT);
security_info->mixed_content_status =
GetContentStatus(visible_security_state.displayed_mixed_content,
visible_security_state.ran_mixed_content);
@@ -260,17 +235,20 @@ void SecurityInfoForRequest(
security_info->security_level = GetSecurityLevelForRequest(
visible_security_state, used_policy_installed_certificate,
- is_origin_secure_callback, security_info->sha1_deprecation_status,
+ is_origin_secure_callback, security_info->sha1_in_chain,
security_info->mixed_content_status,
security_info->content_with_cert_errors_status);
}
} // namespace
+const base::Feature kHttpFormWarningFeature{"HttpFormWarning",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
SecurityInfo::SecurityInfo()
: security_level(NONE),
malicious_content_status(MALICIOUS_CONTENT_STATUS_NONE),
- sha1_deprecation_status(NO_DEPRECATED_SHA1),
+ sha1_in_chain(false),
mixed_content_status(CONTENT_STATUS_NONE),
content_with_cert_errors_status(CONTENT_STATUS_NONE),
scheme_is_cryptographic(false),
@@ -295,6 +273,10 @@ void GetSecurityInfo(
is_origin_secure_callback, result);
}
+bool IsHttpWarningInFormEnabled() {
+ return base::FeatureList::IsEnabled(kHttpFormWarningFeature);
+}
+
VisibleSecurityState::VisibleSecurityState()
: malicious_content_status(MALICIOUS_CONTENT_STATUS_NONE),
connection_info_initialized(false),
@@ -308,8 +290,7 @@ VisibleSecurityState::VisibleSecurityState()
ran_content_with_cert_errors(false),
pkp_bypassed(false),
displayed_password_field_on_http(false),
- displayed_credit_card_field_on_http(false),
- display_sha1_from_local_anchors_as_neutral(false) {}
+ displayed_credit_card_field_on_http(false) {}
VisibleSecurityState::~VisibleSecurityState() {}
@@ -331,9 +312,7 @@ bool VisibleSecurityState::operator==(const VisibleSecurityState& other) const {
displayed_password_field_on_http ==
other.displayed_password_field_on_http &&
displayed_credit_card_field_on_http ==
- other.displayed_credit_card_field_on_http &&
- display_sha1_from_local_anchors_as_neutral ==
- other.display_sha1_from_local_anchors_as_neutral);
+ other.displayed_credit_card_field_on_http);
}
} // namespace security_state
diff --git a/chromium/components/security_state/core/security_state.h b/chromium/components/security_state/core/security_state.h
index 05424a0e496..7de0fa7437b 100644
--- a/chromium/components/security_state/core/security_state.h
+++ b/chromium/components/security_state/core/security_state.h
@@ -9,6 +9,7 @@
#include <memory>
#include "base/callback.h"
+#include "base/feature_list.h"
#include "base/macros.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/sct_status_flags.h"
@@ -24,6 +25,10 @@
// the form of a VisibleSecurityState struct.
namespace security_state {
+// A feature for showing a warning in autofill dropdowns for password
+// and credit cards fields when the top-level page is not HTTPS.
+extern const base::Feature kHttpFormWarningFeature;
+
// Describes the overall security state of the page.
//
// If you reorder, add, or delete values from this enum, you must also
@@ -63,22 +68,6 @@ enum SecurityLevel {
DANGEROUS,
};
-// Describes how the SHA1 deprecation policy applies to an HTTPS
-// connection.
-enum SHA1DeprecationStatus {
- UNKNOWN_SHA1,
- // No SHA1 deprecation policy applies.
- NO_DEPRECATED_SHA1,
- // The connection used a certificate with a SHA1 signature in the
- // chain, and policy says that the connection should be treated with a
- // warning.
- DEPRECATED_SHA1_MINOR,
- // The connection used a certificate with a SHA1 signature in the
- // chain, and policy says that the connection should be treated as
- // broken HTTPS.
- DEPRECATED_SHA1_MAJOR,
-};
-
// The ContentStatus enum is used to describe content on the page that
// has significantly different security properties than the main page
// load. Content can be passive content that is displayed (such as
@@ -112,7 +101,8 @@ struct SecurityInfo {
SecurityLevel security_level;
// Describes the nature of the page's malicious content, if any.
MaliciousContentStatus malicious_content_status;
- SHA1DeprecationStatus sha1_deprecation_status;
+ // True if a SHA1 signature was observed anywhere in the certificate chain.
+ bool sha1_in_chain;
// |mixed_content_status| describes the presence of content that was
// loaded over a nonsecure (HTTP) connection.
ContentStatus mixed_content_status;
@@ -193,10 +183,6 @@ 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 Enterprise Policy configured to display as neutral all SHA-1 chains
- // to a local trust anchor.
- // TODO(elawrence): remove this in M57, https://crbug.com/676826
- bool display_sha1_from_local_anchors_as_neutral;
};
// These security levels describe the treatment given to pages that
@@ -220,6 +206,11 @@ void GetSecurityInfo(
IsOriginSecureCallback is_origin_secure_callback,
SecurityInfo* result);
+// Returns true if an experimental form warning UI about HTTP passwords
+// and credit cards is enabled. This warning UI can be enabled with the
+// |kHttpFormWarningFeature| feature.
+bool IsHttpWarningInFormEnabled();
+
} // namespace security_state
#endif // COMPONENTS_SECURITY_STATE_SECURITY_STATE_H_
diff --git a/chromium/components/security_state/core/security_state_ui.cc b/chromium/components/security_state/core/security_state_ui.cc
new file mode 100644
index 00000000000..31aff663361
--- /dev/null
+++ b/chromium/components/security_state/core/security_state_ui.cc
@@ -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.
+
+#include "components/security_state/core/security_state_ui.h"
+
+namespace security_state {
+
+bool ShouldAlwaysShowIcon(SecurityLevel security_level) {
+ // Enumerate all |SecurityLevel| values for compile-time enforcement that all
+ // cases are explicitly handled.
+ switch (security_level) {
+ case NONE:
+ return false;
+ case HTTP_SHOW_WARNING:
+ case EV_SECURE:
+ case SECURE:
+ case SECURITY_WARNING:
+ case SECURE_WITH_POLICY_INSTALLED_CERT:
+ case DANGEROUS:
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace security_state
diff --git a/chromium/components/security_state/core/security_state_ui.h b/chromium/components/security_state/core/security_state_ui.h
new file mode 100644
index 00000000000..035091f7789
--- /dev/null
+++ b/chromium/components/security_state/core/security_state_ui.h
@@ -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.
+
+#ifndef COMPONENTS_SECURITY_STATE_SECURITY_STATE_UI_H_
+#define COMPONENTS_SECURITY_STATE_SECURITY_STATE_UI_H_
+
+#include "components/security_state/core/security_state.h"
+
+// Provides helper methods that encapsulate platform-independent security UI
+// logic.
+namespace security_state {
+
+// On some (mobile) form factors, the security indicator icon is hidden to save
+// UI space. This returns whether the icon should always be shown for the given
+// |security_level|, i.e. whether to override the hiding behaviour.
+bool ShouldAlwaysShowIcon(SecurityLevel security_level);
+
+} // namespace security_state
+
+#endif // COMPONENTS_SECURITY_STATE_SECURITY_STATE_UI_H_
diff --git a/chromium/components/security_state/core/security_state_unittest.cc b/chromium/components/security_state/core/security_state_unittest.cc
index 73652481b37..d7c2790137b 100644
--- a/chromium/components/security_state/core/security_state_unittest.cc
+++ b/chromium/components/security_state/core/security_state_unittest.cc
@@ -114,44 +114,56 @@ class TestSecurityStateHelper {
} // namespace
-// Tests that SHA1-signed certificates expiring in 2016 downgrade the
-// security state of the page.
-TEST(SecurityStateTest, SHA1Warning) {
+// Tests that SHA1-signed certificates, when not allowed by policy, downgrade
+// the security state of the page to DANGEROUS.
+TEST(SecurityStateTest, SHA1Blocked) {
TestSecurityStateHelper helper;
+ helper.AddCertStatus(net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM);
+ helper.AddCertStatus(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT);
SecurityInfo security_info;
helper.GetSecurityInfo(&security_info);
- EXPECT_EQ(DEPRECATED_SHA1_MINOR, security_info.sha1_deprecation_status);
+ EXPECT_TRUE(security_info.sha1_in_chain);
EXPECT_EQ(DANGEROUS, security_info.security_level);
}
-// Tests that SHA1 warnings don't interfere with the handling of mixed
-// content.
+// Tests that SHA1-signed certificates, when allowed by policy, downgrade the
+// security state of the page to NONE.
+TEST(SecurityStateTest, SHA1Warning) {
+ TestSecurityStateHelper helper;
+ SecurityInfo security_info;
+ helper.GetSecurityInfo(&security_info);
+ EXPECT_TRUE(security_info.sha1_in_chain);
+ EXPECT_EQ(NONE, security_info.security_level);
+}
+
+// Tests that SHA1-signed certificates, when allowed by policy, don't interfere
+// with the handling of mixed content.
TEST(SecurityStateTest, SHA1WarningMixedContent) {
TestSecurityStateHelper helper;
helper.SetDisplayedMixedContent(true);
SecurityInfo security_info1;
helper.GetSecurityInfo(&security_info1);
- EXPECT_EQ(DEPRECATED_SHA1_MINOR, security_info1.sha1_deprecation_status);
+ EXPECT_TRUE(security_info1.sha1_in_chain);
EXPECT_EQ(CONTENT_STATUS_DISPLAYED, security_info1.mixed_content_status);
- EXPECT_EQ(DANGEROUS, security_info1.security_level);
+ EXPECT_EQ(NONE, security_info1.security_level);
helper.SetDisplayedMixedContent(false);
helper.SetRanMixedContent(true);
SecurityInfo security_info2;
helper.GetSecurityInfo(&security_info2);
- EXPECT_EQ(DEPRECATED_SHA1_MINOR, security_info2.sha1_deprecation_status);
+ EXPECT_TRUE(security_info2.sha1_in_chain);
EXPECT_EQ(CONTENT_STATUS_RAN, security_info2.mixed_content_status);
EXPECT_EQ(DANGEROUS, security_info2.security_level);
}
-// Tests that SHA1 warnings don't interfere with the handling of major
-// cert errors.
+// Tests that SHA1-signed certificates, when allowed by policy,
+// don't interfere with the handling of major cert errors.
TEST(SecurityStateTest, SHA1WarningBrokenHTTPS) {
TestSecurityStateHelper helper;
helper.AddCertStatus(net::CERT_STATUS_DATE_INVALID);
SecurityInfo security_info;
helper.GetSecurityInfo(&security_info);
- EXPECT_EQ(DEPRECATED_SHA1_MINOR, security_info.sha1_deprecation_status);
+ EXPECT_TRUE(security_info.sha1_in_chain);
EXPECT_EQ(DANGEROUS, security_info.security_level);
}
diff --git a/chromium/components/security_state/core/switches.cc b/chromium/components/security_state/core/switches.cc
index 89c35e31f66..ced029d8842 100644
--- a/chromium/components/security_state/core/switches.cc
+++ b/chromium/components/security_state/core/switches.cc
@@ -13,8 +13,6 @@ const char kMarkHttpAsNeutral[] = "neutral";
const char kMarkHttpAsDangerous[] = "non-secure";
const char kMarkHttpWithPasswordsOrCcWithChip[] =
"show-non-secure-passwords-cc-ui";
-const char kMarkHttpWithPasswordsOrCcWithChipAndFormWarning[] =
- "show-non-secure-passwords-cc-chip-and-form-warning";
} // namespace switches
} // namespace security_state
diff --git a/chromium/components/security_state/core/switches.h b/chromium/components/security_state/core/switches.h
index 91a33beced2..1c6ba2950ef 100644
--- a/chromium/components/security_state/core/switches.h
+++ b/chromium/components/security_state/core/switches.h
@@ -12,7 +12,6 @@ extern const char kMarkHttpAs[];
extern const char kMarkHttpAsNeutral[];
extern const char kMarkHttpAsDangerous[];
extern const char kMarkHttpWithPasswordsOrCcWithChip[];
-extern const char kMarkHttpWithPasswordsOrCcWithChipAndFormWarning[];
}
} // namespace security_state
diff --git a/chromium/components/security_state_strings.grdp b/chromium/components/security_state_strings.grdp
index 7901d142a04..a193d68acd3 100644
--- a/chromium/components/security_state_strings.grdp
+++ b/chromium/components/security_state_strings.grdp
@@ -4,17 +4,14 @@
<message name="IDS_PRIVATE_USER_DATA_INPUT" desc="Summary phrase for a security problem where the site collects private user data on an insecure page." translateable="false">
Private User Data Input
</message>
- <message name="IDS_MAJOR_SHA1" desc="Summary phrase for a security problem where the site's certificate expires in 2017 or later and contains a SHA1 signature in the chain." translateable="false">
- SHA-1 Certificate
- </message>
- <message name="IDS_MAJOR_SHA1_DESCRIPTION" desc="Description of a security problem where the site's certificate expires in 2017 or later and contains a SHA1 signature in the chain." translateable="false">
- The certificate for this site expires in 2017 or later, and the certificate chain contains a certificate signed using SHA-1.
+ <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>
- <message name="IDS_MINOR_SHA1" desc="Summary phrase for a security problem where the site's certificate expires in 2016 and contains a SHA1 signature in the chain." translateable="false">
+ <message name="IDS_SHA1" desc="Summary phrase for a security problem where the site's certificate chain contains a SHA1 signature." translateable="false">
SHA-1 Certificate
</message>
- <message name="IDS_MINOR_SHA1_DESCRIPTION" desc="Description of a security problem where the site's certificate expires in 2016 and contains a SHA1 signature in the chain." translateable="false">
- The certificate for this site expires in 2016, and the certificate chain contains a certificate signed using SHA-1.
+ <message name="IDS_SHA1_DESCRIPTION" desc="Description of a security problem where the site's certificate chain contains a SHA1 signature." translateable="false">
+ The certificate chain for this site contains a certificate signed using SHA-1.
</message>
<message name="IDS_CERTIFICATE_CHAIN_ERROR" desc="Summary phrase for a security problem with the site's certificate." translateable="false">
Certificate Error
diff --git a/chromium/components/session_manager/BUILD.gn b/chromium/components/session_manager/BUILD.gn
index 0bca2e4552d..cb6f68743ef 100644
--- a/chromium/components/session_manager/BUILD.gn
+++ b/chromium/components/session_manager/BUILD.gn
@@ -7,7 +7,8 @@ source_set("base") {
"session_manager_export.h",
"session_manager_types.h",
]
- deps = [
+
+ public_deps = [
"//components/signin/core/account_id",
]
}
diff --git a/chromium/components/session_manager/core/session_manager.cc b/chromium/components/session_manager/core/session_manager.cc
index db1c229a768..cd6f44715bf 100644
--- a/chromium/components/session_manager/core/session_manager.cc
+++ b/chromium/components/session_manager/core/session_manager.cc
@@ -62,6 +62,23 @@ void SessionManager::SessionStarted() {
session_started_ = true;
}
+bool SessionManager::IsInSecondaryLoginScreen() const {
+ return session_state_ == SessionState::LOGIN_SECONDARY;
+}
+
+bool SessionManager::IsScreenLocked() const {
+ return session_state_ == SessionState::LOCKED;
+}
+
+uint32_t SessionManager::GetMaximumNumberOfUserSessions() const {
+ // Limits the number of logged in users to 10 due to memory constraints.
+ return 10u;
+}
+
+bool SessionManager::IsUserSessionBlocked() const {
+ return session_state_ != SessionState::ACTIVE;
+}
+
void SessionManager::AddObserver(SessionManagerObserver* observer) {
observers_.AddObserver(observer);
}
diff --git a/chromium/components/session_manager/core/session_manager.h b/chromium/components/session_manager/core/session_manager.h
index f2ddb202198..1d83f8a74a5 100644
--- a/chromium/components/session_manager/core/session_manager.h
+++ b/chromium/components/session_manager/core/session_manager.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_SESSION_MANAGER_CORE_SESSION_MANAGER_H_
#define COMPONENTS_SESSION_MANAGER_CORE_SESSION_MANAGER_H_
+#include <stdint.h>
+
#include <vector>
#include "base/macros.h"
@@ -39,7 +41,7 @@ class SESSION_EXPORT SessionManager {
// Returns true if we're logged in and browser has been started i.e.
// browser_creator.LaunchBrowser(...) was called after sign in
// or restart after crash.
- virtual bool IsSessionStarted() const;
+ bool IsSessionStarted() const;
// Called when browser session is started i.e. after
// browser_creator.LaunchBrowser(...) was called after user sign in.
@@ -49,6 +51,14 @@ class SESSION_EXPORT SessionManager {
// before the session has been started.
virtual void SessionStarted();
+ // Convenience wrapps of session state.
+ bool IsInSecondaryLoginScreen() const;
+ bool IsScreenLocked() const;
+ bool IsUserSessionBlocked() const;
+
+ // Returns the maximum number of allowed user sessions.
+ uint32_t GetMaximumNumberOfUserSessions() const;
+
void AddObserver(SessionManagerObserver* observer);
void RemoveObserver(SessionManagerObserver* observer);
diff --git a/chromium/components/sessions/content/content_live_tab.h b/chromium/components/sessions/content/content_live_tab.h
index e82ac7440e6..f4324411ea1 100644
--- a/chromium/components/sessions/content/content_live_tab.h
+++ b/chromium/components/sessions/content/content_live_tab.h
@@ -14,8 +14,6 @@
namespace content {
class NavigationController;
-class NavigationEntry;
-class SessionStorageNamespace;
}
class PersistentTabRestoreServiceTest;
diff --git a/chromium/components/sessions/core/base_session_service.cc b/chromium/components/sessions/core/base_session_service.cc
index edc2431507c..21726d042bd 100644
--- a/chromium/components/sessions/core/base_session_service.cc
+++ b/chromium/components/sessions/core/base_session_service.cc
@@ -24,7 +24,7 @@ namespace {
void RunIfNotCanceled(
const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
const BaseSessionService::GetCommandsCallback& callback,
- ScopedVector<SessionCommand> commands) {
+ std::vector<std::unique_ptr<SessionCommand>> commands) {
if (is_canceled.Run())
return;
callback.Run(std::move(commands));
@@ -33,7 +33,7 @@ void RunIfNotCanceled(
void PostOrRunInternalGetCommandsCallback(
base::TaskRunner* task_runner,
const BaseSessionService::GetCommandsCallback& callback,
- ScopedVector<SessionCommand> commands) {
+ std::vector<std::unique_ptr<SessionCommand>> commands) {
if (task_runner->RunsTasksOnCurrentThread()) {
callback.Run(std::move(commands));
} else {
@@ -80,21 +80,22 @@ void BaseSessionService::ScheduleCommand(
std::unique_ptr<SessionCommand> command) {
DCHECK(command);
commands_since_reset_++;
- pending_commands_.push_back(command.release());
+ pending_commands_.push_back(std::move(command));
StartSaveTimer();
}
void BaseSessionService::AppendRebuildCommand(
std::unique_ptr<SessionCommand> command) {
DCHECK(command);
- pending_commands_.push_back(command.release());
+ pending_commands_.push_back(std::move(command));
}
void BaseSessionService::EraseCommand(SessionCommand* old_command) {
- ScopedVector<SessionCommand>::iterator it =
- std::find(pending_commands_.begin(),
- pending_commands_.end(),
- old_command);
+ auto it = std::find_if(
+ pending_commands_.begin(), pending_commands_.end(),
+ [old_command](const std::unique_ptr<SessionCommand>& command_ptr) {
+ return command_ptr.get() == old_command;
+ });
CHECK(it != pending_commands_.end());
pending_commands_.erase(it);
}
@@ -102,13 +103,13 @@ void BaseSessionService::EraseCommand(SessionCommand* old_command) {
void BaseSessionService::SwapCommand(
SessionCommand* old_command,
std::unique_ptr<SessionCommand> new_command) {
- ScopedVector<SessionCommand>::iterator it =
- std::find(pending_commands_.begin(),
- pending_commands_.end(),
- old_command);
+ auto it = std::find_if(
+ pending_commands_.begin(), pending_commands_.end(),
+ [old_command](const std::unique_ptr<SessionCommand>& command_ptr) {
+ return command_ptr.get() == old_command;
+ });
CHECK(it != pending_commands_.end());
- *it = new_command.release();
- delete old_command;
+ *it = std::move(new_command);
}
void BaseSessionService::ClearPendingCommands() {
@@ -134,7 +135,7 @@ void BaseSessionService::Save() {
if (pending_commands_.empty())
return;
- // We create a new ScopedVector which will receive all elements from the
+ // We create a new vector which will receive all elements from the
// current commands. This will also clear the current list.
RunTaskOnBackendThread(
FROM_HERE,
diff --git a/chromium/components/sessions/core/base_session_service.h b/chromium/components/sessions/core/base_session_service.h
index 6511cf0d3ac..473b75d6685 100644
--- a/chromium/components/sessions/core/base_session_service.h
+++ b/chromium/components/sessions/core/base_session_service.h
@@ -10,7 +10,6 @@
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/task/cancelable_task_tracker.h"
#include "components/sessions/core/sessions_export.h"
@@ -19,7 +18,6 @@
namespace sessions {
class BaseSessionServiceDelegate;
-class SerializedNavigationEntry;
class SessionCommand;
class SessionBackend;
@@ -36,7 +34,7 @@ class SESSIONS_EXPORT BaseSessionService {
TAB_RESTORE
};
- typedef base::Callback<void(ScopedVector<SessionCommand>)>
+ typedef base::Callback<void(std::vector<std::unique_ptr<SessionCommand>>)>
GetCommandsCallback;
// Creates a new BaseSessionService. After creation you need to invoke
@@ -56,7 +54,7 @@ class SESSIONS_EXPORT BaseSessionService {
// Returns the set of commands which were scheduled to be written. Once
// committed to the backend, the commands are removed from here.
- const ScopedVector<SessionCommand>& pending_commands() {
+ const std::vector<std::unique_ptr<SessionCommand>>& pending_commands() {
return pending_commands_;
}
@@ -113,7 +111,7 @@ class SESSIONS_EXPORT BaseSessionService {
scoped_refptr<SessionBackend> backend_;
// Commands we need to send over to the backend.
- ScopedVector<SessionCommand> pending_commands_;
+ std::vector<std::unique_ptr<SessionCommand>> pending_commands_;
// Whether the backend file should be recreated the next time we send
// over the commands.
diff --git a/chromium/components/sessions/core/base_session_service_test_helper.cc b/chromium/components/sessions/core/base_session_service_test_helper.cc
index 44074c29ac9..73836a700cc 100644
--- a/chromium/components/sessions/core/base_session_service_test_helper.cc
+++ b/chromium/components/sessions/core/base_session_service_test_helper.cc
@@ -30,7 +30,7 @@ bool BaseSessionServiceTestHelper::ProcessedAnyCommands() {
}
bool BaseSessionServiceTestHelper::ReadLastSessionCommands(
- ScopedVector<SessionCommand>* commands) {
+ std::vector<std::unique_ptr<SessionCommand>>* commands) {
return base_session_service_->backend_->ReadLastSessionCommandsImpl(commands);
}
diff --git a/chromium/components/sessions/core/base_session_service_test_helper.h b/chromium/components/sessions/core/base_session_service_test_helper.h
index 438bbbd4b1a..f5c3dfbdffe 100644
--- a/chromium/components/sessions/core/base_session_service_test_helper.h
+++ b/chromium/components/sessions/core/base_session_service_test_helper.h
@@ -5,9 +5,11 @@
#ifndef COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_TEST_HELPER_H_
#define COMPONENTS_SESSIONS_CORE_BASE_SESSION_SERVICE_TEST_HELPER_H_
+#include <memory>
+#include <vector>
+
#include "base/callback.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
#include "base/task/cancelable_task_tracker.h"
namespace sessions {
@@ -29,7 +31,8 @@ class BaseSessionServiceTestHelper {
bool ProcessedAnyCommands();
// Read the last session commands directly from file.
- bool ReadLastSessionCommands(ScopedVector<SessionCommand>* commands);
+ bool ReadLastSessionCommands(
+ std::vector<std::unique_ptr<SessionCommand>>* commands);
private:
BaseSessionService* base_session_service_;
diff --git a/chromium/components/sessions/core/persistent_tab_restore_service.cc b/chromium/components/sessions/core/persistent_tab_restore_service.cc
index d9266e74d7c..d9016ee0369 100644
--- a/chromium/components/sessions/core/persistent_tab_restore_service.cc
+++ b/chromium/components/sessions/core/persistent_tab_restore_service.cc
@@ -211,11 +211,12 @@ class PersistentTabRestoreService::Delegate
// Invoked when we've loaded the session commands that identify the previously
// closed tabs. This creates entries, adds them to staging_entries_, and
// invokes LoadState.
- void OnGotLastSessionCommands(ScopedVector<SessionCommand> commands);
+ void OnGotLastSessionCommands(
+ std::vector<std::unique_ptr<SessionCommand>> commands);
// Populates |loaded_entries| with Entries from |commands|.
void CreateEntriesFromCommands(
- const std::vector<SessionCommand*>& commands,
+ const std::vector<std::unique_ptr<SessionCommand>>& commands,
std::vector<std::unique_ptr<Entry>>* loaded_entries);
// Validates all entries in |entries|, deleting any with no navigations. This
@@ -578,9 +579,9 @@ int PersistentTabRestoreService::Delegate::GetSelectedNavigationIndexToPersist(
}
void PersistentTabRestoreService::Delegate::OnGotLastSessionCommands(
- ScopedVector<SessionCommand> commands) {
+ std::vector<std::unique_ptr<SessionCommand>> commands) {
std::vector<std::unique_ptr<TabRestoreService::Entry>> entries;
- CreateEntriesFromCommands(commands.get(), &entries);
+ CreateEntriesFromCommands(commands, &entries);
// Closed tabs always go to the end.
staging_entries_.insert(staging_entries_.end(),
make_move_iterator(entries.begin()),
@@ -590,7 +591,7 @@ void PersistentTabRestoreService::Delegate::OnGotLastSessionCommands(
}
void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands(
- const std::vector<SessionCommand*>& commands,
+ const std::vector<std::unique_ptr<SessionCommand>>& commands,
std::vector<std::unique_ptr<Entry>>* loaded_entries) {
if (tab_restore_service_helper_->entries().size() == kMaxEntries)
return;
@@ -603,8 +604,7 @@ void PersistentTabRestoreService::Delegate::CreateEntriesFromCommands(
Window* current_window = nullptr;
// If > 0, we've gotten a window command but not all the tabs yet.
int pending_window_tabs = 0;
- for (std::vector<SessionCommand*>::const_iterator i = commands.begin();
- i != commands.end(); ++i) {
+ for (auto i = commands.begin(); i != commands.end(); ++i) {
const SessionCommand& command = *(*i);
switch (command.id()) {
case kCommandRestoredEntry: {
diff --git a/chromium/components/sessions/core/session_backend.cc b/chromium/components/sessions/core/session_backend.cc
index 4b0cbe76bd4..43db0ae5c9c 100644
--- a/chromium/components/sessions/core/session_backend.cc
+++ b/chromium/components/sessions/core/session_backend.cc
@@ -11,7 +11,7 @@
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/macros.h"
-#include "base/memory/scoped_vector.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
@@ -55,17 +55,16 @@ class SessionFileReader {
path, base::File::FLAG_OPEN | base::File::FLAG_READ));
}
// Reads the contents of the file specified in the constructor, returning
- // true on success. It is up to the caller to free all SessionCommands
- // added to commands.
+ // true on success, and filling up |commands| with commands.
bool Read(sessions::BaseSessionService::SessionType type,
- ScopedVector<sessions::SessionCommand>* commands);
+ std::vector<std::unique_ptr<sessions::SessionCommand>>* commands);
private:
// Reads a single command, returning it. A return value of NULL indicates
// either there are no commands, or there was an error. Use errored_ to
// distinguish the two. If NULL is returned, and there is no error, it means
// the end of file was successfully reached.
- sessions::SessionCommand* ReadCommand();
+ std::unique_ptr<sessions::SessionCommand> ReadCommand();
// Shifts the unused portion of buffer_ to the beginning and fills the
// remaining portion with data from the file. Returns false if the buffer
@@ -91,8 +90,9 @@ class SessionFileReader {
DISALLOW_COPY_AND_ASSIGN(SessionFileReader);
};
-bool SessionFileReader::Read(sessions::BaseSessionService::SessionType type,
- ScopedVector<sessions::SessionCommand>* commands) {
+bool SessionFileReader::Read(
+ sessions::BaseSessionService::SessionType type,
+ std::vector<std::unique_ptr<sessions::SessionCommand>>* commands) {
if (!file_->IsValid())
return false;
FileHeader header;
@@ -104,10 +104,10 @@ bool SessionFileReader::Read(sessions::BaseSessionService::SessionType type,
header.version != kFileCurrentVersion)
return false;
- ScopedVector<sessions::SessionCommand> read_commands;
- for (sessions::SessionCommand* command = ReadCommand(); command && !errored_;
- command = ReadCommand())
- read_commands.push_back(command);
+ std::vector<std::unique_ptr<sessions::SessionCommand>> read_commands;
+ for (std::unique_ptr<sessions::SessionCommand> command = ReadCommand();
+ command && !errored_; command = ReadCommand())
+ read_commands.push_back(std::move(command));
if (!errored_)
read_commands.swap(*commands);
if (type == sessions::BaseSessionService::TAB_RESTORE) {
@@ -120,7 +120,7 @@ bool SessionFileReader::Read(sessions::BaseSessionService::SessionType type,
return !errored_;
}
-sessions::SessionCommand* SessionFileReader::ReadCommand() {
+std::unique_ptr<sessions::SessionCommand> SessionFileReader::ReadCommand() {
// Make sure there is enough in the buffer for the size of the next command.
if (available_count_ < sizeof(size_type)) {
if (!FillBuffer())
@@ -157,8 +157,9 @@ sessions::SessionCommand* SessionFileReader::ReadCommand() {
const id_type command_id = buffer_[buffer_position_];
// NOTE: command_size includes the size of the id, which is not part of
// the contents of the SessionCommand.
- sessions::SessionCommand* command =
- new sessions::SessionCommand(command_id, command_size - sizeof(id_type));
+ std::unique_ptr<sessions::SessionCommand> command =
+ base::MakeUnique<sessions::SessionCommand>(
+ command_id, command_size - sizeof(id_type));
if (command_size > sizeof(id_type)) {
memcpy(command->contents(),
&(buffer_[buffer_position_ + sizeof(id_type)]),
@@ -227,7 +228,7 @@ void SessionBackend::Init() {
}
void SessionBackend::AppendCommands(
- ScopedVector<sessions::SessionCommand> commands,
+ std::vector<std::unique_ptr<sessions::SessionCommand>> commands,
bool reset_first) {
Init();
// Make sure and check current_session_file_, if opening the file failed
@@ -252,13 +253,13 @@ void SessionBackend::ReadLastSessionCommands(
Init();
- ScopedVector<sessions::SessionCommand> commands;
+ std::vector<std::unique_ptr<sessions::SessionCommand>> commands;
ReadLastSessionCommandsImpl(&commands);
callback.Run(std::move(commands));
}
bool SessionBackend::ReadLastSessionCommandsImpl(
- ScopedVector<sessions::SessionCommand>* commands) {
+ std::vector<std::unique_ptr<sessions::SessionCommand>>* commands) {
Init();
SessionFileReader file_reader(GetLastSessionPath());
return file_reader.Read(type_, commands);
@@ -299,17 +300,16 @@ void SessionBackend::MoveCurrentSessionToLastSession() {
}
bool SessionBackend::ReadCurrentSessionCommandsImpl(
- ScopedVector<sessions::SessionCommand>* commands) {
+ std::vector<std::unique_ptr<sessions::SessionCommand>>* commands) {
Init();
SessionFileReader file_reader(GetCurrentSessionPath());
return file_reader.Read(type_, commands);
}
-bool SessionBackend::AppendCommandsToFile(base::File* file,
- const ScopedVector<sessions::SessionCommand>& commands) {
- for (ScopedVector<sessions::SessionCommand>::const_iterator i =
- commands.begin();
- i != commands.end(); ++i) {
+bool SessionBackend::AppendCommandsToFile(
+ base::File* file,
+ const std::vector<std::unique_ptr<sessions::SessionCommand>>& commands) {
+ for (auto i = commands.begin(); i != commands.end(); ++i) {
int wrote;
const size_type content_size = static_cast<size_type>((*i)->size());
const size_type total_size = content_size + sizeof(id_type);
diff --git a/chromium/components/sessions/core/session_backend.h b/chromium/components/sessions/core/session_backend.h
index d53fb8f6547..765ae5f08d7 100644
--- a/chromium/components/sessions/core/session_backend.h
+++ b/chromium/components/sessions/core/session_backend.h
@@ -64,8 +64,9 @@ class SESSIONS_EXPORT SessionBackend
// Appends the specified commands to the current file. If reset_first is
// true the the current file is recreated.
- void AppendCommands(ScopedVector<sessions::SessionCommand> commands,
- bool reset_first);
+ void AppendCommands(
+ std::vector<std::unique_ptr<sessions::SessionCommand>> commands,
+ bool reset_first);
// Invoked from the service to read the commands that make up the last
// session, invokes ReadLastSessionCommandsImpl to do the work.
@@ -77,7 +78,7 @@ class SESSIONS_EXPORT SessionBackend
//
// On success, the read commands are added to commands.
bool ReadLastSessionCommandsImpl(
- ScopedVector<sessions::SessionCommand>* commands);
+ std::vector<std::unique_ptr<sessions::SessionCommand>>* commands);
// Deletes the file containing the commands for the last session.
void DeleteLastSession();
@@ -92,7 +93,7 @@ class SESSIONS_EXPORT SessionBackend
// On success, the read commands are added to commands. It is up to the
// caller to delete the commands.
bool ReadCurrentSessionCommandsImpl(
- ScopedVector<sessions::SessionCommand>* commands);
+ std::vector<std::unique_ptr<sessions::SessionCommand>>* commands);
private:
friend class base::RefCountedThreadSafe<SessionBackend>;
@@ -114,7 +115,7 @@ class SESSIONS_EXPORT SessionBackend
// Appends the specified commands to the specified file.
bool AppendCommandsToFile(
base::File* file,
- const ScopedVector<sessions::SessionCommand>& commands);
+ const std::vector<std::unique_ptr<sessions::SessionCommand>>& commands);
const sessions::BaseSessionService::SessionType type_;
diff --git a/chromium/components/sessions/core/session_backend_unittest.cc b/chromium/components/sessions/core/session_backend_unittest.cc
index 76f0950dd0b..4e59f9cab91 100644
--- a/chromium/components/sessions/core/session_backend_unittest.cc
+++ b/chromium/components/sessions/core/session_backend_unittest.cc
@@ -10,23 +10,24 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
-#include "base/stl_util.h"
+#include "base/memory/ptr_util.h"
#include "base/strings/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace sessions {
namespace {
-typedef ScopedVector<sessions::SessionCommand> SessionCommands;
+typedef std::vector<std::unique_ptr<sessions::SessionCommand>> SessionCommands;
struct TestData {
sessions::SessionCommand::id_type command_id;
std::string data;
};
-sessions::SessionCommand* CreateCommandFromData(const TestData& data) {
- sessions::SessionCommand* command =
- new sessions::SessionCommand(
+std::unique_ptr<sessions::SessionCommand> CreateCommandFromData(
+ const TestData& data) {
+ std::unique_ptr<sessions::SessionCommand> command =
+ base::MakeUnique<sessions::SessionCommand>(
data.command_id,
static_cast<sessions::SessionCommand::size_type>(data.data.size()));
if (!data.data.empty())
@@ -73,7 +74,7 @@ TEST_F(SessionBackendTest, SimpleReadWrite) {
backend->ReadLastSessionCommandsImpl(&commands);
ASSERT_EQ(1U, commands.size());
- AssertCommandEqualsData(data, commands[0]);
+ AssertCommandEqualsData(data, commands[0].get());
commands.clear();
@@ -116,10 +117,9 @@ TEST_F(SessionBackendTest, RandomData) {
// Read previous data.
backend->ReadLastSessionCommandsImpl(&commands);
ASSERT_EQ(i, commands.size());
- for (std::vector<sessions::SessionCommand*>::iterator j =
- commands.begin(); j != commands.end(); ++j) {
- AssertCommandEqualsData(data[j - commands.begin()], *j);
- }
+ for (auto j = commands.begin(); j != commands.end(); ++j)
+ AssertCommandEqualsData(data[j - commands.begin()], j->get());
+
backend->AppendCommands(std::move(commands), false);
}
commands.push_back(CreateCommandFromData(data[i]));
@@ -135,17 +135,17 @@ TEST_F(SessionBackendTest, BigData) {
scoped_refptr<SessionBackend> backend(
new SessionBackend(sessions::BaseSessionService::SESSION_RESTORE, path_));
- ScopedVector<sessions::SessionCommand> commands;
+ std::vector<std::unique_ptr<sessions::SessionCommand>> commands;
commands.push_back(CreateCommandFromData(data[0]));
const sessions::SessionCommand::size_type big_size =
SessionBackend::kFileReadBufferSize + 100;
const sessions::SessionCommand::id_type big_id = 50;
- sessions::SessionCommand* big_command =
- new sessions::SessionCommand(big_id, big_size);
+ std::unique_ptr<sessions::SessionCommand> big_command =
+ base::MakeUnique<sessions::SessionCommand>(big_id, big_size);
reinterpret_cast<char*>(big_command->contents())[0] = 'a';
reinterpret_cast<char*>(big_command->contents())[big_size - 1] = 'z';
- commands.push_back(big_command);
+ commands.push_back(std::move(big_command));
commands.push_back(CreateCommandFromData(data[1]));
backend->AppendCommands(std::move(commands), false);
@@ -155,8 +155,8 @@ TEST_F(SessionBackendTest, BigData) {
backend->ReadLastSessionCommandsImpl(&commands);
ASSERT_EQ(3U, commands.size());
- AssertCommandEqualsData(data[0], commands[0]);
- AssertCommandEqualsData(data[1], commands[2]);
+ AssertCommandEqualsData(data[0], commands[0].get());
+ AssertCommandEqualsData(data[1], commands[2].get());
EXPECT_EQ(big_id, commands[1]->id());
ASSERT_EQ(big_size, commands[1]->size());
@@ -179,7 +179,7 @@ TEST_F(SessionBackendTest, EmptyCommand) {
SessionCommands commands;
backend->ReadLastSessionCommandsImpl(&commands);
ASSERT_EQ(1U, commands.size());
- AssertCommandEqualsData(empty_command, commands[0]);
+ AssertCommandEqualsData(empty_command, commands[0].get());
commands.clear();
}
@@ -206,7 +206,7 @@ TEST_F(SessionBackendTest, Truncate) {
// And make sure we get back the expected data.
ASSERT_EQ(1U, commands.size());
- AssertCommandEqualsData(second_data, commands[0]);
+ AssertCommandEqualsData(second_data, commands[0].get());
commands.clear();
}
diff --git a/chromium/components/sessions/core/session_service_commands.cc b/chromium/components/sessions/core/session_service_commands.cc
index 9e506ee1123..d066372deaa 100644
--- a/chromium/components/sessions/core/session_service_commands.cc
+++ b/chromium/components/sessions/core/session_service_commands.cc
@@ -113,7 +113,7 @@ enum PersistedWindowShowState {
// SHOW_STATE_INACTIVE (4) never persisted.
PERSISTED_SHOW_STATE_FULLSCREEN = 5,
PERSISTED_SHOW_STATE_DETACHED_DEPRECATED = 6,
- PERSISTED_SHOW_STATE_DOCKED = 7,
+ PERSISTED_SHOW_STATE_DOCKED_DEPRECATED = 7,
PERSISTED_SHOW_STATE_END = 7
};
@@ -140,8 +140,10 @@ PersistedWindowShowState ShowStateToPersistedShowState(
return PERSISTED_SHOW_STATE_MAXIMIZED;
case ui::SHOW_STATE_FULLSCREEN:
return PERSISTED_SHOW_STATE_FULLSCREEN;
+
+ // TODO(afakhry): Remove Docked Windows in M58.
case ui::SHOW_STATE_DOCKED:
- return PERSISTED_SHOW_STATE_DOCKED;
+ return PERSISTED_SHOW_STATE_DOCKED_DEPRECATED;
case ui::SHOW_STATE_DEFAULT:
case ui::SHOW_STATE_INACTIVE:
@@ -165,7 +167,7 @@ ui::WindowShowState PersistedShowStateToShowState(int state) {
return ui::SHOW_STATE_MAXIMIZED;
case PERSISTED_SHOW_STATE_FULLSCREEN:
return ui::SHOW_STATE_FULLSCREEN;
- case PERSISTED_SHOW_STATE_DOCKED:
+ case PERSISTED_SHOW_STATE_DOCKED_DEPRECATED:
return ui::SHOW_STATE_DOCKED;
case PERSISTED_SHOW_STATE_DETACHED_DEPRECATED:
return ui::SHOW_STATE_NORMAL;
@@ -323,17 +325,18 @@ void AddTabsToWindows(IdToSessionTab* tabs, IdToSessionWindow* windows) {
//
// This does NOT add any created SessionTabs to SessionWindow.tabs, that is
// done by AddTabsToWindows.
-bool CreateTabsAndWindows(const ScopedVector<SessionCommand>& data,
- IdToSessionTab* tabs,
- IdToSessionWindow* windows,
- SessionID::id_type* active_window_id) {
+bool CreateTabsAndWindows(
+ const std::vector<std::unique_ptr<SessionCommand>>& data,
+ IdToSessionTab* tabs,
+ IdToSessionWindow* windows,
+ SessionID::id_type* active_window_id) {
// If the file is corrupt (command with wrong size, or unknown command), we
// still return true and attempt to restore what we we can.
DVLOG(1) << "CreateTabsAndWindows";
- for (auto i = data.begin(); i != data.end(); ++i) {
+ for (const auto& command_ptr : data) {
const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
- const SessionCommand* command = *i;
+ const SessionCommand* command = command_ptr.get();
DVLOG(1) << "Read command " << (int) command->id();
switch (command->id()) {
@@ -825,10 +828,9 @@ bool ReplacePendingCommand(BaseSessionService* base_session_service,
(*command)->id() != kCommandSetActiveWindow) {
return false;
}
- for (ScopedVector<SessionCommand>::const_reverse_iterator i =
- base_session_service->pending_commands().rbegin();
+ for (auto i = base_session_service->pending_commands().rbegin();
i != base_session_service->pending_commands().rend(); ++i) {
- SessionCommand* existing_command = *i;
+ SessionCommand* existing_command = i->get();
if ((*command)->id() == kCommandUpdateTabNavigation &&
existing_command->id() == kCommandUpdateTabNavigation) {
std::unique_ptr<base::Pickle> command_pickle(
@@ -859,8 +861,8 @@ bool ReplacePendingCommand(BaseSessionService* base_session_service,
// existing_command is an update for the same tab/index pair. Replace
// it with the new one. We need to add to the end of the list just in
// case there is a prune command after the update command.
- base_session_service->EraseCommand(*(i.base() - 1));
- base_session_service->AppendRebuildCommand((std::move(*command)));
+ base_session_service->EraseCommand((i.base() - 1)->get());
+ base_session_service->AppendRebuildCommand(std::move(*command));
return true;
}
return false;
@@ -881,7 +883,7 @@ bool IsClosingCommand(SessionCommand* command) {
}
void RestoreSessionFromCommands(
- const ScopedVector<SessionCommand>& commands,
+ const std::vector<std::unique_ptr<SessionCommand>>& commands,
std::vector<std::unique_ptr<SessionWindow>>* valid_windows,
SessionID::id_type* active_window_id) {
IdToSessionTab tabs;
diff --git a/chromium/components/sessions/core/session_service_commands.h b/chromium/components/sessions/core/session_service_commands.h
index 7d0f80988d1..be854759c1a 100644
--- a/chromium/components/sessions/core/session_service_commands.h
+++ b/chromium/components/sessions/core/session_service_commands.h
@@ -96,7 +96,7 @@ SESSIONS_EXPORT bool IsClosingCommand(SessionCommand* command);
// id of the last active window, but it's only valid when this id corresponds
// to the id of one of the windows in |valid_windows|.
SESSIONS_EXPORT void RestoreSessionFromCommands(
- const ScopedVector<SessionCommand>& commands,
+ const std::vector<std::unique_ptr<SessionCommand>>& commands,
std::vector<std::unique_ptr<SessionWindow>>* valid_windows,
SessionID::id_type* active_window_id);
diff --git a/chromium/components/sessions/core/session_types.h b/chromium/components/sessions/core/session_types.h
index 92839c785b0..dbaabe6e24e 100644
--- a/chromium/components/sessions/core/session_types.h
+++ b/chromium/components/sessions/core/session_types.h
@@ -22,11 +22,6 @@
#include "ui/gfx/geometry/rect.h"
#include "url/gurl.h"
-namespace content {
-class BrowserContext;
-class NavigationEntry;
-}
-
namespace sessions {
// SessionTab ----------------------------------------------------------------
diff --git a/chromium/components/sessions/core/tab_restore_service_client.h b/chromium/components/sessions/core/tab_restore_service_client.h
index d26f0f1a565..a6aa4a8f4c6 100644
--- a/chromium/components/sessions/core/tab_restore_service_client.h
+++ b/chromium/components/sessions/core/tab_restore_service_client.h
@@ -9,7 +9,6 @@
#include "base/callback.h"
#include "base/files/file_path.h"
-#include "base/memory/scoped_vector.h"
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/sessions_export.h"
diff --git a/chromium/components/sessions/ios/ios_live_tab.h b/chromium/components/sessions/ios/ios_live_tab.h
index 645f88013d4..d4363a517a8 100644
--- a/chromium/components/sessions/ios/ios_live_tab.h
+++ b/chromium/components/sessions/ios/ios_live_tab.h
@@ -13,7 +13,6 @@
namespace content {
class NavigationManager;
-class NavigationEntry;
}
namespace sessions {
diff --git a/chromium/components/sessions/ios/ios_serialized_navigation_builder.h b/chromium/components/sessions/ios/ios_serialized_navigation_builder.h
index 7fb7abe360d..def0b1f1434 100644
--- a/chromium/components/sessions/ios/ios_serialized_navigation_builder.h
+++ b/chromium/components/sessions/ios/ios_serialized_navigation_builder.h
@@ -8,8 +8,6 @@
#include <memory>
#include <vector>
-#include "base/memory/scoped_vector.h"
-
namespace web {
class NavigationItem;
}
@@ -34,9 +32,7 @@ class IOSSerializedNavigationBuilder {
// Converts a set of SerializedNavigationEntrys into a list of
// NavigationItems with sequential page IDs.
- // TODO(crbug.com/561329): Change this API to return a
- // std::vector<scoped_ptr> in coordination with changing downstream clients.
- static ScopedVector<web::NavigationItem> ToNavigationItems(
+ static std::vector<std::unique_ptr<web::NavigationItem>> ToNavigationItems(
const std::vector<SerializedNavigationEntry>& navigations);
};
diff --git a/chromium/components/sessions/ios/ios_serialized_navigation_builder.mm b/chromium/components/sessions/ios/ios_serialized_navigation_builder.mm
index 232a1bdea59..70fc1530f17 100644
--- a/chromium/components/sessions/ios/ios_serialized_navigation_builder.mm
+++ b/chromium/components/sessions/ios/ios_serialized_navigation_builder.mm
@@ -52,15 +52,13 @@ IOSSerializedNavigationBuilder::ToNavigationItem(
}
// static
-ScopedVector<web::NavigationItem>
+std::vector<std::unique_ptr<web::NavigationItem>>
IOSSerializedNavigationBuilder::ToNavigationItems(
const std::vector<SerializedNavigationEntry>& navigations) {
- ScopedVector<web::NavigationItem> items;
- for (std::vector<SerializedNavigationEntry>::const_iterator it =
- navigations.begin();
- it != navigations.end(); ++it) {
- items.push_back(ToNavigationItem(&(*it)).release());
- }
+ std::vector<std::unique_ptr<web::NavigationItem>> items;
+ for (const auto& navigation : navigations)
+ items.push_back(ToNavigationItem(&navigation));
+
return items;
}
diff --git a/chromium/components/signin/DEPS b/chromium/components/signin/DEPS
index 89ffc6df341..20e335b0eba 100644
--- a/chromium/components/signin/DEPS
+++ b/chromium/components/signin/DEPS
@@ -5,6 +5,7 @@ include_rules = [
"+components/os_crypt",
"+components/pref_registry",
"+components/prefs",
+ "+components/sync_preferences",
"+components/webdata/common",
"+crypto",
"+google_apis/gaia",
diff --git a/chromium/components/signin/OWNERS b/chromium/components/signin/OWNERS
index b8a8f4aa83a..b3c82866293 100644
--- a/chromium/components/signin/OWNERS
+++ b/chromium/components/signin/OWNERS
@@ -1,2 +1,2 @@
-atwilson@chromium.org
+msarda@chromium.org
rogerta@chromium.org
diff --git a/chromium/components/signin/core/account_id/account_id.cc b/chromium/components/signin/core/account_id/account_id.cc
index 00ceb2e2f52..1ffac80db1e 100644
--- a/chromium/components/signin/core/account_id/account_id.cc
+++ b/chromium/components/signin/core/account_id/account_id.cc
@@ -16,25 +16,20 @@
namespace {
-// Known account types.
-const char kGoogle[] = "google";
-
// Serialization keys
const char kGaiaIdKey[] = "gaia_id";
const char kEmailKey[] = "email";
+const char kObjGuid[] = "obj_guid";
+const char kAccountTypeKey[] = "account_type";
+
+// Serialization values for account type.
+const char kGoogle[] = "google";
+const char kAd[] = "ad";
+const char kUnknown[] = "unknown";
// Prefix for GetAccountIdKey().
const char kKeyGaiaIdPrefix[] = "g-";
-
-struct GoogleStringSingleton {
- GoogleStringSingleton() : google(kGoogle) {}
-
- static GoogleStringSingleton* GetInstance() {
- return base::Singleton<GoogleStringSingleton>::get();
- }
-
- const std::string google;
-};
+const char kKeyAdIdPrefix[] = "a-";
} // anonymous namespace
@@ -49,26 +44,46 @@ struct AccountId::EmptyAccountId {
AccountId::AccountId() {}
-AccountId::AccountId(const std::string& gaia_id, const std::string& user_email)
- : gaia_id_(gaia_id), user_email_(user_email) {
+AccountId::AccountId(const std::string& id,
+ const std::string& user_email,
+ const AccountType& account_type)
+ : id_(id), user_email_(user_email), account_type_(account_type) {
+ DCHECK(account_type != AccountType::UNKNOWN || id.empty());
+ DCHECK(account_type != AccountType::ACTIVE_DIRECTORY || !id.empty());
// Fail if e-mail looks similar to GaiaIdKey.
LOG_ASSERT(!base::StartsWith(user_email, kKeyGaiaIdPrefix,
base::CompareCase::SENSITIVE) ||
user_email.find('@') != std::string::npos)
- << "Bad e-mail: '" << user_email << "' with gaia_id='" << gaia_id << "'";
+ << "Bad e-mail: '" << user_email << "' with gaia_id='" << id << "'";
// TODO(alemate): DCHECK(!email.empty());
// TODO(alemate): check gaia_id is not empty once it is required.
}
AccountId::AccountId(const AccountId& other)
- : gaia_id_(other.gaia_id_), user_email_(other.user_email_) {}
+ : id_(other.id_),
+ user_email_(other.user_email_),
+ account_type_(other.account_type_) {}
bool AccountId::operator==(const AccountId& other) const {
- return (this == &other) ||
- (gaia_id_ == other.gaia_id_ && user_email_ == other.user_email_) ||
- (!gaia_id_.empty() && gaia_id_ == other.gaia_id_) ||
- (!user_email_.empty() && user_email_ == other.user_email_);
+ if (this == &other)
+ return true;
+ if (account_type_ == AccountType::UNKNOWN ||
+ other.account_type_ == AccountType::UNKNOWN)
+ return user_email_ == other.user_email_;
+ if (account_type_ != other.account_type_)
+ return false;
+ switch (account_type_) {
+ case AccountType::GOOGLE:
+ return (id_ == other.id_ && user_email_ == other.user_email_) ||
+ (!id_.empty() && id_ == other.id_) ||
+ (!user_email_.empty() && user_email_ == other.user_email_);
+ case AccountType::ACTIVE_DIRECTORY:
+ return id_ == other.id_ && user_email_ == other.user_email_;
+ default:
+ NOTREACHED() << "Unknown account type";
+ }
+ return false;
}
bool AccountId::operator!=(const AccountId& other) const {
@@ -81,45 +96,70 @@ bool AccountId::operator<(const AccountId& right) const {
}
bool AccountId::empty() const {
- return gaia_id_.empty() && user_email_.empty();
+ return id_.empty() && user_email_.empty() &&
+ account_type_ == AccountType::UNKNOWN;
}
bool AccountId::is_valid() const {
- return /* !gaia_id_.empty() && */ !user_email_.empty();
+ switch (account_type_) {
+ case AccountType::GOOGLE:
+ return /* !id_.empty() && */ !user_email_.empty();
+ case AccountType::ACTIVE_DIRECTORY:
+ return !id_.empty() && !user_email_.empty();
+ case AccountType::UNKNOWN:
+ return id_.empty() && !user_email_.empty();
+ }
+ NOTREACHED();
+ return false;
}
void AccountId::clear() {
- gaia_id_.clear();
+ id_.clear();
user_email_.clear();
+ account_type_ = AccountType::UNKNOWN;
}
-const std::string& AccountId::GetAccountType() const {
- return GoogleStringSingleton::GetInstance()->google;
+AccountType AccountId::GetAccountType() const {
+ return account_type_;
}
const std::string& AccountId::GetGaiaId() const {
- return gaia_id_;
+ if (account_type_ != AccountType::GOOGLE)
+ NOTIMPLEMENTED() << "Failed to get gaia_id for non-Google account.";
+ return id_;
+}
+
+const std::string& AccountId::GetObjGuid() const {
+ if (account_type_ != AccountType::ACTIVE_DIRECTORY)
+ NOTIMPLEMENTED()
+ << "Failed to get obj_guid for non-Active Directory account.";
+ return id_;
}
const std::string& AccountId::GetUserEmail() const {
return user_email_;
}
+bool AccountId::HasAccountIdKey() const {
+ return account_type_ != AccountType::UNKNOWN && !id_.empty();
+}
+
const std::string AccountId::GetAccountIdKey() const {
#ifdef NDEBUG
- if (gaia_id_.empty())
- LOG(FATAL) << "GetAccountIdKey(): no gaia id for " << Serialize();
-
+ if (id_.empty())
+ LOG(FATAL) << "GetAccountIdKey(): no id for " << Serialize();
#else
- CHECK(!gaia_id_.empty());
+ CHECK(!id_.empty());
#endif
-
- return std::string(kKeyGaiaIdPrefix) + gaia_id_;
-}
-
-void AccountId::SetGaiaId(const std::string& gaia_id) {
- DCHECK(!gaia_id.empty());
- gaia_id_ = gaia_id;
+ switch (GetAccountType()) {
+ case AccountType::GOOGLE:
+ return std::string(kKeyGaiaIdPrefix) + id_;
+ case AccountType::ACTIVE_DIRECTORY:
+ return std::string(kKeyAdIdPrefix) + id_;
+ default:
+ NOTREACHED() << "Unknown account type";
+ }
+ return std::string();
}
void AccountId::SetUserEmail(const std::string& email) {
@@ -130,24 +170,75 @@ void AccountId::SetUserEmail(const std::string& email) {
// static
AccountId AccountId::FromUserEmail(const std::string& email) {
// TODO(alemate): DCHECK(!email.empty());
- return AccountId(std::string() /* gaia_id */, email);
+ return AccountId(std::string() /* id */, email, AccountType::UNKNOWN);
}
+// static
AccountId AccountId::FromGaiaId(const std::string& gaia_id) {
DCHECK(!gaia_id.empty());
- return AccountId(gaia_id, std::string() /* email */);
+ return AccountId(gaia_id, std::string() /* email */, AccountType::GOOGLE);
}
// static
AccountId AccountId::FromUserEmailGaiaId(const std::string& email,
const std::string& gaia_id) {
DCHECK(!(email.empty() && gaia_id.empty()));
- return AccountId(gaia_id, email);
+ return AccountId(gaia_id, email, AccountType::GOOGLE);
+}
+
+// static
+AccountId AccountId::AdFromUserEmailObjGuid(const std::string& email,
+ const std::string& obj_guid) {
+ DCHECK(!email.empty() && !obj_guid.empty());
+ return AccountId(obj_guid, email, AccountType::ACTIVE_DIRECTORY);
+}
+
+// static
+AccountId AccountId::AdFromObjGuid(const std::string& obj_guid) {
+ DCHECK(!obj_guid.empty());
+ return AccountId(obj_guid, std::string() /* email */,
+ AccountType::ACTIVE_DIRECTORY);
+}
+
+// static
+AccountType AccountId::StringToAccountType(
+ const std::string& account_type_string) {
+ if (account_type_string == kGoogle)
+ return AccountType::GOOGLE;
+ if (account_type_string == kAd)
+ return AccountType::ACTIVE_DIRECTORY;
+ if (account_type_string == kUnknown)
+ return AccountType::UNKNOWN;
+ NOTREACHED() << "Unknown account type " << account_type_string;
+ return AccountType::UNKNOWN;
+}
+
+// static
+std::string AccountId::AccountTypeToString(const AccountType& account_type) {
+ switch (account_type) {
+ case AccountType::GOOGLE:
+ return kGoogle;
+ case AccountType::ACTIVE_DIRECTORY:
+ return kAd;
+ case AccountType::UNKNOWN:
+ return kUnknown;
+ }
+ return std::string();
}
std::string AccountId::Serialize() const {
base::DictionaryValue value;
- value.SetString(kGaiaIdKey, gaia_id_);
+ switch (GetAccountType()) {
+ case AccountType::GOOGLE:
+ value.SetString(kGaiaIdKey, id_);
+ break;
+ case AccountType::ACTIVE_DIRECTORY:
+ value.SetString(kObjGuid, id_);
+ break;
+ case AccountType::UNKNOWN:
+ break;
+ }
+ value.SetString(kAccountTypeKey, AccountTypeToString(GetAccountType()));
value.SetString(kEmailKey, user_email_);
std::string serialized;
@@ -167,23 +258,65 @@ bool AccountId::Deserialize(const std::string& serialized,
std::string gaia_id;
std::string user_email;
+ std::string obj_guid;
+ std::string account_type_string;
+ AccountType account_type = AccountType::GOOGLE;
const bool found_gaia_id = dictionary_value->GetString(kGaiaIdKey, &gaia_id);
const bool found_user_email =
dictionary_value->GetString(kEmailKey, &user_email);
-
- if (!found_gaia_id)
- LOG(ERROR) << "gaia_id is not found in '" << serialized << "'";
-
- if (!found_user_email)
- LOG(ERROR) << "user_email is not found in '" << serialized << "'";
-
- if (!found_gaia_id && !found_user_email)
- return false;
-
- *account_id = FromUserEmailGaiaId(user_email, gaia_id);
-
- return true;
+ const bool found_obj_guid = dictionary_value->GetString(kObjGuid, &obj_guid);
+ const bool found_account_type =
+ dictionary_value->GetString(kAccountTypeKey, &account_type_string);
+ if (found_account_type)
+ account_type = StringToAccountType(account_type_string);
+
+ switch (account_type) {
+ case AccountType::GOOGLE:
+ if (found_obj_guid)
+ DLOG(ERROR) << "AccountType is 'google' but obj_guid is found in '"
+ << serialized << "'";
+
+ if (!found_gaia_id)
+ DLOG(ERROR) << "gaia_id is not found in '" << serialized << "'";
+
+ if (!found_user_email)
+ DLOG(ERROR) << "user_email is not found in '" << serialized << "'";
+
+ if (!found_gaia_id && !found_user_email)
+ return false;
+
+ *account_id = FromUserEmailGaiaId(user_email, gaia_id);
+ return true;
+
+ case AccountType::ACTIVE_DIRECTORY:
+ if (found_gaia_id)
+ DLOG(ERROR)
+ << "AccountType is 'active directory' but gaia_id is found in '"
+ << serialized << "'";
+
+ if (!found_obj_guid) {
+ DLOG(ERROR) << "obj_guid is not found in '" << serialized << "'";
+ return false;
+ }
+
+ if (!found_user_email) {
+ DLOG(ERROR) << "user_email is not found in '" << serialized << "'";
+ }
+
+ if (!found_obj_guid || !found_user_email)
+ return false;
+
+ *account_id = AdFromUserEmailObjGuid(user_email, obj_guid);
+ return true;
+
+ case AccountType::UNKNOWN:
+ if (!found_user_email)
+ return false;
+ *account_id = FromUserEmail(user_email);
+ return true;
+ }
+ return false;
}
const AccountId& EmptyAccountId() {
diff --git a/chromium/components/signin/core/account_id/account_id.h b/chromium/components/signin/core/account_id/account_id.h
index 7cba697805d..81082b5fe04 100644
--- a/chromium/components/signin/core/account_id/account_id.h
+++ b/chromium/components/signin/core/account_id/account_id.h
@@ -10,18 +10,35 @@
#include <string>
#include "base/containers/hash_tables.h"
+enum class AccountType { UNKNOWN, GOOGLE, ACTIVE_DIRECTORY };
+
// Type that contains enough information to identify user.
//
// TODO(alemate): we are in the process of moving away from std::string as a
// type for storing user identifier to AccountId. At this time GaiaId is mostly
// empty, so this type is used as a replacement for e-mail string.
// But in near future AccountId will become full feature user identifier.
+// TODO(alemate): Rename functions and fields to reflect different types of
+// accounts. (see crbug.com/672253)
class AccountId {
public:
struct EmptyAccountId;
+ // Creates an empty account id.
+ //
+ // Note: This constructor is public as it is required for mojo serialization
+ // To create an AccountId object, prefer using the static FromXXXX methods or
+ // the EmptyAccountId method when creating an empty account id.
+ AccountId();
+
AccountId(const AccountId& other);
+ // If any of the comparable AccountIds has AccountType == UNKNOWN then it
+ // compares emails.
+ // If both are not UNKNOWN and not equal then it returns false.
+ // If AccountType == GOOGLE then it checks if either ids or emails are equal.
+ // If AccountType == ACTIVE_DIRECTORY then it checks if ids and emails are
+ // equal.
bool operator==(const AccountId& other) const;
bool operator!=(const AccountId& other) const;
bool operator<(const AccountId& right) const;
@@ -31,28 +48,45 @@ class AccountId {
bool is_valid() const;
void clear();
- const std::string& GetAccountType() const;
+ AccountType GetAccountType() const;
const std::string& GetGaiaId() const;
+ const std::string& GetObjGuid() const;
// Users of AccountId should make no assumptions on the format of email.
// I.e. it cannot be used as account identifier, because it is (in general)
// non-comparable.
const std::string& GetUserEmail() const;
+ // Returns true if |GetAccountIdKey| would return valid key.
+ bool HasAccountIdKey() const;
// This returns prefixed some string that can be used as a storage key.
// You should make no assumptions on the format of this string.
const std::string GetAccountIdKey() const;
- void SetGaiaId(const std::string& gaia_id);
void SetUserEmail(const std::string& email);
// This method is to be used during transition period only.
+ // AccountId with UNKNOWN AccountType;
static AccountId FromUserEmail(const std::string& user_email);
+ // AccountId with GOOGLE AccountType;
// This method is to be used during transition period only.
static AccountId FromGaiaId(const std::string& gaia_id);
// This method is the preferred way to construct AccountId if you have
// full account information.
+ // AccountId with GOOGLE AccountType;
static AccountId FromUserEmailGaiaId(const std::string& user_email,
const std::string& gaia_id);
+ // These methods are used to construct Active Directory AccountIds.
+ // AccountId with ACTIVE_DIRECTORY AccountType;
+ static AccountId AdFromUserEmailObjGuid(const std::string& email,
+ const std::string& obj_guid);
+ // AccountId with ACTIVE_DIRECTORY AccountType;
+ static AccountId AdFromObjGuid(const std::string& obj_guid);
+
+ // Translation functions between AccountType and std::string. Used for
+ // serialization.
+ static AccountType StringToAccountType(
+ const std::string& account_type_string);
+ static std::string AccountTypeToString(const AccountType& account_type);
// These are (for now) unstable and cannot be used to store serialized data to
// persistent storage. Only in-memory storage is safe.
@@ -63,11 +97,13 @@ class AccountId {
AccountId* out_account_id);
private:
- AccountId();
- AccountId(const std::string& gaia_id, const std::string& user_email);
+ AccountId(const std::string& id,
+ const std::string& user_email,
+ const AccountType& account_type);
- std::string gaia_id_;
+ std::string id_;
std::string user_email_;
+ AccountType account_type_ = AccountType::UNKNOWN;
};
// Returns a reference to a singleton.
diff --git a/chromium/components/signin/core/browser/BUILD.gn b/chromium/components/signin/core/browser/BUILD.gn
index 602802d96fe..a381719d0ba 100644
--- a/chromium/components/signin/core/browser/BUILD.gn
+++ b/chromium/components/signin/core/browser/BUILD.gn
@@ -162,8 +162,9 @@ source_set("unit_tests") {
":test_support",
"//components/content_settings/core/browser",
"//components/os_crypt:test_support",
- "//components/pref_registry:test_support",
+ "//components/pref_registry:pref_registry",
"//components/signin/core/common",
+ "//components/sync_preferences:test_support",
"//testing/gmock",
]
diff --git a/chromium/components/signin/core/browser/about_signin_internals.h b/chromium/components/signin/core/browser/about_signin_internals.h
index 0e32b0eb345..3caeb6ebd3e 100644
--- a/chromium/components/signin/core/browser/about_signin_internals.h
+++ b/chromium/components/signin/core/browser/about_signin_internals.h
@@ -10,7 +10,6 @@
#include <string>
#include "base/macros.h"
-#include "base/memory/linked_ptr.h"
#include "base/observer_list.h"
#include "base/values.h"
#include "components/keyed_service/core/keyed_service.h"
@@ -26,7 +25,6 @@ class PrefRegistrySyncable;
}
class AccountTrackerService;
-class GaiaAuthFetcher;
class ProfileOAuth2TokenService;
class SigninClient;
diff --git a/chromium/components/signin/core/browser/account_fetcher_service.cc b/chromium/components/signin/core/browser/account_fetcher_service.cc
index 2cd588b94d3..7f25b2d377c 100644
--- a/chromium/components/signin/core/browser/account_fetcher_service.cc
+++ b/chromium/components/signin/core/browser/account_fetcher_service.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
#include "base/profiler/scoped_tracker.h"
#include "base/trace_event/trace_event.h"
@@ -201,13 +202,16 @@ void AccountFetcherService::StartFetchingUserInfo(
DCHECK(CalledOnValidThread());
DCHECK(network_fetches_enabled_);
- if (!ContainsKey(user_info_requests_, account_id)) {
+ std::unique_ptr<AccountInfoFetcher>& request =
+ user_info_requests_[account_id];
+ if (!request) {
DVLOG(1) << "StartFetching " << account_id;
- std::unique_ptr<AccountInfoFetcher> fetcher(new AccountInfoFetcher(
- token_service_, signin_client_->GetURLRequestContext(), this,
- account_id));
- user_info_requests_.set(account_id, std::move(fetcher));
- user_info_requests_.get(account_id)->Start();
+ std::unique_ptr<AccountInfoFetcher> fetcher =
+ base::MakeUnique<AccountInfoFetcher>(
+ token_service_, signin_client_->GetURLRequestContext(), this,
+ account_id);
+ request = std::move(fetcher);
+ request->Start();
}
}
@@ -271,7 +275,7 @@ void AccountFetcherService::SendRefreshTokenAnnotationRequest(
// If request was sent AccountFetcherService needs to own request till it
// finishes.
if (request)
- refresh_token_annotation_requests_.set(account_id, std::move(request));
+ refresh_token_annotation_requests_[account_id] = std::move(request);
}
#endif
}
diff --git a/chromium/components/signin/core/browser/account_fetcher_service.h b/chromium/components/signin/core/browser/account_fetcher_service.h
index 0f3e57515e8..5ca34f1fc8a 100644
--- a/chromium/components/signin/core/browser/account_fetcher_service.h
+++ b/chromium/components/signin/core/browser/account_fetcher_service.h
@@ -8,8 +8,8 @@
#include <stdint.h>
#include <memory>
+#include <unordered_map>
-#include "base/containers/scoped_ptr_hash_map.h"
#include "base/macros.h"
#include "base/threading/non_thread_safe.h"
#include "base/timer/timer.h"
@@ -132,11 +132,11 @@ class AccountFetcherService : public KeyedService,
std::unique_ptr<ChildAccountInfoFetcher> child_info_request_;
// Holds references to account info fetchers keyed by account_id.
- base::ScopedPtrHashMap<std::string, std::unique_ptr<AccountInfoFetcher>>
+ std::unordered_map<std::string, std::unique_ptr<AccountInfoFetcher>>
user_info_requests_;
// Holds references to refresh token annotation requests keyed by account_id.
- base::ScopedPtrHashMap<std::string,
- std::unique_ptr<RefreshTokenAnnotationRequest>>
+ std::unordered_map<std::string,
+ std::unique_ptr<RefreshTokenAnnotationRequest>>
refresh_token_annotation_requests_;
DISALLOW_COPY_AND_ASSIGN(AccountFetcherService);
diff --git a/chromium/components/signin/core/browser/account_investigator_unittest.cc b/chromium/components/signin/core/browser/account_investigator_unittest.cc
index d70acac2036..11050b6f45c 100644
--- a/chromium/components/signin/core/browser/account_investigator_unittest.cc
+++ b/chromium/components/signin/core/browser/account_investigator_unittest.cc
@@ -13,13 +13,13 @@
#include "base/test/histogram_tester.h"
#include "base/timer/timer.h"
#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
#include "components/signin/core/browser/fake_signin_manager.h"
#include "components/signin/core/browser/signin_metrics.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 "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/google_service_auth_error.h"
@@ -155,7 +155,7 @@ class AccountInvestigatorTest : public testing::Test {
private:
// Timer needs a message loop.
base::MessageLoop message_loop_;
- user_prefs::TestingPrefServiceSyncable prefs_;
+ sync_preferences::TestingPrefServiceSyncable prefs_;
AccountTrackerService account_tracker_service_;
TestSigninClient signin_client_;
FakeSigninManager signin_manager_;
diff --git a/chromium/components/signin/core/browser/account_reconcilor.h b/chromium/components/signin/core/browser/account_reconcilor.h
index 68ad603880c..6217588d974 100644
--- a/chromium/components/signin/core/browser/account_reconcilor.h
+++ b/chromium/components/signin/core/browser/account_reconcilor.h
@@ -31,10 +31,6 @@
class ProfileOAuth2TokenService;
class SigninClient;
-namespace net {
-class CanonicalCookie;
-}
-
class AccountReconcilor : public KeyedService,
public content_settings::Observer,
public GaiaCookieManagerService::Observer,
diff --git a/chromium/components/signin/core/browser/android/BUILD.gn b/chromium/components/signin/core/browser/android/BUILD.gn
index c9f5bc3f598..400ccabe384 100644
--- a/chromium/components/signin/core/browser/android/BUILD.gn
+++ b/chromium/components/signin/core/browser/android/BUILD.gn
@@ -35,6 +35,7 @@ android_library("javatests") {
":signin_java_test_support",
"//base:base_java",
"//base:base_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
]
java_files = [ "javatests/src/org/chromium/components/signin/test/AccountManagerHelperTest.java" ]
diff --git a/chromium/components/signin/core/browser/fake_account_fetcher_service.h b/chromium/components/signin/core/browser/fake_account_fetcher_service.h
index 3e1d6de5cb5..761940ad228 100644
--- a/chromium/components/signin/core/browser/fake_account_fetcher_service.h
+++ b/chromium/components/signin/core/browser/fake_account_fetcher_service.h
@@ -12,10 +12,6 @@
class KeyedService;
-namespace content {
-class BrowserContext;
-}
-
// AccountTrackerService is a KeyedService that retrieves and caches GAIA
// information about Google Accounts. This fake class can be used in tests
// to prevent AccountTrackerService from sending network requests.
diff --git a/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc b/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc
index bf95c888a37..10bd27a1e98 100644
--- a/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc
+++ b/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc
@@ -109,8 +109,14 @@ void FakeGaiaCookieManagerService::SetListAccountsResponseTwoAccountsWithExpiry(
}
std::string FakeGaiaCookieManagerService::GetSourceForRequest(
- const GaiaCookieManagerService::GaiaCookieRequest& request,
- const std::string& source_default) {
+ const GaiaCookieManagerService::GaiaCookieRequest& request) {
+ // Always return the default. This value must match the source used in the
+ // SetXXXResponseYYY methods above so that the test URLFetcher factory will
+ // be able to find the URLs.
+ return GaiaConstants::kChromeSource;
+}
+
+std::string FakeGaiaCookieManagerService::GetDefaultSourceForRequest() {
// Always return the default. This value must match the source used in the
// SetXXXResponseYYY methods above so that the test URLFetcher factory will
// be able to find the URLs.
diff --git a/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.h b/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.h
index 4d67f40b41a..504eb03e125 100644
--- a/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.h
+++ b/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.h
@@ -11,10 +11,6 @@
#include "components/signin/core/browser/gaia_cookie_manager_service.h"
#include "net/url_request/test_url_fetcher_factory.h"
-namespace content {
-class BrowserContext;
-}
-
class FakeGaiaCookieManagerService : public GaiaCookieManagerService {
public:
FakeGaiaCookieManagerService(OAuth2TokenService* token_service,
@@ -44,8 +40,8 @@ class FakeGaiaCookieManagerService : public GaiaCookieManagerService {
private:
std::string GetSourceForRequest(
- const GaiaCookieManagerService::GaiaCookieRequest& request,
- const std::string& source_default) override;
+ const GaiaCookieManagerService::GaiaCookieRequest& request) override;
+ std::string GetDefaultSourceForRequest() override;
// Provide a fake response for calls to /ListAccounts.
net::FakeURLFetcherFactory* url_fetcher_factory_;
diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc b/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc
index 9cfd9c9ae0f..3d14c265528 100644
--- a/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -62,6 +62,9 @@ const int kMaxFetcherRetries = 8;
// accounts have changed in the content-area.
const char* kGaiaCookieName = "APISID";
+// String format appended to GAIA fetcher source if request context has changed.
+const char* kRequestContextChangedTag = "__changed_%d__";
+
enum GaiaCookieRequestType {
ADD_ACCOUNT,
LOG_OUT_ALL_ACCOUNTS,
@@ -69,6 +72,11 @@ enum GaiaCookieRequestType {
LIST_ACCOUNTS
};
+void AppendRequestContextChangedTagIfNeeded(std::string* source, int changes) {
+ if (changes != 0)
+ base::StringAppendF(source, kRequestContextChangedTag, changes);
+}
+
} // namespace
GaiaCookieManagerService::GaiaCookieRequest::GaiaCookieRequest(
@@ -99,6 +107,7 @@ GaiaCookieManagerService::GaiaCookieRequest::CreateLogOutRequest(
source);
}
+// static
GaiaCookieManagerService::GaiaCookieRequest
GaiaCookieManagerService::GaiaCookieRequest::CreateListAccountsRequest(
const std::string& source) {
@@ -134,7 +143,8 @@ void GaiaCookieManagerService::ExternalCcResultFetcher::Start() {
results_.clear();
helper_->gaia_auth_fetcher_.reset(
helper_->signin_client_->CreateGaiaAuthFetcher(
- this, helper_->source_, helper_->request_context()));
+ this, helper_->GetDefaultSourceForRequest(),
+ helper_->request_context()));
helper_->gaia_auth_fetcher_->StartGetCheckConnectionInfo();
// Some fetches may timeout. Start a timer to decide when the result fetcher
@@ -462,9 +472,20 @@ void GaiaCookieManagerService::CancelAll() {
}
std::string GaiaCookieManagerService::GetSourceForRequest(
- const GaiaCookieManagerService::GaiaCookieRequest& request,
- const std::string& source_default) {
- return request.source().empty() ? source_default : request.source();
+ const GaiaCookieManagerService::GaiaCookieRequest& request) {
+ std::string source = request.source().empty() ? source_ : request.source();
+ AppendRequestContextChangedTagIfNeeded(
+ &source,
+ signin_client_->number_of_request_context_pointer_changes());
+ return source;
+}
+
+std::string GaiaCookieManagerService::GetDefaultSourceForRequest() {
+ std::string source = source_;
+ AppendRequestContextChangedTagIfNeeded(
+ &source,
+ signin_client_->number_of_request_context_pointer_changes());
+ return source;
}
void GaiaCookieManagerService::OnCookieChanged(
@@ -480,13 +501,22 @@ void GaiaCookieManagerService::OnCookieChanged(
// cause an endless loop (see crbug.com/516070).
if (requests_.empty()) {
// Build gaia "source" based on cause to help track down channel id issues.
- std::string source(source_);
+ std::string source(GetDefaultSourceForRequest());
switch (cause) {
case net::CookieStore::ChangeCause::INSERTED:
source += "INSERTED";
break;
- case net::CookieStore::ChangeCause::EXPLICIT:
- source += "EXPLICIT";
+ case net::CookieStore::ChangeCause::EXPLICIT_DELETE:
+ source += "EXPLICIT_DELETE";
+ break;
+ case net::CookieStore::ChangeCause::EXPLICIT_DUPLICATE_IN_BACKING_STORE:
+ source += "EXPLICIT_DUPLICATE_IN_BACKING_STORE";
+ break;
+ case net::CookieStore::ChangeCause::EXPLICIT_DONT_RECORD:
+ source += "EXPLICIT_DONT_RECORD";
+ break;
+ case net::CookieStore::ChangeCause::EXPLICIT_LAST_ENTRY:
+ source += "EXPLICIT_LAST_ENTRY";
break;
case net::CookieStore::ChangeCause::UNKNOWN_DELETION:
source += "UNKNOWN_DELETION";
@@ -700,7 +730,8 @@ void GaiaCookieManagerService::StartFetchingUbertoken() {
VLOG(1) << "GaiaCookieManagerService::StartFetchingUbertoken account_id="
<< requests_.front().account_id();
uber_token_fetcher_.reset(new UbertokenFetcher(
- token_service_, this, source_, signin_client_->GetURLRequestContext(),
+ token_service_, this, GetDefaultSourceForRequest(),
+ signin_client_->GetURLRequestContext(),
base::Bind(&SigninClient::CreateGaiaAuthFetcher,
base::Unretained(signin_client_))));
if (access_token_.empty()) {
@@ -714,7 +745,7 @@ void GaiaCookieManagerService::StartFetchingUbertoken() {
void GaiaCookieManagerService::StartFetchingMergeSession() {
DCHECK(!uber_token_.empty());
gaia_auth_fetcher_.reset(signin_client_->CreateGaiaAuthFetcher(
- this, GetSourceForRequest(requests_.front(), source_),
+ this, GetSourceForRequest(requests_.front()),
signin_client_->GetURLRequestContext()));
gaia_auth_fetcher_->StartMergeSession(uber_token_,
@@ -725,7 +756,7 @@ void GaiaCookieManagerService::StartFetchingLogOut() {
DCHECK(requests_.front().request_type() == GaiaCookieRequestType::LOG_OUT);
VLOG(1) << "GaiaCookieManagerService::StartFetchingLogOut";
gaia_auth_fetcher_.reset(signin_client_->CreateGaiaAuthFetcher(
- this, GetSourceForRequest(requests_.front(), source_),
+ this, GetSourceForRequest(requests_.front()),
signin_client_->GetURLRequestContext()));
gaia_auth_fetcher_->StartLogOut();
}
@@ -733,7 +764,7 @@ void GaiaCookieManagerService::StartFetchingLogOut() {
void GaiaCookieManagerService::StartFetchingListAccounts() {
VLOG(1) << "GaiaCookieManagerService::ListAccounts";
gaia_auth_fetcher_.reset(signin_client_->CreateGaiaAuthFetcher(
- this, GetSourceForRequest(requests_.front(), source_),
+ this, GetSourceForRequest(requests_.front()),
signin_client_->GetURLRequestContext()));
gaia_auth_fetcher_->StartListAccounts();
}
diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service.h b/chromium/components/signin/core/browser/gaia_cookie_manager_service.h
index 72f178d72bc..ae4246422c1 100644
--- a/chromium/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/chromium/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -242,8 +242,11 @@ class GaiaCookieManagerService : public KeyedService,
// Returns the source value to use for GaiaFetcher requests. This is
// virtual to allow tests and fake classes to override.
virtual std::string GetSourceForRequest(
- const GaiaCookieManagerService::GaiaCookieRequest& request,
- const std::string& source_default);
+ const GaiaCookieManagerService::GaiaCookieRequest& request);
+
+ // Returns the default source value to use for GaiaFetcher requests. This is
+ // virtual to allow tests and fake classes to override.
+ virtual std::string GetDefaultSourceForRequest();
// Called when a cookie changes. If the cookie relates to a GAIA APISID
// cookie, then we call ListAccounts and fire OnGaiaAccountsInCookieUpdated.
diff --git a/chromium/components/signin/core/browser/signin_client.cc b/chromium/components/signin/core/browser/signin_client.cc
index 9473e0bb59c..f9018c85900 100644
--- a/chromium/components/signin/core/browser/signin_client.cc
+++ b/chromium/components/signin/core/browser/signin_client.cc
@@ -36,6 +36,10 @@ void SigninClient::PreSignOut(const base::Callback<void()>& sign_out) {
sign_out.Run();
}
+int SigninClient::number_of_request_context_pointer_changes() const {
+ return 0;
+}
+
void SigninClient::SignOut() {
GetPrefs()->ClearPref(prefs::kGoogleServicesSigninScopedDeviceId);
OnSignedOut();
diff --git a/chromium/components/signin/core/browser/signin_client.h b/chromium/components/signin/core/browser/signin_client.h
index 6c2c17dc323..43d90bc9d69 100644
--- a/chromium/components/signin/core/browser/signin_client.h
+++ b/chromium/components/signin/core/browser/signin_client.h
@@ -16,7 +16,6 @@
#include "url/gurl.h"
class PrefService;
-class SigninManagerBase;
class TokenWebData;
namespace content_settings {
@@ -127,6 +126,10 @@ class SigninClient : public KeyedService {
// Called once the credentials has been copied to another SigninManager.
virtual void AfterCredentialsCopied() {}
+ // Used do debug channel id binding problem in chrome. Returns the number of
+ // times the request context changed unexpectedly.
+ virtual int number_of_request_context_pointer_changes() const;
+
protected:
// Returns device id that is scoped to single signin.
// Stores the ID in the kGoogleServicesSigninScopedDeviceId pref.
diff --git a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc
index cd560ab5ee4..239b0a57e96 100644
--- a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc
+++ b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc
@@ -7,9 +7,9 @@
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "components/content_settings/core/browser/cookie_settings.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
#include "components/signin/core/browser/signin_header_helper.h"
#include "components/signin/core/common/signin_switches.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -58,7 +58,7 @@ class SigninHeaderHelperTest : public testing::Test {
base::MessageLoop loop_;
- user_prefs::TestingPrefServiceSyncable prefs_;
+ sync_preferences::TestingPrefServiceSyncable prefs_;
net::TestURLRequestContext url_request_context_;
scoped_refptr<HostContentSettingsMap> settings_map_;
diff --git a/chromium/components/signin/core/browser/signin_manager.cc b/chromium/components/signin/core/browser/signin_manager.cc
index fc3a0c503a5..2e3a7044510 100644
--- a/chromium/components/signin/core/browser/signin_manager.cc
+++ b/chromium/components/signin/core/browser/signin_manager.cc
@@ -269,7 +269,7 @@ bool SigninManager::IsSigninAllowed() const {
}
void SigninManager::OnSigninAllowedPrefChanged() {
- if (!IsSigninAllowed())
+ if (!IsSigninAllowed() && (IsAuthenticated() || AuthInProgress()))
SignOut(signin_metrics::SIGNOUT_PREF_CHANGED,
signin_metrics::SignoutDelete::IGNORE_METRIC);
}
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 04d54fa8a7f..699e61d6190 100644
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider.cc
+++ b/chromium/components/signin/core/browser/signin_status_metrics_provider.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/location.h"
+#include "base/memory/ptr_util.h"
#include "base/metrics/histogram.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -45,9 +46,11 @@ void SigninStatusMetricsProvider::ProvideGeneralMetrics(
}
// static
-SigninStatusMetricsProvider* SigninStatusMetricsProvider::CreateInstance(
+std::unique_ptr<SigninStatusMetricsProvider>
+SigninStatusMetricsProvider::CreateInstance(
std::unique_ptr<SigninStatusMetricsProviderDelegate> delegate) {
- return new SigninStatusMetricsProvider(std::move(delegate), false);
+ return base::WrapUnique(
+ new SigninStatusMetricsProvider(std::move(delegate), false));
}
void SigninStatusMetricsProvider::OnSigninManagerCreated(
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 859363ea813..9d6e663d126 100644
--- a/chromium/components/signin/core/browser/signin_status_metrics_provider.h
+++ b/chromium/components/signin/core/browser/signin_status_metrics_provider.h
@@ -37,7 +37,7 @@ class SigninStatusMetricsProvider : public SigninStatusMetricsProviderBase,
metrics::ChromeUserMetricsExtension* uma_proto) override;
// Factory method, creates a new instance of this class.
- static SigninStatusMetricsProvider* CreateInstance(
+ static std::unique_ptr<SigninStatusMetricsProvider> CreateInstance(
std::unique_ptr<SigninStatusMetricsProviderDelegate> delegate);
// Update the sign-in status when a SigninManager is created.
diff --git a/chromium/components/signin/core/browser/webdata/token_web_data.cc b/chromium/components/signin/core/browser/webdata/token_web_data.cc
index 8c390faf642..0c6425529c0 100644
--- a/chromium/components/signin/core/browser/webdata/token_web_data.cc
+++ b/chromium/components/signin/core/browser/webdata/token_web_data.cc
@@ -6,7 +6,7 @@
#include "base/bind.h"
#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted_delete_on_message_loop.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "components/signin/core/browser/webdata/token_service_table.h"
@@ -16,11 +16,10 @@ using base::Bind;
using base::Time;
class TokenWebDataBackend
- : public base::RefCountedDeleteOnMessageLoop<TokenWebDataBackend> {
-
+ : public base::RefCountedDeleteOnSequence<TokenWebDataBackend> {
public:
TokenWebDataBackend(scoped_refptr<base::SingleThreadTaskRunner> db_thread)
- : base::RefCountedDeleteOnMessageLoop<TokenWebDataBackend>(db_thread) {}
+ : base::RefCountedDeleteOnSequence<TokenWebDataBackend>(db_thread) {}
WebDatabase::State RemoveAllTokens(WebDatabase* db) {
if (TokenServiceTable::FromWebDatabase(db)->RemoveAllTokens()) {
@@ -59,7 +58,7 @@ class TokenWebDataBackend
}
private:
- friend class base::RefCountedDeleteOnMessageLoop<TokenWebDataBackend>;
+ friend class base::RefCountedDeleteOnSequence<TokenWebDataBackend>;
friend class base::DeleteHelper<TokenWebDataBackend>;
};
diff --git a/chromium/components/signin/ios/browser/BUILD.gn b/chromium/components/signin/ios/browser/BUILD.gn
index c0a9f35c7ed..9a736cc272a 100644
--- a/chromium/components/signin/ios/browser/BUILD.gn
+++ b/chromium/components/signin/ios/browser/BUILD.gn
@@ -55,11 +55,11 @@ source_set("unit_tests") {
deps = [
":test_support",
- "//components/pref_registry:test_support",
"//components/prefs:test_support",
"//components/signin/core/browser",
"//components/signin/core/browser:test_support",
"//components/signin/core/common",
+ "//components/sync_preferences:test_support",
"//ios/web",
"//ios/web:test_support",
"//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 cc61d8fe64a..1f7a1e6eb3f 100644
--- a/chromium/components/signin/ios/browser/account_consistency_service.h
+++ b/chromium/components/signin/ios/browser/account_consistency_service.h
@@ -116,7 +116,7 @@ class AccountConsistencyService : public KeyedService,
// Can return nil if the browser state is not active.
WKWebView* GetWKWebView();
// Actually creates a WKWebView. Virtual for testing.
- virtual WKWebView* CreateWKWebView() NS_RETURNS_RETAINED;
+ virtual WKWebView* BuildWKWebView();
// Stops any page loading in the WKWebView currently in use and releases it.
void ResetWKWebView();
diff --git a/chromium/components/signin/ios/browser/account_consistency_service.mm b/chromium/components/signin/ios/browser/account_consistency_service.mm
index 1ce0e2e2cc4..5f15df39e15 100644
--- a/chromium/components/signin/ios/browser/account_consistency_service.mm
+++ b/chromium/components/signin/ios/browser/account_consistency_service.mm
@@ -4,7 +4,7 @@
#include "components/signin/ios/browser/account_consistency_service.h"
-#include <WebKit/WebKit.h>
+#import <WebKit/WebKit.h>
#import "base/ios/weak_nsobject.h"
#include "base/logging.h"
@@ -390,7 +390,7 @@ WKWebView* AccountConsistencyService::GetWKWebView() {
return nil;
}
if (!web_view_) {
- web_view_.reset(CreateWKWebView());
+ web_view_.reset([BuildWKWebView() retain]);
navigation_delegate_.reset([[AccountConsistencyNavigationDelegate alloc]
initWithCallback:base::Bind(&AccountConsistencyService::
FinishedApplyingCookieRequest,
@@ -400,8 +400,8 @@ WKWebView* AccountConsistencyService::GetWKWebView() {
return web_view_.get();
}
-WKWebView* AccountConsistencyService::CreateWKWebView() {
- return web::CreateWKWebView(CGRectZero, browser_state_);
+WKWebView* AccountConsistencyService::BuildWKWebView() {
+ return web::BuildWKWebView(CGRectZero, browser_state_);
}
void AccountConsistencyService::ResetWKWebView() {
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 984a802ead7..ba160b6bf69 100644
--- a/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm
+++ b/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm
@@ -9,16 +9,16 @@
#include <memory>
#import "base/mac/scoped_nsobject.h"
-#include "components/pref_registry/testing_pref_service_syncable.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"
#include "components/signin/core/browser/gaia_cookie_manager_service.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 "google_apis/gaia/gaia_constants.h"
-#include "ios/web/public/test/test_browser_state.h"
-#include "ios/web/public/test/test_web_state.h"
+#include "ios/web/public/test/fakes/test_browser_state.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#include "ios/web/public/web_state/web_state_policy_decider.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -58,17 +58,17 @@ class FakeAccountConsistencyService : public AccountConsistencyService {
cookie_settings,
gaia_cookie_manager_service,
signin_client,
- signin_manager),
- mock_web_view_(nil) {}
+ signin_manager) {}
private:
- WKWebView* CreateWKWebView() override {
+ WKWebView* BuildWKWebView() override {
if (!mock_web_view_) {
- mock_web_view_ = [OCMockObject niceMockForClass:[WKWebView class]];
+ mock_web_view_.reset(
+ [[OCMockObject niceMockForClass:[WKWebView class]] retain]);
}
- return [mock_web_view_ retain];
+ return mock_web_view_;
}
- id mock_web_view_;
+ base::scoped_nsobject<id> mock_web_view_;
};
// Mock AccountReconcilor to catch call to OnReceivedManageAccountsResponse.
@@ -202,7 +202,7 @@ class AccountConsistencyServiceTest : public PlatformTest {
MockAccountReconcilor account_reconcilor_;
AccountTrackerService account_tracker_service_;
web::TestBrowserState browser_state_;
- user_prefs::TestingPrefServiceSyncable prefs_;
+ sync_preferences::TestingPrefServiceSyncable prefs_;
TestWebState web_state_;
// AccountConsistencyService being tested. Actually a
// FakeAccountConsistencyService to be able to use a mock web view.
diff --git a/chromium/components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.mm b/chromium/components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.mm
index 763fa823542..50cb0f853b0 100644
--- a/chromium/components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.mm
+++ b/chromium/components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.mm
@@ -4,7 +4,7 @@
#include "components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.h"
-#include <Foundation/Foundation.h>
+#import <Foundation/Foundation.h>
#include "base/logging.h"
#include "base/strings/sys_string_conversions.h"
diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h
index 10ea6c9d379..d437a8b44a7 100644
--- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h
+++ b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h
@@ -15,6 +15,7 @@
class AccountTrackerService;
class ProfileOAuth2TokenServiceIOSProvider;
+class SigninClient;
class ProfileOAuth2TokenServiceIOSDelegate : public OAuth2TokenServiceDelegate {
public:
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 6577c507280..49606f24e79 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
@@ -4,7 +4,7 @@
#include "components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h"
-#include <Foundation/Foundation.h>
+#import <Foundation/Foundation.h>
#include <memory>
#include <set>
diff --git a/chromium/components/signin/public/interfaces/BUILD.gn b/chromium/components/signin/public/interfaces/BUILD.gn
new file mode 100644
index 00000000000..98d8a275153
--- /dev/null
+++ b/chromium/components/signin/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 = [
+ "account_id.mojom",
+ ]
+}
diff --git a/chromium/components/signin/public/interfaces/OWNERS b/chromium/components/signin/public/interfaces/OWNERS
new file mode 100644
index 00000000000..08850f42120
--- /dev/null
+++ b/chromium/components/signin/public/interfaces/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/signin/public/interfaces/account_id.mojom b/chromium/components/signin/public/interfaces/account_id.mojom
new file mode 100644
index 00000000000..f0f9abdc1a3
--- /dev/null
+++ b/chromium/components/signin/public/interfaces/account_id.mojom
@@ -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.
+
+module signin.mojom;
+
+// Matches AccountType of AccountId.
+enum AccountType {
+ UNKNOWN,
+ GOOGLE,
+ ACTIVE_DIRECTORY,
+};
+
+// Mirror of AccountId in Mojo.
+struct AccountId {
+ AccountType account_type;
+ string id;
+ string user_email;
+};
diff --git a/chromium/components/signin/public/interfaces/account_id.typemap b/chromium/components/signin/public/interfaces/account_id.typemap
new file mode 100644
index 00000000000..9d44e045e8e
--- /dev/null
+++ b/chromium/components/signin/public/interfaces/account_id.typemap
@@ -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.
+
+mojom = "//components/signin/public/interfaces/account_id.mojom"
+public_headers = [ "//components/signin/core/account_id/account_id.h" ]
+traits_headers = [ "//components/signin/public/interfaces/account_id_traits.h" ]
+public_deps = [
+ "//components/signin/core/account_id",
+]
+type_mappings = [
+ "signin.mojom.AccountType=AccountType",
+ "signin.mojom.AccountId=AccountId",
+]
diff --git a/chromium/components/signin/public/interfaces/account_id_traits.h b/chromium/components/signin/public/interfaces/account_id_traits.h
new file mode 100644
index 00000000000..cc1d39de917
--- /dev/null
+++ b/chromium/components/signin/public/interfaces/account_id_traits.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_SIGNIN_PUBLIC_INTERFACES_ACCOUNT_ID_TRAITS_H_
+#define COMPONENTS_SIGNIN_PUBLIC_INTERFACES_ACCOUNT_ID_TRAITS_H_
+
+#include <string>
+
+#include "components/signin/core/account_id/account_id.h"
+#include "components/signin/public/interfaces/account_id.mojom.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<signin::mojom::AccountType, AccountType> {
+ static signin::mojom::AccountType ToMojom(AccountType input) {
+ switch (input) {
+ case AccountType::UNKNOWN:
+ return signin::mojom::AccountType::UNKNOWN;
+ case AccountType::GOOGLE:
+ return signin::mojom::AccountType::GOOGLE;
+ case AccountType::ACTIVE_DIRECTORY:
+ return signin::mojom::AccountType::ACTIVE_DIRECTORY;
+ }
+ NOTREACHED();
+ return signin::mojom::AccountType::UNKNOWN;
+ }
+
+ static bool FromMojom(signin::mojom::AccountType input,
+ AccountType* out) {
+ switch (input) {
+ case signin::mojom::AccountType::UNKNOWN:
+ *out = AccountType::UNKNOWN;
+ return true;
+ case signin::mojom::AccountType::GOOGLE:
+ *out = AccountType::GOOGLE;
+ return true;
+ case signin::mojom::AccountType::ACTIVE_DIRECTORY:
+ *out = AccountType::ACTIVE_DIRECTORY;
+ return true;
+ }
+ NOTREACHED();
+ return false;
+ }
+};
+
+
+template <>
+struct StructTraits<signin::mojom::AccountIdDataView, AccountId> {
+ static AccountType account_type(const AccountId& r) {
+ return r.GetAccountType();
+ }
+ static std::string id(const AccountId& r) {
+ switch (r.GetAccountType()) {
+ case AccountType::GOOGLE:
+ return r.GetGaiaId();
+ case AccountType::ACTIVE_DIRECTORY:
+ return r.GetObjGuid();
+ case AccountType::UNKNOWN:
+ // UNKNOWN type is used for users that have only email (e.g. in tests
+ // or legacy users that have not run through migration code).
+ // Return an empty string for such accounts.
+ return std::string();
+ }
+ NOTREACHED();
+ return std::string();
+ }
+ static std::string user_email(const AccountId& r) {
+ return r.GetUserEmail();
+ }
+
+ static bool Read(signin::mojom::AccountIdDataView data, AccountId* out) {
+ AccountType account_type;
+ std::string id;
+ std::string user_email;
+ if (!data.ReadAccountType(&account_type) ||
+ !data.ReadId(&id) ||
+ !data.ReadUserEmail(&user_email)) {
+ return false;
+ }
+
+ switch (account_type) {
+ case AccountType::GOOGLE:
+ *out = AccountId::FromUserEmailGaiaId(user_email, id);
+ break;
+ case AccountType::ACTIVE_DIRECTORY:
+ *out = AccountId::AdFromUserEmailObjGuid(user_email, id);
+ break;
+ case AccountType::UNKNOWN:
+ // UNKNOWN type is used for users that have only email (e.g. in tests
+ // or legacy users that have not run through migration code).
+ // Bail if there is no user email.
+ if (user_email.empty())
+ return false;
+
+ *out = AccountId::FromUserEmail(user_email);
+ break;
+ }
+
+ return out->is_valid();
+ }
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_SIGNIN_PUBLIC_INTERFACES_ACCOUNT_ID_TRAITS_H_
diff --git a/chromium/components/spellcheck/browser/spellcheck_platform.h b/chromium/components/spellcheck/browser/spellcheck_platform.h
index 4455046239a..28450afc9e7 100644
--- a/chromium/components/spellcheck/browser/spellcheck_platform.h
+++ b/chromium/components/spellcheck/browser/spellcheck_platform.h
@@ -16,10 +16,6 @@
struct SpellCheckResult;
-namespace content {
-class BrowserMessageFilter;
-} // namespace content
-
namespace spellcheck_platform {
typedef base::Callback<void(
diff --git a/chromium/components/spellcheck/browser/spelling_service_client.cc b/chromium/components/spellcheck/browser/spelling_service_client.cc
index 44c51ccb695..985732a8db5 100644
--- a/chromium/components/spellcheck/browser/spelling_service_client.cc
+++ b/chromium/components/spellcheck/browser/spelling_service_client.cc
@@ -196,7 +196,7 @@ bool SpellingServiceClient::ParseResponse(
static_cast<base::DictionaryValue*>(
base::JSONReader::Read(data, base::JSON_ALLOW_TRAILING_COMMAS)
.release()));
- if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
+ if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY))
return false;
// Check for errors from spelling service.
diff --git a/chromium/components/spellcheck/browser/spelling_service_client.h b/chromium/components/spellcheck/browser/spelling_service_client.h
index 344e4558167..30ff387eae6 100644
--- a/chromium/components/spellcheck/browser/spelling_service_client.h
+++ b/chromium/components/spellcheck/browser/spelling_service_client.h
@@ -16,7 +16,6 @@
#include "net/url_request/url_fetcher_delegate.h"
class GURL;
-class TextCheckClientDelegate;
struct SpellCheckResult;
namespace content {
diff --git a/chromium/components/spellcheck/common/spellcheck_features.cc b/chromium/components/spellcheck/common/spellcheck_features.cc
index f6e8bb31d5f..c3865f7c231 100644
--- a/chromium/components/spellcheck/common/spellcheck_features.cc
+++ b/chromium/components/spellcheck/common/spellcheck_features.cc
@@ -17,16 +17,14 @@ const base::Feature kAndroidSpellChecker{
// Enables/disables Android spellchecker on non low-end Android devices.
const base::Feature kAndroidSpellCheckerNonLowEnd{
- "AndroidSpellCheckerNonLowEnd", base::FEATURE_DISABLED_BY_DEFAULT};
+ "AndroidSpellCheckerNonLowEnd", base::FEATURE_ENABLED_BY_DEFAULT};
bool IsAndroidSpellCheckFeatureEnabled() {
- if (base::FeatureList::IsEnabled(
- spellcheck::kAndroidSpellCheckerNonLowEnd)) {
+ if (base::FeatureList::IsEnabled(spellcheck::kAndroidSpellCheckerNonLowEnd)) {
return !base::SysInfo::IsLowEndDevice();
}
- if (base::FeatureList::IsEnabled(
- spellcheck::kAndroidSpellChecker)) {
+ if (base::FeatureList::IsEnabled(spellcheck::kAndroidSpellChecker)) {
return true;
}
diff --git a/chromium/components/spellcheck/renderer/hunspell_engine.cc b/chromium/components/spellcheck/renderer/hunspell_engine.cc
index 8617aaa2185..c3853483105 100644
--- a/chromium/components/spellcheck/renderer/hunspell_engine.cc
+++ b/chromium/components/spellcheck/renderer/hunspell_engine.cc
@@ -86,7 +86,7 @@ bool HunspellEngine::CheckSpelling(const base::string16& word_to_check,
// to check rather than crash.
if (hunspell_.get()) {
// |hunspell_->spell| returns 0 if the word is misspelled.
- word_correct = (hunspell_->spell(word_to_check_utf8.c_str()) != 0);
+ word_correct = (hunspell_->spell(word_to_check_utf8) != 0);
}
}
@@ -106,18 +106,14 @@ void HunspellEngine::FillSuggestionList(
if (!hunspell_.get())
return;
- char** suggestions = NULL;
- int number_of_suggestions =
- hunspell_->suggest(&suggestions, wrong_word_utf8.c_str());
+ std::vector<std::string> suggestions =
+ hunspell_->suggest(wrong_word_utf8);
// Populate the vector of WideStrings.
- for (int i = 0; i < number_of_suggestions; ++i) {
+ for (size_t i = 0; i < suggestions.size(); ++i) {
if (i < spellcheck::kMaxSuggestions)
optional_suggestions->push_back(base::UTF8ToUTF16(suggestions[i]));
- free(suggestions[i]);
}
- if (suggestions != NULL)
- free(suggestions);
}
bool HunspellEngine::InitializeIfNeeded() {
diff --git a/chromium/components/spellcheck/renderer/spellcheck_multilingual_unittest.cc b/chromium/components/spellcheck/renderer/spellcheck_multilingual_unittest.cc
index 1eda2490d3e..9c700753917 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_multilingual_unittest.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_multilingual_unittest.cc
@@ -81,8 +81,9 @@ class MultilingualSpellCheckTest : public testing::Test {
int misspelling_start = 0;
int misspelling_length = 0;
static_cast<blink::WebSpellCheckClient*>(provider())
- ->spellCheck(blink::WebString(base::WideToUTF16(test_cases[i].input)),
- misspelling_start, misspelling_length, nullptr);
+ ->checkSpelling(
+ blink::WebString(base::WideToUTF16(test_cases[i].input)),
+ misspelling_start, misspelling_length, nullptr);
EXPECT_EQ(test_cases[i].expected_misspelling_start, misspelling_start)
<< "Improper misspelling location found with the languages "
@@ -233,8 +234,9 @@ TEST_F(MultilingualSpellCheckTest, MultilingualSpellCheckSuggestions) {
int misspelling_start;
int misspelling_length;
static_cast<blink::WebSpellCheckClient*>(provider())
- ->spellCheck(blink::WebString(base::WideToUTF16(kTestCases[i].input)),
- misspelling_start, misspelling_length, &suggestions);
+ ->checkSpelling(
+ blink::WebString(base::WideToUTF16(kTestCases[i].input)),
+ misspelling_start, misspelling_length, &suggestions);
EXPECT_EQ(kTestCases[i].expected_misspelling_start, misspelling_start);
EXPECT_EQ(kTestCases[i].expected_misspelling_length, misspelling_length);
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider.cc b/chromium/components/spellcheck/renderer/spellcheck_provider.cc
index 097d0c96568..9aeb92aa79f 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider.cc
@@ -122,7 +122,7 @@ void SpellCheckProvider::FocusedNodeChanged(const blink::WebNode& unused) {
#endif // USE_BROWSER_SPELLCHECKER
}
-void SpellCheckProvider::spellCheck(
+void SpellCheckProvider::checkSpelling(
const WebString& text,
int& offset,
int& length,
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider.h b/chromium/components/spellcheck/renderer/spellcheck_provider.h
index 82f8fa43972..fd849f41ba2 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider.h
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider.h
@@ -35,7 +35,7 @@ class SpellCheckProvider
public content::RenderViewObserverTracker<SpellCheckProvider>,
public blink::WebSpellCheckClient {
public:
- typedef IDMap<blink::WebTextCheckingCompletion> WebTextCheckCompletions;
+ using WebTextCheckCompletions = IDMap<blink::WebTextCheckingCompletion*>;
SpellCheckProvider(content::RenderView* render_view,
SpellCheck* spellcheck);
@@ -76,7 +76,7 @@ class SpellCheckProvider
void OnDestruct() override;
// blink::WebSpellCheckClient implementation.
- void spellCheck(
+ void checkSpelling(
const blink::WebString& text,
int& offset,
int& length,
diff --git a/chromium/components/spellcheck/renderer/spellcheck_unittest.cc b/chromium/components/spellcheck/renderer/spellcheck_unittest.cc
index 5d0e6c97a0a..3c09b7b7035 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_unittest.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_unittest.cc
@@ -178,7 +178,13 @@ class MockTextCheckingCompletion : public blink::WebTextCheckingCompletion {
// A test with a "[ROBUSTNESS]" mark shows it is a robustness test and it uses
// grammatically incorrect string.
// TODO(groby): Please feel free to add more tests.
-TEST_F(SpellCheckTest, SpellCheckStrings_EN_US) {
+#if defined(OS_WIN) && !defined(NDEBUG)
+// Test times out on win dbg. crbug.com/678753.
+#define MAYBE_SpellCheckStrings_EN_US DISABLED_SpellCheckStrings_EN_US
+#else
+#define MAYBE_SpellCheckStrings_EN_US SpellCheckStrings_EN_US
+#endif
+TEST_F(SpellCheckTest, MAYBE_SpellCheckStrings_EN_US) {
static const struct {
// A string to be tested.
const wchar_t* input;
@@ -489,9 +495,10 @@ TEST_F(SpellCheckTest, SpellCheckSuggestions_EN_US) {
// This test verifies our spellchecker can split a text into words and check
// the spelling of each word in the text.
-#if defined(THREAD_SANITIZER)
+#if defined(THREAD_SANITIZER) || defined(OS_WIN)
// SpellCheckTest.SpellCheckText fails under ThreadSanitizer v2.
// See http://crbug.com/217909.
+// Also fails on windows: crbug.com/678300.
#define MAYBE_SpellCheckText DISABLED_SpellCheckText
#else
#define MAYBE_SpellCheckText SpellCheckText
@@ -1333,7 +1340,8 @@ TEST_F(SpellCheckTest, NoSuggest) {
EXPECT_EQ(kTestCases[i].should_pass, result) << kTestCases[i].suggestion <<
" in " << kTestCases[i].locale;
-
+ // TODO(cb/673424): Bring this back when suggestions are sped up.
+#if 0
// Now verify that this test case does not show up as a suggestion.
std::vector<base::string16> suggestions;
size_t input_length = 0;
@@ -1360,6 +1368,7 @@ TEST_F(SpellCheckTest, NoSuggest) {
" in " << kTestCases[i].locale;
}
}
+#endif
}
}
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 46a26c575b8..0dab204ee2f 100644
--- a/chromium/components/ssl_config/ssl_config_service_manager_pref.cc
+++ b/chromium/components/ssl_config/ssl_config_service_manager_pref.cc
@@ -85,6 +85,10 @@ uint16_t SSLProtocolVersionFromString(const std::string& version_str) {
return version;
}
+const base::Feature kTLS13Feature{
+ "NegotiateTLS13", base::FEATURE_DISABLED_BY_DEFAULT,
+};
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -190,6 +194,12 @@ SSLConfigServiceManagerPref::SSLConfigServiceManagerPref(
io_task_runner_(io_task_runner) {
DCHECK(local_state);
+ if (base::FeatureList::IsEnabled(kTLS13Feature)) {
+ local_state->SetDefaultPrefValue(
+ ssl_config::prefs::kSSLVersionMax,
+ new base::StringValue(switches::kSSLVersionTLSv13));
+ }
+
PrefChangeRegistrar::NamedChangeCallback local_state_callback =
base::Bind(&SSLConfigServiceManagerPref::OnPreferenceChanged,
base::Unretained(this), local_state);
@@ -230,7 +240,7 @@ void SSLConfigServiceManagerPref::RegisterPrefs(PrefRegistrySimple* registry) {
ssl_config::prefs::kCertRevocationCheckingRequiredLocalAnchors,
default_config.rev_checking_required_local_anchors);
registry->RegisterBooleanPref(ssl_config::prefs::kCertEnableSha1LocalAnchors,
- default_config.sha1_local_anchors_enabled);
+ false);
registry->RegisterStringPref(ssl_config::prefs::kSSLVersionMin,
std::string());
registry->RegisterStringPref(ssl_config::prefs::kSSLVersionMax,
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 1030f009e10..9c2e24b80f6 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
@@ -179,7 +179,7 @@ TEST_F(SSLConfigServiceManagerPrefTest, NoSSL3) {
EXPECT_LE(net::SSL_PROTOCOL_VERSION_TLS1, ssl_config.version_min);
}
-// Tests that TLS 1.3 may not be enabled via features.
+// Tests that TLS 1.3 may be enabled via features.
TEST_F(SSLConfigServiceManagerPrefTest, TLS13Feature) {
// Toggle the feature.
base::test::ScopedFeatureList scoped_feature_list;
@@ -194,8 +194,58 @@ TEST_F(SSLConfigServiceManagerPrefTest, TLS13Feature) {
scoped_refptr<SSLConfigService> config_service(config_manager->Get());
ASSERT_TRUE(config_service.get());
- // The feature should still be TLS 1.2.
+ // The feature should have switched the default version_fallback_min value.
SSLConfig ssl_config;
config_service->GetSSLConfig(&ssl_config);
- EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_2, ssl_config.version_max);
+ EXPECT_EQ(net::SSL_PROTOCOL_VERSION_TLS1_3, ssl_config.version_max);
+}
+
+// Tests that SHA-1 signatures for local trust anchors can be enabled.
+TEST_F(SSLConfigServiceManagerPrefTest, SHA1ForLocalAnchors) {
+ scoped_refptr<TestingPrefStore> local_state_store(new TestingPrefStore());
+
+ TestingPrefServiceSimple local_state;
+ SSLConfigServiceManager::RegisterPrefs(local_state.registry());
+
+ std::unique_ptr<SSLConfigServiceManager> config_manager(
+ SSLConfigServiceManager::CreateDefaultManager(
+ &local_state, base::ThreadTaskRunnerHandle::Get()));
+ ASSERT_TRUE(config_manager);
+ scoped_refptr<SSLConfigService> config_service(config_manager->Get());
+ ASSERT_TRUE(config_service);
+
+ // By default, SHA-1 local trust anchors should be enabled when not
+ // using any pref service.
+ SSLConfig config1;
+ EXPECT_TRUE(config1.sha1_local_anchors_enabled);
+
+ // Using a pref service without any preference set should result in
+ // SHA-1 local trust anchors being disabled.
+ SSLConfig config2;
+ config_service->GetSSLConfig(&config2);
+ EXPECT_FALSE(config2.sha1_local_anchors_enabled);
+
+ // Enabling the local preference should result in SHA-1 local trust anchors
+ // being enabled.
+ local_state.SetUserPref(ssl_config::prefs::kCertEnableSha1LocalAnchors,
+ new base::FundamentalValue(true));
+ // Pump the message loop to notify the SSLConfigServiceManagerPref that the
+ // preferences changed.
+ base::RunLoop().RunUntilIdle();
+
+ SSLConfig config3;
+ config_service->GetSSLConfig(&config3);
+ EXPECT_TRUE(config3.sha1_local_anchors_enabled);
+
+ // Disabling the local preference should result in SHA-1 local trust
+ // anchors being disabled.
+ local_state.SetUserPref(ssl_config::prefs::kCertEnableSha1LocalAnchors,
+ new base::FundamentalValue(false));
+ // Pump the message loop to notify the SSLConfigServiceManagerPref that the
+ // preferences changed.
+ base::RunLoop().RunUntilIdle();
+
+ SSLConfig config4;
+ config_service->GetSSLConfig(&config4);
+ EXPECT_FALSE(config4.sha1_local_anchors_enabled);
}
diff --git a/chromium/components/ssl_errors/error_classification_unittest.cc b/chromium/components/ssl_errors/error_classification_unittest.cc
index 800ccd78f55..7dbff066979 100644
--- a/chromium/components/ssl_errors/error_classification_unittest.cc
+++ b/chromium/components/ssl_errors/error_classification_unittest.cc
@@ -11,6 +11,7 @@
#include "base/test/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "base/test/simple_test_tick_clock.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_clock.h"
#include "base/time/default_tick_clock.h"
#include "components/network_time/network_time_test_utils.h"
@@ -229,6 +230,7 @@ TEST_F(SSLErrorClassificationTest, GetClockState) {
new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get()));
+ ssl_errors::SetBuildTimeForTesting(base::Time::Now());
EXPECT_EQ(
ssl_errors::ClockState::CLOCK_STATE_UNKNOWN,
ssl_errors::GetClockState(base::Time::Now(), &network_time_tracker));
diff --git a/chromium/components/startup_metric_utils/OWNERS b/chromium/components/startup_metric_utils/OWNERS
index 221025c99d0..711005f7f95 100644
--- a/chromium/components/startup_metric_utils/OWNERS
+++ b/chromium/components/startup_metric_utils/OWNERS
@@ -1,2 +1,4 @@
fdoray@chromium.org
+
+# To increase bus factor but prefer above OWNERS :).
gab@chromium.org
diff --git a/chromium/components/startup_metric_utils/browser/BUILD.gn b/chromium/components/startup_metric_utils/browser/BUILD.gn
index 2f9f4176f9e..a03e6a601b0 100644
--- a/chromium/components/startup_metric_utils/browser/BUILD.gn
+++ b/chromium/components/startup_metric_utils/browser/BUILD.gn
@@ -25,6 +25,7 @@ static_library("lib") {
deps = [
"//base",
+ "//components/metrics:metrics",
"//components/prefs",
"//components/version_info",
]
diff --git a/chromium/components/startup_metric_utils/browser/DEPS b/chromium/components/startup_metric_utils/browser/DEPS
index d4215491b67..29510d99592 100644
--- a/chromium/components/startup_metric_utils/browser/DEPS
+++ b/chromium/components/startup_metric_utils/browser/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+components/metrics",
"+components/prefs",
"+components/version_info",
"+content/public/browser",
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 d64d62602bc..bcb1259f85e 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
@@ -4,6 +4,7 @@
#include "components/startup_metric_utils/browser/startup_metric_host_impl.h"
+#include "base/memory/ptr_util.h"
#include "components/startup_metric_utils/browser/startup_metric_utils.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
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 6d9ffcc2da6..d45b768c291 100644
--- a/chromium/components/startup_metric_utils/browser/startup_metric_utils.cc
+++ b/chromium/components/startup_metric_utils/browser/startup_metric_utils.cc
@@ -16,11 +16,13 @@
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_macros.h"
#include "base/process/process_info.h"
+#include "base/profiler/stack_sampling_profiler.h"
#include "base/strings/string_number_conversions.h"
#include "base/sys_info.h"
#include "base/threading/platform_thread.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
+#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/startup_metric_utils/browser/pref_names.h"
@@ -592,15 +594,16 @@ void RecordBrowserMainMessageLoopStart(const base::TimeTicks& ticks,
PrefService* pref_service) {
DCHECK(pref_service);
+ // Keep RecordSameVersionStartupCount() and RecordHardFaultHistogram()
+ // near the top of this method (as much as possible) as many other
+ // histograms depend on it setting |g_startup_temperature| and
+ // |g_startups_with_current_version|.
RecordSameVersionStartupCount(pref_service);
- // Keep RecordHardFaultHistogram() first as much as possible as many other
- // histograms depend on it setting |g_startup_temperature|.
RecordHardFaultHistogram();
- AddStartupEventsForTelemetry();
- RecordTimeSinceLastStartup(pref_service);
- RecordSystemUptimeHistogram();
- RecordMainEntryTimeHistogram();
+ // Record timing of the browser message-loop start time.
+ base::StackSamplingProfiler::SetProcessMilestone(
+ metrics::CallStackProfileMetricsProvider::MAIN_LOOP_START);
const base::TimeTicks& process_creation_ticks =
g_process_creation_ticks.Get();
if (!is_first_run && !process_creation_ticks.is_null()) {
@@ -609,13 +612,6 @@ void RecordBrowserMainMessageLoopStart(const base::TimeTicks& ticks,
process_creation_ticks, ticks);
}
- // TODO(fdoray): Remove histograms that are only recorded after 7 minutes of
- // OS uptime once M54 hits stable. These histograms are kept for now to allow
- // regressions to be caught reliably in M54, to be removed in M55.
- // crbug.com/634408
- const bool is_seven_minutes_after_boot =
- base::SysInfo::Uptime() < base::TimeDelta::FromMinutes(7);
-
// Record timing between the shared library's main() entry and the browser
// main message loop start.
if (is_first_run) {
@@ -623,25 +619,18 @@ void RecordBrowserMainMessageLoopStart(const base::TimeTicks& ticks,
UMA_HISTOGRAM_LONG_TIMES,
"Startup.BrowserMessageLoopStartTimeFromMainEntry.FirstRun2",
g_browser_main_entry_point_ticks.Get(), ticks);
- if (is_seven_minutes_after_boot) {
- UMA_HISTOGRAM_WITH_TEMPERATURE(
- UMA_HISTOGRAM_LONG_TIMES,
- "Startup.BrowserMessageLoopStartTimeFromMainEntry.FirstRun",
- ticks - g_browser_main_entry_point_ticks.Get());
- }
} else {
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES,
"Startup.BrowserMessageLoopStartTimeFromMainEntry2",
g_browser_main_entry_point_ticks.Get(), ticks);
- if (is_seven_minutes_after_boot) {
- UMA_HISTOGRAM_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
- UMA_HISTOGRAM_LONG_TIMES,
- "Startup.BrowserMessageLoopStartTimeFromMainEntry",
- ticks - g_browser_main_entry_point_ticks.Get());
- }
}
+ AddStartupEventsForTelemetry();
+ RecordTimeSinceLastStartup(pref_service);
+ RecordSystemUptimeHistogram();
+ RecordMainEntryTimeHistogram();
+
// Record timings between process creation, the main() in the executable being
// reached and the main() in the shared library being reached.
if (!process_creation_ticks.is_null() &&
@@ -665,18 +654,6 @@ void RecordBrowserMainMessageLoopStart(const base::TimeTicks& ticks,
UMA_HISTOGRAM_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ProcessCreateToDllMain2",
main_entry_ticks - process_creation_ticks);
-
- if (is_seven_minutes_after_boot) {
- UMA_HISTOGRAM_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
- UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ProcessCreateToExeMain",
- exe_main_ticks - process_creation_ticks);
- UMA_HISTOGRAM_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
- UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ExeMainToDllMain",
- main_entry_ticks - exe_main_ticks);
- UMA_HISTOGRAM_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
- UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ProcessCreateToDllMain",
- main_entry_ticks - process_creation_ticks);
- }
}
}
@@ -736,6 +713,8 @@ void RecordFirstWebContentsNonEmptyPaint(const base::TimeTicks& ticks) {
if (WasNonBrowserUIDisplayed() || g_process_creation_ticks.Get().is_null())
return;
+ base::StackSamplingProfiler::SetProcessMilestone(
+ 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);
@@ -749,6 +728,8 @@ void RecordFirstWebContentsMainNavigationStart(const base::TimeTicks& ticks) {
if (WasNonBrowserUIDisplayed() || g_process_creation_ticks.Get().is_null())
return;
+ base::StackSamplingProfiler::SetProcessMilestone(
+ metrics::CallStackProfileMetricsProvider::MAIN_NAVIGATION_START);
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100,
"Startup.FirstWebContents.MainNavigationStart",
@@ -764,6 +745,8 @@ void RecordFirstWebContentsMainNavigationFinished(
if (WasNonBrowserUIDisplayed() || g_process_creation_ticks.Get().is_null())
return;
+ base::StackSamplingProfiler::SetProcessMilestone(
+ metrics::CallStackProfileMetricsProvider::MAIN_NAVIGATION_FINISHED);
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100,
"Startup.FirstWebContents.MainNavigationFinished",
diff --git a/chromium/components/startup_metric_utils/common/startup_metric.mojom b/chromium/components/startup_metric_utils/common/startup_metric.mojom
index 80161078ba8..f970df0b6f5 100644
--- a/chromium/components/startup_metric_utils/common/startup_metric.mojom
+++ b/chromium/components/startup_metric_utils/common/startup_metric.mojom
@@ -4,7 +4,7 @@
module startup_metric_utils.mojom;
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
interface StartupMetricHost {
RecordRendererMainEntryTime(
diff --git a/chromium/components/storage_monitor/storage_monitor_linux.cc b/chromium/components/storage_monitor/storage_monitor_linux.cc
index 4b3a01fae7b..1bbffa9e65e 100644
--- a/chromium/components/storage_monitor/storage_monitor_linux.cc
+++ b/chromium/components/storage_monitor/storage_monitor_linux.cc
@@ -15,7 +15,7 @@
#include "base/bind.h"
#include "base/macros.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/process/process.h"
diff --git a/chromium/components/storage_monitor/storage_monitor_linux_unittest.cc b/chromium/components/storage_monitor/storage_monitor_linux_unittest.cc
index 92311f2c1ea..769d46e19d0 100644
--- a/chromium/components/storage_monitor/storage_monitor_linux_unittest.cc
+++ b/chromium/components/storage_monitor/storage_monitor_linux_unittest.cc
@@ -315,8 +315,7 @@ class StorageMonitorLinuxTest : public testing::Test {
};
// Simple test case where we attach and detach a media device.
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_BasicAttachDetach) {
+TEST_F(StorageMonitorLinuxTest, BasicAttachDetach) {
base::FilePath test_path = CreateMountPointWithDCIMDir(kMountPointA);
ASSERT_FALSE(test_path.empty());
MtabTestData test_data[] = {
@@ -340,8 +339,7 @@ TEST_F(StorageMonitorLinuxTest, DISABLED_BasicAttachDetach) {
}
// Only removable devices are recognized.
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_Removable) {
+TEST_F(StorageMonitorLinuxTest, Removable) {
base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
ASSERT_FALSE(test_path_a.empty());
MtabTestData test_data1[] = {
@@ -389,8 +387,7 @@ TEST_F(StorageMonitorLinuxTest, DISABLED_Removable) {
}
// More complicated test case with multiple devices on multiple mount points.
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_SwapMountPoints) {
+TEST_F(StorageMonitorLinuxTest, SwapMountPoints) {
base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
ASSERT_FALSE(test_path_a.empty());
@@ -427,8 +424,7 @@ TEST_F(StorageMonitorLinuxTest, DISABLED_SwapMountPoints) {
}
// More complicated test case with multiple devices on multiple mount points.
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_MultiDevicesMultiMountPoints) {
+TEST_F(StorageMonitorLinuxTest, MultiDevicesMultiMountPoints) {
base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
ASSERT_FALSE(test_path_a.empty());
@@ -495,9 +491,7 @@ TEST_F(StorageMonitorLinuxTest, DISABLED_MultiDevicesMultiMountPoints) {
EXPECT_EQ(5, observer().detach_calls());
}
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest,
- DISABLED_MultipleMountPointsWithNonDCIMDevices) {
+TEST_F(StorageMonitorLinuxTest, MultipleMountPointsWithNonDCIMDevices) {
base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
ASSERT_FALSE(test_path_a.empty());
@@ -583,8 +577,7 @@ TEST_F(StorageMonitorLinuxTest,
EXPECT_EQ(4, observer().detach_calls());
}
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_DeviceLookUp) {
+TEST_F(StorageMonitorLinuxTest, DeviceLookUp) {
base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
base::FilePath test_path_c = CreateMountPointWithoutDCIMDir(kMountPointC);
@@ -658,8 +651,7 @@ TEST_F(StorageMonitorLinuxTest, DISABLED_DeviceLookUp) {
EXPECT_EQ(1, observer().detach_calls());
}
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_DevicePartitionSize) {
+TEST_F(StorageMonitorLinuxTest, DevicePartitionSize) {
base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
ASSERT_FALSE(test_path_a.empty());
diff --git a/chromium/components/storage_monitor/storage_monitor_mac.mm b/chromium/components/storage_monitor/storage_monitor_mac.mm
index ed0392bd24c..47d91355feb 100644
--- a/chromium/components/storage_monitor/storage_monitor_mac.mm
+++ b/chromium/components/storage_monitor/storage_monitor_mac.mm
@@ -320,7 +320,7 @@ void StorageMonitorMac::EjectDevice(
EjectDiskOptions* options = new EjectDiskOptions;
options->bsd_name = bsd_name;
options->callback = callback;
- options->disk.reset(disk.release());
+ options->disk = std::move(disk);
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
base::Bind(EjectDisk, options));
}
diff --git a/chromium/components/storage_monitor/udev_util_linux.h b/chromium/components/storage_monitor/udev_util_linux.h
index 0f80d3f6c5e..de7bfa47413 100644
--- a/chromium/components/storage_monitor/udev_util_linux.h
+++ b/chromium/components/storage_monitor/udev_util_linux.h
@@ -7,8 +7,6 @@
#include <string>
-struct udev_device;
-
namespace base {
class FilePath;
}
diff --git a/chromium/components/strings/BUILD.gn b/chromium/components/strings/BUILD.gn
index 395dbee3767..a299d38baf7 100644
--- a/chromium/components/strings/BUILD.gn
+++ b/chromium/components/strings/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/config/locales.gni")
+import("//ppapi/features/features.gni")
import("//tools/grit/grit_rule.gni")
if (is_android) {
@@ -66,6 +67,8 @@ group("strings") {
grit("components_strings") {
source = "../components_strings.grd"
+ defines = [ "enable_plugins=$enable_plugins" ]
+
outputs = [
"grit/components_strings.h",
]
diff --git a/chromium/components/strings/components_chromium_strings_bn.xtb b/chromium/components/strings/components_chromium_strings_bn.xtb
index 6c366d3367f..9a2120e5d04 100644
--- a/chromium/components/strings/components_chromium_strings_bn.xtb
+++ b/chromium/components/strings/components_chromium_strings_bn.xtb
@@ -26,14 +26,14 @@
&gt;
<ph name="PROXIES_TITLE" />
à¦à¦¬à¦‚ আপনার কনফিগারেশন "পà§à¦°à¦•à§à¦¸à¦¿ নয়" বা "সরাসরি" তে সেট আছে কি না নিশà§à¦šà¦¿à¦¤ করà§à¦¨à§·</translation>
-<translation id="6613594504749178791">আপনার পরিবরà§à¦¤à¦¨à¦—à§à¦²à¦¿ পরবরà§à¦¤à§€ সময় আপনি Chromium পà§à¦¨à¦°à¦¾à§Ÿ চালৠহলে ফলপà§à¦°à¦¦ হবে৷</translation>
+<translation id="6613594504749178791">আপনার পরিবরà§à¦¤à¦¨à¦—à§à¦²à¦¿ পরবরà§à¦¤à§€ সময় আপনি Chromium আবার চালৠহলে ফলপà§à¦°à¦¦ হবে৷</translation>
<translation id="7861509383340276692">Chromium মেনà§à¦¤à§‡
যান &gt;
<ph name="SETTINGS_TITLE" />
&gt;
<ph name="ADVANCED_TITLE" />
à¦à¦¬à¦‚ অনিরà§à¦¬à¦¾à¦šà¦¨ করà§à¦¨ "<ph name="NO_PREFETCH_DESCRIPTION" />৷"
- যদি à¦à¦Ÿà¦¿ সমসà§à¦¯à¦¾à¦Ÿà¦¿à¦° সমাধান না করে, তাহলে উনà§à¦¨à¦¤ কারà§à¦¯ সমà§à¦ªà¦¾à¦¦à¦¨à¦¾à¦° জনà§à¦¯ পà§à¦¨à¦°à¦¾à§Ÿ à¦à¦‡ বিকলà§à¦ªà¦Ÿà¦¿ নিরà§à¦¬à¦¾à¦šà¦¨ করা সà§à¦ªà¦¾à¦°à¦¿à¦¶ আমরা করি৷</translation>
+ যদি à¦à¦Ÿà¦¿ সমসà§à¦¯à¦¾à¦Ÿà¦¿à¦° সমাধান না করে, তাহলে উনà§à¦¨à¦¤ কারà§à¦¯ সমà§à¦ªà¦¾à¦¦à¦¨à¦¾à¦° জনà§à¦¯ আবার à¦à¦‡ বিকলà§à¦ªà¦Ÿà¦¿ নিরà§à¦¬à¦¾à¦šà¦¨ করা সà§à¦ªà¦¾à¦°à¦¿à¦¶ আমরা করি৷</translation>
<translation id="8187289872471304532">Applications &gt; System Preferences &gt; Network &gt; Advanced &gt; Proxies ঠযান
à¦à¦¬à¦‚ নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ থাকা যে কোনো পà§à¦°à¦•à§à¦¸à¦¿ অনিরà§à¦¬à¦¾à¦šà¦¨ করà§à¦¨à§·</translation>
<translation id="8684913864886094367">Chromium সঠিকভাবে বনà§à¦§ হয়নি৷</translation>
diff --git a/chromium/components/strings/components_google_chrome_strings_bn.xtb b/chromium/components/strings/components_google_chrome_strings_bn.xtb
index 04b1f33bdc0..e9ea387bdc8 100644
--- a/chromium/components/strings/components_google_chrome_strings_bn.xtb
+++ b/chromium/components/strings/components_google_chrome_strings_bn.xtb
@@ -5,7 +5,7 @@
<translation id="130631256467250065">পরের বার আপনি আপনার ডিভাইস পà§à¦¨à¦°à¦¾à¦°à¦®à§à¦­ করলে আপনার পরিবরà§à¦¤à¦¨à¦—à§à¦²à¦¿ পà§à¦°à¦­à¦¾à¦¬à§€ হবে৷</translation>
<translation id="2748463065602559597">আপনি à¦à¦•à¦Ÿà¦¿ নিরাপদ Google Chrome পৃষà§à¦ à¦¾ দেখছà§à¦¨à§‡à§·</translation>
<translation id="2874156562296220396">Google Chrome, সমà§à¦­à¦¬à¦¤ <ph name="BEGIN_LINK_CHROMIUM" />কà§à¦°à§‹à¦®à¦¿à§Ÿà¦¾à¦®<ph name="END_LINK_CHROMIUM" /> মà§à¦•à§à¦¤ উৎস পà§à¦°à§‹à¦œà§‡à¦•à§à¦Ÿ à¦à¦¬à¦‚ অনà§à¦¯à¦¾à¦¨à§à¦¯ <ph name="BEGIN_LINK_OSS" />মà§à¦•à§à¦¤ উৎস সফà§à¦Ÿà¦“য়à§à¦¯à¦¾à¦°<ph name="END_LINK_OSS" /> দà§à¦¬à¦¾à¦°à¦¾ তৈরি করা হয়েছে৷</translation>
-<translation id="3140883423282498090">আপনি পরবরà§à¦¤à§€ সময়ে যখন Google Chrome পà§à¦¨à¦°à¦¾à§Ÿ লঞà§à¦š করবেন আপনার পরিবরà§à¦¤à¦¨à¦—à§à¦²à¦¿ কারà§à¦¯à¦•à¦° হবে৷</translation>
+<translation id="3140883423282498090">আপনি পরবরà§à¦¤à§€ সময়ে যখন Google Chrome আবার লঞà§à¦š করবেন আপনার পরিবরà§à¦¤à¦¨à¦—à§à¦²à¦¿ কারà§à¦¯à¦•à¦° হবে৷</translation>
<translation id="3444832043240812445">যদি আপনি <ph name="BEGIN_LINK" />কà§à¦°à§à¦¯à¦¾à¦¶ পà§à¦°à¦¤à¦¿à¦¬à§‡à¦¦à¦¨ সকà§à¦·à¦® করেন<ph name="END_LINK" /> তাহলে à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà¦¿ কেবল আপনার সামà§à¦ªà§à¦°à¦¤à¦¿à¦• কà§à¦°à§à¦¯à¦¾à¦¶à¦—à§à¦²à¦¿à¦° তথà§à¦¯ দেখায়৷</translation>
<translation id="3875312571075912821">আপনার ফায়ারওয়াল বা অà§à¦¯à¦¾à¦¨à§à¦Ÿà¦¿à¦­à¦¾à¦‡à¦°à¦¾à¦¸ সেটিংসে Chrome কে নেটওয়ারà§à¦• অà§à¦¯à¦¾à¦•à§à¦¸à§‡à¦¸
করতে অনà§à¦®à¦¤à¦¿ দিন।</translation>
@@ -24,7 +24,7 @@
&gt;
<ph name="ADVANCED_TITLE" />
à¦à¦¬à¦‚ অনিরà§à¦¬à¦¾à¦šà¦¨ করà§à¦¨ "<ph name="NO_PREFETCH_DESCRIPTION" />৷"
- যদি à¦à¦Ÿà¦¿ সমসà§à¦¯à¦¾à¦Ÿà¦¿à¦° সমাধান না করে, তাহলে উনà§à¦¨à¦¤ কারà§à¦¯ সমà§à¦ªà¦¾à¦¦à¦¨à¦¾à¦° জনà§à¦¯ পà§à¦¨à¦°à¦¾à§Ÿ à¦à¦‡ বিকলà§à¦ªà¦Ÿà¦¿ নিরà§à¦¬à¦¾à¦šà¦¨ করা সà§à¦ªà¦¾à¦°à¦¿à¦¶ আমরা করি৷</translation>
+ যদি à¦à¦Ÿà¦¿ সমসà§à¦¯à¦¾à¦Ÿà¦¿à¦° সমাধান না করে, তাহলে উনà§à¦¨à¦¤ কারà§à¦¯ সমà§à¦ªà¦¾à¦¦à¦¨à¦¾à¦° জনà§à¦¯ আবার à¦à¦‡ বিকলà§à¦ªà¦Ÿà¦¿ নিরà§à¦¬à¦¾à¦šà¦¨ করা সà§à¦ªà¦¾à¦°à¦¿à¦¶ আমরা করি৷</translation>
<translation id="6855094794438142393">Chrome মেনà§à¦¤à§‡
যান &gt;
<ph name="SETTINGS_TITLE" />
diff --git a/chromium/components/strings/components_strings_am.xtb b/chromium/components/strings/components_strings_am.xtb
index 1915ef25b8b..e9d53a11223 100644
--- a/chromium/components/strings/components_strings_am.xtb
+++ b/chromium/components/strings/components_strings_am.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">ከWi-Fi ጋር ዳáŒáˆ በማገናኘት</translation>
<translation id="1175364870820465910">&amp;አትáˆâ€¦</translation>
<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="1206967143813997005">መጥᎠየመጀመሪያ áŠáˆ­áˆ›</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">á‹áˆƒ ሰማያዊ</translation>
<translation id="1629803312968146339">ይህን ካርድ Chrome እንዲያስቀáˆáŒ¥áˆá‹Žá‰µ á‹­áˆáˆáŒ‹áˆ‰?</translation>
<translation id="1640180200866533862">የተጠቃሚ መáˆáˆªá‹«á‹Žá‰½</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />የጣቢያá‹áŠ• መáŠáˆ» ገጽ ለመጎብኘት<ph name="END_LINK" /> ይሞክሩá¢</translation>
<translation id="1644184664548287040">የአá‹á‰³áˆ¨ መረቡ á‹á‰…ር áˆáŠ­ á‹«áˆáˆ†áŠ እና ሊመጣ የማይችሠáŠá‹á¢</translation>
<translation id="1644574205037202324">ታሪክ</translation>
<translation id="1645368109819982629">የማይደገá á•áˆ®á‰¶áŠ®áˆ</translation>
<translation id="1676269943528358898"><ph name="SITE" /> የእርስዎን መረጃ ለመጠበቅ በመደበáŠáŠá‰µ áˆáˆµáŒ áˆ« ይጠቀማáˆá¢ Google Chrome አáˆáŠ• ከ<ph name="SITE" /> ጋር ለመገናኘት ሲሞክር ድር ጣቢያዠያáˆá‰°áˆˆáˆ˜á‹± እና ትክክሠያáˆáˆ†áŠ‘ áˆáˆµáŠ­áˆ­áŠá‰¶á‰½áŠ• መáˆáˆ·áˆá¢ ይህ አንድ አጥቂ <ph name="SITE" />ን አስመስሎ ለመቅረብ ሲሞክር áŠá‹ ወይሠአንድ የWi-Fi መáŒá‰¢á‹« ገጽ áŒáŠ•áŠ™áŠá‰±áŠ• ሲያቋረጥ ሊከሰት ይችላáˆá¢ Google Chrome ማንኛá‹áˆ የá‹áˆ‚ብ áˆá‹á‹áŒ¥ ከመካሄዱ በáŠá‰µ áŒáŠ•áŠ™áŠá‰±áŠ• ስላቋረጠዠየእርስዎ መረጃ ደህንáŠá‰µ አáˆáŠ•áˆ የተጠበቀ áŠá‹á¢</translation>
+<translation id="168328519870909584">በአáˆáŠ‘ ጊዜ በ<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ላይ ያሉ አጥቂዎች መረጃዎን (ለáˆáˆ³áˆŒá¦ áŽá‰¶á‹Žá‰½á£ የይለá ቃላትᣠመáˆá‹•áŠ­á‰¶á‰½ እና ክሬዲት ካርዶች) የሚሰርበወይሠየሚሰርዙ አደገኛ መተáŒá‰ áˆªá‹«á‹Žá‰½áŠ• ለመጫን ሊሞክሩ ይችላሉá¢</translation>
<translation id="168841957122794586">የአገáˆáŒ‹á‹­ እá‹á‰…ና ማረጋገጫዠደካማ የሆአባለስá‹áˆ­ መረጃ á‰áˆá áŠá‹ ያለá‹á¢</translation>
-<translation id="1701955595840307032">የተጠቆመ ይዘት á‹«áŒáŠ™</translation>
<translation id="1710259589646384581">ስርዓተ ክወና</translation>
<translation id="1721312023322545264">ይህን ጣቢያ ለመጎብኘት ከ<ph name="NAME" /> áˆá‰ƒá‹µ ያስáˆáˆáŒˆá‹Žá‰³áˆ</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">የገጽ á‰áŒ¥áˆ­</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />የWindows አá‹á‰³áˆ¨ መረብ መመርመሪያá‹áŠ• ለማሄድ ይሞክሩ<ph name="END_LINK" />á¢</translation>
<translation id="1783075131180517613">እባክዎ የማመሳሰሠይለá áˆáˆ¨áŒá‹ŽáŠ• ያዘáˆáŠ‘á¢</translation>
+<translation id="1787142507584202372">የእርስዎ ክáት ትሮች እዚህ ይመጣሉ</translation>
<translation id="1791429645902722292">Google ዘመናዊ á‰áˆá</translation>
+<translation id="1803678881841855883">Google የጥንቃቄ አሰሳ በቅርቡ በ<ph name="SITE" /> ላይ <ph name="BEGIN_LINK" />ተንኮáˆ-አዘሠዌር<ph name="END_LINK" /> እንዳለ ደርሶበታáˆá¢ በመደበኛ ጊዜ ደህንáŠá‰³á‰¸á‹ የተጠበበድር ጣቢያዎች አንዳንድ ጊዜ በተንኮáˆ-አዘሠዌር ሊጠበይችላሉᢠበተንኮáˆ-አዘሠዌር አሰራጭáŠá‰µ ከሚታወቀዠ<ph name="SUBRESOURCE_HOST" /> የመጣ áŠá‹á¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="1821930232296380041">áˆáŠ­ á‹«áˆáˆ†áŠ ጥያቄ ወይሠየጥያቄ áˆáŠ¬á‰¶á‰½</translation>
+<translation id="1834321415901700177">ይህ ጣቢያ ጎጂ á•áˆ®áŒáˆ«áˆžá‰½áŠ• á‹­á‹Ÿáˆ</translation>
<translation id="1838667051080421715">የአንድ ድረ-ገጽ áˆáŠ•áŒ­áŠ• እየተመለከቱ áŠá‹á¢</translation>
<translation id="1871208020102129563">የ.pac ስክሪá•á‰µ ዩአርኤሠሳይሆን ተኪ አገáˆáŒ‹á‹®á‰½áŠ• እንዲጠቀሠáŠá‹ ተኪ የተዋቀረá‹á¢</translation>
<translation id="1883255238294161206">á‹áˆ­á‹áˆ­ ሰብስብ</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">ቤተኛ ደንበኛ</translation>
<translation id="213826338245044447">የተንቀሳቃሽ ስáˆáŠ­ á‹•áˆá‰£á‰¶á‰½</translation>
<translation id="2148716181193084225">ዛሬ</translation>
-<translation id="2149973817440762519">እáˆá‰£á‰µ አርትዕ</translation>
<translation id="2154054054215849342">ስáˆáˆ¨á‰µ ለእርስዎ ጎራ አይገáŠáˆ</translation>
<translation id="2166049586286450108">ሙሉ የአስተዳደር መድረሻ</translation>
<translation id="2166378884831602661">ይህ ጣቢያ ደህንáŠá‰± አስተማማአየሆአáŒáŠ•áŠ™áŠá‰µ ማቅረብ አይችáˆáˆ</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">የድር ጣቢያዠHSTS ስለሚጠቀሠአáˆáŠ• <ph name="SITE" /> መጎብኘት አይችሉáˆá¢ የአá‹á‰³áˆ¨ መረብ ስህተቶች እና ጥቃቶች አብዛኛዠጊዜ ጊዜያዊ ናቸá‹á£ ስለዚህ ይህ ገጽ በኋላ ላይ ሊሠራ ይችላáˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="2328300916057834155">ችላ የተባለ የመረጃ ጠቋሚ <ph name="ENTRY_INDEX" /> áˆáŠ­ á‹«áˆáˆ†áŠ እáˆá‰£á‰µ</translation>
<translation id="2354001756790975382">ሌላ እáˆá‰£á‰¶á‰½</translation>
+<translation id="2355395290879513365">አጥቂዎች በዚህ ጣቢያ ላይ እርስዎ እየተመለከቱዋቸዠያሉ áˆáˆµáˆŽá‰½áŠ• ማየት እና በላያቸዠላይ ለá‹áŒ¦á‰½áŠ• በማድረጠሊያታáˆáˆá‹Žá‰µ ይችሉ ይሆናáˆá¢</translation>
<translation id="2359808026110333948">ቀጥáˆ</translation>
<translation id="2365563543831475020">በ<ph name="CRASH_TIME" /> ላይ የተያዘዠየብáˆáˆ½á‰µ ሪá–ርት አáˆá‰°áˆ°á‰€áˆˆáˆ</translation>
<translation id="2367567093518048410">ደረጃ</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">áˆáŠ•áˆ እሴት á‹«áˆá‰°á‹‹á‰€áˆ¨áˆ‹á‰¸á‹ መáˆáˆªá‹«á‹Žá‰½áŠ• አሳይ</translation>
<translation id="2396249848217231973">&amp;ስረዛን ቀáˆá‰¥áˆµ</translation>
<translation id="2455981314101692989">ይህ ድረ-ገጽ ለዚህ ቅጽ ራስ-መሙላትን አሰናክáˆáˆá¢</translation>
+<translation id="2460160116472764928">Google የጥንቃቄ አሰሳ በቅርብ ጊዜ በ<ph name="SITE" /> ላይ <ph name="BEGIN_LINK" />ተንኮáˆ-አዘሠዌር<ph name="END_LINK" /> ላይ አáŒáŠá‰·áˆá¢ በመደበኛ ጊዜ ደህንáŠá‰³á‰¸á‹ የተጠበበድር ጣቢያዎች አንዳንድ ጊዜ በተንኮáˆ-አዘሠዌር ሊጠበይችላሉᢠ<ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">ሙላ</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />የአá‹á‰³áˆ¨ መረብ መመርመሪያን በማሄድ ላይ<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">áˆáŠ­ á‹«áˆáˆ†áŠ የáለጋ ዩአርኤáˆá¢</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">እርáŒáŒ áŠ› áŠá‹Žá‰µ እáŠá‹šáˆ…ን ገጾች ከታሪክዎ መሰረዠይáˆáˆáŒ‹áˆ‰?</translation>
<translation id="2677748264148917807">ለቅቀህ á‹áŒ£</translation>
<translation id="269990154133806163">አገáˆáŒ‹á‹© የእá‹á‰…ና ማረጋገጫ áŒáˆáŒ½áŠá‰µ መመሪያá‹áŠ• በመጠቀሠበይዠያáˆá‰°áŒˆáˆˆáŒ¸ የእá‹á‰…ና ማረጋገጫን አቅርቧáˆá¢ ይህ ለአንዳንድ የእá‹á‰…ና ማረጋገጫዎች ሊታመኑ የሚችሉ መሆናቸá‹áŠ• ለማረጋገጥ እና ከአጥቂዎች ጥበቃ ለማድረጠእንዲቻሠአስáˆáˆ‹áŒŠ áŠá‹á¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">የንባብ á‹áˆ­á‹áˆ­</translation>
<translation id="2704283930420550640">ዋጋ ከቅርጸት ጋር አይዛመድáˆá¢</translation>
<translation id="2704951214193499422">Chromium በዚህ ጊዜ የእርስዎን ካርድ ማረጋገጥ አáˆá‰»áˆˆáˆá¢ እባክዎ ቆይተዠእንደገና ይሞክሩá¢</translation>
<translation id="2705137772291741111">የተቀመጠዠ(የተሸጎጠ) የዚህ ጣቢያ ቅጂ የሚáŠá‰ á‰¥ አáˆáŠá‰ áˆ¨áˆá¢</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082"><ph name="DOMAIN" />ን ለመድረስ ሞክረዋáˆá£ áŠáŒˆáˆ­ áŒáŠ• አገáˆáŒ‹á‹© ያቀረበዠየእá‹á‰…ና ማረጋገጫ በሰጪዠተሽሯáˆá¢ ይህ ማለት አገáˆáŒ‹á‹© ያቀረበዠየደህንáŠá‰µ áˆáˆµáŠ­áˆ­áŠá‰¶á‰½ áˆáŒ½áˆž ሊታመኑ አይገባáˆá¢ ከአጥቂ ጋር እየተገናኙ ሊሆን ይችላáˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">áቃድ ጠይቅ</translation>
<translation id="2713444072780614174">áŠáŒ­</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">ከቅንብሮች ገጽ ሆáŠá‹ ማናቸá‹áŠ•áˆ ለáŒáŠ•áŠ™áŠá‰µ የተዋቀሩ ተኪዎችን ማሰናከሠይችላሉá¢</translation>
<translation id="2955913368246107853">አáŒáŠ አሞሌን á‹áŒ‹</translation>
<translation id="2958431318199492670">የአá‹á‰³áˆ¨ መረብ á‹á‰…ሩ በኦ ኤን ሲ መስáˆáˆ­á‰± አይገዛáˆá¢ አንዳንድ የá‹á‰…ሩ ክáሎች ላይመጡ ይችላሉá¢</translation>
+<translation id="29611076221683977">በአáˆáŠ‘ ጊዜ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ላይ ያሉ አጥቂዎች በእርስዎ ማክ ላይ መረጃዎን የሚሰርበወይሠየሚሰርዙ (ለáˆáˆ³áˆŒá¦ áŽá‰¶á‹Žá‰½á£ የይለá ቃላትᣠመáˆá‹•áŠ­á‰¶á‰½ እና ክሬዲት ካርዶች) አደገኛ á•áˆ®áŒáˆ«áˆžá‰½áŠ• ለመጫን ሊሞክሩ ይችላሉá¢</translation>
<translation id="2969319727213777354">ደህንáŠá‰± የተጠበቀ áŒáŠ•áŠ™áŠá‰µ ለመመስረት የእርስዎ ሰዓት በትክክሠመዋቀር አለበትᢠይሄ የሆáŠá‰ á‰µ áˆáŠ­áŠ•á‹«á‰µ ድር ጣቢያዎች ራሳቸá‹áŠ• ለማሳወቅ የሚጠቀሙባቸዠየእá‹á‰…ና ማረጋገጫዎች የሚሰሩት ለተወሰኑ ጊዜዎች ብቻ ስለሆአáŠá‹á¢ የእርስዎ መሣሪያ ሰዓት ትክክሠእንዳለመሆኑ መጠን Google Chrome እáŠá‹šáˆ…ን የእá‹á‰…ና ማረጋገጫዎች ሊያረጋáŒáŒ¥ አይችáˆáˆá¢</translation>
<translation id="2972581237482394796">&amp;ድገáˆ</translation>
<translation id="2985306909656435243">ከáŠá‰ƒ Chromium ለተሻለ የቅጽ አሞላሠáጥáŠá‰µ ሲባሠበዚህ መሣሪያ ላይ ያለዠየካርድዎን ቅጂ ያከማቻáˆá¢</translation>
@@ -258,7 +267,6 @@
<translation id="3586931643579894722">á‹áˆ­á‹áˆ­ ደብቅ</translation>
<translation id="3587482841069643663">áˆáˆ‰áˆ</translation>
<translation id="3600246354004376029"><ph name="TITLE" />ᣠ<ph name="DOMAIN" />ᣠ<ph name="TIME" /></translation>
-<translation id="3609138628363401169">አገáˆáŒ‹á‹© የቲኤáˆáŠ¤áˆµ ዳáŒáˆ ድርድር ቅጥያá‹áŠ• አይደáŒááˆá¢</translation>
<translation id="36224234498066874">የአሰሳ á‹áˆ‚ብ አስወáŒá‹µâ€¦</translation>
<translation id="362276910939193118">ሙሉ ታሪክ አሳይ</translation>
<translation id="3623476034248543066">እሴት አሳይ</translation>
@@ -270,19 +278,20 @@
<translation id="3655670868607891010">ይህንን በተደጋጋሚáŠá‰µ የሚያዩ ከሆኑ <ph name="HELP_LINK" />ን ይሞክሩá¢</translation>
<translation id="3658742229777143148">ክለሳ</translation>
<translation id="3678029195006412963">ጥያቄ ሊáˆáˆ¨áˆ አáˆá‰°á‰»áˆˆáˆ</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="3696411085566228381">áˆáŠ•áˆ</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">በመጫን ላይ...</translation>
+<translation id="370665806235115550">በመጫን ላይ…</translation>
<translation id="3712624925041724820">áˆáˆ‰áˆ áቃዶች ተሞክረዋáˆ</translation>
<translation id="3714780639079136834">የሞባይሠá‹áˆ‚ብ ወይሠWi-Fi ማብራት</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />ወኪሉንᣠኬላá‹áŠ• እና የዲኤንኤስ á‹á‰…ረትን መáˆá‰°áˆ½<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">ደህንáŠá‰µá‹Ž ላይ የሚያመጣቸá‹áŠ• ስጋቶች ከተረዱ አደገኛ á•áˆ®áŒáˆ«áˆžá‰¹ ከመወገዳቸዠበáŠá‰µ <ph name="BEGIN_LINK" />ይህን ደህንáŠá‰± á‹«áˆá‰°áŒ á‰ á‰€ ጣቢያ ሊጎብኙ<ph name="END_LINK" /> ይችላሉá¢</translation>
<translation id="3739623965217189342">እርስዎ የቀዱት አገናáŠ</translation>
<translation id="375403751935624634">በአገáˆáŒ‹á‹­ ስህተት áˆáŠ­áŠ•á‹«á‰µ የትርጉሠስራዠተሰናክáˆáˆá¢</translation>
<translation id="3759461132968374835">በቅርብ ጊዜ ሪá–ርት የተደረጉ ብáˆáˆ½á‰¶á‰½ የለዎትáˆá¢ የብáˆáˆ½á‰µ ሪá–ርት ማድረጠተሰናክሎ ሳለ የተከሰቱ ብáˆáˆ½á‰¶á‰½ እዚህ አይታዩáˆá¢</translation>
-<translation id="3788090790273268753">የዚህ ጣቢያ የዕá‹á‰…ና ማረጋገጫ አገáˆáŒáˆŽá‰µ 2016 ላይ ያበቃáˆá£ እና የዕá‹á‰…ና ማረጋገጫ ሰንሰለቱ SHA-1 በመጠቀሠየተáˆáˆ¨áˆ˜ የዕá‹á‰…ና ማረጋገጫን ያካትታáˆá¢</translation>
<translation id="382518646247711829">ተኪ አገáˆáŒ‹á‹­ የሚጠቀሙ ከሆኑ...</translation>
<translation id="3828924085048779000">ባዶ የይለá áˆáˆ¨áŒ አይáˆá‰€á‹µáˆá¢</translation>
<translation id="3845539888601087042">ታሪክን ወደ መለያዎ ከገቡ መሣሪያዎችዎ በማሳየት ላይᢠ<ph name="BEGIN_LINK" />የበለጠ ለመረዳት<ph name="END_LINK" /></translation>
@@ -304,6 +313,7 @@
<translation id="4058922952496707368">á‰áˆá «<ph name="SUBKEY" />»ᦠ<ph name="ERROR" /></translation>
<translation id="4075732493274867456">ደንበኛዠእና አገáˆáŒ‹á‹© የተለመደ የኤስኤስኤሠá•áˆ®á‰¶áŠ®áˆ ስሪት ወይሠየስአመሰá‹áˆ­ ጥቅሠአይደáŒá‰áˆá¢</translation>
<translation id="4079302484614802869">የተኪ á‹á‰…ር ቋሚ አገáˆáŒ‹á‹®á‰½áŠ• ሳይሆን የ.pac ስክሪá•á‰µ ዩአርኤሠለመጠቀሠáŠá‹ የተዋቀረá‹á¢</translation>
+<translation id="4098354747657067197">አሳሳች ጣቢያ ከáŠá‰µ አለ</translation>
<translation id="4103249731201008433">የመሣሪያ መለያ á‰áŒ¥áˆ­ áˆáŠ­ á‹«áˆáˆ†áŠ áŠá‹</translation>
<translation id="4103763322291513355">የተከለከሉ የዩ አር ኤሎች á‹áˆ­á‹áˆ­ እና ሌሎች በስርዓት አስተዳዳሪዎ አስገዳጅáŠá‰µ የተሰጣቸዠመመሪያዎችን ለማየት &lt;strong&gt;chrome://policy&lt;/strong&gt;ን ይጎብኙá¢</translation>
<translation id="4110615724604346410">ይህ አገáˆáŒ‹á‹­ <ph name="DOMAIN" /> መሆኑን ማረጋገጥ አáˆá‰»áˆˆáˆá¤ የዕá‹á‰…ና ማረጋገጫዠስህተቶች አሉበትᢠይህ በተሳሳተ á‹á‰…ረት ወይሠየእርስዎን áŒáŠ•áŠ™áŠá‰µ በሚጠáˆá አጥቂ áˆáŠ­áŠ•á‹«á‰µ የተáˆáŒ áˆ¨ ሊሆን ይችላáˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
@@ -321,15 +331,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{áˆáŠ•áˆ}=1{1 መተáŒá‰ áˆªá‹« ($1)}=2{2 መተáŒá‰ áˆªá‹«á‹Žá‰½ ($1ᣠ$2)}one{# መተáŒá‰ áˆªá‹«á‹Žá‰½ ($1ᣠ$2ᣠ$3)}other{# መተáŒá‰ áˆªá‹«á‹Žá‰½ ($1ᣠ$2ᣠ$3)}}</translation>
<translation id="4220128509585149162">ብáˆáˆ½á‰¶á‰½</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />የአá‹á‰³áˆ¨ መረብ መመርመሪያን አሂደዠይሞክሩ<ph name="END_LINK" /></translation>
+<translation id="4250431568374086873">ወደዚህ ጣቢያ á‹«áˆá‹Žá‰µ áŒáŠ•áŠ™áŠá‰µ ሙሉ በሙሉ ደህንáŠá‰± አስተማማአአይደለáˆá¢</translation>
<translation id="4250680216510889253">አይ</translation>
<translation id="425582637250725228">ያደረጓቸዠለá‹áŒ¦á‰½ ላይቀመጡ ይችላሉá¢</translation>
<translation id="4258748452823770588">መጥᎠáŠáˆ­áˆ›</translation>
<translation id="4269787794583293679">(áˆáŠ•áˆ የተጠቃሚ ስሠየለáˆ)</translation>
+<translation id="4280429058323657511">ᣠአገáˆáŒáˆŽá‰± የሚያበቃዠበ<ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google የጥንቃቄ አሰሳ በቅርቡ በ<ph name="SITE" /> <ph name="BEGIN_LINK" />ጎጂ á•áˆ®áŒáˆ«áˆžá‰½<ph name="END_LINK" />ን ላይ አáŒáŠá‰·áˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="4300246636397505754">የወላጅ አስተያየት ጥቆማዎች</translation>
<translation id="4304224509867189079">á‹­áŒá‰¡</translation>
<translation id="432290197980158659">አገáˆáŒ‹á‹© በá‹áˆµáŒ -áŒáŠ•á‰¡ የሚጠበበáŠáŒˆáˆ®á‰½ ጋር የማይዛመድ የዕá‹á‰…ና ማረጋገጫ አቅርቧáˆá¢ እáŠá‹šáˆ… የሚጠበበáŠáŒˆáˆ®á‰½ እርስዎን ለመጠበቅ ሲባሠከáተኛ ደህንáŠá‰µ ጥበቃ በሚያስáˆáˆáŒ‹á‰¸á‹ የተወሰኑ ድር ጣቢያዎች ላይ ተካትተዋáˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="4325863107915753736">ጽሑá‰áŠ• ማáŒáŠ˜á‰µ አáˆá‰°á‰»áˆˆáˆ</translation>
+<translation id="4326324639298822553">የእርስዎን የአገáˆáŒáˆŽá‰µ ማብቂያ ቀን ይመáˆáŠ¨á‰± እና እንደገና ይሞክሩ</translation>
<translation id="4331708818696583467">ደህንáŠá‰± አáˆá‰°áŒ á‰ á‰€áˆ</translation>
+<translation id="4356973930735388585">በዚህ ጣቢያ ላይ ያሉ አጥቂዎች መረጃዎን (ለáˆáˆ³áˆŒá¦ áŽá‰¶á‹Žá‰½á£ የይለá ቃላትᣠመáˆá‹•áŠ­á‰¶á‰½ እና ክሬዲት ካርዶች) ሊሰርበወይሠሊሰርዙ የሚችሉ አደገኛ á•áˆ®áŒáˆ«áˆžá‰½áŠ• በኮáˆá’á‹á‰°áˆ­á‹Ž ላይ ለመጫን ሊሞክሩ ይችላሉá¢</translation>
<translation id="4372948949327679948">የተጠበቀዠየ<ph name="VALUE_TYPE" /> ዋጋ áŠá‹á¢</translation>
<translation id="4381091992796011497">የተጣቃሚ ስáˆá¦</translation>
<translation id="4394049700291259645">አሰናክáˆ</translation>
@@ -347,6 +362,7 @@
<translation id="4589078953350245614">ወደ <ph name="DOMAIN" /> ለመድረስ ሞክረዠáŠá‰ áˆ­á£ áŠáŒˆáˆ­ áŒáŠ• አገáˆáŒ‹á‹© የማይሠራ የዕá‹á‰…ና ማረጋገጫ አቅርቧáˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="4592951414987517459">ወደ የእርስዎ <ph name="DOMAIN" /> áŒáŠ•áŠ™áŠá‰µ ዘመናዊ የáˆáˆµáŒ áˆ« ጥቅሠበመጠቀሠተመስጥሯáˆá¢</translation>
<translation id="4594403342090139922">&amp;ሰርá‹áŠ• ቀáˆá‰¥áˆµ</translation>
+<translation id="4619615317237390068">ከሌሎች መሣሪያዎች የመጡ ትሮች</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">የቅጥያ ገጽ እየተመለከቱ áŠá‹á¢</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -355,10 +371,13 @@
<translation id="4726672564094551039">መáˆáˆªá‹«á‹Žá‰½áŠ• ዳáŒáˆ ጫን</translation>
<translation id="4728558894243024398">የመሣሪያ ስርዓት</translation>
<translation id="4744603770635761495">የሚáˆáŒ¸áˆ ዱካ</translation>
+<translation id="4750917950439032686">የእርስዎ መረጃ (ለáˆáˆ³áˆŒá¦ የይለá ቃሎች ወይሠየክሬዲት ካርድ á‰áŒ¥áˆ®á‰½) ወደዚህ ጣቢያ በሚላክበት ጊዜ የáŒáˆ áŠá‹á¢</translation>
<translation id="4756388243121344051">&amp;ታሪክ</translation>
+<translation id="4759118997339041434">የክáá‹« ራስ-መሙላት ተሰናክáˆáˆ</translation>
<translation id="4764776831041365478"><ph name="URL" /> ላይ ያለዠድረ-ገጽ ለጊዜዠየማይሰራ ወይሠእስከመጨረሻዠወደ አዲስ የድር አድራሻ ተዛá‹áˆ® ሊሆን ይችላáˆá¢</translation>
<translation id="4771973620359291008">á‹«áˆá‰³á‹ˆá‰€ ስህተት ተከስቷáˆá¢</translation>
<translation id="4800132727771399293">የእርስዎን የአገáˆáŒáˆŽá‰µ ማብቂያ ቀን እና CVC á‹­áˆá‰µáˆ¹ እና እንደገና ይሞክሩ</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">የአá‹á‰³áˆ¨ መረብ ስህተት</translation>
<translation id="4816492930507672669">ገጹን አመጣጥን</translation>
<translation id="4850886885716139402">አሳይ</translation>
@@ -367,6 +386,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />ᣠ<ph name="TYPE_2" />á£, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{እና 1 ተጨማሪ ድረ-ገጽ}one{እና # ተጨማሪ ድረ-ገጾች}other{እና # ተጨማሪ ድረ-ገጾች}}</translation>
<translation id="4923417429809017348">ገጹ ከማይታወቅ ቋንቋ ወደ <ph name="LANGUAGE_LANGUAGE" /> ተተርጉሟáˆ</translation>
+<translation id="4923459931733593730">ክáá‹«</translation>
<translation id="4926049483395192435">መገለጽ አለበትá¢</translation>
<translation id="495170559598752135">እርáˆáŒƒá‹Žá‰½</translation>
<translation id="4958444002117714549">á‹áˆ­á‹áˆ©áŠ• ዘርጋ</translation>
@@ -394,7 +414,6 @@
<translation id="5181140330217080051">በማá‹áˆ¨á‹µ ላይ</translation>
<translation id="5190835502935405962">የዕáˆá‰£á‰¶á‰½ አሞሌ</translation>
<translation id="5199729219167945352">ሙከራዎች</translation>
-<translation id="5199841536747119669">የእርስዎ የአስተያየት ጥቆማዎች እዚህ ይመጣሉ</translation>
<translation id="5251803541071282808">ደመና</translation>
<translation id="5277279256032773186">በሥራ ላይ Chrome እየተጠቀሙ áŠá‹Žá‰µ? ንáŒá‹µ ሥራዎች ለሠራተኞቻቸዠየChrome ቅንብሮችን ማስተዳደር ይችላሉᢠየበለጠ ይረዱ</translation>
<translation id="5299298092464848405">መáˆáˆªá‹«áŠ• መተንተን ላይ ስህተት</translation>
@@ -402,8 +421,10 @@
<translation id="5308689395849655368">የብáˆáˆ½á‰µ ሪá–ርት ማድረጠተሰናክáˆáˆá¢</translation>
<translation id="5317780077021120954">አስቀáˆáŒ¥</translation>
<translation id="5327248766486351172">ስáˆ</translation>
+<translation id="5337705430875057403">በ<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ላይ ያሉ አጥቂዎች እንደ ሶáትዌር መጫን ወይሠየáŒáˆ መረጃዎን (ለáˆáˆ³áˆŒá¦ የይለá ቃሎችᣠየስáˆáŠ­ á‰áŒ¥áˆ®á‰½ ወይሠክሬዲት ካርዶች) አሳáˆáŽ መáŒáˆˆáŒ½ ያሉ አደገኛ áŠáŒˆáˆ®á‰½áŠ• እንዲያደርጉ ሊያታáˆáˆ‰á‹Žá‰µ ይችሉ ይሆናáˆá¢</translation>
<translation id="5359637492792381994">ይህ አገáˆáŒ‹á‹­ <ph name="DOMAIN" /> መሆኑን ማረጋገጥ አáˆá‰»áˆˆáˆá¤ የዕá‹á‰…ና ማረጋገጫዠበዚህ ጊዜ ላይ የሚሠራ አይደለáˆá¢ ይህ በተሳሳተ á‹á‰…ረት ወይሠየእርስዎን áŒáŠ•áŠ™áŠá‰µ በሚጠáˆá አጥቂ áˆáŠ­áŠ•á‹«á‰µ የተáˆáŒ áˆ¨ ሊሆን ይችላáˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="536296301121032821">የመáˆáˆªá‹« ቅንብሮችን ማከማቸት አáˆá‰°áˆ³áŠ«áˆ</translation>
+<translation id="5386426401304769735">የዚህ ጣቢያ የዕá‹á‰…ና ማረጋገጫ ሰንሰለቱ SHA-1 በመጠቀሠየተáˆáˆ¨áˆ˜ የዕá‹á‰…ና ማረጋገጫን ያካትታáˆá¢</translation>
<translation id="5421136146218899937">የአሰሳ á‹áˆ‚ብ አጽዳ…</translation>
<translation id="5430298929874300616">á‹•áˆá‰£á‰µ አስወáŒá‹µ</translation>
<translation id="5431657950005405462">የእርስዎ á‹á‹­áˆ አáˆá‰°áŒˆáŠ˜áˆ</translation>
@@ -426,17 +447,20 @@ nil</translation>
<translation id="5544037170328430102"><ph name="SITE" /> ላይ የተካተተ ገጽ እንዲህ ይላáˆá¦</translation>
<translation id="5556459405103347317">ዳáŒáˆ ጫን</translation>
<translation id="5565735124758917034">ገባሪ</translation>
+<translation id="5572851009514199876">Chrome እርስዎ ይህን ጣቢያ እንዲደርሱ የተáˆá‰€á‹°áˆá‹Ž መሆኑን ወይሠአለመሆኑን እንዲያረጋáŒáŒ¥ እባክዎ ይጀáˆáˆ©áŠ“ ወደ Chrome á‹­áŒá‰¡á¢</translation>
+<translation id="5580958916614886209">የእርስዎን የአገáˆáŒáˆŽá‰µ ማብቂያ ወር ይመáˆáŠ¨á‰± እና እንደገና ይሞክሩ</translation>
<translation id="560412284261940334">አስተዳደር አይደገááˆ</translation>
<translation id="5610142619324316209">áŒáŠ•áŠ™áŠá‰±áŠ• መáˆá‰°áˆ½</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> እርስዎን በጣሠብዙ ጊዜ ወደ ሌላ ቦታ መርተዎታáˆá¢</translation>
<translation id="5622887735448669177">ይህን ጣቢያ መá‹áŒ£á‰µ á‹­áˆáˆáŒ‹áˆ‰?</translation>
<translation id="5629630648637658800">የመáˆáˆªá‹« ቅንብሮችን መጫን አáˆá‰°áˆ³áŠ«áˆ</translation>
<translation id="5631439013527180824">áˆáŠ­ á‹«áˆáˆ†áŠ የመሣሪያ አስተዳደር ማስመሰያ</translation>
+<translation id="5669703222995421982">áŒáˆ‹á‹ŠáŠá‰µ የተላበሰ ይዘት á‹«áŒáŠ™</translation>
+<translation id="5675650730144413517">ይህ ገጽ እየሠራ አይደለáˆ</translation>
<translation id="5677928146339483299">ታáŒá‹·áˆ</translation>
<translation id="5694783966845939798"><ph name="DOMAIN" />ን ለመድረስ ሞክረዋáˆá£ áŠáŒˆáˆ­ áŒáŠ• አገáˆáŒ‹á‹© ደካማ የáŠáˆ­áˆ› ስáˆá‰°-ቀመር (እንደ SHA-1 ያለ) በመጠቀሠየተáˆáˆ¨áˆ˜ የእá‹á‰…ና ማረጋገጫ áŠá‹ ያቀረበá‹á¢ ይህሠማለት አገáˆáŒ‹á‹© ያቀረበዠየደህንáŠá‰µ áˆáˆµáŠ­áˆ­áŠá‰¶á‰½ የተጭበረበሩ ሊሆኑ ይችላሉᣠእናሠአገáˆáŒ‹á‹© እርስዎ የሚጠብá‰á‰µ አገáˆáŒ‹á‹­ ላይሆን ይችላሠ(ከአጥቂ ጋር እየተገናኙ ሊሆን ይችላáˆ)ᢠ<ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="5710435578057952990">የዚህ ድረ-ገጽ ማንáŠá‰µ አáˆá‰°áˆ¨áŒ‹áŒˆáŒ áˆá¢</translation>
<translation id="5720705177508910913">የአáˆáŠ‘ ተጠቃሚ</translation>
-<translation id="572328651809341494">የቅርብ ጊዜ ትሮች</translation>
<translation id="5732392974455271431">የእርስዎ ወላጆች እገዳá‹áŠ• ሊያáŠáˆ±áˆá‹Ž ይችላሉ</translation>
<translation id="5784606427469807560">የእርስዎን ካርድ ማረጋገጥ ላይ አንድ ችáŒáˆ­ áŠá‰ áˆ­á¢ የበይáŠáˆ˜áˆ¨á‰¥ áŒáŠ•áŠ™áŠá‰µá‹ŽáŠ• á‹­áˆá‰µáˆ¹á‰µ እና እንደገና ይሞክሩá¢</translation>
<translation id="5785756445106461925">በተጨማሪᣠይህ ገጽ ደህንáŠá‰³á‰¸á‹ á‹«áˆá‰°áŒ á‰ á‰€ ሌሎች ንብረቶችን አካትቷáˆá¢ እáŠá‹šáˆ… ንብረቶች በሽáŒáŒáˆ­ ወቅት በሌሎች ሊታዩ ይችላሉᣠእናሠየገጹን መáˆáŠ­ ለመለወጥ በአጥቂዎች ሊቀየሩ ይችላሉá¢</translation>
@@ -445,6 +469,7 @@ nil</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="5843436854350372569"><ph name="DOMAIN" />ን ለመድረስ ሞክረዋáˆá£ áŒáŠ• አገáˆáŒ‹á‹© ደካማ á‰áˆá የያዘ የእá‹á‰…ና ማረጋገጫ áŠá‹ ያቀረበá‹á¢ አንድ አጥቂ የáŒáˆ á‰áˆá‰áŠ• ሰብሮ ሊሆን ይችላáˆá£ እና አገáˆáŒ‹á‹© የጠበá‰á‰µ ላይሆን ይችላሠ(ከአጥቂ ጋር እየተገናኙ ሊሆኑ ይችላሉ)ᢠ<ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">አዲስ አቃáŠ</translation>
<translation id="5869405914158311789">ይህ ጣቢያ ሊደረስበት አይችáˆáˆ</translation>
@@ -454,6 +479,7 @@ nil</translation>
<translation id="59107663811261420">ይህ የካርድ አይáŠá‰µ ለዚህ áŠáŒ‹á‹´ በGoogle ክáያዎች አይደገááˆá¢ እባክዎ የተለየ ካርድ á‹­áˆáˆ¨áŒ¡á¢</translation>
<translation id="59174027418879706">áŠá‰…ቷáˆ</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="5966707198760109579">ሳáˆáŠ•á‰µ</translation>
<translation id="5967867314010545767">ከታሪክ አስወáŒá‹µ</translation>
<translation id="5975083100439434680">አሳንስ</translation>
@@ -472,8 +498,11 @@ nil</translation>
<translation id="614940544461990577">ይሞክሩá¦</translation>
<translation id="6151417162996330722">የአገáˆáŒ‹á‹­ እá‹á‰…ና ማረጋገጫዠበጣሠረጅሠየሆአየማረጋገጫ ጊዜ አለá‹á¢</translation>
<translation id="6165508094623778733">ተጨማሪ ለመረዳት</translation>
+<translation id="6177128806592000436">ወደዚህ ጣቢያ á‹«áˆá‹Žá‰µ áŒáŠ•áŠ™áŠá‰µ ደህንáŠá‰± አስተማማአአይደለáˆ</translation>
<translation id="6203231073485539293">የበይáŠáˆ˜áˆ¨á‰¥ áŒáŠ‘áŠáŠá‰µá‹ŽáŠ• ያረጋáŒáŒ¡</translation>
<translation id="6218753634732582820">ከChromium ላይ አድራሻ ይወገድ?</translation>
+<translation id="6251924700383757765">የáŒáˆ‹á‹ŠáŠá‰µ መመሪያ</translation>
+<translation id="625755898061068298">ለዚህ ጣቢያ የደህንáŠá‰µ ማስጠንቀቂያዎችን ለማሰናከሠመርጠዋáˆá¢</translation>
<translation id="6259156558325130047">&amp;ዳáŒáˆ ደርድርን ድገáˆ</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> እáˆá‰£á‰¶á‰½</translation>
<translation id="6264485186158353794">ወደ አስተማማአተመለስ</translation>
@@ -482,6 +511,7 @@ nil</translation>
<translation id="6305205051461490394"><ph name="URL" /> ሊደረስበት አይችáˆáˆá¢</translation>
<translation id="6321917430147971392">የዲኤንኤስ ቅንብሮችዎን á‹­áˆá‰µáˆ¹</translation>
<translation id="6328639280570009161">የአá‹á‰³áˆ¨ መረብ መገመትን አሰናክለዠይሞክሩ</translation>
+<translation id="6328786501058569169">ይህ ጣቢያ አታላይ áŠá‹</translation>
<translation id="6337534724793800597">መáˆáˆªá‹«á‹Žá‰½áŠ• በስሠአጣራ</translation>
<translation id="6342069812937806050">áˆáŠ­ አáˆáŠ•</translation>
<translation id="6345221851280129312">á‹«áˆá‰³á‹ˆá‰€ መጠን</translation>
@@ -490,6 +520,8 @@ nil</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="6404511346730675251">á‹•áˆá‰£á‰µ አርትዕ</translation>
+<translation id="6410264514553301377">የ<ph name="CREDIT_CARD" /> የአገáˆáŒáˆŽá‰µ ማብቂያ ቀን እና ሲቪሲ ያስገቡ</translation>
<translation id="6414888972213066896">ይህን ጣቢያ መጎብኘት ችáŒáˆ­ ካለዠወላጅዎን ጠይቀዋáˆ</translation>
<translation id="6416403317709441254">የድር ጣቢያዠChrome ሊያስተናáŒá‹°á‹ የማይችሠብትንትን ያሉ አሳማአáˆáˆµáŠ­áˆ­áŠ•á‰¶á‰½ ስለላከ አáˆáŠ• <ph name="SITE" />ን መጎብኘት አይችሉáˆá¢ የአá‹á‰³áˆ¨ መረብ ስህተቶች እና ጥቃቶች ብዙá‹áŠ• ጊዜ ጊዜያዊ ናቸዠስለዚህ ይህ ገጽ áˆáŠ“áˆá‰£á‰µ በኋላ ላይ ሊሠራ ይችላáˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="6417515091412812850">የእá‹á‰…ና ማረጋገጫዠተሽሮ እንደሆአማረጋገጥ አáˆá‰°á‰»áˆˆáˆá¢</translation>
@@ -499,10 +531,12 @@ nil</translation>
<translation id="6458467102616083041">áŠá‰£áˆªá‹ áለጋ በመáˆáˆªá‹« ስለተሰናከለ ችላ ተብáˆáˆá¢</translation>
<translation id="6462969404041126431">ይህ አገáˆáŒ‹á‹­ <ph name="DOMAIN" /> መሆኑን ማረጋገጥ አáˆá‰»áˆˆáˆá¤ የደህንáŠá‰µ á‹•á‹á‰…ና ማረጋገጫዠተሽሮ ሊሆን ይችላáˆá¢ ይህ በተሳሳተ á‹á‰…ረት ወይሠየእርስዎን áŒáŠ•áŠ™áŠá‰µ በሚጠáˆá አጥቂ áˆáŠ­áŠ•á‹«á‰µ የተáˆáŒ áˆ¨ ሊሆን ይችላáˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="647261751007945333">የመሣሪያ መáˆáˆªá‹«á‹Žá‰½</translation>
+<translation id="6477321094435799029">Chrome በዚህ ገጽ ላይ á‹«áˆá‰°áˆˆáˆ˜á‹° ኮድ አáŒáŠá‰·áˆá£ እና የእርስዎን የáŒáˆ መረጃ (ለáˆáˆ³áˆŒá¦ የይለá ቃላትᣠስáˆáŠ­ á‰áŒ¥áˆ®á‰½ እና ክሬዲት ካርዶች) ለመጠበቅ ሲባሠአáŒá‹¶á‰³áˆá¢</translation>
<translation id="6489534406876378309">ድáˆáˆµáˆ¶á‰½áŠ• መስቀሠጀáˆáˆ­</translation>
<translation id="6529602333819889595">&amp;ሰርá‹áŠ• ድገáˆ</translation>
<translation id="6534179046333460208">የአካላዊ ድር ጥቆማዎች</translation>
<translation id="6550675742724504774">አማራጮች</translation>
+<translation id="6556239504065605927">ደህንáŠá‰± የተጠበቀ áŒáŠ•áŠ™áŠá‰µ</translation>
<translation id="6563469144985748109">የእርስዎ አስተዳዳሪ ገና አላጸደá‰á‰µáˆ</translation>
<translation id="6593753688552673085">ከ<ph name="UPPER_ESTIMATE" /> በታች</translation>
<translation id="6596325263575161958">የáˆáˆµáŒ áˆ« አማራጮች</translation>
@@ -511,7 +545,6 @@ nil</translation>
<translation id="6644283850729428850">ይህ መመሪያ ተቋርጧáˆá¢</translation>
<translation id="6652240803263749613">ይህ አገáˆáŒ‹á‹­ <ph name="DOMAIN" /> መሆኑን ማረጋገጥ አáˆá‰»áˆˆáˆá¤ የደህንáŠá‰µ á‹•á‹á‰…ና ማረጋገጫዠበእርስዎ ኮáˆá’á‹á‰°áˆ­ ሥርዓተ ክá‹áŠ“ የታመአአይደለáˆá¢ ይህ በተሳሳተ á‹á‰…ረት ወይሠየእርስዎን áŒáŠ•áŠ™áŠá‰µ በሚጠáˆá አጥቂ áˆáŠ­áŠ•á‹«á‰µ የተáˆáŒ áˆ¨ ሊሆን ይችላáˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="6656103420185847513">አቃአያርትዑ</translation>
-<translation id="6660210980321319655"><ph name="CRASH_TIME" /> ላይ በራስ-ሰር ሪá“ርት ተደርጓáˆ</translation>
<translation id="6671697161687535275">የአስተያየት ጥቆማ ከChromium ይወገድ?</translation>
<translation id="6685834062052613830">ዘáŒá‰°á‹ á‹­á‹áŒ¡ እና ቅንብርን ያጠናቅá‰</translation>
<translation id="6710213216561001401">ቀዳሚ</translation>
@@ -523,6 +556,7 @@ nil</translation>
<translation id="6753269504797312559">የመáˆáˆªá‹« እሴት</translation>
<translation id="6757797048963528358">የእርስዎ መሣሪያ ተáŠá‰·áˆá¢</translation>
<translation id="6778737459546443941">የእርስዎ ወላጅ ገና አላጸደá‰á‰µáˆ</translation>
+<translation id="6810899417690483278">የብáŒáŠá‰µ መታወቂያ</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" /> ላይ ያለዠድረ-ገጽ በአáˆáŠ‘ ጊዜ አይገáŠáˆá¢ ከአቅሠበላይ ስራ በá‹á‰¶á‰ á‰µ ወይሠለጥገና ከስራ á‹áŒª ተደርጎ ሊሆን ይችላáˆá¢</translation>
<translation id="6831043979455480757">መተርጎáˆ</translation>
@@ -533,7 +567,7 @@ nil</translation>
<translation id="6897140037006041989">የተጠቀሚ ተወካይ</translation>
<translation id="6915804003454593391">ተጠቃሚá¦</translation>
<translation id="6957887021205513506">የአገáˆáŒ‹á‹© እá‹á‰…ና ማረጋገጫ የተጭበረበረ ይመስላáˆá¢</translation>
-<translation id="6965382102122355670">á‹­áˆáŠ•</translation>
+<translation id="6965382102122355670">እሺ</translation>
<translation id="6965978654500191972">መሣሪያ</translation>
<translation id="6970216967273061347">ወረዳ</translation>
<translation id="6973656660372572881">áˆáˆˆá‰±áˆ ቋሚ ተኪ አገáˆáŒ‹á‹®á‰½ እና የ.pac ስክሪá•á‰µ ዩአርኤሠተገáˆáŒ¸á‹‹áˆá¢</translation>
@@ -543,6 +577,8 @@ nil</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>
+<translation id="7064851114919012435">የእá‹á‰‚á‹« መረጃ</translation>
+<translation id="7079718277001814089">ይህ ጣቢያ ተንኮáˆ-አዘሠዌር á‹­á‹Ÿáˆ</translation>
<translation id="7087282848513945231">አá‹áˆ«áŒƒ</translation>
<translation id="7088615885725309056">የቆየ</translation>
<translation id="7090678807593890770"><ph name="LINK" />ን በGoogle ላይ á‹­áˆáˆáŒ‰</translation>
@@ -557,6 +593,7 @@ nil</translation>
<translation id="7210863904660874423"><ph name="HOST_NAME" /> የደህንáŠá‰µ መስáˆáˆ­á‰¶á‰½áŠ• አያከብርáˆá¢</translation>
<translation id="721197778055552897">ስለዚህ ችáŒáˆ­ <ph name="BEGIN_LINK" />ተጨማሪ ለመረዳት<ph name="END_LINK" /> á¢</translation>
<translation id="7219179957768738017">áŒáŠ•áŠ™áŠá‰± <ph name="SSL_VERSION" />ን ይጠቀማáˆá¢</translation>
+<translation id="724691107663265825">áŠá‰µ ያለዠጣቢያ ተንኮáˆ-አዘሠዌር አለá‹</translation>
<translation id="724975217298816891">የካርድ á‹áˆ­á‹áˆ®á‰½á‹ŽáŠ• ለማዘመን የ<ph name="CREDIT_CARD" /> ጊዜ ማለáŠá‹« ቀን እና ሲቪሲ ያስገቡᢠአንዴ ካረጋገጡ በኋላ የካርድ á‹áˆ­á‹áˆ®á‰½á‹Ž ለዚህ ጣቢያ ይጋራሉá¢</translation>
<translation id="725866823122871198">የእርስዎ የኮáˆá’ዩተር ቀን እና ሰዓት (<ph name="DATE_AND_TIME" />) áˆáŠ­ ስላáˆáˆ†áŠ‘ የáŒáˆ áŒáŠ•áŠ™áŠá‰µ ወደ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ሊመሰረት አይችáˆáˆá¢</translation>
<translation id="7269802741830436641">ድረ-ገጹ የአድራሻ ቅየራ áˆáˆáˆáˆµ አለá‹</translation>
@@ -612,6 +649,7 @@ nil</translation>
<translation id="7658239707568436148">ይቅር</translation>
<translation id="7667346355482952095">የተመለሰዠየመመሪያ ማስመሰያ ባዶ áŠá‹ ወይሠከአáˆáŠ‘ ማስመሰያ ጋር አይዛመድáˆ</translation>
<translation id="7668654391829183341">á‹«áˆá‰³á‹ˆá‰€ መሣሪያ</translation>
+<translation id="7669271284792375604">በዚህ ጣቢያ ላይ ያሉ አጥቂዎች እርስዎ የአሰሳ ተሞክሮዎን ሊጎዱ (ለáˆáˆ³áˆŒá¦ መáŠáˆ» ገጽዎን በመቀየር ወይሠበሚጎበኟቸዠጣቢያዎች ላይ ተጨማሪ ማስታወቂያዎችን በማሳየት) የሚችሉ á•áˆ®áŒáˆ«áˆžá‰½áŠ• እንዲጭኑ ለማታለሠሊሞክሩ ይችላሉá¢</translation>
<translation id="7674629440242451245">አዲስ የሆኑ አሪá የChrome ባህሪያትን á‹­áˆáˆáŒ‹áˆ‰? በ chrome.com/dev ላይ ያለá‹áŠ• የdev ሰርጣችንን ይሞክሩትá¢</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />ወደ <ph name="SITE" /> ቀጥሠ(ደህንáŠá‰± á‹«áˆá‰°áŒ á‰ á‰€)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">ይህ ጣቢያ ከመሸጎጫዠላይ ሊጫን አይችáˆáˆ</translation>
@@ -627,14 +665,17 @@ nil</translation>
<translation id="7800304661137206267"><ph name="MAC" /> ለመáˆá‹•áŠ­á‰µ ማረጋገጥ እና á‹°áŒáˆž <ph name="KX" /> ለá‰áˆá መቀያየሪያ ስáˆá‰¶á‰½ በማድረጠáŒáŠ•áŠ™áŠá‰± <ph name="CIPHER" />ን በመጠቀሠየተመሰጠረ áŠá‹á¢</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="7887683347370398519">የእርስዎን CVC á‹­áˆá‰µáˆ¹ እና እንደገና ይሞክሩ</translation>
<translation id="7912024687060120840">በዚህ አቃአá‹áˆµáŒ¥á¦</translation>
<translation id="7935318582918952113">የDOM ማጣሪያ</translation>
<translation id="7938958445268990899">የአገáˆáŒ‹á‹­ እá‹á‰…ና ማረጋገጫ ገና አáˆáŒ¸áŠ“áˆá¢</translation>
<translation id="7942349550061667556">ቀይ</translation>
+<translation id="7947285636476623132">የእርስዎን የአገáˆáŒáˆŽá‰µ ማብቂያ ዓመት ይመáˆáŠ¨á‰± እና እንደገና ይሞክሩ</translation>
<translation id="7951415247503192394">(32-ቢት)</translation>
<translation id="7956713633345437162">የተንቀሳቃሽ ስáˆáŠ­ á‹•áˆá‰£á‰¶á‰½</translation>
<translation id="7961015016161918242">በáጹáˆ</translation>
@@ -642,7 +683,10 @@ nil</translation>
<translation id="7983301409776629893">áˆáˆáŒŠá‹œ <ph name="ORIGINAL_LANGUAGE" />ን ወደ<ph name="TARGET_LANGUAGE" /> መተርጎáˆ</translation>
<translation id="7995512525968007366">አáˆá‰°áŒ á‰€áˆ°áˆ</translation>
<translation id="8012647001091218357">በዚህ ጊዜ ላይ ወላጆችህን መድረስ አáˆá‰»áˆáŠ•áˆá¢ እባክህ እንደገና ሞክርá¢</translation>
+<translation id="8025119109950072390">በዚህ ጣቢያ ላይ ያሉ አጥቂዎች እርስዎ እንደ ሶáትዌር መጫን ወይሠየáŒáˆ መረጃዎን (ለáˆáˆ³áˆŒá¦ የይለá ቃላትᣠስáˆáŠ­ á‰áŒ¥áˆ®á‰½ ወይሠክሬዲት ካርዶች) አሳáˆáˆá‹ እንዲሰጡ ያሉ አደገኛ áŠáŒˆáˆ­ እንዲያደርጉ ሊያታáˆáˆ‰á‹Žá‰µ ይችላሉá¢</translation>
+<translation id="803030522067524905">Google የጥንቃቄ አሰሳ በ<ph name="SITE" /> ላይ ማስገር እንዳለ ደርሶበታáˆá¢ አስጋሪ ጣቢያዎች እርስዎን ለማታለሠእንደ ሌሎች ድር ጣቢያዎች አስመስለዠይቀርባሉᢠ<ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="8034522405403831421">ይህ ገጽ በ<ph name="SOURCE_LANGUAGE" /> áŠá‹á¢ ወደ <ph name="TARGET_LANGUAGE" /> ይተርጎáˆ?</translation>
+<translation id="8041089156583427627">áŒá‰¥áˆ¨ መáˆáˆµ ላክ</translation>
<translation id="8088680233425245692">ጽሑá‰áŠ• ማየት አáˆá‰°á‰»áˆˆáˆá¢</translation>
<translation id="8089520772729574115">ከ1 ሜባ á‹«áŠáˆ°</translation>
<translation id="8091372947890762290">ማáŒá‰ áˆ­ በአገáˆáŒ‹á‹© ላይ በመጠባበቅ ላይ áŠá‹</translation>
@@ -653,9 +697,11 @@ nil</translation>
<translation id="8150722005171944719"><ph name="URL" /> ላይ ያለዠá‹á‹­áˆ የሚáŠá‰ á‰¥ አይደለáˆá¢ ተወáŒá‹¶á£ ተወስዶ ወይሠየá‹á‹­áˆ áቃዶቹ መዳረሻ እየከለከሉ ሊሆኑ ይችላሉá¢</translation>
<translation id="8194797478851900357">&amp;á‹áˆ°á‹µáŠ• ቀáˆá‰¥áˆµ</translation>
<translation id="8201077131113104583">የማይሰራ የURL á‹áˆ›áŠ” ለቅጥያ ከመታወቂያ «<ph name="EXTENSION_ID" />» ጋርá¢</translation>
+<translation id="8202097416529803614">የትዕዛዠማጠቃለያ</translation>
<translation id="8218327578424803826">የተመደበ መገኛ አካባቢá¦</translation>
<translation id="8225771182978767009">ይህን ኮáˆá’á‹á‰°áˆ­ ያቀናበረዠሰዠይህን ጣቢያ ለማገድ መርጧáˆá¢</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />ᣠ<ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">በአáˆáŠ‘ ጊዜ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ላይ ያሉ አጥቂዎች በእርስዎ ኮáˆá’á‹á‰°áˆ­ ላይ መረጃዎን የሚሰርበወይሠየሚሰርዙ (ለáˆáˆ³áˆŒá¦ áŽá‰¶á‹Žá‰½á£ የይለá ቃላትᣠመáˆá‹•áŠ­á‰¶á‰½ እና ክሬዲት ካርዶች) አደገኛ á•áˆ®áŒáˆ«áˆžá‰½áŠ• ለመጫን ሊሞክሩ ይችላሉá¢á‹¨á‰°á‰€áˆ˜áŒ¡</translation>
<translation id="8241707690549784388">እየáˆáˆˆáŒ‰á‰µ ያሉት ገጽ ያስገቡትን መረጃ ተጥቅሟáˆá¢ ወደዛ ገጽ መመለስ ወስደá‹á‰µ የáŠá‰ áˆ¨á‹áŠ• ርáˆáŒƒ እንዲደገሠሊያደርጠይችላáˆá¢ ለመቀጠሠይáˆáˆáŒ‹áˆ‰?</translation>
<translation id="8249320324621329438">ለመጨረሻ ጊዜ የመጣá‹á¦</translation>
<translation id="8261506727792406068">ሰርá‹</translation>
@@ -664,11 +710,13 @@ nil</translation>
<translation id="8294431847097064396">áˆáŠ•áŒ­</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="8349305172487531364">የዕáˆá‰£á‰¶á‰½ አሞሌ</translation>
<translation id="8363502534493474904">የአá‹áˆ®á•áˆ‹áŠ• áˆáŠá‰³áŠ• ማጥá‹á‰µ</translation>
<translation id="8364627913115013041">አáˆá‰°á‹‹á‰€áˆ¨áˆá¢</translation>
<translation id="8380941800586852976">አደገኛ</translation>
<translation id="8382348898565613901">በቅርቡ የጎበኟቸዠዕáˆá‰£á‰¶á‰½ እዚህ ይመጣሉ</translation>
+<translation id="8398259832188219207">የብáˆáˆ½á‰µ ሪá–ርት <ph name="UPLOAD_TIME" /> ላይ ተሰቅáˆáˆ</translation>
<translation id="8412145213513410671">ብáˆáˆ½á‰¶á‰½ (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">ተመሳሳዩ የይለá áˆáˆ¨áŒ‰áŠ• áˆáˆˆá‰µ ጊዜ ማስገባት አለብዎትá¢</translation>
<translation id="8428213095426709021">ቅንብሮች</translation>
@@ -680,9 +728,9 @@ nil</translation>
<translation id="8498891568109133222"><ph name="HOST_NAME" /> áˆáˆ‹áˆ½ ለመስጠት ከáˆáŠ­ በላይ ረዥሠጊዜ ወስዷáˆá¢</translation>
<translation id="852346902619691059">ይህ አገáˆáŒ‹á‹­ <ph name="DOMAIN" /> መሆኑን ማረጋገጥ አáˆá‰»áˆˆáˆá¤ የዕá‹á‰…ና ማረጋገጫዠበእርስዎ መሣሪያ ሥርዓተ ክወና የታመአአይደለáˆá¢ ይህ በተሳሳተ á‹á‰…ረት ወይሠየእርስዎን áŒáŠ•áŠ™áŠá‰µ በሚጠáˆá አጥቂ áˆáŠ­áŠ•á‹«á‰µ የተáˆáŒ áˆ¨ ሊሆን ይችላáˆá¢ <ph name="BEGIN_LEARN_MORE_LINK" />የበለጠ ለመረዳት<ph name="END_LEARN_MORE_LINK" />á¢</translation>
<translation id="8530504477309582336">ይህ የካርድ አይáŠá‰µ በGoogle ክáያዎች አይደገááˆá¢ እባክዎ የተለየ ካርድ á‹­áˆáˆ¨áŒ¡á¢</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="8553075262323480129">የገጹ ቋንቋ ሊታወቅ ስላáˆá‰»áˆˆ ትርጉሙ አáˆá‰°áˆ³áŠ«áˆá¢</translation>
<translation id="8559762987265718583">የእርስዎ መሣሪያ ቀን (<ph name="DATE_AND_TIME" />) áˆáŠ­ ስላáˆáˆ†áŠ ወደ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> የáŒáˆ áŒáŠ•áŠ™áŠá‰µ መመስረት አይቻáˆáˆá¢</translation>
-<translation id="856992080682148">የዚህ ጣቢያ የዕá‹á‰…ና ማረጋገጫ አገáˆáŒáˆŽá‰µ 2017 ላይ ያበቃáˆá£ እና የዕá‹á‰…ና ማረጋገጫ ሰንሰለቱ SHA-1 በመጠቀሠየተáˆáˆ¨áˆ˜ የዕá‹á‰…ና ማረጋገጫን ያካትታáˆá¢</translation>
<translation id="8571890674111243710">ገጽ ወደ <ph name="LANGUAGE" /> በመተርጎሠላይ...</translation>
<translation id="859285277496340001">የእá‹á‰…ና ማረጋገጫዠተሽሮ እንደሆአየሚታይበት áˆáŠ•áˆ ስáˆá‰µ አይገáˆáŒ½áˆá¢</translation>
<translation id="8620436878122366504">የእርስዎ ወላጆች ገና አላጸደá‰á‰µáˆ</translation>
@@ -695,10 +743,12 @@ nil</translation>
<translation id="8740359287975076522">የ<ph name="HOST_NAME" /> &lt;abbr id="dnsDefinition"&gt;የዲኤንኤስ አድራሻ&lt;/abbr&gt; ሊገአአáˆá‰°á‰»áˆˆáˆá¢ ችáŒáˆ©áŠ• በመመርመር ላይá¢</translation>
<translation id="8790007591277257123">&amp;ሰርá‹áŠ• ድገáˆ</translation>
<translation id="8798099450830957504">እንደወረደ</translation>
+<translation id="8800988563907321413">በአቅራቢያዎ ያሉ የአስተያየት ጥቆማዎች እዚህ ይመጣሉ</translation>
<translation id="8804164990146287819">የáŒáˆ‹á‹ŠáŠá‰µ መመሪያ</translation>
<translation id="8820817407110198400">á‹•áˆá‰£á‰¶á‰½</translation>
<translation id="8834246243508017242">እá‹á‰‚ያዎችን በመጠቀሠየራስ ሰር መሙላትን አንቃ…</translation>
<translation id="883848425547221593">ሌላ እáˆá‰£á‰¶á‰½</translation>
+<translation id="884264119367021077">የመላኪያ አድራሻ</translation>
<translation id="884923133447025588">áˆáŠ•áˆ የመሻሪያ ዘዴ አáˆá‰°áŒˆáŠ˜áˆá¢</translation>
<translation id="885730110891505394">ከGoogle ጋር ማጋራት</translation>
<translation id="8866481888320382733">የመáˆáˆªá‹« ቅንብሮችን መተንተን ላይ ስህተት</translation>
@@ -716,18 +766,21 @@ nil</translation>
<translation id="8971063699422889582">የአገáˆáŒ‹á‹­ እá‹á‰…ና ማረጋገጫ ጊዜዠአáˆáŽá‰ á‰³áˆá¢</translation>
<translation id="8987927404178983737">ወር</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">ቀጥሎ ያለዠጣቢያ ጎጂ á•áˆ®áŒáˆ«áˆ™á‰½ አሉት</translation>
<translation id="9001074447101275817">ተኪ <ph name="DOMAIN" /> የተጠቃሚ ስሠእና የይለá ቃሠያስáˆáˆáŒˆá‹‹áˆá¢</translation>
<translation id="901974403500617787">በመላዠስርዓት ላይ የሚተገበሩ ጥቆማዎች በባለቤቱ ብቻ áŠá‹ ሊዋቀሩ የሚችሉትᦠ<ph name="OWNER_EMAIL" />á¢</translation>
<translation id="9020542370529661692">ይህ ገጽ ወደ <ph name="TARGET_LANGUAGE" /> ተተርጉሟáˆ</translation>
+<translation id="9035022520814077154">የደህንáŠá‰µ ጥበቃ ስህተት</translation>
<translation id="9038649477754266430">ገጾችን ይበáˆáŒ¥ በáጥáŠá‰µ ለመጫን የáŒáˆ˜á‰³ አገáˆáŒáˆŽá‰µáŠ• ይጠቀሙ</translation>
<translation id="9039213469156557790">በተጨማሪᣠይህ ገጽ ደህንáŠá‰³á‰¸á‹ á‹«áˆá‰°áŒ á‰ á‰€ ሌሎች ንብረቶችን አካትቷáˆá¢ እáŠá‹šáˆ… ንብረቶች በሽáŒáŒáˆ­ ወቅት በሌሎች ሊታዩ ይችላሉᣠእናሠየገጹን ባህሪ ለመለወጥ በአጥቂዎች ሊቀየሩ ይችላሉá¢</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ላይ ያሉ አጥቂዎች የአሰሳ ተሞክሮዎን የሚጎዱ á•áˆ®áŒáˆ«áˆžá‰½á‹ŽáŠ• እንዲጭኑ እርስዎን ለማታለሠሊሞክሩ ይችላሉ (ለáˆáˆ³áˆŒá¦ የመáŠáˆ» ገጽዎን በመቀየር ወይሠበጎበኟቸዠጣቢያዎች ላይ ተጨማሪ ማስታወቂያዎችን በማሳየት)á¢</translation>
<translation id="9050666287014529139">የይለá áˆáˆ¨áŒ</translation>
<translation id="9065203028668620118">አርትዕ</translation>
<translation id="9068849894565669697">ቀለሠይáˆáˆ¨áŒ¡</translation>
<translation id="9076283476770535406">ለአዋቂ ብቻ የሚሆን ይዘት ሊኖረዠይችላáˆ</translation>
-<translation id="9092364396508701805">የ<ph name="HOST_NAME" /> ገጽ እየሠራ አይደለáˆ</translation>
<translation id="9103872766612412690"><ph name="SITE" /> የእርስዎን መረጃ ለመጠበቅ በመደበáŠáŠá‰µ áˆáˆµáŒ áˆ«áŠ• ይጠቀማáˆá¢ Chromium አáˆáŠ• ከ<ph name="SITE" /> ጋር ለመገናኘት ሲሞክር ድር ጣቢያዠያáˆá‰°áˆˆáˆ˜á‹± እና ትክክሠያáˆáˆ†áŠ‘ áˆáˆµáŠ­áˆ­áŠá‰¶á‰½áŠ• መáˆáˆ·áˆá¢ ይህ አንድ አጥቂ <ph name="SITE" />ን አስመስሎ ለመቅረብ ሲሞክር áŠá‹ ወይሠአንድ የWi-Fi መáŒá‰¢á‹« ገጽ áŒáŠ•áŠ™áŠá‰±áŠ• ሲቋረጥ ሊከሰት ይችላáˆá¢ Chromium ማንኛá‹áˆ የá‹áˆ‚ብ áˆá‹á‹áŒ¥ ከመካሄዱ በáŠá‰µ áŒáŠ•áŠ™áŠá‰±áŠ• ስላቋረጠዠአáˆáŠ•áˆ የእርስዎ መረጃ ደህንáŠá‰µ የተጠበቀ áŠá‹á¢</translation>
<translation id="9137013805542155359">የመጀመሪያá‹áŠ• አሳይ</translation>
+<translation id="9137248913990643158">ይህን መተáŒá‰ áˆªá‹« ከመጠቀáˆá‹Ž በáŠá‰µ እባክዎ ይጀáˆáˆ©áŠ“ ወደ Chrome á‹­áŒá‰¡á¢</translation>
<translation id="9148507642005240123">&amp;አርትዕን ቀáˆá‰¥áˆµ</translation>
<translation id="9157595877708044936">በማዋቀር ላይ…</translation>
<translation id="9170848237812810038">&amp;ቀáˆá‰¥áˆµ</translation>
@@ -740,7 +793,6 @@ nil</translation>
<translation id="935608979562296692">ቅጽን አጽዳ</translation>
<translation id="939736085109172342">አዲስ ዓቃáŠ</translation>
<translation id="941721044073577244">ይህን ጣቢያ የመጎብኘት áˆá‰ƒá‹µ የሌለዎት ይመስላሉ</translation>
-<translation id="962701380617707048">የካርድ á‹áˆ­á‹áˆ®á‰½á‹ŽáŠ• ለማዘመን የ<ph name="CREDIT_CARD" /> ጊዜ ማለáŠá‹« ቀን እና ሲቪሲ ያስገቡá¢</translation>
<translation id="969892804517981540">á‹­á‹ áŒáŠ•á‰£á‰³</translation>
<translation id="988159990683914416">የገንቢዎች áŒáŠ•á‰£á‰³</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 bb6fdbf51d8..703efc8e896 100644
--- a/chromium/components/strings/components_strings_ar.xtb
+++ b/chromium/components/strings/components_strings_ar.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">â€Ø¥Ø¹Ø§Ø¯Ø© الاتصال بـ Wi-Fi</translation>
<translation id="1175364870820465910">ط&amp;باعة...</translation>
<translation id="1181037720776840403">إزالة</translation>
+<translation id="1184214524891303587">â€<ph name="BEGIN_WHITEPAPER_LINK" />إبلاغ Google تلقائيًا<ph name="END_WHITEPAPER_LINK" /> بتÙاصيل أي مخاطر أمنية محتملة. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">التالي</translation>
<translation id="1201895884277373915">المزيد من هذا الموقع</translation>
<translation id="1206967143813997005">توقيع أوَّلي سيئ</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">سماوي</translation>
<translation id="1629803312968146339">â€Ù‡Ù„ تريد من Chrome Ø­Ùظ هذه البطاقة؟</translation>
<translation id="1640180200866533862">سياسات المستخدم</translation>
+<translation id="1640244768702815859">جرّب <ph name="BEGIN_LINK" />الانتقال إلى الصÙحة الرئيسية للموقع<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">تهيئة الشبكة غير صالحة ويتعذر استيرادها.</translation>
<translation id="1644574205037202324">السجل</translation>
<translation id="1645368109819982629">بروتوكول غير معتمد</translation>
<translation id="1676269943528358898">â€ÙŠØ³ØªØ®Ø¯Ù… <ph name="SITE" /> التشÙير عادة لحماية معلوماتك. عندما حاول Google Chrome الاتصال بموقع <ph name="SITE" /> هذه المرة، أرجَع موقع الويب بيانات اعتماد غير عادية وغير صحيحة. وقد يحدث هذا عندما يحاول أحد المهاجمين التظاهر بأنه موقع <ph name="SITE" />ØŒ أو إذا قاطعت شاشة تسجيل دخول Wi-Fi الاتصال. ولكن لا تزال معلوماتك آمنة نظرًا لأن Google Chrome أوقَ٠الاتصال قبل تبادل أي بيانات.</translation>
+<translation id="168328519870909584">قد يحاول المهاجمون الموجودون حاليًا على <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> تثبيت تطبيقات خطيرة على جهازك والتي تسرق معلوماتك أو تحذÙها (على سبيل المثال، الصور وكلمات المرور والرسائل وبطاقات الائتمان).</translation>
<translation id="168841957122794586">تحتوي شهادة الخادم على Ù…Ùتاح تشÙير ضعيÙ.</translation>
-<translation id="1701955595840307032">الحصول على المحتوى المقترح</translation>
<translation id="1710259589646384581">نظام التشغيل</translation>
<translation id="1721312023322545264">أنت بحاجة لإذن من <ph name="NAME" /> لزيارة هذا الموقع</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">رقم الصÙحة</translation>
<translation id="1768211456781949159">â€<ph name="BEGIN_LINK" />تجربة تشغيل بيانات التشخيص لشبكة Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">الرجاء تحديث عبارة مرور المزامنة.</translation>
+<translation id="1787142507584202372">تظهر علامات التبويب المÙتوحة هنا</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">â€Ø§ÙƒØªØ´Ù التصÙØ­ الآمن من Google‬ مؤخرًا <ph name="BEGIN_LINK" />برامج ضارة<ph name="END_LINK" /> على <ph name="SITE" />. أحيانًا تÙصاب مواقع الويب الآمنة ÙÙŠ الوضع العادي ببرامج ضارة. ويÙعد مصدر محتوى البرامج الضارة هو <ph name="SUBRESOURCE_HOST" />ØŒ وهو موزع معرو٠للبرامج الضارة. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">طلب غير صالح، أو معلمات طلب غير صالحة</translation>
+<translation id="1834321415901700177">يحتوي هذا الموقع على برامج ضارة</translation>
<translation id="1838667051080421715">أنت تعرض مصدر صÙحة ويب.</translation>
<translation id="1871208020102129563">â€ØªÙ… تعيين الخادم الوكيل لاستخدام الخوادم الوكيلة الثابتة وليس عنوان URL لنص برمجي pac.</translation>
<translation id="1883255238294161206">تصغير القائمة</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">الإشارات المرجعية على الجوال</translation>
<translation id="2148716181193084225">اليوم</translation>
-<translation id="2149973817440762519">تعديل الإشارة</translation>
<translation id="2154054054215849342">المزامنة غير متاحة لنطاقك</translation>
<translation id="2166049586286450108">الوصول الكامل للمشرÙ</translation>
<translation id="2166378884831602661">لا يمكن لموقع الويب هذا توÙير اتصال آمن</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">â€Ù„ا يمكنك زيارة <ph name="SITE" /> الآن نظرًا لأن موقع الويب يستخدم HSTS. أخطاء الشبكة والهجمات عليها عادةً ما تكون مؤقتة، لذا ستعمل هذه الصÙحة لاحقًا على الأرجح. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">تم تجاهل الإشارة المرجعية غير الصالحة ÙÙŠ الÙهرس <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">الإشارات الأخرى</translation>
+<translation id="2355395290879513365">قد يتمكن المهاجمون من رؤية الصور التي تتطلع عليها على هذا الموقع وخداعك من خلال تعديلها.</translation>
<translation id="2359808026110333948">المتابعة</translation>
<translation id="2365563543831475020">لم يتم تحميل تقرير الأعطال الذي تم الحصول عليه ÙÙŠ <ph name="CRASH_TIME" /></translation>
<translation id="2367567093518048410">المستوى</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">عرض السياسات التي لم يتم تعيين قيم لها</translation>
<translation id="2396249848217231973">تراجع عن الحذ&amp;Ù</translation>
<translation id="2455981314101692989">عطلت صÙحة الويب هذه الملء التلقائي لهذا النموذج.</translation>
+<translation id="2460160116472764928">â€Ø§ÙƒØªØ´Ù التصÙØ­ الآمن من Google‬ مؤخرًا <ph name="BEGIN_LINK" />برامج ضارة<ph name="END_LINK" /> على <ph name="SITE" />. أحيانًا تÙصاب مواقع الويب الآمنة ÙÙŠ الوضع العادي ببرامج ضارة. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">ملء</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />تشغيل بيانات تشخيص الشبكة<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">â€Ø¹Ù†ÙˆØ§Ù† URL للبحث غير صالح.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">هل تريد Ùعلًا حذ٠هذه الصÙحات من السجل؟</translation>
<translation id="2677748264148917807">الخروج</translation>
<translation id="269990154133806163">قدم الخادم شهادة لم يتم الكش٠عنها علنًا باستخدام سياسة شهادة الشÙاÙية. وهذا ضروري لبعض الشهادات، لضمان أنها جديرة بالثقة ومحمية من المهاجمين. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">قائمة القراءة</translation>
<translation id="2704283930420550640">القيمة لا تطابق التنسيق.</translation>
<translation id="2704951214193499422">â€Ù„Ù… يتمكن Chromium من التأكد من بطاقتك ÙÙŠ الوقت الحالي. ÙŠÙرجى إعادة المحاولة ÙÙŠ وقت لاحق.</translation>
<translation id="2705137772291741111">تعذر قراءة النسخة المحÙوظة (المخزنة ÙÙŠ ذاكرة التخزين المؤقت) لموقع الويب هذا.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">لقد حاولت الوصول إلى <ph name="DOMAIN" />ØŒ ولكن جهة إصدار الشهادة التي قدمها الخادم قد أبطلت الشهادة. وهذا يعني أن بيانات اعتماد الأمان التي قدمها الخادم يجب عدم الوثوق بها مطلقًا. Ùقد تكون على اتصال بأحد المهاجمين. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">طلب إذن</translation>
<translation id="2713444072780614174">أبيض</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">يمكنك تعطيل أي خوادم وكيلة تمت تهيئتها لاتصال من صÙحة الإعدادات.</translation>
<translation id="2955913368246107853">إغلاق شريط البحث</translation>
<translation id="2958431318199492670">â€Ù„ا تتواÙÙ‚ تهيئة الشبكة مع معيار ONC. قد لا يتم استيراد بعض أجزاء التهيئة.</translation>
+<translation id="29611076221683977">â€Ù‚د يحاول المستهدÙون الموجودون حاليًا على <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> تثبيت برامج خطيرة على Mac التابع لك تسرق معلوماتك أو تحذÙها (على سبيل المثال، الصور وكلمات المرور والرسائل وبطاقات الائتمان).</translation>
<translation id="2969319727213777354">â€Ù„إنشاء اتصال آمن، Ùإنك بحاجة إلى ضبط ساعتك بشكل صحيح. وذلك لأن الشهادات التي تستخدمها مواقع الويب لتعري٠نÙسها تكون صالحة Ùقط Ù„Ùترات محددة من الوقت. Ùإذا كانت ساعة جهازك غير صحيحة، Ùلن يتمكن Google Chrome من التحقق من هذه الشهادات.</translation>
<translation id="2972581237482394796">إعا&amp;دة</translation>
<translation id="2985306909656435243">â€Ø¹Ù†Ø¯ التمكين، سيÙخزن Chromium نسخة من بطاقتك على هذا الجهاز لتعبئة النماذج بشكل أسرع.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">إخÙاء التÙاصيل</translation>
<translation id="3587482841069643663">الكل</translation>
<translation id="3600246354004376029"><ph name="TITLE" />، و<ph name="DOMAIN" />، و<ph name="TIME" /></translation>
-<translation id="3609138628363401169">â€Ù„ا يوÙر الخادم إضاÙØ© إعادة التÙاوض بروتوكول أمان طبقة النقل (TLS).</translation>
<translation id="36224234498066874">مسح بيانات التصÙØ­...</translation>
<translation id="362276910939193118">عرض السجل بكامله</translation>
<translation id="3623476034248543066">عرض القيمة</translation>
@@ -271,19 +279,20 @@
<translation id="3655670868607891010">إذا كنت تشاهد هذا بكثرة، Ùجرّب <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">التعديل</translation>
<translation id="3678029195006412963">تعذر توقيع الطلب</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="3696411085566228381">بدون</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">تحميل...</translation>
+<translation id="370665806235115550">جار٠التحميل...</translation>
<translation id="3712624925041724820">التراخيص مستنÙذة</translation>
<translation id="3714780639079136834">â€ØªØ´ØºÙŠÙ„ بيانات شبكة الجوّال أو Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />التحقق من تهيئة الخادم الوكيل والجدار الناري ونظام أسماء النطاقات<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">إذا كنت على دراية بالمخاطر على أمنك، يمكنك <ph name="BEGIN_LINK" />زيارة هذا الموقع غير الآمن<ph name="END_LINK" /> قبل أن تتم إزالة البرامج الخطيرة.</translation>
<translation id="3739623965217189342">الرابط الذي نسخته</translation>
<translation id="375403751935624634">أخÙقت الترجمة بسبب حدوث خطأ ÙÙŠ الخادم.</translation>
<translation id="3759461132968374835">ليس لديك أي أعطال تم الإبلاغ عنها مؤخرًا. الأعطال التي حدثت عندما تم تعطيل الإبلاغ عن الأعطال لن تظهر هنا.</translation>
-<translation id="3788090790273268753">â€ØªÙ†ØªÙ‡ÙŠ صلاحية شهادة هذا الموقع ÙÙŠ 2016ØŒ وتتضمن سلسلة الشهادات شهادة موقعة باستخدام SHA-1.</translation>
<translation id="382518646247711829">إذا كنت تستخدم خادمًا وكيلاً...</translation>
<translation id="3828924085048779000">غير مسموح باستخدام عبارة مرور Ùارغة.</translation>
<translation id="3845539888601087042">عرض السجلّ من الأجهزة التي تم تسجيل الدخول عليها. <ph name="BEGIN_LINK" />مزيد من المعلومات<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">المÙتاح "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">لا يدعم كل من العميل والخادم مجموعة تشÙير أو إصدار بروتوكول طبقة المقابس الآمنة الشائع.</translation>
<translation id="4079302484614802869">â€ØªÙ… تعيين تهيئة الخادم الوكيل لاستخدام عنوان URL نص برمجي ‎.pac وليس الخوادم الوكيلة الثابتة.</translation>
+<translation id="4098354747657067197">موقع مخادع ÙÙŠ انتظارك</translation>
<translation id="4103249731201008433">الرقم التسلسلي للجهاز غير صالح</translation>
<translation id="4103763322291513355">â€Ø§Ù†ØªÙ‚Ù„ إلى &lt;strong&gt;chrome://policy&lt;/strong&gt; لمشاهدة قائمة بعناوين URL المضاÙØ© إلى القائمة السوداء والسياسات الأخرى التي Ùرضها مشر٠النظام.</translation>
<translation id="4110615724604346410">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />ØŒ بل إن شهادة أمانه تحتوي على أخطاء. وربما يكون سبب ذلك خطأً ÙÙŠ التهيئة أو مهاجمًا يعترض اتصالك. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{لا شيء}=1{تطبيق واحد ($1)}=2{تطبيقان (2) ($1، $2)}few{# تطبيقات ($1، $2، $3)}many{# تطبيقًا ($1، $2، $3)}other{# تطبيق ($1، $2، $3)}}</translation>
<translation id="4220128509585149162">الأعطال</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />تجربة تشغيل بيانات تشخيص الشبكة<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">إن اتصالك بهذا الموقع غير آمن تمامًا</translation>
<translation id="4250680216510889253">لا</translation>
<translation id="425582637250725228">قد لا يتم Ø­Ùظ التغييرات التي أجريتها.</translation>
<translation id="4258748452823770588">توقيع غير صالح</translation>
<translation id="4269787794583293679">(اسم المستخدم غير موجود)</translation>
+<translation id="4280429058323657511">، تاريخ انتهاء الصلاحية <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">â€Ø§ÙƒØªØ´Ù التصÙØ­ الآمن من Google‬ مؤخرًا <ph name="BEGIN_LINK" />برامج ضارة<ph name="END_LINK" /> على <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">اقتراحات الآباء</translation>
<translation id="4304224509867189079">تسجيل الدخول</translation>
<translation id="432290197980158659">قدم الخادم شهادة لا تتطابق مع التوقعات المضمّنة. تم تضمين هذه التوقعات للحصول على مواقع ويب موثوقة وآمنة جدًا لتوÙير الحماية لك. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">أخÙÙ‚ العثور على المقالة</translation>
+<translation id="4326324639298822553">تحقق من تاريخ انتهاء الصلاحية وأعÙد المحاولة مرة أخرى</translation>
<translation id="4331708818696583467">غير آمن</translation>
+<translation id="4356973930735388585">قد يحاول المهاجمون الموجودون على هذا الموقع تثبيت برامج خطيرة على الكمبيوتر التابع لك تسرق معلوماتك أو تحذÙها (على سبيل المثال، الصور وكلمات المرور والرسائل وبطاقات الائتمان).</translation>
<translation id="4372948949327679948">القيمة <ph name="VALUE_TYPE" /> المتوقعة.</translation>
<translation id="4381091992796011497">اسم المستخدم:</translation>
<translation id="4394049700291259645">تعطيل</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">لقد حاولت الوصول إلى <ph name="DOMAIN" />، ولكن الخادم قدم شهادة غير صالحة. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">يتم ترميز اتصالك بالنطاق <ph name="DOMAIN" /> باستخدام مجموعة تشÙير حديثة.</translation>
<translation id="4594403342090139922">تراجع عن الحذ&amp;Ù</translation>
+<translation id="4619615317237390068">علامات التبويب من الأجهزة الأخرى</translation>
<translation id="4668929960204016307">،</translation>
<translation id="4670097147947922288">أنت تعرض حاليًا صÙحة إضاÙØ©.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" />†<ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">إعادة تحميل السياسات</translation>
<translation id="4728558894243024398">النظام الأساسي</translation>
<translation id="4744603770635761495">المسار التنÙيذي</translation>
+<translation id="4750917950439032686">إن معلوماتك (على سبيل المثال، كلمات المرور أو أرقام بطاقة الائتمان) تكون خاصة عندما يتم إرسالها إلى هذا الموقع.</translation>
<translation id="4756388243121344051">ال&amp;سجل</translation>
+<translation id="4759118997339041434">تم تعطيل الملء التلقائي لعملية الدÙع</translation>
<translation id="4764776831041365478">قد تكون صÙحة الويب على العنوان <ph name="URL" /> غير متاحة مؤقتًا أو قد يكون تم نقلها نهائيًا إلى عنوان ويب جديد.</translation>
<translation id="4771973620359291008">حدث خطأ غير محدّد.</translation>
<translation id="4800132727771399293">â€ØªØ­Ù‚Ù‚ من تاريخ انتهاء الصلاحية ورمز التحقق من البطاقة (CVC) وأعد المحاولة مرة أخرى.</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">حدث خطأ ÙÙŠ الشبكة</translation>
<translation id="4816492930507672669">احتواء ضمن الصÙحة</translation>
<translation id="4850886885716139402">عرض</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />، <ph name="TYPE_2" />، <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{وصÙحة ويب واحدة إضاÙية}zero{Ùˆ# صÙحة ويب إضاÙية}two{وصÙحتا ويب (#) إضاÙيتان}few{Ùˆ# صÙحات ويب إضاÙية}many{Ùˆ# صÙحة ويب إضاÙية}other{Ùˆ# صÙحة ويب إضاÙية}}</translation>
<translation id="4923417429809017348">تمت ترجمة هذه الصÙحة من لغة غير معروÙØ© إلى اللغة <ph name="LANGUAGE_LANGUAGE" /></translation>
+<translation id="4923459931733593730">الدÙع</translation>
<translation id="4926049483395192435">يجب تحديدها.</translation>
<translation id="495170559598752135">إجراءات</translation>
<translation id="4958444002117714549">توسيع القائمة</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">تنزيل</translation>
<translation id="5190835502935405962">شريط الإشارات</translation>
<translation id="5199729219167945352">التجارب</translation>
-<translation id="5199841536747119669">تظهر اقتراحاتك هنا</translation>
<translation id="5251803541071282808">السحاب</translation>
<translation id="5277279256032773186">â€Ù‡Ù„ تستخدم Chrome ÙÙŠ العمل؟ يمكن للأنشطة التجارية إدارة إعدادات Chrome لموظÙيها. تعرَّ٠على المزيد</translation>
<translation id="5299298092464848405">خطأ ÙÙŠ تحليل السياسة</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">ميزة الإبلاغ عن الأعطال معطلة.</translation>
<translation id="5317780077021120954">Ø­Ùظ</translation>
<translation id="5327248766486351172">الاسم</translation>
+<translation id="5337705430875057403">قد يخدعك المخترقون الموجودون على <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> بÙعل شيء خطير كتثبيت البرامج أو الكش٠عن معلوماتك الشخصية (على سبيل المثال، كلمات المرور، أرقام الهواتÙØŒ أو بطاقات الائتمان).</translation>
<translation id="5359637492792381994">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />ØŒ بل إن شهادة أمانه غير صالحة حاليًا. وربما يكون السبب ÙÙŠ ذلك خطأً ÙÙŠ التهيئة أو مهاجمًا يعترض اتصالك. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">أخÙÙ‚ تخزين إعدادات السياسة</translation>
+<translation id="5386426401304769735">â€ØªØªØ¶Ù…Ù† سلسلة الشهادات لهذا الموقع شهادة موقعة باستخدام SHA-1.</translation>
<translation id="5421136146218899937">محو بيانات التصÙØ­...</translation>
<translation id="5430298929874300616">إزالة إشارة مرجعية</translation>
<translation id="5431657950005405462">لم يتم العثور على ملÙÙƒ</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">صÙحة Ù…Ùضمنة ÙÙŠ <ph name="SITE" /> تعرض:</translation>
<translation id="5556459405103347317">إعادة تحميل</translation>
<translation id="5565735124758917034">نشط</translation>
+<translation id="5572851009514199876">â€ÙŠÙرجى البدء وتسجيل الدخول إلى Chrome لكي يتأكد Chrome مما إذا كان مسموحًا لك الوصول إلى موقع الويب هذا أم لا.</translation>
+<translation id="5580958916614886209">تحقق من شهر انتهاء الصلاحية وأعÙد المحاولة مرة أخرى</translation>
<translation id="560412284261940334">الإدارة غير متوÙرة</translation>
<translation id="5610142619324316209">التحقق من الاتصال</translation>
<translation id="5617949217645503996">أعاد <ph name="HOST_NAME" /> توجيهك مرات كثيرة جدًا.</translation>
<translation id="5622887735448669177">هل تريد الخروج من هذا الموقع؟</translation>
<translation id="5629630648637658800">أخÙÙ‚ تحميل إعدادات السياسة</translation>
<translation id="5631439013527180824">الرمز المميز لإدارة الجهاز غير صالح</translation>
+<translation id="5669703222995421982">الحصول على محتوى مخصص</translation>
+<translation id="5675650730144413517">يتعذّر على هذه الصÙحة العمل</translation>
<translation id="5677928146339483299">تم المنع</translation>
<translation id="5694783966845939798">â€Ù„قد حاولت الوصول إلى <ph name="DOMAIN" />ØŒ ولكن قدَّم الخادم شهادة موقّعة باستخدام خوارزمية توقيع ضعيÙØ© (مثل SHA-1). مما يعني أن بيانات اعتماد الأمان التي قدمها الخادم من المحتمل أنه تم تزييÙها، وأن الخادم قد لا يكون هو الخادم الذي تتوقعه (قد تكون على اتصال بأحد المهاجمين). <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">لم يتمّ التحقق من هوية هذا الموقع.</translation>
<translation id="5720705177508910913">المستخدم الحالي</translation>
-<translation id="572328651809341494">علامات التبويب الأخيرة</translation>
<translation id="5732392974455271431">يمكن لوالديك إلغاء الحظر لك</translation>
<translation id="5784606427469807560">حدثت مشكلة أثناء التأكد من بطاقتك. تحقق من اتصالك بالإنترنت وأعد المحاولة.</translation>
<translation id="5785756445106461925">إضاÙØ© إلى ذلك، تتضمن هذه الصÙحة موارد أخرى غير آمنة. ويستطيع الآخرون مشاهدة هذه الموارد أثناء نقلها، كما يستطيع أي مهاجم تعديلها لتغيير مظهر الصÙحة.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">لقد حاولت الوصول إلى <ph name="DOMAIN" />ØŒ ولكن الخادم قدّم شهادة تحتوي على Ù…Ùتاح ضعيÙ. ربما اخترق أحد المهاجمين المÙتاح الخاص، وقد لا يكون الخادم هو الخادم الذي تتوقعه (قد تكون على اتصال بأحد المهاجمين). <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">مجلد جديد</translation>
<translation id="5869405914158311789">لا يمكن الوصول إلى موقع الويب هذا</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">â€Ù‡Ø°Ø§ النوع من البطاقات غير متواÙÙ‚ مع Google Payments لهذا التاجر. ÙŠÙرجى تحديد بطاقة أخرى.</translation>
<translation id="59174027418879706">تم التمكين</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="5966707198760109579">الأسبوع</translation>
<translation id="5967867314010545767">إزالة من السجل</translation>
<translation id="5975083100439434680">تصغير</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">جرّب:</translation>
<translation id="6151417162996330722">Ùترة صلاحية شهادة الخادم طويلة جدًا.</translation>
<translation id="6165508094623778733">مزيد من المعلومات</translation>
+<translation id="6177128806592000436">إن اتصالك بهذا الموقع غير آمن</translation>
<translation id="6203231073485539293">التحقق من اتصالك بالإنترنت</translation>
<translation id="6218753634732582820">â€Ù‡Ù„ تريد إزالة العنوان من ChromiumØŸ</translation>
+<translation id="6251924700383757765">سياسة الخصوصية</translation>
+<translation id="625755898061068298">لقد اخترت تعطيل تحذيرات الأمان لهذا الموقع.</translation>
<translation id="6259156558325130047">إعادة إ&amp;جراء الترتيب</translation>
<translation id="6263376278284652872">إشعارات <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">الرجوع إلى وضع الأمان</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">يتعذر الوصول إلى <ph name="URL" />.</translation>
<translation id="6321917430147971392">التحقق من إعدادات نظام أسماء النطاقات</translation>
<translation id="6328639280570009161">تجربة تعطيل التنبؤ بإجراءات الشبكة</translation>
+<translation id="6328786501058569169">هذا الموقع مخادع</translation>
<translation id="6337534724793800597">تصÙية السياسات بحسب الاسم</translation>
<translation id="6342069812937806050">الآن</translation>
<translation id="6345221851280129312">حجم غير معروÙ</translation>
@@ -489,6 +519,8 @@
<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="6404511346730675251">تعديل إشارة مرجعية</translation>
+<translation id="6410264514553301377">â€Ø£Ø¯Ø®ÙÙ„ تاريخ انتهاء الصلاحية ورمز التحقق من البطاقة (CVC) لـ <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">لقد سألت والديك ما إذا كانت زيارة هذا الموقع مناسبةً لك</translation>
<translation id="6416403317709441254">â€Ù„ا يمكنك زيارة <ph name="SITE" /> الآن نظرًا لأن موقع الويب أرسل بيانات اعتماد مختلطة يتعذر على Chromium معالجتها. أخطاء الشبكة والهجمات عليها عادةً ما تكون مؤقتة، لذا ستعمل هذه الصÙحة لاحقًا على الأرجح. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">تعذر التحقق مما إذا كانت الشهادة قد تم إبطالها.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">تم التجاهل نظرًا لتعطيل البحث الاÙتراضي بواسطة السياسة.</translation>
<translation id="6462969404041126431">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />ØŒ قد يكون تم إبطال شهادة أمانه. وربما يكون سبب ذلك خطأً ÙÙŠ التهيئة أو مهاجمًا يعترض اتصالك. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">سياسات الأجهزة</translation>
+<translation id="6477321094435799029">â€Ø§ÙƒØªØ´Ù Chrome وجود رمز غير عادي على هذه الصÙحة وأجرى حظرًا لهذا الرمز لحماية معلوماتك الشخصية (على سبيل المثال، كلمات المرور، وأرقام الهواتÙØŒ وبطاقات الائتمان).</translation>
<translation id="6489534406876378309">بدء تحميل الأعطال</translation>
<translation id="6529602333819889595">إعادة الح&amp;Ø°Ù</translation>
<translation id="6534179046333460208">اقتراحات الشبكة المادية</translation>
<translation id="6550675742724504774">خيارات</translation>
+<translation id="6556239504065605927">اتصال آمن</translation>
<translation id="6563469144985748109">لم يواÙÙ‚ عليه مديرك حتى الآن</translation>
<translation id="6593753688552673085">أقل من <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">خيارات التشÙير</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">تم تجاهل هذه السياسة.</translation>
<translation id="6652240803263749613">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />ØŒ بل إن شهادة أمانه غير موثوقة من Ù‚Ùبل نظام تشغيل الكمبيوتر. وربما يكون السبب ÙÙŠ ذلك خطأً ÙÙŠ التهيئة أو مهاجمًا يعترض اتصالك. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">تعديل مجلد</translation>
-<translation id="6660210980321319655">تم الإبلاغ عنه تلقائيًا <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">â€Ù‡Ù„ تريد إزالة اقتراح النموذج من ChromiumØŸ</translation>
<translation id="6685834062052613830">الخروج وإكمال الإعداد</translation>
<translation id="6710213216561001401">السابق</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">قيمة السياسة</translation>
<translation id="6757797048963528358">خضع جهازك إلى وضع السكون.</translation>
<translation id="6778737459546443941">لم يواÙÙ‚ عليه والداك حتى الآن</translation>
+<translation id="6810899417690483278">رقم تعري٠التخصيص</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">صÙحة الويب على <ph name="URL" /> غير متاحة حاليًا.. ربما تكون هناك زيادة ÙÙŠ تحميلها أو أنها معطلة لأسباب تتعلق بالصيانة.</translation>
<translation id="6831043979455480757">ترجمة</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">معلومات الاتصال</translation>
+<translation id="7079718277001814089">يحتوي هذا الموقع على برامج ضارة</translation>
<translation id="7087282848513945231">المقاطعة/الإقليم/المنطقة</translation>
<translation id="7088615885725309056">أقدم</translation>
<translation id="7090678807593890770">â€Ø§Ù„بحث ÙÙŠ Google عن <ph name="LINK" /></translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423">لا يلتزم <ph name="HOST_NAME" /> بمعايير الأمان.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />مزيد من المعلومات<ph name="END_LINK" /> حول هذه المشكلة.</translation>
<translation id="7219179957768738017">الاتصال يستخدم <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">يحتوي موقع الويب المقصود على برامج ضارة</translation>
<translation id="724975217298816891">â€Ø£Ø¯Ø®Ù„ تاريخ انتهاء الصلاحية ورمز التحقق من البطاقة (CVC) لـ <ph name="CREDIT_CARD" /> لتحديث تÙاصيل بطاقتك. بعد تأكيدك، ستتم مشاركة تÙاصيل بطاقتك مع هذا الموقع.</translation>
<translation id="725866823122871198">تعذر إنشاء اتصال خاص بـ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> نظرًا لأن التاريخ والوقت لجهاز الكمبيوتر (<ph name="DATE_AND_TIME" />) غير صحيحين.</translation>
<translation id="7269802741830436641">تحتوي صÙحة الويب هذه على حلقة إعادة توجيه</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">إلغاء</translation>
<translation id="7667346355482952095">الرمز المميز المعروض للسياسة خاليًا أو لا يتطابق مع الرمز المميز الحالي</translation>
<translation id="7668654391829183341">جهاز غير معروÙ</translation>
+<translation id="7669271284792375604">قد يحاول المهاجمون ÙÙŠ هذا الموقع خداعك من خلال تثبيت برامج تضر بتجربة التصÙØ­ (على سبيل المثال، من خلال تغيير صÙحتك الرئيسية أو عرض إعلانات إضاÙية على المواقع التي تزورها).</translation>
<translation id="7674629440242451245">â€Ø¥Ø°Ø§ كنت مهتمًا بميزات Google الجديدة والرائعة، ÙÙŠÙمكنك تجربة قناة مطوري البرامج على chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />متابعة إلى <ph name="SITE" /> (غير آمن)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">لا يمكن تحميل موقع الويب هذا من ذاكرة التخزين المؤقت</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">تم تشÙير الاتصال باستخدام <ph name="CIPHER" />ØŒ مع <ph name="MAC" /> لمصادقة الرسالة Ùˆ<ph name="KX" /> كآلية التبادل الرئيسية.</translation>
<translation id="780301667611848630">لا، شكرًا</translation>
<translation id="7805768142964895445">الحالة</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">â€Ù‡Ù„ تريد إزالة اقتراح النموذج من ChromeØŸ</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="7887683347370398519">â€ØªØ­Ù‚Ù‚ من رمز التحقق من البطاقة (CVC) ثم أعد المحاولة.</translation>
<translation id="7912024687060120840">ÙÙŠ المجلد:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">شهادة الخادم ليست صالحة بعد.</translation>
<translation id="7942349550061667556">أحمر</translation>
+<translation id="7947285636476623132">تحقق من عام انتهاء الصلاحية وأعÙد المحاولة مرة أخرى</translation>
<translation id="7951415247503192394">(32 بت)</translation>
<translation id="7956713633345437162">الإشارات المرجعية على الجوال</translation>
<translation id="7961015016161918242">مطلقًا</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">الترجمة من اللغة <ph name="ORIGINAL_LANGUAGE" /> إلى اللغة <ph name="TARGET_LANGUAGE" /> دومًا</translation>
<translation id="7995512525968007366">غير محدد</translation>
<translation id="8012647001091218357">لم نتمكن من الوصول إلى والديك ÙÙŠ الوقت الحالي. ÙŠÙرجى إعادة المحاولة مرة أخرى.</translation>
+<translation id="8025119109950072390">قد يحاول المهاجمون الموجودون على هذا الموقع Ùعل شيء خطير كتثبيت البرامج أو الكش٠عن معلوماتك الشخصية (على سبيل المثال، كلمات المرور أو أرقام الهوات٠أو بطاقات الائتمان).</translation>
+<translation id="803030522067524905">â€Ø§ÙƒØªØ´Ù التصÙØ­ الآمن من Google‬ مؤخرًا تصيّد احتيالي على <ph name="SITE" />. تتظاهر مواقع التصيّد الاحتيالي بكونها مواقع ويب أخرى للاحتيال عليك. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">هذه الصÙحة باللغة <ph name="SOURCE_LANGUAGE" />. هل تريد ترجمتها إلى اللغة <ph name="TARGET_LANGUAGE" />ØŸ</translation>
+<translation id="8041089156583427627">إرسال تعليقات</translation>
<translation id="8088680233425245692">أخÙÙ‚ عرض المقالة.</translation>
<translation id="8089520772729574115">أقل من ميغابايت واحدة</translation>
<translation id="8091372947890762290">التنشيط قيد الانتظار ÙÙŠ الخادم</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719">المل٠الموجود على <ph name="URL" /> غير قابل للقراءة. ربما تمت إزالته، أو ربما تكون أذونات المل٠هي التي تمنع الدخول.</translation>
<translation id="8194797478851900357">تراجع عن ال&amp;نقل</translation>
<translation id="8201077131113104583">â€Ø¹Ù†ÙˆØ§Ù† URL لتحديث الإضاÙØ© التي تحتوي على رقم التعري٠"<ph name="EXTENSION_ID" />" غير صالح.</translation>
+<translation id="8202097416529803614">ملخص الطلب</translation>
<translation id="8218327578424803826">الموقع الذي تم تعيينه:</translation>
<translation id="8225771182978767009">اختار الشخص الذي أعد جهاز الكمبيوتر حظر موقع الويب هذا.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />، <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">قد يحاول المستهدÙون الموجودون حاليًا على <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> تثبيت برامج خطيرة على جهاز الكمبيوتر التابع لك تسرق معلوماتك أو تحذÙها (على سبيل المثال، الصور وكلمات المرور والرسائل وبطاقات الائتمان).</translation>
<translation id="8241707690549784388">استخدمت الصÙحة التي تبحث عنها المعلومات التي أدخلتها وقد يؤدّي الرجوع إليها إلى تكرار جميع الإجراءات السابقة. هل تريد المتابعة؟</translation>
<translation id="8249320324621329438">تاريخ آخر عملية جلب:</translation>
<translation id="8261506727792406068">حذÙ</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">المصدر</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="8349305172487531364">شريط الإشارات</translation>
<translation id="8363502534493474904">إيقا٠تشغيل وضع الطائرة</translation>
<translation id="8364627913115013041">لم يتم تعيينها.</translation>
<translation id="8380941800586852976">ضارة</translation>
<translation id="8382348898565613901">تظهر الإشارات المرجعية التي زرتها مؤخرًا هنا</translation>
+<translation id="8398259832188219207">تم تحميل تقرير الأعطال ÙÙŠ <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">الأعطال (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">يجب إدخال عبارة المرور Ù†Ùسها مرتين.</translation>
<translation id="8428213095426709021">إعدادات</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222">استغرق <ph name="HOST_NAME" /> وقتًا أطول مما يجب للاستجابة.</translation>
<translation id="852346902619691059">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />Ø› بل إن شهادة أمانه غير موثوقة من Ù‚Ùبل نظام تشغيل جهازك. وربما يكون السبب ÙÙŠ ذلك خطأً ÙÙŠ التهيئة أو مهاجمًا يعترض اتصالك. <ph name="BEGIN_LEARN_MORE_LINK" />مزيد من المعلومات<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">â€Ù‡Ø°Ø§ النوع من البطاقات غير متواÙÙ‚ مع Google Payments. ÙŠÙرجى تحديد بطاقة مختلÙØ©.</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="8553075262323480129">أخÙقت الترجمة لتعذر تحديد لغة الصÙحة.</translation>
<translation id="8559762987265718583">تعذر إنشاء اتصال خاص بـ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> نظرًا لأن التاريخ والوقت للجهاز (<ph name="DATE_AND_TIME" />) غير صحيحين.</translation>
-<translation id="856992080682148">â€ØªÙ†ØªÙ‡ÙŠ صلاحية شهادة هذا الموقع ÙÙŠ 2017 أو بعد ذلك، وتتضمن سلسلة الشهادات شهادة موقعة باستخدام SHA-1.</translation>
<translation id="8571890674111243710">جار٠ترجمة الصÙحة إلى <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">لا تحدد الشهادة آلية للتحقق مما إذا كانت الشهادة قد تم إبطالها.</translation>
<translation id="8620436878122366504">لم يواÙÙ‚ عليه والداك حتى الآن</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522">â€ØªØ¹Ø°Ø± العثور على &lt;/abbr&gt;عنوان نظام أسماء النطاقات (DNS)â€&lt;abbr id="dnsDefinition"&gt; لـ <ph name="HOST_NAME" />. جار٠تشخيص المشكلة.</translation>
<translation id="8790007591277257123">إعادة الح&amp;Ø°Ù</translation>
<translation id="8798099450830957504">الاÙتراضي</translation>
+<translation id="8800988563907321413">تظهر اقتراحاتك "المجاورة" هنا</translation>
<translation id="8804164990146287819">سياسة الخصوصية</translation>
<translation id="8820817407110198400">إشارات</translation>
<translation id="8834246243508017242">تمكين الملء التلقائي باستخدام جهات الاتصال...</translation>
<translation id="883848425547221593">إشارات أخرى</translation>
+<translation id="884264119367021077">عنوان الشحن</translation>
<translation id="884923133447025588">لم يتمّ العثور على أي آلبة إبطال.</translation>
<translation id="885730110891505394">â€Ù…شاركة مع Google</translation>
<translation id="8866481888320382733">خطأ ÙÙŠ إعدادات تحليل السياسة</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">انتهت صلاحية شهادة الخادم.</translation>
<translation id="8987927404178983737">شهر</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" />†[<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">تحتوي مقدمة الموقع على برامج ضارة</translation>
<translation id="9001074447101275817">يتطلب الخادم الوكيل <ph name="DOMAIN" /> اسم مستخدم وكلمة مرور.</translation>
<translation id="901974403500617787">لا يمكن تعيين العلامات التي تسري عبر النظام إلا من Ù‚Ùبل المالك: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">تمت ترجمة هذه الصÙحة إلى <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">خطأ متعلق بالأمان</translation>
<translation id="9038649477754266430">استخدام إحدى خدمات التوقع لتحميل الصÙحات بسرعة أكبر</translation>
<translation id="9039213469156557790">إضاÙØ© إلى ذلك، تتضمن هذه الصÙحة موارد أخرى غير آمنة. ويستطيع الآخرون مشاهدة هذه الموارد أثناء نقلها، كما يستطيع أي مهاجم تعديلها لتغيير سلوك الصÙحة.</translation>
+<translation id="9040185888511745258">قد يحاول المهاجمون ÙÙŠ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> خداعك من خلال تثبيت برامج تضر بتجربة التصÙØ­ (على سبيل المثال، من خلال تغيير صÙحتك الرئيسية أو عرض إعلانات إضاÙية على المواقع التي تزورها).</translation>
<translation id="9050666287014529139">عبارة المرور</translation>
<translation id="9065203028668620118">تحرير</translation>
<translation id="9068849894565669697">اختيار اللون</translation>
<translation id="9076283476770535406">قد يتضمن محتوى للبالغين</translation>
-<translation id="9092364396508701805">صÙحة <ph name="HOST_NAME" /> لا تعمل</translation>
<translation id="9103872766612412690">â€ÙŠØ³ØªØ®Ø¯Ù… <ph name="SITE" /> التشÙير عادة لحماية معلوماتك. عندما حاول Chromium الاتصال بموقع <ph name="SITE" /> هذه المرة، أرجَع موقع الويب بيانات اعتماد غير عادية وغير صحيحة. وقد يحدث هذا عندما يحاول أحد المهاجمين التظاهر بأنه موقع <ph name="SITE" />ØŒ أو إذا قاطعت شاشة تسجيل دخول Wi-Fi الاتصال. ولكن لا تزال معلوماتك آمنة نظرًا لأن Chromium أوقَÙÙŽ الاتصال قبل تبادل أي بيانات.</translation>
<translation id="9137013805542155359">إظهار الصÙحة الأصلية</translation>
+<translation id="9137248913990643158">â€ÙŠÙرجى البدء وتسجيل الدخول إلى Chrome قبل استخدام هذا التطبيق.</translation>
<translation id="9148507642005240123">تراجع عن ا&amp;لتحرير</translation>
<translation id="9157595877708044936">جار٠الإعداد...</translation>
<translation id="9170848237812810038">&amp;إلغاء</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">محو النموذج</translation>
<translation id="939736085109172342">مجلد جديد</translation>
<translation id="941721044073577244">يبدو أنه ليس لديك إذن بالدخول إلى هذا الموقع</translation>
-<translation id="962701380617707048">â€Ø£Ø¯Ø®Ù„ تاريخ انتهاء الصلاحية ورمز التحقق من البطاقة (CVC) لـ <ph name="CREDIT_CARD" /> لتحديث تÙاصيل بطاقتك</translation>
<translation id="969892804517981540">البنية الرسمية</translation>
<translation id="988159990683914416">بنية المطوّÙر</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 aefd6d40d0a..09d8fd00190 100644
--- a/chromium/components/strings/components_strings_bg.xtb
+++ b/chromium/components/strings/components_strings_bg.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Свържете Ñе отново Ñ Wi-Fi.</translation>
<translation id="1175364870820465910">&amp;Печат...</translation>
<translation id="1181037720776840403">Премахване</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Ðвтоматично Ñъобщаване<ph name="END_WHITEPAPER_LINK" /> на Google на подробноÑти за евентуални инциденти, Ñвързани ÑÑŠÑ ÑигурноÑтта. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Ðапред</translation>
<translation id="1201895884277373915">Още от този Ñайт</translation>
<translation id="1206967143813997005">Ðевалиден първоначален подпиÑ</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Ñиньозелено</translation>
<translation id="1629803312968146339">ИÑкате ли Chrome да запази тази карта?</translation>
<translation id="1640180200866533862">Правила за потребителите</translation>
+<translation id="1640244768702815859">Опитайте да <ph name="BEGIN_LINK" />отворите началната Ñтраница на Ñайта<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">КонфигурациÑта на мрежата е невалидна и не можа да Ñе импортира.</translation>
<translation id="1644574205037202324">ИÑториÑ</translation>
<translation id="1645368109819982629">Ðеподдържан протокол</translation>
<translation id="1676269943528358898">Обикновено <ph name="SITE" /> използва шифроване за защита на информациÑта ви. Когато Google Chrome опита да уÑтанови връзка Ñ/ÑŠÑ <ph name="SITE" /> този път, уебÑайтът върна необичайни и неправилни идентификационни данни. Това може да Ñе Ñлучи, когато извършител на атака пробва да Ñе предÑтави за <ph name="SITE" /> или връзката е прекъÑната от екран за вход в Wi-Fi. ИнформациÑта ви продължава да е защитена, тъй като Chrome ÑÐ¿Ñ€Ñ Ð²Ñ€ÑŠÐ·ÐºÐ°Ñ‚Ð°, преди да бъдат обменени данни.</translation>
+<translation id="168328519870909584">ПонаÑтоÑщем извършители на атака Ñрещу <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> може да опитат да инÑталират опаÑни Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° уÑтройÑтвото ви, които крадат или изтриват Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ (например Ñнимки, пароли, ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ номера на кредитни карти).</translation>
<translation id="168841957122794586">Сертификатът на Ñървъра Ñъдържа Ñлаб криптографÑки ключ.</translation>
-<translation id="1701955595840307032">Получавайте предложено Ñъдържание</translation>
<translation id="1710259589646384581">ОС</translation>
<translation id="1721312023322545264">Ðеобходимо ви е разрешение от <ph name="NAME" />, за да поÑетите този Ñайт</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Ðомер на Ñтраницата</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Опитайте да Ñтартирате мрежова диагноÑтика в Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">МолÑ, актуализирайте пропуÑка Ñи за Ñинхронизиране.</translation>
+<translation id="1787142507584202372">Тук ще Ñе показват отворените ви раздели</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google БезопаÑно Ñърфиране наÑкоро <ph name="BEGIN_LINK" />откри злонамерен Ñофтуер<ph name="END_LINK" /> на <ph name="SITE" />. УебÑайтовете, които обикновено Ñа безопаÑни, понÑкога Ñе заразÑват Ñ Ñ‚Ð°ÐºÑŠÐ² Ñофтуер. Източникът на Ñъответното Ñъдържание е <ph name="SUBRESOURCE_HOST" /> – извеÑтен разпроÑтранител на злонамерен Ñофтуер. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">ЗаÑвката или параметрите й Ñа невалидни</translation>
+<translation id="1834321415901700177">Този Ñайт Ñъдържа опаÑни програми</translation>
<translation id="1838667051080421715">Преглеждате Ð¸Ð·Ñ…Ð¾Ð´Ð½Ð¸Ñ ÐºÐ¾Ð´ на уеб Ñтраницата.</translation>
<translation id="1871208020102129563">За прокÑи Ñървъра е зададено да използва фикÑирани прокÑи Ñървъри, а не URL Ð°Ð´Ñ€ÐµÑ Ð½Ð° Ñкрипт във формат .pac.</translation>
<translation id="1883255238294161206">Свиване на ÑпиÑъка</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Клиент Ñ Ð´Ð¸Ñ€ÐµÐºÑ‚Ð½Ð¾ изпълнение</translation>
<translation id="213826338245044447">Мобилни отметки</translation>
<translation id="2148716181193084225">ДнеÑ</translation>
-<translation id="2149973817440762519">Редактиране на отметката</translation>
<translation id="2154054054215849342">Синхронизирането не е налице за Ð²ÑŠÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ñ‚ Ð²Ð°Ñ Ð´Ð¾Ð¼ÐµÐ¹Ð½</translation>
<translation id="2166049586286450108">Пълен админиÑтраторÑки доÑтъп</translation>
<translation id="2166378884831602661">Този Ñайт не може да оÑигури защитена връзка</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Ð’ момента не можете да поÑетите <ph name="SITE" />, защото уебÑайтът използва HSTS. Обикновено мрежовите грешки и атаки Ñа временни, така че тази Ñтраница вероÑтно ще работи по-къÑно. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Пренебрегната бе невалидна отметка в Ð¸Ð½Ð´ÐµÐºÑ <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Други отметки</translation>
+<translation id="2355395290879513365">Извършители на атаки може да Ñа в ÑÑŠÑтоÑние да видÑÑ‚ изображениÑта, които преглеждате на този Ñайт, и да ви подведат, като ги променÑÑ‚.</translation>
<translation id="2359808026110333948">Ðапред</translation>
<translation id="2365563543831475020">Сигналът за Ñрив, запиÑан в/ъв <ph name="CRASH_TIME" />, не бе качен</translation>
<translation id="2367567093518048410">Ðиво</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Да Ñе показват правилата без зададена ÑтойноÑÑ‚</translation>
<translation id="2396249848217231973">&amp;ОтмÑна на изтриването</translation>
<translation id="2455981314101692989">Тази уеб Ñтраница е деактивирала автоматичното попълване за този формулÑÑ€.</translation>
+<translation id="2460160116472764928">Google БезопаÑно Ñърфиране наÑкоро <ph name="BEGIN_LINK" />откри злонамерен Ñофтуер<ph name="END_LINK" /> на <ph name="SITE" />. УебÑайтовете, които обикновено Ñа безопаÑни, понÑкога Ñе заразÑват Ñ Ñ‚Ð°ÐºÑŠÐ² Ñофтуер. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Попълване</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Стартирайте мрежова диагноÑтика<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Ðевалиден URL Ð°Ð´Ñ€ÐµÑ Ð·Ð° Ñ‚ÑŠÑ€Ñене.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">ÐаиÑтина ли иÑкате да изтриете тези Ñтраници от иÑториÑта Ñи?</translation>
<translation id="2677748264148917807">Излизане</translation>
<translation id="269990154133806163">Сървърът предоÑтави Ñертификат, който не е разкрит публично чрез правило в ПрозрачноÑÑ‚ на Ñертификатите. Това Ñе изиÑква за нÑкои Ñертификати Ñ Ñ†ÐµÐ» защита Ñрещу хакери и за да е Ñигурно, че Ñа надеждни. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">СпиÑък за четене</translation>
<translation id="2704283930420550640">СтойноÑтта не ÑъответÑтва на формата.</translation>
<translation id="2704951214193499422">Chromium не можа да потвърди картата ви. МолÑ, опитайте отново по-къÑно.</translation>
<translation id="2705137772291741111">Запазеното (кеширано) копие на този Ñайт не можа да Ñе прочете.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Ðаправихте опит да Ñе Ñвържете Ñ/ÑŠÑ <ph name="DOMAIN" />, но Ñървърът предoÑтави Ñертификат, анулиран от Ð¸Ð·Ð´Ð°Ñ‚ÐµÐ»Ñ Ð¼Ñƒ. Това означава, че в никакъв Ñлучай не Ñ‚Ñ€Ñбва да Ñе доверÑвате на предÑтавените от Ñървъра идентификационни данни за ÑигурноÑÑ‚. Възможно е да Ñте Ñе Ñвързали Ñ Ð¸Ð·Ð²ÑŠÑ€ÑˆÐ¸Ñ‚ÐµÐ» на атака. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">ИÑкане на разрешение</translation>
<translation id="2713444072780614174">бÑло</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Можете да деактивирате вÑички конфигурирани за дадена връзка прокÑи Ñървъри от Ñтраницата „ÐаÑтройки“.</translation>
<translation id="2955913368246107853">ЗатварÑне на лентата за Ñ‚ÑŠÑ€Ñене</translation>
<translation id="2958431318199492670">КонфигурациÑта на мрежата не Ñпазва Ñтандарта на ONC. Възможно е чаÑти от Ð½ÐµÑ Ð´Ð° не Ñа импортирани.</translation>
+<translation id="29611076221683977">ПонаÑтоÑщем извършители на атака Ñрещу <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> може да опитат да инÑталират опаÑни програми на компютъра ви под Mac, които крадат или изтриват информациÑта ви (например Ñнимки, пароли, ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ номера на кредитни карти).</translation>
<translation id="2969319727213777354">За уÑтановÑване на Ñигурна връзка е необходимо чаÑовникът ви да е верен. Това е така, защото Ñертификатите, Ñ ÐºÐ¾Ð¸Ñ‚Ð¾ уебÑайтовете Ñе идентифицират, Ñа валидни Ñамо за конкретни периоди от време. Тъй като чаÑовникът на уÑтройÑтвото ви не е верен, Google Chrome не може да потвърди тези Ñертификати.</translation>
<translation id="2972581237482394796">&amp;ВъзÑтановÑване</translation>
<translation id="2985306909656435243">Ðко наÑтройката е активирана, Chromium ще ÑъхранÑва на това уÑтройÑтво копие на картата ви Ñ Ñ†ÐµÐ» по-бързо попълване на формулÑри.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Скриване на подробноÑтите</translation>
<translation id="3587482841069643663">Ð’Ñички</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Този Ñървър не поддържа разширението за предоговарÑне на TLS.</translation>
<translation id="36224234498066874">ИзчиÑтване на данните за Ñърфирането...</translation>
<translation id="362276910939193118">Показване на пълната иÑториÑ</translation>
<translation id="3623476034248543066">Показване на ÑтойноÑтта</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">Ðко виждате това чеÑто, опитайте <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">РевизиÑ</translation>
<translation id="3678029195006412963">ЗаÑвката не можа да бъде подпиÑана</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Лицензите Ñа изчерпани</translation>
<translation id="3714780639079136834">Включете мобилните данни или Wi-Fi.</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Проверете конфигурациÑта на прокÑи Ñървъра, защитната Ñтена и DNS<ph name="END_LINK" />.</translation>
+<translation id="3736520371357197498">Ðко разбирате риÑковете за ÑигурноÑтта Ñи, може <ph name="BEGIN_LINK" />да поÑетите този ненадежден Ñайт<ph name="END_LINK" /> преди премахването на опаÑните програми.</translation>
<translation id="3739623965217189342">Копирана от Ð²Ð°Ñ Ð²Ñ€ÑŠÐ·ÐºÐ°</translation>
<translation id="375403751935624634">Преводът не бе уÑпешен поради грешка в Ñървъра.</translation>
<translation id="3759461132968374835">ÐаÑкоро не Ñте Ñъобщавали за Ñривове. Тези, възникнали при деактивирано изпращане на Ñигнали за Ñривове, не Ñе показват тук.</translation>
-<translation id="3788090790273268753">ВалидноÑтта на Ñертификата за този Ñайт изтича през 2016 г., а Ñъответната верига Ñъдържа Ñертификат, подпиÑан Ñ SHA-1.</translation>
<translation id="382518646247711829">Ðко използвате прокÑи Ñървър...</translation>
<translation id="3828924085048779000">Ðе може пропуÑкът да не Ñе попълни.</translation>
<translation id="3845539888601087042">Показва Ñе иÑториÑта от уÑтройÑтвата, на които Ñте влезли в профила Ñи. <ph name="BEGIN_LINK" />Ðаучете повече<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Ключ „<ph name="SUBKEY" />“: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Клиентът и Ñървърът не поддържат обща верÑÐ¸Ñ Ð¸Ð»Ð¸ пакет за шифроване за протокола SSL.</translation>
<translation id="4079302484614802869">За конфигурациÑта на прокÑи Ñървъра е зададено да използва URL Ð°Ð´Ñ€ÐµÑ Ð½Ð° Ñкрипт във формат .pac, а не фикÑирани прокÑи Ñървъри.</translation>
+<translation id="4098354747657067197">Ðа хоризонта Ñе задава измамен Ñайт</translation>
<translation id="4103249731201008433">СерийниÑÑ‚ номер на уÑтройÑтвото е невалиден</translation>
<translation id="4103763322291513355">ПоÑетете &lt;strong&gt;chrome://policy&lt;/strong&gt;, за да видите изброени URL адреÑите в Ñ‡ÐµÑ€Ð½Ð¸Ñ ÑпиÑък и другите правила, наложени от ÑиÑÑ‚ÐµÐ¼Ð½Ð¸Ñ Ð²Ð¸ админиÑтратор.</translation>
<translation id="4110615724604346410">Сървърът не можа да докаже, че е <ph name="DOMAIN" />. Сертификатът му за ÑигурноÑÑ‚ Ñъдържа грешки. Това може да Ñе дължи на неправилно конфигуриране или на прехващане на връзката ви от извършител на атака. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{нÑма}=1{1 приложение ($1)}=2{2 Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ ($1, $2)}other{# Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Сривове</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Опитайте да Ñтартирате мрежова диагноÑтика<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Връзката ви Ñ Ñ‚Ð¾Ð·Ð¸ Ñайт не е напълно защитена</translation>
<translation id="4250680216510889253">Ðе</translation>
<translation id="425582637250725228">Ðаправените от Ð²Ð°Ñ Ð¿Ñ€Ð¾Ð¼ÐµÐ½Ð¸ може да не Ñе запазÑÑ‚.</translation>
<translation id="4258748452823770588">Ðевалиден подпиÑ</translation>
<translation id="4269787794583293679">(ÐÑма потребителÑко име)</translation>
+<translation id="4280429058323657511">, изтича: <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google БезопаÑно Ñърфиране наÑкоро <ph name="BEGIN_LINK" />намери опаÑни програми<ph name="END_LINK" /> на <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">ОÑновни предложениÑ</translation>
<translation id="4304224509867189079">Вход</translation>
<translation id="432290197980158659">Сървърът предоÑтави Ñертификат, който не ÑъответÑтва на вградените очакваниÑ. С цел ваша защита те Ñа включени за определени уебÑайтове Ñ Ð³Ð¾Ð»Ñма Ñтепен на ÑигурноÑÑ‚. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Ðамирането на ÑтатиÑта не бе уÑпешно</translation>
+<translation id="4326324639298822553">Проверете датата на валидноÑÑ‚ и опитайте отново</translation>
<translation id="4331708818696583467">ÐÑма защита</translation>
+<translation id="4356973930735388585">Извършители на атака, използващи този Ñайт, може да опитат да инÑталират опаÑни програми на компютъра ви, които крадат или изтриват информациÑта ви (например Ñнимки, пароли, ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ номера на кредитни карти).</translation>
<translation id="4372948949327679948">Очаквана е ÑтойноÑÑ‚ от тип <ph name="VALUE_TYPE" />.</translation>
<translation id="4381091992796011497">ПотребителÑко име:</translation>
<translation id="4394049700291259645">Деактивиране</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Опитахте да Ñе Ñвържете Ñ/ÑŠÑ <ph name="DOMAIN" />, но Ñървърът предоÑтави невалиден Ñертификат. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">Връзката ви Ñ/ÑŠÑ <ph name="DOMAIN" /> е шифрована ÑÑŠÑ Ñъвременен криптографÑки пакет.</translation>
<translation id="4594403342090139922">&amp;ОтмÑна на изтриването</translation>
+<translation id="4619615317237390068">Раздели от други уÑтройÑтва</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Преглеждате Ñтраница на разширение.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Презареждане на правилата</translation>
<translation id="4728558894243024398">Платформа</translation>
<translation id="4744603770635761495">Път към Ð¸Ð·Ð¿ÑŠÐ»Ð½Ð¸Ð¼Ð¸Ñ Ñ„Ð°Ð¹Ð»</translation>
+<translation id="4750917950439032686">ИнформациÑта ви (например пароли или номера на кредитни карти) е чаÑтна, когато Ñе изпраща до този Ñайт.</translation>
<translation id="4756388243121344051">&amp;ИÑториÑ</translation>
+<translation id="4759118997339041434">Ðвтоматичното попълване на данните за плащане е деактивирано</translation>
<translation id="4764776831041365478">До уеб Ñтраницата на Ð°Ð´Ñ€ÐµÑ <ph name="URL" /> може временно да нÑма доÑтъп или да е премеÑтена за поÑтоÑнно на нов уеб адреÑ.</translation>
<translation id="4771973620359291008">Възникна неизвеÑтна грешка.</translation>
<translation id="4800132727771399293">Прегледайте датата на валидноÑÑ‚ и кода за проверка и оптитайте отново</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Грешка в мрежата</translation>
<translation id="4816492930507672669">Да Ñе побере в Ñтраницата</translation>
<translation id="4850886885716139402">Изглед</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> и <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{и още 1 уеб Ñтраница}other{и още # уеб Ñтраници}}</translation>
<translation id="4923417429809017348">Тази Ñтраница е преведена от непознат език на <ph name="LANGUAGE_LANGUAGE" /></translation>
+<translation id="4923459931733593730">Плащане</translation>
<translation id="4926049483395192435">ТрÑбва да Ñе поÑочи.</translation>
<translation id="495170559598752135">ДейÑтвиÑ</translation>
<translation id="4958444002117714549">Разгъване на ÑпиÑъка</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Ð˜Ð·Ñ‚ÐµÐ³Ð»Ñ Ñе</translation>
<translation id="5190835502935405962">Лента на отметките</translation>
<translation id="5199729219167945352">ЕкÑперименти</translation>
-<translation id="5199841536747119669">ПредложениÑта за Ð²Ð°Ñ Ñ‰Ðµ Ñе показват тук</translation>
<translation id="5251803541071282808">Облак</translation>
<translation id="5277279256032773186">Използвате Chrome на работното Ñи мÑÑто? БизнеÑите могат да управлÑват наÑтройките на браузъра за Ñлужителите Ñи. Ðаучете повече</translation>
<translation id="5299298092464848405">Грешка при ÑÐ¸Ð½Ñ‚Ð°ÐºÑ‚Ð¸Ñ‡Ð½Ð¸Ñ Ð°Ð½Ð°Ð»Ð¸Ð· на правилото</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Изпращането на Ñигнали за Ñривове е деактивирано.</translation>
<translation id="5317780077021120954">Запазване</translation>
<translation id="5327248766486351172">Име</translation>
+<translation id="5337705430875057403">Извършителите на атака Ñрещу <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> може да ви подведат да направите нещо опаÑно, като например да инÑталирате Ñофтуер или да разкриете лична Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ (например пароли, телефонни номера или номера на кредитни карти).</translation>
<translation id="5359637492792381994">Сървърът не можа да докаже, че е <ph name="DOMAIN" />. ПонаÑтоÑщем Ñертификатът му за ÑигурноÑÑ‚ не е валиден. Това може да Ñе дължи на неправилно конфигуриране или на прехващане на връзката ви от извършител на атака. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">СъхранÑването на наÑтройките за правилото не бе уÑпешно</translation>
+<translation id="5386426401304769735">Веригата от Ñертификати за този Ñайт Ñъдържа Ñертификат, подпиÑан Ñ SHA-1.</translation>
<translation id="5421136146218899937">ИзчиÑтване на данните за Ñърфирането...</translation>
<translation id="5430298929874300616">Премахване на отметката</translation>
<translation id="5431657950005405462">Файлът ви не бе намерен</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Вградена Ñтраница на Ð°Ð´Ñ€ÐµÑ <ph name="SITE" /> изпраща подкана:</translation>
<translation id="5556459405103347317">Повторно зареждане</translation>
<translation id="5565735124758917034">Ðктивно</translation>
+<translation id="5572851009514199876">МолÑ, Ñтартирайте браузъра Chrome и влезте в него, за да Ñе провери дали имате доÑтъп до този Ñайт.</translation>
+<translation id="5580958916614886209">Проверете меÑеца на валидноÑÑ‚ и опитайте отново</translation>
<translation id="560412284261940334">Управлението не Ñе поддържа</translation>
<translation id="5610142619324316209">Проверете връзката.</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> ви пренаÑочи твърде много пъти.</translation>
<translation id="5622887735448669177">ИÑкате ли да напуÑнете този Ñайт?</translation>
<translation id="5629630648637658800">Зареждането на наÑтройките за правилото не бе уÑпешно</translation>
<translation id="5631439013527180824">Ðевалидно означение за управление на уÑтройÑтвото</translation>
+<translation id="5669703222995421982">Получаване на перÑонализирано Ñъдържание</translation>
+<translation id="5675650730144413517">Тази Ñтраница не работи</translation>
<translation id="5677928146339483299">Блокирано</translation>
<translation id="5694783966845939798">Ðаправихте опит да Ñе Ñвържете Ñ/ÑŠÑ <ph name="DOMAIN" />, но Ñървърът предоÑтави Ñертификат, подпиÑан ÑÑŠÑ Ñлаб алгоритъм (например SHA-1). Това означава, че идентификационните данни за ÑигурноÑÑ‚ от Ñървъра може да Ñа фалшифицирани и той да не е този, който очаквате (възможно е да Ñте Ñе Ñвързали Ñ Ð¸Ð·Ð²ÑŠÑ€ÑˆÐ¸Ñ‚ÐµÐ» на атака). <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">СамоличноÑтта на този уебÑайт не е потвърдена.</translation>
<translation id="5720705177508910913">ТекущиÑÑ‚ потребител</translation>
-<translation id="572328651809341494">Скорошни раздели</translation>
<translation id="5732392974455271431">Родителите ви могат да го отблокират за ваÑ</translation>
<translation id="5784606427469807560">При потвърждаването на картата ви възникна проблем. Проверете връзката Ñи Ñ Ð¸Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚ и опитайте отново.</translation>
<translation id="5785756445106461925">ОÑвен това тази Ñтраница включва други реÑурÑи, които не Ñа защитени. Докато Ñе предават, те могат да бъдат видени от други хора и да бъдат модифицирани от извършител на атака, така че да Ñе промени изгледът на Ñтраницата.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Опитахте да Ñе Ñвържете Ñ/ÑŠÑ <ph name="DOMAIN" />, но Ñървърът предоÑтави Ñертификат, Ñъдържащ Ñлаб ключ. Възможно е извършител на атака да е компрометирал чаÑÑ‚Ð½Ð¸Ñ ÐºÐ»ÑŽÑ‡ и Ñървърът да не е този, който очаквате (възможно е да Ñте Ñе Ñвързали Ñ Ð¸Ð·Ð²ÑŠÑ€ÑˆÐ¸Ñ‚ÐµÐ» на атака). <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Ðова папка</translation>
<translation id="5869405914158311789">ÐÑма доÑтъп до този Ñайт</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Този тип карта не Ñе поддържа от Google Payments за този търговец. МолÑ, изберете друга.</translation>
<translation id="59174027418879706">Ðктивирано</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="5966707198760109579">Седмица</translation>
<translation id="5967867314010545767">Премахване от иÑториÑта</translation>
<translation id="5975083100439434680">ÐамалÑване на мащаба</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Изпробвайте Ñледното:</translation>
<translation id="6151417162996330722">Сертификатът на Ñървъра има твърде дълъг период на валидноÑÑ‚.</translation>
<translation id="6165508094623778733">Ðаучете повече</translation>
+<translation id="6177128806592000436">Връзката ви Ñ Ñ‚Ð¾Ð·Ð¸ Ñайт не е защитена</translation>
<translation id="6203231073485539293">Проверете връзката Ñи Ñ Ð¸Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚</translation>
<translation id="6218753634732582820">ÐдреÑÑŠÑ‚ да Ñе премахне ли от Chromium?</translation>
+<translation id="6251924700383757765">Ð”ÐµÐºÐ»Ð°Ñ€Ð°Ñ†Ð¸Ñ Ð·Ð° поверителноÑÑ‚</translation>
+<translation id="625755898061068298">Деактивирахте предупреждениÑта отноÑно ÑигурноÑтта на този Ñайт.</translation>
<translation id="6259156558325130047">&amp;ВъзÑтановÑване на пренареждането</translation>
<translation id="6263376278284652872">Отметки от <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Ðазад към безопаÑната Ñтраница</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">ÐÑма доÑтъп до <ph name="URL" />.</translation>
<translation id="6321917430147971392">Проверете наÑтройките Ñи за DNS</translation>
<translation id="6328639280570009161">Опитайте да деактивирате предвижданиÑта за мрежата</translation>
+<translation id="6328786501058569169">Този Ñайт е измамен</translation>
<translation id="6337534724793800597">Филтриране на правилата по име</translation>
<translation id="6342069812937806050">Току-що</translation>
<translation id="6345221851280129312">неизвеÑтен размер</translation>
@@ -489,6 +519,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{още 1 предложение}other{още # предложениÑ}}</translation>
<translation id="6387478394221739770">ТърÑите интереÑни нови функции на Chrome? Изпробвайте бета канала на Ð°Ð´Ñ€ÐµÑ chrome.com/beta.</translation>
<translation id="6389758589412724634">Паметта на Chromium Ñе изчерпа, докато браузърът опитваше да покаже тази уеб Ñтраница.</translation>
+<translation id="6404511346730675251">Редактиране на отметката</translation>
+<translation id="6410264514553301377">Въвеждане на датата на валидноÑÑ‚ и кода за проверка за <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Попитахте Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ Ñи дали може да поÑетите този Ñайт</translation>
<translation id="6416403317709441254">Ð’ момента не можете да поÑетите <ph name="SITE" />, защото уебÑайтът изпрати невалидни идентификационни данни, които Chromium не може да обработи. Обикновено мрежовите грешки и атаки Ñа временни, така че Ñтраницата вероÑтно ще работи по-къÑно. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Ðе може да Ñе провери дали Ñертификатът е анулиран.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Бе пренебрегнато, защото оÑновното Ñ‚ÑŠÑ€Ñене е деактивирано от правило.</translation>
<translation id="6462969404041126431">Сървърът не можа да докаже, че е <ph name="DOMAIN" />. Възможно е Ñертификатът му за ÑигурноÑÑ‚ да е анулиран. Това може да Ñе дължи на неправилно конфигуриране или на прехващане на връзката ви от извършител на атака. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">Правила за уÑтройÑтвото</translation>
+<translation id="6477321094435799029">Chrome откри необичаен код на тази Ñтраница и Ñ Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð°, за да защити личната ви Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ (например пароли, телефонни номера и номера на кредитни карти).</translation>
<translation id="6489534406876378309">Стартиране на качването на Ñривове</translation>
<translation id="6529602333819889595">&amp;ВъзÑтановÑване на изтриването</translation>
<translation id="6534179046333460208">ÐŸÑ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¾Ñ‚ ФизичеÑката мрежа</translation>
<translation id="6550675742724504774">Опции</translation>
+<translation id="6556239504065605927">Защитена връзка</translation>
<translation id="6563469144985748109">Мениджърът ви вÑе още не е одобрил заÑвката</translation>
<translation id="6593753688552673085">по-малко от <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Опции за шифроване</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Това правило е оттеглено.</translation>
<translation id="6652240803263749613">Сървърът не можа да докаже, че е <ph name="DOMAIN" />. Сертификатът му за ÑигурноÑÑ‚ не Ñе Ñчита за надежден от операционната ÑиÑтема на компютъра ви. Това може да Ñе дължи на неправилно конфигуриране или на прехващане на връзката ви от извършител на атака. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Редактиране на папката</translation>
-<translation id="6660210980321319655">Сигналът е подаден автоматично в/ъв <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Предложението за формулÑри да Ñе премахне ли от Chromium?</translation>
<translation id="6685834062052613830">Излизане от профила и завършване на наÑтройването</translation>
<translation id="6710213216561001401">Предишна</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">СтойноÑÑ‚ за правилото</translation>
<translation id="6757797048963528358">УÑтройÑтвото ви премина в ÑпÑщ режим.</translation>
<translation id="6778737459546443941">РодителÑÑ‚ ви вÑе още не е одобрил заÑвката</translation>
+<translation id="6810899417690483278">Идент. â„– на перÑонализирането</translation>
<translation id="6820686453637990663">Код за ÑигурноÑÑ‚</translation>
<translation id="6830600606572693159">Уеб Ñтраницата на Ð°Ð´Ñ€ÐµÑ <ph name="URL" /> понаÑтоÑщем не е налице. Възможно е да е претоварена или да не работи поради профилактика.</translation>
<translation id="6831043979455480757">Превод</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° връзка</translation>
+<translation id="7079718277001814089">Този Ñайт Ñъдържа злонамерен Ñофтуер</translation>
<translation id="7087282848513945231">Окръг</translation>
<translation id="7088615885725309056">По-Ñтара</translation>
<translation id="7090678807593890770">ПотърÑете „<ph name="LINK" />“ Ñ Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> не Ñе придържа към Ñтандартите за ÑигурноÑÑ‚.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Ðаучете повече<ph name="END_LINK" /> за този проблем.</translation>
<translation id="7219179957768738017">Връзката използва <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Ðа хоризонта Ñе задава Ñайт ÑÑŠÑ Ð·Ð»Ð¾Ð½Ð°Ð¼ÐµÑ€ÐµÐ½ Ñофтуер</translation>
<translation id="724975217298816891">Въведете датата на валидноÑÑ‚ и кода за проверка за <ph name="CREDIT_CARD" />, за да актуализирате данните за картата Ñи. След като Ñ Ð¿Ð¾Ñ‚Ð²ÑŠÑ€Ð´Ð¸Ñ‚Ðµ, те ще бъдат Ñподелени Ñ Ñ‚Ð¾Ð·Ð¸ Ñайт.</translation>
<translation id="725866823122871198">Ðе може да Ñе уÑтанови чаÑтна връзка Ñ/ÑŠÑ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, тъй като датата и чаÑÑŠÑ‚ на компютъра ви (<ph name="DATE_AND_TIME" />) Ñа неправилни.</translation>
<translation id="7269802741830436641">Тази Ñтраница води до циклично пренаÑочване</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">Отказ</translation>
<translation id="7667346355482952095">Върнатото означение за правилата е празно или не ÑъответÑтва на текущото</translation>
<translation id="7668654391829183341">ÐеизвеÑтно уÑтройÑтво</translation>
+<translation id="7669271284792375604">Извършителите на атаки, използващи този Ñайт, може да опитат да ви подведат да инÑталирате програми, които вредÑÑ‚ на Ñърфирането ви (например, като променÑÑ‚ началната ви Ñтраница или показват допълнителни реклами в поÑещаваните от Ð²Ð°Ñ Ñайтове).</translation>
<translation id="7674629440242451245">ТърÑите интереÑни нови функции на Chrome? Изпробвайте канала за програмиÑти на Ð°Ð´Ñ€ÐµÑ chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Продължаване към <ph name="SITE" /> (опаÑно)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Този Ñайт не може да Ñе зареди от кеш паметта</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">Връзката е шифрована поÑредÑтвом <ph name="CIPHER" />, Ñ/ÑŠÑ <ph name="MAC" /> за удоÑтоверÑване за ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ <ph name="KX" /> като механизъм за обмен на ключове.</translation>
<translation id="780301667611848630">Ðе, благодарÑ</translation>
<translation id="7805768142964895445">СъÑтоÑние</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Предложението за формулÑри да Ñе премахне ли от Chrome?</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="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="7887683347370398519">Прегледайте кода за проверка и опитайте отново</translation>
<translation id="7912024687060120840">В папката:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Сертификатът на Ñървъра още не е валиден.</translation>
<translation id="7942349550061667556">червено</translation>
+<translation id="7947285636476623132">Проверете годината на валидноÑÑ‚ и опитайте отново</translation>
<translation id="7951415247503192394">(32 бита)</translation>
<translation id="7956713633345437162">Мобилни отметки</translation>
<translation id="7961015016161918242">Ðикога</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">Винаги да Ñе превежда от <ph name="ORIGINAL_LANGUAGE" /> на <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Ðе е поÑочено</translation>
<translation id="8012647001091218357">Ðе можахме да Ñе Ñвържем Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ð¸Ñ‚Ðµ ви. МолÑ, опитайте отново.</translation>
+<translation id="8025119109950072390">Извършителите на атаки, използващи този Ñайт, може да ви подведат да направите нещо опаÑно, като например да инÑталирате Ñофтуер или да разкриете лична Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ (например пароли, телефонни номера или номера на кредитни карти).</translation>
+<translation id="803030522067524905">Google БезопаÑно Ñърфиране наÑкоро откри фишинг на <ph name="SITE" />. Сайтовете за фишинг Ñе предÑтавÑÑ‚ за други уебÑайтове Ñ Ñ†ÐµÐ» да ви подведат. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Тази Ñтраница е на <ph name="SOURCE_LANGUAGE" />. Да Ñе преведе ли на <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Изпращане на отзивите</translation>
<translation id="8088680233425245692">Преглеждането на ÑтатиÑта не бе уÑпешно.</translation>
<translation id="8089520772729574115">по-малко от 1 МБ</translation>
<translation id="8091372947890762290">Ð’ Ñървъра Ñе изчаква активиране</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719">Файлът на Ð°Ð´Ñ€ÐµÑ <ph name="URL" /> не може да бъде прочетен. Възможно е да е премахнат, премеÑтен или разрешениÑта му да предотвратÑват доÑтъпа.</translation>
<translation id="8194797478851900357">&amp;ОтмÑна на премеÑтването</translation>
<translation id="8201077131113104583">Ðевалиден URL Ð°Ð´Ñ€ÐµÑ Ð·Ð° актуализиране на разширението Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½ÐµÐ½ номер <ph name="EXTENSION_ID" />.</translation>
+<translation id="8202097416529803614">Обобщена Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° поръчката</translation>
<translation id="8218327578424803826">Зададено меÑтоположение:</translation>
<translation id="8225771182978767009">Човекът, който е наÑтроил компютъра, е блокирал този Ñайт.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" /> и <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">ПонаÑтоÑщем извършители на атака Ñрещу <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> може да опитат да инÑталират опаÑни програми на компютъра ви, които крадат или изтриват информациÑта ви (например Ñнимки, пароли, ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ номера на кредитни карти).</translation>
<translation id="8241707690549784388">Страницата, коÑто Ñ‚ÑŠÑ€Ñите, използва въведената от Ð²Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ. Ðко Ñе върнете на тази Ñтраница, дейÑтвиÑта, които вече Ñте изпълнили, може да бъдат повторени. ИÑкате ли да продължите?</translation>
<translation id="8249320324621329438">ПоÑледно извличане:</translation>
<translation id="8261506727792406068">Изтриване</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">Източник</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="8349305172487531364">Лента на отметките</translation>
<translation id="8363502534493474904">Изключете ÑÐ°Ð¼Ð¾Ð»ÐµÑ‚Ð½Ð¸Ñ Ñ€ÐµÐ¶Ð¸Ð¼.</translation>
<translation id="8364627913115013041">Ðе е зададено.</translation>
<translation id="8380941800586852976">ОпаÑно</translation>
<translation id="8382348898565613901">ÐаÑкоро поÑетените от Ð²Ð°Ñ Ð¾Ñ‚Ð¼ÐµÑ‚ÐºÐ¸ ще Ñе показват тук</translation>
+<translation id="8398259832188219207">Сигналът за Ñрив е качен в/ъв <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Сривове (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">ТрÑбва да въведете един и Ñъщи пропуÑк два пъти.</translation>
<translation id="8428213095426709021">ÐаÑтройки</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> не Ð¾Ñ‚Ð³Ð¾Ð²Ð°Ñ€Ñ Ñ‚Ð²ÑŠÑ€Ð´Ðµ дълго време.</translation>
<translation id="852346902619691059">Сървърът не можа да докаже, че е <ph name="DOMAIN" />. Сертификатът му за ÑигурноÑÑ‚ не Ñе Ñчита за надежден от операционната ÑиÑтема на уÑтройÑтвото ви. Това може да Ñе дължи на неправилно конфигуриране или на прехващане на връзката ви от извършител на атака. <ph name="BEGIN_LEARN_MORE_LINK" />Ðаучете повече<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Този тип карта не Ñе поддържа от Google Payments. МолÑ, изберете друга.</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="8553075262323480129">Преводът не бе уÑпешен, защото езикът на Ñтраницата не можа да бъде определен.</translation>
<translation id="8559762987265718583">Ðе може да Ñе уÑтанови чаÑтна връзка Ñ/ÑŠÑ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, тъй като датата и чаÑÑŠÑ‚ на уÑтройÑтвото ви (<ph name="DATE_AND_TIME" />) Ñа неправилни.</translation>
-<translation id="856992080682148">ВалидноÑтта на Ñертификата за този Ñайт изтича през 2017 г. или по-къÑно, а Ñъответната верига Ñъдържа Ñертификат, подпиÑан Ñ SHA-1.</translation>
<translation id="8571890674111243710">Превод на Ñтраницата на <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Сертификатът не поÑочва механизъм за проверка дали е бил анулиран.</translation>
<translation id="8620436878122366504">Родителите ви вÑе още не Ñа одобрили заÑвката</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;DNS адреÑÑŠÑ‚&lt;/abbr&gt; на <ph name="HOST_NAME" /> не можа да бъде намерен. Проблемът Ñе диагноÑтицира.</translation>
<translation id="8790007591277257123">&amp;ВъзÑтановÑване на изтриването</translation>
<translation id="8798099450830957504">По подразбиране</translation>
+<translation id="8800988563907321413">Тук ще Ñе показват Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð·Ð° неща в близоÑÑ‚</translation>
<translation id="8804164990146287819">Ð”ÐµÐºÐ»Ð°Ñ€Ð°Ñ†Ð¸Ñ Ð·Ð° поверителноÑÑ‚</translation>
<translation id="8820817407110198400">Отметки</translation>
<translation id="8834246243508017242">Ðктивиране на използването на контактите при автоматично попълване…</translation>
<translation id="883848425547221593">Други отметки</translation>
+<translation id="884264119367021077">ÐÐ´Ñ€ÐµÑ Ð·Ð° доÑтавка</translation>
<translation id="884923133447025588">Ðе е намерен механизъм за анулиране.</translation>
<translation id="885730110891505394">СподелÑне Ñ Google</translation>
<translation id="8866481888320382733">Грешка при ÑÐ¸Ð½Ñ‚Ð°ÐºÑ‚Ð¸Ñ‡Ð½Ð¸Ñ Ð°Ð½Ð°Ð»Ð¸Ð· на наÑтройките за правилото</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">Сертификатът на Ñървъра е Ñ Ð¸Ð·Ñ‚ÐµÐºÐ»Ð° валидноÑÑ‚.</translation>
<translation id="8987927404178983737">МеÑец</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Ðа хоризонта Ñе задава Ñайт Ñ Ð¾Ð¿Ð°Ñни програми</translation>
<translation id="9001074447101275817">ИзиÑкват Ñе потребителÑко име и парола за прокÑи Ñървъра <ph name="DOMAIN" />.</translation>
<translation id="901974403500617787">Флаговете, които Ñе прилагат за цÑлата ÑиÑтема, могат да бъдат зададени Ñамо от ÑобÑтвеника: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Тази Ñтраница е преведена на <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Грешка в ÑигурноÑтта</translation>
<translation id="9038649477754266430">Използване на уÑлуга за предвиждане Ñ Ñ†ÐµÐ» по-бързо зареждане на Ñтраниците</translation>
<translation id="9039213469156557790">ОÑвен това тази Ñтраница включва други реÑурÑи, които не Ñа защитени. Докато Ñе предават, те могат да бъдат видени от други хора и да бъдат модифицирани от извършител на атака, така че да Ñе промени поведението на Ñтраницата.</translation>
+<translation id="9040185888511745258">Извършителите на атаки Ñрещу <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> може да опитат да ви подведат да инÑталирате програми, които вредÑÑ‚ на практичеÑката ви работа при Ñърфиране (например като променÑÑ‚ началната ви Ñтраница или показват допълнителни реклами в поÑещаваните от Ð²Ð°Ñ Ñайтове).</translation>
<translation id="9050666287014529139">Парола</translation>
<translation id="9065203028668620118">Редактиране</translation>
<translation id="9068849894565669697">Избор на цвÑÑ‚</translation>
<translation id="9076283476770535406">Възможно е да има Ñъдържание за пълнолетни</translation>
-<translation id="9092364396508701805">Страницата на <ph name="HOST_NAME" /> не работи</translation>
<translation id="9103872766612412690">Обикновено <ph name="SITE" /> използва шифроване за защита на информациÑта ви. Когато Chromium опита да уÑтанови връзка Ñ/ÑŠÑ <ph name="SITE" /> този път, уебÑайтът върна необичайни и неправилни идентификационни данни. Това може да Ñе Ñлучи, когато извършител на атака пробва да Ñе предÑтави за <ph name="SITE" /> или връзката е прекъÑната от екран за вход в Wi-Fi. ИнформациÑта ви продължава да е защитена, тъй като Chromium ÑÐ¿Ñ€Ñ Ð²Ñ€ÑŠÐ·ÐºÐ°Ñ‚Ð°, преди да бъдат обменени данни.</translation>
<translation id="9137013805542155359">Показване на оригинала</translation>
+<translation id="9137248913990643158">МолÑ, Ñтартирайте браузъра Chrome и влезте в него, преди да използвате това приложение.</translation>
<translation id="9148507642005240123">&amp;ОтмÑна на редактирането</translation>
<translation id="9157595877708044936">ÐаÑтройва Ñе...</translation>
<translation id="9170848237812810038">&amp;ОтмÑна</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">ИЗЧИСТВÐÐЕ ÐРФОРМУЛЯРÐ</translation>
<translation id="939736085109172342">Ðова папка</translation>
<translation id="941721044073577244">Изглежда, че нÑмате разрешение да поÑетите този Ñайт</translation>
-<translation id="962701380617707048">Въведете датата на валидноÑÑ‚ и кода за проверка за <ph name="CREDIT_CARD" />, за да актуализирате данните за картата Ñи</translation>
<translation id="969892804517981540">Официално издание</translation>
<translation id="988159990683914416">Компилирана програма за програмиÑти</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 1d34a12b4e4..79be40ae5d1 100644
--- a/chromium/components/strings/components_strings_bn.xtb
+++ b/chromium/components/strings/components_strings_bn.xtb
@@ -21,9 +21,10 @@
<translation id="1132774398110320017">Chrome সà§à¦¬à¦¤à¦ƒà¦ªà§‚রà§à¦£ সেটিংস...</translation>
<translation id="1152921474424827756"><ph name="URL" /> à¦à¦° <ph name="BEGIN_LINK" />কà§à¦¯à¦¾à¦¶à§‡ করা অনà§à¦²à¦¿à¦ªà¦¿<ph name="END_LINK" /> অà§à¦¯à¦¾à¦•à§à¦¸à§‡à¦¸ করà§à¦¨</translation>
<translation id="1158211211994409885"><ph name="HOST_NAME" /> অপà§à¦°à¦¤à§à¦¯à¦¾à¦¶à¦¿à¦¤à¦­à¦¾à¦¬à§‡ সংযোগ বনà§à¦§ করেছে।</translation>
-<translation id="1161325031994447685">ওয়াই-ফাই ঠপà§à¦¨à¦°à¦¾à§Ÿ সংযà§à¦•à§à¦¤ করে দেখà§à¦¨</translation>
+<translation id="1161325031994447685">ওয়াই-ফাই ঠআবার সংযà§à¦•à§à¦¤ করে দেখà§à¦¨</translation>
<translation id="1175364870820465910">&amp;মà§à¦¦à§à¦°à¦£...</translation>
<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="1206967143813997005">নষà§à¦Ÿ পà§à¦°à¦¾à¦¥à¦®à¦¿à¦• সà§à¦¬à¦¾à¦•à§à¦·à¦°</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">নীলাভ</translation>
<translation id="1629803312968146339">আপনি কি চান যে Chrome à¦à¦‡ কারà§à¦¡ সংরকà§à¦·à¦£ করà§à¦•?</translation>
<translation id="1640180200866533862">বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€à¦° নীতিসমূহ</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />à¦à¦‡ সাইটের হোমপেজটি ঘà§à¦°à§‡ দেখà§à¦¨<ph name="END_LINK" />।</translation>
<translation id="1644184664548287040">নেটওয়ারà§à¦• কনফিগারেশনটি অবৈধ à¦à¦¬à¦‚ আমদানি করা যায়নি৷</translation>
<translation id="1644574205037202324">ইতিহাস</translation>
<translation id="1645368109819982629">অসমরà§à¦¥à¦¿à¦¤ পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²</translation>
<translation id="1676269943528358898"><ph name="SITE" /> সাধারণত আপনার তথà§à¦¯ সà§à¦°à¦•à§à¦·à¦¿à¦¤ রাখতে à¦à¦¨à¦•à§à¦°à¦¿à¦ªà¦¶à¦¾à¦¨ বà§à¦¯à¦¬à¦¹à¦¾à¦° করে। à¦à¦‡à¦¬à¦¾à¦° যখন Google Chrome <ph name="SITE" /> à¦à¦° সাথে সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করার চেষà§à¦Ÿà¦¾ করেছে, তখন ওয়েবসাইটটি অসà§à¦¬à¦¾à¦­à¦¾à¦¬à¦¿à¦• à¦à¦¬à¦‚ ভà§à¦² শংসাপতà§à¦° পাঠিয়েছে। হয় à¦à¦•à¦œà¦¨ আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ <ph name="SITE" /> হওয়ার ভান করছে, অথবা কোনো ওয়াই-ফাই পà§à¦°à¦¬à§‡à¦¶ করà§à¦¨ সà§à¦•à§à¦°à§€à¦£ সংযোগকে বাধা দেওয়া হয়েছে। আপনার তথà§à¦¯ à¦à¦–নো নিরাপদ আছে কারণ কোনো ডেটা আদানপà§à¦°à¦¦à¦¾à¦¨à§‡à¦° আগেই Google Chrome সংযোগটিকে বনà§à¦§ করে দিয়েছে।</translation>
+<translation id="168328519870909584">বরà§à¦¤à¦®à¦¾à¦¨à§‡ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ঠথাকা আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ আপনার ডিভাইসে বিপদজনক অà§à¦¯à¦¾à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à¦¾à¦¨ ইনà§à¦¸à¦Ÿà¦² করার চেষà§à¦Ÿà¦¾ করতে পারে, যা আপনার তথà§à¦¯ (উদাহরণসà§à¦¬à¦°à§‚প, ফটো, পাসওয়ারà§à¦¡, বারà§à¦¤à¦¾, à¦à¦¬à¦‚ কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡) চà§à¦°à¦¿ করতে বা মà§à¦›à§‡ দিতে পারে।</translation>
<translation id="168841957122794586">সারà§à¦­à¦¾à¦° শংসাপতà§à¦°à§‡ à¦à¦•à¦Ÿà¦¿ দà§à¦°à§à¦¬à¦² কপিরাইট কী আছে৷</translation>
-<translation id="1701955595840307032">পà§à¦°à¦¸à§à¦¤à¦¾à¦¬à¦¿à¦¤ সামগà§à¦°à§€ পান</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">à¦à¦‡ সাইটে যেতে আপনাকে <ph name="NAME" /> à¦à¦° কাছ থেকে অনà§à¦®à¦¤à¦¿ নিতে হবে</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">পৃষà§à¦ à¦¾ সংখà§à¦¯à¦¾</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows নেটওয়ারà§à¦• ডায়গনিসà§à¦Ÿà¦¿à¦•à§à¦¸ চালিয়ে দেখà§à¦¨<ph name="END_LINK" />।</translation>
<translation id="1783075131180517613">দয়া করে আপনার সিঙà§à¦• পাসফà§à¦°à§‡à¦œ আপডেট করà§à¦¨à§·</translation>
+<translation id="1787142507584202372">আপনার খোলা টà§à¦¯à¦¾à¦¬à¦—à§à¦²à¦¿ à¦à¦–ানে দেখা যাবে</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google নিরাপদ বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚ সমà§à¦ªà§à¦°à¦¤à¦¿ <ph name="SITE" /> ঠ<ph name="BEGIN_LINK" />মালওয়ের শনাকà§à¦¤ করেছে<ph name="END_LINK" />। যেসব ওয়েবসাইট সাধারণত নিরাপদ থাকে, সেগà§à¦²à¦¿ কখনও কখনও মালওয়ের দà§à¦¬à¦¾à¦°à¦¾ আকà§à¦°à¦¾à¦¨à§à¦¤ হয়। à¦à¦•à¦Ÿà¦¿ পরিচিত মালওয়ের বিতরণকারী, <ph name="SUBRESOURCE_HOST" /> থেকে কà§à¦·à¦¤à¦¿à¦•à¦¾à¦°à¦• সামগà§à¦°à§€à¦Ÿà¦¿ à¦à¦¸à§‡à¦›à§‡à¥¤ <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="1821930232296380041">অবৈধ অনà§à¦°à§‹à¦§ বা অনà§à¦°à§‹à¦§ মাপকাঠিগà§à¦²à¦¿</translation>
+<translation id="1834321415901700177">à¦à¦‡ সাইটটিতে কà§à¦·à¦¤à¦¿à¦•à¦° পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® রয়েছে</translation>
<translation id="1838667051080421715">আপনি à¦à¦•à¦Ÿà¦¿ ওয়েব পৃষà§à¦ à¦¾à¦° উৎস কোড দেখছেন।</translation>
<translation id="1871208020102129563">
পà§à¦°à¦•à§à¦¸à¦¿ সà§à¦¥à¦¿à¦° পà§à¦°à¦•à§à¦¸à¦¿ সারà§à¦­à¦¾à¦°à¦—à§à¦²à¦¿ বà§à¦¯à¦¬à¦¹à¦¾à¦° করতে সেট করা আছে কোনো .pac সà§à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ URL নয়৷</translation>
@@ -108,7 +113,6 @@
<translation id="2128531968068887769">নেটিভ কà§à¦²à¦¾à¦¯à¦¼à§‡à¦¨à§à¦Ÿ</translation>
<translation id="213826338245044447">মোবাইল বà§à¦•à¦®à¦¾à¦°à§à¦•</translation>
<translation id="2148716181193084225">আজ</translation>
-<translation id="2149973817440762519">বà§à¦•à¦®à¦¾à¦°à§à¦• সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ করà§à¦¨</translation>
<translation id="2154054054215849342">আপনার ডোমেনের জনà§à¦¯ সিঙà§à¦• উপলবà§à¦§ নেই</translation>
<translation id="2166049586286450108">পূরà§à¦£ পà§à¦°à¦¶à¦¾à¦¸à¦• অà§à¦¯à¦¾à¦•à§à¦¸à§‡à¦¸</translation>
<translation id="2166378884831602661">à¦à¦‡ সাইটটি à¦à¦•à¦Ÿà¦¿ সà§à¦°à¦•à§à¦·à¦¿à¦¤ সংযোগ দিতে পারছে না</translation>
@@ -129,6 +133,7 @@
<translation id="2318774815570432836">আপনি à¦à¦–ন <ph name="SITE" /> ঠযেতে পারবেন না কারণ ওয়েবসাইটটি HSTS বà§à¦¯à¦¬à¦¹à¦¾à¦° করছে। নেটওয়ারà§à¦• তà§à¦°à§à¦Ÿà¦¿ à¦à¦¬à¦‚ আকà§à¦°à¦®à¦£ সাধারণত সাময়িকভাবে হয়ে থাকে, তাই à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà¦¿ সমà§à¦­à¦¬à¦¤ পরে কাজ করবে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="2328300916057834155"><ph name="ENTRY_INDEX" /> সূচিতে অবৈধ বà§à¦•à¦®à¦¾à¦°à§à¦• à¦à§œà¦¿à§Ÿà§‡ যাওয়া হয়েছে</translation>
<translation id="2354001756790975382">অনà§à¦¯ বà§à¦•à¦®à¦¾à¦°à§à¦•à¦¸</translation>
+<translation id="2355395290879513365">আপনি à¦à¦‡ সাইটে যেসব ছবি দেখছেন, আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ সেগà§à¦²à¦¿ দেখতে পেতে পারে à¦à¦¬à¦‚ সেগà§à¦²à¦¿ পরিবরà§à¦¤à¦¨ করে আপনাকে বোকা বানাতে পারে।</translation>
<translation id="2359808026110333948">অবিরত</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> ঠকà§à¦¯à¦¾à¦ªà¦šà¦¾à¦° করা কà§à¦°à§à¦¯à¦¾à¦¶ পà§à¦°à¦¤à¦¿à¦¬à§‡à¦¦à¦¨ আপলোড করা হয়নি</translation>
<translation id="2367567093518048410">সà§à¦¤à¦°</translation>
@@ -139,6 +144,7 @@
<translation id="2392959068659972793">কোনো মান সেট করা নেই à¦à¦®à¦¨ নীতিগà§à¦²à¦¿ দেখান</translation>
<translation id="2396249848217231973">&amp;মà§à¦›à§‡ ফেলাকে পূরà§à¦¬à¦¾à¦¬à¦¸à§à¦¥à¦¾à¦¯à¦¼ ফেরান</translation>
<translation id="2455981314101692989">à¦à¦‡ ওয়েবপৃষà§à¦ à¦¾à¦Ÿà¦¿ à¦à¦‡ ফরà§à¦®à¦Ÿà¦¿à¦° জনà§à¦¯ সà§à¦¬à¦¯à¦¼à¦‚কà§à¦°à¦¿à¦¯à¦¼ পূরণ ফরà§à¦®à¦•à§‡ অকà§à¦·à¦® করেছে৷</translation>
+<translation id="2460160116472764928">Google নিরাপদ বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚ সমà§à¦ªà§à¦°à¦¤à¦¿ <ph name="SITE" /> ঠ<ph name="BEGIN_LINK" />মালওয়ের শনাকà§à¦¤ করেছে<ph name="END_LINK" />। যেসব ওয়েবসাইট সাধারণত নিরাপদ থাকে, সেগà§à¦²à¦¿ কখনও কখনও মালওয়ের দà§à¦¬à¦¾à¦°à¦¾ আকà§à¦°à¦¾à¦¨à§à¦¤ হয়। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="2463739503403862330">পূরণ করà§à¦¨</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />নেটওয়ারà§à¦• ডায়গনিসà§à¦Ÿà¦¿à¦•à§à¦¸ চালান<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">অবৈধ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à§‡à¦° URL৷</translation>
@@ -162,6 +168,7 @@
<translation id="2674170444375937751">আপনি কি আপনার ইতিহাস থেকে à¦à¦‡ পৃষà§à¦ à¦¾à¦—à§à¦²à¦¿ মোছার বিষয়ে নিশà§à¦šà¦¿à¦¤?</translation>
<translation id="2677748264148917807">ছেড়ে চলে যান</translation>
<translation id="269990154133806163">সারà§à¦­à¦¾à¦°à¦Ÿà¦¿ à¦à¦®à¦¨ à¦à¦•à¦Ÿà¦¿ শংসাপতà§à¦° উপসà§à¦¥à¦¾à¦ªà¦¨ করেছে যেটি শংসাপতà§à¦°à§‡à¦° সà§à¦¬à¦šà§à¦›à¦¤à¦¾à¦° নীতি বà§à¦¯à¦¬à¦¹à¦¾à¦° করে সরà§à¦¬à¦œà¦¨à§€à¦¨à¦­à¦¾à¦¬à§‡ পà§à¦°à¦•à¦¾à¦¶ করা হয়নি। à¦à¦Ÿà¦¿ কিছৠশংসাপতà§à¦°à§‡à¦° জনà§à¦¯ à¦à¦•à¦Ÿà¦¿ আবশà§à¦¯à¦•à¦¤à¦¾, যাতে করে সেগà§à¦²à¦¿à¦° বিশà§à¦¬à¦¾à¦¸à¦¯à§‹à¦—à§à¦¯à¦¤à¦¾ নিশà§à¦šà¦¿à¦¤ করা যায় à¦à¦¬à¦‚ আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦¦à§‡à¦° বিরà§à¦¦à§à¦§à§‡ সà§à¦°à¦•à§à¦·à¦¾ নেওয়া যায়।<ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
+<translation id="2702801445560668637">পড়ার তালিকা</translation>
<translation id="2704283930420550640">বিনà§à¦¯à¦¾à¦¸à§‡à¦° সাথে মূলà§à¦¯ মেলে না৷</translation>
<translation id="2704951214193499422">Chromium à¦à¦‡ মà§à¦¹à§‚রà§à¦¤à§‡ আপনার কারà§à¦¡ নিশà§à¦šà¦¿à¦¤ করতে অকà§à¦·à¦® হয়েছে৷ দয়া করে পরে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨à§·</translation>
<translation id="2705137772291741111">à¦à¦‡ সাইটের সংরকà§à¦·à¦¿à¦¤ (সঞà§à¦šà¦¿à¦¤) অনà§à¦²à¦¿à¦ªà¦¿ পড়া সমà§à¦­à¦¬ হয়নি।</translation>
@@ -169,6 +176,7 @@
<translation id="2712118517637785082">আপনি <ph name="DOMAIN" /> ঠপৌà¦à¦›à¦¾à¦¨à§‹à¦° পà§à¦°à¦šà§‡à¦·à§à¦Ÿà¦¾ করেছেন, তবে সারà§à¦­à¦¾à¦°à¦Ÿà¦¿ যে শংসাপতà§à¦°à¦Ÿà¦¿ উপসà§à¦¥à¦¾à¦ªà¦¨ করেছে সেটির ইসà§à¦¯à§à¦•à¦¾à¦°à§€ সেটিকে পà§à¦°à¦¤à§à¦¯à¦¾à¦¹à¦¾à¦° করেছে। à¦à¦° অরà§à¦¥ হ'ল সারà§à¦­à¦¾à¦°à¦Ÿà¦¿ যে সà§à¦°à¦•à§à¦·à¦¾ পà§à¦°à¦®à¦¾à¦¨à¦ªà¦¤à§à¦° উপসà§à¦¥à¦¾à¦ªà¦¨ করেছে তা à¦à¦•à§‡à¦¬à¦¾à¦°à§‡à¦‡ বিশà§à¦¬à¦¾à¦¸à¦¯à§‹à¦—à§à¦¯ নয়। হতে পারে আপনি à¦à¦•à¦œà¦¨ আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦° সাথে যোগাযোগ করছেন। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="2712173769900027643">অনà§à¦®à¦¤à¦¿ নিন</translation>
<translation id="2713444072780614174">সাদা</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>
@@ -180,13 +188,14 @@
<translation id="2826760142808435982"><ph name="CIPHER" /> বà§à¦¯à¦¬à¦¹à¦¾à¦° করে à¦à¦‡ সংযোগটি à¦à¦¨à¦•à§à¦°à¦¿à¦ªà§à¦Ÿà§‡à¦¡ à¦à¦¬à¦‚ পà§à¦°à¦®à¦¾à¦£à§€à¦•à§ƒà¦¤ করা হয়েছে à¦à¦¬à¦‚ কী à¦à¦•à§à¦¸à¦šà§‡à¦žà§à¦œ পà§à¦°à¦•à§à¦°à¦¿à§Ÿà¦¾ হিসাবে <ph name="KX" /> বà§à¦¯à¦¬à¦¹à¦¾à¦° করে৷</translation>
<translation id="2835170189407361413">ফরà§à¦® সাফ করà§à¦¨</translation>
<translation id="284702764277384724"><ph name="HOST_NAME" /> à¦à¦° সারà§à¦­à¦¾à¦° শংসাপতà§à¦°à¦Ÿà¦¿ জাল বলে মনে হচà§à¦›à§‡à¥¤</translation>
-<translation id="2889159643044928134">পà§à¦¨à¦°à¦¾à§Ÿ লোড করবেন না</translation>
+<translation id="2889159643044928134">আবার লোড করবেন না</translation>
<translation id="2900469785430194048">à¦à¦‡ ওয়েবপৃষà§à¦ à¦¾ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করার সময় Google Chrome à¦à¦° মেমরি শেষ হয়ে গেছে।</translation>
<translation id="2909946352844186028">à¦à¦•à¦Ÿà¦¿ নেটওয়ারà§à¦• পরিবরà§à¦¤à¦¨ সনাকà§à¦¤ হয়েছে৷</translation>
<translation id="2922350208395188000">সারà§à¦­à¦¾à¦°à§‡à¦° শংসাপতà§à¦° চেক করা যাবে না৷</translation>
<translation id="2948083400971632585">আপনি সেটিংস পৃষà§à¦ à¦¾ থেকে সংযোগের জনà§à¦¯ কনফিগার করা যেকোনো পà§à¦°à¦•à§à¦¸à¦¿ নিষà§à¦•à§à¦°à¦¿à¦¯à¦¼ করতে পারেন৷</translation>
<translation id="2955913368246107853">খোà¦à¦œ দণà§à¦¡ বনà§à¦§ করà§à¦¨</translation>
<translation id="2958431318199492670">নেটওয়ারà§à¦• কনফিগারেশন ONC মানকের সাথে সমà§à¦®à¦¤ নয়৷ কনফিগারেশনের অংশগà§à¦²à¦¿ আমদানিকৃত নাও হতে পারে৷</translation>
+<translation id="29611076221683977">বরà§à¦¤à¦®à¦¾à¦¨à§‡ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à¦à¦° আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ আপনার Mac ঠকà§à¦·à¦¤à¦¿à¦•à¦¾à¦°à¦• পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® ইনসà§à¦Ÿà¦² করতে পারে বা আপনার তথà§à¦¯ (উদাহরণসà§à¦¬à¦°à§à¦ª, ফটো, পাসওয়ারà§à¦¡, বারà§à¦¤à¦¾ à¦à¦¬à¦‚ কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡) চà§à¦°à¦¿ করতে বা মà§à¦›à§‡ দিতে পারে।</translation>
<translation id="2969319727213777354">নিরাপদ নেটওয়ারà§à¦• সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করতে আপনার ঘড়িকে সঠিকভাবে সেট করতে হবে। à¦à¦®à¦¨ হওয়ার কারণ হলো, নিরাপদ সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করার জনà§à¦¯ নিজেদের সনাকà§à¦¤ করার জনà§à¦¯ ওয়েবসাইটগà§à¦²à¦¿ যে শংসাপতà§à¦°à¦—à§à¦²à¦¿ বà§à¦¯à¦¬à¦¹à¦¾à¦° করে, সেগà§à¦²à¦¿ শà§à¦§à§à¦®à¦¾à¦¤à§à¦° নিরà§à¦¦à¦¿à¦·à§à¦Ÿ সময়ের জনà§à¦¯ বৈধ থাকে। যেহেতৠআপনার ডিভাইসের ঘড়িটি ভà§à¦², সেই জনà§à¦¯ Google Chrome সঠিকভাবে শংসাপতà§à¦°à¦—à§à¦²à¦¿ পরীকà§à¦·à¦¾ করতে পারছে না।</translation>
<translation id="2972581237482394796">&amp;পà§à¦¨à¦°à¦¾à§Ÿ করà§à¦¨</translation>
<translation id="2985306909656435243">সকà§à¦·à¦® করা হলে, ফরà§à¦® পূরনের কাজ দà§à¦°à§à¦¤ করতে Chromium à¦à¦‡ ডিভাইসে আপনার কারà§à¦¡à§‡à¦° à¦à¦•à¦Ÿà¦¿ পà§à¦°à¦¤à¦¿à¦²à¦¿à¦ªà¦¿ সংরকà§à¦·à¦£ করবে।</translation>
@@ -216,7 +225,7 @@
<ph name="PLATFORM_TEXT" /></translation>
<translation id="3202578601642193415">নবীনতম</translation>
<translation id="3207960819495026254">বà§à¦•à¦®à¦¾à¦°à§à¦• করা হয়েছে</translation>
-<translation id="3226128629678568754">পৃষà§à¦ à¦¾à¦Ÿà¦¿ লোড করতে পà§à¦°à§Ÿà§‹à¦œà¦¨à§€à§Ÿ ডেটেটি পà§à¦¨à¦°à¦¾à§Ÿ জমা দিতে পà§à¦¨à¦°à¦¾à§Ÿ লোড করার বোতামটি টিপà§à¦¨à§·</translation>
+<translation id="3226128629678568754">পৃষà§à¦ à¦¾à¦Ÿà¦¿ লোড করতে পà§à¦°à§Ÿà§‹à¦œà¦¨à§€à§Ÿ ডেটেটি আবার জমা দিতে আবার লোড করার বোতামটি টিপà§à¦¨à§·</translation>
<translation id="3228969707346345236">পৃষà§à¦ à¦¾à¦Ÿà¦¿ ইতিমধà§à¦¯à§‡ <ph name="LANGUAGE" />-ঠথাকার কারণে অনà§à¦¬à¦¾à¦¦ বà§à¦¯à¦°à§à¦¥ হয়েছে৷</translation>
<translation id="323107829343500871"><ph name="CREDIT_CARD" /> à¦à¦° CVC লিখà§à¦¨</translation>
<translation id="3254409185687681395">à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà¦¿ বà§à¦•à¦®à¦¾à¦°à§à¦• করà§à¦¨</translation>
@@ -237,7 +246,7 @@
<translation id="3380864720620200369">কà§à¦²à¦¾à§Ÿà§‡à¦¨à§à¦Ÿ ID:</translation>
<translation id="340013220407300675">আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ <ph name="BEGIN_BOLD" /> <ph name="SITE" /> <ph name="END_BOLD" /> (যেমন, পাসওয়ারà§à¦¡, বারà§à¦¤à¦¾ বা কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡) থেকে আপনার তথà§à¦¯ চà§à¦°à¦¿ করার চেষà§à¦Ÿà¦¾ করতে পারে।</translation>
<translation id="3422472998109090673">বরà§à¦¤à¦®à¦¾à¦¨à§‡ <ph name="HOST_NAME" /> পাওয়া যাচà§à¦›à§‡ না।</translation>
-<translation id="3427342743765426898">&amp;সমà§à¦ªà¦¾à¦¦à¦¨à¦¾à¦•à§‡ পà§à¦¨à¦°à¦¾à§Ÿ করà§à¦¨</translation>
+<translation id="3427342743765426898">&amp;সমà§à¦ªà¦¾à¦¦à¦¨à¦¾à¦•à§‡ আবার করà§à¦¨</translation>
<translation id="3435896845095436175">সকà§à¦·à¦® করà§à¦¨</translation>
<translation id="3447661539832366887">à¦à¦‡ ডিভাইসের মালিক ডাইনোসর গেমটি বনà§à¦§ করেছেন৷</translation>
<translation id="3450660100078934250">MasterCard</translation>
@@ -256,22 +265,22 @@
<translation id="3555561725129903880">à¦à¦‡ সারà§à¦­à¦¾à¦° পà§à¦°à¦®à¦¾à¦£ করতে পারেনি যে à¦à¦Ÿà¦¾ <ph name="DOMAIN" />; à¦à¦° নিরাপতà§à¦¤à¦¾ শংসাপতà§à¦°à¦Ÿà¦¿ <ph name="DOMAIN2" /> থেকে à¦à¦¸à§‡à¦›à§‡à¥¤ কোনো ভà§à¦² কনফিগারেশনের কারণে অথবা কোনো আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ আপনার সংযোগ মাà¦à¦ªà¦¥à§‡ আটকে দিচà§à¦›à§‡ বলে à¦à¦®à¦¨à¦Ÿà¦¾ হতে পারে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="3556433843310711081">আপনার পরিচালক আপনার হয়ে à¦à¦Ÿà¦¿ অবরোধ মà§à¦•à§à¦¤ করতে পারবে</translation>
<translation id="3566021033012934673">আপনার সংযোগ বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত নয়</translation>
-<translation id="3583757800736429874">&amp;সরানোর কাজটি পà§à¦¨à¦°à¦¾à§Ÿ করà§à¦¨</translation>
+<translation id="3583757800736429874">&amp;সরানোর কাজটি আবার করà§à¦¨</translation>
<translation id="3586931643579894722">বিশদ বিবরণ লà§à¦•à¦¾à¦¨</translation>
<translation id="3587482841069643663">সকল</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">সারà§à¦­à¦¾à¦°à¦Ÿà¦¿ TLS পà§à¦¨à¦ƒà¦¨à§‡à¦—োশিয়েশান à¦à¦•à§à¦¸à¦Ÿà§‡à¦¨à¦¶à¦¾à¦¨ সমরà§à¦¥à¦¨ করে না৷</translation>
<translation id="36224234498066874">বà§à¦°à¦¾à¦‰à¦œ করা ডেটা সাফ করà§à¦¨...</translation>
<translation id="362276910939193118">সমà§à¦ªà§‚রà§à¦£ ইতিহাস দেখান</translation>
<translation id="3623476034248543066">মান দেখান</translation>
<translation id="3630155396527302611">নেটওয়ারà§à¦• অà§à¦¯à¦¾à¦•à§à¦¸à§‡à¦¸ করতে à¦à¦Ÿà¦¿ যদি ইতোমধà§à¦¯à§‡ মঞà§à¦œà§à¦°à¦¿à¦•à§ƒà¦¤ পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® হিসাবে তালিকাতে থাকে, তাহলে
- তালিকাটি থেকে à¦à¦Ÿà¦¿ সরানোর চেষà§à¦Ÿà¦¾ করে পà§à¦¨à¦°à¦¾à§Ÿ যোগ করে দেখà§à¦¨à¥¤</translation>
+ তালিকাটি থেকে à¦à¦Ÿà¦¿ সরানোর চেষà§à¦Ÿà¦¾ করে আবার যোগ করে দেখà§à¦¨à¥¤</translation>
<translation id="3638794133396384728">à¦à¦‡ সারà§à¦­à¦¾à¦° পà§à¦°à¦®à¦¾à¦£ করতে পারেনি যে à¦à¦Ÿà¦¿ <ph name="DOMAIN" />; à¦à¦° নিরাপতà§à¦¤à¦¾ শংসাপতà§à¦°à¦Ÿà¦¿ মেয়াদোতà§à¦¤à§€à¦°à§à¦£ হয়ে গেছে। কোনো ভà§à¦² কনফিগারেশনের কারণে অথবা কোনো আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ আপনার সংযোগ মাà¦à¦ªà¦¥à§‡ আটকে দিচà§à¦›à§‡ বলে à¦à¦®à¦¨à¦Ÿà¦¾ হতে পারে। আপনার কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡à¦° ঘড়িটি বরà§à¦¤à¦®à¦¾à¦¨à§‡ <ph name="CURRENT_TIME" /> হিসেবে সেট করা আছে। à¦à¦Ÿà¦¿ কি ঠিক আছে? ঠিক না থাকলে, আপনার সিসà§à¦Ÿà§‡à¦® ঘড়িটি ঠিক করে পৃষà§à¦ à¦¾à¦Ÿà¦¿ রিফà§à¦°à§‡à¦¶ করা উচিৎ৷<ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</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="3679803492151881375">কà§à¦°à§à¦¯à¦¾à¦¶ পà§à¦°à¦¤à¦¿à¦¬à§‡à¦¦à¦¨ <ph name="CRASH_TIME" /> ঠকà§à¦¯à¦¾à¦ªà¦šà¦¾à¦° করা হয়েছে, <ph name="UPLOAD_TIME" /> ঠআপলোড করা হয়েছে</translation>
<translation id="3681007416295224113">শংসাপতà§à¦° তথà§à¦¯</translation>
<translation id="3690164694835360974">লগইন সà§à¦°à¦•à§à¦·à¦¿à¦¤ নয়</translation>
<translation id="3693415264595406141">পাসওয়ারà§à¦¡:</translation>
@@ -281,13 +290,13 @@
<translation id="3712624925041724820">লাইসেনà§à¦¸à¦—à§à¦²à¦¿à¦° মেয়াদ শেষ হয়ে গেছে</translation>
<translation id="3714780639079136834">মোবাইল ডেটা বা ওয়াই-ফাই চালৠকরে দেখà§à¦¨</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />পà§à¦°à¦•à§à¦¸à¦¿, ফায়ারওয়াল à¦à¦¬à¦‚ DNS কনফিগারেশন পরীকà§à¦·à¦¾ করে দেখà§à¦¨<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">আপনি যদি আপনার নিরাপতà§à¦¤à¦¾à¦° à¦à§à¦à¦•à¦¿à¦—à§à¦²à¦¿ বà§à¦à¦¤à§‡ পারেন, তাহলে কà§à¦·à¦¤à¦¿à¦•à¦¾à¦°à¦• পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® সরানোর আগে আপনি <ph name="BEGIN_LINK" />অসà§à¦°à¦•à§à¦·à¦¿à¦¤ সাইটে যেতে পারেন<ph name="END_LINK" />।</translation>
<translation id="3739623965217189342">আপনার অনà§à¦²à¦¿à¦ªà¦¿ করা লিঙà§à¦•</translation>
<translation id="375403751935624634">à¦à¦•à¦Ÿà¦¿ সারà§à¦­à¦¾à¦° তà§à¦°à§à¦Ÿà¦¿à¦° কারণে অনà§à¦¬à¦¾à¦¦ বà§à¦¯à¦°à§à¦¥ হয়েছে৷</translation>
<translation id="3759461132968374835">আপনার কাছে সামà§à¦ªà§à¦°à¦¤à¦¿à¦• পà§à¦°à¦¤à¦¿à¦¬à§‡à¦¦à¦¨ করা কোনও কà§à¦°à§à¦¯à¦¾à¦¶ নেই৷ কà§à¦°à§à¦¯à¦¾à¦¶ পà§à¦°à¦¤à¦¿à¦¬à§‡à¦¦à¦¨ অকà§à¦·à¦® থাকাকালীন ঘটা কà§à¦°à§à¦¯à¦¾à¦¶ à¦à¦–ানে উপসà§à¦¥à¦¿à¦¤ হবে না৷</translation>
-<translation id="3788090790273268753">২০১৬ সালে à¦à¦‡ সাইটের শংসাপতà§à¦° মেয়াদোতà§à¦¤à§€à¦°à§à¦£ হবে, à¦à¦¬à¦‚ শংসাপতà§à¦° শৃঙà§à¦–লে SHA-1 বà§à¦¯à¦¬à¦¹à¦¾à¦° করে সà§à¦¬à¦¾à¦•à§à¦·à¦° করা à¦à¦•à¦Ÿà¦¿ শংসাপতà§à¦° রয়েছে।</translation>
<translation id="382518646247711829">যদি আপনি à¦à¦•à¦Ÿà¦¿ পà§à¦°à¦•à§à¦¸à¦¿ সারà§à¦­à¦¾à¦° বà§à¦¯à¦¬à¦¹à¦¾à¦° করেন...</translation>
<translation id="3828924085048779000">ফাà¦à¦•à¦¾ পাসফà§à¦°à§‡à¦œà§‡à¦° অনà§à¦®à¦¤à¦¿ নেই৷</translation>
-<translation id="3845539888601087042">আপনার পà§à¦°à¦¬à§‡à¦¶ করà§à¦¨ করা ডিভাইসগà§à¦²à¦¿ থেকে ইতিহাস দেখাচà§à¦›à§‡à¥¤ <ph name="BEGIN_LINK" />আরো জানà§à¦¨<ph name="END_LINK" />।</translation>
+<translation id="3845539888601087042">আপনার পà§à¦°à¦¬à§‡à¦¶ করা ডিভাইসগà§à¦²à¦¿ থেকে ইতিহাস দেখাচà§à¦›à§‡à¥¤ <ph name="BEGIN_LINK" />আরো জানà§à¦¨<ph name="END_LINK" />।</translation>
<translation id="385051799172605136">ফিরà§à¦¨</translation>
<translation id="3858027520442213535">তারিখ à¦à¦¬à¦‚ সময় আপডেট করà§à¦¨</translation>
<translation id="3884278016824448484">পরসà§à¦ªà¦° বিরোধী ডিভাইস সনাকà§à¦¤à¦•à¦¾à¦°à§€</translation>
@@ -306,6 +315,7 @@
<translation id="4058922952496707368">কী "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">কà§à¦²à¦¾à§Ÿà§‡à¦¨à§à¦Ÿ ও সারà§à¦­à¦¾à¦°à¦Ÿà¦¿ কোনো অভিনà§à¦¨ SSL পà§à¦°à§‹à¦Ÿà§‹à¦•à¦² সংসà§à¦•à¦°à¦£ বা সাইফার সà§à¦¯à§à¦Ÿ সমরà§à¦¥à¦¨ করে না।</translation>
<translation id="4079302484614802869">পà§à¦°à¦•à§à¦¸à¦¿ কনফিগারেশনটি .pac সà§à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ URL-ঠবà§à¦¯à¦¬à¦¹à¦¾à¦° করাতে সেট থাকে সà§à¦¥à¦¿à¦° পà§à¦°à¦•à§à¦¸à¦¿ সারà§à¦­à¦¾à¦°à¦—à§à¦²à¦¿à¦¤à§‡ নয়৷</translation>
+<translation id="4098354747657067197">সামনে ধোà¦à¦•à¦¾à¦¬à¦¾à¦œ সাইট</translation>
<translation id="4103249731201008433">ডিভাইসের কà§à¦°à¦®à¦¿à¦• সংখà§à¦¯à¦¾ অবৈধ</translation>
<translation id="4103763322291513355">নিবারিত URLগà§à¦²à¦¿à¦° তালিকা à¦à¦¬à¦‚ আপনার সিসà§à¦Ÿà§‡à¦® পà§à¦°à¦¶à¦¾à¦¸à¦•à§‡à¦° দà§à¦¬à¦¾à¦°à¦¾ জারি করা অনà§à¦¯à¦¾à¦¨à§à¦¯ নীতিগà§à¦²à¦¿ দেখার জনà§à¦¯ &lt;strong&gt;chrome://policy&lt;/strong&gt; ঠযান৷</translation>
<translation id="4110615724604346410">à¦à¦‡ সারà§à¦­à¦¾à¦° পà§à¦°à¦®à¦¾à¦£ করতে পারেনি যে à¦à¦Ÿà¦¾ <ph name="DOMAIN" />; à¦à¦° নিরাপতà§à¦¤à¦¾ শংসাপতà§à¦°à¦Ÿà¦¿à¦¤à§‡ তà§à¦°à§à¦Ÿà¦¿ রয়েছে। কোনো ভà§à¦² কনফিগারেশনের কারণে অথবা কোনো আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ আপনার সংযোগ মাà¦à¦ªà¦¥à§‡ আটকে দিচà§à¦›à§‡ বলে à¦à¦®à¦¨à¦Ÿà¦¾ হতে পারে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
@@ -318,20 +328,25 @@
<translation id="4169947484918424451">আপনি কি চান যে Chromium à¦à¦‡ কারà§à¦¡ সংরকà§à¦·à¦£ করà§à¦•?</translation>
<translation id="4171400957073367226">খারাপ যাচাইকরণের সà§à¦¬à¦¾à¦•à§à¦·à¦°</translation>
<translation id="4176463684765177261">অকà§à¦·à¦®</translation>
-<translation id="4196861286325780578">&amp;সরানোর কাজটি পà§à¦¨à¦°à¦¾à§Ÿ করà§à¦¨</translation>
+<translation id="4196861286325780578">&amp;সরানোর কাজটি আবার করà§à¦¨</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ফায়ারওয়াল à¦à¦¬à¦‚ অà§à¦¯à¦¾à¦¨à§à¦Ÿà¦¿à¦­à¦¾à¦‡à¦°à¦¾à¦¸ কনফিগারেশন পরীকà§à¦·à¦¾ করে দেখà§à¦¨<ph name="END_LINK" /></translation>
<translation id="4206349416402437184">{COUNT,plural, =0{কিছà§à¦‡ নেই}=1{১টি অà§à¦¯à¦¾à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à¦¾à¦¨ ($1)}=2{২টি অà§à¦¯à¦¾à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à¦¾à¦¨ ($1, $2)}one{#টি অà§à¦¯à¦¾à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à¦¾à¦¨ ($1, $2, $3)}other{#টি অà§à¦¯à¦¾à¦ªà§à¦²à¦¿à¦•à§‡à¦¶à¦¾à¦¨ ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">কà§à¦°à§à¦¯à¦¾à¦¶à§‡à¦¸</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />নেটওয়ারà§à¦• ডায়গনিসà§à¦Ÿà¦¿à¦•à§à¦¸ চালিয়ে দেখà§à¦¨<ph name="END_LINK" />।</translation>
+<translation id="4250431568374086873">à¦à¦‡ সাইটে আপনার সংযোগ সমà§à¦ªà§‚রà§à¦£à¦°à§‚পে নিরাপদ নয়</translation>
<translation id="4250680216510889253">না</translation>
<translation id="425582637250725228">আপনার করা পরিবরà§à¦¤à¦¨à¦—à§à¦²à¦¿ সংরকà§à¦·à¦£ নাও করা হতে পারে।</translation>
<translation id="4258748452823770588">তà§à¦°à§à¦Ÿà¦¿à¦ªà§‚রà§à¦£ সà§à¦¬à¦¾à¦•à§à¦·à¦°</translation>
<translation id="4269787794583293679">(কোনো বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€à¦° নাম নেই)</translation>
+<translation id="4280429058323657511">, মেয়াদ শেষ <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google নিরাপদ বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚ সমà§à¦ªà§à¦°à¦¤à¦¿ <ph name="SITE" /> ঠ<ph name="BEGIN_LINK" />কà§à¦·à¦¤à¦¿à¦•à¦° পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® সনাকà§à¦¤ করেছে<ph name="END_LINK" />। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="4300246636397505754">মূল পà§à¦°à¦¸à§à¦¤à¦¾à¦¬à¦¨à¦¾à¦—à§à¦²à¦¿</translation>
<translation id="4304224509867189079">লগ ইন</translation>
<translation id="432290197980158659">সারà§à¦­à¦¾à¦°à¦Ÿà¦¿ à¦à¦®à¦¨ à¦à¦•à¦Ÿà¦¿ শংসাপতà§à¦° উপসà§à¦¥à¦¾à¦ªà¦¨ করেছে যা বিলà§à¦Ÿ-ইন পà§à¦°à¦¤à§à¦¯à¦¾à¦¶à¦¾à¦—à§à¦²à¦¿à¦° সাথে মিলছে না। à¦à¦‡ পà§à¦°à¦¤à§à¦¯à¦¾à¦¶à¦¾à¦—à§à¦²à¦¿ আপনাকে সà§à¦°à¦•à§à¦·à¦¿à¦¤ করতে কিছৠনিরà§à¦¦à¦¿à¦·à§à¦Ÿ, উচà§à¦š সà§à¦°à¦•à§à¦·à¦¾à¦° ওয়েবসাইটের জনà§à¦¯ অনà§à¦¤à¦°à§à¦­à§à¦•à§à¦¤ করা হয়। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="4325863107915753736">নিবনà§à¦§ খà§à¦à¦œà§‡ পেতে বà§à¦¯à¦°à§à¦¥ হয়েছে</translation>
+<translation id="4326324639298822553">আপনার মেয়াদ শেষের তারিখ পরীকà§à¦·à¦¾ করে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨</translation>
<translation id="4331708818696583467">সà§à¦°à¦•à§à¦·à¦¿à¦¤ নয়</translation>
+<translation id="4356973930735388585">à¦à¦‡ সাইটে আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ আপনার কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡ কà§à¦·à¦¤à¦¿à¦•à¦¾à¦°à¦• পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® ইনসà§à¦Ÿà¦² করতে পারে যা আপনার তথà§à¦¯ (উদাহরণসà§à¦¬à¦°à§à¦ª, ফটো, পাসওয়ারà§à¦¡, বারà§à¦¤à¦¾ à¦à¦¬à¦‚ কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡) চà§à¦°à¦¿ করতে বা মà§à¦›à§‡ দিতে পারে।</translation>
<translation id="4372948949327679948">পà§à¦°à¦¤à§à¦¯à¦¾à¦¶à¦¿à¦¤ <ph name="VALUE_TYPE" /> মান৷</translation>
<translation id="4381091992796011497">বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ নাম:</translation>
<translation id="4394049700291259645">অকà§à¦·à¦®</translation>
@@ -349,6 +364,7 @@
<translation id="4589078953350245614">আপনি <ph name="DOMAIN" /> ঠপৌà¦à¦›à¦¾à¦¨à§‹à¦° পà§à¦°à¦šà§‡à¦·à§à¦Ÿà¦¾ চালিয়েছেন কিনà§à¦¤à§ সারà§à¦­à¦¾à¦°à¦Ÿà¦¿ à¦à¦•à¦Ÿà¦¿ অবৈধ শংসাপতà§à¦° উপসà§à¦¥à¦¾à¦ªà¦¨ করেছে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="4592951414987517459">à¦à¦•à¦Ÿà¦¿ আধà§à¦¨à¦¿à¦• সাইফার সà§à¦¯à§à¦Ÿ বà§à¦¯à¦¬à¦¹à¦¾à¦° করে <ph name="DOMAIN" />-ঠআপনার সংযোগ à¦à¦¨à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ করা হয়েছে।</translation>
<translation id="4594403342090139922">&amp;মà§à¦›à§‡ ফেলাকে পূরà§à¦¬à¦¾à¦¬à¦¸à§à¦¥à¦¾à¦¯à¦¼ ফেরান</translation>
+<translation id="4619615317237390068">অনà§à¦¯à¦¾à¦¨à§à¦¯ ডিভাইসগà§à¦²à¦¿ থেকে টà§à¦¯à¦¾à¦¬</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">আপনি à¦à¦•à¦Ÿà¦¿ à¦à¦•à§à¦¸à¦Ÿà§‡à¦¨à¦¶à¦¾à¦¨ পৃষà§à¦ à¦¾ দেখছেন।</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -357,10 +373,13 @@
<translation id="4726672564094551039">নীতিগà§à¦²à¦¿ পà§à¦¨à¦ƒà¦²à§‹à¦¡ করà§à¦¨</translation>
<translation id="4728558894243024398">পà§à¦²à§à¦¯à¦¾à¦Ÿà¦«à¦°à§à¦®</translation>
<translation id="4744603770635761495">সমà§à¦ªà¦¾à¦¦à¦¨à¦¯à§‹à¦—à§à¦¯ পথ</translation>
+<translation id="4750917950439032686">আপনার তথà§à¦¯ (উদাহরণসà§à¦¬à¦°à§‚প, পাসওয়ারà§à¦¡ বা কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡ নমà§à¦¬à¦°) যখন à¦à¦‡ সাইটে পাঠানো হয় তখন সেটি বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত থাকে।</translation>
<translation id="4756388243121344051">&amp;ইতিহাস</translation>
+<translation id="4759118997339041434">পেমেনà§à¦Ÿ সà§à¦¬à¦¤à¦ƒà¦ªà§‚রà§à¦£à¦•à¦°à¦£ অকà§à¦·à¦® করা হয়েছে</translation>
<translation id="4764776831041365478"><ph name="URL" />-ঠওয়েবপৃষà§à¦ à¦¾à¦Ÿà¦¿ হতে পারে অসà§à¦¥à¦¾à§Ÿà§€à¦­à¦¾à¦¬à§‡ ডাউন আছে অথবা হতে পারে à¦à¦Ÿà¦¿ সà§à¦¥à¦¾à§Ÿà§€à¦­à¦¾à¦¬à§‡ কোনো নতà§à¦¨ ওয়েব ঠিকানাতে সরানো হয়েছে৷</translation>
<translation id="4771973620359291008">à¦à¦•à¦Ÿà¦¿ অজানা তà§à¦°à§à¦Ÿà¦¿ ঘটেছে৷</translation>
<translation id="4800132727771399293">আপনার মেয়াদ শেষের তারিখ à¦à¦¬à¦‚ CVC পরীকà§à¦·à¦¾ করà§à¦¨ à¦à¦¬à¦‚ আবার চেষà§à¦Ÿà¦¾ করà§à¦¨</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">নেটওয়ারà§à¦• তà§à¦°à§à¦Ÿà¦¿</translation>
<translation id="4816492930507672669">পৃষà§à¦ à¦¾à¦¤à§‡ মানানসই</translation>
<translation id="4850886885716139402">দেখà§à¦¨</translation>
@@ -369,6 +388,7 @@
<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="4923417429809017348">à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà¦¿ কোন অজানা ভাষা থেকে <ph name="LANGUAGE_LANGUAGE" />-ঠঅনà§à¦¬à¦¾à¦¦ করা হয়েছে</translation>
+<translation id="4923459931733593730">অরà§à¦¥à¦ªà§à¦°à¦¦à¦¾à¦¨</translation>
<translation id="4926049483395192435">নিরà§à¦¦à¦¿à¦·à§à¦Ÿ করা উচিত৷</translation>
<translation id="495170559598752135">কà§à¦°à¦¿à§Ÿà¦¾à¦¸à¦®à§‚হ</translation>
<translation id="4958444002117714549">তালিকা পà§à¦°à¦¸à¦¾à¦°à¦¿à¦¤ করà§à¦¨</translation>
@@ -396,7 +416,6 @@
<translation id="5181140330217080051">ডাউনলোড হচà§à¦›à§‡</translation>
<translation id="5190835502935405962">বà§à¦•à¦®à¦¾à¦°à§à¦• দণà§à¦¡</translation>
<translation id="5199729219167945352">পরীকà§à¦·à¦¾à¦¦à¦¿</translation>
-<translation id="5199841536747119669">আপনার পà§à¦°à¦¸à§à¦¤à¦¾à¦¬à¦¨à¦¾à¦—à§à¦²à¦¿ à¦à¦–ানে দেখা যাবে</translation>
<translation id="5251803541071282808">কà§à¦²à¦¾à¦‰à¦¡</translation>
<translation id="5277279256032773186">করà§à¦®à¦•à§à¦·à§‡à¦¤à§à¦°à§‡ Chrome বà§à¦¯à¦¬à¦¹à¦¾à¦° করছেন? বà§à¦¯à¦¬à¦¸à¦¾à¦—à§à¦²à§‹ তাদের করà§à¦®à¦šà¦¾à¦°à§€à¦¦à§‡à¦° জনà§à¦¯ Chrome সেটিংস পরিচালনা করতে পারে। আরো জানà§à¦¨</translation>
<translation id="5299298092464848405">নীতি বিশà§à¦²à§‡à¦·à¦£ করার সময় তà§à¦°à§à¦Ÿà¦¿</translation>
@@ -404,8 +423,10 @@
<translation id="5308689395849655368">কà§à¦°à§à¦¯à¦¾à¦¶ পà§à¦°à¦¤à¦¿à¦¬à§‡à¦¦à¦¨ অকà§à¦·à¦® আছে৷</translation>
<translation id="5317780077021120954">সংরকà§à¦·à¦£ করà§à¦¨</translation>
<translation id="5327248766486351172">নাম</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ঠআকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ আপনাকে ধোà¦à¦•à¦¾ দিয়ে সফটওয়à§à¦¯à¦¾à¦° ইনসà§à¦Ÿà¦² করা বা আপনার বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত তথà§à¦¯ (উদাহরণসà§à¦¬à¦°à§‚প, পাসওয়ারà§à¦¡, ফোন নমà§à¦¬à¦°, বা কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡) পà§à¦°à¦•à¦¾à¦¶ করার মত বিপজà§à¦œà¦¨à¦• কিছৠকরাতে পারে।</translation>
<translation id="5359637492792381994">à¦à¦‡ সারà§à¦­à¦¾à¦° পà§à¦°à¦®à¦¾à¦£ করতে পারেনি যে à¦à¦Ÿà¦¾ <ph name="DOMAIN" />; বরà§à¦¤à¦®à¦¾à¦¨à§‡ à¦à¦° নিরাপতà§à¦¤à¦¾ শংসাপতà§à¦°à¦Ÿà¦¿ বৈধ না। কোনো ভà§à¦² কনফিগারেশনের কারণে অথবা কোনো আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ আপনার সংযোগ মাà¦à¦ªà¦¥à§‡ আটকে দিচà§à¦›à§‡ বলে à¦à¦®à¦¨à¦Ÿà¦¾ হতে পারে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="536296301121032821">নীতি সেটিংস সংরকà§à¦·à¦£ করতে বà§à¦¯à¦°à§à¦¥ হয়েছে</translation>
+<translation id="5386426401304769735">à¦à¦‡ সাইটের শংসাপতà§à¦° শৃঙà§à¦–লে SHA-1 বà§à¦¯à¦¬à¦¹à¦¾à¦° করে সà§à¦¬à¦¾à¦•à§à¦·à¦° করা à¦à¦•à¦Ÿà¦¿ শংসাপতà§à¦° রয়েছে।</translation>
<translation id="5421136146218899937">বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚ ডেটা সাফ করà§à¦¨...</translation>
<translation id="5430298929874300616">বà§à¦•à¦®à¦¾à¦°à§à¦• সরান</translation>
<translation id="5431657950005405462">আপনার ফাইলটি পাওয়া যায়নি</translation>
@@ -414,7 +435,7 @@
<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="5470861586879999274">&amp;সমà§à¦ªà¦¾à¦¦à¦¨à¦¾à¦•à§‡ পà§à¦¨à¦°à¦¾à§Ÿ করà§à¦¨</translation>
+<translation id="5470861586879999274">&amp;সমà§à¦ªà¦¾à¦¦à¦¨à¦¾à¦•à§‡ আবার করà§à¦¨</translation>
<translation id="5492298309214877701">কোমà§à¦ªà¦¾à¦¨à§€, সংসà§à¦¥à¦¾ বা সà§à¦•à§à¦² ইনà§à¦Ÿà§à¦°à¦¾à¦¨à§‡à¦Ÿà§‡ à¦à¦‡ সাইটটির à¦à¦•à¦Ÿà¦¿ বাহà§à¦¯à¦¿à¦• ওয়েবসাইটের মতো à¦à¦•à¦‡ URL আছে।
<ph name="LINE_BREAK" />
আপনার সিসà§à¦Ÿà§‡à¦® পà§à¦°à¦¶à¦¾à¦¸à¦•à§‡à¦° সাথে যোগাযোগের চেষà§à¦Ÿà¦¾ করà§à¦¨à¥¤</translation>
@@ -424,19 +445,22 @@
<translation id="552553974213252141">পাঠà§à¦¯ কি সঠিকভাবে পà§à¦°à¦¾à¦ªà§à¦¤ হয়েছে?</translation>
<translation id="5540224163453853">অনà§à¦°à§‹à¦§à¦•à§ƒà¦¤ নিবনà§à¦§ খà§à¦à¦œà§‡ পাওয়া যায়নি৷</translation>
<translation id="5544037170328430102"><ph name="SITE" /> ঠà¦à¦•à¦Ÿà¦¿ à¦à¦®à§à¦¬à§‡à¦¡à§‡à¦¡ পৃষà§à¦ à¦¾ বলছে:</translation>
-<translation id="5556459405103347317">পà§à¦¨à¦°à¦¾à§Ÿ লোড করà§à¦¨</translation>
+<translation id="5556459405103347317">আবার লোড করà§à¦¨</translation>
<translation id="5565735124758917034">সকà§à¦°à¦¿à§Ÿ</translation>
+<translation id="5572851009514199876">আপনার à¦à¦‡ সাইটে অà§à¦¯à¦¾à¦•à§à¦¸à§‡à¦¸ করার অনà§à¦®à¦¤à¦¿ আছে কিনা তা Chrome পরীকà§à¦·à¦¾ করার জনà§à¦¯ অনà§à¦—à§à¦°à¦¹ করে শà§à¦°à§ করà§à¦¨ à¦à¦¬à¦‚ Chrome ঠপà§à¦°à¦¬à§‡à¦¶ করà§à¦¨à¥¤</translation>
+<translation id="5580958916614886209">আপনার মেয়াদ শেষের মাস পরীকà§à¦·à¦¾ করে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨</translation>
<translation id="560412284261940334">পরিচালনা সমরà§à¦¥à¦¿à¦¤ নয়</translation>
<translation id="5610142619324316209">সংযোগ পরীকà§à¦·à¦¾ করে দেখà§à¦¨</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> আপনাকে অনেক বেশিবার পà§à¦¨à¦ƒà¦¨à¦¿à¦°à§à¦¦à§‡à¦¶à¦¿à¦¤ করেছে।</translation>
<translation id="5622887735448669177">আপনি কি à¦à¦‡ সাইটটি ছেড়ে যেতে চান?</translation>
<translation id="5629630648637658800">নীতি সেটিংস লোড করতে বà§à¦¯à¦°à§à¦¥ হয়েছে</translation>
<translation id="5631439013527180824">অবৈধ ডিভাইস পরিচালনা টোকেন</translation>
+<translation id="5669703222995421982">বà§à¦¯à¦•à§à¦¤à¦¿à¦—তকৃত সামগà§à¦°à§€ পান</translation>
+<translation id="5675650730144413517">à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà¦¿ কাজ করছে না</translation>
<translation id="5677928146339483299">অবরà§à¦¦à§à¦§</translation>
<translation id="5694783966845939798">আপনি <ph name="DOMAIN" /> ঠপৌà¦à¦›à¦¾à¦¨à§‹à¦° চেষà§à¦Ÿà¦¾ করেছেন, কিনà§à¦¤à§ সারà§à¦­à¦¾à¦°à¦Ÿà¦¿ à¦à¦•à¦Ÿà¦¿ দà§à¦°à§à¦¬à¦² সà§à¦¬à¦¾à¦•à§à¦·à¦° অà§à¦¯à¦¾à¦²à¦—রিদম (যেমন SHA-1) বà§à¦¯à¦¬à¦¹à¦¾à¦° করে à¦à¦•à¦Ÿà¦¿ সà§à¦¬à¦¾à¦•à§à¦·à¦°à¦¿à¦¤ শংসাপতà§à¦° উপসà§à¦¥à¦¾à¦ªà¦¨ করেছে। à¦à¦° অরà§à¦¥ হ'ল সারà§à¦­à¦¾à¦° যে সà§à¦°à¦•à§à¦·à¦¾ শংসাপতà§à¦° উপসà§à¦¥à¦¾à¦ªà¦¨ করেছে তা জাল হতে পারে à¦à¦¬à¦‚ সারà§à¦­à¦¾à¦°à¦Ÿà¦¿ আপনার পà§à¦°à¦¤à§à¦¯à¦¾à¦¶à¦¿à¦¤ সারà§à¦­à¦¾à¦° নাও হতে পারে (হতে পারে আপনি à¦à¦•à¦œà¦¨ আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦° সাথে যোগাযোগ করছেন)। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="5710435578057952990">à¦à¦‡ ওয়েবসাইটির পরিচয় যাচাই করা হয় নি৷</translation>
<translation id="5720705177508910913">বরà§à¦¤à¦®à¦¾à¦¨ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€</translation>
-<translation id="572328651809341494">সামà§à¦ªà§à¦°à¦¤à¦¿à¦• টà§à¦¯à¦¾à¦¬à¦—à§à¦²à¦¿</translation>
<translation id="5732392974455271431">আপনার পিতামাতা à¦à¦Ÿà¦¿ আপনার জনà§à¦¯ অবরোধ মà§à¦•à§à¦¤ করতে পারবেন</translation>
<translation id="5784606427469807560">আপনার কারà§à¦¡à¦Ÿà¦¿ নিশà§à¦šà¦¿à¦¤ করতে à¦à¦•à¦Ÿà¦¿ সমসà§à¦¯à¦¾ হয়েছিল৷আপনার ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ সংযোগ পরীকà§à¦·à¦¾ করে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨à§·</translation>
<translation id="5785756445106461925">উপরনà§à¦¤à§, à¦à¦‡ পৃষà§à¦ à¦¾à¦¤à§‡ অনà§à¦¯à¦¾à¦¨à§à¦¯ সংসà§à¦¥à¦¾à¦¨ অনà§à¦¤à¦°à§à¦­à§à¦•à§à¦¤ রয়েছে যা নিরাপদ নয়৷ à¦à¦‡ সংসà§à¦¥à¦¾à¦¨à¦—à§à¦²à¦¿ টà§à¦°à¦¾à¦¨à¦œà¦¿à¦Ÿà§‡à¦° সময় অনà§à¦¯à¦°à¦¾ দেখতে পাবে à¦à¦¬à¦‚ পৃষà§à¦ à¦¾à¦Ÿà¦¿à¦° চেহারাটি পরিবরà§à¦¤à¦¨ করতে কোনও আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ à¦à¦° পরিবরà§à¦¤à¦¨ করতে পারেন৷</translation>
@@ -445,6 +469,7 @@
<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="5843436854350372569">আপনি <ph name="DOMAIN" /> ঠপৌà¦à¦›à¦¾à¦¨à§‹à¦° পà§à¦°à¦šà§‡à¦·à§à¦Ÿà¦¾ করেছেন, কিনà§à¦¤à§ সারà§à¦­à¦¾à¦° à¦à¦•à¦Ÿà¦¿ দà§à¦°à§à¦¬à¦² কী সমà§à¦¬à¦²à¦¿à¦¤ শংসাপতà§à¦° উপসà§à¦¥à¦¾à¦ªà¦¨ করেছে৷ কোনো আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত কী ভঙà§à¦— করে থাকতে পারে à¦à¦¬à¦‚ সারà§à¦­à¦¾à¦°à¦Ÿà¦¿ আপনার পà§à¦°à¦¤à§à¦¯à¦¾à¦¶à¦¿à¦¤ সারà§à¦­à¦¾à¦° নাও হতে পারে (হতে পারে আপনি à¦à¦•à¦œà¦¨ আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦° সাথে যোগাযোগ করছেন)। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="5857090052475505287">নতà§à¦¨ ফোলà§à¦¡à¦¾à¦°</translation>
<translation id="5869405914158311789">à¦à¦‡ সাইটটিতে পৌছানো যাচà§à¦›à§‡ না</translation>
@@ -454,6 +479,7 @@
<translation id="59107663811261420">à¦à¦‡ বণিকের জনà§à¦¯ à¦à¦‡ ধরনের কারà§à¦¡ Google Payments দà§à¦¬à¦¾à¦°à¦¾ সমরà§à¦¥à¦¿à¦¤ নয়৷ দয়া করে à¦à¦•à¦Ÿà¦¿ আলাদা কারà§à¦¡ নিরà§à¦¬à¦¾à¦šà¦¨ করà§à¦¨à§·</translation>
<translation id="59174027418879706">সকà§à¦·à¦® করা হযেছে</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="5966707198760109579">সপà§à¦¤à¦¾à¦¹</translation>
<translation id="5967867314010545767">ইতিহাস থেকে সরান</translation>
<translation id="5975083100439434680">জà§à¦® কমান</translation>
@@ -467,13 +493,16 @@
<translation id="6042308850641462728">আরো</translation>
<translation id="6060685159320643512">সাবধান হন, à¦à¦‡ পরীকà§à¦·à¦¾à¦—à§à¦²à¦¿ সমসà§à¦¯à¦¾ সৃষà§à¦Ÿà¦¿ করতে পারে</translation>
<translation id="6108835911243775197">{COUNT,plural, =0{কিছà§à¦‡ নেই}=1{১}one{#}other{#}}</translation>
-<translation id="6146055958333702838">সব কেবল পরীকà§à¦·à¦¾ করà§à¦¨ à¦à¦¬à¦‚ আপনি বà§à¦¯à¦¬à¦¹à¦¾à¦° করছেন à¦à¦®à¦¨ যেকোনো রাউটার, মডেম বা অনà§à¦¯à¦¾à¦¨à§à¦¯ নেটওয়ারà§à¦• ডিভাইসগà§à¦²à¦¿ পà§à¦¨à¦°à¦¾à§Ÿ চালৠকরà§à¦¨à¥¤</translation>
+<translation id="6146055958333702838">সব কেবল পরীকà§à¦·à¦¾ করà§à¦¨ à¦à¦¬à¦‚ আপনি বà§à¦¯à¦¬à¦¹à¦¾à¦° করছেন à¦à¦®à¦¨ যেকোনো রাউটার, মডেম বা অনà§à¦¯à¦¾à¦¨à§à¦¯ নেটওয়ারà§à¦• ডিভাইসগà§à¦²à¦¿ আবার চালৠকরà§à¦¨à¥¤</translation>
<translation id="614940544461990577">à¦à¦Ÿà¦¿ করে দেখà§à¦¨:</translation>
<translation id="6151417162996330722">সারà§à¦­à¦¾à¦°à§‡à¦° শংসাপতà§à¦°à§‡à¦° বৈধতার সময়সীমা আছে যা খà§à¦¬à¦‡ দীরà§à¦˜à¥¤</translation>
<translation id="6165508094623778733">আরো জানà§à¦¨</translation>
+<translation id="6177128806592000436">à¦à¦‡ সাইটে আপনার সংযোগ নিরাপদ নয়</translation>
<translation id="6203231073485539293">আপনার ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ সংযোগ পরীকà§à¦·à¦¾ করà§à¦¨</translation>
<translation id="6218753634732582820">Chromium থেকে ঠিকানা সরাবেন?</translation>
-<translation id="6259156558325130047">&amp;পà§à¦¨à¦°à§à¦¬à¦¿à¦¨à§à¦¯à¦¾à¦¸à¦•à§‡ পà§à¦¨à¦°à¦¾à§Ÿ করà§à¦¨</translation>
+<translation id="6251924700383757765">গোপনীয়তা নীতি</translation>
+<translation id="625755898061068298">আপনি à¦à¦‡ সাইটের জনà§à¦¯ নিরাপতà§à¦¤à¦¾ সতরà§à¦•à¦¬à¦¾à¦°à§à¦¤à¦¾ অকà§à¦·à¦® করতে চয়ন করেছেন।</translation>
+<translation id="6259156558325130047">&amp;পà§à¦¨à¦°à§à¦¬à¦¿à¦¨à§à¦¯à¦¾à¦¸à¦•à§‡ আবার করà§à¦¨</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> বà§à¦•à¦®à¦¾à¦°à§à¦•à¦—à§à¦²à¦¿</translation>
<translation id="6264485186158353794">সà§à¦°à¦•à§à¦·à¦¾à¦¤à§‡ ফিরà§à¦¨</translation>
<translation id="6282194474023008486">পোসà§à¦Ÿà¦¾à¦² কোড</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> ঠপৌà¦à¦›à¦¾à¦¨à§‹ যাচà§à¦›à§‡ না</translation>
<translation id="6321917430147971392">আপনার DNS সেটিংস পরীকà§à¦·à¦¾ করà§à¦¨</translation>
<translation id="6328639280570009161">নেটওয়ারà§à¦• পূরà§à¦¬à¦¾à¦¨à§à¦®à¦¾à¦¨ নিষà§à¦•à§à¦°à¦¿à¦¯à¦¼ করার চেষà§à¦Ÿà¦¾ করà§à¦¨</translation>
+<translation id="6328786501058569169">à¦à¦‡ সাইটটি পà§à¦°à¦¤à¦¾à¦°à¦£à¦¾à¦®à§‚লক</translation>
<translation id="6337534724793800597">নাম অনà§à¦¸à¦¾à¦°à§‡ ফিলà§à¦Ÿà¦¾à¦°à¦—à§à¦²à¦¿ বাছাই করà§à¦¨</translation>
<translation id="6342069812937806050">à¦à¦–নই</translation>
<translation id="6345221851280129312">অজানা আকার</translation>
@@ -489,6 +519,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{আরো ১টি পà§à¦°à¦¸à§à¦¤à¦¾à¦¬}one{অনà§à¦¯à¦¾à¦¨à§à¦¯ #টি পà§à¦°à¦¸à§à¦¤à¦¾à¦¬}other{অনà§à¦¯à¦¾à¦¨à§à¦¯ #টি পà§à¦°à¦¸à§à¦¤à¦¾à¦¬}}</translation>
<translation id="6387478394221739770">Chrome à¦à¦° নতà§à¦¨ দà§à¦°à§à¦¦à¦¾à¦¨à§à¦¤ বৈশিষà§à¦Ÿà§à¦¯à¦—à§à¦²à¦¿à¦¤à§‡ আগà§à¦°à¦¹à§€? chrome.com/beta ঠআমাদের বিটা চà§à¦¯à¦¾à¦¨à§‡à¦² বà§à¦¯à¦¬à¦¹à¦¾à¦° করে দেখà§à¦¨à§·</translation>
<translation id="6389758589412724634">à¦à¦‡ ওয়েবপৃষà§à¦ à¦¾ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করার সময় Chromium à¦à¦° মেমরি শেষ হয়ে গেছে।</translation>
+<translation id="6404511346730675251">বà§à¦•à¦®à¦¾à¦°à§à¦• সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ করà§à¦¨</translation>
+<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> à¦à¦° মেয়াদ শেষের তারিখ à¦à¦¬à¦‚ CVC লিখà§à¦¨</translation>
<translation id="6414888972213066896">à¦à¦‡ সাইটটি ঘà§à¦°à§‡ দেখা ঠিক হবে কিনা সেই বিষয়ে আপনি আপনার পিতামাতাকে জিজà§à¦žà¦¾à¦¸à¦¾ করেছেন</translation>
<translation id="6416403317709441254">আপনি <ph name="SITE" /> ঠযেতে পারবেন না কারণ ওয়েবসাইটটি à¦à¦•à¦Ÿà¦¿ অবোধà§à¦¯ শংসাপতà§à¦° পাঠিয়েছে যেটি Chromium পà§à¦°à¦•à§à¦°à¦¿à§Ÿà¦¾ করতে পারছে না। নেটওয়ারà§à¦• তà§à¦°à§à¦Ÿà¦¿ à¦à¦¬à¦‚ আকà§à¦°à¦®à¦£ সাধারণত সাময়িকভাবে হয়ে থাকে, তাই à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà¦¿ সমà§à¦­à¦¬à¦¤ পরে কাজ করবে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="6417515091412812850">শংসাপতà§à¦°à¦•à¦°à¦£à¦Ÿà¦¿ পà§à¦°à¦¤à§à¦¯à¦¾à¦¹à¦¾à¦° করা হয়েছে কিনা তা যাচাইয়ে অকà§à¦·à¦®à§·</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">নীতি দà§à¦¬à¦¾à¦°à¦¾ ডিফলà§à¦Ÿ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ অকà§à¦·à¦® করা হয়েছে সেই কারণে উপেকà§à¦·à¦¾ করা হয়েছে৷</translation>
<translation id="6462969404041126431">à¦à¦‡ সারà§à¦­à¦¾à¦° পà§à¦°à¦®à¦¾à¦£ করতে পারেনি যে à¦à¦Ÿà¦¾ <ph name="DOMAIN" />; à¦à¦° নিরাপতà§à¦¤à¦¾ শংসাপতà§à¦°à¦Ÿà¦¿ হয়ত পà§à¦°à¦¤à§à¦¯à¦¾à¦¹à¦¾à¦° করা হয়েছে। কোনো ভà§à¦² কনফিগারেশনের কারণে অথবা কোনো আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ আপনার সংযোগ মাà¦à¦ªà¦¥à§‡ আটকে দিচà§à¦›à§‡ বলে à¦à¦®à¦¨à¦Ÿà¦¾ হতে পারে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="647261751007945333">ডিভাইস নীতিগà§à¦²à¦¿</translation>
+<translation id="6477321094435799029">Chrome à¦à¦‡ পৃষà§à¦ à¦¾à¦¤à§‡ অসà§à¦¬à¦¾à¦­à¦¾à¦¬à¦¿à¦• কোড পেয়েছে à¦à¦¬à¦‚ আপনার বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত তথà§à¦¯à§‡à¦° (উদাহরণসà§à¦¬à¦°à§‚প, পাসওয়ারà§à¦¡, ফোন নমà§à¦¬à¦°, à¦à¦¬à¦‚ কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡) সà§à¦°à¦•à§à¦·à¦¾à¦° জনà§à¦¯ à¦à¦Ÿà¦¿ অবরà§à¦¦à§à¦§ করেছে।</translation>
<translation id="6489534406876378309">কà§à¦°à§à¦¯à¦¾à¦¶à¦—à§à¦²à¦¿ আপলোড করা শà§à¦°à§ করà§à¦¨</translation>
-<translation id="6529602333819889595">&amp;মà§à¦›à§‡ ফেলাকে পà§à¦¨à¦°à¦¾à§Ÿ করà§à¦¨</translation>
+<translation id="6529602333819889595">&amp;মà§à¦›à§‡ ফেলাকে আবার করà§à¦¨</translation>
<translation id="6534179046333460208">বাসà§à¦¤à¦¬à¦¿à¦• ওয়েব পà§à¦°à¦¸à§à¦¤à¦¾à¦¬à¦¨à¦¾à¦—à§à¦²à¦¿</translation>
<translation id="6550675742724504774">বিকলà§à¦ªà¦¸à¦®à§‚হ</translation>
+<translation id="6556239504065605927">সà§à¦°à¦•à§à¦·à¦¿à¦¤ সংযোগ</translation>
<translation id="6563469144985748109">আপনার পরিচালক à¦à¦–নও à¦à¦Ÿà¦¿ অনà§à¦®à§‹à¦¦à¦¨ করেন নি</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" /> à¦à¦° কম</translation>
<translation id="6596325263575161958">à¦à¦¨à¦•à§à¦°à¦¿à¦ªà¦¶à¦¨ বিকলà§à¦ªà¦—à§à¦²à¦¿</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">à¦à¦‡ নীতিটি অসমরà§à¦¥à¦¿à¦¤ হয়েছে৷</translation>
<translation id="6652240803263749613">à¦à¦‡ সারà§à¦­à¦¾à¦° পà§à¦°à¦®à¦¾à¦£ করতে পারেনি যে à¦à¦Ÿà¦¾ <ph name="DOMAIN" />; আপনার কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡à¦° অপারেটিং সিসà§à¦Ÿà§‡à¦® à¦à¦° নিরাপতà§à¦¤à¦¾ শংসাপতà§à¦°à¦Ÿà¦¿à¦•à§‡ বিশà§à¦¬à¦¾à¦¸ করে না। কোনো ভà§à¦² কনফিগারেশনের কারণে অথবা কোনো আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ আপনার সংযোগ মাà¦à¦ªà¦¥à§‡ আটকে দিচà§à¦›à§‡ বলে à¦à¦®à¦¨à¦Ÿà¦¾ হতে পারে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="6656103420185847513">ফোলà§à¦¡à¦¾à¦° সমà§à¦ªà¦¾à¦¦à¦¨à¦¾ করà§à¦¨</translation>
-<translation id="6660210980321319655">সà§à¦¬à¦¯à¦¼à¦‚কà§à¦°à¦¿à¦¯à¦¼à¦­à¦¾à¦¬à§‡ পà§à¦°à¦¤à¦¿à¦¬à§‡à¦¦à¦¨ করা হয়েছে <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Chromium থেকে ফরà§à¦® পà§à¦°à¦¸à§à¦¤à¦¾à¦¬à¦¨à¦¾ সরাবেন?</translation>
<translation id="6685834062052613830">পà§à¦°à¦¸à§à¦¥à¦¾à¦¨ করà§à¦¨ করে সেটআপ সমà§à¦ªà§‚রà§à¦£ করà§à¦¨</translation>
<translation id="6710213216561001401">পূরà§à¦¬à¦¬à¦°à§à¦¤à§€</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">নীতি মান</translation>
<translation id="6757797048963528358">আপনার ডিভাইস নিদà§à¦°à¦¾ মোডে গিয়েছে।</translation>
<translation id="6778737459546443941">আপনার পিতামাতা à¦à¦–নও à¦à¦Ÿà¦¿ অনà§à¦®à§‹à¦¦à¦¨ করেন নি</translation>
+<translation id="6810899417690483278">কাসà§à¦Ÿà¦®à¦¾à¦‡à¦œà§‡à¦¶à¦¨ আইডি</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"> <ph name="URL" />তে থাকা ওয়েবপৃষà§à¦ à¦¾à¦Ÿà¦¿ বরà§à¦¤à¦®à¦¾à¦¨à§‡ অনà§à¦ªà¦²à¦¬à§à¦§à§· রকà§à¦·à¦£à¦¾à¦¬à§‡à¦•à§à¦·à¦£à§‡à¦° জনà§à¦¯ à¦à¦Ÿà¦¿ ওভারলোড বা ডাউন হয়ে থাকতে পারে৷</translation>
<translation id="6831043979455480757">অনà§à¦¬à¦¾à¦¦</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">পরিচিতি তথà§à¦¯</translation>
+<translation id="7079718277001814089">à¦à¦‡ সাইটে মালওয়ের আছে</translation>
<translation id="7087282848513945231">দেশ</translation>
<translation id="7088615885725309056">পূরà§à¦¬à¦¤à¦°</translation>
<translation id="7090678807593890770">Google ঠ<ph name="LINK" /> à¦à¦° অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ করà§à¦¨</translation>
@@ -556,13 +592,14 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> নিরাপতà§à¦¤à¦¾ মান মেনে চলে না।</translation>
<translation id="721197778055552897">à¦à¦‡ সমসà§à¦¯à¦¾à¦Ÿà¦¿ সমà§à¦ªà¦°à§à¦•à§‡ <ph name="BEGIN_LINK" />আরো জানà§à¦¨<ph name="END_LINK" />৷</translation>
<translation id="7219179957768738017">à¦à¦‡ সংযোগটি <ph name="SSL_VERSION" />টি বà§à¦¯à¦¬à¦¹à¦¾à¦° করে</translation>
+<translation id="724691107663265825">à¦à¦‡ সাইটটিতে মà§à¦¯à¦¾à¦²à¦“য়à§à¦¯à¦¾à¦° আছে</translation>
<translation id="724975217298816891">আপনার কারà§à¦¡à§‡à¦° বিবরণ আপডেট করার জনà§à¦¯ মেয়াদ শেষের তারিখ à¦à¦¬à¦‚ <ph name="CREDIT_CARD" /> à¦à¦° CVC লিখà§à¦¨à¥¤ আপনি নিশà§à¦šà¦¿à¦¤ করলে, আপনার কারà§à¦¡à§‡à¦° বিবরণ à¦à¦‡ সাইটের সাথে শেয়ার করা হবে।</translation>
<translation id="725866823122871198"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ঠà¦à¦•à¦Ÿà¦¿ বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করা যায়নি কারণ আপনার কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡à¦° তারিখ à¦à¦¬à¦‚ সময় (<ph name="DATE_AND_TIME" />) সঠিক নয়৷</translation>
<translation id="7269802741830436641">à¦à¦‡ ওয়েবপৃষà§à¦ à¦¾à¦Ÿà¦¿à¦° à¦à¦•à¦Ÿà¦¿ পà§à¦¨à¦ƒà¦šà¦¾à¦²à¦¨à¦¾ লà§à¦ª আছে</translation>
<translation id="7275334191706090484">পরিচালিত বà§à¦•à¦®à¦¾à¦°à§à¦•à¦—à§à¦²à¦¿</translation>
<translation id="7298195798382681320">পà§à¦°à¦¸à§à¦¤à¦¾à¦¬à¦¿à¦¤</translation>
<translation id="7309308571273880165"><ph name="CRASH_TIME" /> ঠকà§à¦°à§à¦¯à¦¾à¦¶ পà§à¦°à¦¤à¦¿à¦¬à§‡à¦¦à¦¨ কà§à¦¯à¦¾à¦ªà¦šà¦¾à¦° করা হয়েছে (বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ দà§à¦¬à¦¾à¦°à¦¾ আপলোডের অনà§à¦°à§‹à¦§ করা হয়েছে, à¦à¦–নও আপলোড করা হয়নি)</translation>
-<translation id="7334320624316649418">&amp;পà§à¦¨à¦°à§à¦¬à¦¿à¦¨à§à¦¯à¦¾à¦¸à¦•à§‡ পà§à¦¨à¦°à¦¾à§Ÿ করà§à¦¨</translation>
+<translation id="7334320624316649418">&amp;পà§à¦¨à¦°à§à¦¬à¦¿à¦¨à§à¦¯à¦¾à¦¸à¦•à§‡ আবার করà§à¦¨</translation>
<translation id="733923710415886693">সারà§à¦­à¦¾à¦°à§‡à¦° শংসাপতà§à¦°à¦Ÿà¦¿ শংসাপতà§à¦°à§‡à¦° সà§à¦¬à¦šà§à¦›à¦¤à¦¾à¦° মাধà§à¦¯à¦®à§‡ পà§à¦°à¦•à¦¾à¦¶ করা হয়নি।</translation>
<translation id="7351800657706554155">আপনি à¦à¦–ন <ph name="SITE" /> ঠযেতে পারবেন না কারণ à¦à¦‡ শংসাপতà§à¦° পà§à¦°à¦¤à§à¦¯à¦¾à¦¹à¦¾à¦° করা হয়েছে। নেটওয়ারà§à¦• তà§à¦°à§à¦Ÿà¦¿ à¦à¦¬à¦‚ আকà§à¦°à¦®à¦£ সাধারণত সাময়িকভাবে হয়ে থাকে, তাই à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà¦¿ সমà§à¦­à¦¬à¦¤ পরে কাজ করবে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="7353601530677266744">কমà§à¦¯à¦¾à¦¨à§à¦¡ লাইন</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">বাতিল</translation>
<translation id="7667346355482952095">ফিরে পাওয়া নীতির টোকেন খালি অথবা বরà§à¦¤à¦®à¦¾à¦¨ টোকেনের সঙà§à¦—ে মেলে না</translation>
<translation id="7668654391829183341">অজানা ডিভাইস</translation>
+<translation id="7669271284792375604">à¦à¦‡ সাইটে থাকা আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ কৌশলে আপনাকে দিয়ে à¦à¦®à¦¨ পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® ইনসà§à¦Ÿà¦² করাতে পারে যা আপনার বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚ অভিজà§à¦žà¦¤à¦¾à¦° জনà§à¦¯ কà§à¦·à¦¤à¦¿à¦•à¦° হতে পারে (উদাহরণসà§à¦¬à¦°à§‚প, আপনার হোমপৃষà§à¦ à¦¾ পরিবরà§à¦¤à¦¨ করা বা আপনার পরিদরà§à¦¶à¦¿à¦¤ সাইটগà§à¦²à¦¿à¦¤à§‡ অতিরিকà§à¦¤ বিজà§à¦žà¦¾à¦ªà¦¨ দেখানো)৷</translation>
<translation id="7674629440242451245">Chrome à¦à¦° নতà§à¦¨ দà§à¦°à§à¦¦à¦¾à¦¨à§à¦¤ বৈশিষà§à¦Ÿà§à¦¯à¦—à§à¦²à¦¿à¦¤à§‡ আগà§à¦°à¦¹à§€? chrome.com/dev ঠআমাদের ডেভ চà§à¦¯à¦¾à¦¨à§‡à¦² বà§à¦¯à¦¬à¦¹à¦¾à¦° করে দেখà§à¦¨à§·</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /> <ph name="SITE" /> ঠà¦à¦—িয়ে যান (নিরাপদ নয়) <ph name="END_LINK" /></translation>
<translation id="7716424297397655342">à¦à¦‡ সাইটটি কà§à¦¯à¦¾à¦¶à§‡ থেকে লোড করা যাবে না</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">বারà§à¦¤à¦¾ পà§à¦°à¦®à¦¾à¦£à§€à¦•à¦°à¦£ à¦à¦¬à¦‚ <ph name="KX" />-কে কী à¦à¦•à§à¦¸à¦šà§‡à¦žà§à¦œ নিরà§à¦®à¦¾à¦£à¦•à§Œà¦¶à¦² হিসাবে সংযোগটি <ph name="CIPHER" /> বà§à¦¯à¦¬à¦¹à¦¾à¦° করে <ph name="MAC" />-à¦à¦° সাথে à¦à¦¨à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ হয়েছে৷</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="7887683347370398519">আপনার CVC পরীকà§à¦·à¦¾ করà§à¦¨ à¦à¦¬à¦‚ আবার চেষà§à¦Ÿà¦¾ করà§à¦¨</translation>
<translation id="7912024687060120840">ফোলà§à¦¡à¦¾à¦°à§‡:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">সারà§à¦­à¦¾à¦°à§‡à¦° শংসাপতà§à¦° à¦à¦–নও কারà§à¦¯à¦•à¦° নয়.</translation>
<translation id="7942349550061667556">লাল</translation>
+<translation id="7947285636476623132">আপনার মেয়াদ শেষের বছর পরীকà§à¦·à¦¾ করে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨</translation>
<translation id="7951415247503192394">(৩২-বিট)</translation>
<translation id="7956713633345437162">মোবাইল বà§à¦•à¦®à¦¾à¦°à§à¦•</translation>
<translation id="7961015016161918242">কখনই নয়</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">সরà§à¦¬à¦¦à¦¾ <ph name="ORIGINAL_LANGUAGE" />-কে <ph name="TARGET_LANGUAGE" />-তে অনà§à¦¬à¦¾à¦¦ করà§à¦¨</translation>
<translation id="7995512525968007366">নিরà§à¦¦à¦¿à¦·à§à¦Ÿ করে উলà§à¦²à§‡à¦– করা নেই</translation>
<translation id="8012647001091218357">à¦à¦‡ মà§à¦¹à§‚রà§à¦¤à§‡ আমরা আপনার পিতামাতার কাছে পৌà¦à¦›à¦¾à¦¤à§‡ পারিনি৷ অনà§à¦—à§à¦°à¦¹ করে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨à§·</translation>
+<translation id="8025119109950072390">à¦à¦‡ সাইটে আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ আপনাকে ধোà¦à¦•à¦¾ দিয়ে সফটওয়à§à¦¯à¦¾à¦° ইনসà§à¦Ÿà¦² করা বা আপনার বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত তথà§à¦¯ (উদাহরণসà§à¦¬à¦°à§‚প, পাসওয়ারà§à¦¡, ফোন নমà§à¦¬à¦°, বা কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡) পà§à¦°à¦•à¦¾à¦¶ করার মত বিপজà§à¦œà¦¨à¦• কিছৠকরাতে পারে।</translation>
+<translation id="803030522067524905">সমà§à¦ªà§à¦°à¦¤à¦¿ Google à¦à¦° নিরাপদ বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚ <ph name="SITE" /> ঠফিশিং শনাকà§à¦¤ করেছে। ফিশিং সাইটগà§à¦²à¦¿ আপনার থেকে কৌশলে তথà§à¦¯ নিয়ে নেওয়ার জনà§à¦¯ অনà§à¦¯à¦¾à¦¨à§à¦¯ সাইট যেমন হয় সেইরকম হওয়ার ভান করে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="8034522405403831421">à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà¦¿ <ph name="SOURCE_LANGUAGE" /> ভাষায় রয়েছে৷ à¦à¦Ÿà¦¿à¦•à§‡ <ph name="TARGET_LANGUAGE" /> ভাষায় অনà§à¦¬à¦¾à¦¦ করবেন?</translation>
+<translation id="8041089156583427627">পà§à¦°à¦¤à¦¿à¦•à§à¦°à¦¿à¦¯à¦¼à¦¾ পাঠান</translation>
<translation id="8088680233425245692">নিবনà§à¦§ দেখতে বà§à¦¯à¦°à§à¦¥ হয়েছে৷</translation>
<translation id="8089520772729574115">১ মেগাবাইটের কম</translation>
<translation id="8091372947890762290">সারà§à¦­à¦¾à¦°à§‡ সকà§à¦°à¦¿à§Ÿà¦•à¦°à¦£ বাকি আছে</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719"><ph name="URL" />ঠফাইলটি পাঠযোগà§à¦¯ নয়৷ à¦à¦Ÿà¦¾ মà§à¦›à§‡ ফেলা হয়েছে পারে, সরিয়ে দেওয়া হয়েছে, অথবা ফাইল অনà§à¦®à¦¤à¦¿ পà§à¦°à¦¬à§‡à¦¶à¦¾à¦§à¦¿à¦•à¦¾à¦° পà§à¦°à¦¤à¦¿à¦°à§‹à¦§ করতে পারে৷</translation>
<translation id="8194797478851900357">&amp;সরানোকে পূরà§à¦¬à¦¾à¦¬à¦¸à§à¦¥à¦¾à¦¯à¦¼ ফেরান</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" ID যà§à¦•à§à¦¤ à¦à¦•à§à¦¸à¦Ÿà§‡à¦¨à¦¶à¦¾à¦¨à§‡à¦° অবৈধ আপডেট URL।</translation>
+<translation id="8202097416529803614">অরà§à¦¡à¦¾à¦°à§‡à¦° সারসংকà§à¦·à§‡à¦ª</translation>
<translation id="8218327578424803826">নিরà§à¦§à¦¾à¦°à¦¿à¦¤ অবসà§à¦¥à¦¾à¦¨:</translation>
<translation id="8225771182978767009">à¦à¦‡ কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦° যিনি সেট আপ করেছেন তিনি à¦à¦‡ সাইটটি অবরà§à¦¦à§à¦§ করার বিষয়টি চয়ন করেছেন।</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">বরà§à¦¤à¦®à¦¾à¦¨à§‡ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à¦à¦° আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€à¦°à¦¾ আপনার কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡ কà§à¦·à¦¤à¦¿à¦•à¦¾à¦°à¦• পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® ইনসà§à¦Ÿà¦² করতে পারে যা আপনার তথà§à¦¯ (উদাহরণসà§à¦¬à¦°à§à¦ª, ফটো, পাসওয়ারà§à¦¡, বারà§à¦¤à¦¾ à¦à¦¬à¦‚ কà§à¦°à§‡à¦¡à¦¿à¦Ÿ কারà§à¦¡) চà§à¦°à¦¿ করতে বা মà§à¦›à§‡ দিতে পারে।</translation>
<translation id="8241707690549784388">আপনি যে পৃষà§à¦ à¦¾à¦Ÿà¦¿ খà§à¦à¦œà¦›à§‡à¦¨ সেটি আপনার পà§à¦°à¦¬à§‡à¦¶ করানো তথà§à¦¯ বà§à¦¯à¦¬à¦¹à¦¾à¦° করছে৷ à¦à¦‡ পৃষà§à¦ à¦¾à¦¤à§‡ ফিরে à¦à¦²à§‡ কোনো কà§à¦°à¦¿à§Ÿà¦¾ আবার করতে হতে পারে৷ আপনি কি অবিরত করতে চান?</translation>
<translation id="8249320324621329438">সরà§à¦¬à¦¶à§‡à¦· পà§à¦°à¦¾à¦ªà§à¦¤ করেছে:</translation>
<translation id="8261506727792406068">মà§à¦›à§à¦¨</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">উৎস</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="8349305172487531364">বà§à¦•à¦®à¦¾à¦°à§à¦•à¦¸ দণà§à¦¡</translation>
<translation id="8363502534493474904">বিমান মোড বনà§à¦§ করে দেখà§à¦¨</translation>
<translation id="8364627913115013041">সেট করা নেই৷</translation>
<translation id="8380941800586852976">বিপজà§à¦œà¦¨à¦•</translation>
<translation id="8382348898565613901">আপনার সমà§à¦ªà§à¦°à¦¤à¦¿ ঘà§à¦°à§‡ দেখা বà§à¦•à¦®à¦¾à¦°à§à¦•à¦—à§à¦²à¦¿ à¦à¦–ানে দেখা যাবে</translation>
+<translation id="8398259832188219207">কà§à¦°à§à¦¯à¦¾à¦¶ পà§à¦°à¦¤à¦¿à¦¬à§‡à¦¦à¦¨ <ph name="UPLOAD_TIME" /> ঠআপলোড করা হয়েছে</translation>
<translation id="8412145213513410671">কà§à¦°à§à¦¯à¦¾à¦¶ (<ph name="CRASH_COUNT" />টি)</translation>
<translation id="8412392972487953978">আপনাকে à¦à¦•à¦‡ পাসফà§à¦°à§‡à¦œ অবশà§à¦¯à¦‡ দà§'বার পà§à¦°à¦¬à§‡à¦¶ করাতে হবে৷</translation>
<translation id="8428213095426709021">সেটিংস</translation>
@@ -679,25 +727,27 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> সাড়া দিতে খà§à¦¬à§‡ বেশি সময় নিয়েছে।</translation>
<translation id="852346902619691059">à¦à¦‡ সারà§à¦­à¦¾à¦° পà§à¦°à¦®à¦¾à¦£ করতে পারেনি যে à¦à¦Ÿà¦¾ <ph name="DOMAIN" />; আপনার ডিভাইসের অপারেটিং সিসà§à¦Ÿà§‡à¦® à¦à¦° নিরাপতà§à¦¤à¦¾ শংসাপতà§à¦°à¦Ÿà¦¿à¦•à§‡ বিশà§à¦¬à¦¾à¦¸ করে না। কোনো ভà§à¦² কনফিগারেশনের কারণে অথবা কোনো আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ আপনার সংযোগ মাà¦à¦ªà¦¥à§‡ আটকে দিচà§à¦›à§‡ বলে à¦à¦®à¦¨à¦Ÿà¦¾ হতে পারে। <ph name="BEGIN_LEARN_MORE_LINK" />আরো জানà§à¦¨<ph name="END_LEARN_MORE_LINK" />।</translation>
<translation id="8530504477309582336">à¦à¦‡ ধরনের কারà§à¦¡ Google Payments দà§à¦¬à¦¾à¦°à¦¾ সমরà§à¦¥à¦¿à¦¤ নয়৷ দয়া করে à¦à¦•à¦Ÿà¦¿ আলাদা কারà§à¦¡ নিরà§à¦¬à¦¾à¦šà¦¨ করà§à¦¨à§·</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="8553075262323480129">পৃষà§à¦ à¦¾à¦° ভাষা নিরà§à¦§à¦¾à¦°à¦£ না করতে পারার কারণে অনà§à¦¬à¦¾à¦¦ বà§à¦¯à¦°à§à¦¥ হয়েছে৷</translation>
<translation id="8559762987265718583"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ঠà¦à¦•à¦Ÿà¦¿ বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করা যায়নি কারণ আপনার ডিভাইসের তারিখ à¦à¦¬à¦‚ সময় (<ph name="DATE_AND_TIME" />) সঠিক নয়৷</translation>
-<translation id="856992080682148">à¦à¦‡ সাইটের শংসাপতà§à¦° ২০১৭ সালে বা তার পরে মেয়াদোতà§à¦¤à§€à¦°à§à¦£ হবে, à¦à¦¬à¦‚ শংসাপতà§à¦° শৃঙà§à¦–লে SHA-1 বà§à¦¯à¦¬à¦¹à¦¾à¦° করে সà§à¦¬à¦¾à¦•à§à¦·à¦° করা à¦à¦•à¦Ÿà¦¿ শংসাপতà§à¦° রয়েছে।</translation>
<translation id="8571890674111243710">পৃষà§à¦ à¦¾à¦Ÿà¦¿<ph name="LANGUAGE" />তে অনà§à¦¬à¦¾à¦¦ করà§à¦¨...</translation>
<translation id="859285277496340001">শংসাপতà§à¦°à¦Ÿà¦¿ পà§à¦°à¦¤à§à¦¯à¦¾à¦¹à¦¾à¦° করা হয়েছে কিনা তা যাচাই করতে শংসাপতà§à¦°à¦Ÿà¦¿ কোনও কারিগরীকে নিরà§à¦¦à¦¿à¦·à§à¦Ÿ করে না৷</translation>
<translation id="8620436878122366504">আপনার পিতামাতা à¦à¦–নও à¦à¦Ÿà¦¿ অনà§à¦®à§‹à¦¦à¦¨ করেন নি</translation>
-<translation id="8647750283161643317">সবগà§à¦²à¦¿à¦•à§‡ ডিফলà§à¦Ÿà§‡ পà§à¦¨à¦°à¦¾à§Ÿ সেট করà§à¦¨</translation>
+<translation id="8647750283161643317">সবগà§à¦²à¦¿à¦•à§‡ ডিফলà§à¦Ÿà§‡ আবার সেট করà§à¦¨</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" />-ঠআপনার কানেকশন à¦à¦¨à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ হয় নি৷</translation>
<translation id="8725066075913043281">আবার চেষà§à¦Ÿà¦¾ করà§à¦¨</translation>
<translation id="8728672262656704056">আপনি ছদà§à¦®à¦¬à§‡à¦¶à§‡ রয়েছেন৷</translation>
<translation id="8730621377337864115">সমà§à¦ªà¦¨à§à¦¨ হয়েছে</translation>
<translation id="8738058698779197622">নিরাপদ নেটওয়ারà§à¦• সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করতে আপনার ঘড়িকে সঠিকভাবে সেট করতে হবে৷ নিরাপদ সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করার জনà§à¦¯ নিজেদের সনাকà§à¦¤ করার জনà§à¦¯ ওয়েবসাইটগà§à¦²à¦¿ যে শংসাপতà§à¦°à¦—à§à¦²à¦¿ বà§à¦¯à¦¬à¦¹à¦¾à¦° করে, সেগà§à¦²à¦¿ শà§à¦§à§à¦®à¦¾à¦¤à§à¦° নিরà§à¦¦à¦¿à¦·à§à¦Ÿ সময়ের জনà§à¦¯ বৈধ থাকে৷ যেহেতৠআপনার ডিভাইসের ঘড়িটি ভà§à¦², সেই জনà§à¦¯ Chromium সঠিকভাবে শংসাপতà§à¦°à¦—à§à¦²à¦¿ পরীকà§à¦·à¦¾ করতে পারে না৷</translation>
<translation id="8740359287975076522"><ph name="HOST_NAME" /> à¦à¦° &lt;abbr id="dnsDefinition"&gt;DNS ঠিকানা&lt;/abbr&gt; পাওয়া যায়নি। সমসà§à¦¯à¦¾ নিরà§à¦£à§Ÿ করা হচà§à¦›à§‡à¥¤</translation>
-<translation id="8790007591277257123">&amp;মà§à¦›à§‡ ফেলাকে পà§à¦¨à¦°à¦¾à§Ÿ করà§à¦¨</translation>
+<translation id="8790007591277257123">&amp;মà§à¦›à§‡ ফেলাকে আবার করà§à¦¨</translation>
<translation id="8798099450830957504">ডিফলà§à¦Ÿ</translation>
+<translation id="8800988563907321413">আপনার জনà§à¦¯ আশেপাশের পà§à¦°à¦¸à§à¦¤à¦¾à¦¬à¦¨à¦¾à¦—à§à¦²à¦¿ à¦à¦–ানে দেখা যাবে</translation>
<translation id="8804164990146287819">গোপনীয়তা নীতি</translation>
<translation id="8820817407110198400">বà§à¦•à¦®à¦¾à¦°à§à¦•à¦¸</translation>
<translation id="8834246243508017242">পরিচিতিগà§à¦²à¦¿ বà§à¦¯à¦¬à¦¹à¦¾à¦° করে সà§à¦¬à¦¤à¦ƒà¦ªà§‚রà§à¦£ সকà§à¦·à¦® করà§à¦¨...</translation>
<translation id="883848425547221593">অনà§à¦¯à¦¾à¦¨à§à¦¯ বà§à¦•à¦®à¦¾à¦°à§à¦•</translation>
+<translation id="884264119367021077">শিপিং ঠিকানা</translation>
<translation id="884923133447025588">কোন পà§à¦°à¦¤à§à¦¯à¦¾à¦¹à¦¾à¦° নিরà§à¦®à¦¾à¦£à¦•à§Œà¦¶à¦² পাওয়া যায় নি৷</translation>
<translation id="885730110891505394">Google à¦à¦° সাথে ভাগ করছে</translation>
<translation id="8866481888320382733">নীতি সেটিংস বিশà§à¦²à§‡à¦·à¦£ করার সময় তà§à¦°à§à¦Ÿà¦¿</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">সারà§à¦­à¦¾à¦°à§‡à¦° শংসাপতà§à¦°à§‡à¦° মেয়াদ ফà§à¦°à¦¿à§Ÿà§‡à¦›à§‡à§·</translation>
<translation id="8987927404178983737">মাস</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">সামনের সাইটটিতে কà§à¦·à¦¤à¦¿à¦•à¦° পà§à¦°à§‹à¦—à§à¦°à¦¾à¦®à¦—à§à¦²à¦¿ রয়েছে</translation>
<translation id="9001074447101275817"><ph name="DOMAIN" /> পà§à¦°à¦•à§à¦¸à§€à¦Ÿà¦¿à¦° à¦à¦•à¦Ÿà¦¿ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ নাম à¦à¦¬à¦‚ পাসওয়ারà§à¦¡ পà§à¦°à§Ÿà§‹à¦œà¦¨à¥¤</translation>
<translation id="901974403500617787">যে ফà§à¦²à§à¦¯à¦¾à¦—গà§à¦²à¦¿ সমসà§à¦¤ সিসà§à¦Ÿà§‡à¦® জà§à§œà§‡ পà§à¦°à§Ÿà§‹à¦— করা হয় সেগà§à¦²à¦¿ শà§à¦§à§à¦®à¦¾à¦¤à§à¦° মালিকের দà§à¦¬à¦¾à¦°à¦¾ সেট করা যেতে পারে: <ph name="OWNER_EMAIL" />৷</translation>
<translation id="9020542370529661692">à¦à¦‡ পৃষà§à¦ à¦¾à¦Ÿà¦¿ <ph name="TARGET_LANGUAGE" /> ঠঅনà§à¦¬à¦¾à¦¦ করা হয়েছে</translation>
+<translation id="9035022520814077154">নিরাপতà§à¦¤à¦¾ তà§à¦°à§à¦Ÿà¦¿</translation>
<translation id="9038649477754266430">পৃষà§à¦ à¦¾ আরো দà§à¦°à§à¦¤ লোড করার জনà§à¦¯ কোনো পূরà§à¦¬à¦¾à¦­à¦¾à¦· পরিষেবা বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨</translation>
<translation id="9039213469156557790">উপরনà§à¦¤à§, à¦à¦‡ পৃষà§à¦ à¦¾à¦¤à§‡ অনà§à¦¯à¦¾à¦¨à§à¦¯ সংসà§à¦¥à¦¾à¦¨ অনà§à¦¤à¦°à§à¦­à§à¦•à§à¦¤ রয়েছে যা নিরাপদ নয়৷ à¦à¦‡ সংসà§à¦¥à¦¾à¦¨à¦—à§à¦²à¦¿ টà§à¦°à¦¾à¦¨à¦œà¦¿à¦Ÿà§‡à¦° সময় অনà§à¦¯à¦°à¦¾ দেখতে পাবে à¦à¦¬à¦‚ পৃষà§à¦ à¦¾à¦Ÿà¦¿à¦° আচরণ পরিবরà§à¦¤à¦¨ করার জনà§à¦¯ কোনো আকà§à¦°à¦®à¦£à¦•à¦¾à¦°à§€ à¦à¦° পরিবরà§à¦¤à¦¨ করতে পারেন৷</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ঠথাকা আকà§à¦°à¦®à¦¨à¦•à¦¾à¦°à§€à¦°à¦¾ ছà§à¦²à¦¨à¦¾à¦° আশà§à¦°à§Ÿ নিয়ে আপনাকে পà§à¦°à§‹à¦—à§à¦°à¦¾à¦®à¦—à§à¦²à¦¿ ইনসà§à¦Ÿà¦² করতে পà§à¦°à¦£à§‹à¦¦à¦¿à¦¤ করে যা আপনার বà§à¦°à¦¾à¦‰à¦œà¦¿à¦‚ অভিজà§à¦žà¦¤à¦¾à¦•à§‡ পà§à¦°à¦­à¦¾à¦¬à¦¿à¦¤ করতে পারে (যেমন, আপনার হোমপৃষà§à¦ à¦¾ পরিবরà§à¦¤à¦¨ করা বা আপনার পরিদরà§à¦¶à¦¿à¦¤ সাইটগà§à¦²à¦¿à¦¤à§‡ অতিরিকà§à¦¤ বিজà§à¦žà¦¾à¦ªà¦¨ দেখানো)৷</translation>
<translation id="9050666287014529139">পাসফà§à¦°à§‡à¦œ</translation>
<translation id="9065203028668620118">সমà§à¦ªà¦¾à¦¦à¦¨à¦¾</translation>
<translation id="9068849894565669697">রঙ নিরà§à¦¬à¦¾à¦šà¦¨ করà§à¦¨</translation>
<translation id="9076283476770535406">à¦à¦¤à§‡ পà§à¦°à¦¾à¦ªà§à¦¤à¦¬à§Ÿà¦¸à§à¦•à¦¦à§‡à¦° সামগà§à¦°à§€ থাকতে পারে</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> পৃষà§à¦ à¦¾à¦Ÿà¦¿ কাজ করছে না</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>
<translation id="9148507642005240123">&amp;সমà§à¦ªà¦¾à¦¦à¦¨à¦¾à¦•à§‡ পূরà§à¦¬à¦¾à¦¬à¦¸à§à¦¥à¦¾à¦¯à¦¼ ফেরান</translation>
<translation id="9157595877708044936">সেট আপ হচà§à¦›à§‡...</translation>
<translation id="9170848237812810038">&amp;পূরà§à¦¬à¦¾à¦¬à¦¸à§à¦¥à¦¾à§Ÿ ফিরà§à¦¨</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">ফরà§à¦® সাফ করà§à¦¨</translation>
<translation id="939736085109172342">নতà§à¦¨ ফোলà§à¦¡à¦¾à¦°</translation>
<translation id="941721044073577244">মনে হচà§à¦›à§‡ à¦à¦‡ সাইটটি ঘà§à¦°à§‡ দেখার অনà§à¦®à¦¤à¦¿ আপনার নেই</translation>
-<translation id="962701380617707048">আপনার কারà§à¦¡à§‡à¦° বিবরণ আপডেট করার জনà§à¦¯ মেয়াদ শেষের তারিখ à¦à¦¬à¦‚ <ph name="CREDIT_CARD" /> à¦à¦° CVC লিখà§à¦¨</translation>
<translation id="969892804517981540">অফিসিয়াল বিলà§à¦¡</translation>
<translation id="988159990683914416">বিকাশকারী বিলà§à¦¡</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 42fae31722f..7c079e0b9dd 100644
--- a/chromium/components/strings/components_strings_ca.xtb
+++ b/chromium/components/strings/components_strings_ca.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Torneu-vos a connectar a la xarxa Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Imprimeix...</translation>
<translation id="1181037720776840403">Suprimeix</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Informa automàticament<ph name="END_WHITEPAPER_LINK" /> Google dels detalls sobre possibles incidències de seguretat. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Següent</translation>
<translation id="1201895884277373915">Més entrades d'aquest lloc</translation>
<translation id="1206967143813997005">Signatura inicial incorrecta</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cian</translation>
<translation id="1629803312968146339">Voleu que Chrome desi aquesta targeta?</translation>
<translation id="1640180200866533862">Polítiques d'usuari</translation>
+<translation id="1640244768702815859">Prova d'<ph name="BEGIN_LINK" />anar a la pàgina d'inici del lloc<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">La configuració de la xarxa no és vàlida i no s'ha pogut importar.</translation>
<translation id="1644574205037202324">Historial</translation>
<translation id="1645368109819982629">Protocol no admès</translation>
<translation id="1676269943528358898"><ph name="SITE" /> utilitza normalment l'encriptació per protegir la vostra informació. En aquesta ocasió, quan Google Chrome ha provat de connectar-se a <ph name="SITE" />, el lloc web ha enviat credencials poc comunes i incorrectes. Pot ser que un atacant estigui provant de fer-se passar per <ph name="SITE" /> o que una pantalla d'inici de sessió a la xarxa Wi-Fi hagi interromput la connexió. En qualsevol cas, la vostra informació continua estant segura, perquè Google Chrome ha aturat la connexió abans no s'intercanviés cap dada.</translation>
+<translation id="168328519870909584">Els atacants que actualment són al lloc <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> poden provar d'instal·lar aplicacions perilloses al dispositiu per robar o suprimir la teva informació (com ara fotos, contrasenyes, missatges i targetes de crèdit).</translation>
<translation id="168841957122794586">El certificat de servidor conté una clau criptogràfica dèbil.</translation>
-<translation id="1701955595840307032">Obtén suggeriments de contingut</translation>
<translation id="1710259589646384581">SO</translation>
<translation id="1721312023322545264">Cal que <ph name="NAME" /> et doni permís per visitar aquest lloc</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Número de pàgina</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Proveu d'executar el Diagnòstic de xarxa de Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Actualitzeu la frase de contrasenya de sincronització.</translation>
+<translation id="1787142507584202372">Les pestanyes obertes es mostren aquí</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Fa poc, la funció Navegació segura de Google <ph name="BEGIN_LINK" />ha detectat programari maliciós<ph name="END_LINK" /> 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. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Sol·licitud o paràmetres de la sol·licitud no vàlids</translation>
+<translation id="1834321415901700177">Aquest lloc conté programes perjudicials</translation>
<translation id="1838667051080421715">Estàs veient el codi font d'una pàgina web.</translation>
<translation id="1871208020102129563">El servidor intermediari està configurat perquè utilitzi servidors intermediaris fixos, en lloc d'un URL d'script .pac.</translation>
<translation id="1883255238294161206">Redueix la llista</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Client nadiu</translation>
<translation id="213826338245044447">Adreces d'interès per a mòbils</translation>
<translation id="2148716181193084225">Avui</translation>
-<translation id="2149973817440762519">Edició de l'adreça d'interès</translation>
<translation id="2154054054215849342">La sincronització no està disponible per al teu domini</translation>
<translation id="2166049586286450108">Accés complet d'administrador</translation>
<translation id="2166378884831602661">Aquest lloc no pot proporcionar una connexió segura</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">En aquest moment no pots visitar <ph name="SITE" /> perquè el lloc web fa servir HSTS. Els errors i els atacs de xarxa solen ser temporals, de manera que és probable que aquesta pàgina torni a funcionar més tard. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">S'ha ignorat l'adreça d'interès no vàlida a l'índex <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Altres adreces d'interès</translation>
+<translation id="2355395290879513365">És possible que els atacants puguin veure les imatges que miris en aquest lloc i que les modifiquin per enganyar-te.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Mostra les polítiques sense valors definits</translation>
<translation id="2396249848217231973">&amp;Desfés la supressió</translation>
<translation id="2455981314101692989">Aquesta pàgina web ha desactivat l'emplenament automàtic per a aquest formulari.</translation>
+<translation id="2460160116472764928">Fa poc, la funció Navegació segura de Google <ph name="BEGIN_LINK" />ha detectat programari maliciós<ph name="END_LINK" /> a <ph name="SITE" />. De vegades, els llocs web que acostumen a ser segurs s'infecten amb programari maliciós. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Emplena</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Executar el Diagnòstic de xarxa<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL de cerca no vàlid.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Estàs segur que vols suprimir aquestes pàgines de l'historial?</translation>
<translation id="2677748264148917807">Surt</translation>
<translation id="269990154133806163">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. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Llista de lectura</translation>
<translation id="2704283930420550640">El valor no coincideix amb el format.</translation>
<translation id="2704951214193499422">En aquest moment Chromium no ha pogut confirmar la teva targeta. Torna-ho a provar més tard.</translation>
<translation id="2705137772291741111">La còpia desada (a la memòria cau) d'aquest lloc no s'ha pogut llegir.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Has provat d'accedir a <ph name="DOMAIN" />, però l'emissor ha revocat el certificat que ha presentat el servidor. Això vol dir que les credencials de seguretat que ha proporcionat el servidor no són de confiança. És possible que t'estiguis comunicant amb un atacant. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Demana permís</translation>
<translation id="2713444072780614174">Blanc</translation>
+<translation id="2720342946869265578">A prop</translation>
<translation id="2721148159707890343">Sol·licitud realitzada correctament</translation>
<translation id="2728127805433021124">El certificat del servidor està signat mitjançant un algoritme de signatura dèbil.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Executar el Diagnòstic de connectivitat<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Podeu desactivar qualsevol servidor intermediari configurat per a una connexió des de la pàgina de configuració.</translation>
<translation id="2955913368246107853">Tanca la barra de cerca</translation>
<translation id="2958431318199492670">La configuració de la xarxa no compleix l'estàndard ONC. Pot ser que algunes opcions de configuració no s'hagin importat.</translation>
+<translation id="29611076221683977">Els atacants que actualment són al lloc <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> poden provar d'instal·lar programes perillosos al dispositiu Mac per robar o suprimir la teva informació (per exemple, les fotos, les contrasenyes, els missatges i les targetes de crèdit).</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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Oculta els detalls</translation>
<translation id="3587482841069643663">Tots</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">El servidor no admet l'extensió de renegociació TLS.</translation>
<translation id="36224234498066874">Esborra les dades de navegació</translation>
<translation id="362276910939193118">Mostra l'historial complet</translation>
<translation id="3623476034248543066">Mostra el valor</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Llicències exhaurides</translation>
<translation id="3714780639079136834">Activeu les dades mòbils o la Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Comproveu el servidor intermediari, el tallafoc i la configuració de DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Si enteneu el risc que suposa per a la vostra seguretat, podeu <ph name="BEGIN_LINK" />visitar aquest lloc no segur<ph name="END_LINK" /> abans que no s'hagin suprimit els programes perillosos.</translation>
<translation id="3739623965217189342">Enllaç que heu copiat</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="3788090790273268753">El certificat d'aquest lloc caduca al 2016, i la cadena de certificats conté un certificat que s'ha signat amb SHA-1.</translation>
<translation id="382518646247711829">Si feu servir un servidor intermediari...</translation>
<translation id="3828924085048779000">Les frases de contrasenya no poder estar buides.</translation>
<translation id="3845539888601087042">Es mostra l'historial dels dispositius en què heu iniciat la sessió. <ph name="BEGIN_LINK" />Obteniu més informació<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Tecla "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">El client i el servidor no admeten cap versió de protocol SSL ni cap sistema de xifratge comuns.</translation>
<translation id="4079302484614802869">La configuració del servidor intermediari s'ha definit perquè utilitzi un URL d'script .pac, en lloc de servidors intermedis fixos.</translation>
+<translation id="4098354747657067197">Aquest lloc web és enganyós</translation>
<translation id="4103249731201008433">El número de sèrie del dispositiu no és vàlid</translation>
<translation id="4103763322291513355">Visiteu &lt;strong&gt;chrome://policy&lt;/strong&gt; per veure la llista d'URL inclosos a la llista negra i altres polítiques aplicades per l'administrador del sistema.</translation>
<translation id="4110615724604346410">Aquest servidor no ha pogut demostrar que és <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 ha interceptat la teva connexió. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{cap}=1{1 aplicació ($1)}=2{2 aplicacions ($1, $2)}other{# aplicacions ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Errors</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Proveu d'executar el Diagnòstic de xarxa<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">La teva connexió amb aquest lloc no és del tot segura</translation>
<translation id="4250680216510889253">No</translation>
<translation id="425582637250725228">És possible que els canvis que heu fet no es desin.</translation>
<translation id="4258748452823770588">Signatura errònia</translation>
<translation id="4269787794583293679">(Sense nom d'usuari)</translation>
+<translation id="4280429058323657511">, caduca el dia <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Fa poc, la funció Navegació segura de Google <ph name="BEGIN_LINK" />ha trobat programes perjudicials<ph name="END_LINK" /> a <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Suggeriments per als responsables</translation>
<translation id="4304224509867189079">Accedeix</translation>
<translation id="432290197980158659">El servidor ha presentat un certificat que no coincideix amb el que s'esperava. Aquestes expectatives es posen en pràctica en determinats llocs web d'alta seguretat per protegir-te. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">No s'ha pogut trobar l'article</translation>
+<translation id="4326324639298822553">Comprova la data de caducitat i torna-ho a provar</translation>
<translation id="4331708818696583467">No segur</translation>
+<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="4381091992796011497">Nom d'usuari:</translation>
<translation id="4394049700291259645">Desactiva</translation>
@@ -348,18 +363,22 @@
<translation id="4589078953350245614">Has provat d'accedir a <ph name="DOMAIN" />, però el certificat que ha presentat el servidor no és vàlid. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Pestanyes d'altres dispositius</translation>
<translation id="4668929960204016307">,</translation>
-<translation id="4670097147947922288">Esteu consultant una pàgina d'extensions.</translation>
+<translation id="4670097147947922288">Estàs consultant la pàgina d'una extensió.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
<translation id="4708268264240856090">La connexió s'ha interromput</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>
<translation id="4744603770635761495">Camí executable</translation>
+<translation id="4750917950439032686">La teva informació (com ara les contrasenyes o els números de targeta de crèdit) es considera privada quan s'envia a aquest lloc.</translation>
<translation id="4756388243121344051">&amp;Historial</translation>
+<translation id="4759118997339041434">S'ha desactivat l'emplenament automàtic de pagaments</translation>
<translation id="4764776831041365478">És possible que la pàgina web de <ph name="URL" /> estigui temporalment inactiva o que s'hagi desplaçat permanentment a una nova adreça web.</translation>
<translation id="4771973620359291008">S'ha produït un error desconegut.</translation>
<translation id="4800132727771399293">Comproveu la data de caducitat i el CVC i torneu-ho a provar</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Error de xarxa</translation>
<translation id="4816492930507672669">Ajusta a la mida de la pàgina</translation>
<translation id="4850886885716139402">Visualització</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Accions</translation>
<translation id="4958444002117714549">Desplega la llista</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">S'està baixant</translation>
<translation id="5190835502935405962">Barra d'adreces d'interès</translation>
<translation id="5199729219167945352">Experiments</translation>
-<translation id="5199841536747119669">Els suggeriments es mostren aquí</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="5299298092464848405">S'ha produït un error en analitzar la política</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">La creació d'informes de bloqueig està desactivada.</translation>
<translation id="5317780077021120954">Desa</translation>
<translation id="5327248766486351172">Nom</translation>
+<translation id="5337705430875057403">Els atacants de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> us poden enganyar perquè feu 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="5359637492792381994">Aquest servidor no ha pogut demostrar que és <ph name="DOMAIN" /> perquè el seu certificat de seguretat no és vàlid en aquest moment. Això pot ser a causa d'una configuració incorrecta o d'un atacant que ha interceptat la teva connexió. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">No s'ha pogut emmagatzemar la configuració de la política</translation>
+<translation id="5386426401304769735">La cadena de certificats d'aquest lloc conté un certificat que s'ha signat amb SHA-1.</translation>
<translation id="5421136146218899937">Esborra les dades de navegació...</translation>
<translation id="5430298929874300616">Suprimeix l'adreça d'interès</translation>
<translation id="5431657950005405462">No s'ha trobat el fitxer</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Una pàgina inserida a <ph name="SITE" /> diu:</translation>
<translation id="5556459405103347317">Torna a carregar</translation>
<translation id="5565735124758917034">Actiu</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>
+<translation id="5580958916614886209">Comprova el mes de caducitat i torna-ho a provar</translation>
<translation id="560412284261940334">Gestió no compatible</translation>
<translation id="5610142619324316209">Comproveu la connexió</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> us ha redirigit massa vegades.</translation>
<translation id="5622887735448669177">Voleu sortir d'aquest lloc?</translation>
<translation id="5629630648637658800">No s'ha pogut carregar la configuració de la política</translation>
<translation id="5631439013527180824">Testimoni de gestió del dispositiu no vàlid</translation>
+<translation id="5669703222995421982">Obtén contingut personalitzat</translation>
+<translation id="5675650730144413517">Aquesta pàgina no funciona</translation>
<translation id="5677928146339483299">Bloquejades</translation>
<translation id="5694783966845939798">Has provat d'accedir a <ph name="DOMAIN" />, però el servidor ha proporcionat un certificat signat mitjançant un algoritme de signatura poc segur (com ara SHA-1). Això indica que les credencials de seguretat que ha proporcionat el servidor es podrien haver falsificat i que és possible que el servidor no sigui el que esperaves (sinó un atacant). <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">La identitat d'aquest lloc web no ha estat verificada.</translation>
<translation id="5720705177508910913">Usuari actual</translation>
-<translation id="572328651809341494">Pestanyes recents</translation>
<translation id="5732392974455271431">Els teus pares te'l poden desbloquejar</translation>
<translation id="5784606427469807560">S'ha produït un problema en confirmar la targeta. Comprova la connexió a Internet i torna-ho a provar.</translation>
<translation id="5785756445106461925">A més, aquesta pàgina conté altres recursos que no són segurs. La resta d'usuaris poden visualitzar-los mentre estan en trànsit, i algun atacant podria modificar-los per canviar l'aparença de la pàgina.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Has provat d'accedir a <ph name="DOMAIN" />, però el servidor ha presentat un certificat que conté una clau poc segura. És possible que un atacant hagi desencriptat la clau privada i que aquest servidor no sigui el que esperaves (sinó un atacant). <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Carpeta nova</translation>
<translation id="5869405914158311789">No es pot accedir a aquest lloc</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Google Payments no admet aquest tipus de targeta per a aquest comerciant. Seleccioneu-ne una altra.</translation>
<translation id="59174027418879706">Activada</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="5966707198760109579">Setmana</translation>
<translation id="5967867314010545767">Elimina de l'historial</translation>
<translation id="5975083100439434680">Redueix</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Proveu aquestes solucions:</translation>
<translation id="6151417162996330722">El període de validesa del certificat del servidor és massa gran.</translation>
<translation id="6165508094623778733">Més informació</translation>
+<translation id="6177128806592000436">La teva connexió amb aquest lloc no és segura</translation>
<translation id="6203231073485539293">Comprovació de la connexió a Internet</translation>
<translation id="6218753634732582820">Voleu suprimir l'adreça de Chromium?</translation>
+<translation id="6251924700383757765">Política de privadesa</translation>
+<translation id="625755898061068298">Has decidit desactivar els advertiments de seguretat en aquest lloc.</translation>
<translation id="6259156558325130047">&amp;Refés el canvi d'ordre</translation>
<translation id="6263376278284652872">Adreces d'interès de <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Torna a l'àrea de seguretat</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">No es pot accedir a <ph name="URL" />.</translation>
<translation id="6321917430147971392">Reviseu la configuració de DNS</translation>
<translation id="6328639280570009161">Proveu de desactivar la predicció de xarxa</translation>
+<translation id="6328786501058569169">Aquest lloc web és enganyós</translation>
<translation id="6337534724793800597">Filtra les polítiques pel nom</translation>
<translation id="6342069812937806050">Ara mateix</translation>
<translation id="6345221851280129312">mida desconeguda</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">En aquest moment no pots visitar <ph name="SITE" /> perquè el lloc web ha enviat credencials aleatòries que Chromium no pot processar. Els errors i els atacs de xarxa solen ser temporals, de manera que és probable que aquesta pàgina torni a funcionar més tard. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">No es pot comprovar si s'ha revocat el certificat.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">S'ha ignorat perquè la política ha desactivat la cerca predeterminada.</translation>
<translation id="6462969404041126431">Aquest servidor no ha pogut demostrar que és <ph name="DOMAIN" />. És possible que el seu certificat de seguretat s'hagi revocat. Això pot ser a causa d'una configuració incorrecta o d'un atacant que ha interceptat la teva connexió. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Refés la supressió</translation>
<translation id="6534179046333460208">Suggeriments del Web físic</translation>
<translation id="6550675742724504774">Opcions</translation>
+<translation id="6556239504065605927">Connexió segura</translation>
<translation id="6563469144985748109">El teu gestor encara no ho ha aprovat</translation>
<translation id="6593753688552673085">menys de: <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opcions d'encriptació</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Aquesta política ha quedat obsoleta.</translation>
<translation id="6652240803263749613">Aquest servidor no ha pogut demostrar que és <ph name="DOMAIN" />. El sistema operatiu del teu ordinador no confia en el certificat de seguretat presentat. Això pot ser a causa d'una configuració incorrecta o d'un atacant que ha interceptat la teva connexió. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Edició de la carpeta</translation>
-<translation id="6660210980321319655">Data i hora del bloqueig notificat automàticament: <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Valor de la política</translation>
<translation id="6757797048963528358">El dispositiu ha entrat en mode de repòs.</translation>
<translation id="6778737459546443941">El teu pare o la teva mare encara no ho han aprovat</translation>
+<translation id="6810899417690483278">Identificador de personalització</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">En aquests moments, la pàgina web de <ph name="URL" /> no està disponible. Pot ser que estigui sobrecarregada o bé que s'hi estiguin realitzant tasques de manteniment.</translation>
<translation id="6831043979455480757">Tradueix</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Informació de contacte</translation>
+<translation id="7079718277001814089">Aquest lloc conté programari maliciós</translation>
<translation id="7087282848513945231">Comptat</translation>
<translation id="7088615885725309056">Més antic</translation>
<translation id="7090678807593890770">Cerqueu <ph name="LINK" /> a Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> no compleix la normativa de seguretat.</translation>
<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="724691107663265825">Aquest lloc 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="725866823122871198">No s'ha pogut establir una connexió privada amb <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> perquè la data i l'hora (<ph name="DATE_AND_TIME" />) de l'ordinador no són correctes.</translation>
<translation id="7269802741830436641">Aquesta pàgina web té un bucle de redirecció</translation>
@@ -611,6 +648,7 @@ El mode d'incògnit, <ph name="SHORTCUT_KEY" />, us pot resultar pràctic la pro
<translation id="7658239707568436148">Cancel·la</translation>
<translation id="7667346355482952095">El testimoni de la política que s'ha retornat és buit o no coincideix amb el testimoni actual</translation>
<translation id="7668654391829183341">Dispositiu desconegut</translation>
+<translation id="7669271284792375604">És possible que els atacants d'aquest lloc intentin enganyar-te perquè instal·lis programes que perjudiquen la teva navegació (per exemple, et poden canviar la pàgina d'inici o mostrar anuncis addicionals als llocs que visites).</translation>
<translation id="7674629440242451245">Esteu interessat en les funcions de Chrome noves i interessants? Proveu el nostre canal per a desenvolupadors a la pàgina chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Continua per accedir a <ph name="SITE" /> (no segur)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Aquest lloc no es pot carregar des de la memòria cau</translation>
@@ -626,14 +664,17 @@ El mode d'incògnit, <ph name="SHORTCUT_KEY" />, us pot resultar pràctic la pro
<translation id="7800304661137206267">La connexió està encriptada mitjançant <ph name="CIPHER" />, amb <ph name="MAC" /> per a l'autenticació de missatges i amb <ph name="KX" /> com a mecanisme d'intercanvi clau.</translation>
<translation id="780301667611848630">No, gràcies</translation>
<translation id="7805768142964895445">Estat</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Voleu suprimir el suggeriment de formulari de Chrome?</translation>
<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="7887683347370398519">Comproveu el CVC i torneu-ho a provar</translation>
<translation id="7912024687060120840">A la carpeta:</translation>
<translation id="7935318582918952113">Destil·lador DOM</translation>
<translation id="7938958445268990899">El certificat del servidor encara no és vàlid.</translation>
<translation id="7942349550061667556">Vermell</translation>
+<translation id="7947285636476623132">Comprova l'any de caducitat i torna-ho a provar</translation>
<translation id="7951415247503192394">(32 bits)</translation>
<translation id="7956713633345437162">Adreces d'interès per a mòbils</translation>
<translation id="7961015016161918242">Mai</translation>
@@ -641,7 +682,10 @@ El mode d'incògnit, <ph name="SHORTCUT_KEY" />, us pot resultar pràctic la pro
<translation id="7983301409776629893">Tradueix sempre de: <ph name="ORIGINAL_LANGUAGE" /> a: <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">No especificat</translation>
<translation id="8012647001091218357">En aquests moments no ens hem pogut posar en contacte amb els pares. Torneu-ho a provar.</translation>
+<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="803030522067524905">Fa poc, la funció Navegació segura de Google ha detectat activitat de pesca (suplantació d'identitat) a <ph name="SITE" />. Els llocs de pesca es fan passar per altres llocs web per enganyar-te. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</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="8041089156583427627">Envia comentaris</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>
@@ -652,9 +696,11 @@ El mode d'incògnit, <ph name="SHORTCUT_KEY" />, us pot resultar pràctic la pro
<translation id="8150722005171944719">El fitxer de <ph name="URL" /> no es pot llegir. Pot ser que s'hagi eliminat, que s'hagi traslladat o que els permisos del fitxer n'impedeixin l'accés.</translation>
<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="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="8230421197304563332">Els atacants que actualment són al lloc <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 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="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="8249320324621329438">Última obtenció:</translation>
<translation id="8261506727792406068">Suprimeix</translation>
@@ -663,11 +709,13 @@ El mode d'incògnit, <ph name="SHORTCUT_KEY" />, us pot resultar pràctic la pro
<translation id="8294431847097064396">Font</translation>
<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="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>
<translation id="8380941800586852976">Perillosa</translation>
<translation id="8382348898565613901">Les adreces d'interès que has visitat fa poc es mostren aquí</translation>
+<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="8428213095426709021">Configuració</translation>
@@ -679,9 +727,9 @@ El mode d'incògnit, <ph name="SHORTCUT_KEY" />, us pot resultar pràctic la pro
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ha tardat massa a respondre.</translation>
<translation id="852346902619691059">Aquest servidor no ha pogut demostrar que és <ph name="DOMAIN" />. El sistema operatiu del teu dispositiu no confia en el certificat de seguretat presentat. Això pot ser a causa d'una configuració incorrecta o d'un atacant que ha interceptat la teva connexió. <ph name="BEGIN_LEARN_MORE_LINK" />Obtén més informació<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Google Payments no admet aquest tipus de targeta. Seleccioneu-ne una altra.</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="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="8559762987265718583">No es pot establir una connexió privada amb <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> perquè la data i l'hora (<ph name="DATE_AND_TIME" />) del dispositiu són incorrectes.</translation>
-<translation id="856992080682148">El certificat d'aquest lloc caduca al 2017 o més tard, i la cadena de certificats conté un certificat que s'ha signat amb SHA-1.</translation>
<translation id="8571890674111243710">S'està traduint la pàgina a l'idioma <ph name="LANGUAGE" />...</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>
@@ -694,10 +742,12 @@ El mode d'incògnit, <ph name="SHORTCUT_KEY" />, us pot resultar pràctic la pro
<translation id="8740359287975076522">No s'ha trobat l'&lt;abbr id="dnsDefinition"&gt;adreça DNS&lt;/abbr&gt; de <ph name="HOST_NAME" />. S'està diagnosticant el problema.</translation>
<translation id="8790007591277257123">&amp;Refés la supressió</translation>
<translation id="8798099450830957504">Predeterminat</translation>
+<translation id="8800988563907321413">Els suggeriments més propers es mostren aquí</translation>
<translation id="8804164990146287819">Política de privadesa</translation>
<translation id="8820817407110198400">Adreces d'interès</translation>
<translation id="8834246243508017242">Activa l'emplenament automàtic mitjançant els contactes...</translation>
<translation id="883848425547221593">Altres adreces d'interès</translation>
+<translation id="884264119367021077">Adreça d’enviament</translation>
<translation id="884923133447025588">No s'ha trobat cap mecanisme de revocació.</translation>
<translation id="885730110891505394">Comparteix informació amb Google</translation>
<translation id="8866481888320382733">S'ha produït un error en analitzar la configuració de la política</translation>
@@ -715,18 +765,21 @@ El mode d'incògnit, <ph name="SHORTCUT_KEY" />, us pot resultar pràctic la pro
<translation id="8971063699422889582">El certificat del servidor ha caducat.</translation>
<translation id="8987927404178983737">Mes</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Aquest lloc conté programes perjudicials</translation>
<translation id="9001074447101275817">El servidor intermediari del domini <ph name="DOMAIN" /> requereix un nom d'usuari i una contrasenya.</translation>
<translation id="901974403500617787">Les marques que s'apliquen a tot el sistema només les pot definir l'usuari: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Aquesta pàgina s'ha traduït a <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Error de seguretat</translation>
<translation id="9038649477754266430">Utilitza un servei de predicció per poder carregar les pàgines més ràpidament</translation>
<translation id="9039213469156557790">A més, aquesta pàgina conté altres recursos que no són segurs. La resta d'usuaris poden visualitzar-los mentre estan en trànsit, i algun atacant podria modificar-los per canviar el comportament de la pàgina.</translation>
+<translation id="9040185888511745258">És possible que els atacants del lloc <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> provin d'enganyar-vos perquè instal·leu programes que poden perjudicar la vostra experiència de navegació (per exemple, us poden canviar la pàgina d'inici o mostrar anuncis addicionals als llocs que visiteu).</translation>
<translation id="9050666287014529139">Frase de contrasenya</translation>
<translation id="9065203028668620118">Edita</translation>
<translation id="9068849894565669697">Selecció de color</translation>
<translation id="9076283476770535406">Pot incloure contingut per a adults</translation>
-<translation id="9092364396508701805">La pàgina de <ph name="HOST_NAME" /> no funciona</translation>
<translation id="9103872766612412690"><ph name="SITE" /> utilitza normalment l'encriptació per protegir la vostra informació. En aquesta ocasió, quan Chromium ha provat de connectar-se a <ph name="SITE" />, el lloc web ha enviat credencials poc comunes i incorrectes. Pot ser que un atacant estigui provant de fer-se passar per <ph name="SITE" /> o que una pantalla d'inici de sessió a la xarxa Wi-Fi hagi interromput la connexió. En qualsevol cas, la vostra informació continua estant segura, perquè Chromium ha aturat la connexió abans no s'intercanviés cap dada.</translation>
<translation id="9137013805542155359">Mostra l'original</translation>
+<translation id="9137248913990643158">Obre Chrome i inicia-hi la sessió abans d'utilitzar aquesta aplicació.</translation>
<translation id="9148507642005240123">&amp;Desfés la modificació</translation>
<translation id="9157595877708044936">S'està configurant...</translation>
<translation id="9170848237812810038">&amp;Desfés</translation>
@@ -739,7 +792,6 @@ El mode d'incògnit, <ph name="SHORTCUT_KEY" />, us pot resultar pràctic la pro
<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="962701380617707048">Introdueix la data de caducitat i el CVC de la targeta <ph name="CREDIT_CARD" /> per actualitzar-ne els detalls</translation>
<translation id="969892804517981540">Muntatge oficial</translation>
<translation id="988159990683914416">Muntatge del desenvolupador</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 8b469a7f1e7..845795663fb 100644
--- a/chromium/components/strings/components_strings_cs.xtb
+++ b/chromium/components/strings/components_strings_cs.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Obnovit připojení k síti Wi-Fi</translation>
<translation id="1175364870820465910">Tisk...</translation>
<translation id="1181037720776840403">Odebrat</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Automaticky Googlu hlásit<ph name="END_WHITEPAPER_LINK" /> podrobnosti o možných bezpeÄnostních incidentech. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Další</translation>
<translation id="1201895884277373915">Více z tohoto webu</translation>
<translation id="1206967143813997005">Chybný poÄáteÄní podpis</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Azurová</translation>
<translation id="1629803312968146339">Chcete, aby Chrome tuto kartu uložil?</translation>
<translation id="1640180200866533862">Zásady pro uživatele</translation>
+<translation id="1640244768702815859">Zkuste <ph name="BEGIN_LINK" />navštívit domovskou stránku webu<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Konfigurace sítě je neplatná a nelze ji importovat.</translation>
<translation id="1644574205037202324">Historie</translation>
<translation id="1645368109819982629">Nepodporovaný protokol</translation>
<translation id="1676269943528358898">Web <ph name="SITE" /> vaÅ¡e informace běžnÄ› chrání Å¡ifrováním. Když se prohlížeÄ Chrome k webu <ph name="SITE" /> pokusil pÅ™ipojit tentokrát, web vrátil neobvyklé a nesprávné identifikaÄní údaje. K tomuto problému může dojít, pokud se za web <ph name="SITE" /> pokouší vydávat nÄ›jaký útoÄník nebo pokud bylo pÅ™ipojení pÅ™eruÅ¡eno pÅ™ihlaÅ¡ovací obrazovkou sítÄ› Wi-Fi. VaÅ¡e informace jsou i nadále v bezpeÄí, protože prohlížeÄ Google Chrome pÅ™ipojení pÅ™eruÅ¡il dříve, než doÅ¡lo k odeslání jakýchkoliv dat.</translation>
+<translation id="168328519870909584">Ú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 zařízení nebezpeÄné aplikace, které mohou ukrást nebo smazat vaÅ¡e informace (například fotky, hesla, zprávy nebo platební karty).</translation>
<translation id="168841957122794586">Certifikát serveru obsahuje slabý kryptografický klíÄ.</translation>
-<translation id="1701955595840307032">Získejte návrhy obsahu</translation>
<translation id="1710259589646384581">OperaÄní systém</translation>
<translation id="1721312023322545264">Zdá se, že k návštěvě tohoto webu potřebujete povolení od správce <ph name="NAME" /></translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Číslo stránky</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Zkuste spustit Diagnostiku sítě systému Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Aktualizujte prosím heslovou frázi pro synchronizaci.</translation>
+<translation id="1787142507584202372">Zde se zobrazí otevřené karty</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">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ý. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">Neplatný požadavek nebo parametry požadavku</translation>
+<translation id="1834321415901700177">Tento web obsahuje škodlivé programy</translation>
<translation id="1838667051080421715">Zobrazuje se vám zdrojový kód webové stránky.</translation>
<translation id="1871208020102129563">Proxy je nastaveno na používání pevně daných serverů proxy, nikoliv adresy URL skriptu PAC.</translation>
<translation id="1883255238294161206">Sbalit seznam</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">PSČ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 návrh}few{# návrhy}many{# návrhu}other{# návrhů}}</translation>
<translation id="2065985942032347596">Vyžaduje se ověření</translation>
-<translation id="2079545284768500474">Zpět</translation>
+<translation id="2079545284768500474" />
<translation id="20817612488360358">Jako aktivní jsou nakonfigurována systémová nastavení proxy serveru, je vÅ¡ak urÄena i explicitní konfigurace proxy serveru.</translation>
<translation id="2086652334978798447">Chcete-li od Googlu získat personalizované návrhy obsahu, přihlaste se do Chromu.</translation>
<translation id="2089090684895656482">Méně</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Mobilní záložky</translation>
<translation id="2148716181193084225">Dnes</translation>
-<translation id="2149973817440762519">Upravit záložku</translation>
<translation id="2154054054215849342">Synchronizace není pro vaši doménu k dispozici</translation>
<translation id="2166049586286450108">Úplný přístup administrátora</translation>
<translation id="2166378884831602661">Tento web nemůže poskytnout zabezpeÄené pÅ™ipojení</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Web <ph name="SITE" /> nyní nemůžete navÅ¡tívit, protože používá zabezpeÄení HSTS. Síťové chyby a útoky jsou obvykle doÄasné, tato stránka pravdÄ›podobnÄ› pozdÄ›ji bude fungovat. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">Byla ignorována neplatná záložka u indexu <ph name="ENTRY_INDEX" />.</translation>
<translation id="2354001756790975382">Ostatní záložky</translation>
+<translation id="2355395290879513365">ÚtoÄníci mohou vidÄ›t obrázky, které si na tomto webu prohlížíte, a oklamat vás tím, že tyto obrázky upraví.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Zobrazit zásady bez nastavených hodnot</translation>
<translation id="2396249848217231973">&amp;Vrátit smazání zpět</translation>
<translation id="2455981314101692989">U tohoto formuláře je na dané webové stránce vypnuto automatické vyplňování.</translation>
+<translation id="2460160116472764928">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é. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">Vyplnit</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Spustit Diagnostiku sítě<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Neplatná adresa URL vyhledávání.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Jste si jisti, že chcete tyto stránky odstranit ze své historie?</translation>
<translation id="2677748264148917807">Odejít</translation>
<translation id="269990154133806163">Server pÅ™edložil certifikát, který nebyl zveÅ™ejnÄ›n v souladu se zásadou Certificate Transparency. U nÄ›kterých certifikátů je to z důvodu kontroly důvÄ›ryhodnosti a ochrany pÅ™ed útoÄníky vyžadováno. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">Seznam Äetby</translation>
<translation id="2704283930420550640">Hodnota neodpovídá formátu.</translation>
<translation id="2704951214193499422">ProhlížeÄ Chromium vaÅ¡i kartu aktuálnÄ› nemohl ověřit. Zkuste to prosím znovu pozdÄ›ji.</translation>
<translation id="2705137772291741111">Kopii tohoto webu uloženou v mezipamÄ›ti se nepodaÅ™ilo pÅ™eÄíst.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Pokusili jste se pÅ™ejít na web <ph name="DOMAIN" />, ale certifikát pÅ™edložený tímto webem byl vydavatelem zruÅ¡en. To znamená, že bezpeÄnostním pověřením, která web pÅ™edložil, nelze vůbec důvěřovat. Je možné, že komunikujete s útoÄníkem. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">Požádat o oprávnění</translation>
<translation id="2713444072780614174">Bílá</translation>
+<translation id="2720342946869265578">Nablízku</translation>
<translation id="2721148159707890343">Požadavek byl úspěšný</translation>
<translation id="2728127805433021124">Certifikát serveru je podepsán slabým algoritmem.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Spustit Diagnostiku připojení<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Libovolné servery proxy nakonfigurované pro připojení můžete zakázat na stránce Nastavení.</translation>
<translation id="2955913368246107853">Zavřít vyhledávací lištu</translation>
<translation id="2958431318199492670">Konfigurace sítÄ› nesplňuje standard ONC. Může se stát, že nÄ›které Äásti konfigurace nebudou importovány.</translation>
+<translation id="29611076221683977">Ú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 Mac nebezpeÄné programy, které mohou ukrást nebo smazat vaÅ¡e informace (například fotky, hesla, zprávy nebo platební karty).</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="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>
@@ -256,7 +265,6 @@
<translation id="3586931643579894722">Skrýt podrobnosti</translation>
<translation id="3587482841069643663">VÅ¡e</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Server nepodporuje rozšíření opětovného vyjednání protokolu TLS.</translation>
<translation id="36224234498066874">Smazat údaje o prohlížení...</translation>
<translation id="362276910939193118">Zobrazit celou historii</translation>
<translation id="3623476034248543066">Zobrazit hodnotu</translation>
@@ -267,6 +275,7 @@
<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="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>
@@ -276,10 +285,10 @@
<translation id="3712624925041724820">Byly vyÄerpány licence</translation>
<translation id="3714780639079136834">Zapnout mobilní datové připojení nebo Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Zkontrolovat proxy server, firewall a konfiguraci DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Pokud bezpeÄnostní rizika chápete, můžete <ph name="BEGIN_LINK" />tento nespolehlivý web navÅ¡tívit<ph name="END_LINK" /> jeÅ¡tÄ› pÅ™ed tím, než budou nebezpeÄné programy odstranÄ›ny.</translation>
<translation id="3739623965217189342">Zkopírovaný odkaz</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="3788090790273268753">Certifikát těchto webových stránek vyprší v roce 2016 a řetězec certifikátů obsahuje certifikát podepsaný pomocí technologie SHA-1.</translation>
<translation id="382518646247711829">Pokud používáte proxy server...</translation>
<translation id="3828924085048779000">Prázdná heslová fráze není povolena.</translation>
<translation id="3845539888601087042">Zobrazuje se historie z vašich přihlášených zařízení. <ph name="BEGIN_LINK" />Další informace<ph name="END_LINK" /></translation>
@@ -301,6 +310,7 @@
<translation id="4058922952496707368">KlÃ­Ä <ph name="SUBKEY" />: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Klient a server nepodporují spoleÄnou verzi protokolu SSL nebo Å¡ifrovací sadu.</translation>
<translation id="4079302484614802869">Proxy je nastaveno na používání adresy URL skriptu PAC, nikoliv pevně daných serverů proxy.</translation>
+<translation id="4098354747657067197">Chystáte se navštívit podvodné webové stránky</translation>
<translation id="4103249731201008433">Sériové Äíslo zařízení je neplatné</translation>
<translation id="4103763322291513355">Na stránce &lt;strong&gt;chrome://policy&lt;/strong&gt; naleznete seznam zakázaných adres URL a další zásady vynucené vaším správcem systému.</translation>
<translation id="4110615724604346410">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. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -318,15 +328,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{žádná aplikace}=1{1 aplikace ($1)}=2{2 aplikace ($1, $2)}few{# aplikace ($1, $2 $3)}many{# aplikace ($1, $2 $3)}other{# aplikací ($1, $2 $3)}}</translation>
<translation id="4220128509585149162">Selhání</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Zkuste spustit Diagnostiku sítě<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Spojení s tímto webem není plnÄ› zabezpeÄené</translation>
<translation id="4250680216510889253">Ne</translation>
<translation id="425582637250725228">Je možné, že provedené změny nebudou uloženy.</translation>
<translation id="4258748452823770588">Chybný podpis</translation>
<translation id="4269787794583293679">(Žádné uživatelské jméno)</translation>
+<translation id="4280429058323657511">s platností do <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">BezpeÄné prohlížení Google na webu <ph name="SITE" /> nedávno <ph name="BEGIN_LINK" />nalezlo Å¡kodlivé programy<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">Návrhy rodiÄů</translation>
<translation id="4304224509867189079">Přihlásit se</translation>
<translation id="432290197980158659">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Ä›. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">Článek nebyl nalezen</translation>
+<translation id="4326324639298822553">Zkontrolujte datum vypršení platnosti a zkuste to znovu.</translation>
<translation id="4331708818696583467">NezabezpeÄeno</translation>
+<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="4381091992796011497">Jméno uživatele:</translation>
<translation id="4394049700291259645">Deaktivovat</translation>
@@ -344,6 +359,7 @@
<translation id="4589078953350245614">Pokusili jste se přejít do domény <ph name="DOMAIN" />, ale server předložil neplatný certifikát. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></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="4619615317237390068">Karty z ostatních zařízení</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Prohlížíte si stránku rozšíření.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -352,10 +368,13 @@
<translation id="4726672564094551039">Znovu naÄíst zásady</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4744603770635761495">Spustitelná cesta</translation>
+<translation id="4750917950439032686">VaÅ¡e údaje (například hesla nebo Äísla platebních karet) jsou pÅ™i odesílání na tento web soukromé.</translation>
<translation id="4756388243121344051">Historie</translation>
+<translation id="4759118997339041434">Automatické vyplňování informací o platebních kartách je zakázáno</translation>
<translation id="4764776831041365478">Webové stránky na adrese <ph name="URL" /> jsou možná doÄasnÄ› nedostupné nebo mohly být pÅ™emístÄ›ny na novou webovou adresu.</translation>
<translation id="4771973620359291008">Došlo k neznámé chybě.</translation>
<translation id="4800132727771399293">Zkontrolujte datum vypršení platnosti a kód CVC a zkuste to znovu.</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Chyba sítě</translation>
<translation id="4816492930507672669">Přizpůsobit na stránku</translation>
<translation id="4850886885716139402">Zobrazit</translation>
@@ -364,6 +383,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Akce</translation>
<translation id="4958444002117714549">Rozbalit seznam</translation>
@@ -391,7 +411,6 @@
<translation id="5181140330217080051">Stahování</translation>
<translation id="5190835502935405962">Lišta záložek</translation>
<translation id="5199729219167945352">Experimenty</translation>
-<translation id="5199841536747119669">Zde se zobrazují vaše návrhy</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="5299298092464848405">Při analýze zásady došlo k chybě</translation>
@@ -399,8 +418,10 @@
<translation id="5308689395849655368">Zprávy o selhání jsou zakázány.</translation>
<translation id="5317780077021120954">Uložit</translation>
<translation id="5327248766486351172">Název</translation>
+<translation id="5337705430875057403">Ú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).</translation>
<translation id="5359637492792381994">Server nedokázal prokázat, že patří doménÄ› <ph name="DOMAIN" />. Jeho bezpeÄnostní certifikát v tuto chvíli není platný. Může to být způsobeno nesprávnou konfigurací nebo tím, že vaÅ¡e pÅ™ipojení zachytává útoÄník. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">Ukládání nastavení zásady se nezdařilo</translation>
+<translation id="5386426401304769735">Řetězec certifikátů tohoto webu obsahuje certifikát podepsaný algoritmem SHA-1.</translation>
<translation id="5421136146218899937">Vymazat údaje o prohlížení...</translation>
<translation id="5430298929874300616">Odstranit záložku</translation>
<translation id="5431657950005405462">Soubor nebyl nalezen</translation>
@@ -421,17 +442,20 @@ Kontaktujte administrátora systému.</translation>
<translation id="5544037170328430102">Vložená stránka na webu <ph name="SITE" /> říká:</translation>
<translation id="5556459405103347317">NaÄíst znovu</translation>
<translation id="5565735124758917034">Aktivní</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>
+<translation id="5580958916614886209">Zkontrolujte měsíc vypršení platnosti a zkuste to znovu.</translation>
<translation id="560412284261940334">Správa není podporována</translation>
<translation id="5610142619324316209">Zkontrolovat připojení</translation>
<translation id="5617949217645503996">Web <ph name="HOST_NAME" /> vás přesměroval příliš mnohokrát.</translation>
<translation id="5622887735448669177">Chcete tento web opustit?</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="5669703222995421982">Získejte personalizovaný obsah</translation>
+<translation id="5675650730144413517">Tato stránka nefunguje</translation>
<translation id="5677928146339483299">Zablokováno</translation>
<translation id="5694783966845939798">Pokusili jste se pÅ™ejít na web <ph name="DOMAIN" />, server vÅ¡ak pÅ™edložil certifikát podepsaný slabým algoritmem (například SHA-1). To znamená, že bezpeÄnostní identifikaÄní údaje pÅ™edložené serverem mohou být faleÅ¡né a může se jednat o úplnÄ› jiný server, než pÅ™edpokládáte (můžete komunikovat s útoÄníkem). <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">Identita těchto webových stránek nebyla ověřena.</translation>
<translation id="5720705177508910913">Aktuální uživatel</translation>
-<translation id="572328651809341494">Nedávno použité karty</translation>
<translation id="5732392974455271431">RodiÄe ti jej mohou odblokovat.</translation>
<translation id="5784606427469807560">Při ověřování vaší karty došlo k problému. Zkontrolujte připojení k internetu a zkuste to znovu.</translation>
<translation id="5785756445106461925">Tato stránka obsahuje jeÅ¡tÄ› další nezabezpeÄené zdroje. Tyto zdroje budou bÄ›hem pÅ™enosu moci zobrazit jiní uživatelé a případní útoÄníci je mohou upravit a zmÄ›nit tak vzhled stránky.</translation>
@@ -440,6 +464,7 @@ Kontaktujte administrátora systému.</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="5843436854350372569">Pokusili jste se pÅ™ejít do domény <ph name="DOMAIN" />, ale server pÅ™edložil certifikát se slabým klíÄem. Je možné, že se útoÄníkovi podaÅ™ilo soukromý klÃ­Ä prolomit a že se jedná o jiný server, než pÅ™edpokládáte (můžete komunikovat s útoÄníkem). <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">Nová složka</translation>
<translation id="5869405914158311789">Tento web není dostupný</translation>
@@ -449,6 +474,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="59107663811261420">Tento typ karty ve službě Google Payments u tohoto obchodníka není podporován. Vyberte prosím jinou kartu.</translation>
<translation id="59174027418879706">Aktivní</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="5966707198760109579">Týden</translation>
<translation id="5967867314010545767">Odstranit z historie</translation>
<translation id="5975083100439434680">Oddálit</translation>
@@ -466,8 +492,11 @@ Kontaktujte administrátora systému.</translation>
<translation id="614940544461990577">Zkuste:</translation>
<translation id="6151417162996330722">Certifikát serveru má příliš dlouhé období platnosti.</translation>
<translation id="6165508094623778733">Další informace</translation>
+<translation id="6177128806592000436">Spojení s tímto webem není bezpeÄné</translation>
<translation id="6203231073485539293">Zkontrolujte připojení k internetu</translation>
<translation id="6218753634732582820">Odstranit adresu z prohlížeÄe Chromium?</translation>
+<translation id="6251924700383757765">Zásady ochrany soukromí</translation>
+<translation id="625755898061068298">U tohoto webu jste deaktivovali bezpeÄnostní upozornÄ›ní.</translation>
<translation id="6259156558325130047">&amp;Opakovat změnu uspořádání</translation>
<translation id="6263376278284652872">Záložky webu <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">ZpÄ›t na bezpeÄnÄ›jší stránku</translation>
@@ -476,6 +505,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="6305205051461490394">Web <ph name="URL" /> není dostupný.</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="6337534724793800597">Filtrovat zásady podle názvu</translation>
<translation id="6342069812937806050">PrávÄ› teÄ</translation>
<translation id="6345221851280129312">neznámá velikost</translation>
@@ -484,6 +514,8 @@ Kontaktujte administrátora systému.</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="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>
<translation id="6416403317709441254">Web <ph name="SITE" /> nyní nelze navÅ¡tívit, protože tento web odeslal nesprávné identifikaÄní údaje, které prohlížeÄ Chromium nedokáže zpracovat. Síťové chyby a útoky jsou obvykle doÄasné, tato stránka pravdÄ›podobnÄ› pozdÄ›ji bude fungovat. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">Nelze ověřit, zda byl certifikát zrušen.</translation>
@@ -493,10 +525,12 @@ Kontaktujte administrátora systému.</translation>
<translation id="6458467102616083041">Ignorováno, protože výchozí vyhledávání je zakázáno zásadou.</translation>
<translation id="6462969404041126431">Server nedokázal prokázat, že patří doménÄ› <ph name="DOMAIN" />. Je možné, že jeho bezpeÄnostní certifikát byl zruÅ¡en. Může to být způsobeno nesprávnou konfigurací nebo tím, že vaÅ¡e pÅ™ipojení zachytává útoÄník. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></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="6529602333819889595">&amp;Opakovat smazání</translation>
<translation id="6534179046333460208">Návrhy fyzického webu</translation>
<translation id="6550675742724504774">Možnosti</translation>
+<translation id="6556239504065605927">ZabezpeÄené pÅ™ipojení</translation>
<translation id="6563469144985748109">Správce vám přístup na web dosud neschválil.</translation>
<translation id="6593753688552673085">méně než <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Možnosti šifrování</translation>
@@ -505,7 +539,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="6644283850729428850">Tato zásada se již nepoužívá.</translation>
<translation id="6652240803263749613">Server nedokázal prokázat, že patří doménÄ› <ph name="DOMAIN" />. OperaÄní systém vaÅ¡eho poÄítaÄe 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. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">Úprava složky</translation>
-<translation id="6660210980321319655">Automaticky zaznamenáno <ph name="CRASH_TIME" /></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>
@@ -517,6 +550,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="6753269504797312559">Hodnota zásady</translation>
<translation id="6757797048963528358">Zařízení přešlo do režimu spánku.</translation>
<translation id="6778737459546443941">RodiÄ ti přístup na web dosud neschválil.</translation>
+<translation id="6810899417690483278">ID přizpůsobení</translation>
<translation id="6820686453637990663">BezpeÄnostní kód platební karty (CVC)</translation>
<translation id="6830600606572693159">Stránky na adrese <ph name="URL" /> nejsou v tuto chvíli k dispozici. Důvodem může být přetížení nebo provádění údržby.</translation>
<translation id="6831043979455480757">Přeložit</translation>
@@ -537,6 +571,8 @@ Kontaktujte administrátora systému.</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>
+<translation id="7064851114919012435">Kontaktní údaje</translation>
+<translation id="7079718277001814089">Tento web obsahuje malware</translation>
<translation id="7087282848513945231">Obvod</translation>
<translation id="7088615885725309056">Starší</translation>
<translation id="7090678807593890770">Vyhledejte na Googlu <ph name="LINK" /></translation>
@@ -551,6 +587,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="7210863904660874423">Web <ph name="HOST_NAME" /> nevyhovuje bezpeÄnostním standardům.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Další informace<ph name="END_LINK" /> o tomto problému.</translation>
<translation id="7219179957768738017">Připojení používá protokol <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Webové stránky, které chcete otevřít, obsahují malware</translation>
<translation id="724975217298816891">Chcete-li aktualizovat údaje o kartě, zadejte datum vypršení platnosti a kód CVC karty <ph name="CREDIT_CARD" />. Po ověření budou údaje o kartě sdíleny s tímto webem.</translation>
<translation id="725866823122871198">Soukromé pÅ™ipojení k doménÄ› <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> nelze navázat, protože máte v poÄítaÄi nastaveno chybné datum a Äas (<ph name="DATE_AND_TIME" />).</translation>
<translation id="7269802741830436641">Tato stránka obsahuje smyÄku pÅ™esmÄ›rování</translation>
@@ -606,6 +643,7 @@ Pssst! Příště by se vám mohl hodit anonymní režim (<ph name="SHORTCUT_KEY
<translation id="7658239707568436148">Zrušit</translation>
<translation id="7667346355482952095">Vrácený klÃ­Ä zásady je prázdný nebo neodpovídá aktuálnímu klíÄi.</translation>
<translation id="7668654391829183341">Neznámé zařízení</translation>
+<translation id="7669271284792375604">ÚtoÄníci na tomto webu by se mohli pokusit pÅ™imÄ›t vás k instalaci programů, které nepříznivÄ› ovlivní procházení webu (například zmÄ›ní vaÅ¡i domovskou stránku nebo na navÅ¡tÄ›vovaných stránkách budou zobrazovat další reklamy).</translation>
<translation id="7674629440242451245">Zajímají vás nové funkce Chromu? Vyzkoušejte kanál pro vývojáře na adrese chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />PokraÄovat na web <ph name="SITE" /> (nespolehlivý)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Tento web nelze naÄíst z mezipamÄ›ti</translation>
@@ -621,14 +659,17 @@ Pssst! Příště by se vám mohl hodit anonymní režim (<ph name="SHORTCUT_KEY
<translation id="7800304661137206267">PÅ™ipojení je Å¡ifrováno pomocí standardu <ph name="CIPHER" /> s algoritmem <ph name="MAC" /> pro ověřování zpráv a mechanizmem výmÄ›ny klíÄů <ph name="KX" />.</translation>
<translation id="780301667611848630">Ne, děkuji</translation>
<translation id="7805768142964895445">Stav</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Odstranit návrh položky formuláře z Chromu?</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="7887683347370398519">Zkontrolujte kód CVC a zkuste to znovu</translation>
<translation id="7912024687060120840">Ve složce:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Certifikát serveru ještě není platný.</translation>
<translation id="7942349550061667556">Červená</translation>
+<translation id="7947285636476623132">Zkontrolujte rok vypršení platnosti a zkuste to znovu.</translation>
<translation id="7951415247503192394">(32bitový)</translation>
<translation id="7956713633345437162">Mobilní záložky</translation>
<translation id="7961015016161918242">Nikdy</translation>
@@ -636,7 +677,10 @@ Pssst! Příště by se vám mohl hodit anonymní režim (<ph name="SHORTCUT_KEY
<translation id="7983301409776629893">Vždy překládat z jazyka <ph name="ORIGINAL_LANGUAGE" /> do jazyka <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Není zadáno</translation>
<translation id="8012647001091218357">V tuto chvíli se nám s vaÅ¡imi rodiÄi nepodaÅ™ilo spojit. Zkuste to prosím znovu.</translation>
+<translation id="8025119109950072390">ÚtoÄníci na tomto webu 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).</translation>
+<translation id="803030522067524905">Služba BezpeÄné prohlížení Google nedávno na webu <ph name="SITE" /> zjistila phishing. Phishingové weby se vás snaží oklamat tím, že se vydávají za jiné weby. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">Stránka je v jazyce <ph name="SOURCE_LANGUAGE" />. Chcete ji přeložit do jazyka <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Odeslat svůj názor</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>
@@ -647,9 +691,11 @@ Pssst! Příště by se vám mohl hodit anonymní režim (<ph name="SHORTCUT_KEY
<translation id="8150722005171944719">Soubor na adrese <ph name="URL" /> nelze naÄíst. Možná byl odstranÄ›n, pÅ™esunut nebo mohou přístupu bránit oprávnÄ›ní souboru.</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="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="8230421197304563332">Ú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).</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="8249320324621329438">Naposledy naÄteno:</translation>
<translation id="8261506727792406068">Vymazat</translation>
@@ -658,11 +704,13 @@ Pssst! Příště by se vám mohl hodit anonymní režim (<ph name="SHORTCUT_KEY
<translation id="8294431847097064396">Zdroj</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="8349305172487531364">Lišta záložek</translation>
<translation id="8363502534493474904">Vypnout režim Letadlo</translation>
<translation id="8364627913115013041">Nenastaveno.</translation>
<translation id="8380941800586852976">NebezpeÄné</translation>
<translation id="8382348898565613901">Zde se zobrazí nedávno navštívené záložky</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="8428213095426709021">Nastavení</translation>
@@ -674,9 +722,9 @@ Pssst! Příště by se vám mohl hodit anonymní režim (<ph name="SHORTCUT_KEY
<translation id="8498891568109133222">OdpovÄ›Ä webu <ph name="HOST_NAME" /> trvala příliÅ¡ dlouho.</translation>
<translation id="852346902619691059">Server nedokázal prokázat, že patří doménÄ› <ph name="DOMAIN" />. OperaÄní systém vaÅ¡eho zařízení 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. <ph name="BEGIN_LEARN_MORE_LINK" />Další informace<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Služba Google Payments tento typ karty nepodporuje. Vyberte prosím jinou kartu.</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="8553075262323480129">Překlad se nezdařil. Nepodařilo se rozpoznat jazyk stránky.</translation>
<translation id="8559762987265718583">Soukromé pÅ™ipojení k doménÄ› <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> nelze navázat, protože máte v zařízení nastaveno chybné datum a Äas (<ph name="DATE_AND_TIME" />).</translation>
-<translation id="856992080682148">Certifikát těchto webových stránek vyprší v roce 2017 nebo později a řetězec certifikátů obsahuje certifikát podepsaný pomocí technologie SHA-1.</translation>
<translation id="8571890674111243710">Překlad stránky do jazyka: <ph name="LANGUAGE" />...</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>
@@ -689,10 +737,12 @@ Pssst! Příště by se vám mohl hodit anonymní režim (<ph name="SHORTCUT_KEY
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;Adresu DNS&lt;/abbr&gt; webu <ph name="HOST_NAME" /> nelze najít. Diagnostikování problému…</translation>
<translation id="8790007591277257123">&amp;Opakovat smazání</translation>
<translation id="8798099450830957504">Výchozí</translation>
+<translation id="8800988563907321413">Zde se zobrazí návrhy funkce Nablízku</translation>
<translation id="8804164990146287819">Zásady ochrany soukromí</translation>
<translation id="8820817407110198400">Záložky</translation>
<translation id="8834246243508017242">Povolit automatické vyplňování pomocí Kontaktů…</translation>
<translation id="883848425547221593">Jiné záložky</translation>
+<translation id="884264119367021077">Dodací adresa</translation>
<translation id="884923133447025588">Nebyl nalezen žádný mechanismus zamítnutí.</translation>
<translation id="885730110891505394">Sdílení s Googlem</translation>
<translation id="8866481888320382733">Při analýze nastavení zásady došlo k chybě</translation>
@@ -710,18 +760,21 @@ Pssst! Příště by se vám mohl hodit anonymní režim (<ph name="SHORTCUT_KEY
<translation id="8971063699422889582">Platnost certifikátu serveru vypršela.</translation>
<translation id="8987927404178983737">Měsíc</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Web, na který se chystáte přejít, obsahuje škodlivé programy</translation>
<translation id="9001074447101275817">Proxy <ph name="DOMAIN" /> vyžaduje uživatelské jméno a heslo.</translation>
<translation id="901974403500617787">Příznaky, které platí v celém systému, může nastavit pouze vlastník: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Stránka byla přeložena do jazyka <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Chyba zabezpeÄení</translation>
<translation id="9038649477754266430">Používat službu pÅ™edpovídání k rychlejšímu naÄítání stránek</translation>
<translation id="9039213469156557790">Tato stránka obsahuje jeÅ¡tÄ› další nezabezpeÄené zdroje. Tyto zdroje budou bÄ›hem pÅ™enosu moci zobrazit jiní uživatelé a případní útoÄníci je mohou upravit a zmÄ›nit tak chování stránky.</translation>
+<translation id="9040185888511745258">ÚtoÄníci na webu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> by se mohli pokusit pÅ™imÄ›t vás k instalaci programů, které nepříznivÄ› ovlivní procházení webu (například zmÄ›ní vaÅ¡i domovskou stránku nebo na navÅ¡tÄ›vovaných stránkách budou zobrazovat další reklamy).</translation>
<translation id="9050666287014529139">Heslová fráze</translation>
<translation id="9065203028668620118">Upravit</translation>
<translation id="9068849894565669697">Výběr barvy</translation>
<translation id="9076283476770535406">Může obsahovat materiály pouze pro dospělé</translation>
-<translation id="9092364396508701805">Stránka <ph name="HOST_NAME" /> nefunguje</translation>
<translation id="9103872766612412690">Web <ph name="SITE" /> vaÅ¡e informace běžnÄ› chrání Å¡ifrováním. Když se prohlížeÄ Chromium k webu <ph name="SITE" /> pokusil pÅ™ipojit tentokrát, web vrátil neobvyklé a nesprávné identifikaÄní údaje. K tomuto problému může dojít, pokud se za web <ph name="SITE" /> pokouší vydávat nÄ›jaký útoÄník nebo pokud bylo pÅ™ipojení pÅ™eruÅ¡eno pÅ™ihlaÅ¡ovací obrazovkou sítÄ› Wi-Fi. VaÅ¡e informace jsou i nadále v bezpeÄí, protože prohlížeÄ Chromium pÅ™ipojení pÅ™eruÅ¡il dříve, než doÅ¡lo k odeslání jakýchkoliv dat.</translation>
<translation id="9137013805542155359">Zobrazit originál</translation>
+<translation id="9137248913990643158">Chcete-li tuto aplikaci použít, přihlaste se do Chromu.</translation>
<translation id="9148507642005240123">&amp;Vrátit úpravy zpět</translation>
<translation id="9157595877708044936">Nastavování...</translation>
<translation id="9170848237812810038">Z&amp;pět</translation>
@@ -734,7 +787,6 @@ Pssst! Příště by se vám mohl hodit anonymní režim (<ph name="SHORTCUT_KEY
<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="962701380617707048">Chcete-li aktualizovat údaje o kartě, zadejte datum vypršení platnosti a kód CVC karty <ph name="CREDIT_CARD" />.</translation>
<translation id="969892804517981540">Oficiální sestavení</translation>
<translation id="988159990683914416">Vývojářské sestavení</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 8eab1d8b707..77dca149eca 100644
--- a/chromium/components/strings/components_strings_da.xtb
+++ b/chromium/components/strings/components_strings_da.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Genoprette forbindelse til Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Udskriv...</translation>
<translation id="1181037720776840403">Fjern</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Rapportér automatisk<ph name="END_WHITEPAPER_LINK" /> oplysninger om mulige sikkerhedsproblemer til Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Næste</translation>
<translation id="1201895884277373915">Mere fra dette website</translation>
<translation id="1206967143813997005">Ugyldig første signatur</translation>
@@ -32,7 +33,7 @@
<translation id="1227224963052638717">Ukendt politik.</translation>
<translation id="1227633850867390598">Skjul værdi</translation>
<translation id="1228893227497259893">Forkert enheds-id</translation>
-<translation id="1232569758102978740">Ikke-navngivet</translation>
+<translation id="1232569758102978740">Unavngivet</translation>
<translation id="1254117744268754948">Vælg mappe</translation>
<translation id="1264126396475825575">Der blev registreret en nedbrudsrapport <ph name="CRASH_TIME" /> (endnu ikke uploadet eller ignoreret)</translation>
<translation id="1285320974508926690">Oversæt aldrig dette website</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cyan</translation>
<translation id="1629803312968146339">Skal Chrome gemme dette kort?</translation>
<translation id="1640180200866533862">Brugerpolitikker</translation>
+<translation id="1640244768702815859">Prøv at <ph name="BEGIN_LINK" />gå til websitets startside<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Netværkskonfigurationen er ugyldig og kunne ikke importeres.</translation>
<translation id="1644574205037202324">Historik</translation>
<translation id="1645368109819982629">Ikke-understøttet protokol</translation>
<translation id="1676269943528358898"><ph name="SITE" /> bruger normalt kryptering til at beskytte dine oplysninger. Da Google Chrome forsøgte at oprette forbindelse til <ph name="SITE" /> denne gang, returnerede websitet usædvanlige og forkerte legitimationsoplysninger. Dette kan skyldes, at en hacker forsøger at udgive sig for at være <ph name="SITE" />, eller at en Wi-Fi-loginskærm har forstyrret forbindelsen. Dine oplysninger er stadig sikre, idet Google Chrome afbrød forbindelsen, inden der blev udvekslet data.</translation>
+<translation id="168328519870909584">Hackere, der i øjeblikket befinder sig på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, vil muligvis forsøge at installere farlige apps på din enhed for at stjæle eller slette dine oplysninger (f.eks. billeder, adgangskoder, beskeder og kreditkortoplysninger).</translation>
<translation id="168841957122794586">Servercertifikatet indeholder en svag kryptografisk nøgle.</translation>
-<translation id="1701955595840307032">Hent foreslået indhold</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Du skal have tilladelse fra <ph name="NAME" /> til at besøge dette website</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Sidetal</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Prøv at køre Windows Netværksdiagnosticering<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Opdater din adgangssætning til synkronisering.</translation>
+<translation id="1787142507584202372">Dine åbne faner vises her</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google Beskyttet browsing har for nylig <ph name="BEGIN_LINK" />registreret malware<ph name="END_LINK" /> på <ph name="SITE" />. Websites, der normalt er sikre, bliver undertiden inficeret med malware. Det skadelige indhold kommer fra <ph name="SUBRESOURCE_HOST" />, som er en kendt malwaredistributør. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Ugyldig anmodning eller anmodningsparametre</translation>
+<translation id="1834321415901700177">Dette website indeholder skadelige programmer</translation>
<translation id="1838667051080421715">Du får vist kilden for en webside.</translation>
<translation id="1871208020102129563">Proxy er indstillet til at bruge faste proxyservere, ikke webadresser til .pac-scripts.</translation>
<translation id="1883255238294161206">Skjul liste</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Bogmærker på mobil</translation>
<translation id="2148716181193084225">I dag</translation>
-<translation id="2149973817440762519">Rediger bogmærke</translation>
<translation id="2154054054215849342">Synkronisering er ikke tilgængelig for dit domæne</translation>
<translation id="2166049586286450108">Fuld administratoradgang</translation>
<translation id="2166378884831602661">Dette website kan ikke levere en sikker forbindelse</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Du kan ikke gå til <ph name="SITE" /> lige nu, fordi websitet bruger HSTS. Netværksfejl og hackerangreb er normalt midlertidige, så siden vil sandsynligvis fungere senere. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Ugyldigt bogmærke ved indeks <ph name="ENTRY_INDEX" /> er ignoreret</translation>
<translation id="2354001756790975382">Andre bogmærker</translation>
+<translation id="2355395290879513365">Hackere kan muligvis se de billeder, du kigger på på dette website, og narre dig ved at ændre dem.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Vis politikker uden nogen værdier</translation>
<translation id="2396249848217231973">&amp;Fortryd sletning</translation>
<translation id="2455981314101692989">Denne webside har deaktiveret automatisk udfyldning af denne formular.</translation>
+<translation id="2460160116472764928">Google Beskyttet browsing har for nylig <ph name="BEGIN_LINK" />registreret malware<ph name="END_LINK" /> på <ph name="SITE" />. Websites, der normalt er sikre, bliver undertiden inficeret med malware. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Udfyld</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Køre Netværksdiagnosticering<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Ugyldig søgewebadresse.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Er du sikker på, at du vil slette disse sider fra din historik?</translation>
<translation id="2677748264148917807">Forlad</translation>
<translation id="269990154133806163">Serveren præsenterede et certifikat, som ikke var offentliggjort via politikken Certifikatgennemsigtighed. Dette er et krav for visse certifikater for at sikre, at de er pålidelige og beskytter mod hackere. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Læseliste</translation>
<translation id="2704283930420550640">Værdien stemmer ikke overens med formatet.</translation>
<translation id="2704951214193499422">Chromium kan ikke bekræfte dit kort i øjeblikket. Prøv igen senere.</translation>
<translation id="2705137772291741111">Den gemte (cachelagrede) kopi af dette website kunne ikke læses.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Du har forsøgt at få forbindelse til <ph name="DOMAIN" />, men serverens certifikat er blevet tilbagekaldt af udstederen. Det betyder, at du bestemt ikke bør have tillid til serverens sikkerhedsoplysninger. Du kommunikerer muligvis med en hacker. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Spørg om tilladelse</translation>
<translation id="2713444072780614174">Hvid</translation>
+<translation id="2720342946869265578">Tæt på</translation>
<translation id="2721148159707890343">Anmodning lykkedes</translation>
<translation id="2728127805433021124">Serverens certifikat er signeret ved hjælp af en svag signaturalgoritme.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Køre Diagnosticering af forbindelse<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Du kan deaktivere alle proxyer, der er konfigureret for en forbindelse, fra siden Indstillinger.</translation>
<translation id="2955913368246107853">Luk søgefeltet</translation>
<translation id="2958431318199492670">Netværkskonfigurationen overholder ikke ONC-standarden. Dele af konfiguration kan muligvis ikke importeres.</translation>
+<translation id="29611076221683977">Hackere, der i øjeblikket befinder sig på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, vil muligvis forsøge at installere skadelige programmer på din Mac for at stjæle eller slette dine oplysninger (f.eks. billeder, adgangskoder, beskeder og kreditkortoplysninger).</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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Skjul oplysninger</translation>
<translation id="3587482841069643663">Alle</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Serveren understøtter ikke tilføjelse af TLS-genforhandling.</translation>
<translation id="36224234498066874">Ryd browserdata...</translation>
<translation id="362276910939193118">Vis hele historikken</translation>
<translation id="3623476034248543066">Vis værdi</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Licenserne er opbrugt</translation>
<translation id="3714780639079136834">Tænde for mobildata eller Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Tjekke proxy-, firewall- og DNS-konfigurationen<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Hvis du forstår den sikkerhedsrisiko, du udsætter dig for, kan du <ph name="BEGIN_LINK" />gå til dette usikre website<ph name="END_LINK" />, inden de farlige programmer er fjernet.</translation>
<translation id="3739623965217189342">Link, du har kopieret</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="3788090790273268753">Certifikatet for dette website udløber i 2016, og certifikatkæden indeholder et certifikat, der er signeret med SHA-1.</translation>
<translation id="382518646247711829">Hvis du bruger en proxyserver...</translation>
<translation id="3828924085048779000">Tomme adgangssætninger er ikke tilladt.</translation>
<translation id="3845539888601087042">Viser historik fra de enheder, hvor du er logget ind. <ph name="BEGIN_LINK" />FÃ¥ flere oplysninger<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Nøgle "<ph name="SUBKEY" />": <ph name="ERROR" /></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="4103249731201008433">Enhedens serienummer er ugyldigt</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>
<translation id="4110615724604346410">Denne server kunne ikke bevise, at den tilhører <ph name="DOMAIN" />, da sikkerhedscertifikatet indeholder fejl. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ingen}=1{1 app ($1)}=2{2 apps ($1, $2)}one{# app ($1, $2, $3)}other{# apps ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Nedbrud</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Prøv at køre Netværksdiagnosticering<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Din forbindelse til dette website er ikke helt sikker</translation>
<translation id="4250680216510889253">Nej</translation>
<translation id="425582637250725228">Ændringer, du har foretaget, gemmes muligvis ikke.</translation>
<translation id="4258748452823770588">Forkert signatur</translation>
<translation id="4269787794583293679">(Intet brugernavn)</translation>
+<translation id="4280429058323657511">udløber <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google Beskyttet browsing har for nylig <ph name="BEGIN_LINK" />fundet skadelige programmer<ph name="END_LINK" /> på <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Forslag fra forældre</translation>
<translation id="4304224509867189079">Log ind</translation>
<translation id="432290197980158659">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. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Artiklen blev ikke fundet.</translation>
+<translation id="4326324639298822553">Kontrollér, om udløbsdatoen er korrekt, og prøv igen.</translation>
<translation id="4331708818696583467">Ikke sikker</translation>
+<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="4381091992796011497">Brugernavn:</translation>
<translation id="4394049700291259645">Deaktiver</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Du har forsøgt at få forbindelse til <ph name="DOMAIN" />, men serveren præsenterede et ugyldigt certifikat. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Faner fra andre enheder</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Du er i øjeblikket på en udvidelsesside.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Opdater politikker</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4744603770635761495">Eksekverbar sti</translation>
+<translation id="4750917950439032686">Dine oplysninger (f.eks. adgangskoder eller kreditkortnumre) er private, når de sendes til dette website.</translation>
<translation id="4756388243121344051">&amp;Historik</translation>
+<translation id="4759118997339041434">Autofyld af betalingsoplysninger er deaktiveret</translation>
<translation id="4764776831041365478">Websiden på <ph name="URL" /> kan være midlertidigt nede, eller også er den permanent flyttet til en ny webadresse.</translation>
<translation id="4771973620359291008">Der er opstået en ukendt fejl.</translation>
<translation id="4800132727771399293">Kontrollér, om din kontrolkode og udløbsdato er korrekte, og prøv igen.</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Netværksfejl</translation>
<translation id="4816492930507672669">Tilpas til siden</translation>
<translation id="4850886885716139402">Vis</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{og 1 anden webside}one{og # anden webside}other{og # andre websider}}</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="495170559598752135">Handlinger</translation>
<translation id="4958444002117714549">Udvid liste</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Downloader</translation>
<translation id="5190835502935405962">Bogmærkelinje</translation>
<translation id="5199729219167945352">Eksperimenter</translation>
-<translation id="5199841536747119669">Forslag til dig vises her</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="5299298092464848405">Der opstod en fejl ved parsing af politik</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Rapportering af nedbrud er deaktiveret.</translation>
<translation id="5317780077021120954">Gem</translation>
<translation id="5327248766486351172">Navn</translation>
+<translation id="5337705430875057403">Hackere på <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).</translation>
<translation id="5359637492792381994">Denne server kunne ikke bevise, at den tilhører <ph name="DOMAIN" />, da sikkerhedscertifikatet ikke er gyldigt i øjeblikket. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Der kunne ikke gemmes indstillinger for politik</translation>
+<translation id="5386426401304769735">Certifikatkæden for dette website indeholder et certifikat, der er signeret med SHA-1.</translation>
<translation id="5421136146218899937">Ryd browserdata...</translation>
<translation id="5430298929874300616">Fjern bogmærke</translation>
<translation id="5431657950005405462">Din fil blev ikke fundet</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">En integreret side på <ph name="SITE" /> siger:</translation>
<translation id="5556459405103347317">Genindlæs</translation>
<translation id="5565735124758917034">Aktiv</translation>
+<translation id="5572851009514199876">Start og log ind på Chrome, så Chrome kan kontrollere, om du har adgang til dette website.</translation>
+<translation id="5580958916614886209">Kontrollér, om udløbsmåneden er korrekt, og prøv igen.</translation>
<translation id="560412284261940334">Administration er ikke understøttet</translation>
<translation id="5610142619324316209">Kontrollere forbindelsen</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> omdirigerede dig for mange gange.</translation>
<translation id="5622887735448669177">Vil du forlade denne side?</translation>
<translation id="5629630648637658800">Der kunne ikke indlæses indstillinger for politik</translation>
<translation id="5631439013527180824">Ugyldigt token for enhedsadministration</translation>
+<translation id="5669703222995421982">FÃ¥ tilpasset indhold</translation>
+<translation id="5675650730144413517">Denne side virker ikke</translation>
<translation id="5677928146339483299">Blokeret</translation>
<translation id="5694783966845939798">Du forsøgte at få forbindelse til <ph name="DOMAIN" />, men serveren har præsenteret et certifikat, der er signeret med en svag signaturalgoritme (f.eks. SHA-1). Det betyder, at sikkerhedsoplysningerne fra serveren kan være forfalskede, og at serveren muligvis ikke er den server, du forventede (du kommunikerer muligvis med en hacker). <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Dette websites identitet er ikke blevet bekræftet.</translation>
<translation id="5720705177508910913">Aktuel bruger</translation>
-<translation id="572328651809341494">Seneste faner</translation>
<translation id="5732392974455271431">Dine forældre kan fjerne blokeringen for dig</translation>
<translation id="5784606427469807560">Der opstod et problem under bekræftelsen af dit kort. Kontrollér, at du har forbindelse til internettet, og prøv igen.</translation>
<translation id="5785756445106461925">Desuden indeholder denne side andre ressourcer, som ikke er sikre. Disse ressourcer kan ses af andre under overførslen og kan ændres af en hacker, så siden ser anderledes ud.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Du har forsøgt at få forbindelse til <ph name="DOMAIN" />, men serveren har præsenteret et certifikat med en svag nøgle. En hacker kan have knækket den private nøgle, og serveren er muligvis ikke den forventede server (du kommunikerer muligvis med en hacker). <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Ny mappe</translation>
<translation id="5869405914158311789">Der kan ikke oprettes forbindelse til dette website</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Denne korttype understøttes ikke af Google Payments for denne sælger. Vælg et andet kort.</translation>
<translation id="59174027418879706">Aktiveret</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="5966707198760109579">Uge</translation>
<translation id="5967867314010545767">Fjern fra historik</translation>
<translation id="5975083100439434680">Zoom ud</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Prøv at:</translation>
<translation id="6151417162996330722">Servercertifikatet har en gyldighedsperiode, der er for lang.</translation>
<translation id="6165508094623778733">Flere oplysninger</translation>
+<translation id="6177128806592000436">Din forbindelse til dette website er ikke sikker.</translation>
<translation id="6203231073485539293">Kontrollér din internetforbindelse</translation>
<translation id="6218753634732582820">Vil du fjerne adressen fra Chromium?</translation>
+<translation id="6251924700383757765">Privatlivspolitik</translation>
+<translation id="625755898061068298">Du har valgt at deaktivere sikkerhedsadvarsler for dette website.</translation>
<translation id="6259156558325130047">&amp;Annuller fortryd omarrangering</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" />-bogmærker</translation>
<translation id="6264485186158353794">Tilbage i sikkerhed</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> kan ikke nås.</translation>
<translation id="6321917430147971392">Kontrollér dine DNS-indstillinger</translation>
<translation id="6328639280570009161">Prøv at deaktivere netværksforslag</translation>
+<translation id="6328786501058569169">Dette website er vildledende</translation>
<translation id="6337534724793800597">Filtrer politikker efter navn</translation>
<translation id="6342069812937806050">Lige nu</translation>
<translation id="6345221851280129312">ukendt størrelse</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Du kan ikke gå til <ph name="SITE" /> lige nu, da websitet sender krypterede loginoplysninger, som Chromium ikke kan behandle. Netværksfejl og hackerangreb er normalt midlertidige, så siden vil sandsynligvis fungere senere. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Kan ikke kontrollere, om certifikatet er tilbagekaldt.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Ignoreret, fordi standardsøgning er deaktiveret af politikken.</translation>
<translation id="6462969404041126431">Denne server kunne ikke bevise, at den tilhører <ph name="DOMAIN" />, da sikkerhedscertifikatet muligvis er blevet tilbagekaldt. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Annuller fortryd slet</translation>
<translation id="6534179046333460208">Forslag til Fysisk web</translation>
<translation id="6550675742724504774">Valgmuligheder</translation>
+<translation id="6556239504065605927">Sikker forbindelse</translation>
<translation id="6563469144985748109">Din administrator har ikke godkendt det endnu</translation>
<translation id="6593753688552673085">mindre end <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Krypteringsmuligheder</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Denne politik er forældet.</translation>
<translation id="6652240803263749613">Denne server kunne ikke bevise, at den tilhører <ph name="DOMAIN" />, da din computers operativsystem ikke har tillid til sikkerhedscertifikatet. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Rediger mappe</translation>
-<translation id="6660210980321319655">Rapporteret automatisk <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Politikkens værdi</translation>
<translation id="6757797048963528358">Din enhed gik i dvale.</translation>
<translation id="6778737459546443941">Din forælder har ikke godkendt det endnu</translation>
+<translation id="6810899417690483278">Tilpasnings-id</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Websiden på <ph name="URL" /> er ikke tilgængelig lige nu. Den kan enten være overbelastet eller under vedligeholdelse.</translation>
<translation id="6831043979455480757">Oversæt</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Kontaktoplysninger</translation>
+<translation id="7079718277001814089">Dette website indeholder malware</translation>
<translation id="7087282848513945231">Amt/region</translation>
<translation id="7088615885725309056">Ældre</translation>
<translation id="7090678807593890770">Søg efter <ph name="LINK" /> på Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> overholder ikke sikkerhedsstandarderne.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />FÃ¥ flere oplysninger<ph name="END_LINK" /> om dette problem.</translation>
<translation id="7219179957768738017">Forbindelsen bruger <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Det site, du er på vej til, indeholder malware</translation>
<translation id="724975217298816891">Opdater dine kortoplysninger ved at indtaste udløbsdatoen og kontrolkoden for <ph name="CREDIT_CARD" />. Når du bekræfter, deles dine kortoplysninger med dette website.</translation>
<translation id="725866823122871198">Der kunne ikke oprettes en privat forbindelse til <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, da dato og tid (<ph name="DATE_AND_TIME" />) på din enhed er forkerte.</translation>
<translation id="7269802741830436641">Denne webside har et loop ved omdirigering</translation>
@@ -611,6 +648,7 @@ Psst! Prøv at bruge Inkognitotilstand <ph name="SHORTCUT_KEY" /> næste gang.</
<translation id="7658239707568436148">Annuller</translation>
<translation id="7667346355482952095">Det returnerede token for politikken er tomt eller stemmer ikke overens med det nuværende token</translation>
<translation id="7668654391829183341">Ukendt enhed</translation>
+<translation id="7669271284792375604">Hackere på dette website kan forsøge at narre dig til at installere programmer, der skader din browseroplevelse (f.eks. ved at ændre din startside eller vise flere annoncer på de websites, du besøger).</translation>
<translation id="7674629440242451245">Er du interesseret i smarte nye Chrome-funktioner? Prøv vores udviklerkanal på chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Fortsæt til <ph name="SITE" /> (usikkert)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Dette website kan ikke indlæses fra cachen</translation>
@@ -626,14 +664,17 @@ Psst! Prøv at bruge Inkognitotilstand <ph name="SHORTCUT_KEY" /> næste gang.</
<translation id="7800304661137206267">Forbindelsen er krypteret ved hjælp af <ph name="CIPHER" /> med <ph name="MAC" /> til meddelelsesgodkendelse og <ph name="KX" /> som hovedudvekslingsmekanisme.</translation>
<translation id="780301667611848630">Nej tak</translation>
<translation id="7805768142964895445">Status</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Vil du fjerne formularforslaget fra Chrome?</translation>
<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="7887683347370398519">Kontrollér, om din kontrolkode er korrekt, og prøv igen.</translation>
<translation id="7912024687060120840">I mappe:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Serverens certifikatet er endnu ikke gyldigt.</translation>
<translation id="7942349550061667556">Rød</translation>
+<translation id="7947285636476623132">Kontrollér, om udløbsåret er korrekt, og prøv igen.</translation>
<translation id="7951415247503192394">(32-bit)</translation>
<translation id="7956713633345437162">Bogmærker på mobil</translation>
<translation id="7961015016161918242">Aldrig</translation>
@@ -641,7 +682,10 @@ Psst! Prøv at bruge Inkognitotilstand <ph name="SHORTCUT_KEY" /> næste gang.</
<translation id="7983301409776629893">Oversæt altid <ph name="ORIGINAL_LANGUAGE" /> til <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Ikke angivet</translation>
<translation id="8012647001091218357">Vi kan ikke få kontakt til dine forældre på nuværende tidspunkt. Prøv igen.</translation>
+<translation id="8025119109950072390">Hackere på dette website 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).</translation>
+<translation id="803030522067524905">Google Beskyttet browsing har for nylig registreret phishing på <ph name="SITE" />. Phishingwebsites udgiver sig for at være andre websites for at narre dig. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Denne side er på <ph name="SOURCE_LANGUAGE" />. Vil du oversætte den til <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Send feedback</translation>
<translation id="8088680233425245692">Artiklen kunne ikke vises.</translation>
<translation id="8089520772729574115">mindre end 1 MB</translation>
<translation id="8091372947890762290">Aktivering afventer serveren</translation>
@@ -652,9 +696,11 @@ Psst! Prøv at bruge Inkognitotilstand <ph name="SHORTCUT_KEY" /> næste gang.</
<translation id="8150722005171944719">Filen i <ph name="URL" /> kan ikke læses. Den kan være blevet fjernet, flyttet, eller også forhindrer filtilladelser muligvis adgangen.</translation>
<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="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="8230421197304563332">Hackere, der i øjeblikket befinder sig på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, 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="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="8249320324621329438">Sidste hentet:</translation>
<translation id="8261506727792406068">Slet</translation>
@@ -663,11 +709,13 @@ Psst! Prøv at bruge Inkognitotilstand <ph name="SHORTCUT_KEY" /> næste gang.</
<translation id="8294431847097064396">Kilde</translation>
<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="8349305172487531364">Bogmærkelinje</translation>
<translation id="8363502534493474904">Deaktivere flytilstand</translation>
<translation id="8364627913115013041">Ikke angivet.</translation>
<translation id="8380941800586852976">Farlig</translation>
<translation id="8382348898565613901">De bogmærker, du har besøgt for nylig, vises her</translation>
+<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="8428213095426709021">Indstillinger</translation>
@@ -679,9 +727,9 @@ Psst! Prøv at bruge Inkognitotilstand <ph name="SHORTCUT_KEY" /> næste gang.</
<translation id="8498891568109133222"><ph name="HOST_NAME" /> var for lang tid om at svare.</translation>
<translation id="852346902619691059">Denne server kunne ikke bevise, at den tilhører <ph name="DOMAIN" />, da din enheds operativsystem ikke har tillid til sikkerhedscertifikatet. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse. <ph name="BEGIN_LEARN_MORE_LINK" />Få flere oplysninger<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Denne korttype understøttes ikke af Google Payments. Vælg et andet kort.</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="8553075262323480129">Oversættelsen mislykkedes, fordi sidens sprog ikke kunne fastslås.</translation>
<translation id="8559762987265718583">Der kan ikke oprettes en privat forbindelse til <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, da tid og dato (<ph name="DATE_AND_TIME" />) på din enhed er forkerte.</translation>
-<translation id="856992080682148">Certifikatet for dette website udløber i 2017 eller senere, og certifikatkæden indeholder et certifikat, der er signeret med SHA-1.</translation>
<translation id="8571890674111243710">Oversætter siden til <ph name="LANGUAGE" />...</translation>
<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>
@@ -694,10 +742,12 @@ Psst! Prøv at bruge Inkognitotilstand <ph name="SHORTCUT_KEY" /> næste gang.</
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;DNS-adressen&lt;/abbr&gt; for <ph name="HOST_NAME" /> blev ikke fundet. Diagnosticerer problemet.</translation>
<translation id="8790007591277257123">&amp;Annuller fortryd sletning</translation>
<translation id="8798099450830957504">Standard</translation>
+<translation id="8800988563907321413">Her vises de forslag, der er tæt på dig</translation>
<translation id="8804164990146287819">Privatlivspolitik</translation>
<translation id="8820817407110198400">Bogmærker</translation>
<translation id="8834246243508017242">Aktivér autofyld med kontaktpersoner...</translation>
<translation id="883848425547221593">Andre bogmærker</translation>
+<translation id="884264119367021077">Leveringsadresse</translation>
<translation id="884923133447025588">Der blev ikke fundet nogen funktion til tilbagekaldelse.</translation>
<translation id="885730110891505394">Deling med Google</translation>
<translation id="8866481888320382733">Der opstod en fejl ved parsing af indstillinger for politik</translation>
@@ -715,18 +765,21 @@ Psst! Prøv at bruge Inkognitotilstand <ph name="SHORTCUT_KEY" /> næste gang.</
<translation id="8971063699422889582">Serverens certifikat er udløbet.</translation>
<translation id="8987927404178983737">MÃ¥ned</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Det website, du er på vej til, indeholder skadelige programmer</translation>
<translation id="9001074447101275817">Proxyserveren <ph name="DOMAIN" /> kræver et brugernavn og en adgangskode.</translation>
<translation id="901974403500617787">Markeringer, der gælder for hele systemet, kan kun indstilles af ejeren: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Denne side er oversat til <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Sikkerhedsfejl</translation>
<translation id="9038649477754266430">Brug en forudsigelsestjeneste til hurtigere sideindlæsning</translation>
<translation id="9039213469156557790">Desuden indeholder denne side andre ressourcer, som ikke er sikre. Disse ressourcer kan ses af andre under overførslen og kan ændres af en hacker, så siden opfører sig anderledes.</translation>
+<translation id="9040185888511745258">Hackere, der befinder sig på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kan forsøge at narre dig til at installere programmer, der skader din browseroplevelse (f.eks. ved at ændre din startside eller vise flere annoncer på de websites, du besøger).</translation>
<translation id="9050666287014529139">Adgangssætning</translation>
<translation id="9065203028668620118">Rediger</translation>
<translation id="9068849894565669697">Vælg farve</translation>
<translation id="9076283476770535406">Der er muligvis voksenindhold på websitet</translation>
-<translation id="9092364396508701805">Siden fra <ph name="HOST_NAME" /> fungerer ikke</translation>
<translation id="9103872766612412690"><ph name="SITE" /> bruger normalt kryptering til at beskytte dine oplysninger. Da Chromium forsøgte at oprette forbindelse til <ph name="SITE" /> denne gang, returnerede websitet usædvanlige og forkerte loginoplysninger. Dette kan skyldes, at en hacker forsøger at udgive sig for at være <ph name="SITE" />, eller at en Wi-Fi-loginskærm har forstyrret forbindelsen. Dine oplysninger er stadig sikre, idet Chromium afbrød forbindelsen, inden der blev udvekslet data.</translation>
<translation id="9137013805542155359">Vis oprindelig</translation>
+<translation id="9137248913990643158">Start og log ind på Chrome, inden du bruger denne app.</translation>
<translation id="9148507642005240123">&amp;Fortryd redigering</translation>
<translation id="9157595877708044936">Konfigurerer...</translation>
<translation id="9170848237812810038">&amp;Fortryd</translation>
@@ -739,7 +792,6 @@ Psst! Prøv at bruge Inkognitotilstand <ph name="SHORTCUT_KEY" /> næste gang.</
<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="962701380617707048">Indtast udløbsdato og kontrolkode for <ph name="CREDIT_CARD" /> for at opdatere dine kortoplysninger</translation>
<translation id="969892804517981540">Officiel version</translation>
<translation id="988159990683914416">Udviklerversion</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 941dc15dc80..f3d1d90f54f 100644
--- a/chromium/components/strings/components_strings_de.xtb
+++ b/chromium/components/strings/components_strings_de.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">WLAN-Verbindung erneut herstellen</translation>
<translation id="1175364870820465910">&amp;Drucken...</translation>
<translation id="1181037720776840403">Entfernen</translation>
+<translation id="1184214524891303587">Informationen zu sicherheitsrelevanten Zwischenfällen <ph name="BEGIN_WHITEPAPER_LINK" />automatisch Google melden<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Weiter</translation>
<translation id="1201895884277373915">Mehr von dieser Website</translation>
<translation id="1206967143813997005">Erste Signatur ungültig</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cyan</translation>
<translation id="1629803312968146339">Möchten Sie, dass Chrome diese Karte speichert?</translation>
<translation id="1640180200866533862">Nutzerrichtlinien</translation>
+<translation id="1640244768702815859">Versuchen Sie, <ph name="BEGIN_LINK" />die Startseite aufzurufen<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Die Netzwerkkonfiguration ist ungültig und konnte nicht importiert werden.</translation>
<translation id="1644574205037202324">Verlauf</translation>
<translation id="1645368109819982629">Nicht unterstütztes Protokoll</translation>
<translation id="1676269943528358898"><ph name="SITE" /> schützt Ihre Daten in der Regel durch Verschlüsselung. Als Google Chrome dieses Mal versuchte, eine Verbindung zu <ph name="SITE" /> herzustellen, gab die Website ungewöhnliche und falsche Anmeldedaten zurück. Entweder versucht ein Angreifer, sich als <ph name="SITE" /> auszugeben, oder die Verbindung wurde durch eine WLAN-Anmeldeseite unterbrochen. Da Google Chrome die Verbindung vor dem Austausch von Daten unterbrochen hat, sind Ihre Informationen weiterhin sicher.</translation>
+<translation id="168328519870909584">Hacker, die derzeit auf <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> zugreifen, versuchen unter Umständen, gefährliche Programme auf Ihrem Gerät zu installieren, um Ihre Daten zu stehlen oder zu löschen, zum Beispiel Fotos, Passwörter, Nachrichten und Kreditkartendaten.</translation>
<translation id="168841957122794586">Das Serverzertifikat weist einen schwachen kryptografischen Schlüssel auf.</translation>
-<translation id="1701955595840307032">Vorgeschlagene Inhalte erhalten</translation>
<translation id="1710259589646384581">Betriebssystem</translation>
<translation id="1721312023322545264">Du benötigst die Berechtigung von <ph name="NAME" />, um diese Website zu besuchen</translation>
<translation id="1734864079702812349">American Express</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Seitennummer</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Versuchen Sie, die Windows-Netzwerkdiagnose auszuführen.<ph name="END_LINK" /></translation>
<translation id="1783075131180517613">Synchronisierungs-Passphrase aktualisieren</translation>
+<translation id="1787142507584202372">Hier werden Ihre offenen Tabs angezeigt</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">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. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">Anfrage oder Anfrageparameter ungültig</translation>
+<translation id="1834321415901700177">Diese Website enthält schädliche Programme</translation>
<translation id="1838667051080421715">Dies ist die Quelle einer Webseite.</translation>
<translation id="1871208020102129563">Der Proxy ist zur Verwendung von festen Proxyservern konfiguriert, nicht zur Verwendung einer PAC-Skript-URL.</translation>
<translation id="1883255238294161206">Liste ausblenden</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">Postleitzahl</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 Vorschlag}other{# Vorschläge}}</translation>
<translation id="2065985942032347596">Authentifizierung erforderlich</translation>
-<translation id="2079545284768500474">Rückgängig</translation>
+<translation id="2079545284768500474">Rückgängig machen</translation>
<translation id="20817612488360358">Die System-Proxy-Einstellungen sind zur Verwendung angegeben, gleichzeitig wurde aber auch eine explizite Proxy-Konfiguration festgelegt.</translation>
<translation id="2086652334978798447">Melden Sie sich in Chrome an, um personalisierte, von Google vorgeschlagene Inhalte zu erhalten.</translation>
<translation id="2089090684895656482">Weniger</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Mobile Lesezeichen</translation>
<translation id="2148716181193084225">Heute</translation>
-<translation id="2149973817440762519">Lesezeichen bearbeiten</translation>
<translation id="2154054054215849342">Die Synchronisierung ist für Ihre Domain nicht verfügbar</translation>
<translation id="2166049586286450108">Vollständiger Administratorzugriff</translation>
<translation id="2166378884831602661">Diese Website kann keine sichere Verbindung bereitstellen</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Sie können <ph name="SITE" /> zurzeit nicht aufrufen, da die Website HSTS verwendet. Netzwerkfehler und Angriffe sind in der Regel nur vorübergehend, sodass die Seite wahrscheinlich später wieder funktioniert. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">Ungültiges Lesezeichen bei Index <ph name="ENTRY_INDEX" /> ignoriert</translation>
<translation id="2354001756790975382">Weitere Lesezeichen</translation>
+<translation id="2355395290879513365">Angreifer können unter Umständen die Bilder sehen, die Sie sich auf dieser Website ansehen, und könnten dann versuchen, Sie durch Ändern der Bilder zu täuschen.</translation>
<translation id="2359808026110333948">Weiter</translation>
<translation id="2365563543831475020">Absturzbericht erfasst um <ph name="CRASH_TIME" />, wurde nicht hochgeladen</translation>
<translation id="2367567093518048410">Ebene</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Richtlinien ohne Wert zeigen</translation>
<translation id="2396249848217231973">&amp;Löschen rückgängig machen</translation>
<translation id="2455981314101692989">Auf dieser Webseite ist die AutoFill-Funktion für das Formular deaktiviert.</translation>
+<translation id="2460160116472764928">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. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">Ausfüllen</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Netzwerkdiagnose ausführen<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Ungültige Such-URL</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Möchten Sie diese Seiten wirklich aus dem Verlauf löschen?</translation>
<translation id="2677748264148917807">Verlassen</translation>
<translation id="269990154133806163">Der Server hat ein Zertifikat übermittelt, das nicht gemäß der Richtlinien 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. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">Leseliste</translation>
<translation id="2704283930420550640">Wert stimmt nicht mit dem Format überein.</translation>
<translation id="2704951214193499422">Ihre Karte kann von Chromium zurzeit nicht bestätigt werden. Bitte versuchen Sie es später noch einmal.</translation>
<translation id="2705137772291741111">Die (im Cache) gespeicherte Kopie dieser Website war nicht lesbar.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">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. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">Berechtigung anfordern</translation>
<translation id="2713444072780614174">Weiß</translation>
+<translation id="2720342946869265578">In der Nähe</translation>
<translation id="2721148159707890343">Anfrage erfolgreich</translation>
<translation id="2728127805433021124">Das Serverzertifikat ist mit einem schwachen Signaturalgorithmus signiert.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Verbindungsdiagnose ausführen<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Sie können alle für eine Verbindung konfigurierten Proxys auf der Seite "Einstellungen" deaktivieren.</translation>
<translation id="2955913368246107853">Suchleiste schließen</translation>
<translation id="2958431318199492670">Die Netzwerkkonfiguration stimmt nicht mit dem ONC-Standard überein. Die Konfiguration wird unter Umständen nicht vollständig importiert.</translation>
+<translation id="29611076221683977">Zurzeit auf <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> befindliche Angreifer versuchen unter Umständen, gefährliche Programme auf Ihrem Mac zu installieren, um Ihre Daten zu stehlen oder zu löschen, zum Beispiel Fotos, Passwörter, Nachrichten und Kreditkartendaten.</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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Details ausblenden</translation>
<translation id="3587482841069643663">Alle</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Der Server unterstützt die TLS-Renegotiation-Erweiterung nicht.</translation>
<translation id="36224234498066874">Browserdaten löschen...</translation>
<translation id="362276910939193118">Gesamtverlauf anzeigen</translation>
<translation id="3623476034248543066">Wert zeigen</translation>
@@ -270,6 +278,7 @@
<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="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>
@@ -279,10 +288,10 @@
<translation id="3712624925041724820">Lizenzen aufgebraucht</translation>
<translation id="3714780639079136834">Mobile Daten oder WLAN aktivieren</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Proxy, Firewall und DNS-Konfiguration prüfen<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Wenn Sie die Sicherheitsrisiken kennen, können Sie <ph name="BEGIN_LINK" />diese unsichere Website aufrufen<ph name="END_LINK" />, bevor die gefährlichen Programme entfernt wurden.</translation>
<translation id="3739623965217189342">Von Ihnen kopierter Link</translation>
<translation id="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="3788090790273268753">Das Zertifikat für diese Website läuft 2016 ab und die Zertifikatskette enthält ein Zertifikat mit SHA-1-Signatur.</translation>
<translation id="382518646247711829">Falls Sie einen Proxyserver verwenden...</translation>
<translation id="3828924085048779000">Eine leere Passphrase ist nicht zulässig.</translation>
<translation id="3845539888601087042">Der Verlauf für alle Geräte, auf denen Sie angemeldet sind, wird angezeigt. <ph name="BEGIN_LINK" />Weitere Informationen.<ph name="END_LINK" /></translation>
@@ -304,6 +313,7 @@
<translation id="4058922952496707368">Schlüssel "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Client und Server unterstützen keine gemeinsame SSL-Protokollversion oder Verschlüsselungssammlung.</translation>
<translation id="4079302484614802869">Die Proxy-Konfiguration ist auf die Verwendung einer PAC-Skript-URL und nicht die von festen Proxyservern eingestellt.</translation>
+<translation id="4098354747657067197">Bei der aufgerufenen Website besteht Phishing-Verdacht!</translation>
<translation id="4103249731201008433">Seriennummer des Geräts ist ungültig.</translation>
<translation id="4103763322291513355">Unter &lt;strong&gt;chrome://policy&lt;/strong&gt; finden Sie eine Liste der blockierten URLs und andere Richtlinien, die durch Ihren Systemadministrator erzwungen werden.</translation>
<translation id="4110615724604346410">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. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -321,15 +331,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{keine}=1{1 App ($1)}=2{2 Apps ($1, $2)}other{# Apps ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Abstürze</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Versuchen Sie, die Netzwerkdiagnose auszuführen.<ph name="END_LINK" /></translation>
+<translation id="4250431568374086873">Die Verbindung zu dieser Website ist nicht uneingeschränkt sicher</translation>
<translation id="4250680216510889253">Nein</translation>
<translation id="425582637250725228">Die von Ihnen vorgenommenen Änderungen werden möglicherweise nicht gespeichert.</translation>
<translation id="4258748452823770588">Fehlerhafte Signatur</translation>
<translation id="4269787794583293679">(Kein Nutzername)</translation>
+<translation id="4280429058323657511">Gültig bis: <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google Safe Browsing hat kürzlich <ph name="BEGIN_LINK" />schädliche Programme<ph name="END_LINK" /> auf <ph name="SITE" /> gefunden. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">Vorschläge für Eltern</translation>
<translation id="4304224509867189079">Anmelden</translation>
<translation id="432290197980158659">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. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">Der Artikel wurde nicht gefunden.</translation>
+<translation id="4326324639298822553">Prüfen Sie Ihr Ablaufdatum und versuchen Sie es dann erneut</translation>
<translation id="4331708818696583467">Nicht sicher</translation>
+<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="4381091992796011497">Nutzername:</translation>
<translation id="4394049700291259645">Deaktivieren</translation>
@@ -347,6 +362,7 @@
<translation id="4589078953350245614">Sie haben versucht, auf <ph name="DOMAIN" /> zuzugreifen, der Server hat sich jedoch mit einem ungültigen Zertifikat ausgewiesen. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></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="4619615317237390068">Tabs von anderen Geräten</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Sie sehen sich eine Erweiterungsseite an.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -355,10 +371,13 @@
<translation id="4726672564094551039">Richtlinien neu laden</translation>
<translation id="4728558894243024398">Plattform</translation>
<translation id="4744603770635761495">Ausführbarer Pfad</translation>
+<translation id="4750917950439032686">Ihre Daten wie Passwörter oder Kreditkartennummern sind privat, wenn Sie sie an diese Website senden.</translation>
<translation id="4756388243121344051">&amp;Verlauf</translation>
+<translation id="4759118997339041434">AutoFill für Zahlung deaktiviert</translation>
<translation id="4764776831041365478">Die Webseite unter <ph name="URL" /> ist möglicherweise vorübergehend nicht verfügbar oder wurde dauerhaft an eine neue Webadresse verschoben.</translation>
<translation id="4771973620359291008">Ein unbekannter Fehler ist aufgetreten.</translation>
<translation id="4800132727771399293">Prüfen Sie Ihr Ablaufdatum und Ihren CVC und versuchen Sie es dann erneut.</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Netzwerkfehler</translation>
<translation id="4816492930507672669">An Seite anpassen</translation>
<translation id="4850886885716139402">Anzeigen</translation>
@@ -367,6 +386,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{und 1 weitere Webseite}other{und # weitere Webseiten}}</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="495170559598752135">Aktionen</translation>
<translation id="4958444002117714549">Liste einblenden</translation>
@@ -394,7 +414,6 @@
<translation id="5181140330217080051">Download wird ausgeführt...</translation>
<translation id="5190835502935405962">Lesezeichenleiste</translation>
<translation id="5199729219167945352">Experimente</translation>
-<translation id="5199841536747119669">Hier werden Ihre Vorschläge angezeigt</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="5299298092464848405">Fehler beim Parsen der Richtlinie</translation>
@@ -402,8 +421,10 @@
<translation id="5308689395849655368">Die Absturzberichtsfunktion ist deaktiviert.</translation>
<translation id="5317780077021120954">Speichern</translation>
<translation id="5327248766486351172">Name</translation>
+<translation id="5337705430875057403">Angreifer auf <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> versuchen unter Umständen auf betrügerische Weise, Sie zur Installation von Software zu bewegen oder Ihnen personenbezogene Daten zu entlocken, zum Beispiel Passwörter, Telefonnummern oder Kreditkartendaten.</translation>
<translation id="5359637492792381994">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat ist zurzeit ungültig. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">Fehler beim Speichern der Richtlinieneinstellungen</translation>
+<translation id="5386426401304769735">Die Zertifikatskette für diese Website enthält ein Zertifikat mit SHA-1-Signatur.</translation>
<translation id="5421136146218899937">Browserdaten löschen...</translation>
<translation id="5430298929874300616">Lesezeichen löschen</translation>
<translation id="5431657950005405462">Ihre Datei wurde nicht gefunden</translation>
@@ -424,17 +445,20 @@
<translation id="5544037170328430102">Auf einer in <ph name="SITE" /> eingebetteten Seite wird Folgendes angezeigt:</translation>
<translation id="5556459405103347317">Neu laden</translation>
<translation id="5565735124758917034">Aktiv</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>
+<translation id="5580958916614886209">Prüfen Sie Ihren Ablaufmonat und versuchen Sie es dann erneut</translation>
<translation id="560412284261940334">Verwaltung wird nicht unterstützt.</translation>
<translation id="5610142619324316209">Verbindung prüfen</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> hat Sie zu oft weitergeleitet.</translation>
<translation id="5622887735448669177">Möchten Sie diese Website verlassen?</translation>
<translation id="5629630648637658800">Fehler beim Laden der Richtlinieneinstellungen</translation>
<translation id="5631439013527180824">Ungültiges Management-Token für das Gerät</translation>
+<translation id="5669703222995421982">Personalisierte Inhalte erhalten</translation>
+<translation id="5675650730144413517">Diese Seite funktioniert nicht</translation>
<translation id="5677928146339483299">Blockiert</translation>
<translation id="5694783966845939798">Sie haben versucht, auf <ph name="DOMAIN" /> zuzugreifen, der Server hat jedoch ein Zertifikat übermittelt, das einen schwachen Signaturalgorithmus verwendet, zum Beispiel SHA-1. Das bedeutet, dass die vom Server übermittelten Sicherheitsinformationen gefälscht sein könnten und es sich möglicherweise gar nicht um den erwarteten Server handelt, sondern Sie mit einem Hacker kommunizieren. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">Die Identität dieser Website wurde nicht verifiziert.</translation>
<translation id="5720705177508910913">Aktueller Nutzer</translation>
-<translation id="572328651809341494">Zuletzt geöffnete Tabs</translation>
<translation id="5732392974455271431">Deine Eltern können die Blockierung aufheben</translation>
<translation id="5784606427469807560">Beim Bestätigen Ihrer Karte ist ein Problem aufgetreten. Überprüfen Sie Ihre Internetverbindung und versuchen Sie es noch einmal.</translation>
<translation id="5785756445106461925">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 Layout der Seite verändern.</translation>
@@ -443,6 +467,7 @@
<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="5843436854350372569">Sie haben versucht, auf <ph name="DOMAIN" /> zuzugreifen, der Server hat jedoch ein Zertifikat mit einem schwachen Schlüssel übermittelt. Ein Hacker könnte den privaten Schlüssel geknackt haben, sodass es sich möglicherweise nicht um den erwarteten Server handelt, sondern Sie stattdessen mit einem Hacker kommunizieren. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">Neuer Ordner</translation>
<translation id="5869405914158311789">Diese Website ist nicht erreichbar</translation>
@@ -452,6 +477,7 @@
<translation id="59107663811261420">Dieser Kartentyp wird von Google Payments für diesen Händler nicht unterstützt. Bitte verwenden Sie eine andere Karte.</translation>
<translation id="59174027418879706">Aktiviert</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="5966707198760109579">Woche</translation>
<translation id="5967867314010545767">Aus Verlauf entfernen</translation>
<translation id="5975083100439434680">Verkleinern</translation>
@@ -470,8 +496,11 @@
<translation id="614940544461990577">Versuchen Sie Folgendes:</translation>
<translation id="6151417162996330722">Die Gültigkeitsdauer des Serverzertifikats ist zu lang.</translation>
<translation id="6165508094623778733">Weitere Informationen</translation>
+<translation id="6177128806592000436">Die Verbindung zu dieser Website ist nicht sicher</translation>
<translation id="6203231073485539293">Bitte überprüfen Sie Ihre Internetverbindung.</translation>
<translation id="6218753634732582820">Adresse aus Chromium entfernen?</translation>
+<translation id="6251924700383757765">Datenschutzerklärung</translation>
+<translation id="625755898061068298">Sie haben die Sicherheitswarnmeldungen für diese Website deaktiviert.</translation>
<translation id="6259156558325130047">&amp;Neu anordnen rückgängig machen</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" />-Lesezeichen</translation>
<translation id="6264485186158353794">Zurück zu sicherer Website</translation>
@@ -480,6 +509,7 @@
<translation id="6305205051461490394"><ph name="URL" /> ist nicht erreichbar.</translation>
<translation id="6321917430147971392">Überprüfen Sie die DNS-Einstellungen.</translation>
<translation id="6328639280570009161">Deaktivieren Sie die Netzwerkvervollständigung.</translation>
+<translation id="6328786501058569169">Sie befinden sich auf einer betrügerischen Website</translation>
<translation id="6337534724793800597">Richtlinien nach Name filtern</translation>
<translation id="6342069812937806050">Abgeschlossen</translation>
<translation id="6345221851280129312">unbekannte Größe</translation>
@@ -488,6 +518,8 @@
<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="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>
<translation id="6416403317709441254">Sie können <ph name="SITE" /> zurzeit nicht aufrufen, weil die Website verschlüsselte Anmeldedaten gesendet hat, die von Chromium nicht verarbeitet werden können. Netzwerkfehler und Angriffe sind in der Regel nur vorübergehend, sodass die Seite wahrscheinlich später wieder funktioniert. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">Überprüfung, ob das Zertifikat zurückgerufen wurde, nicht möglich</translation>
@@ -497,10 +529,12 @@
<translation id="6458467102616083041">Ignoriert, da Standardsuche durch Richtlinie deaktiviert</translation>
<translation id="6462969404041126431">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat wurde möglicherweise widerrufen. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<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="6529602333819889595">&amp;Löschen wiederholen</translation>
<translation id="6534179046333460208">Physical Web-Vorschläge</translation>
<translation id="6550675742724504774">Optionen</translation>
+<translation id="6556239504065605927">Sichere Verbindung</translation>
<translation id="6563469144985748109">Der Administrator hat die Berechtigung noch nicht erteilt</translation>
<translation id="6593753688552673085">weniger als <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Verschlüsselungsoptionen</translation>
@@ -509,7 +543,6 @@
<translation id="6644283850729428850">Diese Richtlinie ist veraltet.</translation>
<translation id="6652240803263749613">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat wird vom Betriebssystem Ihres Computers nicht als vertrauenswürdig eingestuft. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">Ordner bearbeiten</translation>
-<translation id="6660210980321319655">Automatische Meldung am <ph name="CRASH_TIME" /></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>
@@ -521,6 +554,7 @@
<translation id="6753269504797312559">Wert der Richtlinie</translation>
<translation id="6757797048963528358">Ihr Gerät ist im Ruhemodus.</translation>
<translation id="6778737459546443941">Dein Elternteil hat die Berechtigung noch nicht erteilt</translation>
+<translation id="6810899417690483278">Personalisierungs-ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Die Webseite unter <ph name="URL" /> ist zurzeit nicht verfügbar. Möglicherweise ist sie überlastet oder wird gerade gewartet.</translation>
<translation id="6831043979455480757">Ãœbersetzen</translation>
@@ -541,6 +575,8 @@
<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>
+<translation id="7064851114919012435">Kontaktdaten</translation>
+<translation id="7079718277001814089">Diese Website enthält Malware</translation>
<translation id="7087282848513945231">Landkreis</translation>
<translation id="7088615885725309056">Älter</translation>
<translation id="7090678807593890770">Auf Google nach <ph name="LINK" /> suchen</translation>
@@ -555,6 +591,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> erfüllt die Sicherheitsstandards nicht.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Weitere Informationen<ph name="END_LINK" /> zu diesem Problem.</translation>
<translation id="7219179957768738017">Die Verbindung verwendet <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Malware auf nachfolgender Website</translation>
<translation id="724975217298816891">Geben Sie das Gültigkeitsdatum und den CVC für <ph name="CREDIT_CARD" /> ein, um Ihre Kartendetails zu aktualisieren. Nach erfolgter Bestätigung werden die Kartendetails an diese Website weitergegeben.</translation>
<translation id="725866823122871198">Es kann keine private Verbindung zu <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> hergestellt werden, weil Datum und Uhrzeit Ihres Computers falsch sind (<ph name="DATE_AND_TIME" />).</translation>
<translation id="7269802741830436641">Diese Webseite weist eine Weiterleitung auf.</translation>
@@ -610,6 +647,7 @@ Geheimtipp: Verwenden Sie nächstes Mal den Inkognitomodus (<ph name="SHORTCUT_K
<translation id="7658239707568436148">Abbrechen</translation>
<translation id="7667346355482952095">Zurückgegebenes Token der Richtlinie ist leer oder entspricht nicht dem aktuellen Token</translation>
<translation id="7668654391829183341">Unbekanntes Gerät</translation>
+<translation id="7669271284792375604">Unbefugte Dritte auf dieser Website versuchen eventuell, Sie zur Installation von Programmen zu bewegen, die sich nachteilig auf Ihre Browsernutzung auswirken. Dabei kann zum Beispiel Ihre Startseite geändert werden oder es erscheinen zusätzliche Anzeigen auf von Ihnen besuchten Websites.</translation>
<translation id="7674629440242451245">Interessiert an coolen neuen Chrome-Funktionen? Testen Sie unsere Dev-Version unter chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Weiter zu <ph name="SITE" /> (unsicher)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Diese Website kann nicht aus dem Cache geladen werden</translation>
@@ -625,14 +663,17 @@ Geheimtipp: Verwenden Sie nächstes Mal den Inkognitomodus (<ph name="SHORTCUT_K
<translation id="7800304661137206267">Die Verbindung ist mit <ph name="CIPHER" /> verschlüsselt; für die Nachrichtenauthentifizierung wird <ph name="MAC" /> verwendet und als Mechanismus für den Schlüsselaustausch <ph name="KX" />.</translation>
<translation id="780301667611848630">Kein Interesse</translation>
<translation id="7805768142964895445">Status</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Vorschlag für das Formular aus Chrome entfernen?</translation>
<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="7887683347370398519">Prüfen Sie Ihren CVC und versuchen Sie es dann erneut.</translation>
<translation id="7912024687060120840">Im Ordner:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Serverzertifikat ist noch nicht gültig.</translation>
<translation id="7942349550061667556">Rot</translation>
+<translation id="7947285636476623132">Prüfen Sie Ihr Ablaufjahr und versuchen Sie es dann erneut</translation>
<translation id="7951415247503192394">(32-Bit)</translation>
<translation id="7956713633345437162">Mobile Lesezeichen</translation>
<translation id="7961015016161918242">Nie</translation>
@@ -640,7 +681,10 @@ Geheimtipp: Verwenden Sie nächstes Mal den Inkognitomodus (<ph name="SHORTCUT_K
<translation id="7983301409776629893"><ph name="ORIGINAL_LANGUAGE" /> immer auf <ph name="TARGET_LANGUAGE" /> übersetzen</translation>
<translation id="7995512525968007366">Nicht angegeben</translation>
<translation id="8012647001091218357">Wir können deine Eltern momentan nicht erreichen. Bitte versuche es später erneut.</translation>
+<translation id="8025119109950072390">Unbefugte Dritte auf dieser Website versuchen unter Umständen auf betrügerische Weise, Sie zur Installation von Software zu bewegen oder Ihnen personenbezogene Daten zu entlocken, zum Beispiel Passwörter, Telefonnummern oder Kreditkartendaten.</translation>
+<translation id="803030522067524905">Google Safe Browsing hat kürzlich Phishing auf <ph name="SITE" /> gefunden. Phishing-Websites täuschen die Nutzer, indem sie sich als andere Websites ausgeben. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">Diese Seite ist auf <ph name="SOURCE_LANGUAGE" />. In folgende Sprache übersetzen: <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Feedback geben</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>
@@ -651,9 +695,11 @@ Geheimtipp: Verwenden Sie nächstes Mal den Inkognitomodus (<ph name="SHORTCUT_K
<translation id="8150722005171944719">Die Datei unter <ph name="URL" /> kann nicht gelesen werden. Sie wurde möglicherweise entfernt oder verschoben oder die Dateiberechtigungen verhindern den Zugriff.</translation>
<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="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="8230421197304563332">Zurzeit auf <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> befindliche Angreifer 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="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="8249320324621329438">Letzter Abruf:</translation>
<translation id="8261506727792406068">Löschen</translation>
@@ -662,11 +708,13 @@ Geheimtipp: Verwenden Sie nächstes Mal den Inkognitomodus (<ph name="SHORTCUT_K
<translation id="8294431847097064396">Quelle</translation>
<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="8349305172487531364">Lesezeichenleiste</translation>
<translation id="8363502534493474904">Flugmodus ausschalten</translation>
<translation id="8364627913115013041">Nicht eingerichtet</translation>
<translation id="8380941800586852976">Schädlich</translation>
<translation id="8382348898565613901">Hier werden Ihre kürzlich aufgerufenen Lesezeichen angezeigt</translation>
+<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="8428213095426709021">Einstellungen</translation>
@@ -678,9 +726,9 @@ Geheimtipp: Verwenden Sie nächstes Mal den Inkognitomodus (<ph name="SHORTCUT_K
<translation id="8498891568109133222">Die Antwort von <ph name="HOST_NAME" /> hat zu lange gedauert.</translation>
<translation id="852346902619691059">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat wird vom Betriebssystem Ihres Geräts nicht als vertrauenswürdig eingestuft. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Dieser Kartentyp wird von Google Payments nicht unterstützt. Bitte verwenden Sie eine andere Karte.</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="8553075262323480129">Die Ãœbersetzung ist fehlgeschlagen, weil die Sprache der Seite nicht ermittelt werden konnte.</translation>
<translation id="8559762987265718583">Es kann keine private Verbindung zu <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> hergestellt werden, weil Datum und Uhrzeit Ihres Geräts falsch sind (<ph name="DATE_AND_TIME" />).</translation>
-<translation id="856992080682148">Das Zertifikat für diese Website läuft 2017 oder später ab und die Zertifikatskette enthält ein Zertifikat mit SHA-1-Signatur.</translation>
<translation id="8571890674111243710">Seite wird in folgende Sprache übersetzt: <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">In dem Zertifikat ist kein Mechanismus angegeben, mit dem geprüft werden kann, ob es zurückgerufen wurde.</translation>
<translation id="8620436878122366504">Deine Eltern haben die Berechtigung noch nicht erteilt</translation>
@@ -693,10 +741,12 @@ Geheimtipp: Verwenden Sie nächstes Mal den Inkognitomodus (<ph name="SHORTCUT_K
<translation id="8740359287975076522">Die &lt;abbr id="dnsDefinition"&gt;DNS-Adresse&lt;/abbr&gt; von <ph name="HOST_NAME" /> wurde nicht gefunden. Eine Problemdiagnose wird durchgeführt.</translation>
<translation id="8790007591277257123">&amp;Löschen wiederholen</translation>
<translation id="8798099450830957504">Standardeinstellung</translation>
+<translation id="8800988563907321413">Hier werden Ihre Vorschläge in der Nähe angezeigt</translation>
<translation id="8804164990146287819">Datenschutzerklärung</translation>
<translation id="8820817407110198400">Lesezeichen</translation>
<translation id="8834246243508017242">AutoFill aktivieren und dazu die Kontakte verwenden…</translation>
<translation id="883848425547221593">Andere Lesezeichen</translation>
+<translation id="884264119367021077">Versandadresse</translation>
<translation id="884923133447025588">Kein Sperrmechanismus gefunden.</translation>
<translation id="885730110891505394">Datenfreigabe an Google</translation>
<translation id="8866481888320382733">Fehler beim Parsen der Richtlinieneinstellungen</translation>
@@ -714,18 +764,21 @@ Geheimtipp: Verwenden Sie nächstes Mal den Inkognitomodus (<ph name="SHORTCUT_K
<translation id="8971063699422889582">Das Serverzertifikat ist abgelaufen.</translation>
<translation id="8987927404178983737">Monat</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="9001074447101275817">Für den Proxy <ph name="DOMAIN" /> sind ein Nutzername und ein Passwort erforderlich.</translation>
<translation id="901974403500617787">Parameter, die systemweit gelten, können nur vom Eigentümer festgelegt werden: <ph name="OWNER_EMAIL" />.</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">Vorhersagedienst 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="9040185888511745258">Unbefugte Dritte könnten auf <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> versuchen, Sie zur Installation von Programmen zu bewegen, die sich nachteilig auf Ihre Browsernutzung auswirken. Dabei kann zum Beispiel Ihre Startseite geändert werden oder es erscheinen zusätzliche Anzeigen auf von Ihnen besuchten Websites.</translation>
<translation id="9050666287014529139">Passphrase</translation>
<translation id="9065203028668620118">Bearbeiten</translation>
<translation id="9068849894565669697">Farbe auswählen</translation>
<translation id="9076283476770535406">Eventuell enthält sie nicht jugendfreie Inhalte</translation>
-<translation id="9092364396508701805">Die Seite <ph name="HOST_NAME" /> funktioniert nicht</translation>
<translation id="9103872766612412690"><ph name="SITE" /> schützt Ihre Daten in der Regel durch Verschlüsselung. Als Chromium dieses Mal versuchte, eine Verbindung zu <ph name="SITE" /> herzustellen, gab die Website ungewöhnliche und falsche Anmeldedaten zurück. Entweder versucht ein Angreifer, sich als <ph name="SITE" /> auszugeben, oder die Verbindung wurde durch eine WLAN-Anmeldeseite unterbrochen. Da Chromium die Verbindung vor dem Austausch von Daten unterbrochen hat, sind Ihre Informationen weiterhin sicher.</translation>
<translation id="9137013805542155359">Original anzeigen</translation>
+<translation id="9137248913990643158">Melden Sie sich in Chrome an, bevor Sie diese App nutzen.</translation>
<translation id="9148507642005240123">&amp;Bearbeiten rückgängig machen</translation>
<translation id="9157595877708044936">Einrichtung läuft...</translation>
<translation id="9170848237812810038">&amp;Rückgängig</translation>
@@ -738,7 +791,6 @@ Geheimtipp: Verwenden Sie nächstes Mal den Inkognitomodus (<ph name="SHORTCUT_K
<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="962701380617707048">Gültigkeitsdatum und CVC für <ph name="CREDIT_CARD" /> zur Aktualisierung Ihrer Kartendetails eingeben</translation>
<translation id="969892804517981540">Offizieller Build</translation>
<translation id="988159990683914416">Entwickler-Build</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 04bb33d6efe..3d5bbb29832 100644
--- a/chromium/components/strings/components_strings_el.xtb
+++ b/chromium/components/strings/components_strings_el.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Επανασυνδεθείτε στο Wi-Fi</translation>
<translation id="1175364870820465910">&amp;ΕκτÏπωση...</translation>
<translation id="1181037720776840403">ΚατάÏγηση</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Αυτόματη αναφοÏά<ph name="END_WHITEPAPER_LINK" /> στην Google λεπτομεÏειών σχετικά με πιθανά πεÏιστατικά ασφάλειας.<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Επόμενο</translation>
<translation id="1201895884277373915">ΠεÏισσότεÏα από αυτόν τον ιστότοπο</translation>
<translation id="1206967143813997005">Εσφαλμένη αÏχική υπογÏαφή</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Κυανό</translation>
<translation id="1629803312968146339">Θέλετε το Chrome να αποθηκεÏσει αυτήν την κάÏτα;</translation>
<translation id="1640180200866533862">Πολιτικές χÏηστών</translation>
+<translation id="1640244768702815859">Δοκιμάστε να <ph name="BEGIN_LINK" />επισκεφτείτε την αÏχική σελίδα του ιστότοπου<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Η διαμόÏφωση δικτÏου είναι μη έγκυÏη και δεν ήταν δυνατή η εισαγωγή της.</translation>
<translation id="1644574205037202324">ΙστοÏικό</translation>
<translation id="1645368109819982629">Μη υποστηÏιζόμενο Ï€Ïωτόκολλο</translation>
<translation id="1676269943528358898">Κανονικά, ο ιστότοπος <ph name="SITE" /> χÏησιμοποιεί κÏυπτογÏάφηση για να Ï€ÏοστατεÏει τα στοιχεία σας. Όταν το Google Chrome επιχείÏησε Ï€Ïόσφατα να συνδεθεί στο <ph name="SITE" />, ο ιστότοπος ανταποκÏίθηκε δημιουÏγώντας ασυνήθιστα και εσφαλμένα διαπιστευτήÏια. Αυτό μποÏεί να συμβεί όταν κάποιος εισβολέας Ï€Ïοσπαθεί να υποκÏιθεί ότι είναι ο ιστότοπος <ph name="SITE" /> ή όταν κάποια οθόνη σÏνδεσης Wi-Fi έχει διακόψει τη σÏνδεσή σας. Τα στοιχεία σας εξακολουθοÏν να είναι ασφαλή επειδή το Google Chrome διέκοψε τη σÏνδεση Ï€Ïιν από την ανταλλαγή δεδομένων.</translation>
+<translation id="168328519870909584">Οι εισβολείς που βÏίσκονται αυτήν τη στιγμή στον ιστότοπο <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ενδέχεται να επιχειÏήσουν να εγκαταστήσουν επικίνδυνες εφαÏμογές στη συσκευή σας, οι οποίες μποÏοÏν να υποκλέψουν ή να διαγÏάψουν τα δεδομένα σας (για παÏάδειγμα, φωτογÏαφίες, κωδικοÏÏ‚ Ï€Ïόσβασης, μηνÏματα και στοιχεία πιστωτικών καÏτών).</translation>
<translation id="168841957122794586">Το πιστοποιητικό διακομιστή πεÏιέχει ένα αδÏναμο κÏυπτογÏαφικό κλειδί.</translation>
-<translation id="1701955595840307032">Λήψη Ï€Ïοτεινόμενου πεÏιεχομένου</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Για να επισκεφτείτε αυτήν τη σελίδα, χÏειάζεστε άδεια από τον διαχειÏιστή <ph name="NAME" /></translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">ΑÏιθμός σελίδας</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Δοκιμάστε να εκτελέσετε τον Διαγνωστικό έλεγχο δικτÏου των Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">ΕνημεÏώστε την κωδική φÏάση Ï€Ïόσβασης συγχÏονισμοÏ.</translation>
+<translation id="1787142507584202372">Οι ανοιχτές καÏτέλες σας εμφανίζονται εδώ</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Η Ασφαλής πεÏιήγηση Google <ph name="BEGIN_LINK" />εντόπισε κακόβουλο Ï€ÏόγÏαμμα<ph name="END_LINK" /> Ï€Ïόσφατα στον ιστότοπο <ph name="SITE" />. Οι ιστότοποι που είναι συνήθως ασφαλείς Ï€Ïοσβάλλονται οÏισμένες φοÏές από κακόβουλο λογισμικό. Το κακόβουλο λογισμικό Ï€ÏοέÏχεται από τον κεντÏικό υπολογιστή <ph name="SUBRESOURCE_HOST" />, γνωστό διανομέα κακόβουλου λογισμικοÏ. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Μη έγκυÏο αίτημα ή παÏάμετÏοι αιτήματος</translation>
+<translation id="1834321415901700177">Αυτός ο ιστότοπος πεÏιέχει κακόβουλα Ï€ÏογÏάμματα</translation>
<translation id="1838667051080421715">Βλέπετε την πηγή μιας ιστοσελίδας.</translation>
<translation id="1871208020102129563">Ο διακομιστής μεσολάβησης έχει Ïυθμιστεί να χÏησιμοποιεί διακομιστές μεσολάβησης και όχι μια διεÏθυνση URL σεναÏίου .pac.</translation>
<translation id="1883255238294161206">ΣÏμπτυξη λίστας</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Σελιδοδείκτες κινητής συσκευής</translation>
<translation id="2148716181193084225">ΣήμεÏα</translation>
-<translation id="2149973817440762519">ΕπεξεÏγασία σελιδοδείκτη</translation>
<translation id="2154054054215849342">Ο συγχÏονισμός δεν είναι διαθέσιμος για τον τομέα σας</translation>
<translation id="2166049586286450108">ΠλήÏης Ï€Ïόσβαση διαχειÏιστή</translation>
<translation id="2166378884831602661">Αυτός ο ιστότοπος δεν μποÏεί να Ï€ÏοσφέÏει ασφαλή σÏνδεση</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Δεν μποÏείτε να επισκεφτείτε τον ιστότοπο <ph name="SITE" /> αυτήν τη στιγμή γιατί ο ιστότοπος χÏησιμοποιεί HSTS. Τα σφάλματα δικτÏου και οι επιθέσεις είναι συνήθως Ï€ÏοσωÏινά, συνεπώς αυτή η σελίδα πιθανότατα θα λειτουÏγήσει αÏγότεÏα. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Μη έγκυÏος σελιδοδείκτης στο ευÏετήÏιο <ph name="ENTRY_INDEX" /> που παÏαβλέφθηκε</translation>
<translation id="2354001756790975382">Άλλοι σελιδοδείκτες</translation>
+<translation id="2355395290879513365">Οι εισβολείς ενδέχεται να έχουν τη δυνατότητα να δουν τις εικόνες που Ï€Ïοβάλετε σε αυτόν τον ιστότοπο και να σας εξαπατήσουν Ï„Ïοποποιώντας τες.</translation>
<translation id="2359808026110333948">Συνέχεια</translation>
<translation id="2365563543831475020">Οι αναφοÏές σφαλμάτων που καταγÏάφηκαν <ph name="CRASH_TIME" /> δεν έχουν μεταφοÏτωθεί</translation>
<translation id="2367567093518048410">Επίπεδο</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Εμφάνιση πολιτικών χωÏίς τιμή που να έχει οÏιστεί.</translation>
<translation id="2396249848217231973">&amp;ΑναίÏεση διαγÏαφής</translation>
<translation id="2455981314101692989">Σε αυτήν την ιστοσελίδα έχει απενεÏγοποιηθεί η αυτόματη συμπλήÏωση για τη συγκεκÏιμένη φόÏμα.</translation>
+<translation id="2460160116472764928">Η ασφαλής πεÏιήγηση Google <ph name="BEGIN_LINK" />εντόπισε κακόβουλο Ï€ÏόγÏαμμα<ph name="END_LINK" /> Ï€Ïόσφατα στον ιστότοπο <ph name="SITE" />. Οι ιστότοποι που είναι ασφαλείς υπό φυσιολογικές συνθήκες μεÏικές φοÏές Ï€Ïοσβάλλονται από κακόβουλα λογισμικά. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">ΣυμπλήÏωση</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Îα εκτελέσετε τον Διαγνωστικό έλεγχο δικτÏου<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Μη έγκυÏη διεÏθυνση URL αναζήτησης.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Είστε βέβαιοι ότι θέλετε να διαγÏάψετε αυτές τις σελίδες από το ιστοÏικό σας;</translation>
<translation id="2677748264148917807">ΑποχώÏηση</translation>
<translation id="269990154133806163">Ο διακομιστής παÏουσίασε ένα πιστοποιητικό το οποίο δεν αποκαλÏφθηκε δημοσίως με χÏήση της πολιτικής Διαφάνειας πιστοποιητικών. Αυτό απαιτείται για οÏισμένα πιστοποιητικά, Ï€Ïοκειμένου να διασφαλιστεί ότι είναι αξιόπιστα και παÏέχουν Ï€Ïοστασία ενάντια σε εισβολείς. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Λίστα ανάγνωσης</translation>
<translation id="2704283930420550640">Η τιμή δεν συμφωνεί με τη μοÏφή.</translation>
<translation id="2704951214193499422">Δεν ήταν δυνατή η επιβεβαίωση της κάÏτας σας από το Chromium αυτήν τη στιγμή. Δοκιμάστε ξανά αÏγότεÏα.</translation>
<translation id="2705137772291741111">Δεν είναι δυνατή η ανάγνωση του αποθηκευμένου αντιγÏάφου (κÏυφής μνήμης) Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… ιστότοπου.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">ΠÏοσπαθήσατε να μεταβείτε στον τομέα <ph name="DOMAIN" />, όμως το πιστοποιητικό που παÏουσιάστηκε από τον διακομιστή ανακλήθηκε από τον εκδότη του. Αυτό σημαίνει ότι τα διαπιστευτήÏια ασφαλείας που παÏουσιάστηκαν από τον διακομιστή δεν Ï€Ïέπει σε καμία πεÏίπτωση να θεωÏηθοÏν αξιόπιστα. Ενδέχεται να επικοινωνείτε με κάποιον εισβολέα. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Îα ζητηθεί άδεια</translation>
<translation id="2713444072780614174">Λευκό</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">ΜποÏείτε να απενεÏγοποιήσετε τυχόν διακομιστές μεσολάβησης που έχουν διαμοÏφωθεί για μια σÏνδεση από τη σελίδα Ïυθμίσεων.</translation>
<translation id="2955913368246107853">Κλείσιμο γÏαμμής εÏÏεσης</translation>
<translation id="2958431318199492670">Η διαμόÏφωση δικτÏου δεν συμμοÏφώνεται με το Ï€Ïότυπο ONC. ΟÏισμένα τμήματα αυτής της διαμόÏφωσης ενδέχεται να μην εισαχθοÏν.</translation>
+<translation id="29611076221683977">Οι εισβολείς που βÏίσκονται αυτήν τη στιγμή στον ιστότοπο <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ενδέχεται να επιχειÏήσουν να εγκαταστήσουν επικίνδυνα Ï€ÏογÏάμματα στον υπολογιστή σας Mac, για να υποκλέψουν ή να διαγÏάψουν τα δεδομένα σας (για παÏάδειγμα, φωτογÏαφίες, κωδικοÏÏ‚ Ï€Ïόσβασης, μηνÏματα και πιστωτικές κάÏτες).</translation>
<translation id="2969319727213777354">Για την επίτευξη μιας ασφαλοÏÏ‚ σÏνδεσης, θα Ï€Ïέπει να γίνει σωστή ÏÏθμιση του ÏÎ¿Î»Î¿Î³Î¹Î¿Ï ÏƒÎ±Ï‚. Αυτό οφείλεται στο γεγονός ότι τα πιστοποιητικά που χÏησιμοποιοÏν οι ιστότοποι για την ταυτοποίησή τους είναι έγκυÏα μόνο για συγκεκÏιμένες χÏονικές πεÏιόδους. Εφόσον το Ïολόι της συσκευής σας δεν είναι σωστά Ïυθμισμένο, το Google Chrome δεν μποÏεί να επαληθεÏσει αυτά τα πιστοποιητικά.</translation>
<translation id="2972581237482394796">&amp;Επανάληψη ενέÏγειας</translation>
<translation id="2985306909656435243">Εάν ενεÏγοποιηθεί, το Chromium θα αποθηκεÏσει ένα αντίγÏαφο της κάÏτας σας σε αυτήν τη συσκευή για ταχÏτεÏη συμπλήÏωση φοÏμών.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">ΑπόκÏυψη λεπτομεÏειών</translation>
<translation id="3587482841069643663">Όλες</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Ο διακομιστής δεν υποστηÏίζει την επέκταση επαναδιαπÏαγμάτευσης TLS.</translation>
<translation id="36224234498066874">ΔιαγÏαφή Δεδομένων ΠεÏιήγησης...</translation>
<translation id="362276910939193118">Εμφάνιση πλήÏους ιστοÏικοÏ</translation>
<translation id="3623476034248543066">Εμφάνιση τιμής</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">Αν αυτό το μήνυμα εμφανίζεται συχνά, μποÏείτε να βÏείτε βοήθεια εδώ <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">ΑναθεώÏηση</translation>
<translation id="3678029195006412963">Δεν ήταν δυνατή η έγκÏιση του αιτήματος</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Οι άδειες έχουν εξαντληθεί</translation>
<translation id="3714780639079136834">ΕνεÏγοποιήστε τα δεδομένα κινητής τηλεφωνίας ή το Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Ελέγξτε το διακομιστή μεσολάβησης, το τείχος Ï€Ïοστασίας και τη διαμόÏφωση DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Εάν κατανοείτε τους κινδÏνους για την ασφάλειά σας, μποÏείτε να <ph name="BEGIN_LINK" />επισκεφτείτε αυτόν τον μη ασφαλή ιστότοπο<ph name="END_LINK" /> Ï€Ïιν από την κατάÏγηση των επικίνδυνων Ï€ÏογÏαμμάτων.</translation>
<translation id="3739623965217189342">ΣÏνδεσμος που αντιγÏ.</translation>
<translation id="375403751935624634">Η μετάφÏαση απέτυχε λόγω σφάλματος διακομιστή.</translation>
<translation id="3759461132968374835">Δεν έχετε Ï€Ïόσφατα αναφεÏθέντα σφάλματα. Τα σφάλματα που Ï€Ïοέκυψαν όταν η αναφοÏά σφαλμάτων ήταν απενεÏγοποιημένη δεν θα εμφανιστοÏν εδώ.</translation>
-<translation id="3788090790273268753">Το πιστοποιητικό για αυτόν τον ιστότοπο λήγει το 2016 και η αλυσίδα πιστοποιητικών πεÏιέχει ένα πιστοποιητικό το οποίο έχει υπογÏαφεί με SHA-1.</translation>
<translation id="382518646247711829">Εάν χÏησιμοποιείτε διακομιστή μεσολάβησης…</translation>
<translation id="3828924085048779000">Δεν επιτÏέπεται να είναι κενή η φÏάση Ï€Ïόσβασης.</translation>
<translation id="3845539888601087042">Εμφάνιση ιστοÏÎ¹ÎºÎ¿Ï Î±Ï€ÏŒ τις συνδεδεμένες συσκευές σας. <ph name="BEGIN_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Κλειδί "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Η εφαÏμογή πελάτης και ο διακομιστής δεν υποστηÏίζουν κάποια κοινή έκδοση Ï€Ïωτοκόλλου SSL ή σουίτα κÏυπτογÏάφησης.</translation>
<translation id="4079302484614802869">Η διαμόÏφωση του διακομιστή μεσολάβησης είναι οÏισμένη να χÏησιμοποιεί μια διεÏθυνση URL σεναÏίου .pac και όχι σταθεÏοÏÏ‚ διακομιστές μεσολάβησης.</translation>
+<translation id="4098354747657067197">Ακολουθεί παÏαπλανητικός ιστότοπος</translation>
<translation id="4103249731201008433">Ο σειÏιακός αÏιθμός της συσκευής δεν είναι έγκυÏος</translation>
<translation id="4103763322291513355">Επισκεφτείτε την &lt;strong&gt;chrome://policy&lt;/strong&gt; για να δείτε τη λίστα των ανεπιθÏμητων διευθÏνσεων URL και άλλες πολιτικές που έχουν τεθεί σε εφαÏμογή από το διαχειÏιστή του συστήματός σας.</translation>
<translation id="4110615724604346410">Αυτός ο διακομιστής δεν μπόÏεσε να αποδείξει ότι είναι το <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του πεÏιέχει σφάλματα. Αυτό μποÏεί να οφείλεται σε εσφαλμένη ÏÏθμιση ή σε κάποιον εισβολέα που παÏεμβαίνει στη σÏνδεσή σας. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{καμία}=1{1 εφαÏμογή ($1)}=2{2 εφαÏμογές ($1, $2)}other{# εφαÏμογές ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Απότομες διακοπές λειτουÏγίας</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Δοκιμάστε να εκτελέσετε τον Διαγνωστικό έλεγχο δικτÏου<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Η σÏνδεσή σας σε αυτόν τον ιστότοπο δεν είναι πλήÏως ασφαλής</translation>
<translation id="4250680216510889253">Όχι</translation>
<translation id="425582637250725228">Οι αλλαγές που κάνατε ενδέχεται να μην έχουν αποθηκευτεί.</translation>
<translation id="4258748452823770588">Εσφαλμένη υπογÏαφή</translation>
<translation id="4269787794583293679">(ΧωÏίς όνομα χÏήστη)</translation>
+<translation id="4280429058323657511">, λήξη <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Η ασφαλής πεÏιήγηση Google <ph name="BEGIN_LINK" />εντόπισε επιβλαβή Ï€ÏογÏάμματα<ph name="END_LINK" /> στον ιστότοπο <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Γονικές Ï€Ïοτάσεις</translation>
<translation id="4304224509867189079">ΣÏνδεση</translation>
<translation id="432290197980158659">Αυτός ο διακομιστής παÏουσίασε ένα πιστοποιητικό που δεν αντιστοιχεί στις ενσωματωμένες Ï€Ïοϋποθέσεις. Αυτές οι Ï€Ïοϋποθέσεις συμπεÏιλαμβάνονται σε συγκεκÏιμένους ιστότοπους υψηλής ασφάλειας για την Ï€Ïοστασία σας. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Αποτυχία εÏÏεσης άÏθÏου</translation>
+<translation id="4326324639298822553">Ελέγξτε την ημεÏομηνία λήξης σας και δοκιμάστε ξανά</translation>
<translation id="4331708818696583467">Μη ασφαλές</translation>
+<translation id="4356973930735388585">Οι εισβολείς σε αυτόν τον ιστότοπο μποÏεί να επιχειÏήσουν να εγκαταστήσουν επικίνδυνα Ï€ÏογÏάμματα στον υπολογιστή σας, τα οποία μποÏοÏν να υποκλέψουν ή να διαγÏάψουν τα δεδομένα σας (για παÏάδειγμα, φωτογÏαφίες, κωδικοÏÏ‚ Ï€Ïόσβασης, μηνÏματα και στοιχεία πιστωτικών καÏτών).</translation>
<translation id="4372948949327679948">Αναμενόμενη τιμή <ph name="VALUE_TYPE" />.</translation>
<translation id="4381091992796011497">Όνομα χÏήστη:</translation>
<translation id="4394049700291259645">ΑπενεÏγοποίηση</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">ΠÏοσπαθήσατε να μεταβείτε στον τομέα <ph name="DOMAIN" />, αλλά ο διακομιστής παÏουσίασε ένα μη έγκυÏο πιστοποιητικό. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">Η σÏνδεσή σας στο <ph name="DOMAIN" /> κÏυπτογÏαφείται χÏησιμοποιώντας ένα σÏγχÏονο Ï€ÏόγÏαμμα κÏυπτογÏάφησης.</translation>
<translation id="4594403342090139922">&amp;ΑναίÏεση διαγÏαφής</translation>
+<translation id="4619615317237390068">ΚαÏτέλες από άλλες συσκευές</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Βλέπετε μια σελίδα επέκτασης.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Επανάληψη φόÏτωσης πολιτικών</translation>
<translation id="4728558894243024398">ΠλατφόÏμα</translation>
<translation id="4744603770635761495">ΔιαδÏομή εκτελέσιμου</translation>
+<translation id="4750917950439032686">Οι πληÏοφοÏίες σας (για παÏάδειγμα, οι κωδικοί Ï€Ïόσβασης ή οι αÏιθμοί πιστωτικών καÏτών) είναι ιδιωτικές κατά την αποστολή σε αυτόν τον ιστότοπο.</translation>
<translation id="4756388243121344051">&amp;ΙστοÏικό</translation>
+<translation id="4759118997339041434">Η αυτόματη συμπλήÏωση στοιχείων πληÏωμής είναι απενεÏγοποιημένη</translation>
<translation id="4764776831041365478">Η ιστοσελίδα στη διεÏθυνση <ph name="URL" /> μποÏεί να βÏίσκεται Ï€ÏοσωÏινά εκτός λειτουÏγίας ή ίσως έχει μεταφεÏθεί μόνιμα σε νέα διεÏθυνση ιστοÏ.</translation>
<translation id="4771973620359291008">ΠαÏουσιάστηκε άγνωστο σφάλμα.</translation>
<translation id="4800132727771399293">Ελέγξτε την ημεÏομηνία λήξης και τον κωδικό σας CVC και δοκιμάστε ξανά</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Σφάλμα δικτÏου</translation>
<translation id="4816492930507672669">ΠÏοσαÏμογή στη σελίδα</translation>
<translation id="4850886885716139402">ΠÏοβολή</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{και 1 ακόμη ιστοσελίδα}other{και # ακόμη ιστοσελίδες}}</translation>
<translation id="4923417429809017348">Αυτή η σελίδα έχει μεταφÏαστεί από μια άγνωστη γλώσσας στα <ph name="LANGUAGE_LANGUAGE" /></translation>
+<translation id="4923459931733593730">ΠληÏωμή</translation>
<translation id="4926049483395192435">ΠÏέπει να καθοÏιστεί.</translation>
<translation id="495170559598752135">ΕνέÏγειες</translation>
<translation id="4958444002117714549">Ανάπτυξη λίστας</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Λήψη</translation>
<translation id="5190835502935405962">ΓÏαμμή σελιδοδεικτών</translation>
<translation id="5199729219167945352">ΠειÏάματα</translation>
-<translation id="5199841536747119669">Οι Ï€Ïοτάσεις σας εμφανίζονται εδώ</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">ΧÏησιμοποιείτε το Chrome στη δουλειά σας; Οι επιχειÏήσεις μποÏοÏν να διαχειÏίζονται τις Ïυθμίσεις του Chrome για τους εÏγαζόμενοÏÏ‚ τους. Μάθετε πεÏισσότεÏα</translation>
<translation id="5299298092464848405">Σφάλμα ανάλυσης πολιτικής</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Η αναφοÏά σφαλμάτων είναι απενεÏγοποιημένη.</translation>
<translation id="5317780077021120954">Αποθήκευση</translation>
<translation id="5327248766486351172">Όνομα</translation>
+<translation id="5337705430875057403">Οι εισβολείς στον ιστότοπο <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> μποÏεί να σας ξεγελάσουν και να κάνετε κάτι επικίνδυνο, όπως να εγκαταστήσετε κάποιο λογισμικό ή να αποκαλÏψετε Ï€Ïοσωπικά σας στοιχεία (για παÏάδειγμα, κωδικοÏÏ‚ Ï€Ïόσβασης, αÏιθμοÏÏ‚ τηλεφώνου ή πιστωτικές κάÏτες).</translation>
<translation id="5359637492792381994">Αυτός ο διακομιστής δεν μπόÏεσε να αποδείξει ότι είναι το <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του δεν είναι έγκυÏο αυτήν τη στιγμή. Αυτό μποÏεί να οφείλεται σε εσφαλμένη ÏÏθμιση ή σε κάποιον εισβολέα που παÏεμβαίνει στη σÏνδεσή σας. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Αποτυχία αποθήκευσης Ïυθμίσεων πολιτικής</translation>
+<translation id="5386426401304769735">Η αλυσίδα Ï€Î¹ÏƒÏ„Î¿Ï€Î¿Î¹Î·Ï„Î¹ÎºÎ¿Ï Î³Î¹Î± αυτόν τον ιστότοπο πεÏιέχει ένα πιστοποιητικό το οποίο είναι υπογεγÏαμμένο με χÏήση SHA-1.</translation>
<translation id="5421136146218899937">ΔιαγÏαφή δεδομένων πεÏιήγησης...</translation>
<translation id="5430298929874300616">ΚατάÏγηση σελιδοδείκτη</translation>
<translation id="5431657950005405462">Το αÏχείο σας δεν βÏέθηκε</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Μια ενσωματωμένη σελίδα στον ιστότοπο <ph name="SITE" /> λέει:</translation>
<translation id="5556459405103347317">ΕπαναφόÏτωση</translation>
<translation id="5565735124758917034">ΕνεÏγό</translation>
+<translation id="5572851009514199876">Εκκινήστε και συνδεθείτε στο Chrome, έτσι ώστε το Chrome να μποÏεί να ελέγξει εάν έχετε δικαίωμα Ï€Ïόσβασης σε αυτόν τον ιστότοπο.</translation>
+<translation id="5580958916614886209">Ελέγξτε τον μήνα λήξης σας και δοκιμάστε ξανά</translation>
<translation id="560412284261940334">Η διαχείÏιση δεν υποστηÏίζεται</translation>
<translation id="5610142619324316209">Ελέγξτε τη σÏνδεση</translation>
<translation id="5617949217645503996">Ο κεντÏικός υπολογιστής <ph name="HOST_NAME" /> έκανε πάÏα πολλές ανακατευθÏνσεις.</translation>
<translation id="5622887735448669177">Θέλετε να φÏγετε από αυτόν τον ιστότοπο;</translation>
<translation id="5629630648637658800">Αποτυχία φόÏτωσης Ïυθμίσεων πολιτικής</translation>
<translation id="5631439013527180824">Μη έγκυÏο διακÏιτικό διαχείÏισης συσκευής</translation>
+<translation id="5669703222995421982">Λήψη εξατομικευμένου πεÏιεχομένου</translation>
+<translation id="5675650730144413517">Αυτή η σελίδα δεν λειτουÏγεί</translation>
<translation id="5677928146339483299">Αποκλεισμένος</translation>
<translation id="5694783966845939798">ΠÏοσπαθήσατε να μεταβείτε στον τομέα <ph name="DOMAIN" />, αλλά ο διακομιστής παÏουσίασε ένα πιστοποιητικό το οποίο ήταν υπογεγÏαμμένο με έναν αδÏναμο αλγόÏιθμο υπογÏαφής (όπως SHA-1). Αυτό σημαίνει ότι μποÏεί να έχουν πλαστογÏαφηθεί τα διαπιστευτήÏια ασφαλείας που επέδειξε ο διακομιστής και ότι αυτός ο διακομιστής μποÏεί να μην είναι αυτό που πεÏιμένατε (μποÏεί να επικοινωνείτε με έναν εισβολέα). <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Η ταυτότητα Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… ιστότοπου δεν έχει επαληθευτεί.</translation>
<translation id="5720705177508910913">ΤÏέχων χÏήστης</translation>
-<translation id="572328651809341494">ΠÏόσφατες καÏτέλες</translation>
<translation id="5732392974455271431">Οι γονείς σου μποÏοÏν να καταÏγήσουν τον αποκλεισμό του</translation>
<translation id="5784606427469807560">ΠαÏουσιάστηκε κάποιο Ï€Ïόβλημα κατά την επιβεβαίωση της κάÏτας σας. Ελέγξτε τη σÏνδεσή σας στο διαδίκτυο και δοκιμάστε ξανά.</translation>
<translation id="5785756445106461925">Επίσης, αυτή η σελίδα πεÏιέχει άλλους πόÏους, οι οποίοι δεν είναι ασφαλείς. Αυτοί οι πόÏοι μποÏοÏν να Ï€ÏοβληθοÏν από άλλους χÏήστες κατά τη μετάβαση και μποÏοÏν να Ï„ÏοποποιηθοÏν από έναν εισβολέα ώστε να αλλάξει η εμφάνιση της σελίδας.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">ΠÏοσπαθήσατε να μεταβείτε στον τομέα <ph name="DOMAIN" />, αλλά ο διακομιστής παÏουσίασε ένα πιστοποιητικό που πεÏιέχει ένα αδÏναμο κλειδί. Κάποιος εισβολέας θα μποÏοÏσε να έχει παÏαβιάσει το ιδιωτικό κλειδί και ο διακομιστής ενδέχεται να μην είναι ο διακομιστής που αναμένατε (μποÏεί να επικοινωνείτε με έναν εισβολέα). <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Îέος φάκελος</translation>
<translation id="5869405914158311789">Δεν είναι δυνατή η Ï€Ïόσβαση σε αυτόν τον ιστότοπο</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Αυτός ο Ï„Ïπος κάÏτας δεν υποστηÏίζεται από το Google Payments γι' αυτόν τον έμποÏο. Επιλέξτε διαφοÏετική κάÏτα.</translation>
<translation id="59174027418879706">ΕνεÏγή</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="5966707198760109579">Εβδομάδα</translation>
<translation id="5967867314010545767">ΚατάÏγηση από το ιστοÏικό</translation>
<translation id="5975083100439434680">ΣμίκÏυνση</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Δοκιμάστε να κάνετε τα εξής:</translation>
<translation id="6151417162996330722">Το πιστοποιητικό του διακομιστή έχει Ï€Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î· πεÏίοδο εγκυÏότητας.</translation>
<translation id="6165508094623778733">Μάθετε πεÏισσότεÏα</translation>
+<translation id="6177128806592000436">Η σÏνδεσή σας σε αυτόν τον ιστότοπο δεν είναι ασφαλής</translation>
<translation id="6203231073485539293">Ελέγξτε τη σÏνδεσή σας στο Internet</translation>
<translation id="6218753634732582820">Îα καταÏγηθεί η διεÏθυνση από το Chromium;</translation>
+<translation id="6251924700383757765">Πολιτική αποÏÏήτου</translation>
+<translation id="625755898061068298">Έχετε επιλέξει να απενεÏγοποιήσετε τις Ï€Ïοειδοποιήσεις ασφάλειας για αυτόν τον ιστότοπο.</translation>
<translation id="6259156558325130047">&amp;Επανάληψη αναδιάταξης</translation>
<translation id="6263376278284652872">Σελιδοδείκτες <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Πίσω στην ασφάλεια</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">Δεν είναι δυνατή η Ï€Ïόσβαση στο <ph name="URL" />.</translation>
<translation id="6321917430147971392">Ελέγξτε τις Ïυθμίσεις DNS</translation>
<translation id="6328639280570009161">Δοκιμάστε να απενεÏγοποιήσετε την Ï€Ïόβλεψη δικτÏου</translation>
+<translation id="6328786501058569169">Αυτός ο ιστότοπος είναι παÏαπλανητικός</translation>
<translation id="6337534724793800597">ΦιλτÏάÏισμα πολιτικών με βάση το όνομα</translation>
<translation id="6342069812937806050">ΠÏιν λίγο</translation>
<translation id="6345221851280129312">άγνωστο μέγεθος</translation>
@@ -489,6 +519,8 @@
<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="6404511346730675251">ΕπεξεÏγασία σελιδοδείκτη</translation>
+<translation id="6410264514553301377">Εισαγάγετε την ημεÏομηνία λήξης και τον κωδικό CVC για την κάÏτα <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Ρώτησες τους γονείς σου εάν σου επιτÏέπουν να επισκεφτείς αυτόν τον ιστότοπο</translation>
<translation id="6416403317709441254">Δεν μποÏείτε να επισκεφτείτε τον ιστότοπο <ph name="SITE" /> αυτήν τη στιγμή γιατί ο ιστότοπος έστειλε κωδικοποιημένα διαπιστευτήÏια τα οποία δεν είναι δυνατό να επεξεÏγαστεί το Chromium. Τα σφάλματα δικτÏου και οι επιθέσεις είναι συνήθως Ï€ÏοσωÏινά φαινόμενα, συνεπώς η σελίδα πιθανότατα θα λειτουÏγήσει αÏγότεÏα. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Δεν είναι δυνατόν να ελεγχθεί αν το πιστοποιητικό έχει ακυÏωθεί.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Αγνοήθηκε επειδή η Ï€Ïοεπιλεγμένη αναζήτηση είναι απενεÏγοποιημένη από την πολιτική.</translation>
<translation id="6462969404041126431">Αυτός ο διακομιστής δεν μπόÏεσε να αποδείξει ότι είναι το <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του μποÏεί να ανακληθεί. Αυτό μποÏεί να οφείλεται σε εσφαλμένη ÏÏθμιση ή σε κάποιον εισβολέα που παÏεμβαίνει στη σÏνδεσή σας. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">Πολιτικές συσκευών </translation>
+<translation id="6477321094435799029">Το Chrome εντόπισε ασυνήθιστο κώδικα σε αυτήν τη σελίδα και τον απέκλεισε για να Ï€ÏοστατεÏσει τα Ï€Ïοσωπικά σας στοιχεία (για παÏάδειγμα, κωδικοÏÏ‚ Ï€Ïόσβασης, αÏιθμοÏÏ‚ τηλεφώνου ή πιστωτικές κάÏτες).</translation>
<translation id="6489534406876378309">ΈναÏξη μεταφόÏτωσης σφαλμάτων</translation>
<translation id="6529602333819889595">&amp;Επανάληψη διαγÏαφής</translation>
<translation id="6534179046333460208">ΠÏοτάσεις Î¦Ï…ÏƒÎ¹ÎºÎ¿Ï Î´Î¹ÎºÏ„Ïου</translation>
<translation id="6550675742724504774">Επιλογές</translation>
+<translation id="6556239504065605927">Ασφαλής σÏνδεση</translation>
<translation id="6563469144985748109">Ο διαχειÏιστής σας δεν τον έχει εγκÏίνει ακόμα</translation>
<translation id="6593753688552673085">λιγότεÏο από <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Επιλογές κÏυπτογÏάφησης</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Αυτή η πολιτική έχει αποσυÏθεί.</translation>
<translation id="6652240803263749613">Αυτός ο διακομιστής δεν μπόÏεσε να αποδείξει ότι είναι το <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του δεν θεωÏείται έμπιστο από το λειτουÏγικό σÏστημα του υπολογιστή σας. Αυτό μποÏεί να οφείλεται σε εσφαλμένη ÏÏθμιση ή σε κάποιον εισβολέα που παÏεμβαίνει στη σÏνδεσή σας. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">ΕπεξεÏγασία φακέλου</translation>
-<translation id="6660210980321319655">Αυτόματη αναφοÏά <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Îα καταÏγηθεί η Ï€Ïόταση φόÏμας από το Chromium;</translation>
<translation id="6685834062052613830">Αποσυνδεθείτε και ολοκληÏώστε την εγκατάσταση</translation>
<translation id="6710213216561001401">ΠÏοηγοÏμενο</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Τιμή πολιτικής</translation>
<translation id="6757797048963528358">Η συσκευή σας τέθηκε σε αδÏάνεια.</translation>
<translation id="6778737459546443941">Ο γονέας σου δεν τον έχει εγκÏίνει ακόμα</translation>
+<translation id="6810899417690483278">ΑναγνωÏιστικό Ï€ÏοσαÏμογής</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Η ιστοσελίδα στο <ph name="URL" /> δεν είναι διαθέσιμη Ï€Ïος το παÏόν. ΜποÏεί να είναι υπεÏφοÏτωμένη ή εκτός λειτουÏγίας για συντήÏηση.</translation>
<translation id="6831043979455480757">ΜετάφÏαση</translation>
@@ -532,7 +566,7 @@
<translation id="6897140037006041989">ΠαÏάγοντας χÏήστη</translation>
<translation id="6915804003454593391">ΧÏήστης</translation>
<translation id="6957887021205513506">Το πιστοποιητικό του διακομιστή φαίνεται να είναι πλαστό.</translation>
-<translation id="6965382102122355670">OK</translation>
+<translation id="6965382102122355670">ΕÎΤΑΞΕΙ</translation>
<translation id="6965978654500191972">Συσκευή</translation>
<translation id="6970216967273061347">ΠεÏιοχή</translation>
<translation id="6973656660372572881">ΚαθοÏίζονται τόσο οι σταθεÏοί διακομιστές μεσολάβησης όσο και μια διεÏθυνση URL σεναÏίου .pac.</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Στοιχεία επικοινωνίας</translation>
+<translation id="7079718277001814089">Ο ιστότοπος πεÏιέχει κακόβουλο Ï€ÏόγÏαμμα</translation>
<translation id="7087282848513945231">ΠεÏιφέÏεια</translation>
<translation id="7088615885725309056">ΠαλαιότεÏο</translation>
<translation id="7090678807593890770">Αναζητήστε στο Google για <ph name="LINK" /></translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423">Ο κεντÏικός υπολογιστής <ph name="HOST_NAME" /> δεν συμμοÏφώνεται με τα Ï€Ïότυπα ασφάλειας.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LINK" /> σχετικά με αυτό το Ï€Ïόβλημα.</translation>
<translation id="7219179957768738017">Στη σÏνδεση χÏησιμοποιείται <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Ο ιστότοπος μετάβασης πεÏιέχει κακόβουλο λογισμικό</translation>
<translation id="724975217298816891">Εισαγάγετε την ημεÏομηνία λήξης και τον κωδικό CVC για την πιστωτική κάÏτα <ph name="CREDIT_CARD" />, Ï€Ïοκειμένου να ενημεÏώσετε τα στοιχεία της κάÏτας σας. Μετά την επιβεβαίωση, θα γίνει κοινή χÏήση των στοιχείων της κάÏτας σας με αυτόν τον ιστότοπο.</translation>
<translation id="725866823122871198">Δεν είναι δυνατή η επίτευξη ιδιωτικής σÏνδεσης με τον τομέα <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> επειδή η ημεÏομηνία και η ÏŽÏα (<ph name="DATE_AND_TIME" />) του υπολογιστή σας είναι λανθασμένες.</translation>
<translation id="7269802741830436641">Αυτή η ιστοσελίδα πεÏιλαμβάνει βÏόχο ανακατεÏθυνσης</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">ΑκÏÏωση</translation>
<translation id="7667346355482952095">Το εμφανιζόμενο διακÏιτικό πολιτικής είναι κενό ή δεν αντιστοιχεί στο Ï„Ïέχον διακÏιτικό</translation>
<translation id="7668654391829183341">Άγνωστη συσκευή</translation>
+<translation id="7669271284792375604">Οι εισβολείς σε αυτόν τον ιστότοπο μποÏεί να επιχειÏήσουν να σας ξεγελάσουν, έτσι ώστε να εγκαταστήσετε Ï€ÏογÏάμματα που βλάπτουν την εμπειÏία πεÏιήγησής σας (για παÏάδειγμα, αλλάζοντας την αÏχική σελίδα σας ή εμφανίζοντας επιπλέον διαφημίσεις στους ιστότοπους που επισκέπτεστε).</translation>
<translation id="7674629440242451245">Θέλετε να ενημεÏώνεστε για τις συναÏπαστικές νέες δυνατότητες του Chrome; Επισκεφτείτε το κανάλι Ï€ÏογÏαμματιστών στη διεÏθυνση chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Μετάβαση στον ιστότοπο <ph name="SITE" /> (μη ασφαλής)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Δεν είναι δυνατή η φόÏτωση Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… ιστότοπου από την κÏυφή μνήμη</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">Η σÏνδεση είναι κÏυπτογÏαφημένη με χÏήση <ph name="CIPHER" />, με <ph name="MAC" /> για έλεγχο ταυτότητας μηνυμάτων και <ph name="KX" /> ως μηχανισμό ανταλλαγής κλειδιών.</translation>
<translation id="780301667611848630">Όχι, ευχαÏιστώ</translation>
<translation id="7805768142964895445">Κατάσταση</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">ΚατάÏγηση Ï€Ïότασης φόÏμας από το Chrome;</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="7887683347370398519">Ελέγξτε τον κωδικό σας CVC και δοκιμάστε ξανά</translation>
<translation id="7912024687060120840">Στον φάκελο:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Το πιστοποιητικό του διακομιστή δεν είναι ακόμα έγκυÏο.</translation>
<translation id="7942349550061667556">Κόκκινο</translation>
+<translation id="7947285636476623132">Ελέγξτε το έτος λήξης σας και δοκιμάστε ξανά</translation>
<translation id="7951415247503192394">(32-bit)</translation>
<translation id="7956713633345437162">Σελιδοδείκτες κινητής συσκευής</translation>
<translation id="7961015016161918242">Ποτέ</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">Îα γίνεται πάντα μετάφÏαση των <ph name="ORIGINAL_LANGUAGE" /> στα <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Δεν καθοÏίστηκε</translation>
<translation id="8012647001091218357">Δεν ήταν δυνατή η επικοινωνία με τους γονείς σας αυτήν τη στιγμή. Δοκιμάστε ξανά.</translation>
+<translation id="8025119109950072390">Οι εισβολείς σε αυτόν τον ιστότοπο μποÏεί να σας ξεγελάσουν και να κάνετε κάτι επικίνδυνο, όπως να εγκαταστήσετε κάποιο λογισμικό ή να αποκαλÏψετε Ï€Ïοσωπικά σας στοιχεία (για παÏάδειγμα, κωδικοÏÏ‚ Ï€Ïόσβασης, αÏιθμοÏÏ‚ τηλεφώνου ή πιστωτικές κάÏτες).</translation>
+<translation id="803030522067524905">Η Ασφαλής πεÏιήγηση Google εντόπισε Ï€Ïόσφατα εκδηλώσεις ηλεκτÏÎ¿Î½Î¹ÎºÎ¿Ï ÏˆÎ±Ïέματος (phishing) <ph name="SITE" />. Οι ιστότοποι ηλεκτÏÎ¿Î½Î¹ÎºÎ¿Ï ÏˆÎ±Ïέματος (phishing) παÏουσιάζονται ψευδώς σαν άλλου Ï„Ïπου ιστότοποι για να σας ξεγελάσουν. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Αυτή η σελίδα είναι στα <ph name="SOURCE_LANGUAGE" />. ΜετάφÏασή της στα <ph name="TARGET_LANGUAGE" />;</translation>
+<translation id="8041089156583427627">Αποστολή σχολίων</translation>
<translation id="8088680233425245692">Αποτυχία Ï€Ïοβολής άÏθÏου.</translation>
<translation id="8089520772729574115">λιγότεÏο από 1 MB</translation>
<translation id="8091372947890762290">Η ενεÏγοποίηση στο διακομιστή εκκÏεμεί</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719">Δεν είναι δυνατή η ανάγνωση του αÏχείου στη διεÏθυνση <ph name="URL" />. Ενδέχεται να καταÏγήθηκε, να μετακινήθηκε ή τα δικαιώματα αÏχείου μποÏεί να μην επιτÏέπουν την Ï€Ïόσβαση.</translation>
<translation id="8194797478851900357">&amp;ΑναίÏεση μετακίνησης</translation>
<translation id="8201077131113104583">Η διεÏθυνση URL ενημέÏωσης για την επέκταση με αναγνωÏιστικό ID "<ph name="EXTENSION_ID" />", δεν είναι έγκυÏη.</translation>
+<translation id="8202097416529803614">ΣÏνοψη παÏαγγελίας</translation>
<translation id="8218327578424803826">ΕκχωÏημένη τοποθεσία:</translation>
<translation id="8225771182978767009">Το άτομο που ÏÏθμισε αυτόν τον υπολογιστή επέλεξε να αποκλείσει αυτόν τον ιστότοπο.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">Οι εισβολείς που βÏίσκονται αυτήν τη στιγμή στον ιστότοπο <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ενδέχεται να επιχειÏήσουν να εγκαταστήσουν επικίνδυνα Ï€ÏογÏάμματα στον υπολογιστή σας, τα οποία μποÏοÏν να υποκλέψουν ή να διαγÏάψουν τα δεδομένα σας (για παÏάδειγμα, φωτογÏαφίες, κωδικοÏÏ‚ Ï€Ïόσβασης, μηνÏματα και στοιχεία πιστωτικών καÏτών).</translation>
<translation id="8241707690549784388">Στη σελίδα που αναζητάτε χÏησιμοποποιήθηκαν πληÏοφοÏίες που καταχωÏίσατε. Αν επιστÏέψετε σε αυτή τη σελίδα ίσως επαναληφθοÏν ενέÏγειες που εκτελέσατε. Θέλετε να συνεχίσετε;</translation>
<translation id="8249320324621329438">Τελευταία ανάκτηση:</translation>
<translation id="8261506727792406068">ΔιαγÏαφή</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">Πηγή</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="8349305172487531364">ΓÏαμμή σελιδοδεικτών</translation>
<translation id="8363502534493474904">ΑπενεÏγοποιήστε τη λειτουÏγία πτήσης</translation>
<translation id="8364627913115013041">Δεν έχει οÏιστεί.</translation>
<translation id="8380941800586852976">Επικίνδυνο</translation>
<translation id="8382348898565613901">Οι σελιδοδείκτες που επισκεφτήκατε Ï€Ïόσφατα εμφανίζονται εδώ.</translation>
+<translation id="8398259832188219207">Η αναφοÏά σφαλμάτων μεταφοÏτώθηκε στις <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Σφάλματα (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">ΠÏέπει να εισαγάγετε δÏο φοÏές την ίδια φÏάση Ï€Ïόσβασης.</translation>
<translation id="8428213095426709021">Ρυθμίσεις</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222">Ο κεντÏικός υπολογιστής <ph name="HOST_NAME" /> άÏγησε Ï€Î¿Î»Ï Î½Î± ανταποκÏιθεί.</translation>
<translation id="852346902619691059">Αυτός ο διακομιστής δεν μπόÏεσε να αποδείξει ότι είναι το <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του δεν θεωÏείται έμπιστο από το λειτουÏγικό σÏστημα της συσκευής σας. Αυτό μποÏεί να οφείλεται σε εσφαλμένη ÏÏθμιση ή σε κάποιον εισβολέα που παÏεμβαίνει στη σÏνδεσή σας. <ph name="BEGIN_LEARN_MORE_LINK" />Μάθετε πεÏισσότεÏα<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Αυτός ο Ï„Ïπος κάÏτας δεν υποστηÏίζεται από το Google Payments. Επιλέξτε άλλη κάÏτα.</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="8553075262323480129">Η μετάφÏαση απέτυχε επειδή δεν ήταν δυνατός ο Ï€ÏοσδιοÏισμός της σελίδας.</translation>
<translation id="8559762987265718583">Δεν είναι δυνατή η επίτευξη ιδιωτικής σÏνδεσης με τον τομέα <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> επειδή η ημεÏομηνία και η ÏŽÏα (<ph name="DATE_AND_TIME" />) της συσκευής σας είναι λανθασμένες.</translation>
-<translation id="856992080682148">Το πιστοποιητικό για αυτόν τον ιστότοπο λήγει το 2017 και η αλυσίδα πιστοποιητικών πεÏιέχει ένα πιστοποιητικό το οποίο έχει υπογÏαφεί με SHA-1.</translation>
<translation id="8571890674111243710">ΜετάφÏαση σελίδας σε <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Αυτό το πιστοποιητικό δεν καθοÏίζει κάποιο μηχανισμό για να ελέγχεται εάν έχει ανακληθεί.</translation>
<translation id="8620436878122366504">Οι γονείς σας δεν τον έχουν εγκÏίνει ακόμα</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522">Δεν ήταν δυνατός ο εντοπισμός της &lt;abbr id="dnsDefinition"&gt;διεÏθυνσης DNS&lt;/abbr&gt; του κεντÏÎ¹ÎºÎ¿Ï Ï…Ï€Î¿Î»Î¿Î³Î¹ÏƒÏ„Î® <ph name="HOST_NAME" />. Γίνεται διάγνωση του Ï€Ïοβλήματος.</translation>
<translation id="8790007591277257123">&amp;Επανάληψη διαγÏαφής</translation>
<translation id="8798099450830957504">ΠÏοεπιλογή</translation>
+<translation id="8800988563907321413">Οι Ï€Ïοτάσεις σε κοντινή απόσταση εμφανίζονται εδώ</translation>
<translation id="8804164990146287819">Πολιτική αποÏÏήτου</translation>
<translation id="8820817407110198400">Σελιδοδείκτες</translation>
<translation id="8834246243508017242">ΕνεÏγοποίηση Αυτόματης συμπλήÏωσης χÏησιμοποιώντας τις Επαφές…</translation>
<translation id="883848425547221593">Άλλοι σελιδοδείκτες</translation>
+<translation id="884264119367021077">ΔιεÏθυνση αποστολής</translation>
<translation id="884923133447025588">Δεν εντοπίστηκε μηχανισμός ακÏÏωσης.</translation>
<translation id="885730110891505394">Κοινοποίηση στις υπηÏεσίες της Google</translation>
<translation id="8866481888320382733">Σφάλμα ανάλυσης Ïυθμίσεων πολιτικής</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">Το πιστοποιητικό του διακομιστή έχει λήξει.</translation>
<translation id="8987927404178983737">Μήνας</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Ο ιστότοπος που Ï€Ïόκειται να επισκεφτείτε πεÏιέχει κακόβουλα Ï€ÏογÏάμματα</translation>
<translation id="9001074447101275817">Απαιτείται όνομα χÏήστη και κωδικός Ï€Ïόσβασης για το διακομιστή μεσολάβησης <ph name="DOMAIN" />.</translation>
<translation id="901974403500617787">Οι επισημάνσεις που ισχÏουν για ολόκληÏο το σÏστημα μποÏοÏν να οÏιστοÏν μόνο από τον κάτοχο: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Αυτή η σελίδα έχει μεταφÏαστεί στα <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="9035022520814077154">Σφάλμα ασφάλειας</translation>
<translation id="9038649477754266430">ΧÏήση μιας υπηÏεσίας Ï€Ïόβλεψης για ταχÏτεÏη φόÏτωση σελίδων</translation>
<translation id="9039213469156557790">Επίσης, αυτή η σελίδα πεÏιέχει άλλους πόÏους, οι οποίοι δεν είναι ασφαλείς. Αυτοί οι πόÏοι μποÏοÏν να Ï€ÏοβληθοÏν από άλλους χÏήστες κατά τη μετάβαση και μποÏοÏν να Ï„ÏοποποιηθοÏν από έναν εισβολέα ώστε να αλλάξει η συμπεÏιφοÏά της σελίδας.</translation>
+<translation id="9040185888511745258">Όσοι εισβάλλουν στο <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> μποÏεί να Ï€Ïοσπαθήσουν να σας ξεγελάσουν ώστε να εγκαταστήσετε Ï€ÏογÏάμματα που βλάπτουν την εμπειÏία πεÏιήγησής σας (για παÏάδειγμα, αλλάζοντας την αÏχική σελίδα σας ή εμφανίζοντας επιπλέον διαφημίσεις στους ιστότοπους που επισκέπτεστε).</translation>
<translation id="9050666287014529139">ΦÏάση Ï€Ïόσβασής σας</translation>
<translation id="9065203028668620118">ΕπεξεÏγασία</translation>
<translation id="9068849894565669697">Επιλογή χÏώματος</translation>
<translation id="9076283476770535406">ΜποÏεί να διαθέτει πεÏιεχόμενο για ενηλίκους</translation>
-<translation id="9092364396508701805">Η σελίδα του κεντÏÎ¹ÎºÎ¿Ï Ï…Ï€Î¿Î»Î¿Î³Î¹ÏƒÏ„Î® <ph name="HOST_NAME" /> δεν λειτουÏγεί</translation>
<translation id="9103872766612412690">Κανονικά, ο ιστότοπος <ph name="SITE" /> χÏησιμοποιεί κÏυπτογÏάφηση για να Ï€ÏοστατεÏει τα στοιχεία σας. Όταν το Chromium επιχείÏησε Ï€Ïόσφατα να συνδεθεί στο <ph name="SITE" />, ο ιστότοπος ανταποκÏίθηκε δημιουÏγώντας ασυνήθιστα και εσφαλμένα διαπιστευτήÏια. Αυτό μποÏεί να συμβεί όταν κάποιος εισβολέας Ï€Ïοσπαθεί να υποκÏιθεί ότι είναι ο ιστότοπος <ph name="SITE" /> ή όταν κάποια οθόνη σÏνδεσης Wi-Fi έχει διακόψει τη σÏνδεσή σας. Τα στοιχεία σας εξακολουθοÏν να είναι ασφαλή επειδή το Chromium διέκοψε τη σÏνδεση Ï€Ïιν από την ανταλλαγή δεδομένων.</translation>
<translation id="9137013805542155359">Εμφάνιση Ï€ÏωτοτÏπου</translation>
+<translation id="9137248913990643158">Εκκινήστε και συνδεθείτε στο Chrome Ï€Ïιν χÏησιμοποιήσετε αυτήν την εφαÏμογή.</translation>
<translation id="9148507642005240123">&amp;ΑναίÏεση επεξεÏγασίας</translation>
<translation id="9157595877708044936">ΡÏθμιση...</translation>
<translation id="9170848237812810038">Αναί&amp;Ïεση</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">ΔΙΑΓΡΑΦΗ ΦΟΡΜΑΣ</translation>
<translation id="939736085109172342">Îέος φάκελος</translation>
<translation id="941721044073577244">Φαίνεται ότι δεν έχετε άδεια να επισκεφτείτε αυτόν τον ιστότοπο</translation>
-<translation id="962701380617707048">Εισαγάγετε την ημεÏομηνία λήξης και τον κωδικό CVC για την κάÏτα <ph name="CREDIT_CARD" />, Ï€Ïοκειμένου να ενημεÏώσετε τα στοιχεία της κάÏτας σας</translation>
<translation id="969892804517981540">Επίσημη έκδοση</translation>
<translation id="988159990683914416">Έκδοση Ï€ÏογÏαμματιστή</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 5d9b1e6a064..69b9501b3f6 100644
--- a/chromium/components/strings/components_strings_en-GB.xtb
+++ b/chromium/components/strings/components_strings_en-GB.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Reconnecting to Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Print...</translation>
<translation id="1181037720776840403">Remove</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Automatically report<ph name="END_WHITEPAPER_LINK" /> details of possible security incidents to Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Next</translation>
<translation id="1201895884277373915">More from this site</translation>
<translation id="1206967143813997005">Bad initial signature</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cyan</translation>
<translation id="1629803312968146339">Do you want Chrome to save this card?</translation>
<translation id="1640180200866533862">User policies</translation>
+<translation id="1640244768702815859">Try <ph name="BEGIN_LINK" />visiting the site's homepage<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">The network configuration is invalid and couldn't be imported.</translation>
<translation id="1644574205037202324">History</translation>
<translation id="1645368109819982629">Unsupported protocol</translation>
<translation id="1676269943528358898"><ph name="SITE" /> normally uses encryption to protect your information. When Google Chrome tried to connect to <ph name="SITE" /> this time, the website sent back unusual and incorrect credentials. This may happen when an attacker is trying to pretend to be <ph name="SITE" />, or a Wi-Fi sign-in screen has interrupted the connection. Your information is still secure because Google Chrome stopped the connection before any data was exchanged.</translation>
+<translation id="168328519870909584">Attackers currently on <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> might attempt to install dangerous apps on your device that steal or delete your information (for example, photos, passwords, messages and credit cards).</translation>
<translation id="168841957122794586">The server certificate contains a weak cryptographic key.</translation>
-<translation id="1701955595840307032">Get suggested content</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">You need permission from <ph name="NAME" /> to visit this site</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Page number</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Try running Windows Network Diagnostics<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Please update your sync passphrase.</translation>
+<translation id="1787142507584202372">Your open tabs appear here</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">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. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Invalid request or request parameters</translation>
+<translation id="1834321415901700177">This site contains harmful programs</translation>
<translation id="1838667051080421715">You are viewing the source of a web page.</translation>
<translation id="1871208020102129563">Proxy is set to use fixed proxy servers, not a .pac script URL.</translation>
<translation id="1883255238294161206">Collapse list</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Mobile Bookmarks</translation>
<translation id="2148716181193084225">Today</translation>
-<translation id="2149973817440762519">Edit Bookmark</translation>
<translation id="2154054054215849342">Sync is not available for your domain</translation>
<translation id="2166049586286450108">Full Admin Access</translation>
<translation id="2166378884831602661">This site can’t provide a secure connection</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">You cannot visit <ph name="SITE" /> at the moment because the website uses HSTS. Network errors and attacks are usually temporary, so this page will probably work later. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Ignored invalid bookmark at index <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Other bookmarks</translation>
+<translation id="2355395290879513365">Attackers might be able to see the images that you’re looking at on this site and trick you by modifying them.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Show policies with no value set</translation>
<translation id="2396249848217231973">&amp;Undo delete</translation>
<translation id="2455981314101692989">This web page has disabled automatic filling for this form.</translation>
+<translation id="2460160116472764928">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. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Fill in</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Running Network Diagnostics<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Invalid search URL.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Are you sure that you want to delete these pages from your history?</translation>
<translation id="2677748264148917807">Leave</translation>
<translation id="269990154133806163">The server presented a certificate that was not publicly disclosed using the Certificate Transparency policy. This is a requirement for some certificates, to ensure that they are trustworthy and protect against attackers. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Reading List</translation>
<translation id="2704283930420550640">Value doesn't match format.</translation>
<translation id="2704951214193499422">Chromium was unable to confirm your card at this time. Please try again later.</translation>
<translation id="2705137772291741111">The saved (cached) copy of this site was unreadable.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">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. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Ask permission</translation>
<translation id="2713444072780614174">White</translation>
+<translation id="2720342946869265578">Nearby</translation>
<translation id="2721148159707890343">Request succeeded</translation>
<translation id="2728127805433021124">Server's certificate is signed using a weak signature algorithm.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Running Connectivity Diagnostics<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">You can disable any proxies configured for a connection from the settings page.</translation>
<translation id="2955913368246107853">Close find bar</translation>
<translation id="2958431318199492670">The network configuration doesn't comply to the ONC standard. Parts of the configuration may not be imported.</translation>
+<translation id="29611076221683977">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).</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="2985306909656435243">If enabled, Chromium will store a copy of your card on this device for faster form filling.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Hide details</translation>
<translation id="3587482841069643663">All</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">The server does not support the TLS renegotiation extension.</translation>
<translation id="36224234498066874">Clear Browsing Data...</translation>
<translation id="362276910939193118">Show Full History</translation>
<translation id="3623476034248543066">Show value</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Licenses exhausted</translation>
<translation id="3714780639079136834">Turning on mobile data or Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Checking the proxy, firewall and DNS configuration<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">If you understand the risks to your security, you may <ph name="BEGIN_LINK" />visit this unsafe site<ph name="END_LINK" /> before the dangerous programs have been removed.</translation>
<translation id="3739623965217189342">Link that you copied</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="3788090790273268753">The certificate for this site expires in 2016, and the certificate chain contains a certificate signed using SHA-1.</translation>
<translation id="382518646247711829">If you use a proxy server...</translation>
<translation id="3828924085048779000">Empty passphrase is not allowed.</translation>
<translation id="3845539888601087042">Showing history from your signed-in devices. <ph name="BEGIN_LINK" />Find out more<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Key "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">The client and server don't support a common SSL protocol version or cipher suite.</translation>
<translation id="4079302484614802869">Proxy configuration is set to use a .pac script URL, not fixed proxy servers.</translation>
+<translation id="4098354747657067197">Deceptive site ahead</translation>
<translation id="4103249731201008433">Device serial number is invalid</translation>
<translation id="4103763322291513355">Visit &lt;strong&gt;chrome://policy&lt;/strong&gt; to see the list of blacklisted URLs and other policies enforced by your system administrator.</translation>
<translation id="4110615724604346410">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. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{none}=1{1 app ($1)}=2{2 apps ($1, $2)}other{# apps ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Crashes</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Try running Network Diagnostics<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Your connection to this site is not fully secure</translation>
<translation id="4250680216510889253">No</translation>
<translation id="425582637250725228">Changes that you made may not be saved.</translation>
<translation id="4258748452823770588">Bad signature</translation>
<translation id="4269787794583293679">(No username)</translation>
+<translation id="4280429058323657511">, exp <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google Safe Browsing recently <ph name="BEGIN_LINK" />found harmful programs<ph name="END_LINK" /> on <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Parent suggestions</translation>
<translation id="4304224509867189079">Log In</translation>
<translation id="432290197980158659">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. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Failed to find article</translation>
+<translation id="4326324639298822553">Check your expiry date and try again</translation>
<translation id="4331708818696583467">Not Secure</translation>
+<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="4381091992796011497">User Name:</translation>
<translation id="4394049700291259645">Disable</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">You attempted to reach <ph name="DOMAIN" />, but the server presented an invalid certificate. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Tabs from other devices</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">You are viewing an extension page.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Reload policies</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4744603770635761495">Executable Path</translation>
+<translation id="4750917950439032686">Your information (for example, passwords or credit card numbers) is private when it is sent to this site.</translation>
<translation id="4756388243121344051">&amp;History</translation>
+<translation id="4759118997339041434">Payment autofilling disabled</translation>
<translation id="4764776831041365478">The web page at <ph name="URL" /> might be temporarily down or it may have moved permanently to a new web address.</translation>
<translation id="4771973620359291008">An unknown error has occurred.</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="4813512666221746211">Network error</translation>
<translation id="4816492930507672669">Fit to page</translation>
<translation id="4850886885716139402">View</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{and 1 more web page}other{and # more web pages}}</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="495170559598752135">Actions</translation>
<translation id="4958444002117714549">Expand list</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Downloading</translation>
<translation id="5190835502935405962">Bookmarks Bar</translation>
<translation id="5199729219167945352">Experiments</translation>
-<translation id="5199841536747119669">Your suggestions appear here</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="5299298092464848405">Error parsing policy</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Crash reporting is disabled.</translation>
<translation id="5317780077021120954">Save</translation>
<translation id="5327248766486351172">Name</translation>
+<translation id="5337705430875057403">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).</translation>
<translation id="5359637492792381994">This server could not prove that it is <ph name="DOMAIN" />; its security certificate is not valid at this time. This may be caused by a misconfiguration or an attacker intercepting your connection. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Failed to store policy settings</translation>
+<translation id="5386426401304769735">The certificate chain for this site contains a certificate signed using SHA-1.</translation>
<translation id="5421136146218899937">Clear browsing data...</translation>
<translation id="5430298929874300616">Remove bookmark</translation>
<translation id="5431657950005405462">Your file was not found</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">An embedded page at <ph name="SITE" /> says:</translation>
<translation id="5556459405103347317">Reload</translation>
<translation id="5565735124758917034">Active</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>
+<translation id="5580958916614886209">Check your expiry month and try again</translation>
<translation id="560412284261940334">Management not supported</translation>
<translation id="5610142619324316209">Checking the connection</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> redirected you too many times.</translation>
<translation id="5622887735448669177">Do you want to leave this site?</translation>
<translation id="5629630648637658800">Failed to load policy settings</translation>
<translation id="5631439013527180824">Invalid device management token</translation>
+<translation id="5669703222995421982">Get personalised content</translation>
+<translation id="5675650730144413517">This page isn’t working</translation>
<translation id="5677928146339483299">Blocked</translation>
<translation id="5694783966845939798">You attempted to reach <ph name="DOMAIN" />, but the server presented a certificate signed using a weak signature algorithm (such as SHA-1). This means that the security credentials the server presented could have been forged, and the server may not be the server you expected (you may be communicating with an attacker). <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">The identity of this website has not been verified.</translation>
<translation id="5720705177508910913">Current user</translation>
-<translation id="572328651809341494">Recent tabs</translation>
<translation id="5732392974455271431">Your parents can unblock it for you</translation>
<translation id="5784606427469807560">There was a problem confirming your card. Check your Internet connection and try again.</translation>
<translation id="5785756445106461925">Further, this page includes other resources which are not secure. These resources can be viewed by others while in transit, and can be modified by an attacker to change the look of the page.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">You attempted to reach <ph name="DOMAIN" />, but the server presented a certificate containing a weak key. An attacker could have broken the private key, and the server may not be the server you expected (you may be communicating with an attacker). <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">New Folder</translation>
<translation id="5869405914158311789">This site can’t be reached</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">This type of card is not supported by Google Payments for this merchant. Please select a different card.</translation>
<translation id="59174027418879706">Enabled</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="5966707198760109579">Week</translation>
<translation id="5967867314010545767">Remove from history</translation>
<translation id="5975083100439434680">Zoom out</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Try:</translation>
<translation id="6151417162996330722">The server certificate has a validity period that is too long.</translation>
<translation id="6165508094623778733">Learn more</translation>
+<translation id="6177128806592000436">Your connection to this site is not secure</translation>
<translation id="6203231073485539293">Check your Internet connection</translation>
<translation id="6218753634732582820">Remove address from Chromium?</translation>
+<translation id="6251924700383757765">Privacy Policy</translation>
+<translation id="625755898061068298">You have chosen to disable security warnings for this site.</translation>
<translation id="6259156558325130047">&amp;Redo Reorder</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> bookmarks</translation>
<translation id="6264485186158353794">Back to safety</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> is unreachable.</translation>
<translation id="6321917430147971392">Check your DNS settings</translation>
<translation id="6328639280570009161">Try disabling network prediction</translation>
+<translation id="6328786501058569169">This site is deceptive</translation>
<translation id="6337534724793800597">Filter policies by name</translation>
<translation id="6342069812937806050">Just now</translation>
<translation id="6345221851280129312">unknown size</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">You cannot visit <ph name="SITE" /> at the moment because the website sent scrambled credentials that Chromium cannot process. Network errors and attacks are usually temporary, so this page will probably work later. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Unable to check whether the certificate has been revoked.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Ignored because default search is disabled by policy.</translation>
<translation id="6462969404041126431">This server could not prove that it is <ph name="DOMAIN" />; its security certificate might be revoked. This may be caused by a misconfiguration or an attacker intercepting your connection. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Redo Delete</translation>
<translation id="6534179046333460208">Physical Web suggestions</translation>
<translation id="6550675742724504774">Options</translation>
+<translation id="6556239504065605927">Secure connection</translation>
<translation id="6563469144985748109">Your manager hasn't approved it yet</translation>
<translation id="6593753688552673085">less than <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Encryption options</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">This policy has been deprecated.</translation>
<translation id="6652240803263749613">This server could not prove that it is <ph name="DOMAIN" />; its security certificate is not trusted by your computer's operating system. This may be caused by a misconfiguration or an attacker intercepting your connection. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Edit Folder</translation>
-<translation id="6660210980321319655">Automatically reported <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Remove form suggestion from Chromium?</translation>
<translation id="6685834062052613830">Sign out and complete setup</translation>
<translation id="6710213216561001401">Previous</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Policy Value</translation>
<translation id="6757797048963528358">Your device went to sleep.</translation>
<translation id="6778737459546443941">Your parent hasn't approved it yet</translation>
+<translation id="6810899417690483278">Customisation ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">The web page at <ph name="URL" /> is currently unavailable. It may be overloaded or down for maintenance.</translation>
<translation id="6831043979455480757">Translate</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Contact info</translation>
+<translation id="7079718277001814089">This site contains malware</translation>
<translation id="7087282848513945231">County</translation>
<translation id="7088615885725309056">Older</translation>
<translation id="7090678807593890770">Search Google for <ph name="LINK" /></translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> doesn't adhere to security standards.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Learn more<ph name="END_LINK" /> about this problem.</translation>
<translation id="7219179957768738017">The connection uses <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">The site ahead contains malware</translation>
<translation id="724975217298816891">Enter the expiry date and CVC for <ph name="CREDIT_CARD" /> to update your card details. Once you've confirmed, your card details will be shared with this site.</translation>
<translation id="725866823122871198">A private connection to <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> can't be established because your computer's date and time (<ph name="DATE_AND_TIME" />) are incorrect.</translation>
<translation id="7269802741830436641">This web page has a redirect loop</translation>
@@ -611,6 +648,7 @@ Psst! Incognito mode <ph name="SHORTCUT_KEY" /> may come in handy next time.</tr
<translation id="7658239707568436148">Cancel</translation>
<translation id="7667346355482952095">Returned policy token is empty or doesn't match current token</translation>
<translation id="7668654391829183341">Unknown device</translation>
+<translation id="7669271284792375604">Attackers on this site might try to trick you into installing programs that harm your browsing experience (for example, by changing your homepage or showing extra ads on sites that you visit).</translation>
<translation id="7674629440242451245">Interested in cool new Chrome features? Try our dev channel at chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Proceed to <ph name="SITE" /> (unsafe)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">This site can’t be loaded from the cache</translation>
@@ -626,14 +664,17 @@ Psst! Incognito mode <ph name="SHORTCUT_KEY" /> may come in handy next time.</tr
<translation id="7800304661137206267">The connection is encrypted using <ph name="CIPHER" />, with <ph name="MAC" /> for message authentication and <ph name="KX" /> as the key exchange mechanism.</translation>
<translation id="780301667611848630">No, thank you</translation>
<translation id="7805768142964895445">Status</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Remove form suggestion from Chrome?</translation>
<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="7887683347370398519">Check your CVC and try again</translation>
<translation id="7912024687060120840">In Folder:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Server's certificate is not yet valid.</translation>
<translation id="7942349550061667556">Red</translation>
+<translation id="7947285636476623132">Check your expiry year and try again</translation>
<translation id="7951415247503192394">(32-bit)</translation>
<translation id="7956713633345437162">Mobile bookmarks</translation>
<translation id="7961015016161918242">Never</translation>
@@ -641,7 +682,10 @@ Psst! Incognito mode <ph name="SHORTCUT_KEY" /> may come in handy next time.</tr
<translation id="7983301409776629893">Always translate <ph name="ORIGINAL_LANGUAGE" /> to <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Not Specified</translation>
<translation id="8012647001091218357">We could not reach your parents at the moment. Please try again.</translation>
+<translation id="8025119109950072390">Attackers on this site may trick you into doing something dangerous like installing software or revealing your personal information (for example passwords, phone numbers or credit cards).</translation>
+<translation id="803030522067524905">Google Safe Browsing recently detected phishing on <ph name="SITE" />. Phishing sites pretend to be other websites to trick you. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">This page is in <ph name="SOURCE_LANGUAGE" />. Translate it to <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Send Feedback</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>
@@ -652,9 +696,11 @@ Psst! Incognito mode <ph name="SHORTCUT_KEY" /> may come in handy next time.</tr
<translation id="8150722005171944719">The file at <ph name="URL" /> is not readable. It may have been removed, moved or file permissions may be preventing access.</translation>
<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="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="8230421197304563332">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).</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="8249320324621329438">Last fetched:</translation>
<translation id="8261506727792406068">Delete</translation>
@@ -663,11 +709,13 @@ Psst! Incognito mode <ph name="SHORTCUT_KEY" /> may come in handy next time.</tr
<translation id="8294431847097064396">Source</translation>
<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="8349305172487531364">Bookmarks bar</translation>
<translation id="8363502534493474904">Turning off aeroplane mode</translation>
<translation id="8364627913115013041">Not set.</translation>
<translation id="8380941800586852976">Dangerous</translation>
<translation id="8382348898565613901">Your recently visited bookmarks appear here</translation>
+<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="8428213095426709021">Settings</translation>
@@ -679,9 +727,9 @@ Psst! Incognito mode <ph name="SHORTCUT_KEY" /> may come in handy next time.</tr
<translation id="8498891568109133222"><ph name="HOST_NAME" /> took too long to respond.</translation>
<translation id="852346902619691059">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. <ph name="BEGIN_LEARN_MORE_LINK" />Find out more<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">This type of card is not supported by Google Payments. Please select a different card.</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="8553075262323480129">The translation failed because the page's language could not be determined.</translation>
<translation id="8559762987265718583">A private connection to <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> can't be established because your device's date and time (<ph name="DATE_AND_TIME" />) are incorrect.</translation>
-<translation id="856992080682148">The certificate for this site expires in 2017 or later, and the certificate chain contains a certificate signed using SHA-1.</translation>
<translation id="8571890674111243710">Translating page into <ph name="LANGUAGE" />...</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>
@@ -694,10 +742,12 @@ Psst! Incognito mode <ph name="SHORTCUT_KEY" /> may come in handy next time.</tr
<translation id="8740359287975076522"><ph name="HOST_NAME" />’s &lt;abbr id="dnsDefinition"&gt;DNS address&lt;/abbr&gt; could not be found. Diagnosing the problem.</translation>
<translation id="8790007591277257123">&amp;Redo delete</translation>
<translation id="8798099450830957504">Default</translation>
+<translation id="8800988563907321413">Your nearby suggestions appear here</translation>
<translation id="8804164990146287819">Privacy Policy</translation>
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="8834246243508017242">Enable Autofill using Contacts…</translation>
<translation id="883848425547221593">Other Bookmarks</translation>
+<translation id="884264119367021077">Delivery address</translation>
<translation id="884923133447025588">No revocation mechanism found.</translation>
<translation id="885730110891505394">Sharing with Google</translation>
<translation id="8866481888320382733">Error parsing policy settings</translation>
@@ -715,18 +765,21 @@ Psst! Incognito mode <ph name="SHORTCUT_KEY" /> may come in handy next time.</tr
<translation id="8971063699422889582">Server's certificate has expired.</translation>
<translation id="8987927404178983737">Month</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">The site ahead contains harmful programs</translation>
<translation id="9001074447101275817">The proxy <ph name="DOMAIN" /> requires a username and password.</translation>
<translation id="901974403500617787">Flags that apply system-wide can only be set by the owner: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">This page has been translated to <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Security error</translation>
<translation id="9038649477754266430">Use a prediction service to load pages more quickly</translation>
<translation id="9039213469156557790">Furthermore, this page includes other resources which are not secure. These resources can be viewed by others while in transit, and can be modified by an attacker to change the behaviour of the page.</translation>
+<translation id="9040185888511745258">Attackers on <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> might attempt to trick you into installing programs that harm your browsing experience (for example, by changing your homepage or showing extra ads on sites that you visit).</translation>
<translation id="9050666287014529139">Passphrase</translation>
<translation id="9065203028668620118">Edit</translation>
<translation id="9068849894565669697">Select colour</translation>
<translation id="9076283476770535406">It may have mature content</translation>
-<translation id="9092364396508701805">The <ph name="HOST_NAME" /> page isn’t working</translation>
<translation id="9103872766612412690"><ph name="SITE" /> normally uses encryption to protect your information. When Chromium tried to connect to <ph name="SITE" /> this time, the website sent back unusual and incorrect credentials. This may happen when an attacker is trying to pretend to be <ph name="SITE" />, or a Wi-Fi sign-in screen has interrupted the connection. Your information is still secure because Chromium stopped the connection before any data was exchanged.</translation>
<translation id="9137013805542155359">Show original</translation>
+<translation id="9137248913990643158">Please start and sign in to Chrome before using this app.</translation>
<translation id="9148507642005240123">&amp;Undo edit</translation>
<translation id="9157595877708044936">Setting up...</translation>
<translation id="9170848237812810038">&amp;Undo</translation>
@@ -739,7 +792,6 @@ Psst! Incognito mode <ph name="SHORTCUT_KEY" /> may come in handy next time.</tr
<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="962701380617707048">Enter the expiry date and CVC for <ph name="CREDIT_CARD" /> to update your card details</translation>
<translation id="969892804517981540">Official Build</translation>
<translation id="988159990683914416">Developer Build</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 f02468ac584..dcbe5fcf9a3 100644
--- a/chromium/components/strings/components_strings_es-419.xtb
+++ b/chromium/components/strings/components_strings_es-419.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Volver a conectarte a Wi-Fi</translation>
<translation id="1175364870820465910">Im&amp;primir...</translation>
<translation id="1181037720776840403">Eliminar</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Informar automáticamente<ph name="END_WHITEPAPER_LINK" /> los detalles de posibles incidentes de seguridad a Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Siguiente</translation>
<translation id="1201895884277373915">Más sobre este sitio</translation>
<translation id="1206967143813997005">La firma inicial no es válida</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cian</translation>
<translation id="1629803312968146339">¿Quieres que Chrome guarde esta tarjeta?</translation>
<translation id="1640180200866533862">Políticas de usuario</translation>
+<translation id="1640244768702815859">Intenta <ph name="BEGIN_LINK" />visitar la página principal del sitio<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">La configuración de red no es válida y no se pudo importar.</translation>
<translation id="1644574205037202324">Historial</translation>
<translation id="1645368109819982629">Protocolo no compatible</translation>
<translation id="1676269943528358898"><ph name="SITE" /> suele utilizar la encriptación para proteger la información. Cuando Google Chrome intentó conectarse a <ph name="SITE" />, el sitio web devolvió credenciales incorrectas y poco comunes. Es posible que un atacante quiera suplantar a <ph name="SITE" /> o que una pantalla de acceso Wi-Fi haya interrumpido la conexión. Tu información permanece segura porque Google Chrome detuvo la conexión para evitar el intercambio de datos.</translation>
+<translation id="168328519870909584">Los atacantes que se encuentran actualmente en <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podrían intentar instalar aplicaciones peligrosas en tu dispositivo con el objetivo de robarte información o borrarla (por ejemplo, fotos, contraseñas, mensajes y tarjetas de crédito).</translation>
<translation id="168841957122794586">El certificado del servidor contiene una clave criptográfica no segura.</translation>
-<translation id="1701955595840307032">Obtener contenido sugerido</translation>
<translation id="1710259589646384581">SO</translation>
<translation id="1721312023322545264">Necesitas permiso de <ph name="NAME" /> para visitar este sitio</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Número de página</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Intenta ejecutar el Diagnóstico de red de Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Actualiza tu frase de contraseña de sincronización.</translation>
+<translation id="1787142507584202372">Tus pestañas abiertas aparecen aquí</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Navegación segura de Google <ph name="BEGIN_LINK" />detectó software malicioso<ph name="END_LINK" /> en <ph name="SITE" /> recientemente. Los sitios web que por lo general son seguros a veces se infectan con software malicioso. El contenido malicioso proviene de <ph name="SUBRESOURCE_HOST" />, un conocido distribuidor de software malicioso. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">Solicitud o parámetros de solicitud no válidos</translation>
+<translation id="1834321415901700177">Este sitio contiene programas dañinos</translation>
<translation id="1838667051080421715">Estás viendo la fuente de una página web.</translation>
<translation id="1871208020102129563">El proxy está configurado para usar servidores proxy fijos, no una URL de script .pac.</translation>
<translation id="1883255238294161206">Ocultar lista</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Favoritos del celular</translation>
<translation id="2148716181193084225">Hoy</translation>
-<translation id="2149973817440762519">Editar marcador</translation>
<translation id="2154054054215849342">El servicio de sincronización no está disponible para tu dominio</translation>
<translation id="2166049586286450108">Acceso de administrador completo</translation>
<translation id="2166378884831602661">Este sitio no puede proporcionar una conexión segura</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">No puedes visitar <ph name="SITE" /> ahora mismo porque el sitio web usa HSTS. Los ataques y errores de red suelen ser temporales, por lo que es posible que esta página funcione más tarde. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">Marcador no válido ignorado en el índice <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Otros favoritos</translation>
+<translation id="2355395290879513365">Es posible que los atacantes vean las imágenes que observas en este sitio y las modifiquen para engañarte.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Mostrar políticas sin valor establecido</translation>
<translation id="2396249848217231973">&amp;Deshacer Eliminar</translation>
<translation id="2455981314101692989">Esta página web ha inhabilitado la función de rellenado automático para este formulario.</translation>
+<translation id="2460160116472764928">Navegación segura de Google <ph name="BEGIN_LINK" />detectó software malicioso<ph name="END_LINK" /> en <ph name="SITE" /> recientemente. Los sitios web que por lo general son seguros a veces se infectan con software malicioso. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">Llenar</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Ejecución del Diagnóstico de red<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL de búsqueda no válida</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">¿Estás seguro de que deseas eliminar estas páginas del historial?</translation>
<translation id="2677748264148917807">Abandonar</translation>
<translation id="269990154133806163">El servidor presentó un certificado que no se divulgó de forma pública con la política Certificado de transparencia. Este es un requisito para algunos certificados que permite garantizar su confiabilidad y protegerte de atacantes. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">Lista de lectura</translation>
<translation id="2704283930420550640">El valor no coincide con el formato.</translation>
<translation id="2704951214193499422">Chrome no pudo confirmar tu tarjeta. Vuelve a intentarlo más tarde.</translation>
<translation id="2705137772291741111">La copia guardada (en caché) de este sitio es ilegible.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">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; es posible que te comuniques con un atacante. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">Solicitar permiso</translation>
<translation id="2713444072780614174">Blanco</translation>
+<translation id="2720342946869265578">Cercanas</translation>
<translation id="2721148159707890343">Solicitud correcta</translation>
<translation id="2728127805433021124">El certificado del servidor está firmado con un algoritmo de firma no seguro.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Ejecución del Diagnóstico de conectividad<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Puedes inhabilitar los servidores proxy configurados para una conexión desde la página de configuración.</translation>
<translation id="2955913368246107853">Cerrar la barra de búsqueda</translation>
<translation id="2958431318199492670">La configuración de red no cumple con el estándar ONC. Es posible que no se importen algunas partes de la configuración.</translation>
+<translation id="29611076221683977">Los atacantes que se encuentran actualmente en <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podrían intentar instalar programas peligrosos en tu Mac con el fin de robarte información o eliminarla (por ejemplo, fotos, contraseñas, mensajes y tarjetas de crédito).</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="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>
@@ -260,7 +269,6 @@
<translation id="3586931643579894722">Ocultar detalles</translation>
<translation id="3587482841069643663">Todos</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">El servidor no admite la extensión de negociación TLS.</translation>
<translation id="36224234498066874">Eliminar datos de navegación...</translation>
<translation id="362276910939193118">Mostrar historial completo</translation>
<translation id="3623476034248543066">Mostrar valor</translation>
@@ -272,6 +280,7 @@
<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="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>
@@ -281,10 +290,10 @@
<translation id="3712624925041724820">Licencias agotadas</translation>
<translation id="3714780639079136834">Activar los datos móviles o la conexión Wi-Fi.</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Comprobar la configuración del proxy, firewall o DNS<ph name="END_LINK" />.</translation>
+<translation id="3736520371357197498">Si comprendes los riesgos para tu seguridad, puedes <ph name="BEGIN_LINK" />visitar este sitio no seguro<ph name="END_LINK" /> antes de que se hayan eliminado los programas peligrosos.</translation>
<translation id="3739623965217189342">Vínculo copiado</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="3788090790273268753">El certificado de este sitio web vence en 2016 y la cadena del certificado contiene un certificado que se firmó con SHA-1.</translation>
<translation id="382518646247711829">Si utilizas un servidor proxy...</translation>
<translation id="3828924085048779000">No se permite una frase de contraseña vacía.</translation>
<translation id="3845539888601087042">Se muestra el historial de los dispositivos a los que accediste. <ph name="BEGIN_LINK" />Más información<ph name="END_LINK" />.</translation>
@@ -306,6 +315,7 @@
<translation id="4058922952496707368">Clave "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">El cliente y el servidor no admiten un conjunto de cifrado o una versión de protocolo SSL en común.</translation>
<translation id="4079302484614802869">El proxy está configurado para usar una URL de script .pac, no servidores proxy fijos.</translation>
+<translation id="4098354747657067197">Sitio engañoso</translation>
<translation id="4103249731201008433">El número de serie del dispositivo no es válido.</translation>
<translation id="4103763322291513355">Visita &lt;strong&gt;chrome://policy&lt;/strong&gt; para ver las URL en lista negra y otras políticas que estableció el administrador del sistema.</translation>
<translation id="4110615724604346410">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; su certificado de seguridad tiene errores. Es posible que se deba a una configuración incorrecta o a que un atacante haya interceptado tu conexión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -323,15 +333,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ninguna}=1{1 app ($1)}=2{2 apps ($1, $2)}other{# apps ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Fallos</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Intenta ejecutar el Diagnóstico de red<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Tu conexión con este sitio no es completamente segura</translation>
<translation id="4250680216510889253">No</translation>
<translation id="425582637250725228">Es posible que los cambios que implementaste no se puedan guardar.</translation>
<translation id="4258748452823770588">Firma no válida</translation>
<translation id="4269787794583293679">(Sin nombre de usuario)</translation>
+<translation id="4280429058323657511">, exp <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Navegación segura de Google <ph name="BEGIN_LINK" />encontró programas dañinos<ph name="END_LINK" /> en <ph name="SITE" /> recientemente. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">Sugerencias para padres</translation>
<translation id="4304224509867189079">Acceder</translation>
<translation id="432290197980158659">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. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">No se pudo encontrar el artículo</translation>
+<translation id="4326324639298822553">Comprueba la fecha de vencimiento y vuelve a intentarlo</translation>
<translation id="4331708818696583467">No seguro</translation>
+<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="4381091992796011497">Nombre de usuario:</translation>
<translation id="4394049700291259645">Inhabilitar</translation>
@@ -349,6 +364,7 @@
<translation id="4589078953350245614">Intentaste acceder a <ph name="DOMAIN" />, pero el servidor presentó un certificado no válido. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></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="4619615317237390068">Pestañas de otros dispositivos</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Estás viendo la página de una extensión.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -357,10 +373,13 @@
<translation id="4726672564094551039">Volver a cargar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
<translation id="4744603770635761495">Ruta ejecutable</translation>
+<translation id="4750917950439032686">Tu información (p. ej., contraseñas o números de tarjetas de crédito) es privada cuando se envía a este sitio.</translation>
<translation id="4756388243121344051">&amp;Historial</translation>
+<translation id="4759118997339041434">Se inhabilitó la función Autocompletar en los pagos</translation>
<translation id="4764776831041365478">Es posible que la página web en <ph name="URL" /> no funcione temporalmente o se haya trasladado de manera permanente a una nueva dirección web.</translation>
<translation id="4771973620359291008">Se ha producido un error desconocido.</translation>
<translation id="4800132727771399293">Verifica la fecha de vencimiento y el CVC, y vuelve a intentarlo.</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Error de red</translation>
<translation id="4816492930507672669">Ajustar a la página</translation>
<translation id="4850886885716139402">Ver</translation>
@@ -369,6 +388,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Acciones</translation>
<translation id="4958444002117714549">Mostrar lista</translation>
@@ -396,7 +416,6 @@
<translation id="5181140330217080051">Descargando...</translation>
<translation id="5190835502935405962">Barra de favoritos</translation>
<translation id="5199729219167945352">Experimentos</translation>
-<translation id="5199841536747119669">Tus sugerencias aparecen aquí</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="5299298092464848405">Error al analizar la política</translation>
@@ -404,8 +423,10 @@
<translation id="5308689395849655368">Notificación de fallas desactivada.</translation>
<translation id="5317780077021120954">Guardar</translation>
<translation id="5327248766486351172">Nombre</translation>
+<translation id="5337705430875057403">Los atacantes de <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podrían engañarte para que hagas algo peligroso, como instalar software o divulgar información personal (por ejemplo, contraseñas, números de teléfono o tarjetas de crédito).</translation>
<translation id="5359637492792381994">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; su certificado de seguridad no es válido en este momento. Es posible que se deba a una configuración incorrecta o a que un atacante haya interceptado tu conexión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">Error al almacenar la configuración de la política</translation>
+<translation id="5386426401304769735">La cadena del certificado de este sitio web contiene un certificado que se firmó con SHA-1.</translation>
<translation id="5421136146218899937">Borrar datos de navegación...</translation>
<translation id="5430298929874300616">Eliminar marcador</translation>
<translation id="5431657950005405462">No se encontró tu archivo</translation>
@@ -426,17 +447,20 @@
<translation id="5544037170328430102">Una página incrustada en <ph name="SITE" /> dice:</translation>
<translation id="5556459405103347317">Cargar de nuevo</translation>
<translation id="5565735124758917034">Activo</translation>
+<translation id="5572851009514199876">Abre Chrome y accede a tu cuenta para que el programa pueda comprobar si puedes acceder a este sitio.</translation>
+<translation id="5580958916614886209">Comprueba el mes de vencimiento y vuelve a intentarlo</translation>
<translation id="560412284261940334">No se admite la administración.</translation>
<translation id="5610142619324316209">Comprobar la conexión.</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> te redireccionó demasiadas veces.</translation>
<translation id="5622887735448669177">¿Deseas salir de este sitio?</translation>
<translation id="5629630648637658800">Error al cargar la configuración de la política</translation>
<translation id="5631439013527180824">Token de administración de dispositivos no válido</translation>
+<translation id="5669703222995421982">Obtener contenido personalizado</translation>
+<translation id="5675650730144413517">Esta página no funciona</translation>
<translation id="5677928146339483299">Bloqueado</translation>
<translation id="5694783966845939798">Intentaste acceder a <ph name="DOMAIN" />, pero el servidor presentó un certificado firmado con un algoritmo de firma no seguro (como SHA-1). Es posible que se hayan falsificado las credenciales de seguridad presentadas por el servidor y que este no sea el servidor esperado (es posible que te comuniques con un atacante). <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">No se ha verificado la identidad de este sitio web.</translation>
<translation id="5720705177508910913">Usuario actual</translation>
-<translation id="572328651809341494">Pestañas recientes</translation>
<translation id="5732392974455271431">Tus padres pueden desbloquearlo por ti</translation>
<translation id="5784606427469807560">Se produjo un problema al confirmar tu tarjeta. Comprueba tu conexión a Internet y vuelve a intentarlo.</translation>
<translation id="5785756445106461925">Además, esta página incluye otros recursos que no son seguros. Otras personas pueden ver estos recursos mientras se encuentran en tránsito, y un atacante puede modificarlos para cambiar la apariencia de la página.</translation>
@@ -445,6 +469,7 @@
<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="5843436854350372569">Intentaste acceder a <ph name="DOMAIN" />, pero el servidor presentó un certificado que contiene una clave no segura. Es posible que un atacante haya descifrado la clave privada y que este no sea el servidor esperado (es posible que te comuniques con un atacante). <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">Nueva carpeta</translation>
<translation id="5869405914158311789">No se puede acceder a este sitio</translation>
@@ -454,6 +479,7 @@
<translation id="59107663811261420">Google Payments no admite este tipo de tarjeta para este comerciante. Selecciona otra tarjeta.</translation>
<translation id="59174027418879706">Activado</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="5966707198760109579">Semana</translation>
<translation id="5967867314010545767">Eliminar del historial</translation>
<translation id="5975083100439434680">Alejar</translation>
@@ -472,8 +498,11 @@
<translation id="614940544461990577">Intenta:</translation>
<translation id="6151417162996330722">El certificado de servidor tiene un período de validez demasiado extenso.</translation>
<translation id="6165508094623778733">Más información</translation>
+<translation id="6177128806592000436">Tu conexión con este sitio no es segura</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="6251924700383757765">Política de privacidad</translation>
+<translation id="625755898061068298">Decidiste inhabilitar las advertencias de seguridad para este sitio.</translation>
<translation id="6259156558325130047">&amp;Rehacer Reorganizar</translation>
<translation id="6263376278284652872">Favoritos de <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Volver a seguridad</translation>
@@ -482,6 +511,7 @@
<translation id="6305205051461490394">No se puede acceder a <ph name="URL" />.</translation>
<translation id="6321917430147971392">Revisa la configuración de DNS.</translation>
<translation id="6328639280570009161">Intenta inhabilitar la predicción de red.</translation>
+<translation id="6328786501058569169">Este sitio es engañoso</translation>
<translation id="6337534724793800597">Filtrar políticas por nombre</translation>
<translation id="6342069812937806050">Recién</translation>
<translation id="6345221851280129312">tamaño desconocido</translation>
@@ -490,6 +520,8 @@
<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="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>
<translation id="6416403317709441254">No puedes visitar <ph name="SITE" /> ahora mismo porque el sitio web envió credenciales confusas que Chromium no puede procesar. Los ataques y errores de red suelen ser temporales, por lo que es posible que esta página funcione más tarde. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">No se pudo verificar si el certificado ha sido revocado.</translation>
@@ -499,10 +531,12 @@
<translation id="6458467102616083041">Se ignora porque la política inhabilita la búsqueda predeterminada.</translation>
<translation id="6462969404041126431">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; es posible que su certificado de seguridad se haya revocado. Es posible que se deba a una configuración incorrecta o a que un atacante haya interceptado tu conexión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<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="6529602333819889595">&amp;Rehacer Eliminar</translation>
<translation id="6534179046333460208">Sugerencias de la Web física</translation>
<translation id="6550675742724504774">Opciones</translation>
+<translation id="6556239504065605927">Conexión segura</translation>
<translation id="6563469144985748109">Tu administrador aún no lo aprobó</translation>
<translation id="6593753688552673085">menos de <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opciones de encriptación</translation>
@@ -511,7 +545,6 @@
<translation id="6644283850729428850">Esta política no ha sido aprobada.</translation>
<translation id="6652240803263749613">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; el sistema operativo de tu computadora no confía en el certificado de seguridad. Es posible que se deba a una configuración incorrecta o a que un atacante haya interceptado tu conexión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">Editar carpeta</translation>
-<translation id="6660210980321319655">Se informó automáticamente el <ph name="CRASH_TIME" /></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>
@@ -523,6 +556,7 @@
<translation id="6753269504797312559">Valor de la política</translation>
<translation id="6757797048963528358">El dispositivo se suspendió.</translation>
<translation id="6778737459546443941">Uno de tus padres aún no lo aprobó</translation>
+<translation id="6810899417690483278">ID de personalización</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">La página web en <ph name="URL" /> no está disponible en este momento. Es posible que esté sobrecargado o no disponible por mantenimiento.</translation>
<translation id="6831043979455480757">Traducir</translation>
@@ -533,7 +567,7 @@
<translation id="6897140037006041989">User agent</translation>
<translation id="6915804003454593391">Usuario:</translation>
<translation id="6957887021205513506">El certificado del servidor parece falso.</translation>
-<translation id="6965382102122355670">Aceptar</translation>
+<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Dispositivo</translation>
<translation id="6970216967273061347">Distrito</translation>
<translation id="6973656660372572881">Se especifican servidores proxy fijos y URL de secuencias de comandos .pac.</translation>
@@ -543,6 +577,8 @@
<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>
+<translation id="7064851114919012435">Información de contacto</translation>
+<translation id="7079718277001814089">Este sitio contiene software malicioso</translation>
<translation id="7087282848513945231">Condado</translation>
<translation id="7088615885725309056">Anterior</translation>
<translation id="7090678807593890770">Buscar <ph name="LINK" /> en Google</translation>
@@ -557,6 +593,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> no cumple con los estándares de seguridad.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Más información<ph name="END_LINK" /> acerca de este problema.</translation>
<translation id="7219179957768738017">La conexión usa <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Este sitio web contiene software malicioso</translation>
<translation id="724975217298816891">Ingresa la fecha de vencimiento y el CVC de la tarjeta <ph name="CREDIT_CARD" /> para actualizar sus datos. Después de confirmarla, los datos de tu tarjeta se compartirán con este sitio.</translation>
<translation id="725866823122871198">No se puede establecer una conexión privada a <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> porque la fecha y la hora del equipo (<ph name="DATE_AND_TIME" />) son incorrectas.</translation>
<translation id="7269802741830436641">Esta página web tiene un bucle de redirección</translation>
@@ -612,6 +649,7 @@
<translation id="7658239707568436148">Cancelar</translation>
<translation id="7667346355482952095">El token de política mostrado está vacío o no coincide con el token actual</translation>
<translation id="7668654391829183341">Dispositivo desconocido</translation>
+<translation id="7669271284792375604">Es posible que los atacantes de este sitio intenten engañarte para que instales programas que pueden afectar tu experiencia de navegación (p. ej., podrían cambiar la página principal o mostrar más anuncios en los sitios que visitas).</translation>
<translation id="7674629440242451245">Si estás interesado en probar nuevas e interesantes funciones de Chrome, visita nuestro canal de programadores en chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Continuar a <ph name="SITE" /> (no seguro)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">No se puede cargar este sitio desde la caché</translation>
@@ -627,14 +665,17 @@
<translation id="7800304661137206267">La conexión está encriptada mediante <ph name="CIPHER" />, con <ph name="MAC" /> para la autenticación de mensajes y <ph name="KX" /> como mecanismo de intercambio clave.</translation>
<translation id="780301667611848630">No, gracias</translation>
<translation id="7805768142964895445">Estado</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">¿Confirmas que quieres quitar la sugerencia de formulario de Chrome?</translation>
<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="7887683347370398519">Verifica tu CVC y vuelve a intentarlo.</translation>
<translation id="7912024687060120840">En carpeta:</translation>
<translation id="7935318582918952113">Filtro de DOM</translation>
<translation id="7938958445268990899">El certificado del servidor aún no es válido.</translation>
<translation id="7942349550061667556">Rojo</translation>
+<translation id="7947285636476623132">Comprueba el año de vencimiento y vuelve a intentarlo</translation>
<translation id="7951415247503192394">(32 bits)</translation>
<translation id="7956713633345437162">Favoritos del celular</translation>
<translation id="7961015016161918242">Nunca</translation>
@@ -642,7 +683,10 @@
<translation id="7983301409776629893">Siempre traducir del <ph name="ORIGINAL_LANGUAGE" /> al <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Sin especificar</translation>
<translation id="8012647001091218357">No pudimos comunicarnos con tus padres. Vuelve a intentarlo.</translation>
+<translation id="8025119109950072390">Es posible que los atacantes de este sitio te engañen para que hagas algo peligroso, como instalar software o divulgar información personal (p. ej., contraseñas, números de teléfono o tarjetas de crédito).</translation>
+<translation id="803030522067524905">Navegación segura de Google detectó suplantación de identidad (phishing) en <ph name="SITE" /> recientemente. Este tipo de sitios se hacen pasar por otros sitios web para engañarte. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">Esta página está en <ph name="SOURCE_LANGUAGE" />. ¿Quieres traducirla al <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Enviar comentario</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>
@@ -653,9 +697,11 @@
<translation id="8150722005171944719">El archivo de <ph name="URL" /> no se puede leer. Es posible que se haya eliminado o movido o que se impida el acceso a los permisos del archivo.</translation>
<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="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="8230421197304563332">Los atacantes que se encuentran actualmente en <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podrían intentar instalar programas peligrosos en tu computadora con el fin de robarte información o eliminarla (por ejemplo, fotos, contraseñas, mensajes y tarjetas de crédito).</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="8249320324621329438">Se obtuvo por última vez:</translation>
<translation id="8261506727792406068">Eliminar</translation>
@@ -664,11 +710,13 @@
<translation id="8294431847097064396">Fuente</translation>
<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="8349305172487531364">Barra de favoritos</translation>
<translation id="8363502534493474904">Desactivar el modo de avión.</translation>
<translation id="8364627913115013041">Sin establecer</translation>
<translation id="8380941800586852976">Peligrosa</translation>
<translation id="8382348898565613901">Los favoritos que visitaste recientemente aparecen aquí</translation>
+<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="8428213095426709021">Configuración</translation>
@@ -680,9 +728,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> tardó demasiado en responder.</translation>
<translation id="852346902619691059">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; el sistema operativo de tu dispositivo no confía en el certificado de seguridad. Es posible que se deba a una configuración incorrecta o a que un atacante haya interceptado tu conexión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Google Payments no admite este tipo de tarjeta. Selecciona otra tarjeta.</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="8553075262323480129">Falló la traducción debido a que no se pudo determinar el idioma de la página.</translation>
<translation id="8559762987265718583">No se puede establecer una conexión privada a <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> porque la fecha y la hora del dispositivo (<ph name="DATE_AND_TIME" />) son incorrectas.</translation>
-<translation id="856992080682148">El certificado de este sitio web vence en 2017 o más adelante, y la cadena del certificado contiene un certificado que se firmó con SHA-1.</translation>
<translation id="8571890674111243710">Traduciendo página a <ph name="LANGUAGE" />...</translation>
<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>
@@ -695,10 +743,12 @@
<translation id="8740359287975076522">No se encontró <ph name="HOST_NAME" />’s &lt;abbr id="dnsDefinition"&gt;DNS address&lt;/abbr&gt;. Se está diagnosticando el problema.</translation>
<translation id="8790007591277257123">&amp;Rehacer Eliminar</translation>
<translation id="8798099450830957504">Predeterminado</translation>
+<translation id="8800988563907321413">Las sugerencias de la sección Cercanas aparecen aquí</translation>
<translation id="8804164990146287819">Política de privacidad</translation>
<translation id="8820817407110198400">Favoritos</translation>
<translation id="8834246243508017242">Habilitar Autocompletar con Contactos…</translation>
<translation id="883848425547221593">Otros favoritos</translation>
+<translation id="884264119367021077">Dirección de envío</translation>
<translation id="884923133447025588">No se ha encontrado ningún mecanismo de revocación.</translation>
<translation id="885730110891505394">Compartir con Google</translation>
<translation id="8866481888320382733">Error al analizar la configuración de la política</translation>
@@ -716,18 +766,21 @@
<translation id="8971063699422889582">El certificado del servidor ha caducado.</translation>
<translation id="8987927404178983737">Mes</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">El siguiente sitio contiene programas peligrosos</translation>
<translation id="9001074447101275817">El proxy <ph name="DOMAIN" /> requiere un nombre de usuario y una contraseña.</translation>
<translation id="901974403500617787">El propietario es el único que puede especificar las marcas que se aplican a todo el sistema: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Esta página se tradujo al <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="9035022520814077154">Error de seguridad</translation>
<translation id="9038649477754266430">Utilizar un servicio de predicción para cargar las páginas más rápido</translation>
<translation id="9039213469156557790">Además, esta página incluye otros recursos que no son seguros. Otras personas pueden ver estos recursos mientras se encuentran en tránsito, y un atacante puede modificarlos para cambiar el funcionamiento de la página.</translation>
+<translation id="9040185888511745258">Los atacantes del sitio <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podrían intentar engañarte para que instales programas que pueden afectar la navegación (por ejemplo, podrían cambiar la página principal o mostrar más anuncios en los sitios que visitas).</translation>
<translation id="9050666287014529139">Frase de contraseña</translation>
<translation id="9065203028668620118">Editar</translation>
<translation id="9068849894565669697">Seleccionar color</translation>
<translation id="9076283476770535406">Es posible que incluya contenido para adultos</translation>
-<translation id="9092364396508701805">La página <ph name="HOST_NAME" /> no está funcionando</translation>
<translation id="9103872766612412690"><ph name="SITE" /> suele utilizar la encriptación para proteger la información. Cuando Chromium intentó conectarse a <ph name="SITE" />, el sitio web devolvió credenciales incorrectas y poco comunes. Es posible que un atacante quiera suplantar a <ph name="SITE" /> o que una pantalla de acceso Wi-Fi haya interrumpido la conexión. Tu información permanece segura porque Chromium detuvo la conexión para evitar el intercambio de datos.</translation>
<translation id="9137013805542155359">Mostrar original</translation>
+<translation id="9137248913990643158">Abre Chrome y accede a tu cuenta antes de usar esta app.</translation>
<translation id="9148507642005240123">&amp;Deshacer Editar</translation>
<translation id="9157595877708044936">Configurando...</translation>
<translation id="9170848237812810038">&amp;Deshacer</translation>
@@ -740,7 +793,6 @@
<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="962701380617707048">Ingresar la fecha de vencimiento y el CVC de tu tarjeta <ph name="CREDIT_CARD" /> para actualizar sus datos</translation>
<translation id="969892804517981540">Build oficial</translation>
<translation id="988159990683914416">Build para desarrolladores</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 377b58ac8f8..9776b707f21 100644
--- a/chromium/components/strings/components_strings_es.xtb
+++ b/chromium/components/strings/components_strings_es.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Volver a conectarte a una red Wi-Fi</translation>
<translation id="1175364870820465910">Im&amp;primir...</translation>
<translation id="1181037720776840403">Eliminar</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Informar automáticamente<ph name="END_WHITEPAPER_LINK" /> a Google sobre los detalles de posibles incidentes de seguridad. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Siguiente</translation>
<translation id="1201895884277373915">Más entradas de este sitio</translation>
<translation id="1206967143813997005">Firma inicial no válida</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cian</translation>
<translation id="1629803312968146339">¿Quieres que Chrome guarde esta tarjeta?</translation>
<translation id="1640180200866533862">Políticas de usuario</translation>
+<translation id="1640244768702815859">Prueba a <ph name="BEGIN_LINK" />acceder a la página principal del sitio web<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">La configuración de red no es válida y no se ha podido importar.</translation>
<translation id="1644574205037202324">Historial</translation>
<translation id="1645368109819982629">Protocolo no admitido</translation>
<translation id="1676269943528358898"><ph name="SITE" /> utiliza normalmente el cifrado para proteger tu información. Cuando Google Chrome intentó establecer conexión con <ph name="SITE" />, el sitio web devolvió unas credenciales inusuales e incorrectas. Esto puede ocurrir si un atacante intenta suplantar la identidad de <ph name="SITE" /> o si una pantalla de inicio de sesión Wi-Fi interrumpe la conexión. Tu información sigue estando protegida, ya que Google Chrome detuvo la conexión antes de que se intercambiaran datos.</translation>
+<translation id="168328519870909584">Los atacantes que se encuentran actualmente en el sitio <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podrían intentar instalar aplicaciones peligrosas en tu dispositivo para robar o eliminar tu información (como fotos, contraseñas, mensajes y tarjetas de crédito).</translation>
<translation id="168841957122794586">El certificado del servidor contiene una clave criptográfica no segura.</translation>
-<translation id="1701955595840307032">Obtener contenido sugerido</translation>
<translation id="1710259589646384581">Sistema operativo</translation>
<translation id="1721312023322545264">Necesitas permiso de <ph name="NAME" /> para acceder a este sitio web</translation>
<translation id="1734864079702812349">American Express</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Número de página</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Prueba a ejecutar Diagnósticos de red de Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Actualiza tu frase de contraseña de sincronización.</translation>
+<translation id="1787142507584202372">Las pestañas abiertas aparecen aquí</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">La función de navegación segura de Google <ph name="BEGIN_LINK" />detectó software malicioso<ph name="END_LINK" /> recientemente en <ph name="SITE" />. En ocasiones, los sitios web que suelen ser seguros contienen software malicioso. Este contenido procede de <ph name="SUBRESOURCE_HOST" />, un conocido distribuidor de este tipo de software. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">Parámetros de solicitud o solicitud no válidos</translation>
+<translation id="1834321415901700177">Este sitio web contiene programas dañinos</translation>
<translation id="1838667051080421715">Estás viendo la fuente de una página web.</translation>
<translation id="1871208020102129563">Se ha configurado el proxy de forma que use servidores proxy fijos, en lugar de una URL de secuencia de comandos .pac.</translation>
<translation id="1883255238294161206">Contraer lista</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Cliente nativo</translation>
<translation id="213826338245044447">Marcadores del móvil</translation>
<translation id="2148716181193084225">Hoy</translation>
-<translation id="2149973817440762519">Editar marcador</translation>
<translation id="2154054054215849342">La sincronización no está disponible para tu dominio</translation>
<translation id="2166049586286450108">Acceso de administrador completo</translation>
<translation id="2166378884831602661">Este sitio web no puede proporcionar una conexión segura</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">No puedes acceder a <ph name="SITE" /> en este momento por que el sitio web utiliza HSTS. Los ataques y los errores de red suelen ser temporales, por lo que es probable que esta página funcione más tarde. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">Se ha ignorado un marcador no válido en el índice <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Otros marcadores</translation>
+<translation id="2355395290879513365">Es posible que los atacantes puedan ver las imágenes que ves en este sitio web y que las modifiquen para engañarte.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Mostrar políticas sin valores establecidos</translation>
<translation id="2396249848217231973">&amp;Deshacer eliminación</translation>
<translation id="2455981314101692989">Esta página ha inhabilitado la opción de autocompletar para este formulario.</translation>
+<translation id="2460160116472764928">La función de navegación segura de Google <ph name="BEGIN_LINK" />detectó software malicioso<ph name="END_LINK" /> recientemente en <ph name="SITE" />. En ocasiones, los sitios web que suelen ser seguros contienen software malicioso. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">Rellenar</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Ejecutar Diagnósticos de red<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">La URL de búsqueda no es válida.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">¿Seguro que quieres eliminar estas páginas del historial?</translation>
<translation id="2677748264148917807">Salir</translation>
<translation id="269990154133806163">El servidor ha mostrado un certificado que no se ha hecho público mediante la política de transparencia en los certificados. Este requisito se aplica a algunos certificados para garantizar que son de confianza y ofrecer protección contra los atacantes. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">Lista de lectura</translation>
<translation id="2704283930420550640">El valor no coincide con el formato.</translation>
<translation id="2704951214193499422">Chromium no ha podido confirmar tu tarjeta en este momento. Vuelve a intentarlo más tarde.</translation>
<translation id="2705137772291741111">La copia guardada (almacenada en caché) de este sitio web no se ha podido leer.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">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. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">Solicitar permiso</translation>
<translation id="2713444072780614174">Blanco</translation>
+<translation id="2720342946869265578">Cercanas</translation>
<translation id="2721148159707890343">Solicitud correcta</translation>
<translation id="2728127805433021124">El certificado del servidor está firmado con un algoritmo de firma no seguro.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Ejecutar Diagnóstico de conectividad<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Puedes inhabilitar los servidores proxy configurados para una conexión en la página de configuración.</translation>
<translation id="2955913368246107853">Cerrar la barra de búsqueda</translation>
<translation id="2958431318199492670">La configuración de red no cumple el estándar ONC. Es posible que no se importen partes de la configuración.</translation>
+<translation id="29611076221683977">Los atacantes que se encuentran actualmente en el sitio <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podrían intentar instalar programas peligrosos en tu Mac para robar o eliminar tu información (por ejemplo, fotos, contraseñas, mensajes y tarjetas de crédito).</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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Ocultar detalles</translation>
<translation id="3587482841069643663">Todo</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">El servidor no admite la modificación de renegociación de seguridad de la capa de transporte.</translation>
<translation id="36224234498066874">Borrar datos de navegación...</translation>
<translation id="362276910939193118">Mostrar historial completo</translation>
<translation id="3623476034248543066">Mostrar valor</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Licencias agotadas</translation>
<translation id="3714780639079136834">Activar los datos móviles o la conexión Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Comprobar el proxy, el cortafuegos y la configuración de DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Si entiendes los riesgos para tu seguridad, puedes <ph name="BEGIN_LINK" />acceder a este sitio no seguro<ph name="END_LINK" /> antes de que se hayan eliminado los programas peligrosos.</translation>
<translation id="3739623965217189342">Enlace copiado</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="3788090790273268753">El certificado de este sitio web vence en 2016 y la cadena de certificados incluye un certificado firmado con SHA-1.</translation>
<translation id="382518646247711829">Si utilizas un servidor proxy...</translation>
<translation id="3828924085048779000">La frase de contraseña no puede estar vacía.</translation>
<translation id="3845539888601087042">Mostrando historial de dispositivos en los que has iniciado sesión. <ph name="BEGIN_LINK" />Más información<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Clave "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">El cliente y el servidor no son compatibles con la misma versión de protocolo SSL o de cifrado.</translation>
<translation id="4079302484614802869">Se ha configurado el proxy para que use una URL de secuencia de comandos .pac, en lugar de servidores proxy fijos.</translation>
+<translation id="4098354747657067197">El sitio web al que vas a acceder es engañoso</translation>
<translation id="4103249731201008433">El número de serie del dispositivo no es válido.</translation>
<translation id="4103763322291513355">Accede a la página &lt;strong&gt;chrome://policy&lt;/strong&gt; para ver la lista de URLs no admitidas y otras políticas establecidas por el administrador del sistema.</translation>
<translation id="4110615724604346410">Este servidor no ha podido demostrar que es <ph name="DOMAIN" />; su certificado de seguridad contiene errores. Este problema puede deberse a una configuración incorrecta o a que un atacante ha interceptado la conexión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ninguna}=1{Una aplicación ($1)}=2{Dos aplicaciones ($1, $2)}other{# aplicaciones ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">La página no responde o se cierra</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Prueba a ejecutar Diagnósticos de red<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Tu conexión con este sitio web no es completamente segura</translation>
<translation id="4250680216510889253">No</translation>
<translation id="425582637250725228">Es posible que los cambios no se guarden.</translation>
<translation id="4258748452823770588">Firma errónea</translation>
<translation id="4269787794583293679">(Ningún nombre de usuario)</translation>
+<translation id="4280429058323657511">Vcto. <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">La función de navegación segura de Google <ph name="BEGIN_LINK" />encontró programas dañinos<ph name="END_LINK" /> recientemente en <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">Sugerencias de padres</translation>
<translation id="4304224509867189079">Iniciar sesión</translation>
<translation id="432290197980158659">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. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">Error al buscar el artículo</translation>
+<translation id="4326324639298822553">Consulta la fecha de vencimiento y vuelve a intentarlo</translation>
<translation id="4331708818696583467">No es seguro</translation>
+<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="4381091992796011497">Nombre de usuario:</translation>
<translation id="4394049700291259645">Inhabilitar</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Has intentado acceder a <ph name="DOMAIN" />, pero el servidor ha presentado un certificado no válido. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></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="4619615317237390068">Pestañas de otros dispositivos</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Estas viendo la página de una extensión.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Volver a cargar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
<translation id="4744603770635761495">Ruta del ejecutable</translation>
+<translation id="4750917950439032686">Tu información (por ejemplo, las contraseñas o los números de las tarjetas de crédito) es privada cuando se envía a este sitio web.</translation>
<translation id="4756388243121344051">&amp;Historial</translation>
+<translation id="4759118997339041434">Opción de autocompletar pagos inhabilitada</translation>
<translation id="4764776831041365478">Es posible que la página web <ph name="URL" /> esté temporalmente inactiva o que se haya trasladado definitivamente a otra dirección.</translation>
<translation id="4771973620359291008">Se ha producido un error desconocido.</translation>
<translation id="4800132727771399293">Comprueba la fecha de caducidad y el código CVC, y vuelve a intentarlo</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Error de red</translation>
<translation id="4816492930507672669">Ajustar a página</translation>
<translation id="4850886885716139402">Ver</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> y <ph name="TYPE_3" /></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="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="495170559598752135">Acciones</translation>
<translation id="4958444002117714549">Expandir lista</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Descargando</translation>
<translation id="5190835502935405962">Barra de marcadores</translation>
<translation id="5199729219167945352">Experimentos</translation>
-<translation id="5199841536747119669">Las sugerencias aparecen aquí</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="5299298092464848405">Error al analizar la política</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Notificación de fallos inhabilitada</translation>
<translation id="5317780077021120954">Guardar</translation>
<translation id="5327248766486351172">Nombre</translation>
+<translation id="5337705430875057403">Los atacantes del <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podrían engañarte para que hagas algo peligroso, como instalar software o revelar tu información personal (por ejemplo, contraseñas, números de teléfono o datos de tarjetas de crédito).</translation>
<translation id="5359637492792381994">Este servidor no ha podido demostrar que es <ph name="DOMAIN" />; su certificado de seguridad no es válido en este momento. Este problema puede deberse a una configuración incorrecta o a que un atacante ha interceptado la conexión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">Error al almacenar la configuración de la política</translation>
+<translation id="5386426401304769735">La cadena de certificados de este sitio web contiene un certificado firmado con SHA-1.</translation>
<translation id="5421136146218899937">Borrar datos de navegación...</translation>
<translation id="5430298929874300616">Eliminar marcador</translation>
<translation id="5431657950005405462">No se ha encontrado tu archivo</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Una página insertada en <ph name="SITE" /> dice:</translation>
<translation id="5556459405103347317">Cargar de nuevo</translation>
<translation id="5565735124758917034">Activo</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>
+<translation id="5580958916614886209">Consulta el mes de vencimiento y vuelve a intentarlo</translation>
<translation id="560412284261940334">Administración no admitida</translation>
<translation id="5610142619324316209">Comprobar la conexión</translation>
<translation id="5617949217645503996">La página <ph name="HOST_NAME" /> te ha redirigido demasiadas veces.</translation>
<translation id="5622887735448669177">¿Quieres salir de este sitio web?</translation>
<translation id="5629630648637658800">Error al cargar la configuración de la política</translation>
<translation id="5631439013527180824">Token de administración de dispositivos no válido</translation>
+<translation id="5669703222995421982">Obtener contenido personalizado</translation>
+<translation id="5675650730144413517">Esta página no funciona</translation>
<translation id="5677928146339483299">Con bloqueo</translation>
<translation id="5694783966845939798">Has intentado acceder a <ph name="DOMAIN" />, pero el servidor ha presentado un certificado firmado con un algoritmo de firma no seguro (por ejemplo, SHA-1). Una posible causa de este problema es que se hayan falsificado las credenciales de seguridad presentadas por el servidor y que hayas accedido a la página de un atacante en lugar de establecer conexión con el servidor en cuestión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">No se ha verificado la identidad de este sitio web.</translation>
<translation id="5720705177508910913">Usuario actual</translation>
-<translation id="572328651809341494">Pestañas recientes</translation>
<translation id="5732392974455271431">Tus padres pueden desbloquearlo</translation>
<translation id="5784606427469807560">Se ha producido un problema al confirmar tu tarjeta. Comprueba tu conexión a Internet y vuelve a intentarlo.</translation>
<translation id="5785756445106461925">Además, esta página incluye otros recursos que no son seguros. Otros usuarios pueden acceder a estos recursos mientras están en circulación y un atacante puede modificarlos para cambiar el aspecto de la página.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Has intentado acceder a <ph name="DOMAIN" />, pero el servidor ha presentado un certificado que contiene una clave no segura. Es posible que alguien haya descifrado la clave privada y que hayas accedido a la página de esa persona en lugar de establecer conexión con el servidor. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">Nueva carpeta</translation>
<translation id="5869405914158311789">No se puede acceder a este sitio web</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Google Payments no admite este tipo de tarjeta para este comerciante. Selecciona otra.</translation>
<translation id="59174027418879706">Habilitada</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="5966707198760109579">Semana</translation>
<translation id="5967867314010545767">Eliminar del historial</translation>
<translation id="5975083100439434680">Reducir</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Prueba a:</translation>
<translation id="6151417162996330722">El certificado del servidor tiene un período de validez demasiado largo.</translation>
<translation id="6165508094623778733">Más información</translation>
+<translation id="6177128806592000436">Tu conexión con este sitio web no es segura</translation>
<translation id="6203231073485539293">Comprueba tu conexión a Internet</translation>
<translation id="6218753634732582820">¿Quitar dirección de Chromium?</translation>
+<translation id="6251924700383757765">Política de Privacidad</translation>
+<translation id="625755898061068298">Has elegido inhabilitar las advertencias de seguridad de este sitio web.</translation>
<translation id="6259156558325130047">&amp;Rehacer reorganización</translation>
<translation id="6263376278284652872">Marcadores de <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Volver para estar a salvo</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">No se puede acceder a <ph name="URL" />.</translation>
<translation id="6321917430147971392">Comprueba tu configuración de DNS</translation>
<translation id="6328639280570009161">Prueba a inhabilitar la predicción de red</translation>
+<translation id="6328786501058569169">Este sitio web es engañoso</translation>
<translation id="6337534724793800597">Filtrar políticas por nombre</translation>
<translation id="6342069812937806050">Ahora</translation>
<translation id="6345221851280129312">tamaño desconocido</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">No puedes acceder a <ph name="SITE" /> en este momento porque el sitio web ha enviado credenciales codificadas que Chromium no puede procesar. Los ataques y los errores de red suelen ser temporales, por lo que es probable que esta página funcione más tarde. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">No se ha podido comprobar si se ha revocado el certificado.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Se ha ignorado el valor porque se ha establecido una política que inhabilita la búsqueda predeterminada.</translation>
<translation id="6462969404041126431">Este servidor no ha podido demostrar que es <ph name="DOMAIN" />; es posible que su certificado de seguridad se revoque. Este problema puede deberse a una configuración incorrecta o a que un atacante ha interceptado la conexión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<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="6529602333819889595">&amp;Rehacer eliminación</translation>
<translation id="6534179046333460208">Sugerencias de la Web física</translation>
<translation id="6550675742724504774">Configuración</translation>
+<translation id="6556239504065605927">Conexión segura</translation>
<translation id="6563469144985748109">Tu administrador aún no la ha aprobado</translation>
<translation id="6593753688552673085">menos de <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opciones de cifrado</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Esta política está obsoleta.</translation>
<translation id="6652240803263749613">Este servidor no ha podido demostrar que es <ph name="DOMAIN" />; el sistema operativo de tu ordenador no confía en su certificado de seguridad. Este problema puede deberse a una configuración incorrecta o a que un atacante ha interceptado la conexión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">Editar carpeta</translation>
-<translation id="6660210980321319655">Notificado automáticamente el <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Valor de la política</translation>
<translation id="6757797048963528358">El dispositivo se ha suspendido.</translation>
<translation id="6778737459546443941">Uno de tus padres aún no lo ha aprobado</translation>
+<translation id="6810899417690483278">ID de personalización</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">La página web de la URL <ph name="URL" /> no está disponible en este momento. Es posible que esté sobrecargada o inactiva debido a tareas de mantenimiento.</translation>
<translation id="6831043979455480757">Traducir</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Información de contacto</translation>
+<translation id="7079718277001814089">Este sitio web contiene software malicioso</translation>
<translation id="7087282848513945231">Condado</translation>
<translation id="7088615885725309056">Más antiguos</translation>
<translation id="7090678807593890770">Busca <ph name="LINK" /> en Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423">La página <ph name="HOST_NAME" /> no cumple los estándares de seguridad.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Más información<ph name="END_LINK" /> sobre este problema.</translation>
<translation id="7219179957768738017">La conexión utiliza <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">El sitio al que vas a acceder contiene software malicioso</translation>
<translation id="724975217298816891">Introduce la fecha de caducidad y el código CVC de la tarjeta <ph name="CREDIT_CARD" /> para actualizar sus detalles. Cuando la confirmes, su información se compartirá con este sitio web.</translation>
<translation id="725866823122871198">No se puede establecer una conexión privada con <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> porque la fecha y la hora de tu ordenador (<ph name="DATE_AND_TIME" />) no son correctas.</translation>
<translation id="7269802741830436641">Esta página web tiene un bucle de redireccionamiento</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">Cancelar</translation>
<translation id="7667346355482952095">El token de política devuelto está vacío o no coincide con el token actual</translation>
<translation id="7668654391829183341">Dispositivo desconocido</translation>
+<translation id="7669271284792375604">Es posible que los atacantes que se encuentren en este sitio web intenten engañarte para que instales programas que empeoren tu experiencia de navegación (por ejemplo, que cambien tu página principal o muestren anuncios adicionales en los sitios web a los que accedas).</translation>
<translation id="7674629440242451245">Si estás interesado en probar nuevas e interesantes funciones de Chrome, prueba el canal para desarrolladores en la página chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Acceder a <ph name="SITE" /> (sitio no seguro)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">No se puede cargar este sitio web desde la caché</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">La conexión se ha encriptado mediante <ph name="CIPHER" />, con <ph name="MAC" /> para la autenticación del mensaje y con <ph name="KX" /> como mecanismo de intercambio de claves.</translation>
<translation id="780301667611848630">No, gracias</translation>
<translation id="7805768142964895445">Estado</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">¿Eliminar sugerencia de formulario de Chrome?</translation>
<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="7887683347370398519">Comprueba el código CVC y vuelve a intentarlo</translation>
<translation id="7912024687060120840">En la carpeta:</translation>
<translation id="7935318582918952113">Extractor de DOM</translation>
<translation id="7938958445268990899">Aún no es válido el certificado de servidor.</translation>
<translation id="7942349550061667556">Rojo</translation>
+<translation id="7947285636476623132">Consulta el año de vencimiento y vuelve a intentarlo</translation>
<translation id="7951415247503192394">(32 bits)</translation>
<translation id="7956713633345437162">Marcadores del móvil</translation>
<translation id="7961015016161918242">Nunca</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">Traducir siempre del <ph name="ORIGINAL_LANGUAGE" /> al <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Sin especificar</translation>
<translation id="8012647001091218357">No hemos podido contactar con tus padres. Vuelve a intentarlo.</translation>
+<translation id="8025119109950072390">Es posible que los atacantes que se encuentren en este sitio web 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).</translation>
+<translation id="803030522067524905">La función de navegación segura de Google detectó phishing recientemente en <ph name="SITE" />. Los sitios web de phishing imitan el aspecto de otros sitios web para engañarte. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">Esta página está escrita en <ph name="SOURCE_LANGUAGE" />. ¿Quieres traducirla al <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Enviar</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>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719">El archivo que se encuentra en <ph name="URL" /> no se puede leer. Puede que se haya eliminado o movido o que los permisos del archivo impidan acceder a él.</translation>
<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="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="8230421197304563332">Los atacantes que se encuentran actualmente en el sitio <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podrían intentar 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="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="8249320324621329438">Última comprobación:</translation>
<translation id="8261506727792406068">Eliminar</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">Origen</translation>
<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="8349305172487531364">Barra de marcadores</translation>
<translation id="8363502534493474904">Desactivar el modo avión</translation>
<translation id="8364627913115013041">No establecida</translation>
<translation id="8380941800586852976">Peligroso</translation>
<translation id="8382348898565613901">Los marcadores a los que hayas accedido recientemente aparecen aquí</translation>
+<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="8428213095426709021">Ajustes</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ha tardado demasiado tiempo en responder.</translation>
<translation id="852346902619691059">Este servidor no ha podido demostrar que 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 ha interceptado la conexión. <ph name="BEGIN_LEARN_MORE_LINK" />Más información<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Google Payments no admite este tipo de tarjeta. Selecciona otra.</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="8553075262323480129">La traducción no se ha realizado correctamente porque no se ha podido determinar el idioma de la página.</translation>
<translation id="8559762987265718583">No se puede establecer una conexión privada con <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> porque la fecha y la hora de tu dispositivo (<ph name="DATE_AND_TIME" />) no son correctas.</translation>
-<translation id="856992080682148">El certificado de este sitio web vence en 2017 o más adelante, y la cadena de certificados incluye un certificado firmado con SHA-1.</translation>
<translation id="8571890674111243710">Traduciendo página a <ph name="LANGUAGE" />...</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>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522">No se ha podido encontrar la &lt;abbr id="dnsDefinition"&gt;dirección DNS&lt;/abbr&gt; de la página <ph name="HOST_NAME" />. Se está diagnosticando el problema.</translation>
<translation id="8790007591277257123">&amp;Rehacer eliminación</translation>
<translation id="8798099450830957504">Predeterminado</translation>
+<translation id="8800988563907321413">Las sugerencias de la sección Cercanas aparecen aquí</translation>
<translation id="8804164990146287819">Política de Privacidad</translation>
<translation id="8820817407110198400">Marcadores</translation>
<translation id="8834246243508017242">Habilitar Autocompletar usando contactos…</translation>
<translation id="883848425547221593">Otros marcadores</translation>
+<translation id="884264119367021077">Dirección de envío</translation>
<translation id="884923133447025588">No se ha encontrado ningún mecanismo de revocación.</translation>
<translation id="885730110891505394">Compartir con Google</translation>
<translation id="8866481888320382733">Error al analizar la configuración de la política</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">El certificado del servidor ha caducado.</translation>
<translation id="8987927404178983737">Mes</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">El sitio al que vas a acceder contiene programas dañinos</translation>
<translation id="9001074447101275817">El proxy <ph name="DOMAIN" /> requiere un nombre de usuario y una contraseña.</translation>
<translation id="901974403500617787">Las opciones que se aplican a todo el sistema solo las puede establecer el propietario: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Esta página se ha traducido al <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Error de seguridad</translation>
<translation id="9038649477754266430">Utilizar un servicio de predicciones para que las páginas se carguen más rápido</translation>
<translation id="9039213469156557790">Además, esta página incluye otros recursos que no son seguros. Otros usuarios pueden acceder a estos recursos mientras están en circulación y un atacante puede modificarlos para cambiar el comportamiento de la página.</translation>
+<translation id="9040185888511745258">Los atacantes del sitio <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podrían intentar engañarte para que instales programas que afecten a tu navegación (por ejemplo, que cambien tu página principal o muestren anuncios adicionales en los sitios a los que accedas).</translation>
<translation id="9050666287014529139">Frase de contraseña</translation>
<translation id="9065203028668620118">Editar</translation>
<translation id="9068849894565669697">Seleccionar color</translation>
<translation id="9076283476770535406">Es posible que incluya contenido para adultos</translation>
-<translation id="9092364396508701805">La página <ph name="HOST_NAME" /> no funciona</translation>
<translation id="9103872766612412690"><ph name="SITE" /> utiliza normalmente el cifrado para proteger tu información. Cuando Chromium intentó establecer conexión con <ph name="SITE" />, el sitio web devolvió unas credenciales inusuales e incorrectas. Esto puede ocurrir si un atacante intenta suplantar la identidad de <ph name="SITE" /> o si una pantalla de inicio de sesión Wi-Fi interrumpe la conexión. Tu información sigue estando protegida, ya que Chromium detuvo la conexión antes de que se intercambiaran datos.</translation>
<translation id="9137013805542155359">Mostrar original</translation>
+<translation id="9137248913990643158">Abre Chrome e inicia sesión en el navegador para usar esta aplicación.</translation>
<translation id="9148507642005240123">&amp;Deshacer edición</translation>
<translation id="9157595877708044936">Configurando...</translation>
<translation id="9170848237812810038">&amp;Deshacer</translation>
@@ -739,7 +792,6 @@
<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="962701380617707048">Introduce la fecha de caducidad y el código CVC de la tarjeta <ph name="CREDIT_CARD" /> para actualizar su información</translation>
<translation id="969892804517981540">Build oficial</translation>
<translation id="988159990683914416">Build para desarrolladores</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 9099f2dc1ef..f299002f523 100644
--- a/chromium/components/strings/components_strings_et.xtb
+++ b/chromium/components/strings/components_strings_et.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Ãœhendage uuesti WiFi-ga</translation>
<translation id="1175364870820465910">&amp;Prindi...</translation>
<translation id="1181037720776840403">Eemalda</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Teavita Google'it automaatselt<ph name="END_WHITEPAPER_LINK" /> võimalikest turvaintsidentidest. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Edasi</translation>
<translation id="1201895884277373915">Veel sellelt saidilt</translation>
<translation id="1206967143813997005">Sobimatu algne allkiri</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Tsüaan</translation>
<translation id="1629803312968146339">Kas soovite, et Chrome salvestaks selle kaardi?</translation>
<translation id="1640180200866533862">Kasutajareeglid</translation>
+<translation id="1640244768702815859">Proovige <ph name="BEGIN_LINK" />külastada saidi avalehte<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Võrgu seadistus on sobimatu ja seda ei saa importida.</translation>
<translation id="1644574205037202324">Ajalugu</translation>
<translation id="1645368109819982629">Toetuseta protokoll</translation>
<translation id="1676269943528358898">Sait <ph name="SITE" /> kasutab teie teabe kaitsmiseks tavaliselt krüpteerimist. Kui Google Chrome püüdis seekord saidiga <ph name="SITE" /> ühendust luua, tagastas veebisait ebatavalised ja valed mandaadid. See võib juhtuda siis, kui ründaja proovib teeselda, et on sait <ph name="SITE" />, või WiFi sisselogimisekraan on ühenduse katkestanud. Teie teave on endiselt kaitstud, sest Google Chrome peatas ühenduse enne andmevahetust.</translation>
+<translation id="168328519870909584">Saidil <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> olevad ründajad võivad proovida installida teie seadmesse ohtlikke rakendusi, mis varastavad teie teavet või kustutavad selle (nt fotod, paroolid, sõnumid ja krediitkaardiandmed).</translation>
<translation id="168841957122794586">Serveri sertifikaat sisaldab nõrka krüptograafilist võtit.</translation>
-<translation id="1701955595840307032">Soovitatud sisu hankimine</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Vajate saidi külastamiseks halduri <ph name="NAME" /> luba</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Lk</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Proovige käitada Windowsi võrgudiagnostika tööriista<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Värskendage sünkroonimise parooli.</translation>
+<translation id="1787142507584202372">Teie avatud vahelehed kuvatakse siin</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google'i ohutu sirvimine avastas saidilt <ph name="SITE" /> hiljuti <ph name="BEGIN_LINK" />pahavara<ph name="END_LINK" />. Tavaliselt ohutud veebisaidid nakatuvad mõnikord pahavaraga. Pahatahtliku sisu allikas on tuntud pahavaralevitaja <ph name="SUBRESOURCE_HOST" />. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Taotlus või selle parameetrid on kehtetud</translation>
+<translation id="1834321415901700177">See sait sisaldab kahjulikke programme</translation>
<translation id="1838667051080421715">Vaatate veebilehe lähtekoodi.</translation>
<translation id="1871208020102129563">Puhverserver on seatud kasutama fikseeritud puhverservereid, mitte pac-skripti URL-i.</translation>
<translation id="1883255238294161206">Ahenda loend</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Mobiili järjehoidjad</translation>
<translation id="2148716181193084225">Täna</translation>
-<translation id="2149973817440762519">Muuda järjehoidjat</translation>
<translation id="2154054054215849342">Sünkroonimisteenus pole domeeni jaoks saadaval</translation>
<translation id="2166049586286450108">Täielik administraatorijuurdepääs</translation>
<translation id="2166378884831602661">See sait ei saa turvalist ühendust luua</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Te ei saa saiti <ph name="SITE" /> praegu külastada, sest veebisait kasutab HSTS-i. Võrguvead ja -rünnakud on tavaliselt ajutised, nii et leht on tõenäoliselt hiljem töökorras. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Ignoreeriti vale järjehoidjat registris <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Muud järjehoidjad</translation>
+<translation id="2355395290879513365">Ründajad võivad näha pilte, mida sellel saidil vaatate, ja teid neid pilte muutes petta.</translation>
<translation id="2359808026110333948">Jätka</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> talletatud krahhiaruannet ei laaditud üles</translation>
<translation id="2367567093518048410">Tase</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Kuva reeglid, mille väärtusi pole määratud</translation>
<translation id="2396249848217231973">&amp;Võta kustutamine tagasi</translation>
<translation id="2455981314101692989">See veebileht on keelanud automaatse täitmise selle vormi puhul.</translation>
+<translation id="2460160116472764928">Google'i ohutu sirvimine avastas saidilt <ph name="SITE" /> hiljuti <ph name="BEGIN_LINK" />pahavara<ph name="END_LINK" />. Tavaliselt ohutud veebisaidid nakatuvad mõnikord pahavaraga. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Täida</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Võrgudiagnostika käitamine<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Kehtetu otsingu URL.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Olete kindel, et soovite need leheküljed oma ajaloost kustutada?</translation>
<translation id="2677748264148917807">Lahku</translation>
<translation id="269990154133806163">Server esitas sertifikaadi, mida ei ole sertifikaadi läbipaistvuse reegli kohaselt avalikustatud. See on teatud sertifikaatide puhul nõutav, et need oleksid usaldusväärsed ja kaitseksid ründajate eest. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Lugemisloend</translation>
<translation id="2704283930420550640">Väärtus ei vasta vormingule.</translation>
<translation id="2704951214193499422">Chromium ei saanud praegu teie kaarti kinnitada. Proovige hiljem uuesti.</translation>
<translation id="2705137772291741111">Selle saidi (vahemällu) salvestatud koopia oli loetamatu.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Püüdsite jõuda domeenile <ph name="DOMAIN" />, kuid sertifikaadi väljaandja on serveri esitatud sertifikaadi tühistanud. See tähendab, et serveri esitatud turvamandaate ei tohi mingil juhul usaldada. Võimalik, et suhtlete ründajaga. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Küsi luba</translation>
<translation id="2713444072780614174">Valge</translation>
+<translation id="2720342946869265578">Läheduses</translation>
<translation id="2721148159707890343">Taotlus õnnestus</translation>
<translation id="2728127805433021124">Serveri sertifikaat on allkirjastatud nõrga allkirjaalgoritmiga.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Ühenduvusdiagnostika käitamine<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Seadete lehel saate keelata kõik ühenduse jaoks konfigureeritud puhverserverid.</translation>
<translation id="2955913368246107853">Sule leiuriba</translation>
<translation id="2958431318199492670">Võrgu seadistus ei vasta ONC standardile. On võimalik, et seadistuse mõnd osa ei saa importida.</translation>
+<translation id="29611076221683977">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 krediitkaardiandmed).</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="2985306909656435243">Kui see on lubatud, salvestab Chromium teie kaardi koopia vormide kiiremaks täitmiseks sellesse seadmesse.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Peida üksikasjad</translation>
<translation id="3587482841069643663">Kõik</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Server ei toeta TLS-i taasläbirääkimise laiendust.</translation>
<translation id="36224234498066874">Kustuta sirvimise andmed...</translation>
<translation id="362276910939193118">Näita kogu ajalugu</translation>
<translation id="3623476034248543066">Kuva väärtused</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Litsentsid on ammendunud</translation>
<translation id="3714780639079136834">Lülitage sisse mobiilne andmeside või WiFi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Kontrollige puhverserveri, tulemüüri ja DNS-i seadistust<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Kui mõistate, kuidas teie turvalisust ohustatakse, siis võite <ph name="BEGIN_LINK" />seda ebaturvalist saiti külastada<ph name="END_LINK" /> enne, kui ohtlikud programmid on eemaldatud.</translation>
<translation id="3739623965217189342">Teie kopeeritud 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="3788090790273268753">Selle saidi sertifikaat aegub 2016. aastal ja sertifikaadiahel sisaldab sertifikaati, mis on allkirjastatud SHA-1-ga.</translation>
<translation id="382518646247711829">Kui kasutate puhverserverit ...</translation>
<translation id="3828924085048779000">Tühi parool ei ole lubatud.</translation>
<translation id="3845539888601087042">Kuvatakse ajalugu teie sisselogitud seadmetest. <ph name="BEGIN_LINK" />Lisateave<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Võti „<ph name="SUBKEY" />â€: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Klient ja server ei toeta tavapärast SSL-protokolli versiooni ega šifreerimiskomplekti.</translation>
<translation id="4079302484614802869">Puhverserveri konfigureerimine on määratud kasutama pac-skripti URL-i, mitte fikseeritud puhverservereid.</translation>
+<translation id="4098354747657067197">Avatav sait on petturlik</translation>
<translation id="4103249731201008433">Seadme seerianumber on kehtetu</translation>
<translation id="4103763322291513355">Külastage saiti &lt;strong&gt;chrome://policy&lt;/strong&gt;, et näha mustas nimekirjas olevate URL-ide loendit ja teisi reegleid, mille on jõustanud teie süsteemiadministraator.</translation>
<translation id="4110615724604346410">Server ei suutnud tõestada, et tegemist on domeeniga <ph name="DOMAIN" />. Selle turvasertifikaat sisaldab vigu. Selle põhjuseks võib olla vale seadistus või teie ühendust segav ründaja. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{mitte ühtegi}=1{1 rakendus ($1)}=2{2 rakendust ($1, $2)}other{# rakendust ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Krahhid</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Proovige käitada võrgudiagnostikat<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Teie ühendus selle saidiga pole täielikult turvaline</translation>
<translation id="4250680216510889253">Ei</translation>
<translation id="425582637250725228">Tehtud muudatusi ei pruugita salvestada.</translation>
<translation id="4258748452823770588">Sobimatu allkiri</translation>
<translation id="4269787794583293679">(Kasutajanimi puudub)</translation>
+<translation id="4280429058323657511">, aegub <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google'i ohutu sirvimine leidis saidilt <ph name="SITE" /> hiljuti <ph name="BEGIN_LINK" />kahjulikke programme<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Vanema soovitused</translation>
<translation id="4304224509867189079">Logi sisse</translation>
<translation id="432290197980158659">Server esitas sertifikaadi, mis ei kattu sisseehitatud ootustega. Ootusi rakendatakse teatud väga turvaliste veebisaitide puhul teie kaitsmiseks. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Artiklit ei leitud</translation>
+<translation id="4326324639298822553">Kontrollige aegumiskuupäeva ja proovige uuesti</translation>
<translation id="4331708818696583467">Pole turvaline</translation>
+<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="4381091992796011497">Kasutajanimi:</translation>
<translation id="4394049700291259645">Keela</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Püüdsite jõuda domeenile <ph name="DOMAIN" />, ent server esitas kehtetu sertifikaadi. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Muudest seadmetest pärinevad vahelehed</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Vaatate laienduste lehte.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Laadi reeglid uuesti</translation>
<translation id="4728558894243024398">Platvorm</translation>
<translation id="4744603770635761495">Täitmistee</translation>
+<translation id="4750917950439032686">Teie teave (nt paroolid või krediitkaardi numbrid) on sellele saidile saates privaatne.</translation>
<translation id="4756388243121344051">&amp;Ajalugu</translation>
+<translation id="4759118997339041434">Makse automaatne täitmine on keelatud</translation>
<translation id="4764776831041365478">Veebileht aadressil <ph name="URL" /> võib olla ajutiselt maas või jäädavalt uuele veebiaadressile teisaldatud.</translation>
<translation id="4771973620359291008">Tekkis tundmatu viga.</translation>
<translation id="4800132727771399293">Kontrollige aegumiskuupäeva ja CVC-d ning proovige uuesti</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Võrgu viga</translation>
<translation id="4816492930507672669">Sobita lehele</translation>
<translation id="4850886885716139402">Kuva</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ja veel 1 veebileht}other{ja veel # veebilehte}}</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="495170559598752135">Toimingud</translation>
<translation id="4958444002117714549">Laienda loendit</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Allalaadimine</translation>
<translation id="5190835502935405962">Järjehoidjariba</translation>
<translation id="5199729219167945352">Katsed</translation>
-<translation id="5199841536747119669">Teie soovitused kuvatakse siin</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="5299298092464848405">Reegli sõelumisel ilmnes viga</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Krahhide aruandlus on keelatud.</translation>
<translation id="5317780077021120954">Salvesta</translation>
<translation id="5327248766486351172">Nimi</translation>
+<translation id="5337705430875057403">Ründajad saidil <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> võivad teid meelitada ohtlikele tegevustele, nagu tarkvara installimine või isiklike andmete avaldamine (nt paroolid, telefoninumbrid või krediitkaardid).</translation>
<translation id="5359637492792381994">Server ei suutnud tõestada, et tegemist on domeeniga <ph name="DOMAIN" />. Selle turvasertifikaat on praegu kehtetu. Selle põhjuseks võib olla vale seadistus või teie ühendust segav ründaja. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Reegli seadete talletamine ebaõnnestus</translation>
+<translation id="5386426401304769735">Selle saidi sertifikaadiahel sisaldab sertifikaati, mis on allkirjastatud SHA-1-ga.</translation>
<translation id="5421136146218899937">Sirvimisandmete kustutamine ...</translation>
<translation id="5430298929874300616">Järjehoidja eemaldamine</translation>
<translation id="5431657950005405462">Teie faili ei leitud</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Manustatud leht saidil <ph name="SITE" /> ütleb:</translation>
<translation id="5556459405103347317">Laadi uuesti</translation>
<translation id="5565735124758917034">Aktiivne</translation>
+<translation id="5572851009514199876">Alustage ja logige Chrome'i sisse, et Chrome saaks kontrollida, kas teil on luba sellele saidile juurdepääsemiseks.</translation>
+<translation id="5580958916614886209">Kontrollige aegumiskuud ja proovige uuesti</translation>
<translation id="560412284261940334">Haldust ei toetata</translation>
<translation id="5610142619324316209">Kontrollige ühendust</translation>
<translation id="5617949217645503996">Host <ph name="HOST_NAME" /> suunas teid liiga mitu korda ümber.</translation>
<translation id="5622887735448669177">Kas soovite sellelt saidilt lahkuda?</translation>
<translation id="5629630648637658800">Reegli seadete laadimine ebaõnnestus</translation>
<translation id="5631439013527180824">Seadme halduse luba on kehtetu</translation>
+<translation id="5669703222995421982">Isikupärastatud sisu hankimine</translation>
+<translation id="5675650730144413517">See leht ei tööta</translation>
<translation id="5677928146339483299">Blokeeritud</translation>
<translation id="5694783966845939798">Püüdsite jõuda domeenile <ph name="DOMAIN" />, kuid server esitas sertifikaadi, mis on allkirjastatud nõrga allkirjaalgoritmiga (nt SHA-1). See tähendab, et serveri esitatud turvamandaadid võivad olla võltsitud ja tegemist ei pruugi olla õige serveriga (võimalik, et suhtlete ründajaga). <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Selle veebisaidi identiteeti pole kinnitanud.</translation>
<translation id="5720705177508910913">Praegune kasutaja</translation>
-<translation id="572328651809341494">Hiljutised vahelehed</translation>
<translation id="5732392974455271431">Vanemad saavad blokeeringu teie eest tühistada</translation>
<translation id="5784606427469807560">Kaardi kinnitamisel tekkis probleem. Kontrollige Interneti-ühendust ja proovige uuesti.</translation>
<translation id="5785756445106461925">Lisaks sisaldab see leht teisi ressursse, mis pole turvalised. Edastamise ajal võivad ressursse vaadata ka teised ja ründajad saavad lehe välimuse muutmiseks ressursse muuta.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Proovisite jõuda domeenile <ph name="DOMAIN" />, kuid server esitas nõrka võtit sisaldava sertifikaadi. Võimalik, et ründaja on privaatvõtme lahti murdnud ja tegemist ei pruugi olla oodatud serveriga (võib-olla suhtlete ründajaga). <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Uus kaust</translation>
<translation id="5869405914158311789">Selle saidiga ei saa ühendust</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Google Payments ei toeta selle kaupmehe puhul seda tüüpi kaarti. Valige teine kaart.</translation>
<translation id="59174027418879706">Lubatud</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="5966707198760109579">Nädal</translation>
<translation id="5967867314010545767">Eemalda ajaloost</translation>
<translation id="5975083100439434680">Suumib välja</translation>
@@ -461,7 +487,7 @@
<translation id="6008256403891681546">JCB</translation>
<translation id="6016158022840135739">{COUNT,plural, =1{Lk 1}other{Lk #}}</translation>
<translation id="6017514345406065928">Roheline</translation>
-<translation id="6040143037577758943">Sule</translation>
+<translation id="6040143037577758943">Sulge</translation>
<translation id="604124094241169006">Automaatne</translation>
<translation id="6042308850641462728">Rohkem</translation>
<translation id="6060685159320643512">Ettevaatust, need katsed võivad hammustada.</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Proovige järgmist.</translation>
<translation id="6151417162996330722">Serveri sertifikaadi kehtivusaeg on liiga pikk.</translation>
<translation id="6165508094623778733">Lisateave</translation>
+<translation id="6177128806592000436">Teie ühendus selle saidiga pole turvaline</translation>
<translation id="6203231073485539293">Kontrollige Interneti-ühendust</translation>
<translation id="6218753634732582820">Kas eemaldada Chromiumist aadress?</translation>
+<translation id="6251924700383757765">Privaatsuseeskirjad</translation>
+<translation id="625755898061068298">Otsustasite turvahoiatused selle saidi puhul keelata.</translation>
<translation id="6259156558325130047">&amp;Korrasta uuesti</translation>
<translation id="6263376278284652872">Domeeni <ph name="DOMAIN" /> järjehoidjad</translation>
<translation id="6264485186158353794">Tagasi turvalisusse</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">URL-iga <ph name="URL" /> ei saa ühendust.</translation>
<translation id="6321917430147971392">Kontrollige DNS-i seadeid</translation>
<translation id="6328639280570009161">Proovige keelata võrguprognoos</translation>
+<translation id="6328786501058569169">See sait on petturlik</translation>
<translation id="6337534724793800597">Reeglite filtreerimine nime järgi</translation>
<translation id="6342069812937806050">Äsja</translation>
<translation id="6345221851280129312">tundmatu suurus</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Te ei saa saiti <ph name="SITE" /> praegu külastada, sest veebisait saatis šifreeritud mandaadi, mida Chromium ei saa töödelda. Võrguvead ja -rünnakud on tavaliselt ajutised, nii et leht on tõenäoliselt hiljem töökorras. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Ei saa kontrollida sertifikaadi võimalikku tühistamist.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Seda eiratakse, kuna vaikeotsing on reegliga keelatud.</translation>
<translation id="6462969404041126431">Server ei suutnud tõestada, et tegemist on domeeniga <ph name="DOMAIN" />. Selle turvasertifikaat on võib-olla tühistatud. Selle põhjuseks võib olla vale seadistus või teie ühendust segav ründaja. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Kustuta uuesti</translation>
<translation id="6534179046333460208">Füüsilise veebi soovitused</translation>
<translation id="6550675742724504774">Valikud</translation>
+<translation id="6556239504065605927">Turvaline ühendus</translation>
<translation id="6563469144985748109">Haldur ei ole seda veel kinnitanud</translation>
<translation id="6593753688552673085">vähem kui <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Krüpteerimise valikud</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">See reegel on aegunud.</translation>
<translation id="6652240803263749613">Server ei suutnud tõestada, et tegemist on domeeniga <ph name="DOMAIN" />. Teie arvuti operatsioonisüsteem ei usalda selle turvasertifikaati. Selle põhjuseks võib olla vale seadistus või teie ühendust segav ründaja. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Kausta muutmine</translation>
-<translation id="6660210980321319655">Automaatne teavitamine: <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Reegli väärtus</translation>
<translation id="6757797048963528358">Teie seade lülitus unerežiimile.</translation>
<translation id="6778737459546443941">Vanem ei ole seda veel kinnitanud</translation>
+<translation id="6810899417690483278">Kohandamise ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Veebileht pole aadressil <ph name="URL" /> praegu saadaval. See võib olla üle koormatud või hooldamiseks seisatud.</translation>
<translation id="6831043979455480757">Tõlgi</translation>
@@ -532,7 +566,7 @@
<translation id="6897140037006041989">Kasutajaagent</translation>
<translation id="6915804003454593391">Kasutaja:</translation>
<translation id="6957887021205513506">Serveri sertifikaat näib olevat võltsing.</translation>
-<translation id="6965382102122355670">OK</translation>
+<translation id="6965382102122355670">Ok</translation>
<translation id="6965978654500191972">Seade</translation>
<translation id="6970216967273061347">Ringkond</translation>
<translation id="6973656660372572881">Määratud on nii fikseeritud puhverserverid kui ka pac-skriptiga URL.</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Kontaktteave</translation>
+<translation id="7079718277001814089">See sait sisaldab pahavara</translation>
<translation id="7087282848513945231">Maakond</translation>
<translation id="7088615885725309056">Vanemad</translation>
<translation id="7090678807593890770">Sisestage Google'isse otsing <ph name="LINK" /></translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423">Host <ph name="HOST_NAME" /> ei pea turvastandarditest kinni.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Lisateave<ph name="END_LINK" /> selle probleemi kohta.</translation>
<translation id="7219179957768738017">Ãœhendus kasutab protokolli <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Avatav veebisait sisaldab pahavara</translation>
<translation id="724975217298816891">Kaardi üksikasjade värskendamiseks sisestage krediitkaardi <ph name="CREDIT_CARD" /> aegumiskuupäev ja CVC. Kui selle kinnitate, jagatakse teie kaardi üksikasju selle saidiga.</translation>
<translation id="725866823122871198">Privaatset ühendust ei saa domeeniga <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> luua, kuna arvuti kuupäev ja kellaaeg (<ph name="DATE_AND_TIME" />) on valed.</translation>
<translation id="7269802741830436641">Sellel veebilehel on ümbersuunamissilmus</translation>
@@ -611,6 +648,7 @@ Inkognito režiim <ph name="SHORTCUT_KEY" /> võib järgmisel korral kasulikuks
<translation id="7658239707568436148">Tühista</translation>
<translation id="7667346355482952095">Tagastatud reegli luba on tühi või ei kattu praeguse loaga</translation>
<translation id="7668654391829183341">Tundmatu seade</translation>
+<translation id="7669271284792375604">Sellel saidil asuvad ründajad võivad proovida meelitada teid installima programme, mis kahjustavad sirvimiskogemust (nt muudavad avalehte või kuvavad külastatavatel saitidel lisareklaame).</translation>
<translation id="7674629440242451245">Kas olete huvitatud Chrome'i uutest lahedatest funktsioonidest? Proovige meie arenduskanalit aadressil chrome.com/beta.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Edasiliikumine saidile <ph name="SITE" /> (ebaturvaline)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Seda saiti ei saa vahemälust laadida</translation>
@@ -626,14 +664,17 @@ Inkognito režiim <ph name="SHORTCUT_KEY" /> võib järgmisel korral kasulikuks
<translation id="7800304661137206267">Ühenduse krüptimise moodus on <ph name="CIPHER" />, <ph name="MAC" /> sõnumite autentimiseks ja võtme vahetusmehhanism on <ph name="KX" />.</translation>
<translation id="780301667611848630">Ei, aitäh</translation>
<translation id="7805768142964895445">Olek</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Kas eemaldada Chrome'ist vormi soovitus?</translation>
<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="7887683347370398519">Kontrollige CVC-d ja proovige uuesti</translation>
<translation id="7912024687060120840">Kaustas:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Serveri sertifikaat ei kehti veel.</translation>
<translation id="7942349550061667556">Punane</translation>
+<translation id="7947285636476623132">Kontrollige aegumisaastat ja proovige uuesti</translation>
<translation id="7951415247503192394">(32-bitine)</translation>
<translation id="7956713633345437162">Mobiili järjehoidjad</translation>
<translation id="7961015016161918242">Mitte kunagi</translation>
@@ -641,7 +682,10 @@ Inkognito režiim <ph name="SHORTCUT_KEY" /> võib järgmisel korral kasulikuks
<translation id="7983301409776629893">Tõlgi alati keelest <ph name="ORIGINAL_LANGUAGE" /> keelde <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Ei ole määratud</translation>
<translation id="8012647001091218357">Teie vanematega ei õnnestunud praegu ühendust võtta. Proovige hiljem uuesti.</translation>
+<translation id="8025119109950072390">Sellel saidil asuvad ründajad võivad teid meelitada ohtlikele tegevustele, nagu tarkvara installimine või isiklike andmete (nt paroolid, telefoninumbrid või krediitkaardid) avaldamine.</translation>
+<translation id="803030522067524905">Google'i ohutu sirvimise teenus tuvastas hiljuti andmepüügi saidil <ph name="SITE" />. Andmepüügisaidid esitavad end teiste saitidena, et teid petta. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Leht on <ph name="SOURCE_LANGUAGE" /> keeles. Kas tõlkida <ph name="TARGET_LANGUAGE" /> keelde?</translation>
+<translation id="8041089156583427627">Saada tagasiside</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>
@@ -652,9 +696,11 @@ Inkognito režiim <ph name="SHORTCUT_KEY" /> võib järgmisel korral kasulikuks
<translation id="8150722005171944719">Fail <ph name="URL" /> ei ole loetav. Võimalik, et see on eemaldatud, teisaldatud või faili load takistavad juurdepääsu.</translation>
<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="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="8230421197304563332">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 krediitkaardiandmed).</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="8249320324621329438">Viimati toodud:</translation>
<translation id="8261506727792406068">Kustuta</translation>
@@ -663,11 +709,13 @@ Inkognito režiim <ph name="SHORTCUT_KEY" /> võib järgmisel korral kasulikuks
<translation id="8294431847097064396">Allikas</translation>
<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="8349305172487531364">Järjehoidjariba</translation>
<translation id="8363502534493474904">Lülitage lennukirežiim välja</translation>
<translation id="8364627913115013041">Määramata.</translation>
<translation id="8380941800586852976">Ohtlik</translation>
<translation id="8382348898565613901">Teie hiljuti külastatud järjehoidjad kuvatakse siin</translation>
+<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="8428213095426709021">Seaded</translation>
@@ -679,9 +727,9 @@ Inkognito režiim <ph name="SHORTCUT_KEY" /> võib järgmisel korral kasulikuks
<translation id="8498891568109133222">Hostil <ph name="HOST_NAME" /> kulus vastamiseks liiga kaua aega.</translation>
<translation id="852346902619691059">Server ei suutnud tõestada, et tegemist on domeeniga <ph name="DOMAIN" />. Teie seadme operatsioonisüsteem ei usalda selle turvasertifikaati. Selle põhjuseks võib olla vale seadistus või teie ühendust segav ründaja. <ph name="BEGIN_LEARN_MORE_LINK" />Vaadake lisateavet<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Google Payments ei toeta seda tüüpi kaarti. Valige teine kaart.</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="8553075262323480129">Tõlkimine nurjus, kuna lehe keelt ei õnnestunud määrata.</translation>
<translation id="8559762987265718583">Privaatset ühendust ei saa domeeniga <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> luua, kuna seadme kuupäev ja kellaaeg (<ph name="DATE_AND_TIME" />) on valed.</translation>
-<translation id="856992080682148">Selle saidi sertifikaat aegub 2017. aastal või hiljem ja sertifikaadiahel sisaldab sertifikaati, mis on allkirjastatud SHA-1-ga.</translation>
<translation id="8571890674111243710">Lehe tõlkimine <ph name="LANGUAGE" /> keelde...</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>
@@ -694,10 +742,12 @@ Inkognito režiim <ph name="SHORTCUT_KEY" /> võib järgmisel korral kasulikuks
<translation id="8740359287975076522">Hosti <ph name="HOST_NAME" /> &lt;abbr id="dnsDefinition"&gt;DNS-aadressi&lt;/abbr&gt; ei leitud. Probleemi diagnoositakse.</translation>
<translation id="8790007591277257123">&amp;Kustuta uuesti</translation>
<translation id="8798099450830957504">Vaikimisi</translation>
+<translation id="8800988563907321413">Teie lähedalasuvad soovitused kuvatakse siin</translation>
<translation id="8804164990146287819">Privaatsuseeskirjad</translation>
<translation id="8820817407110198400">Järjehoidjad</translation>
<translation id="8834246243508017242">Luba automaatne täitmine kontakte kasutades …</translation>
<translation id="883848425547221593">Muud järjehoidjad</translation>
+<translation id="884264119367021077">Tarneaadress</translation>
<translation id="884923133447025588">Tühistusmehhanismi ei leitud.</translation>
<translation id="885730110891505394">Jagamine Google'iga</translation>
<translation id="8866481888320382733">Reegli seadete sõelumisel ilmnes viga</translation>
@@ -715,18 +765,21 @@ Inkognito režiim <ph name="SHORTCUT_KEY" /> võib järgmisel korral kasulikuks
<translation id="8971063699422889582">Serveri sertifikaat on aegunud.</translation>
<translation id="8987927404178983737">kuu</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Sait, mille soovite avada, sisaldab kahjulikke programme</translation>
<translation id="9001074447101275817">Puhverserver <ph name="DOMAIN" /> nõuab kasutajanime ja parooli.</translation>
<translation id="901974403500617787">Kogu süsteemis kehtivaid märgiseid saab määrata ainult omanik: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">See leht on tõlgitud <ph name="TARGET_LANGUAGE" /> keelde</translation>
+<translation id="9035022520814077154">Turvaviga</translation>
<translation id="9038649477754266430">Kasuta lehtede kiiremaks laadimiseks ennustusteenust</translation>
<translation id="9039213469156557790">Lisaks sisaldab see leht teisi ressursse, mis pole turvalised. Edastamise ajal võivad ressursse vaadata ka teised ja ründajad saavad lehe käitumise muutmiseks ressursse muuta.</translation>
+<translation id="9040185888511745258">Saidil <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> asuvad ründajad võivad proovida meelitada teid installima programme, mis kahjustavad sirvimiskogemust (nt muudavad avalehte või kuvavad külastatavatel saitidel lisareklaame).</translation>
<translation id="9050666287014529139">Parool</translation>
<translation id="9065203028668620118">Muuda</translation>
<translation id="9068849894565669697">Värvi valimine</translation>
<translation id="9076283476770535406">See võib sisaldada täiskasvanutele mõeldud sisu</translation>
-<translation id="9092364396508701805">Hosti <ph name="HOST_NAME" /> leht ei tööta</translation>
<translation id="9103872766612412690">Sait <ph name="SITE" /> kasutab teie teabe kaitsmiseks tavaliselt krüpteerimist. Kui Chromium püüdis seekord saidiga <ph name="SITE" /> ühendust luua, tagastas veebisait ebatavalised ja valed mandaadid. See võib juhtuda siis, kui ründaja proovib teeselda, et on sait <ph name="SITE" />, või WiFi sisselogimisekraan on ühenduse katkestanud. Teie teave on endiselt kaitstud, sest Chromium peatas ühenduse enne andmevahetust.</translation>
<translation id="9137013805542155359">Kuva originaal</translation>
+<translation id="9137248913990643158">Enne selle rakenduse kasutamist alustage ja logige Chrome'i sisse.</translation>
<translation id="9148507642005240123">&amp;Võta muudatus tagasi</translation>
<translation id="9157595877708044936">Seadistamine...</translation>
<translation id="9170848237812810038">&amp;Võta tagasi</translation>
@@ -739,7 +792,6 @@ Inkognito režiim <ph name="SHORTCUT_KEY" /> võib järgmisel korral kasulikuks
<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="962701380617707048">Kaardi üksikasjade värskendamiseks sisestage krediitkaardi <ph name="CREDIT_CARD" /> aegumiskuupäev ja CVC</translation>
<translation id="969892804517981540">Ametlik järk</translation>
<translation id="988159990683914416">Arendaja järk</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 5502e564906..8a8eac9cd03 100644
--- a/chromium/components/strings/components_strings_fa.xtb
+++ b/chromium/components/strings/components_strings_fa.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">â€Ø§ØªØµØ§Ù„ مجدد به Wi-Fi</translation>
<translation id="1175364870820465910">&amp;چاپ...</translation>
<translation id="1181037720776840403">حذÙ</translation>
+<translation id="1184214524891303587">â€<ph name="BEGIN_WHITEPAPER_LINK" /> گزارش خودکار <ph name="END_WHITEPAPER_LINK" /> جزئیات حوادث امنیتی احتمالی به Google.†<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">بعدی</translation>
<translation id="1201895884277373915">موارد بیشتر از این سایت</translation>
<translation id="1206967143813997005">امضای اولیه نادرست</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Ùیروزه‌ای</translation>
<translation id="1629803312968146339">â€Ù…ی‌خواهید Chrome این کارت را ذخیره کند؟</translation>
<translation id="1640180200866533862">خط‌مشی‌های کاربر</translation>
+<translation id="1640244768702815859">از<ph name="BEGIN_LINK" />صÙحه اصلی سایت دیدن کنید<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">پیکربندی شبکه نامعتبر است و نتوانست وارد شود.</translation>
<translation id="1644574205037202324">سابقه</translation>
<translation id="1645368109819982629">پروتکل پشتیبانی‌نشده</translation>
<translation id="1676269943528358898">â€<ph name="SITE" /> معمولاً برای محاÙظت از اطلاعات شما از رمزگذاری استÙاده می‌کند. اما این بار Ú©Ù‡ Chrome تلاش کرد به <ph name="SITE" /> متصل شود، وب‌سایت اعتبارنامه‌ای نامعمول Ùˆ نادرست را برگرداند. ممکن است مهاجمی در تلاش باشد خود را به‌جای <ph name="SITE" /> معرÙÛŒ کند یا یک صÙحه ورود به سیستم Wi-Fi در ارتباط اختلال ایجاد کرده باشد. اطلاعات شما همچنان ایمن است، زیرا Google Chrome قبل از هرگونه تبادل داده، اتصال را متوق٠کرد.</translation>
+<translation id="168328519870909584">مهاجم‌ها در <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> درحال‌حاضر ممکن است تلاش کنند تا برنامه‌های خطرناکی در دستگاهتان نصب کنند که اطلاعات شما (مانند کارت‌های اعتباری، عکس‌ها، گذرواژه‌ها و پیام‌هایتان) را سرقت یا حذ٠می‌کنند.</translation>
<translation id="168841957122794586">گواهی‌نامه سرور دارای یک کلید رمزنگاری ضعی٠است.</translation>
-<translation id="1701955595840307032">دریاÙت محتوای پیشنهادی</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">برای بازدید این سایت باید از <ph name="NAME" /> اجازه بگیرید</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">شماره صÙحه</translation>
<translation id="1768211456781949159">â€<ph name="BEGIN_LINK" />Windows Network Diagnostics را اجرا کنید<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">لطÙاً «رمز عبارتی همگام‌سازی» خود را به‌روزرسانی کنید.</translation>
+<translation id="1787142507584202372">برگه‌های بازتان در اینجا نشان داده می‌شوند</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">â€Â«Ù…رور ایمن Google» به‌تازگی در <ph name="SITE" />ØŒ â€<ph name="BEGIN_LINK" />بداÙزار شناسایی کرده است<ph name="END_LINK" />. گاهی اوقات وب‌سایت‌هایی Ú©Ù‡ معمولاً امن هستند با بداÙزار آلوده می‌شوند. منبع محتوای مخرب <ph name="SUBRESOURCE_HOST" /> (یک توزیع‌کننده بداÙزار شناخته شده) است. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">پارامترهای درخواست یا درخواست نامعتبر</translation>
+<translation id="1834321415901700177">این سایت محتوی برنامه‌های مضر است</translation>
<translation id="1838667051080421715">درحال مشاهده منبع یک صÙحه وب هستید.</translation>
<translation id="1871208020102129563">â€ØªÙ†Ø¸ÛŒÙ… پروکسی به گونه‌ای است Ú©Ù‡ از سرورهای ثابت پروکسی استÙاده کند Ùˆ از آدرس اسکریپت pac. استÙاده نکند.</translation>
<translation id="1883255238294161206">Ú©ÙˆÚ†Ú© کردن Ùهرست</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">نشانک‌های تلÙن‌ همراه</translation>
<translation id="2148716181193084225">امروز</translation>
-<translation id="2149973817440762519">ویرایش نشانک</translation>
<translation id="2154054054215849342">همگام‌سازی برای دامنه شما در دسترس نیست</translation>
<translation id="2166049586286450108">دسترسی کامل سرپرست</translation>
<translation id="2166378884831602661">این سایت نمی‌تواند اتصالی ایمن ارائه دهد</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">â€Ø¯Ø±Ø­Ø§Ù„‌حاضر نمی‌توانید از <ph name="SITE" /> دیدن کنید، زیرا این وب‌سایت از HSTS استÙاده می‌کند. خطاها Ùˆ حمله‌های شبکه معمولاً موقتی هستند، بنابراین احتمالاً این صÙحه بعداً کار خواهد کرد. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">نشانک نامعتبر در نمایه <ph name="ENTRY_INDEX" /> نادیده گرÙته شد</translation>
<translation id="2354001756790975382">نشانک‌های دیگر</translation>
+<translation id="2355395290879513365">مهاجمین ممکن است بتوانند تصاویری را Ú©Ù‡ در این سایت می‌بینید مشاهده کنند Ùˆ با دست‌کاری آن‌ها شما را Ùریب دهند.</translation>
<translation id="2359808026110333948">ادامه</translation>
<translation id="2365563543831475020">گزارش خرابی ثبت‌شده در <ph name="CRASH_TIME" /> بارگذاری نشد</translation>
<translation id="2367567093518048410">سطح</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">نمایش خط‌مشی‌ها با مقدار تنظیم نشده</translation>
<translation id="2396249848217231973">&amp;واگرد حذÙ</translation>
<translation id="2455981314101692989">این صÙحهٔ وب، تکمیل خودکار این Ùرم را غیر Ùعال کرده است.</translation>
+<translation id="2460160116472764928">â€Â«Ù…رور ایمن Google» به‌تازگی در <ph name="SITE" />ØŒ â€<ph name="BEGIN_LINK" />بداÙزار شناسایی کرده است<ph name="END_LINK" />. گاهی اوقات وب‌سایت‌هایی Ú©Ù‡ معمولاً امن هستند با بداÙزار آلوده می‌شوند. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">تکمیل</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />در حال اجرای عیب‌یابی شبکه<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">â€URL جستجو نامعتبر است.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">آیا مطمئن هستید می‌خواهید این صÙحات را از سابقه خود حذ٠کنید؟</translation>
<translation id="2677748264148917807">خروج</translation>
<translation id="269990154133806163">سرور گواهینامه‌ای ارائه کرد Ú©Ù‡ با استÙاده از خط‌مشی «شÙاÙ‌سازی گواهینامه» به‌صورت عمومی آشکار نشده بود. این شرط لازمی برای بعضی از گواهینامه‌ها است تا اطمینان حاصل شود Ú©Ù‡ قابل‌اعتماد هستند Ùˆ در برابر مهاجمین محاÙظت می‌شوند. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Ùهرست خواندن</translation>
<translation id="2704283930420550640">مقدار با Ùرمت مطابقت ندارد.</translation>
<translation id="2704951214193499422">â€Chromium درحال حاضر نمی‌تواند کارت شما را تأیید کند. لطÙاً بعداً دوباره امتحان کنید.</translation>
<translation id="2705137772291741111">کپی ذخیره‌شده (ذخیره موقت‌شده) این سایت قابل خواندن نبود.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">سعی داشتید به <ph name="DOMAIN" /> دسترسی داشته باشید، اما گواهینامه‌ای که سرور ارائه کرد توسط صادرکننده آن باطل شده است. به این معنی که قطعاً نباید به گواهینامه‌های امنیتی که این سرور ارائه می‌دهد اعتماد کرد. ممکن است درحال ارتباط با یک مهاجم باشید. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">درخواست اجازه</translation>
<translation id="2713444072780614174">سÙید</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">در صÙحه تنظیمات می‌توانید همه پراکسی‌های پیکربندی شده برای هر اتصال را از کار بیاندازید.</translation>
<translation id="2955913368246107853">بستن نوار یاÙتن</translation>
<translation id="2958431318199492670">â€Ù¾ÛŒÚ©Ø±Ø¨Ù†Ø¯ÛŒ شبکه با استاندارد ONC تطابق ندارد. بخشی از پیکربندی ممکن است وارد نشود.</translation>
+<translation id="29611076221683977">â€Ù…هاجم‌ها در <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> درحال‌حاضر ممکن است تلاش کنند تا برنامه‌های خطرناکی در Mac شما نصب کنند Ú©Ù‡ اطلاعات شما (مانند کارت‌های اعتباری، عکس‌ها، گذرواژه‌ها Ùˆ پیام‌هایتان) را سرقت یا حذ٠می‌کنند.</translation>
<translation id="2969319727213777354">â€Ø¨Ø±Ø§ÛŒ برقراری یک اتصال امن، لازم است ساعت شما درست تنظیم شده باشد. زیرا گواهی‌هایی Ú©Ù‡ وب‌سایت‌ها برای شناسایی خودشان استÙاده می‌کنند، تنها برای دوره‌های زمانی خاصی معتبرند. از آنجا Ú©Ù‡ ساعت دستگاه شما نادرست است، Google Chrome نمی‌تواند این گواهی‌ها را تأیید کند.</translation>
<translation id="2972581237482394796">انجام مجدد</translation>
<translation id="2985306909656435243">â€Ø§Ú¯Ø± Ùعال شود، Chromium برای پر کردن سریع‌تر Ùرم، یک Ú©Ù¾ÛŒ از کارت شما در این دستگاه ذخیره می‌کند.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">عدم نمایش جزئیات</translation>
<translation id="3587482841069643663">همه</translation>
<translation id="3600246354004376029"><ph name="TITLE" />، <ph name="DOMAIN" />، <ph name="TIME" /></translation>
-<translation id="3609138628363401169">â€Ø³Ø±ÙˆØ± از برنامه اضاÙÛŒ Ú¯Ùتگوی مجدد TLS پشتیبانی نمی‌کند.</translation>
<translation id="36224234498066874">پاک کردن داده‌های مرور...</translation>
<translation id="362276910939193118">نمایش کل سابقه</translation>
<translation id="3623476034248543066">نشان دادن مقدار</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">اگر مکرراً با این مشکل مواجه می‌شوید، <ph name="HELP_LINK" /> را امتحان کنید.</translation>
<translation id="3658742229777143148">ویرایش</translation>
<translation id="3678029195006412963">درخواست امضا نشد</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">مجوزها دیگر معتبر نیستند</translation>
<translation id="3714780639079136834">â€Ø±ÙˆØ´Ù† کردن داده‌ شبکه تلÙÙ† همراه یا Wi-Fi</translation>
<translation id="3717027428350673159">â€<ph name="BEGIN_LINK" />بررسی پروکسی، دیوار آتش Ùˆ پیکربندی DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">اگر خطرات امنیتی که متوجه شما هستند را درک می‌کنید، می‌توانید قبل از حذ٠شدن برنامه‌های خطرناک از <ph name="BEGIN_LINK" />این سایت غیرایمن بازدید کنید<ph name="END_LINK" />.</translation>
<translation id="3739623965217189342">پیوندی که کپی کرده‌اید</translation>
<translation id="375403751935624634">بدلیل خطای سرور ترجمه انجام نشد.</translation>
<translation id="3759461132968374835">شما اخیراً گزارش خرابی ارسال نکرده‌اید. مشکلاتی Ú©Ù‡ در هنگام غیرÙعال بودن ویژگی ارائه گزارش خرابی ایجاد شده است، در اینجا نمایش داده نمی‌شود.</translation>
-<translation id="3788090790273268753">â€Ú¯ÙˆØ§Ù‡ÛŒ این سایت در سال Û²Û°Û±Û¶ منقضی می‌شود Ùˆ زنجیره گواهی حاوی یک گواهی با امضای SHA-1 است.</translation>
<translation id="382518646247711829">اگر از سرور پراکسی استÙاده می‌کنید...</translation>
<translation id="3828924085048779000">عبارت عبور خالی مجاز نیست.</translation>
<translation id="3845539888601087042">در حال نمایش سابقه از دستگاه‌هایی که در آن‌ها به سیستم وارد شده‌اید. <ph name="BEGIN_LINK" />بیشتر بدانید<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">کلید "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">â€Ú©Ù„اینت Ùˆ سرور از مجموعه رمزگذاری یا نسخه پروتکل SSL مشترکی استÙاده نمی‌کنند.</translation>
<translation id="4079302484614802869">â€ØªÙ†Ø¸ÛŒÙ…ات پروکسی، برای استÙاده از آدرس اسکریپت pac. تنظیم شده است Ùˆ از سرورهای ثابت نمی‌تواند استÙاده کند.</translation>
+<translation id="4098354747657067197">سایت پیش‌رو Ùریب‌کار است</translation>
<translation id="4103249731201008433">شماره سریال دستگاه نامعتبر است</translation>
<translation id="4103763322291513355">â€Ø¨Ø±Ø§ÛŒ مشاهده Ùهرست نشانی‌های وب ممنوع Ùˆ سایر خط‌مشی‌های اجباری براساس تصمیم سرپرست سیستم خود از &lt;strong&gt;chrome://policy&lt;/strong&gt; بازدید نمایید.</translation>
<translation id="4110615724604346410">این سرور نتوانست ثابت کند <ph name="DOMAIN" /> است؛ گواهینامه امنیتی آن خطاهایی دارد. ممکن است پیکربندی اشتباهی علت آن باشد یا مهاجمی اتصالتان را قطع کرده باشد. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{هیچ‌}=1{۱ برنامه ($1)}=2{۲ برنامه ($1،†$2)}one{# برنامه ($1،†$2،†$3)}other{# برنامه ($1،†$2،†$3)}}</translation>
<translation id="4220128509585149162">خرابی ها</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />عیب‌یابی شبکه را اجرا کنید<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">اتصال شما به این سایت کاملاً امن نیست</translation>
<translation id="4250680216510889253">نه</translation>
<translation id="425582637250725228">تغییراتی که انجام داده‌اید ممکن است ذخیره نشده باشند.</translation>
<translation id="4258748452823770588">امضای نادرست</translation>
<translation id="4269787794583293679">(بدون نام کاربری)</translation>
+<translation id="4280429058323657511">، انقضا <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">â€Â«Ù…رور ایمن Google» به‌تازگی <ph name="BEGIN_LINK" />برنامه‌های مخرب<ph name="END_LINK" /> در <ph name="SITE" /> پیدا کرده است. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">پیشنهادات والدین</translation>
<translation id="4304224509867189079">ورود به سیستم</translation>
<translation id="432290197980158659">سرور گواهینامه‌ای ارائه کرد Ú©Ù‡ با انتظارات داخلی مطابقت ندارد. این انتظارات برای بعضی از وب‌سایت‌های با امنیت بالا Ùˆ با هد٠محاÙظت از شما درنظر گرÙته شده‌اند. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">مقاله یاÙت نشد.</translation>
+<translation id="4326324639298822553">تاریخ انقضا را بررسی و دوباره امتحان کنید</translation>
<translation id="4331708818696583467">امن نیست</translation>
+<translation id="4356973930735388585">مهاجم‌ها در این سایت ممکن است تلاش کنند برنامه‌های خطرناکی در رایانه شما نصب کنند که اطلاعات شما (مانند عکس‌ها، گذرواژه‌ها، پیام‌ها و کارت‌های اعتباری) را به سرقت می‌برند یا حذ٠می‌کنند.</translation>
<translation id="4372948949327679948">مقدار مورد انتظار <ph name="VALUE_TYPE" />.</translation>
<translation id="4381091992796011497">نام کاربری:</translation>
<translation id="4394049700291259645">غیر Ùعال کردن</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">سعی داشتید به <ph name="DOMAIN" /> دسترسی داشته باشید، اما سرور گواهینامه نامعتبری ارائه کرد. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">اتصال شما به <ph name="DOMAIN" /> با استÙاده از یک مجموعه رمز مدرن، رمزگذاری شده است.</translation>
<translation id="4594403342090139922">&amp;واگرد حذÙ</translation>
+<translation id="4619615317237390068">برگه‌ها از دستگاه‌های دیگر</translation>
<translation id="4668929960204016307">،</translation>
<translation id="4670097147947922288">درحال مشاهده یک صÙحه اÙزونه هستید.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">تازه‌سازی خط مشی‌ها</translation>
<translation id="4728558894243024398">پلت Ùورم</translation>
<translation id="4744603770635761495">مسیر قابل اجرا</translation>
+<translation id="4750917950439032686">اطلاعات شما (به‌عنوان مثال، گذرواژه‌ها یا شماره‌ کارت‌های اعتباری) وقتی به این سایت ارسال می‌شوند، خصوصی هستند.</translation>
<translation id="4756388243121344051">&amp;سابقه</translation>
+<translation id="4759118997339041434">تکمیل خودکار پرداخت غیرÙعال شد</translation>
<translation id="4764776831041365478">ممکن است صÙحهٔ وب <ph name="URL" /> موقتاً خراب باشد یا ممکن است برای همیشه به آدرس وب جدید منتقل شده باشد.</translation>
<translation id="4771973620359291008">خطای ناشناخته‌ای رخ داد.</translation>
<translation id="4800132727771399293">â€ØªØ§Ø±ÛŒØ® انقضا Ùˆ CVC را بررسی کرده Ùˆ دوباره امتحان کنید</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">خطای شبکه</translation>
<translation id="4816492930507672669">متناسب با صÙحه</translation>
<translation id="4850886885716139402">نما</translation>
@@ -368,6 +387,7 @@
<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="4923417429809017348">این صÙحه از یک زبان ناشناس به <ph name="LANGUAGE_LANGUAGE" /> ترجمه شده است.</translation>
+<translation id="4923459931733593730">پرداخت</translation>
<translation id="4926049483395192435">باید مشخص شود.</translation>
<translation id="495170559598752135">عملکردها</translation>
<translation id="4958444002117714549">بزرگ کردن Ùهرست</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">در حال بارگیری</translation>
<translation id="5190835502935405962">نوار نشانک‌ها</translation>
<translation id="5199729219167945352">آزمایشات</translation>
-<translation id="5199841536747119669">پیشنهادات در اینجا نشان داده می‌شوند</translation>
<translation id="5251803541071282808">Cloud</translation>
<translation id="5277279256032773186">â€Ø§Ø² Chrome در محل کار استÙاده می‌کنید؟ کسب Ùˆ کارها می‌توانند تنظیمات Chrome را برای کارمندانشان مدیریت کنند. بیشتر بدانید</translation>
<translation id="5299298092464848405">خطا در تجزیه خط‌‌مشی</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">گزارش خرابی غیر Ùعال است.</translation>
<translation id="5317780077021120954">ذخیره</translation>
<translation id="5327248766486351172">نام</translation>
+<translation id="5337705430875057403">مهاجمان <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ممکن است شما را Ùریب دهند Ùˆ به انجام کارهای خطرناکی مثل نصب نرم‌اÙزار یا ارائه اطلاعات شخصی‌تان (مثلاً گذرواژه، شماره تلÙÙ† یا کارت‌های اعتباری) ترغیب کنند.</translation>
<translation id="5359637492792381994">این سرور نتوانست ثابت کند <ph name="DOMAIN" /> است؛ گواهینامه امنیتی آن درحال‌حاضر معتبر نیست. ممکن است پیکربندی اشتباهی علت آن باشد یا مهاجمی اتصالتان را قطع کرده باشد. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">تنظیمات خط‌‌مشی ذخیره نشد</translation>
+<translation id="5386426401304769735">â€Ø²Ù†Ø¬ÛŒØ±Ù‡ گواهی این سایت حاوی یک گواهی با امضای SHA-1 است.</translation>
<translation id="5421136146218899937">پاک کردن داده‌های مرور...</translation>
<translation id="5430298929874300616">حذ٠نشانک</translation>
<translation id="5431657950005405462">Ùایل شما پیدا نشد</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">صÙحه جاسازی‌شده‌ای در <ph name="SITE" /> می‌گوید:</translation>
<translation id="5556459405103347317">تازه‌سازی</translation>
<translation id="5565735124758917034">Ùعال</translation>
+<translation id="5572851009514199876">â€Ù„Ø·Ùاً Chrome را باز کنید Ùˆ به سیستم آن وارد شوید تا Chrome بتواند بررسی کند آیا مجاز به دسترسی به این سایت هستید یا خیر.</translation>
+<translation id="5580958916614886209">ماه انقضا را بررسی و دوباره امتحان کنید</translation>
<translation id="560412284261940334">مدیریت پشتیبانی نمی‌شود</translation>
<translation id="5610142619324316209">بررسی اتصال</translation>
<translation id="5617949217645503996">تعداد دÙعاتی Ú©Ù‡ <ph name="HOST_NAME" /> شما را به نشانی‌های دیگر هدایت کرده بیش از حد است.</translation>
<translation id="5622887735448669177">می‌خواهید از این سایت خارج شوید؟</translation>
<translation id="5629630648637658800">تنظیمات خط‌مشی بارگیری نشد</translation>
<translation id="5631439013527180824">نشانه مدیریت دستگاه نامعتبر است</translation>
+<translation id="5669703222995421982">دریاÙت محتوای شخصی‌سازی‌شده</translation>
+<translation id="5675650730144413517">این صÙحه کار نمی‌کند</translation>
<translation id="5677928146339483299">مسدود است</translation>
<translation id="5694783966845939798">â€Ø³Ø¹ÛŒ کردید به <ph name="DOMAIN" /> دسترسی داشته باشید، اما سرور گواهینامه‌ای ارائه کرد Ú©Ù‡ با استÙاده از الگوریتم امضای ضعیÙÛŒ (مثل SHA-1) امضا شده بود. این یعنی ممکن است اعتبارنامه‌های امنیتی ارائه‌شده توسط این سرور جعلی باشند Ùˆ ممکن است این سرور، سرور موردانتظار شما نباشد (شاید درحال ارتباط با یک مهاجم باشید). <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">هویت این وب سایت تأیید نشده است.</translation>
<translation id="5720705177508910913">کاربر کنونی</translation>
-<translation id="572328651809341494">برگه‌های اخیر</translation>
<translation id="5732392974455271431">والدینتان می‌توانند این سایت را برای شما بگشایند</translation>
<translation id="5784606427469807560">هنگام تأیید کارت مشکلی پیش آمد. اتصال اینترنتتان را بررسی و دوباره امتحان کنید.</translation>
<translation id="5785756445106461925">علاوه بر این، این صÙحه دارای منابع دیگری است Ú©Ù‡ امن نیستند. دیگران می‌توانند در حین انتقال، این منابع را ببینند Ùˆ این منابع می‌توانند برای تغییر Ù‚ÙÙ„ صÙحه، توسط یک مهاجم تغییر داده شوند.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">سعی داشتید به <ph name="DOMAIN" /> دسترسی داشته باشید، اما سرور گواهینامه‌ای حاوی یک کلید ضعی٠ارائه کرد. ممکن است مهاجمی کلید خصوصی را شکسته باشد و ممکن است این سرور، سرور موردانتظار شما نباشد (ممکن است درحال ارتباط با یک مهاجم باشید). <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">پوشهٔ جدید</translation>
<translation id="5869405914158311789">دسترسی به این سایت امکان‌پذیر نیست</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">â€Google Payments این نوع کارت را برای این تاجر پشتیبانی نمی‌کند. لطÙاً کارت دیگری انتخاب کنید.</translation>
<translation id="59174027418879706">Ùعال شد</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="5966707198760109579">Ù‡Ùته</translation>
<translation id="5967867314010545767">حذ٠از سابقه</translation>
<translation id="5975083100439434680">کوچک نمایی</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">این موارد را امتحان کنید:</translation>
<translation id="6151417162996330722">دوره اعتبار گواهینامه سرور بسیار طولانی است.</translation>
<translation id="6165508094623778733">بیشتر بدانید</translation>
+<translation id="6177128806592000436">اتصال شما به این سایت امن نیست</translation>
<translation id="6203231073485539293">اتصال اینترنتتان را بررسی کنید</translation>
<translation id="6218753634732582820">â€Ø¢Ø¯Ø±Ø³ از Chromium پاک شود؟</translation>
+<translation id="6251924700383757765">خط‌مشی رازداری</translation>
+<translation id="625755898061068298">انتخاب کردید اخطارهای امنیتی برای این سایت غیرÙعال شود.</translation>
<translation id="6259156558325130047">&amp;انجام مجدد ترتیب‌بندی مجدد</translation>
<translation id="6263376278284652872">نشانک‌های <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">بازگشت به ایمنی</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">دسترسی به <ph name="URL" /> امکان‌پذیر نیست.</translation>
<translation id="6321917430147971392">â€ØªÙ†Ø¸ÛŒÙ…ات DNS خودتان را بررسی کنید</translation>
<translation id="6328639280570009161">غیرÙعال کردن پیش‌بینی شبکه را امتحان کنید</translation>
+<translation id="6328786501058569169">این سایت گمراه‌کننده است</translation>
<translation id="6337534724793800597">Ùیلتر کردن خط‌مشی‌ها براساس نام</translation>
<translation id="6342069812937806050">Ùقط اکنون</translation>
<translation id="6345221851280129312">اندازه ناشناس</translation>
@@ -489,6 +519,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{۱ پیشنهاد دیگر}one{# پیشنهاد دیگر}other{# پیشنهاد دیگر}}</translation>
<translation id="6387478394221739770">â€Ø¹Ù„اقه‌مند به قابلیت‌های جدید Ùˆ جالب Chrome هستید؟ کانال بتای ما را در chrome.com/beta امتحان کنید.</translation>
<translation id="6389758589412724634">â€Chromium هنگام تلاش برای نمایش این صÙحه با کمبود حاÙظه روبرو شد.</translation>
+<translation id="6404511346730675251">ویرایش نشانک</translation>
+<translation id="6410264514553301377">â€CVC†و تاریخ انقضای <ph name="CREDIT_CARD" /> را وارد کنید</translation>
<translation id="6414888972213066896">از والدینتان پرسیدید آیا اجازه بازدید از این سایت را دارید</translation>
<translation id="6416403317709441254">â€Ø¯Ø±Ø­Ø§Ù„‌حاضر نمی‌توانید از <ph name="SITE" /> دیدن کنید، زیرا این وب‌سایت اعتبارنامه‌های درهمی ارسال کرده است Ú©Ù‡ Chromium نمی‌تواند آن‌ها را پردازش کند. خطاها Ùˆ حمله‌های شبکه معمولاً موقتی هستند، بنابراین احتمالاً این صÙحه بعداً کار خواهد کرد. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">امکان بررسی اینکه آیا مجوز باطل شده است یا نه وجود ندارد.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">نادیده گرÙته شد زیرا جستجوی پیش‌Ùرض توسط قانون غیرÙعال شده است.</translation>
<translation id="6462969404041126431">این سرور نتوانست ثابت کند <ph name="DOMAIN" /> است؛ شاید گواهینامه امنیتی آن باطل شده باشد. ممکن است پیکربندی اشتباهی علت آن باشد یا مهاجمی اتصالتان را قطع کرده باشد. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">خط‌‌مشی‌های دستگاه</translation>
+<translation id="6477321094435799029">â€Chrome کد نامعمول در این این صÙحه شناسایی کرده Ùˆ برای محاÙظت از اطلاعات شخصی‌تان (مثلاً گذرواژه‌ها، شماره تلÙن‌‌ها Ùˆ کارت‌های اعتباری) آن را مسدود کرده است.</translation>
<translation id="6489534406876378309">شروع بارگذاری کردن خرابی‌ها</translation>
<translation id="6529602333819889595">&amp;انجام مجدد حذÙ</translation>
<translation id="6534179046333460208">پیشنهادهای «وب Ùیزیکی»</translation>
<translation id="6550675742724504774">گزینه‌ها</translation>
+<translation id="6556239504065605927">اتصال امن</translation>
<translation id="6563469144985748109">مدیرتان هنوز این سایت را تأیید نکرده است</translation>
<translation id="6593753688552673085">کمتر از <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">گزینه‌های رمزگذاری</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">این قانون قدیمی شده است.</translation>
<translation id="6652240803263749613">این سرور نتوانست ثابت کند <ph name="DOMAIN" /> است؛ گواهینامه امنیتی آن مورداعتماد سیستم‌عامل رایانه شما نیست. ممکن است پیکربندی اشتباهی علت آن باشد یا مهاجمی اتصالتان را قطع کرده باشد. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">ویرایش پوشه</translation>
-<translation id="6660210980321319655">به‌صورت خودکار در <ph name="CRASH_TIME" /> گزارش داده شد</translation>
<translation id="6671697161687535275">â€Ù¾ÛŒØ´Ù†Ù‡Ø§Ø¯ Ùرم از Chromium پاک شود؟</translation>
<translation id="6685834062052613830">خروج از سیستم و تکمیل راه‌اندازی</translation>
<translation id="6710213216561001401">قبلی</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">مقدار خط‌‌مشی</translation>
<translation id="6757797048963528358">دستگاهتان به خواب رÙته است.</translation>
<translation id="6778737459546443941">والدینتان هنوز این سایت را تأیید نکرده‌اند</translation>
+<translation id="6810899417690483278">شناسه سÙارشی‌سازی</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">صÙحهٔ وب موجود در <ph name="URL" /> در حال حاضر موجود نیست. ممکن است بیش از حد مشغول باشد یا برای انجام تعمیرات در دسترس نباشد.</translation>
<translation id="6831043979455480757">ترجمه</translation>
@@ -532,7 +566,7 @@
<translation id="6897140037006041989">نماینده کاربر</translation>
<translation id="6915804003454593391">کاربر:</translation>
<translation id="6957887021205513506">به نظر می‌رسد که گواهی سرور جعلی باشد.</translation>
-<translation id="6965382102122355670">تأیید</translation>
+<translation id="6965382102122355670">قبول</translation>
<translation id="6965978654500191972">دستگاه</translation>
<translation id="6970216967273061347">حوزه</translation>
<translation id="6973656660372572881">â€Ù‡Ù… سرورهای پروکسی ثابت Ùˆ هم آدرس اسکریپت pac. مشخص شده‌اند.</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">اطلاعات تماس</translation>
+<translation id="7079718277001814089">این سایت حاوی بداÙزار است</translation>
<translation id="7087282848513945231">شهرستان</translation>
<translation id="7088615885725309056">قدیمی تر</translation>
<translation id="7090678807593890770">â€Ø¬Ø³ØªØ¬ÙˆÛŒ <ph name="LINK" /> در Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> از استانداردهای امنیتی پیروی نمی‌کند.</translation>
<translation id="721197778055552897">دربارهٔ این مشکل <ph name="BEGIN_LINK" />بیشتر بیاموزید<ph name="END_LINK" />.</translation>
<translation id="7219179957768738017">اتصال از <ph name="SSL_VERSION" /> استÙاده می‌کند.</translation>
+<translation id="724691107663265825">این وب‌سایت بداÙزار دارد</translation>
<translation id="724975217298816891">â€Ø¨Ø±Ø§ÛŒ به‌روزرسانی جزئیات کارتتان، تاریخ انقضا Ùˆ CVC کارت <ph name="CREDIT_CARD" /> را وارد کنید. بعد از تأیید شدن، جزئیات کارتتان با این سایت به اشتراک گذاشته می‌شود.</translation>
<translation id="725866823122871198">اتصال خصوصی به <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> برقرار نمی‌شود زیرا تاریخ و زمان رایانه شما (<ph name="DATE_AND_TIME" />) نادرست است.</translation>
<translation id="7269802741830436641">این صÙحهٔ وب دارای یک دور هدایت مجدد است</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">لغو</translation>
<translation id="7667346355482952095">کد خط‌مشی برگردانده‌شده خالی است یا با کد کنونی مطابقت ندارد</translation>
<translation id="7668654391829183341">دستگاه ناشناس</translation>
+<translation id="7669271284792375604">مهاجمان در این سایت ممکن است تلاش کنند شما را با نصب برنامه‌هایی Ú©Ù‡ به تجربه مرور شما آسیب می‌رساند، Ùریب دهند (مثلاً با تغییر دادن صÙحه اصلی شما یا با نشان دادن آگهی‌های بیش از حد در سایت‌هایی Ú©Ù‡ بازدید می‌کنید).</translation>
<translation id="7674629440242451245">â€Ø¹Ù„اقه‌مند به قابلیت‌های جدید Ùˆ جالب Chrome هستید؟ کانال برنامه‌نویسان ما را در chrome.com/dev امتحان کنید.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />ادامه به <ph name="SITE" /> (غیرایمن)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">این سایت نمی‌تواند از حاÙظه پنهان بار شود</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">اتصال با استÙاده از <ph name="CIPHER" /> Ùˆ با <ph name="MAC" /> برای راستی‌آزمایی پیام Ùˆ <ph name="KX" /> به‌عنوان مکانیسم تبدیل کلید رمزگذاری می‌شود.</translation>
<translation id="780301667611848630">نه متشکرم</translation>
<translation id="7805768142964895445">وضعیت</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">â€Ù¾ÛŒØ´Ù†Ù‡Ø§Ø¯ Ùرم از Chrome پاک شود؟</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="7887683347370398519">â€CVC را بررسی کرده Ùˆ دوباره امتحان کنید</translation>
<translation id="7912024687060120840">در پوشه:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">گواهی سرور هنوز معتبر نیست.</translation>
<translation id="7942349550061667556">قرمز</translation>
+<translation id="7947285636476623132">سال انقضا را بررسی و دوباره امتحان کنید</translation>
<translation id="7951415247503192394">(۳۲ بیت)</translation>
<translation id="7956713633345437162">نشانک‌های تلÙن‌ همراه</translation>
<translation id="7961015016161918242">هرگز</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">همیشه <ph name="ORIGINAL_LANGUAGE" /> به <ph name="TARGET_LANGUAGE" /> ترجمه شود</translation>
<translation id="7995512525968007366">تعیین نشده</translation>
<translation id="8012647001091218357">در حال حاضر نمی‌توانیم با والدینتان ارتباط برقرار کنیم. لطÙاً دوباره امتحان کنید.</translation>
+<translation id="8025119109950072390">مهاجمان در این سایت ممکن است شما را Ùریب دهند Ú©Ù‡ کارهای خطرناکی مثل نصب نرم‌اÙزار یا ارائه اطلاعات شخصی‌تان (مثلاً گذرواژه‌ها، شماره تلÙن‌ها یا کارت‌های اعتباری) انجام دهید.</translation>
+<translation id="803030522067524905">â€Â«Ù…رور ایمن Google» به‌تازگی در <ph name="SITE" /> Ùیشینگ شناسایی کرده است. سایت‌های Ùیشینگ برای Ùریب دادن شما وانمود می‌کنند سایت‌های دیگری هستند. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">این صÙحه به زبان <ph name="SOURCE_LANGUAGE" /> است. مایلید آن را به <ph name="TARGET_LANGUAGE" /> ترجمه کنید؟</translation>
+<translation id="8041089156583427627">ارسال بازخورد</translation>
<translation id="8088680233425245692">مشاهده مقاله ناموÙÙ‚ بود.</translation>
<translation id="8089520772729574115">کمتر از ۱ مگابایت</translation>
<translation id="8091372947890762290">Ùعال‌سازی در سرور در حالت تعلیق است</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719">این Ùایل در <ph name="URL" /> قابل خواندن نیست. ممکن است حذ٠شده، جابجا شده باشد Ùˆ یا مجوزهای Ùایل از دسترسی جلوگیری می‌کنند.</translation>
<translation id="8194797478851900357">&amp;واگرد انتقال</translation>
<translation id="8201077131113104583">نشانی وب به‌روزرسانی نامعتبر برای برنامه اÙزودنی با شناسه «<ph name="EXTENSION_ID" />».</translation>
+<translation id="8202097416529803614">خلاصه سÙارش</translation>
<translation id="8218327578424803826">مکان اختصاص یاÙته:</translation>
<translation id="8225771182978767009">شخصی که این رایانه را راه‌اندازی کرده این سایت را مسدود کرده است.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />،†<ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">مهاجم‌ها در <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ممکن است تلاش کنند تا برنامه‌های خطرناکی در رایانه شما نصب کنند که اطلاعات شما (مانند کارت‌های اعتباری، عکس‌ها، گذرواژه‌ها و پیام‌هایتان) را سرقت یا حذ٠می‌کنند.</translation>
<translation id="8241707690549784388">صÙحه‌ای Ú©Ù‡ جستجو می‌کنید از اطلاعاتی استÙاده می‌کند Ú©Ù‡ شما وارد کرده‌اید. بازگشت به آن صÙحه ممکن است باعث شود اقدامی را Ú©Ù‡ قبلاً انجام دادید دوباره تکرار کنید. آیا می‌خواهید ادامه دهید؟</translation>
<translation id="8249320324621329438">آخرین واکشی شده:</translation>
<translation id="8261506727792406068">حذÙ</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">منبع</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="8349305172487531364">نوار نشانک‌ها</translation>
<translation id="8363502534493474904">خاموش کردن حالت هواپیما</translation>
<translation id="8364627913115013041">تنظیم نشده است.</translation>
<translation id="8380941800586852976">خطرناک</translation>
<translation id="8382348898565613901">نشانک‌هایی که به‌تازگی از آن‌ها دیدن کرده‌اید، در اینجا نشان داده می‌شوند</translation>
+<translation id="8398259832188219207">گزارش خرابی در <ph name="UPLOAD_TIME" /> بارگذاری شد</translation>
<translation id="8412145213513410671">خرابی ها (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">شما باید همان عبارت عبور را دوبار وارد کنید.</translation>
<translation id="8428213095426709021">تنظیمات</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222">پاسخ <ph name="HOST_NAME" /> بیش از حد طول کشیده است.</translation>
<translation id="852346902619691059">این سرور نتوانست ثابت کند <ph name="DOMAIN" /> است؛ گواهینامه امنیتی آن مورداعتماد سیستم‌عامل دستگاه شما نیست. ممکن است پیکربندی اشتباهی علت آن باشد یا مهاجمی اتصالتان را قطع کرده باشد. <ph name="BEGIN_LEARN_MORE_LINK" />بیشتر بدانید<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">â€Google Payments این نوع کارت را پشتیبانی نمی‌کند. لطÙاً کارت دیگری انتخاب کنید.</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="8553075262323480129">ترجمه انجام نشد زیرا زبان صÙحه تعیین نشد.</translation>
<translation id="8559762987265718583">اتصال خصوصی به <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> انجام نمی‌شود، زیرا تاریخ و زمان دستگاه شما (<ph name="DATE_AND_TIME" />) نادرست است.</translation>
-<translation id="856992080682148">â€Ú¯ÙˆØ§Ù‡ÛŒ این سایت در سال Û²Û°Û±Û· یا بعد از آن منقضی می‌شود Ùˆ زنجیره گواهی حاوی یک گواهی با امضای SHA-1 است.</translation>
<translation id="8571890674111243710">ترجمه صÙحه به <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">این مجوز هیچ مکانیزمی را برای بررسی اینکه آیا باطل شده یا نه مشخص نمی‌کند.</translation>
<translation id="8620436878122366504">والدینتان هنوز این سایت را تأیید نکرده‌اند</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522">â€<ph name="HOST_NAME" />’s &lt;abbr id="dnsDefinition"&gt;آدرس DNS&lt;/abbr&gt; پیدا نشد. درحال بررسی برای تشخیص مشکل.</translation>
<translation id="8790007591277257123">&amp;انجام مجدد حذÙ</translation>
<translation id="8798099450830957504">پیش‌Ùرض</translation>
+<translation id="8800988563907321413">پیشنهادات اطرا٠شما در اینجا نشان داده می‌شوند</translation>
<translation id="8804164990146287819">خط‌مشی رازداری</translation>
<translation id="8820817407110198400">نشانک‌ها</translation>
<translation id="8834246243508017242">Ùعال‌سازی تکمیل خودکار با استÙاده از مخاطبین…</translation>
<translation id="883848425547221593">نشانک‌های دیگر</translation>
+<translation id="884264119367021077">اطلاعات ارسال</translation>
<translation id="884923133447025588">هیچ مکانیزم ابطالی یاÙت نشد.</translation>
<translation id="885730110891505394">â€Ø§Ø´ØªØ±Ø§Ú©â€ŒÚ¯Ø°Ø§Ø±ÛŒ با Google</translation>
<translation id="8866481888320382733">خطا در تجزیه تنظیمات خط‌مشی</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">گواهی سرور منقضی شده است.</translation>
<translation id="8987927404178983737">ماه</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">سایت در پیش‌رو حاوی برنامه‌های خطرناک است</translation>
<translation id="9001074447101275817">پروکسی <ph name="DOMAIN" /> به نام کاربری و گذرواژه نیاز دارد.</translation>
<translation id="901974403500617787">پرچم‌هایی Ú©Ù‡ در تمام سیستم اعمال می‌شوند Ùقط توسط مالک قابل تنظیم هستند: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">این صÙحه به <ph name="TARGET_LANGUAGE" /> ترجمه شده است</translation>
+<translation id="9035022520814077154">خطای امنیتی</translation>
<translation id="9038649477754266430">استÙاده از یک سرویس پیش‌بینی برای بار کردن سریع‌تر صÙحه‌ها</translation>
<translation id="9039213469156557790">علاوه بر این، این صÙحه دارای منابع دیگری است Ú©Ù‡ امن نیستند. دیگران می‌توانند در حین انتقال، این منابع را ببینند Ùˆ این منابع می‌توانند برای تغییر رÙتار صÙحه، توسط یک مهاجم تغییر داده شوند.</translation>
+<translation id="9040185888511745258">ممکن است مهاجمان در <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> با Ùریب شما تلاش کنند شما را به نصب برنامه‌هایی تشویق کنند Ú©Ù‡ به تجربه مرورتان آسیب می‌رساند (برای مثال با تغییر صÙحه اصلی‌تان یا نشان دادن آگهی‌های زیادی در سایت‌هایی Ú©Ù‡ بازدید می‌کنید).</translation>
<translation id="9050666287014529139">عبارت عبور</translation>
<translation id="9065203028668620118">ویرایش</translation>
<translation id="9068849894565669697">انتخاب رنگ</translation>
<translation id="9076283476770535406">این سایت ممکن است شامل محتوای مخصوص بزرگسالان باشد</translation>
-<translation id="9092364396508701805">صÙحه <ph name="HOST_NAME" /> کار نمی‌کند</translation>
<translation id="9103872766612412690">â€<ph name="SITE" /> معمولاً برای محاÙظت از اطلاعات شما از رمزگذاری استÙاده می‌کند. اما این بار Ú©Ù‡ Chromium تلاش کرد به <ph name="SITE" /> متصل شود، وب‌سایت اعتبارنامه‌ای نامعمول Ùˆ نادرست را برگرداند. ممکن است مهاجمی در تلاش باشد خود را به‌جای <ph name="SITE" /> معرÙÛŒ کند یا یک صÙحه ورود به سیستم Wi-Fi در ارتباط اختلال ایجاد کرده باشد. اطلاعات شما همچنان ایمن است، زیرا Chromium قبل از هرگونه تبادل داده، اتصال را متوق٠کرد.</translation>
<translation id="9137013805542155359">نمایش مورد اصلی</translation>
+<translation id="9137248913990643158">â€Ù„Ø·Ùاً پیش از استÙاده از این برنامه، Chrome را باز کنید Ùˆ به سیستم آن وارد شوید.</translation>
<translation id="9148507642005240123">&amp;واگرد ویرایش</translation>
<translation id="9157595877708044936">در حال راه‌اندازی...</translation>
<translation id="9170848237812810038">&amp;واگرد</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">پاک کردن Ùرم</translation>
<translation id="939736085109172342">پوشهٔ جدید</translation>
<translation id="941721044073577244">گویا اجازه بازدید از این سایت را ندارید</translation>
-<translation id="962701380617707048">â€Ø¨Ø±Ø§ÛŒ به‌روزرسانی جزئیات کارتتان، تاریخ انقضا Ùˆ CVC کارت <ph name="CREDIT_CARD" /> را وارد کنید</translation>
<translation id="969892804517981540">ساخت رسمی</translation>
<translation id="988159990683914416">ساخت برنامه‌نویس</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 6dcb8ed1073..551330bdf7c 100644
--- a/chromium/components/strings/components_strings_fi.xtb
+++ b/chromium/components/strings/components_strings_fi.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Muodosta yhteys Wi-Fi-verkkoon uudelleen.</translation>
<translation id="1175364870820465910">Tulo&amp;sta...</translation>
<translation id="1181037720776840403">Poista</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Ilmoita Googlelle automaattisesti<ph name="END_WHITEPAPER_LINK" /> mahdollisista turvallisuusongelmista. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Seuraava</translation>
<translation id="1201895884277373915">Lisää tästä sivustosta</translation>
<translation id="1206967143813997005">Virheellinen alkuperäinen allekirjoitus</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Turkoosi</translation>
<translation id="1629803312968146339">Haluatko, että Chrome tallentaa tämän kortin?</translation>
<translation id="1640180200866533862">Käyttäjäkäytännöt</translation>
+<translation id="1640244768702815859">Kokeile <ph name="BEGIN_LINK" />siirtyä sivuston etusivulle<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Verkkoasetukset ovat virheelliset eikä niitä voi tuoda.</translation>
<translation id="1644574205037202324">Historia</translation>
<translation id="1645368109819982629">Protokollaa ei tueta</translation>
<translation id="1676269943528358898"><ph name="SITE" /> suojaa tietosi normaalisti salauksen avulla. Kun Chrome yritti tällä kertaa yhdistää sivustoon <ph name="SITE" />, sivusto palautti epätavalliset ja virheelliset kirjautumistiedot. Hyökkääjä saattaa yrittää esiintyä sivustona <ph name="SITE" />, tai Wi-Fi-kirjautumisruutu on keskeyttänyt yhteyden. Tietosi ovat edelleen turvassa, sillä Google Chrome katkaisi yhteyden, ennen kuin mitään tietoja vaihdettiin.</translation>
+<translation id="168328519870909584">Sivustolle <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> hyökännyt taho voi yrittää asentaa laitteellesi vaarallisia ohjelmia, jotka varastavat tai poistavat tietojasi, kuten kuviasi, salasanojasi, viestejäsi ja luottokorttiesi tietoja.</translation>
<translation id="168841957122794586">Palvelinvarmenne sisältää heikon salausavaimen.</translation>
-<translation id="1701955595840307032">Lisää suositeltua sisältöä</translation>
<translation id="1710259589646384581">Käyttöjärjestelmä</translation>
<translation id="1721312023322545264">Tarvitset henkilön <ph name="NAME" /> luvan käydä tällä sivustolla</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Sivunumero</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Kokeile Windowsin verkon diagnostiikkaa<ph name="END_LINK" /></translation>
<translation id="1783075131180517613">Päivitä synkronoinnin tunnuslause.</translation>
+<translation id="1787142507584202372">Avoimet välilehdet näkyvät tässä.</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Googlen selaussuoja <ph name="BEGIN_LINK" />havaitsi äskettäin haittaohjelman<ph name="END_LINK" /> sivustolla <ph name="SITE" />. Myös tavallisesti turvalliset verkkosivustot voivat joskus saada haittaohjelmatartunnan. Haitallisen sisällön lähde on <ph name="SUBRESOURCE_HOST" />, tunnettu haittaohjelmien jakelija. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">Pyyntö on virheellinen tai pyynnön parametrit ovat virheelliset</translation>
+<translation id="1834321415901700177">Tämä sivusto sisältää haitallisia ohjelmia</translation>
<translation id="1838667051080421715">Katselet verkkosivun lähdekoodia.</translation>
<translation id="1871208020102129563">Välityspalvelin on asetettu käyttämään kiinteitä välityspalvelimia, ei .pac-URL-osoitetta.</translation>
<translation id="1883255238294161206">Tiivistä luettelo</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Asiakassovellus</translation>
<translation id="213826338245044447">Mobiilikirjanmerkit</translation>
<translation id="2148716181193084225">Tänään</translation>
-<translation id="2149973817440762519">Muokkaa kirjanmerkkiä</translation>
<translation id="2154054054215849342">Synkronointi ei ole käytettävissä verkkotunnuksessasi.</translation>
<translation id="2166049586286450108">Järjestelmänvalvojan täydet käyttöoikeudet</translation>
<translation id="2166378884831602661">Tämä sivusto ei voi tarjota suojattua yhteyttä</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Et juuri nyt voi siirtyä sivustolle <ph name="SITE" />, sillä se käyttää HSTS:ää. Verkkovirheet ja ‑hyökkäykset ovat yleensä väliaikaisia, joten voit todennäköisesti vierailla sivustolla myöhemmin. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">Ohitettiin virheellinen kirjanmerkki kohdassa <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Muut kirjanmerkit</translation>
+<translation id="2355395290879513365">Hyökkääjät voivat mahdollisesti nähdä kuvat, joita katselet tällä sivustolla, ja huijata sinua muokkaamalla niitä.</translation>
<translation id="2359808026110333948">Jatka</translation>
<translation id="2365563543831475020">Kaatumisraportti tallennettu <ph name="CRASH_TIME" />, ei vielä lähetetty</translation>
<translation id="2367567093518048410">Taso</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Näytä käytännöt, joille ei ole asetettu arvoa</translation>
<translation id="2396249848217231973">K&amp;umoa poisto</translation>
<translation id="2455981314101692989">Tämä verkkosivu on poistanut tämän lomakkeen automaattisen täytön käytöstä.</translation>
+<translation id="2460160116472764928">Googlen selaussuoja <ph name="BEGIN_LINK" />havaitsi äskettäin haittaohjelman<ph name="END_LINK" /> sivustolla <ph name="SITE" />. Myös tavallisesti turvalliset verkkosivustot voivat joskus saada haittaohjelmatartunnan. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">Täytä</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />verkon diagnostiikkaa<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Virheellinen hakukoneen URL-osoite.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Haluatko varmasti poistaa nämä sivut historiastasi?</translation>
<translation id="2677748264148917807">Poistu</translation>
<translation id="269990154133806163">Palvelimen lähettämän varmenteen tietoja ei ole annettu yleiseen käyttöön Certificate Transparency ‑käytännön mukaisesti. Tietojen antamista edellytetään joitakin varmenteita käytettäessä, jotta niiden luotettavuus voidaan varmistaa ja käyttäjiä voidaan suojella hakkereilta. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">Lukulista</translation>
<translation id="2704283930420550640">Arvo ei vastaa muotoa.</translation>
<translation id="2704951214193499422">Chromium ei voinut vahvistaa korttiasi. Yritä myöhemmin uudelleen.</translation>
<translation id="2705137772291741111">Tämän sivuston välimuistiin tallennettu kopio oli lukukelvoton.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Yritit muodostaa yhteyden verkkotunnukseen <ph name="DOMAIN" />, mutta varmenteen myöntäjä on peruuttanut palvelimen esittämän varmenteen. Palvelimen esittämiin tunnistetietoihin ei missään tapauksessa tule luottaa. Saatat olla tekemisissä hakkerin kanssa. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">Pyydä käyttölupaa</translation>
<translation id="2713444072780614174">Valkoinen</translation>
+<translation id="2720342946869265578">Nearby</translation>
<translation id="2721148159707890343">Pyyntö onnistui</translation>
<translation id="2728127805433021124">Palvelimen varmenne on allekirjoitettu heikolla allekirjoitusalgoritmilla.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />yhteysdiagnostiikkaa<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Voit poistaa yhteydelle määritetyt välityspalvelimet käytöstä asetussivulla.</translation>
<translation id="2955913368246107853">Sulje hakupalkki</translation>
<translation id="2958431318199492670">Verkkoasetukset eivät noudata ONC-standardia. Kaikkia asetuksia ei välttämättä tuoda.</translation>
+<translation id="29611076221683977">Sivustoon <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, kuten kuviasi, salasanojasi, viestejäsi ja luottokorttiesi tietoja.</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="2985306909656435243">Jos tämä on käytössä, Chromium tallentaa kortin kopion tälle laitteelle nopeuttaakseen lomakkeiden täyttöä.</translation>
@@ -260,7 +269,6 @@
<translation id="3586931643579894722">Piilota lisätiedot</translation>
<translation id="3587482841069643663">Kaikki</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Palvelin ei tue TLS-edelleensiirto-laajennusta</translation>
<translation id="36224234498066874">Poista selaustiedot...</translation>
<translation id="362276910939193118">Näytä koko selaushistoria</translation>
<translation id="3623476034248543066">Näytä arvo</translation>
@@ -272,6 +280,7 @@
<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="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>
@@ -281,10 +290,10 @@
<translation id="3712624925041724820">Käyttöluvat ovat lopussa</translation>
<translation id="3714780639079136834">Ota mobiilidata tai Wi-Fi käyttöön.</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Tarkista välityspalvelimen, palomuurin ja nimipalvelun määritykset<ph name="END_LINK" />.</translation>
+<translation id="3736520371357197498">Jos ymmärrät käyntiä koskevat turvallisuusriskit, voit <ph name="BEGIN_LINK" />siirtyä vaarantuneeseen sivustoon<ph name="END_LINK" /> jo ennen haitallisten ohjelmien poistamista.</translation>
<translation id="3739623965217189342">Kopioimasi linkki</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="3788090790273268753">Tämän sivuston varmenne vanhenee vuonna 2016, ja varmenneketju sisältää varmenteen, joka on allekirjoitettu SHA-1:llä.</translation>
<translation id="382518646247711829">Jos käytät välityspalvelinta…</translation>
<translation id="3828924085048779000">Tunnuslause ei voi olla tyhjä.</translation>
<translation id="3845539888601087042">Näytetään kirjautuneiden laitteiden historia. <ph name="BEGIN_LINK" />Lisätietoja<ph name="END_LINK" /></translation>
@@ -306,6 +315,7 @@
<translation id="4058922952496707368">Avain <ph name="SUBKEY" />: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Asiakassovellus ja palvelin eivät tue samaa SSL-protokollaversiota tai salaustekniikkaa.</translation>
<translation id="4079302484614802869">Välityspalvelinmääritykset on asetettu käyttämään .pac-URL-osoitteita, ei kiinteitä välityspalvelimia.</translation>
+<translation id="4098354747657067197">Seuraava sivusto on petollinen</translation>
<translation id="4103249731201008433">Laitteen sarjanumero on virheellinen</translation>
<translation id="4103763322291513355">Voit lukea listan kielletyistä URL-osoitteista ja muut järjestelmänvalvojasi määräämät käytännöt osoitteessa &lt;strong&gt;chrome://policy&lt;/strong&gt;.</translation>
<translation id="4110615724604346410">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />, sillä sen suojausvarmenteessa on virheitä. Tämä voi johtua määritysvirheestä tai hyökkäyksestä, jonka tavoitteena on verkkoyhteytesi sieppaaminen. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -323,15 +333,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ei yhtään}=1{1 sovellus ($1)}=2{2 sovellusta ($1 ja $2)}other{# sovellusta ($1, $2 $3)}}</translation>
<translation id="4220128509585149162">Kaatuu</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Kokeile verkon diagnostiikkaa<ph name="END_LINK" /></translation>
+<translation id="4250431568374086873">Yhteytesi sivustoon ei ole täysin suojattu.</translation>
<translation id="4250680216510889253">Ei</translation>
<translation id="425582637250725228">Tekemiäsi muutoksia ei välttämättä tallenneta.</translation>
<translation id="4258748452823770588">Virheellinen allekirjoitus</translation>
<translation id="4269787794583293679">(Ei käyttäjänimeä)</translation>
+<translation id="4280429058323657511">, vanhentumispäivä <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Googlen selaussuoja <ph name="BEGIN_LINK" />löysi äskettäin haitallisia ohjelmia<ph name="END_LINK" /> sivustolta <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">Ylätason ehdotukset</translation>
<translation id="4304224509867189079">Kirjaudu sisään</translation>
<translation id="432290197980158659">Palvelimen esittämä varmenne ei vastaa sisäänrakennettuja odotuksia. Tietyillä tehokkaasti suojatuilla verkkosivustoilla on odotuksia, joilla suojataan käyttäjiä. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">Artikkelia ei löydy</translation>
+<translation id="4326324639298822553">Tarkista vanhentumispäivä ja yritä uudelleen.</translation>
<translation id="4331708818696583467">Ei turvallinen</translation>
+<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="4381091992796011497">Käyttäjätunnus:</translation>
<translation id="4394049700291259645">Poista käytöstä</translation>
@@ -349,6 +364,7 @@
<translation id="4589078953350245614">Yritit muodostaa yhteyden verkkotunnukseen <ph name="DOMAIN" />, mutta palvelin esitti virheellisen varmenteen. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4592951414987517459">Yhteytesi kohteeseen <ph name="DOMAIN" /> on salattu nykyaikaisella salaustekniikalla.</translation>
<translation id="4594403342090139922">K&amp;umoa poisto</translation>
+<translation id="4619615317237390068">Välilehdet muista laitteista</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Katselet laajennussivua.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -357,10 +373,13 @@
<translation id="4726672564094551039">Päivitä käytännöt</translation>
<translation id="4728558894243024398">Käyttöympäristö</translation>
<translation id="4744603770635761495">Suoritettavan tiedoston polku</translation>
+<translation id="4750917950439032686">Salasanat, luottokorttinumerot ja muut tietosi pysyvät yksityisinä, kun ne lähetetään tälle sivustolle.</translation>
<translation id="4756388243121344051">&amp;Historia</translation>
+<translation id="4759118997339041434">Maksujen automaattinen täyttö pois käytöstä</translation>
<translation id="4764776831041365478">Osoitteessa <ph name="URL" /> oleva sivu saattaa olla väliaikaisesti pois käytöstä tai se on voitu siirtää pysyvästi uuteen osoitteeseen.</translation>
<translation id="4771973620359291008">Tapahtui tuntematon virhe.</translation>
<translation id="4800132727771399293">Tarkista vanhenemispäivä ja CVC ja yritä uudelleen.</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Verkkovirhe</translation>
<translation id="4816492930507672669">Sovita sivulle</translation>
<translation id="4850886885716139402">Näytä</translation>
@@ -369,6 +388,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ja 1 muu verkkosivu}other{ja # muuta verkkosivua}}</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="495170559598752135">Toiminnot</translation>
<translation id="4958444002117714549">Laajenna luettelo</translation>
@@ -396,7 +416,6 @@
<translation id="5181140330217080051">Ladataan</translation>
<translation id="5190835502935405962">Kirjanmerkkipalkki</translation>
<translation id="5199729219167945352">Kokeilut</translation>
-<translation id="5199841536747119669">Ehdotuksesi näkyvät tässä.</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="5299298092464848405">Virhe jäsennettäessä käytäntöä</translation>
@@ -404,8 +423,10 @@
<translation id="5308689395849655368">Kaatumisraportit on poistettu käytöstä.</translation>
<translation id="5317780077021120954">Tallenna</translation>
<translation id="5327248766486351172">Nimi</translation>
+<translation id="5337705430875057403">Sivustoon <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 ohjelmistoja tai paljastamaan henkilötietojasi (esimerkiksi salasanoja, puhelinnumeroita tai luottokorttitietoja).</translation>
<translation id="5359637492792381994">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />, sillä sen suojausvarmenne ei ole tällä hetkellä voimassa. Tämä voi johtua määritysvirheestä tai hyökkäyksestä, jonka tavoitteena on verkkoyhteytesi sieppaaminen. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">Käytännön asetuksien tallentaminen epäonnistui</translation>
+<translation id="5386426401304769735">Tämän sivuston varmenneketju sisältää varmenteen, joka on allekirjoitettu SHA-1:llä.</translation>
<translation id="5421136146218899937">Poista selaustiedot...</translation>
<translation id="5430298929874300616">Poista kirjanmerkki</translation>
<translation id="5431657950005405462">Tiedostoasi ei löydy</translation>
@@ -426,17 +447,20 @@
<translation id="5544037170328430102">Viesti upotetulta sivulta osoitteessa <ph name="SITE" />:</translation>
<translation id="5556459405103347317">Lataa uudelleen</translation>
<translation id="5565735124758917034">Aktiivinen</translation>
+<translation id="5572851009514199876">Aloita ja kirjaudu sisään, jotta Chrome voi tarkistaa, onko sinulla oikeus käyttää tätä sivustoa.</translation>
+<translation id="5580958916614886209">Tarkista vanhentumiskuukausi ja yritä uudelleen.</translation>
<translation id="560412284261940334">Hallintaa ei tueta</translation>
<translation id="5610142619324316209">Tarkista yhteys.</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> uudelleenohjasi sinut liian monta kertaa.</translation>
<translation id="5622887735448669177">Haluatko poistua tältä sivustolta?</translation>
<translation id="5629630648637658800">Käytännön asetuksien lataaminen epäonnistui</translation>
<translation id="5631439013527180824">Laitteenhallintatunnus on virheellinen</translation>
+<translation id="5669703222995421982">Hanki räätälöityä sisältöä</translation>
+<translation id="5675650730144413517">Sivu ei toimi</translation>
<translation id="5677928146339483299">Estetty</translation>
<translation id="5694783966845939798">Yritit muodostaa yhteyden verkkotunnukseen <ph name="DOMAIN" />, mutta palvelimen esittämä varmenne käyttää heikkoa allekirjoitusalgoritmia (esim. SHA-1). Palvelimen esittämät tunnistetiedot voivat olla väärennettyjä, eikä palvelin välttämättä ole tavoittelemasi palvelin (saatat olla tekemisissä hakkerin kanssa). <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">Tämän sivuston identiteettiä ei ole vahvistettu.</translation>
<translation id="5720705177508910913">Nykyinen käyttäjä</translation>
-<translation id="572328651809341494">Hiljattain suljetut välilehdet</translation>
<translation id="5732392974455271431">Vanhempasi voivat kumota eston puolestasi.</translation>
<translation id="5784606427469807560">Korttia vahvistettaessa tapahtui virhe. Tarkista internetyhteys ja yritä uudelleen.</translation>
<translation id="5785756445106461925">Tällä sivulla on kuitenkin muita osia, jotka eivät ole suojattuja. Muut voivat tarkastella näitä osia siirron aikana, ja hyökkääjä voi muuttaa sivun ulkoasua muokkaamalla näitä osia.</translation>
@@ -445,6 +469,7 @@
<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="5843436854350372569">Yritit muodostaa yhteyden verkkotunnukseen <ph name="DOMAIN" />, mutta palvelimen esittämä varmenne sisältää heikon avaimen. Hakkeri on ehkä murtanut yksityisen avaimen, eikä palvelin välttämättä ole tavoittelemasi palvelin (saatat olla tekemisissä hakkerin kanssa). <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">Uusi kansio</translation>
<translation id="5869405914158311789">Sivustoon ei saada yhteyttä</translation>
@@ -454,6 +479,7 @@
<translation id="59107663811261420">Google Payments ei tue tämän tyyppisen kortin käyttämistä ostoksien tekemiseen tältä myyjältä. Valitse eri kortti.</translation>
<translation id="59174027418879706">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="5966707198760109579">Viikko</translation>
<translation id="5967867314010545767">Poista historiasta</translation>
<translation id="5975083100439434680">Loitonna</translation>
@@ -472,8 +498,11 @@
<translation id="614940544461990577">Kokeile seuraavia toimenpiteitä:</translation>
<translation id="6151417162996330722">Palvelimen varmenteen voimassaoloaika on liian pitkä.</translation>
<translation id="6165508094623778733">Lisätietoja</translation>
+<translation id="6177128806592000436">Sivustoon ei ole muodostettu turvallista yhteyttä.</translation>
<translation id="6203231073485539293">Tarkista internetyhteytesi</translation>
<translation id="6218753634732582820">Poistetaanko osoite Chromiumista?</translation>
+<translation id="6251924700383757765">Tietosuojakäytäntö</translation>
+<translation id="625755898061068298">Olet poistanut tämän sivuston tietoturvavaroitukset käytöstä.</translation>
<translation id="6259156558325130047">&amp;Toista uudelleenjärjestely</translation>
<translation id="6263376278284652872">Verkkotunnuksen <ph name="DOMAIN" /> kirjanmerkit</translation>
<translation id="6264485186158353794">Takaisin suojaukseen</translation>
@@ -482,6 +511,7 @@
<translation id="6305205051461490394">Sivustoon <ph name="URL" /> ei saada yhteyttä.</translation>
<translation id="6321917430147971392">Tarkista DNS-asetukset</translation>
<translation id="6328639280570009161">Kokeile verkon ennakoinnin poistamista käytöstä.</translation>
+<translation id="6328786501058569169">Tämä sivusto on petollinen</translation>
<translation id="6337534724793800597">Suodata käytäntöjä nimen mukaan</translation>
<translation id="6342069812937806050">Valmistui juuri</translation>
<translation id="6345221851280129312">tuntematon koko</translation>
@@ -490,6 +520,8 @@
<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="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>
<translation id="6416403317709441254">Et voi juuri nyt siirtyä sivustolle <ph name="SITE" />, sillä se lähetti sekoitettuja tunnistetietoja, joita Chromium ei voi käsitellä. Verkkovirheet ja ‑hyökkäykset ovat yleensä väliaikaisia, joten voit todennäköisesti vierailla sivustolla myöhemmin. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">Ei voida tarkistaa, onko varmenne kumottu.</translation>
@@ -499,10 +531,12 @@
<translation id="6458467102616083041">Ohitettu, koska oletushaku on poistettu käytöstä käytännön mukaan.</translation>
<translation id="6462969404041126431">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />. Sen suojausvarmenne on ehkä peruutettu. Tämä voi johtua määritysvirheestä tai hyökkäyksestä, jonka tavoitteena on verkkoyhteytesi sieppaaminen. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<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="6529602333819889595">&amp;Toista poisto</translation>
<translation id="6534179046333460208">Fyysisen webin ehdotukset</translation>
<translation id="6550675742724504774">Asetukset</translation>
+<translation id="6556239504065605927">Suojattu yhteys</translation>
<translation id="6563469144985748109">Ylläpitäjä ei ole hyväksynyt sitä vielä.</translation>
<translation id="6593753688552673085">alle <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Salausasetukset</translation>
@@ -511,7 +545,6 @@
<translation id="6644283850729428850">Tämä käytäntö on vanhentunut.</translation>
<translation id="6652240803263749613">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />, sillä tietokoneesi käyttöjärjestelmä ei luota sen suojausvarmenteeseen. Tämä voi johtua määritysvirheestä tai hyökkäyksestä, jonka tavoitteena on verkkoyhteytesi sieppaaminen. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">Muokkaa kansiota</translation>
-<translation id="6660210980321319655">Ilmoitus tehtiin automaattisesti <ph name="CRASH_TIME" />.</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>
@@ -523,6 +556,7 @@
<translation id="6753269504797312559">Käytännön arvo</translation>
<translation id="6757797048963528358">Laitteesi siirtyi virransäästötilaan.</translation>
<translation id="6778737459546443941">Vanhempasi ei ole hyväksynyt sitä vielä.</translation>
+<translation id="6810899417690483278">Muokkaustunnus</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Verkkosivusto osoitteessa <ph name="URL" /> ei ole juuri nyt käytettävissä. Se saattaa olla ylikuormitettu tai suljettuna ylläpitotoimien vuoksi.</translation>
<translation id="6831043979455480757">Käännä</translation>
@@ -543,6 +577,8 @@
<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>
+<translation id="7064851114919012435">Yhteystiedot</translation>
+<translation id="7079718277001814089">Tämä sivusto sisältää haittaohjelmia</translation>
<translation id="7087282848513945231">Kunta</translation>
<translation id="7088615885725309056">Vanhempi</translation>
<translation id="7090678807593890770">Tee Google-haku: <ph name="LINK" /></translation>
@@ -557,6 +593,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> ei noudata tietoturvastandardeja.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Lisätietoja<ph name="END_LINK" /> ongelmasta.</translation>
<translation id="7219179957768738017">Yhteys käyttää protokollaa <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Seuraava verkkosivusto sisältää haittaohjelmia</translation>
<translation id="724975217298816891">Päivitä kortin <ph name="CREDIT_CARD" /> tiedot antamalla sen CVC ja vanhenemispäivämäärä. Vahvistamisen jälkeen korttisi tiedot jaetaan sivuston kanssa.</translation>
<translation id="725866823122871198">Verkkotunnukseen <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ei voi muodostaa salattua yhteyttä, koska tietokoneesi aika ja päivämäärä (<ph name="DATE_AND_TIME" />) ovat virheelliset.</translation>
<translation id="7269802741830436641">Tällä verkkosivulla on uudelleenohjaussilmukka</translation>
@@ -612,6 +649,7 @@ Psst! Suosittelemme käyttämään ensi kerralla incognito-tilaa <ph name="SHORT
<translation id="7658239707568436148">Peruuta</translation>
<translation id="7667346355482952095">Palautettu käytäntötunnus on tyhjä tai ei vastaa nykyistä tunnusta.</translation>
<translation id="7668654391829183341">Tuntematon laite</translation>
+<translation id="7669271284792375604">Tämän sivuston hyökkääjät saattavat yrittää huijata sinua asentamaan ohjelmia, jotka ovat haitallisia selauskokemuksellesi (esimerkiksi vaihtamalla aloitussivusi tai näyttämällä ylimääräisiä mainoksia käymilläsi sivustoilla).</translation>
<translation id="7674629440242451245">Oletko kiinnostunut Chromen uusista jännittävistä ominaisuuksista? Kokeile kehittäjäkanavaa osoitteessa chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Siirry sivustoon <ph name="SITE" /> (tämä ei ole turvallista)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Tätä sivustoa ei voi ladata välimuistista</translation>
@@ -627,14 +665,17 @@ Psst! Suosittelemme käyttämään ensi kerralla incognito-tilaa <ph name="SHORT
<translation id="7800304661137206267">Tämä yhteys on salattu käyttäen algoritmia <ph name="CIPHER" />, viestit todennetaan menetelmällä <ph name="MAC" /> ja avainvaihtomekanismi on toteutettu menetelmällä <ph name="KX" />.</translation>
<translation id="780301667611848630">Ei kiitos</translation>
<translation id="7805768142964895445">Tila</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Poistetaanko lomake-ehdotus Chromen tiedoista?</translation>
<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="7887683347370398519">Tarkista CVC ja yritä uudelleen.</translation>
<translation id="7912024687060120840">Kansiossa:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Palvelimen varmenne ei ole vielä voimassa.</translation>
<translation id="7942349550061667556">Punainen</translation>
+<translation id="7947285636476623132">Tarkista vanhentumisvuosi ja yritä uudelleen.</translation>
<translation id="7951415247503192394">(32-bittinen)</translation>
<translation id="7956713633345437162">Mobiilikirjanmerkit</translation>
<translation id="7961015016161918242">Ei koskaan</translation>
@@ -642,7 +683,10 @@ Psst! Suosittelemme käyttämään ensi kerralla incognito-tilaa <ph name="SHORT
<translation id="7983301409776629893">Käännä aina kielestä <ph name="ORIGINAL_LANGUAGE" /> kielelle <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Ei määritetty</translation>
<translation id="8012647001091218357">Emme tavoittaneet vanhempiasi. Yritä uudelleen.</translation>
+<translation id="8025119109950072390">Tälle sivustolle hyökännyt taho voi yrittää huijata sinua tekemään jotain vaarallista, kuten asentamaan ohjelmistoja tai paljastamaan henkilötietojasi (esimerkiksi salasanoja, puhelinnumeroita tai luottokorttitietoja).</translation>
+<translation id="803030522067524905">Googlen selaussuoja havaitsi äskettäin tietojenkalasteluyrityksen sivustolla <ph name="SITE" />. Tietojenkalastelusivustot yrittävät huijata käyttäjiä tekeytymällä toisiksi sivustoiksi. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">Sivu on kirjoitettu kielellä <ph name="SOURCE_LANGUAGE" />. Haluatko kääntää sen kielelle <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Lähetä palautetta</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>
@@ -653,9 +697,11 @@ Psst! Suosittelemme käyttämään ensi kerralla incognito-tilaa <ph name="SHORT
<translation id="8150722005171944719">Tiedosto osoitteessa <ph name="URL" /> ei ole luettavissa. Se on voitu poistaa tai siirtää, tai tiedoston käyttöluvat voivat estää sen käytön.</translation>
<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="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="8230421197304563332">Sivustoon <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> hyökännyt taho voi yrittää asentaa tietokoneeseesi vaarallisia ohjelmia, jotka varastavat tai poistavat tietojasi, kuten kuviasi, salasanojasi, viestejäsi ja luottokorttiesi tietoja.</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="8249320324621329438">Viimeksi haettu:</translation>
<translation id="8261506727792406068">Poista</translation>
@@ -664,11 +710,13 @@ Psst! Suosittelemme käyttämään ensi kerralla incognito-tilaa <ph name="SHORT
<translation id="8294431847097064396">Lähde</translation>
<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="8349305172487531364">Kirjanmerkkipalkki</translation>
<translation id="8363502534493474904">Poista lentokonetila käytöstä.</translation>
<translation id="8364627913115013041">Ei määritetty.</translation>
<translation id="8380941800586852976">Vaarallinen</translation>
<translation id="8382348898565613901">Viimeksi käyttämäsi kirjanmerkit näkyvät tässä.</translation>
+<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="8428213095426709021">Asetukset</translation>
@@ -680,9 +728,9 @@ Psst! Suosittelemme käyttämään ensi kerralla incognito-tilaa <ph name="SHORT
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ei vastannut riittävän nopeasti.</translation>
<translation id="852346902619691059">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />, sillä laitteesi käyttöjärjestelmä ei luota sen suojausvarmenteeseen. Tämä voi johtua määritysvirheestä tai hyökkäyksestä, jonka tavoitteena on verkkoyhteytesi sieppaaminen. <ph name="BEGIN_LEARN_MORE_LINK" />Lisätietoja<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Google Payments ei tue tämän tyyppistä korttia. Valitse eri kortti.</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="8553075262323480129">Käännös epäonnistui, sillä sivun kieltä ei voitu määrittää.</translation>
<translation id="8559762987265718583">Verkkotunnukseen <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ei voi muodostaa salattua yhteyttä, koska laitteesi aika ja päivämäärä (<ph name="DATE_AND_TIME" />) ovat virheelliset.</translation>
-<translation id="856992080682148">Tämän sivuston varmenne vanhenee vuonna 2017 tai myöhemmin, ja varmenneketju sisältää varmenteen, joka on allekirjoitettu SHA-1:llä.</translation>
<translation id="8571890674111243710">Käännetään sivua kielelle <ph name="LANGUAGE" />...</translation>
<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>
@@ -695,10 +743,12 @@ Psst! Suosittelemme käyttämään ensi kerralla incognito-tilaa <ph name="SHORT
<translation id="8740359287975076522">Sivuston <ph name="HOST_NAME" /> &lt;abbr id="dnsDefinition"&gt;DNS-osoitetta&lt;/abbr&gt; ei löydy. Ongelmaa diagnosoidaan.</translation>
<translation id="8790007591277257123">&amp;Toista poisto</translation>
<translation id="8798099450830957504">Oletus</translation>
+<translation id="8800988563907321413">Nearby-ehdotuksesi näkyvät tässä.</translation>
<translation id="8804164990146287819">Tietosuojakäytäntö</translation>
<translation id="8820817407110198400">Kirjanmerkit</translation>
<translation id="8834246243508017242">Ota käyttöön automaattinen täyttö kontaktien avulla…</translation>
<translation id="883848425547221593">Muut kirjanmerkit</translation>
+<translation id="884264119367021077">Toimitusosoite</translation>
<translation id="884923133447025588">Kumoamisjärjestelmää ei löytynyt.</translation>
<translation id="885730110891505394">Googlen kanssa jakaminen</translation>
<translation id="8866481888320382733">Virhe jäsennettäessä käytännön asetuksia</translation>
@@ -716,18 +766,21 @@ Psst! Suosittelemme käyttämään ensi kerralla incognito-tilaa <ph name="SHORT
<translation id="8971063699422889582">Palvelimen varmenne on vanhentunut.</translation>
<translation id="8987927404178983737">Kuukausi</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Sivusto sisältää haitallisia ohjelmia</translation>
<translation id="9001074447101275817">Välityspalvelin <ph name="DOMAIN" /> vaatii käyttäjänimen ja salasanan.</translation>
<translation id="901974403500617787">Koko järjestelmään vaikuttavat merkinnät voi tehdä vain omistaja: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Sivu on käännetty kielelle <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Suojausvirhe</translation>
<translation id="9038649477754266430">Ennakointipalvelun avulla voit ladata sivuja nopeammin</translation>
<translation id="9039213469156557790">Tällä sivulla on kuitenkin muita osia, jotka eivät ole suojattuja. Muut voivat tarkastella näitä osia siirron aikana, ja hyökkääjä voi muuttaa sivun käyttäytymistä muokkaamalla näitä osia.</translation>
+<translation id="9040185888511745258">Sivuston <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> hyökkääjät saattavat yrittää huijata sinua asentamaan ohjelmia, jotka ovat haitallisia selauskokemuksellesi (esimerkiksi vaihtamalla aloitussivusi tai näyttämällä ylimääräisiä mainoksia käymilläsi sivustoilla).</translation>
<translation id="9050666287014529139">Tunnuslause</translation>
<translation id="9065203028668620118">Muokkaa</translation>
<translation id="9068849894565669697">Valitse väri</translation>
<translation id="9076283476770535406">Se saattaa sisältää vain aikuisille tarkoitettua sisältöä.</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" />-sivu ei toimi</translation>
<translation id="9103872766612412690"><ph name="SITE" /> suojaa tietosi normaalisti salauksen avulla. Kun Chromium yritti tällä kertaa yhdistää sivustoon <ph name="SITE" />, sivusto palautti epätavalliset ja virheelliset kirjautumistiedot. Hyökkääjä saattaa yrittää esiintyä sivustona <ph name="SITE" />, tai Wi-Fi-kirjautumisruutu on keskeyttänyt yhteyden. Tietosi ovat edelleen turvassa, sillä Chromium katkaisi yhteyden, ennen kuin mitään tietoja vaihdettiin.</translation>
<translation id="9137013805542155359">Näytä alkuperäinen</translation>
+<translation id="9137248913990643158">Aloita ja kirjaudu sisään Chromeen ennen tämän sovelluksen käyttämistä.</translation>
<translation id="9148507642005240123">K&amp;umoa muokkaus</translation>
<translation id="9157595877708044936">Valmistellaan...</translation>
<translation id="9170848237812810038">K&amp;umoa</translation>
@@ -740,7 +793,6 @@ Psst! Suosittelemme käyttämään ensi kerralla incognito-tilaa <ph name="SHORT
<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="962701380617707048">Päivitä kortin <ph name="CREDIT_CARD" /> tiedot antamalla sen CVC ja vanhenemispäivämäärä.</translation>
<translation id="969892804517981540">Virallinen koontiversio</translation>
<translation id="988159990683914416">Kehittäjän koontiversio</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 12fc3b8eba1..16940cf35a2 100644
--- a/chromium/components/strings/components_strings_fil.xtb
+++ b/chromium/components/strings/components_strings_fil.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Muling kumonekta sa Wi-Fi</translation>
<translation id="1175364870820465910">&amp;I-print...</translation>
<translation id="1181037720776840403">Alisin</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Awtomatikong iulat<ph name="END_WHITEPAPER_LINK" /> ang mga detalye ng mga posibleng insidente ng seguridad sa Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Susunod</translation>
<translation id="1201895884277373915">Higit pa mula sa site na ito</translation>
<translation id="1206967143813997005">Hindi magandang paunang signature</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cyan</translation>
<translation id="1629803312968146339">Gusto mo bang i-save ng Chrome ang card na ito?</translation>
<translation id="1640180200866533862">Mga patakaran ng user</translation>
+<translation id="1640244768702815859">Subukang <ph name="BEGIN_LINK" />bisitahin ang homepage ng site<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Di-wasto ang configuration ng network at hindi maaaring i-import.</translation>
<translation id="1644574205037202324">History</translation>
<translation id="1645368109819982629">Hindi sinusuportahang protocol</translation>
<translation id="1676269943528358898">Karaniwang gumagamit ang <ph name="SITE" /> ng pag-encrypt upang protektahan ang iyong impormasyon. Noong sinubukang kumonekta ng Chrome sa <ph name="SITE" /> sa pagkakataong ito, nagbalik ang website ng mga hindi pangkaraniwan at maling kredensyal. Maaari itong mangyari kapag sinusubukan ng isang attacker na magpanggap bilang <ph name="SITE" />, o naputol ang koneksyon dahil sa isang screen ng pag-sign in sa Wi-Fi. Secure pa rin ang iyong impormasyon dahil inihinto ng Google Chrome ang koneksyon bago magkaroon ng palitan ng anumang data.</translation>
+<translation id="168328519870909584">Ang mga umaatakeng kasalukuyang nasa <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ay maaaring magtangkang mag-install ng mapapanganib na app sa iyong device na nagnanakaw o nagde-delete ng iyong impormasyon (halimbawa, mga larawan, password, mensahe at credit card).</translation>
<translation id="168841957122794586">Naglalaman ang server certificate ng isang mahinang cryptographic key.</translation>
-<translation id="1701955595840307032">Kumuha ng iminumungkahing content</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Kailangan mo ng pahintulot mula kay <ph name="NAME" /> upang mabisita ang site na ito</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Numero ng page</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Subukang patakbuhin ang Windows Network Diagnostics<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Mangyaring i-update ang iyong passphrase ng pag-sync.</translation>
+<translation id="1787142507584202372">Lalabas dito ang iyong mga bukas na tab</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Kamakailan lang, <ph name="BEGIN_LINK" />nakakita ng malware<ph name="END_LINK" /> ang Google Safe Browsing sa <ph name="SITE" />. Nagkakaroon ng malware paminsan-minsan ang mga website na karaniwang ligtas. Nanggagaling ang nakakahamak na content sa <ph name="SUBRESOURCE_HOST" />, isang kilalang distributor ng malware. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Di-wastong kahilingan o mga parameter ng kahilingan</translation>
+<translation id="1834321415901700177">Naglalaman ng mga mapanirang program ang site na ito</translation>
<translation id="1838667051080421715">Tinitingnan mo ang pinagmumulan ng isang web page.</translation>
<translation id="1871208020102129563">Nakatakda ang proxy upang gumamit ng mga hindi nababagong proxy server, hindi ng isang .pac script URL.</translation>
<translation id="1883255238294161206">Tiklupin ang listahan</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Mga Bookmark sa Mobile</translation>
<translation id="2148716181193084225">Ngayon</translation>
-<translation id="2149973817440762519">I-edit ang Bookmark</translation>
<translation id="2154054054215849342">Hindi available ang pag-sync para sa iyong domain</translation>
<translation id="2166049586286450108">Ganap na Access ng Admin</translation>
<translation id="2166378884831602661">Hindi makakapagbigay ng secure na koneksyon ang site na ito</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Hindi mo mabibisita ang <ph name="SITE" /> sa ngayon dahil gumagamit ang website ng HSTS. Kadalasang pansamantala lang ang mga error at atake sa network, kaya malamang na gagana ang page na ito sa ibang pagkakataon. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Binalewalang di-wastong bookmark sa index <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Iba pang mga bookmark</translation>
+<translation id="2355395290879513365">Maaaring makita ng mga umaatake ang mga larawang tinitingnan mo sa site na ito at lilinlangin ka sa pamamagitan ng pagbago sa mga ito.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Ipakita ang mga patakarang walang nakatakdang halaga</translation>
<translation id="2396249848217231973">&amp;I-undo ang pagtanggal</translation>
<translation id="2455981314101692989">Hindi pinagana ng webpage na ito ang awtomatikong pagpuno para sa form na ito.</translation>
+<translation id="2460160116472764928">Kamakailan lang, <ph name="BEGIN_LINK" />nakakita ng malware<ph name="END_LINK" /> ang Google Safe Browsing sa <ph name="SITE" />.Nagkakaroon ng malware paminsan-minsan ang mga website na karaniwang ligtas. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Punan</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Magpatakbo ng Network Diagnostics<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Di-wastong URL ng paghahanap.</translation>
@@ -160,7 +166,8 @@
<translation id="2653659639078652383">Isumite</translation>
<translation id="2674170444375937751">Sigurado ka bang nais mong tanggalin ang mga pahinang ito mula sa iyong history?</translation>
<translation id="2677748264148917807">Umalis</translation>
-<translation id="269990154133806163">Nagpakita ang server ng certificate na hindi ibinunyag sa publiko gamit ang patakaran sa Transparency ng Certificate. Kinakailangan ito para sa ilang certificate, upang siguraduhing mapagkakatiwalaan ang mga ito at upang maprotektahan laban sa mga nang-aatake. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="269990154133806163">Nagpakita ang server ng certificate na hindi inihayag sa publiko gamit ang patakaran sa Transparency ng Certificate. Kinakailangan ito para sa ilang certificate, upang siguraduhing mapagkakatiwalaan ang mga ito at upang maprotektahan laban sa mga nang-aatake. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Listahan ng Babasahin</translation>
<translation id="2704283930420550640">Hindi tumutugma ang format sa halaga.</translation>
<translation id="2704951214193499422">Hindi nakumpirma ng Chromium ang iyong card sa pagkakataong ito. Pakisubukang muli sa ibang pagkakataon.</translation>
<translation id="2705137772291741111">Hindi mabasa ang naka-save (naka-cache) na kopya ng site na ito.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Tinangka mong makakonekta sa <ph name="DOMAIN" />, ngunit ang ipinakitang certificate ng server ay binawi ng nagbigay. Nangangahulugan ito na malinaw na hindi dapat pagkatiwalaan ang mga kredensyal na panseguridad na ipinakita ng server. Maaaring nakikipag-ugnayan ka sa isang nang-aatake. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Humingi ng pahintulot</translation>
<translation id="2713444072780614174">Puti</translation>
+<translation id="2720342946869265578">Malapit</translation>
<translation id="2721148159707890343">Matagumpay ang kahilingan</translation>
<translation id="2728127805433021124">Nilagdaan ang certificate ng server gamit ang mahinang algorithm ng lagda.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Magpatakbo ng Connectivity Diagnostics<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Maaari mong i-disable ang anumang mga proxy na naka-configure para sa isang koneksyon mula sa pahina ng mga setting.</translation>
<translation id="2955913368246107853">Isara ang bar sa paghahanap</translation>
<translation id="2958431318199492670">Hindi sumusunod ang configuration ng network sa pamantayan ng ONC. Hindi maaaring i-import ang mga bahagi ng configuration.</translation>
+<translation id="29611076221683977">Maaaring subukan ng 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 nagnanakaw o nagde-delete ng iyong impormasyon (halimbawa, mga larawan, password, mensahe at credit card).</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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Magtago ng mga detalye</translation>
<translation id="3587482841069643663">Lahat</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Hindi sinusuportahan ng server ang extension ng muling pagsasaayos ng TLS.</translation>
<translation id="36224234498066874">Clear Browsing Data...</translation>
<translation id="362276910939193118">Ipakita ang Buong History</translation>
<translation id="3623476034248543066">Ipakita ang halaga</translation>
@@ -271,19 +279,20 @@
<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="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="3696411085566228381">wala</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">Kumakarga...</translation>
+<translation id="370665806235115550">Naglo-load...</translation>
<translation id="3712624925041724820">Naubos na ang mga lisensya</translation>
<translation id="3714780639079136834">I-on ang mobile data o Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Suriin ang configuration ng proxy, firewall at DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Kung nauunawaan mo ang mga peligro sa iyong seguridad, maaari mong <ph name="BEGIN_LINK" />bisitahin ang hindi ligtas na site na ito<ph name="END_LINK" /> bago maalis ang mga mapanganib na program.</translation>
<translation id="3739623965217189342">Link na kinopya mo</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="3788090790273268753">Mag-e-expire ang certificate para sa site na ito sa 2016, at naglalaman ang chain ng certificate ng isang certificate na naka-sign gamit ang SHA-1.</translation>
<translation id="382518646247711829">Kung gumagamit ka ng proxy server...</translation>
<translation id="3828924085048779000">Hindi pinapayagan ang walang laman na passphrase.</translation>
<translation id="3845539888601087042">Ipinapakita ang history mula sa iyong mga naka-sign in na device. <ph name="BEGIN_LINK" />Matuto nang higit pa<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Key "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Hindi sinusuportahan ng client at server ang isang karaniwang bersyon o cipher suite ng SSL protocol.</translation>
<translation id="4079302484614802869">Nakatakda ang configuration ng proxy upang gumamit ng isang .pac script URL, hindi ng mga hindi nababagong proxy server.</translation>
+<translation id="4098354747657067197">May malapit na nakakapanlinlang na site</translation>
<translation id="4103249731201008433">Di-wasto ang serial number ng device</translation>
<translation id="4103763322291513355">Bisitahin ang &lt;strong&gt;chrome://policy&lt;/strong&gt; upang makita ang listahan ng mga naka-blacklist na URL at iba pang mga patakaran na ipinapatupad ng iyong system administrator.</translation>
<translation id="4110615724604346410">Hindi mapatunayan ng server na ito na ito ang <ph name="DOMAIN" />; naglalaman ng mga error ang certificate na panseguridad nito. Maaaring sanhi ito ng maling pag-configure o ng isang nang-aatake na humahadlang sa iyong koneksyon. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{wala}=1{1 app ($1)}=2{2 app ($1, $2)}one{# app ($1, $2, $3)}other{# na app ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Mga Pag-crash</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Subukang magpatakbo ng Network Diagnostics<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Hindi ganap na ligtas ang iyong koneksyon sa site na ito</translation>
<translation id="4250680216510889253">Hindi</translation>
<translation id="425582637250725228">Maaaring hindi ma-save ang mga pagbabagong ginawa mo.</translation>
<translation id="4258748452823770588">Maling lagda</translation>
<translation id="4269787794583293679">(Walang username)</translation>
+<translation id="4280429058323657511">, exp <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Kamakailan lang, <ph name="BEGIN_LINK" />nakakita ng mga nakakapinsalang program<ph name="END_LINK" /> ang Google Safe Browsing sa <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Mga suhestyon ng magulang</translation>
<translation id="4304224509867189079">Mag-log In</translation>
<translation id="432290197980158659">Nagpakita ang server ng isang certificate na hindi tumutugma sa mga inaasahang built-in. Ang mga inaasahang ito ay isinama para sa ilang partikular na website na mahigpit sa seguridad upang maprotektahan ka. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Hindi nahanap ang artikulo</translation>
+<translation id="4326324639298822553">Tingnan ang iyong petsa ng pag-expire at subukang muli</translation>
<translation id="4331708818696583467">Hindi Secure</translation>
+<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="4381091992796011497">User Name:</translation>
<translation id="4394049700291259645">Huwag paganahin</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Tinangka mong makakonekta sa <ph name="DOMAIN" />, ngunit nagpakita ang server ng di-wastong certificate. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Mga tab mula sa iba pang mga device</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Isang page ng extension ang iyong tinitingnan.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">I-reload ang mga patakaran</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4744603770635761495">Naipapatupad na Path</translation>
+<translation id="4750917950439032686">Pribado ang iyong impormasyon (halimbawa, mga password o credit card number) kapag ipinadala ito sa site na ito.</translation>
<translation id="4756388243121344051">&amp;History</translation>
+<translation id="4759118997339041434">Naka-disable ang pag-autofill sa pagbabayad</translation>
<translation id="4764776831041365478">Ang webpage sa <ph name="URL" /> ay maaaring pansamantalang hindi gumagana o maaaring permanente itong inilipat sa isang bagong web address.</translation>
<translation id="4771973620359291008">Isang hindi alam na error ang nangyari.</translation>
<translation id="4800132727771399293">Tingnan ang iyong petsa ng pag-expire at CVC at subukang muli</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Error sa network</translation>
<translation id="4816492930507672669">Pagkasyahin sa pahina</translation>
<translation id="4850886885716139402">View</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Mga Pagkilos</translation>
<translation id="4958444002117714549">Palawakin ang listahan</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Nagda-download</translation>
<translation id="5190835502935405962">Bar ng Mga Bookmark</translation>
<translation id="5199729219167945352">Mga Eksperimento</translation>
-<translation id="5199841536747119669">Lalabas dito ang lahat ng iyong suhestyon</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="5299298092464848405">Error sa pag-parse ng patakaran</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Hindi pinagana ang pag-uulat ng pag-crash.</translation>
<translation id="5317780077021120954">I-save</translation>
<translation id="5327248766486351172">Pangalan</translation>
+<translation id="5337705430875057403">Maaaring subukan ng mga attacker sa <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> na linlangin ka sa paggawa ng bagay na mapanganib gaya ng pag-i-install ng software o pagbubunyag ng iyong personal na impormasyon (halimbawa, mga password, numero ng telepono o credit card).</translation>
<translation id="5359637492792381994">Hindi mapatunayan ng server na ito na ito ang <ph name="DOMAIN" />; walang bisa ang certificate na panseguridad nito sa ngayon. Maaaring sanhi ito ng maling pag-configure o ng isang nang-aatake na humahadlang sa iyong koneksyon. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Nabigo i-load ang mga setting ng patakaran sa store</translation>
+<translation id="5386426401304769735">Naglalaman ang chain ng certificate para sa site na ito ng certificate na naka-sign gamit ang SHA-1.</translation>
<translation id="5421136146218899937">I-clear ang data sa pagba-browse...</translation>
<translation id="5430298929874300616">Alisin ang bookmark</translation>
<translation id="5431657950005405462">Hindi nakita ang iyong file</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Isinasaad ng isang naka-embed na page sa <ph name="SITE" /> na:</translation>
<translation id="5556459405103347317">I-reload</translation>
<translation id="5565735124758917034">Aktibo</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>
+<translation id="5580958916614886209">Tingnan ang iyong buwan ng pag-expire at subukang muli</translation>
<translation id="560412284261940334">Hindi sinusuportahan ang pamamahala</translation>
<translation id="5610142619324316209">Suriin ang koneksyon</translation>
<translation id="5617949217645503996">Masyadong maraming beses kang na-redirect ng <ph name="HOST_NAME" />.</translation>
<translation id="5622887735448669177">Gusto mo bang umalis sa site na ito?</translation>
<translation id="5629630648637658800">Nabigong i-load ang mga setting ng patakaran</translation>
<translation id="5631439013527180824">Di-wastong token sa pamamahala ng device</translation>
+<translation id="5669703222995421982">Makakuha ng naka-personalize na content</translation>
+<translation id="5675650730144413517">Hindi gumagana ang page na ito</translation>
<translation id="5677928146339483299">Naka-block</translation>
<translation id="5694783966845939798">Sinubukan mong makakonekta sa <ph name="DOMAIN" />, ngunit ang certificate na ipinakita ng server ay nilagdaan gamit ang mahinang signature algorithm (gaya ng SHA-1). Nangangahulugan ito na maaaring napeke ang kredensyal na panseguridad na ipinakita ng server, at maaaring ang server ay hindi ang inaasahan mong server (maaaring nakikipag-ugnayan ka sa isang attacker). <ph name="BEGIN_LEARN_MORE_LINK" />Matuto pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Ang pagkilala ng website na ito ay hindi natukoy.</translation>
<translation id="5720705177508910913">Kasalukuyang user</translation>
-<translation id="572328651809341494">Mga kamakailang tab</translation>
<translation id="5732392974455271431">Maaari itong alisin sa pagkaka-block ng iyong mga magulang para sa iyo</translation>
<translation id="5784606427469807560">Nagkaroon ng problema sa pagkumpirma ng iyong card. Suriin ang koneksyon sa internet at subukang muli.</translation>
<translation id="5785756445106461925">Bukod pa rito, ang page na ito ay may iba pang mga mapagkukunang hindi secure. Makikita ng iba ang mga mapagkukunang ito habang ipinadadala, at maaaring baguhin ng isang umaatake upang baguhin ang hitsura ng page.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Tinangka mong makakonekta sa <ph name="DOMAIN" />, ngunit nagpakita ang server ng certificate na naglalaman ng isang mahinang key. Maaaring sinira ng isang nang-aatake ang pribadong key, at maaaring ang server ay hindi ang inaasahan mong server (maaaring nakikipag-ugnayan ka sa isang nang-aatake). <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Bagong Folder</translation>
<translation id="5869405914158311789">Hindi makakonekta sa site na ito</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Ang ganitong uri ng card ay hindi sinusuportahan ng Google Payments para sa merchant na ito. Mangyaring pumili ng ibang card.</translation>
<translation id="59174027418879706">Naka-enable</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="5966707198760109579">Linggo</translation>
<translation id="5967867314010545767">Alisin sa history</translation>
<translation id="5975083100439434680">Mag-zoom out</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Subukang:</translation>
<translation id="6151417162996330722">Masyadong mahaba ang panahon ng pagkakaroon ng bisa ng certificate ng server.</translation>
<translation id="6165508094623778733">Matuto nang higit pa</translation>
+<translation id="6177128806592000436">Hindi ligtas ang iyong koneksyon sa site na ito</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="6251924700383757765">Patakaran sa privacy</translation>
+<translation id="625755898061068298">Pinili mong i-disable ang mga panseguridad na babala para sa site na ito.</translation>
<translation id="6259156558325130047">&amp;Gawing Muli ang Pagbabago sa Ayos</translation>
<translation id="6263376278284652872">Mga bookmark ng <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Bumalik sa safety</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">Hindi makakonekta sa <ph name="URL" />.</translation>
<translation id="6321917430147971392">Suriin ang iyong mga setting ng DNS</translation>
<translation id="6328639280570009161">Subukang i-disable ang paghula ng network</translation>
+<translation id="6328786501058569169">Mapanlinlang ang site na ito</translation>
<translation id="6337534724793800597">I-filter ang mga patakaran ayon sa pangalan</translation>
<translation id="6342069812937806050">Ngayon lang</translation>
<translation id="6345221851280129312">hindi pa natutukoy na laki</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Hindi mo mabibisita ang <ph name="SITE" /> sa ngayon dahil nagpadala ang website ng mga pinaghalu-halong kredensyal na hindi maproseso ng Chromium. Kadalasang pansamantala lang ang mga error at atake sa network, kaya malamang na gagana ang page na ito sa ibang pagkakataon. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Hindi nagawang masuri kung nabawi na ang certificate.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Binalewala dahil hindi pinagana ng patakaran ang default na paghahanap.</translation>
<translation id="6462969404041126431">Hindi mapatunayan ng server na ito na ito ang <ph name="DOMAIN" />; maaaring mabawi ang certificate na panseguridad nito. Maaaring sanhi ito ng maling pag-configure o ng isang nang-aatake na humahadlang sa iyong koneksyon. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Gawing Muli ang Pagtanggal</translation>
<translation id="6534179046333460208">Mga suhestyon sa Pisikal na Web</translation>
<translation id="6550675742724504774">Mga Pagpipilian</translation>
+<translation id="6556239504065605927">Secure na koneksyon</translation>
<translation id="6563469144985748109">Hindi pa ito inaaprubahan ng iyong manager</translation>
<translation id="6593753688552673085">wala pang <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Mga pagpipilian sa pag-encrypt</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Hindi na ginagamit ang patakarang ito.</translation>
<translation id="6652240803263749613">Hindi mapatunayan ng server na ito na ito ang <ph name="DOMAIN" />; ang certificate na panseguridad nito ay hindi pinagkakatiwalaan ng operating system ng iyong computer. Maaaring sanhi ito ng maling pag-configure o ng isang nang-aatake na humahadlang sa iyong koneksyon. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">I-edit ang Folder</translation>
-<translation id="6660210980321319655">Awtomatikong naiulat noong <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Halaga ng patakaran</translation>
<translation id="6757797048963528358">Nag-sleep ang iyong device.</translation>
<translation id="6778737459546443941">Hindi pa ito inaaprubahan ng iyong magulang</translation>
+<translation id="6810899417690483278">Customization ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Kasalukuyang hindi available ang webpage sa <ph name="URL" />. Maaaring overloaded ito o hindi pinagana para sa maintenance.</translation>
<translation id="6831043979455480757">Isalin</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Impormasyon sa pakikipag-ugnayan</translation>
+<translation id="7079718277001814089">Naglalaman ng malware ang site na ito</translation>
<translation id="7087282848513945231">County</translation>
<translation id="7088615885725309056">Mas Nauna</translation>
<translation id="7090678807593890770">Hanapin sa Google ang <ph name="LINK" /></translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423">Hindi sumusunod ang <ph name="HOST_NAME" /> sa mga pamantayan sa seguridad.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Dagdagan ang nalalaman<ph name="END_LINK" /> tungkol sa problemang ito.</translation>
<translation id="7219179957768738017">Gumagamit ng <ph name="SSL_VERSION" /> ang koneksyon.</translation>
+<translation id="724691107663265825">Naglalaman ng malware ang site</translation>
<translation id="724975217298816891">Ilagay ang petsa ng expiration at CVC para sa <ph name="CREDIT_CARD" /> upang i-update ang mga detalye ng iyong card. Kapag nagkumpirma ka na, ibabahagi ang mga detalye ng iyong card sa site na ito.</translation>
<translation id="725866823122871198">Hindi makapagtatag ng pribadong koneksyon sa <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> dahil mali ang petsa at oras ng iyong computer (<ph name="DATE_AND_TIME" />).</translation>
<translation id="7269802741830436641">Ang webpage na ito ay may loop na pag-redirect</translation>
@@ -563,7 +600,7 @@
<translation id="7298195798382681320">Inirerekomenda</translation>
<translation id="7309308571273880165">Ulat ng pag-crash na nakuha noong <ph name="CRASH_TIME" /> (humiling ng pag-upload ang user, hindi pa naa-upload)</translation>
<translation id="7334320624316649418">&amp;Gawing muli ang pagbabago sa ayos</translation>
-<translation id="733923710415886693">Ang certificate ng server ay hindi ibinunyag sa pamamagitan ng Transparency ng Certificate.</translation>
+<translation id="733923710415886693">Ang certificate ng server ay hindi inihayag sa pamamagitan ng Transparency ng Certificate.</translation>
<translation id="7351800657706554155">Hindi mo maaaring bisitahin ang <ph name="SITE" /> sa ngayon dahil binawi na ang certificate nito. Karaniwang pansamantala lang ang mga error at atake sa network, kaya malamang na gagana ang page na ito sa ibang pagkakataon. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="7353601530677266744">Command Line</translation>
<translation id="7372973238305370288">resulta ng paghahanap</translation>
@@ -611,6 +648,7 @@ Psst! Maaaring maging kapaki-pakinabang ang mode na incognito <ph name="SHORTCUT
<translation id="7658239707568436148">Ikansela</translation>
<translation id="7667346355482952095">Walang laman ang ibinalik na token ng patakaran o hindi tumutugma sa kasalukuyang token</translation>
<translation id="7668654391829183341">Hindi kilalang device</translation>
+<translation id="7669271284792375604">Maaaring subukan ng mga attacker sa site na ito na linlangin ka upang mag-install ng mga program na makakasama sa iyong karanasan sa pag-browse (halimbawa, sa pamamagitan ng pagbabago ng iyong homepage o pagpapakita ng mga karagdagang ad sa mga site na binibisita mo).</translation>
<translation id="7674629440242451245">Interesado sa mga astig at bagong tampok sa Chrome? Subukan ang aming dev channel sa chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Magpatuloy sa <ph name="SITE" /> (hindi ligtas)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Hindi maaaring i-load ang site na ito mula sa cache</translation>
@@ -626,14 +664,17 @@ Psst! Maaaring maging kapaki-pakinabang ang mode na incognito <ph name="SHORTCUT
<translation id="7800304661137206267">Na-encrypt ang koneksyon gamit ang <ph name="CIPHER" />, kasama ang <ph name="MAC" /> para sa pagpapatunay ng mensahe at <ph name="KX" /> bilang mekanismo ng pangunahing pagpapalit.</translation>
<translation id="780301667611848630">Hindi salamat</translation>
<translation id="7805768142964895445">Katayuan</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Alisin ang suhestyon sa Chrome?</translation>
<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="7887683347370398519">Tingnan ang iyong CVC at subukang muli</translation>
<translation id="7912024687060120840">Sa Folder:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Wala pang bisa ang certificate ng server.</translation>
<translation id="7942349550061667556">Pula</translation>
+<translation id="7947285636476623132">Tingnan ang iyong taon ng pag-expire at subukang muli</translation>
<translation id="7951415247503192394">(32-bit)</translation>
<translation id="7956713633345437162">Mga bookmark sa mobile</translation>
<translation id="7961015016161918242">Hindi Kailanman</translation>
@@ -641,7 +682,10 @@ Psst! Maaaring maging kapaki-pakinabang ang mode na incognito <ph name="SHORTCUT
<translation id="7983301409776629893">Palaging isalin ang <ph name="ORIGINAL_LANGUAGE" /> sa <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Hindi Tinukoy</translation>
<translation id="8012647001091218357">Hindi namin makaugnayan ang iyong mga magulang sa sandaling ito. Pakisubukang muli.</translation>
+<translation id="8025119109950072390">Maaari kang linlangin ng mga attacker sa site na ito na gumawa ng mga bagay na mapanganib tulad ng pag-i-install ng software o pagbubunyag ng iyong personal na impormasyon (halimbawa, mga password, numero ng telepono o credit card).</translation>
+<translation id="803030522067524905">Kamakailan lang, nakakita ang Google Safe Browsing ng phishing sa <ph name="SITE" />. Nagpapanggap ang mga site ng phishing bilang ibang mga website upang linlangin ka. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Nasa <ph name="SOURCE_LANGUAGE" /> ang pahinang ito. Isalin ito sa <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Magpadala ng Feedback</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>
@@ -652,9 +696,11 @@ Psst! Maaaring maging kapaki-pakinabang ang mode na incognito <ph name="SHORTCUT
<translation id="8150722005171944719">Hindi nababasa ang file sa <ph name="URL" />. Maaaring ito ay naalis, nalipat, o maaaring pinipigilan ng mga pagpapahintulot ng file ang access.</translation>
<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="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="8230421197304563332">Maaaring subukan ng 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 nagnanakaw o nagde-delete ng iyong impormasyon (halimbawa, mga larawan, password, mensahe at credit card).</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="8249320324621329438">Huling kinuha:</translation>
<translation id="8261506727792406068">Burahin</translation>
@@ -663,11 +709,13 @@ Psst! Maaaring maging kapaki-pakinabang ang mode na incognito <ph name="SHORTCUT
<translation id="8294431847097064396">Pinagmulan</translation>
<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="8349305172487531364">Bookmarks bar</translation>
<translation id="8363502534493474904">I-off ang airplane mode</translation>
<translation id="8364627913115013041">Hindi nakatakda.</translation>
<translation id="8380941800586852976">Mapanganib</translation>
<translation id="8382348898565613901">Lalabas dito ang iyong mga kamakailang binisitang bookmark</translation>
+<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="8428213095426709021">Mga Setting</translation>
@@ -679,9 +727,9 @@ Psst! Maaaring maging kapaki-pakinabang ang mode na incognito <ph name="SHORTCUT
<translation id="8498891568109133222">Masyadong matagal bago nakatugon ang <ph name="HOST_NAME" />.</translation>
<translation id="852346902619691059">Hindi mapatunayan ng server na ito na ito ang <ph name="DOMAIN" />; ang certificate na panseguridad nito ay hindi pinagkakatiwalaan ng operating system ng iyong device. Maaaring sanhi ito ng maling pag-configure o ng isang nang-aatake na humahadlang sa iyong koneksyon. <ph name="BEGIN_LEARN_MORE_LINK" />Matuto nang higit pa<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Ang ganitong uri ng card ay hindi sinusuportahan ng Google Payments. Mangyaring pumili ng ibang card.</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="8553075262323480129">Nabigo ang pag-translate dahil hindi matukoy ang wika ng pahina.</translation>
<translation id="8559762987265718583">Hindi makapagtatag ng pribadong koneksyon sa <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> dahil mali ang petsa at oras ng iyong device (<ph name="DATE_AND_TIME" />).</translation>
-<translation id="856992080682148">Mag-e-expire ang certificate para sa site na ito sa 2017 o pagkatapos pa nito, at naglalaman ang chain ng certificate ng isang certificate na naka-sign gamit ang SHA-1.</translation>
<translation id="8571890674111243710">Tina-translate ang pahina sa <ph name="LANGUAGE" />...</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>
@@ -694,10 +742,12 @@ Psst! Maaaring maging kapaki-pakinabang ang mode na incognito <ph name="SHORTCUT
<translation id="8740359287975076522">Hindi makita ang &lt;abbr id="dnsDefinition"&gt;DNS address&lt;/abbr&gt; ng <ph name="HOST_NAME" />. Dina-diagnose ang problema.</translation>
<translation id="8790007591277257123">&amp;Gawing muli ang pagtanggal</translation>
<translation id="8798099450830957504">Default</translation>
+<translation id="8800988563907321413">Lalabas dito ang iyong mga suhestyon na malapit</translation>
<translation id="8804164990146287819">Patakaran sa Privacy</translation>
<translation id="8820817407110198400">Mga Bookmark</translation>
<translation id="8834246243508017242">I-enable ang Autofill gamit ang Mga Contact…</translation>
<translation id="883848425547221593">Iba Pang Mga Bookmark</translation>
+<translation id="884264119367021077">Shipping address</translation>
<translation id="884923133447025588">Walang nahanap na mekanismo ng pagpapawalang-bisa.</translation>
<translation id="885730110891505394">Pagbabahagi sa Google</translation>
<translation id="8866481888320382733">Error sa pag-parse ng mga setting ng patakaran</translation>
@@ -715,18 +765,21 @@ Psst! Maaaring maging kapaki-pakinabang ang mode na incognito <ph name="SHORTCUT
<translation id="8971063699422889582">Nag-expire na ang certificate ng server.</translation>
<translation id="8987927404178983737">Buwan</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Ang susunod na site ay naglalaman ng mga mapanirang program</translation>
<translation id="9001074447101275817">Nangangailangan ang proxy na <ph name="DOMAIN" /> ng username at password.</translation>
<translation id="901974403500617787">Ang may-ari lang ang maaaring magtakda ng mga flag na nalalapat sa buong system: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Na-translate sa <ph name="TARGET_LANGUAGE" /> ang pahinang ito</translation>
+<translation id="9035022520814077154">Error sa seguridad</translation>
<translation id="9038649477754266430">Gumamit ng serbisyo sa paghula upang ma-load ang mga page nang mas mabilis</translation>
<translation id="9039213469156557790">Bukod pa rito, ang page na ito ay may iba pang mga mapagkukunang hindi secure. Makikita ng iba ang mga mapagkukunang ito habang ipinadadala, at maaaring baguhin ng isang umaatake upang baguhin ang gawi ng page.</translation>
+<translation id="9040185888511745258">Maaaring subukan ng mga attacker sa <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> na linlangin ka upang mag-install ng mga program na nakakasira sa iyong karanasan sa pagba-browse (halimbawa, sa pamamagitan ng pagbabago ng iyong homepage o pagpapakita ng mga karagdagang ad sa mga site na iyong binibisita).</translation>
<translation id="9050666287014529139">Passphrase</translation>
<translation id="9065203028668620118">I-edit</translation>
<translation id="9068849894565669697">Pumili ng kulay</translation>
<translation id="9076283476770535406">Maaaring mayroon itong mature content</translation>
-<translation id="9092364396508701805">Hindi gumagana ang <ph name="HOST_NAME" /> page</translation>
<translation id="9103872766612412690">Karaniwang gumagamit ang <ph name="SITE" /> ng pag-encrypt upang protektahan ang iyong impormasyon. Noong sinubukang kumonekta ng Chromium sa <ph name="SITE" /> sa pagkakataong ito, nagbalik ang website ng mga hindi pangkaraniwan at maling kredensyal. Maaari itong mangyari kapag sinusubukan ng isang attacker na magpanggap bilang <ph name="SITE" />, o naputol ang koneksyon dahil sa isang screen ng pag-sign in sa Wi-Fi. Secure pa rin ang iyong impormasyon dahil inihinto ng Chromium ang koneksyon bago magkaroon ng palitan ng anumang data.</translation>
<translation id="9137013805542155359">Ipakita ang orihinal</translation>
+<translation id="9137248913990643158">Magsimula at mag-sign in sa Chrome bago gamitin ang app na ito.</translation>
<translation id="9148507642005240123">&amp;I-undo ang pag-e-edit</translation>
<translation id="9157595877708044936">Nagse-set up...</translation>
<translation id="9170848237812810038">&amp;I-undo</translation>
@@ -739,7 +792,6 @@ Psst! Maaaring maging kapaki-pakinabang ang mode na incognito <ph name="SHORTCUT
<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="962701380617707048">Ilagay ang petsa ng expiration at CVC para sa <ph name="CREDIT_CARD" /> upang i-update ang mga detalye ng iyong card</translation>
<translation id="969892804517981540">Official Build</translation>
<translation id="988159990683914416">Bumuo ang Developer</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 f5e7fbdd3a8..e15bf655a62 100644
--- a/chromium/components/strings/components_strings_fr.xtb
+++ b/chromium/components/strings/components_strings_fr.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Se reconnecter au réseau Wi-Fi</translation>
<translation id="1175364870820465910">Im&amp;primer...</translation>
<translation id="1181037720776840403">Supprimer</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Signaler automatiquement<ph name="END_WHITEPAPER_LINK" /> les incidents de sécurité potentiels à Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Suivant</translation>
<translation id="1201895884277373915">Plus de résultats pour ce site</translation>
<translation id="1206967143813997005">Signature initiale incorrecte</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cyan</translation>
<translation id="1629803312968146339">Voulez-vous que cette carte soit enregistrée dans Chrome ?</translation>
<translation id="1640180200866533862">Règles relatives aux utilisateurs</translation>
+<translation id="1640244768702815859">Essayez de <ph name="BEGIN_LINK" />consulter la page d'accueil du site<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Impossible d'importer la configuration du réseau : elle n'est pas valide.</translation>
<translation id="1644574205037202324">Historique</translation>
<translation id="1645368109819982629">Protocole incompatible</translation>
<translation id="1676269943528358898">Un chiffrement est normalement utilisé sur le site <ph name="SITE" /> pour protéger vos informations. Lors de la dernière tentative de connexion de Google Chrome au site <ph name="SITE" />, des identifiants inhabituels et incorrects ont été retournés. Il est possible qu'un individu malveillant tente de se faire passer pour <ph name="SITE" /> ou qu'un écran de connexion Wi-Fi ait interrompu la connexion. Vos informations restent sécurisées, car nous avons arrêté la connexion avant l'échange des données.</translation>
+<translation id="168328519870909584">Des individus malveillants à l'œuvre sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pourraient tenter d'installer des applications dangereuses sur votre appareil 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="168841957122794586">Le certificat du serveur contient une clé de chiffrement faible.</translation>
-<translation id="1701955595840307032">Obtenir une recommandation de contenu</translation>
<translation id="1710259589646384581">Système d'exploitation</translation>
<translation id="1721312023322545264">Vous devez disposer de l'autorisation de <ph name="NAME" /> pour consulter ce site</translation>
<translation id="1734864079702812349">American Express</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Numéro de page</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Essayez d'exécuter les diagnostics réseau de Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Mettre à jour votre phrase secrète de synchronisation</translation>
+<translation id="1787142507584202372">Les onglets ouverts s'affichent ici</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">La navigation sécurisée de Google a récemment <ph name="BEGIN_LINK" />détecté 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. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">La demande ou ses paramètres ne sont pas valides.</translation>
+<translation id="1834321415901700177">Ce site contient des programmes dangereux</translation>
<translation id="1838667051080421715">Vous consultez actuellement la source d'une page Web.</translation>
<translation id="1871208020102129563">Le proxy est configuré pour utiliser des serveurs proxy déterminés, pas une URL de script .pac.</translation>
<translation id="1883255238294161206">Réduire la liste</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Favoris sur mobile</translation>
<translation id="2148716181193084225">Aujourd'hui</translation>
-<translation id="2149973817440762519">Modifier le favori</translation>
<translation id="2154054054215849342">La synchronisation n'est pas disponible pour votre domaine</translation>
<translation id="2166049586286450108">Accès administrateur complet</translation>
<translation id="2166378884831602661">Ce site ne peut pas fournir de connexion sécurisée</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Vous ne pouvez pas consulter le site Web <ph name="SITE" /> pour le moment, car la technologie HSTS y est utilisée. Les attaques et les erreurs réseau sont généralement temporaires. Vous devriez donc pouvoir accéder à cette page ultérieurement. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">Favori non valide ignoré (index <ph name="ENTRY_INDEX" />)</translation>
<translation id="2354001756790975382">Autres favoris</translation>
+<translation id="2355395290879513365">Il se peut que des pirates informatiques puissent voir les images que vous regardez sur ce site et vous piègent en les modifiant.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Afficher les règles non paramétrées</translation>
<translation id="2396249848217231973">&amp;Annuler la suppression</translation>
<translation id="2455981314101692989">Cette page Web a désactivé la saisie automatique dans ce formulaire.</translation>
+<translation id="2460160116472764928">La navigation sécurisée de Google a récemment <ph name="BEGIN_LINK" />détecté 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. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">Remplir</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Exécuter les diagnostics du réseau<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL de recherche incorrecte</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Voulez-vous vraiment supprimer ces pages de votre historique ?</translation>
<translation id="2677748264148917807">Quitter</translation>
<translation id="269990154133806163">Le certificat présenté par le serveur n'a pas été communiqué au public. Selon le règlement de transparence des certificats, certains certificats doivent obligatoirement être communiqués publiquement pour garantir qu'ils sont dignes de confiance et qu'ils protègent contre les individus malveillants. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">Liste lecture</translation>
<translation id="2704283930420550640">La valeur ne respecte pas le format requis.</translation>
<translation id="2704951214193499422">Impossible de valider votre carte dans Chromium pour le moment. Veuillez réessayer plus tard.</translation>
<translation id="2705137772291741111">La copie enregistrée (en cache) de ce site est illisible.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">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 qu'il ne faut en aucun cas faire confiance aux identifiants de sécurité présentés par le serveur. Il est possible que vous communiquiez avec un pirate informatique. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">Demander l'autorisation</translation>
<translation id="2713444072780614174">Blanc</translation>
+<translation id="2720342946869265578">À proximité</translation>
<translation id="2721148159707890343">Demande réussie.</translation>
<translation id="2728127805433021124">Le certificat du serveur a été signé avec un algorithme de signature faible.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Exécuter les diagnostics de connectivité<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Vous pouvez désactiver tout proxy configuré pour une connexion à partir de la page des paramètres.</translation>
<translation id="2955913368246107853">Fermer la barre de recherche</translation>
<translation id="2958431318199492670">La configuration du réseau ne respecte pas les normes de l'ONC. Il est possible que des parties de la configuration ne soient pas importées.</translation>
+<translation id="29611076221683977">Des individus malveillants à l'œuvre sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pourraient tenter d'installer des applications dangereuses sur votre Mac 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="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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Masquer les détails</translation>
<translation id="3587482841069643663">Tous</translation>
<translation id="3600246354004376029">"<ph name="TITLE" />", <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Le serveur ne prend pas en charge l'extension de renégociation TLS.</translation>
<translation id="36224234498066874">Effacer les données de navigation...</translation>
<translation id="362276910939193118">Afficher l'historique complet</translation>
<translation id="3623476034248543066">Afficher la valeur</translation>
@@ -271,19 +279,20 @@
<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="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="3696411085566228381">aucune</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">Chargement...</translation>
+<translation id="370665806235115550">Chargement en cours...</translation>
<translation id="3712624925041724820">Licences épuisées.</translation>
<translation id="3714780639079136834">Activer les données mobiles ou le réseau Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Vérifier les configurations du proxy, du pare-feu et du DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Si vous avez compris les risques auxquels vous vous exposez, vous pouvez <ph name="BEGIN_LINK" />consulter ce site dangereux<ph name="END_LINK" /> avant que les programmes malveillants n'aient été supprimés.</translation>
<translation id="3739623965217189342">Lien copié</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="3788090790273268753">Le certificat de ce site arrive à expiration en 2016, et la chaîne de certificats contient un certificat signé avec SHA-1.</translation>
<translation id="382518646247711829">Si vous utilisez un serveur proxy…</translation>
<translation id="3828924085048779000">La phrase secrète est obligatoire.</translation>
<translation id="3845539888601087042">Affichage de l'historique des appareils auxquels vous êtes connecté. <ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" /></translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Clé "<ph name="SUBKEY" />" : <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Le client et le serveur ne sont pas compatibles avec une version de protocole ou une méthode de chiffrement SSL commune.</translation>
<translation id="4079302484614802869">La configuration du proxy est définie pour utiliser une URL de script .pac, et non pas des serveurs proxy déterminés.</translation>
+<translation id="4098354747657067197">Le site Web que vous allez ouvrir est trompeur</translation>
<translation id="4103249731201008433">Le numéro de série de l'appareil n'est pas valide.</translation>
<translation id="4103763322291513355">Accédez à &lt;strong&gt;chrome://policy&lt;/strong&gt; pour consulter une liste des URL ajoutées à la liste noire et des autres règles définies par votre administrateur système.</translation>
<translation id="4110615724604346410">Impossible de vérifier que ce serveur est bien <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. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{aucune}=1{1 application ($1)}=2{2 applications ($1, $2)}one{# application ($1, $2 $3)}other{# applications ($1, $2 $3)}}</translation>
<translation id="4220128509585149162">Plantages</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Essayez d'exécuter les diagnostics réseau<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Votre connexion à ce site n'est pas totalement sécurisée.</translation>
<translation id="4250680216510889253">Non</translation>
<translation id="425582637250725228">Les modifications que vous avez apportées ne seront peut-être pas enregistrées.</translation>
<translation id="4258748452823770588">Signature incorrecte.</translation>
<translation id="4269787794583293679">(aucun nom d'utilisateur)</translation>
+<translation id="4280429058323657511">, expiration le <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">La navigation sécurisée de Google a récemment <ph name="BEGIN_LINK" />détecté des programmes dangereux<ph name="END_LINK" /> sur le site <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">Suggestions des parents</translation>
<translation id="4304224509867189079">Se connecter</translation>
<translation id="432290197980158659">Le serveur a présenté 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. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">Échec de la recherche de l'article.</translation>
+<translation id="4326324639298822553">Veuillez vérifier la date d'expiration, puis réessayer</translation>
<translation id="4331708818696583467">Non sécurisé</translation>
+<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="4381091992796011497">Nom d'utilisateur :</translation>
<translation id="4394049700291259645">Désactiver</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Vous avez tenté d'accéder à <ph name="DOMAIN" />, mais le certificat présenté par le serveur est incorrect. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></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="4619615317237390068">Onglets d'autres appareils</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Vous consultez actuellement une page d'extension.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Actualiser les règles</translation>
<translation id="4728558894243024398">Plate-forme</translation>
<translation id="4744603770635761495">Chemin d'accès exécutable</translation>
+<translation id="4750917950439032686">Vos informations, par exemple vos mots de passe ou vos numéros de carte de paiement, sont privées lorsqu'elles sont transmises à ce site.</translation>
<translation id="4756388243121344051">&amp;Historique</translation>
+<translation id="4759118997339041434">Saisie automatique des cartes de paiement désactivée</translation>
<translation id="4764776831041365478">Il se peut que la page Web à l'adresse <ph name="URL" /> soit temporairement inaccessible ou qu'elle ait été déplacée de façon permanente à une autre adresse Web.</translation>
<translation id="4771973620359291008">Une erreur inconnue s'est produite.</translation>
<translation id="4800132727771399293">Veuillez vérifier la date d'expiration et le code CVC, puis réessayez.</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Erreur réseau.</translation>
<translation id="4816492930507672669">Ajuster à la page</translation>
<translation id="4850886885716139402">Afficher</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Actions</translation>
<translation id="4958444002117714549">Développer la liste</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Téléchargement</translation>
<translation id="5190835502935405962">Barre de favoris</translation>
<translation id="5199729219167945352">Prototypes</translation>
-<translation id="5199841536747119669">Vos suggestions s'affichent ici</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="5299298092464848405">Erreur d'analyse de la règle.</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">L'envoi de rapports d'erreur est désactivé.</translation>
<translation id="5317780077021120954">Enregistrer</translation>
<translation id="5327248766486351172">Nom</translation>
+<translation id="5337705430875057403">Les individus malveillants à l'œuvre sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pourraient tenter de vous inciter à effectuer des opérations dangereuses, telles que l'installation d'un logiciel ou la révélation d'informations personnelles (par exemple des mots de passe, des numéros de téléphone ou des numéros de carte de paiement).</translation>
<translation id="5359637492792381994">Impossible de vérifier que ce serveur est bien <ph name="DOMAIN" />, car son certificat de sécurité actuel n'est pas valide. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">Échec du stockage des paramètres de la règle.</translation>
+<translation id="5386426401304769735">La chaîne de certificats de ce site contient un certificat signé avec SHA-1.</translation>
<translation id="5421136146218899937">Effacer les données de navigation...</translation>
<translation id="5430298929874300616">Supprimer le favori</translation>
<translation id="5431657950005405462">Votre fichier est introuvable</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Une page intégrée à l'adresse <ph name="SITE" /> indique :</translation>
<translation id="5556459405103347317">Actualiser</translation>
<translation id="5565735124758917034">Actif</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>
+<translation id="5580958916614886209">Veuillez vérifier le mois d'expiration, puis réessayer</translation>
<translation id="560412284261940334">Gestion non acceptée.</translation>
<translation id="5610142619324316209">Vérifier la connexion</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> vous a redirigé à de trop nombreuses reprises.</translation>
<translation id="5622887735448669177">Voulez-vous quitter ce site ?</translation>
<translation id="5629630648637658800">Échec du chargement des paramètres de la règle.</translation>
<translation id="5631439013527180824">Jeton de gestion de l'appareil non valide.</translation>
+<translation id="5669703222995421982">Obtenir une recommandation de contenu personnalisé</translation>
+<translation id="5675650730144413517">Cette page ne fonctionne pas</translation>
<translation id="5677928146339483299">Bloqué</translation>
<translation id="5694783966845939798">Vous avez essayé d'accéder à <ph name="DOMAIN" />, mais le serveur a présenté un certificat signé avec un algorithme de signature faible (tel que SHA-1). Il se peut que les identifiants de sécurité présentés par le serveur aient été falsifiés. Le serveur n'est peut-être pas celui auquel vous souhaitez accéder (il peut s'agir d'une tentative de piratage). <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">L'identité de ce site Web n'a pas été vérifiée.</translation>
<translation id="5720705177508910913">Utilisateur actuel</translation>
-<translation id="572328651809341494">Onglets récents</translation>
<translation id="5732392974455271431">Tes parents peuvent te le débloquer</translation>
<translation id="5784606427469807560">Un problème est survenu lors de la validation de votre carte. Vérifiez votre connexion Internet, puis réessayez.</translation>
<translation id="5785756445106461925">De plus, cette page inclut d'autres ressources qui ne sont pas sécurisées. Ces ressources peuvent être consultées par des tiers pendant leur transfert, et modifiées par un pirate informatique dans le but de changer l'aspect de cette page.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Vous avez tenté d'accéder à <ph name="DOMAIN" />, mais le serveur a présenté un certificat contenant une clé faible. Il est possible qu'un pirate informatique ait compromis la clé privée, que le serveur auquel vous avez accédé ne soit pas celui que vous croyez et que vous soyez en train de communiquer avec un pirate informatique. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">Nouveau dossier</translation>
<translation id="5869405914158311789">Ce site est inaccessible</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Ce type de carte n'est pas compatible avec Google Payments pour ce marchand. Veuillez sélectionner une autre carte.</translation>
<translation id="59174027418879706">Activée</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="5966707198760109579">Semaine</translation>
<translation id="5967867314010545767">Supprimer de l'historique</translation>
<translation id="5975083100439434680">Zoom arrière</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Essayez les suggestions ci-dessous :</translation>
<translation id="6151417162996330722">La durée de validité du certificat du serveur est trop longue.</translation>
<translation id="6165508094623778733">En savoir plus</translation>
+<translation id="6177128806592000436">Votre connexion à ce site n'est pas sécurisée.</translation>
<translation id="6203231073485539293">Vérifiez votre connexion Internet</translation>
<translation id="6218753634732582820">Supprimer l'adresse de Chromium ?</translation>
+<translation id="6251924700383757765">Règles de confidentialité</translation>
+<translation id="625755898061068298">Vous avez choisi de désactiver les avertissements de sécurité pour ce site.</translation>
<translation id="6259156558325130047">&amp;Rétablir la réorganisation</translation>
<translation id="6263376278284652872">Favoris de <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Retour à la sécurité</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> est inaccessible.</translation>
<translation id="6321917430147971392">Vérifiez vos paramètres DNS</translation>
<translation id="6328639280570009161">Essayez de désactiver la prédiction réseau</translation>
+<translation id="6328786501058569169">Ce site est trompeur</translation>
<translation id="6337534724793800597">Filtrer les règles par nom</translation>
<translation id="6342069812937806050">À l'instant</translation>
<translation id="6345221851280129312">taille inconnue</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Vous ne pouvez pas consulter le site Web <ph name="SITE" /> pour le moment, car il a envoyé des identifiants brouillés qui ne peuvent être traités par Chromium. Les attaques et les erreurs réseau sont généralement temporaires. Vous devriez donc pouvoir accéder à cette page ultérieurement. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">Impossible de vérifier si le certificat a été révoqué.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Ignoré parce que la recherche par défaut est désactivée par une règle.</translation>
<translation id="6462969404041126431">Impossible de vérifier que ce serveur est bien <ph name="DOMAIN" />, car il se peut que son certificat de sécurité ait été révoqué. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<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="6529602333819889595">&amp;Rétablir la suppression</translation>
<translation id="6534179046333460208">Suggestions pour le Web physique</translation>
<translation id="6550675742724504774">Options</translation>
+<translation id="6556239504065605927">Connexion sécurisée</translation>
<translation id="6563469144985748109">Votre responsable ne l'a pas encore autorisé</translation>
<translation id="6593753688552673085">moins de <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Options de chiffrement</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Cette règle est obsolète.</translation>
<translation id="6652240803263749613">Impossible de vérifier que ce serveur est bien <ph name="DOMAIN" />, car son certificat de sécurité n'est pas considéré comme fiable par le système d'exploitation de votre ordinateur. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">Modifier le dossier</translation>
-<translation id="6660210980321319655">Signalé automatiquement le <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Valeur de la règle</translation>
<translation id="6757797048963528358">Votre appareil s'est mis en veille.</translation>
<translation id="6778737459546443941">Tes parents ne l'ont pas encore autorisé</translation>
+<translation id="6810899417690483278">ID de la personnalisation</translation>
<translation id="6820686453637990663">Cryptogramme</translation>
<translation id="6830600606572693159">La page Web <ph name="URL" /> n'est pas disponible pour le moment. Cela peut être dû à une surcharge ou à une opération de maintenance.</translation>
<translation id="6831043979455480757">Traduire</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Coordonnées</translation>
+<translation id="7079718277001814089">Ce site contient des logiciels malveillants</translation>
<translation id="7087282848513945231">Comté</translation>
<translation id="7088615885725309056">Ancien</translation>
<translation id="7090678807593890770">Effectuez une recherche Google sur <ph name="LINK" />.</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> ne respecte pas les normes de sécurité.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" /> sur ce problème.</translation>
<translation id="7219179957768738017">La connexion utilise <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Le site que vous allez ouvrir contient des logiciels malveillants</translation>
<translation id="724975217298816891">Saisissez la date d'expiration et le code CVC de la carte <ph name="CREDIT_CARD" /> pour mettre à jour les informations relatives à celle-ci. Une fois la validation effectuée, les informations seront partagées avec ce site.</translation>
<translation id="725866823122871198">Impossible d'établir une connexion privée à <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> : la date et l'heure de votre ordinateur (<ph name="DATE_AND_TIME" />) sont incorrectes.</translation>
<translation id="7269802741830436641">Cette page Web présente une boucle de redirection.</translation>
@@ -611,6 +648,7 @@ Conseil : Le mode navigation privée (<ph name="SHORTCUT_KEY" />) pourra vous Ã
<translation id="7658239707568436148">Annuler</translation>
<translation id="7667346355482952095">Le jeton de règle renvoyé est vide ou ne correspond pas au jeton actuel.</translation>
<translation id="7668654391829183341">Appareil inconnu</translation>
+<translation id="7669271284792375604">Des individus malveillants à l'œuvre sur ce site pourraient vous inciter à installer des programmes qui nuisent à votre confort de navigation (par exemple, en changeant votre page d'accueil ou en affichant des annonces supplémentaires sur les sites que vous consultez.</translation>
<translation id="7674629440242451245">Vous souhaitez bénéficier de nouvelles fonctionnalités Chrome passionnantes ? Essayez notre version en développement à l'adresse chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Continuer vers le site <ph name="SITE" /> (dangereux)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Impossible de charger ce site à partir du cache</translation>
@@ -626,14 +664,17 @@ Conseil : Le mode navigation privée (<ph name="SHORTCUT_KEY" />) pourra vous Ã
<translation id="7800304661137206267">La connexion est chiffrée au moyen de <ph name="CIPHER" />, avec <ph name="MAC" /> pour l'authentification des messages et <ph name="KX" /> pour la méthode d'échange de clés.</translation>
<translation id="780301667611848630">Non merci</translation>
<translation id="7805768142964895445">État</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Supprimer la suggestion de saisie de formulaire de Chrome ?</translation>
<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="7887683347370398519">Veuillez vérifier votre code CVC et réessayer.</translation>
<translation id="7912024687060120840">Dans le dossier :</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Le certificat du serveur n'est pas encore valide.</translation>
<translation id="7942349550061667556">Rouge</translation>
+<translation id="7947285636476623132">Veuillez vérifier l'année d'expiration, puis réessayer</translation>
<translation id="7951415247503192394">(32 bits)</translation>
<translation id="7956713633345437162">Favoris sur mobile</translation>
<translation id="7961015016161918242">Jamais</translation>
@@ -641,7 +682,10 @@ Conseil : Le mode navigation privée (<ph name="SHORTCUT_KEY" />) pourra vous Ã
<translation id="7983301409776629893">Toujours traduire en <ph name="TARGET_LANGUAGE" /> les pages en <ph name="ORIGINAL_LANGUAGE" /></translation>
<translation id="7995512525968007366">Non spécifié</translation>
<translation id="8012647001091218357">Impossible de joindre vos parents pour le moment. Veuillez réessayer.</translation>
+<translation id="8025119109950072390">Des individus malveillants à l'œuvre sur ce site pourraient vous inciter à effectuer des opérations dangereuses, telles que l'installation d'un logiciel ou la révélation d'informations personnelles (par exemple des mots de passe, des numéros de téléphone ou des numéros de carte de paiement).</translation>
+<translation id="803030522067524905">La navigation sécurisée de Google a récemment détecté de l'hameçonnage sur le site <ph name="SITE" />. Les sites d'hameçonnage se font passer pour d'autres sites Web pour vous tromper. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">Cette page est rédigée en <ph name="SOURCE_LANGUAGE" />. Voulez-vous la traduire en <ph name="TARGET_LANGUAGE" /> ?</translation>
+<translation id="8041089156583427627">Envoyer</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>
@@ -652,9 +696,11 @@ Conseil : Le mode navigation privée (<ph name="SHORTCUT_KEY" />) pourra vous Ã
<translation id="8150722005171944719">Le fichier disponible à l'adresse <ph name="URL" /> n'est pas lisible. Il est possible qu'il ait été supprimé ou déplacé, ou que les autorisations associées empêchent d'y accéder.</translation>
<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="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="8230421197304563332">Des individus malveillants à l'œuvre sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 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="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="8249320324621329438">Dernière récupération :</translation>
<translation id="8261506727792406068">Supprimer</translation>
@@ -663,11 +709,13 @@ Conseil : Le mode navigation privée (<ph name="SHORTCUT_KEY" />) pourra vous Ã
<translation id="8294431847097064396">Source</translation>
<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="8349305172487531364">Barre de favoris</translation>
<translation id="8363502534493474904">Désactiver le mode Avion</translation>
<translation id="8364627913115013041">Non définie</translation>
<translation id="8380941800586852976">Dangereux</translation>
<translation id="8382348898565613901">Vos favoris récemment consultés s'affichent ici</translation>
+<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="8428213095426709021">Paramètres</translation>
@@ -679,9 +727,9 @@ Conseil : Le mode navigation privée (<ph name="SHORTCUT_KEY" />) pourra vous Ã
<translation id="8498891568109133222"><ph name="HOST_NAME" /> a mis trop de temps à répondre.</translation>
<translation id="852346902619691059">Impossible de vérifier que ce serveur est bien <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. <ph name="BEGIN_LEARN_MORE_LINK" />En savoir plus<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Ce type de carte n'est pas compatible avec Google Payments. Veuillez sélectionner une autre carte.</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="8553075262323480129">La traduction a échoué, car nous n'avons pas pu déterminer la langue de la page.</translation>
<translation id="8559762987265718583">Impossible d'établir une connexion privée à <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> : la date et l'heure de votre appareil (<ph name="DATE_AND_TIME" />) sont incorrectes.</translation>
-<translation id="856992080682148">Le certificat de ce site arrive à expiration en 2017 ou à une date ultérieure, et la chaîne de certificats contient un certificat signé avec SHA-1.</translation>
<translation id="8571890674111243710">Traduction de la page en <ph name="LANGUAGE" />...</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>
@@ -694,10 +742,12 @@ Conseil : Le mode navigation privée (<ph name="SHORTCUT_KEY" />) pourra vous Ã
<translation id="8740359287975076522">L'&lt;abbr id="dnsDefinition"&gt;adresse DNS&lt;/abbr&gt; de <ph name="HOST_NAME" /> est introuvable. Identification du problème…</translation>
<translation id="8790007591277257123">&amp;Rétablir la suppression</translation>
<translation id="8798099450830957504">Par défaut</translation>
+<translation id="8800988563907321413">Vos suggestions à proximité s'affichent ici</translation>
<translation id="8804164990146287819">Règles de confidentialité</translation>
<translation id="8820817407110198400">Favoris</translation>
<translation id="8834246243508017242">Activer la saisie automatique à l'aide de la fonctionnalité Contacts</translation>
<translation id="883848425547221593">Autres favoris</translation>
+<translation id="884264119367021077">Adresse de livraison</translation>
<translation id="884923133447025588">Aucun système de révocation trouvé</translation>
<translation id="885730110891505394">Partage avec Google</translation>
<translation id="8866481888320382733">Erreur d'analyse des paramètres de la règle.</translation>
@@ -715,18 +765,21 @@ Conseil : Le mode navigation privée (<ph name="SHORTCUT_KEY" />) pourra vous Ã
<translation id="8971063699422889582">Le certificat du serveur a expiré.</translation>
<translation id="8987927404178983737">Mois</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Le site Web que vous allez ouvrir contient des programmes dangereux</translation>
<translation id="9001074447101275817">Le proxy <ph name="DOMAIN" /> nécessite un nom d'utilisateur et un mot de passe.</translation>
<translation id="901974403500617787">Seul le propriétaire suivant peut définir les options qui s'appliquent à tout le système : <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Cette page a été traduite en <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="9035022520814077154">Erreur liée à la sécurité</translation>
<translation id="9038649477754266430">Utiliser un service de prédiction pour charger les pages plus rapidement</translation>
<translation id="9039213469156557790">De plus, cette page inclut d'autres ressources qui ne sont pas sécurisées. Ces ressources peuvent être consultées par des tiers pendant leur transfert, et modifiées par un pirate informatique dans le but de changer le comportement de cette page.</translation>
+<translation id="9040185888511745258">Des pirates informatiques sur le site <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pourraient vous inciter à installer des programmes qui nuisent à votre confort de navigation (par exemple, en changeant votre page d'accueil ou en affichant des annonces supplémentaires sur les sites que vous consultez).</translation>
<translation id="9050666287014529139">Phrase secrète</translation>
<translation id="9065203028668620118">Modifier</translation>
<translation id="9068849894565669697">Sélectionner couleur</translation>
<translation id="9076283476770535406">Il est possible qu'il comporte du contenu réservé aux adultes</translation>
-<translation id="9092364396508701805">La page <ph name="HOST_NAME" /> ne fonctionne pas</translation>
<translation id="9103872766612412690">Un chiffrement est normalement utilisé sur le site <ph name="SITE" /> pour protéger vos informations. Lors de la dernière tentative de connexion de Chromium au site <ph name="SITE" />, des identifiants inhabituels et incorrects ont été retournés. Il est possible qu'un individu malveillant tente de se faire passer pour <ph name="SITE" /> ou qu'un écran de connexion Wi-Fi ait interrompu la connexion. Vos informations restent sécurisées, car nous avons arrêté la connexion avant l'échange des données.</translation>
<translation id="9137013805542155359">Afficher l'original</translation>
+<translation id="9137248913990643158">Veuillez démarrer Chrome et vous connecter à votre compte avant d'utiliser cette application</translation>
<translation id="9148507642005240123">&amp;Annuler la modification</translation>
<translation id="9157595877708044936">Configuration en cours...</translation>
<translation id="9170848237812810038">Ann&amp;uler</translation>
@@ -739,7 +792,6 @@ Conseil : Le mode navigation privée (<ph name="SHORTCUT_KEY" />) pourra vous Ã
<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="962701380617707048">Saisir la date d'expiration et le code CVC de la carte <ph name="CREDIT_CARD" /> pour mettre à jour les informations relatives à celle-ci</translation>
<translation id="969892804517981540">Build officiel</translation>
<translation id="988159990683914416">Build de développement</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 4ea2db1a6e0..fbe11809a7e 100644
--- a/chromium/components/strings/components_strings_gu.xtb
+++ b/chromium/components/strings/components_strings_gu.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Wi-Fi સાથે ફરીથી કનેકà«àªŸ કરીને</translation>
<translation id="1175364870820465910">&amp;છાપો...</translation>
<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="1206967143813997005">ખોટી નાની સહી</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">સà«àª¯àª¾àª¨</translation>
<translation id="1629803312968146339">શà«àª‚ તમે ઇચà«àª›à«‹ છો કે Chrome આ કારà«àª¡ સાચવે?</translation>
<translation id="1640180200866533862">વપરાશકરà«àª¤àª¾ નીતિઓ</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />સાઇટના હોમપેજની મà«àª²àª¾àª•àª¾àª¤ લેવાનો<ph name="END_LINK" /> પà«àª°àª¯àª¾àª¸ કરો.</translation>
<translation id="1644184664548287040">નેટવરà«àª• ગોઠવણી અમાનà«àª¯ છે અને આયાત કરી શકાઇ નથી.</translation>
<translation id="1644574205037202324">ઇતિહાસ</translation>
<translation id="1645368109819982629">અસમરà«àª¥àª¿àª¤ પà«àª°à«‹àªŸà«‹àª•à«‹àª²</translation>
<translation id="1676269943528358898"><ph name="SITE" /> સામાનà«àª¯ રીતે તમારી માહિતીને સà«àª°àª•à«àª·àª¿àª¤ રાખવા માટે àªàª¨à«àª•à«àª°àª¿àªªà«àª¶àª¨àª¨à«‹ ઉપયોગ કરે છે. જà«àª¯àª¾àª°à«‡ આ સમયે Google Chrome દà«àªµàª¾àª°àª¾ <ph name="SITE" /> થી કનેકà«àªŸ કરવાનો પà«àª°àª¯àª¾àª¸ થયો, તà«àª¯àª¾àª°à«‡ વેબસાઇટે અસામાનà«àª¯ અને ખોટા ઓળખાણપતà«àª°à«‹àª¨à«‡ પાછા મોકલà«àª¯àª¾àª‚. આવà«àª‚ તà«àª¯àª¾àª°à«‡ થઇ શકે જà«àª¯àª¾àª°à«‡ કોઈ હà«àª®àª²àª¾àª–ોર <ph name="SITE" /> હોવાનો ડોળ કરવાનો પà«àª°àª¯àª¾àª¸ કરી રહà«àª¯à«‹ હોય અથવા કોઈ Wi-Fi સાઇન-ઇન સà«àª•à«àª°à«€àª¨à«‡ કનેકà«àª¶àª¨àª®àª¾àª‚ વિકà«àª·à«‡àªª પાડà«àª¯à«‹ હોય. તમારી માહિતી હજી પણ સà«àª°àª•à«àª·àª¿àª¤ છે કારણ કે Google Chrome ઠકોઈપણ ડેટા વિનિમય થાય તે પહેલાં જ કનેકà«àª¶àª¨ રોકી દીધà«àª‚.</translation>
+<translation id="168328519870909584"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> પરનાં હà«àª®àª²àª¾àª–ોરો તમારા ઉપકરણ પર તમારી માહિતી (ઉદાહરણ તરીકે, ફોટા, પાસવરà«àª¡à«àª¸, સંદેશા અને કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡à«àª¸) ચોરી અથવા કાઢી નાખી શકે તેવી જોખમકારક àªàªªà«àª²àª¿àª•à«‡àª¶àª¨à«‹ ઇનà«àª¸à«àªŸà«‹àª² કરવાનો પà«àª°àª¯àª¤à«àª¨ કરી શકે.</translation>
<translation id="168841957122794586">સરà«àªµàª° પà«àª°àª®àª¾àª£àªªàª¤à«àª° àªàª• નબળી કà«àª°àª¿àªªà«àªŸà«‹àª—à«àª°àª¾àª«àª¿àª• કી ધરાવે છે.</translation>
-<translation id="1701955595840307032">સૂચવેલ સામગà«àª°à«€ મેળવો</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">આ સાઇટની મà«àª²àª¾àª•àª¾àª¤ લેવા માટે તમને <ph name="NAME" /> ની પરવાનગીની જરૂર છે</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">પૃષà«àª  નંબર</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows નેટવરà«àª• ડાયગà«àª¨à«‹àª¸à«àªŸàª¿àª•à«àª¸ ચલાવવાનો પà«àª°àª¯àª¾àª¸ કરો<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">કૃપા કરી તમારા સમનà«àªµàª¯àª¨ પાસફà«àª°à«‡àªàª¨à«‡ અપડેટ કરો.</translation>
+<translation id="1787142507584202372">તમારા ખà«àª²à«àª²àª¾ ટૅબà«àª¸ અહીં દેખાય છે</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google સલામત બà«àª°àª¾àª‰àªàª¿àª‚ગને <ph name="SITE" /> પર <ph name="BEGIN_LINK" />માલવેર મળà«àª¯à«àª‚<ph name="END_LINK" />. વેબસાઇટà«àª¸ કે જે સામાનà«àª¯ રીતે સà«àª°àª•à«àª·àª¿àª¤ હોય છે તે કà«àª¯àª¾àª°à«‡àª• માલવેરથી દૂષિત હોય છે. દà«àª°à«àª­àª¾àªµàª¨àª¾àªªà«‚રà«àª£ સામગà«àª°à«€, <ph name="SUBRESOURCE_HOST" />, àªàª• જાણીતા માલવેર વિકà«àª°à«‡àª¤àª¾àª¥à«€ આવે છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">અમાનà«àª¯ વિનંતી અથવા વિનંતી પરિમાણો</translation>
+<translation id="1834321415901700177">આ સાઇટમાં હાનિકારક પà«àª°à«‹àª—à«àª°àª¾àª®à«àª¸ છે</translation>
<translation id="1838667051080421715">તમે વેબ પૃષà«àª àª¨à«‹ સà«àª°à«‹àª¤ જોઈ રહà«àª¯àª¾àª‚ છો.</translation>
<translation id="1871208020102129563">પà«àª°à«‹àª•à«àª¸à«€ નિયત કરેલા પà«àª°à«‹àª•à«àª¸à«€ સરà«àªµàª°àª¨à«‹ ઉપયોગ કરવા માટે સેટ કરેલી છે, .pac સà«àª•à«àª°àª¿àªªà«àªŸ URL નથી.</translation>
<translation id="1883255238294161206">સૂચિ સંકà«àªšàª¿àª¤ કરો</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">પિન કોડ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 સૂચન}one{# સૂચન}other{# સૂચન}}</translation>
<translation id="2065985942032347596">પà«àª°àª®àª¾àª£à«€àª•àª°àª£ આવશà«àª¯àª•</translation>
-<translation id="2079545284768500474">પૂરà«àªµàªµàª¤à« કરો</translation>
+<translation id="2079545284768500474">પૂરà«àªµàªµàª¤ કરો</translation>
<translation id="20817612488360358">સિસà«àªŸàª® પà«àª°à«‹àª•à«àª¸à«€ સેટિંગà«àª¸ ઉપયોગમાં લેવા માટે સેટ છે પણ àªàª• સà«àªªàª·à«àªŸ પà«àª°à«‹àª•à«àª¸à«€ ગોઠવણી પણ ઉલà«àª²à«‡àª–િત કરેલી છે.</translation>
<translation id="2086652334978798447">Google દà«àªµàª¾àª°àª¾ સૂચવેલ વà«àª¯àª•à«àª¤àª¿àª—ત કરેલ સામગà«àª°à«€ મેળવવા માટે, Chrome માં સાઇન ઇન કરો.</translation>
<translation id="2089090684895656482">ઓછà«àª‚</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">મૂળ કà«àª²àª¾àª‡àª¨à«àªŸ</translation>
<translation id="213826338245044447">મોબાઇલ બà«àª•àª®àª¾àª°à«àª•à«àª¸</translation>
<translation id="2148716181193084225">આજે</translation>
-<translation id="2149973817440762519">બà«àª•àª®àª¾àª°à«àª• સંપાદિત કરો</translation>
<translation id="2154054054215849342">સમનà«àªµàª¯àª¨ તમારા ડોમેન માટે ઉપલબà«àª§ નથી.</translation>
<translation id="2166049586286450108">સંપૂરà«àª£ વà«àª¯àªµàª¸à«àª¥àª¾àªªàª• àªàª•à«àª¸à«‡àª¸</translation>
<translation id="2166378884831602661">આ સાઇટ àªàª• સà«àª°àª•à«àª·àª¿àª¤ કનેકà«àª¶àª¨ આપી શકતી નથી</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">તમે અતà«àª¯àª¾àª°à«‡ <ph name="SITE" /> ની મà«àª²àª¾àª•àª¾àª¤ લઈ શકતાં નથી કારણ કે વેબસાઇટ HSTS નો ઉપયોગ કરે છે. નેટવરà«àª• ભૂલો અને હà«àª®àª²àª¾ સામાનà«àª¯ રીતે અસà«àª¥àª¾àª¯à«€ છે, તેથી આ પૃષà«àª  સંભવિત રૂપે પછીથી કારà«àª¯ કરશે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155"><ph name="ENTRY_INDEX" /> અનà«àª•à«àª°àª®àª£àª¿àª•àª¾ પર અમાનà«àª¯ બà«àª•àª®àª¾àª°à«àª• અવગà«àª£à«àª¯à«‹</translation>
<translation id="2354001756790975382">અનà«àª¯ બà«àª•àª®àª¾àª°à«àª•à«àª¸</translation>
+<translation id="2355395290879513365">તમે આ સાઇટ પર જોઈ રહà«àª¯àª¾àª‚ છો તે છબીઓને હà«àª®àª²àª¾àª–ોરો જોઈ શકે છે અને તેમને સંશોધિત કરીને તમને છેતરી શકે છે.</translation>
<translation id="2359808026110333948">ચાલૠરાખો</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> ઠકૅપà«àªšàª° કરેલ કà«àª°à«‡àª¶ રિપોરà«àªŸ અપલોડ કરી ન હતી</translation>
<translation id="2367567093518048410">સà«àª¤àª°</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">કોઈ કિંમત સેટ નહીં સાથે નીતિઓ બતાવો</translation>
<translation id="2396249848217231973">&amp;કાઢી નાખવà«àª‚ પૂરà«àªµàªµàª¤à«â€Œ કરો</translation>
<translation id="2455981314101692989">આ વેબપૃષà«àª à«‡ આ ફોરà«àª®àª¨à«àª‚ આપમેળે ભરણ અકà«àª·àª® કરà«àª¯à«àª‚ છે.</translation>
+<translation id="2460160116472764928">Google સલામત બà«àª°àª¾àª‰àªàª¿àª‚ગને તાજેતરમાં <ph name="SITE" /> પર <ph name="BEGIN_LINK" />માલવેર મળà«àª¯à«àª‚<ph name="END_LINK" />. વેબસાઇટà«àª¸ કે જે સામાનà«àª¯ રીતે સà«àª°àª•à«àª·àª¿àª¤ હોય છે તે કà«àª¯àª¾àª°à«‡àª• માલવેરથી દૂષિત હોય છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">ભરો</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />નેટવરà«àª• ડાયગà«àª¨à«‹àª¸à«àªŸàª¿àª•à«àª¸ ચલાવી રહà«àª¯àª¾àª‚ છે<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">અમાનà«àª¯ શોધ URL.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">શà«àª‚ તમે ખરેખર તમારા ઇતિહાસમાંથી આ પૃષà«àª à«‹àª¨à«‡ કાઢી નાખવા માંગો છો?</translation>
<translation id="2677748264148917807">છોડો</translation>
<translation id="269990154133806163">સરà«àªµàª°à«‡ àªàªµà«àª‚ પà«àª°àª®àª¾àª£àªªàª¤à«àª° પà«àª°àª¸à«àª¤à«àª¤ કરà«àª¯à«àª‚ કે જે પà«àª°àª®àª¾àª£àªªàª¤à«àª° પારદરà«àª¶àª¿àª¤àª¾ નીતિનો ઉપયોગ કરીને સારà«àªµàªœàª¨àª¿àª• રીતે જાહેર કરà«àª¯à«àª‚ ન હતà«àª‚. આ કેટલાક પà«àª°àª®àª¾àª£àªªàª¤à«àª°à«‹ માટે ઠખાતરી કરવા હેતૠઆવશà«àª¯àª• છે કે તેઓ વિશà«àªµàª¸àª¨à«€àª¯ છે અને હà«àª®àª²àª¾àª–ોરો સામે રકà«àª·àª£ કરે છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">વાંચન સૂચિ</translation>
<translation id="2704283930420550640">મૂલà«àª¯ ફોરà«àª®à«‡àªŸàª¥à«€ મેળ ખાતà«àª‚ નથી.</translation>
<translation id="2704951214193499422">આ સમયે Chromium તમારા કારà«àª¡àª¨à«€ પà«àª·à«àªŸàª¿ કરવામાં અસમરà«àª¥ હતà«àª‚. કૃપા કરીને પછીથી ફરી પà«àª°àª¯àª¾àª¸ કરો.</translation>
<translation id="2705137772291741111">આ સાઇટની સાચવેલ (કૅશ કરેલ) કૉપિ વાંચવા યોગà«àª¯ ન હતી.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">તમે <ph name="DOMAIN" /> પર પહોંચવાનો પà«àª°àª¯àª¤à«àª¨ કરà«àª¯à«‹, પરંતૠસરà«àªµàª° દà«àªµàª¾àª°àª¾ પà«àª°àª¸à«àª¤à«àª¤ કરવામાં આવેલà«àª‚ પà«àª°àª®àª¾àª£àªªàª¤à«àª° તેના રજૂકરà«àª¤àª¾ દà«àªµàª¾àª°àª¾ જ રદ કરવામાં આવà«àª¯à«àª‚ છે. આનો અરà«àª¥ છે કે સરà«àªµàª°à«‡ પà«àª°àª¸à«àª¤à«àª¤ કરેલા સà«àª°àª•à«àª·àª¾ ઓળખપતà«àª° પર પૂરà«àª£àªªàª£à«‡ વિશà«àªµàª¾àª¸ કરવો જોઈઠનહીં. તમે કોઈ હà«àª®àª²àª¾àª–ોર સાથે વારà«àª¤àª¾àª²àª¾àªª કરી રહà«àª¯àª¾àª‚ હોઈ શકો છો. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">પરવાનગી માગો</translation>
<translation id="2713444072780614174">શà«àªµà«‡àª¤</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">તમે સેટિંગà«àª¸ પૃષà«àª àª®àª¾àª‚થી કનેકà«àª¶àª¨ માટે ગોઠવવામાં આવેલ કોઇપણ પà«àª°à«‹àª•à«àª¸à«€àª“ અકà«àª·àª® કરી શકો છો.</translation>
<translation id="2955913368246107853">શોધ બાર બંધ કરો</translation>
<translation id="2958431318199492670">નેટવરà«àª• ગોઠવણી ONC માનકનà«àª‚ પાલન કરતી નથી. ગોઠવણીના ભાગો આયાત કરી શકાશે નહીં.</translation>
+<translation id="29611076221683977">હà«àª®àª²àª¾àª–ોરો હાલમાં <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> પર છે તે તમારા Mac પર તમારી માહિતી (ઉદાહરણ તરીકે, ફોટા, પાસવરà«àª¡à«àª¸, સંદેશા અને કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡à«àª¸) ને ચોરી શકે કે કાઢી નાખે તેવા જોખમી પà«àª°à«‹àª—à«àª°àª¾àª®à«àª¸àª¨à«‡ ઇનà«àª¸à«àªŸà«‹àª² કરવાનો પà«àª°àª¯àª¾àª¸ કરી શકે છે.</translation>
<translation id="2969319727213777354">àªàª• સà«àª°àª•à«àª·àª¿àª¤ કનેકà«àª¶àª¨ સà«àª¥àª¾àªªàª¿àª¤ કરવા માટે, તમારી ઘડિયાળ યોગà«àª¯ રીતે સેટ હોવી જરૂરી છે. આનà«àª‚ કારણ ઠકે વેબસાઇટà«àª¸ તેઓને ઓળખવા માટે જે પà«àª°àª®àª¾àª£àªªàª¤à«àª°à«‹àª¨à«‹ ઉપયોગ કરે છે તે ચોકà«àª•àª¸ સમય અવધિ માટે જ માનà«àª¯ હોય છે. તમારા ઉપકરણની ઘડિયાળ ખોટી હોવાને લીધે, Google Chrome આ પà«àª°àª®àª¾àª£àªªàª¤à«àª°à«‹àª¨à«‡ ચકાસી શકતà«àª‚ નથી.</translation>
<translation id="2972581237482394796">&amp;ફરી કરો</translation>
<translation id="2985306909656435243">જો સકà«àª·àª® કરેલà«àª‚ હોય, તો àªàª¡àªªàª¥à«€ ફોરà«àª® ભરવા માટે Chromium આ ઉપકરણ પર તમારા કારà«àª¡àª¨à«€ àªàª• કૉપિ સંગà«àª°àª¹àª¿àª¤ કરશે.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">વિગતો છà«àªªàª¾àªµà«‹</translation>
<translation id="3587482841069643663">બધા</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">સરà«àªµàª° TLS રીનેગોશિàªàª¶àª¨ àªàª•à«àª¸à«àªŸà«‡àª‚શનને સપોરà«àªŸ કરતà«àª‚ નથી. </translation>
<translation id="36224234498066874">બà«àª°àª¾àª‰àªàª¿àª‚ગ ડેટા સાફ કરો...</translation>
<translation id="362276910939193118">પૂરà«àª£ ઇતિહાસ બતાવો</translation>
<translation id="3623476034248543066">કિંમત બતાવો</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">જો તમે આ વારંવાર જોઈ રહà«àª¯àª¾àª‚ છો, તો આ <ph name="HELP_LINK" /> અજમાવી જà«àª“.</translation>
<translation id="3658742229777143148">પà«àª¨àª°àª¾àªµàª°à«àª¤àª¨</translation>
<translation id="3678029195006412963">વિનંતી પર સહી કરી શકà«àª¯àª¾àª‚ નથી</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">લાઇસેંસીસ પૂરà«àª£</translation>
<translation id="3714780639079136834">મોબાઇલ ડેટા અથવા Wi-Fi ચાલૠકરીને</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />પà«àª°à«‹àª•à«àª¸à«€, ફાયરવોલ અને DNS ગોઠવણી તપાસીને<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">જો તમે તમારી સà«àª°àª•à«àª·àª¾àª¨àª¾ જોખમોને સમજો છો, તો તમે જોખમી પà«àª°à«‹àª—à«àª°àª¾àª®à«àª¸ દૂર કરી દેવામાં આવે તે પહેલાં <ph name="BEGIN_LINK" />આ અસલામત સાઇટની મà«àª²àª¾àª•àª¾àª¤<ph name="END_LINK" /> લઈ શકો છો.</translation>
<translation id="3739623965217189342">તમે કૉપિ કરેલ લિંક</translation>
<translation id="375403751935624634">સરà«àªµàª° ભૂલને કારણે ભાષાંતર નિષà«àª«àª³ રહà«àª¯à«àª‚.</translation>
<translation id="3759461132968374835">તમે હાલમાં કà«àª°à«‡àª¶àª¨à«€ જાણ કરી નથી. કà«àª°à«‡àª¶àª¨à«€ જાણ કરવાનà«àª‚ અકà«àª·àª® હતà«àª‚ તà«àª¯àª¾àª°à«‡ થયેલા કà«àª°à«‡àª¶ અહીં દેખાશે નહીં.</translation>
-<translation id="3788090790273268753">આ સાઇટ માટેનà«àª‚ પà«àª°àª®àª¾àª£àªªàª¤à«àª° 2016 માં સમાપà«àª¤ થાય છે અને પà«àª°àª®àª¾àª£àªªàª¤à«àª° શà«àª°à«ƒàª‚ખલા SHA-1 નો ઉપયોગ કરીને સહી કરેલ પà«àª°àª®àª¾àª£àªªàª¤à«àª° ધરાવે છે.</translation>
<translation id="382518646247711829">જો તમે કોઈ પà«àª°à«‹àª•à«àª¸à«€ સરà«àªµàª°àª¨à«‹ ઉપયોગ કરો છો...</translation>
<translation id="3828924085048779000">ખાલી પાસફà«àª°à«‡àªàª¨à«‡ અનà«àª®àª¤àª¿ નથી. </translation>
<translation id="3845539888601087042">તમે સાઇન ઇન થયેલા હોય તેવા ઉપકરણોમાંથી ઇતિહાસ બતાવી રહà«àª¯àª¾àª‚ છે. <ph name="BEGIN_LINK" />વધૠજાણો<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">કી "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">કà«àª²àª¾àª‡àª¨à«àªŸ અને સરà«àªµàª° સામાનà«àª¯ SSL પà«àª°à«‹àªŸà«‹àª•à«‹àª² સંસà«àª•àª°àª£ અથવા સાઇફર સà«àª¯à«‚ટનà«àª‚ સમરà«àª¥àª¨ કરતાં નથી.</translation>
<translation id="4079302484614802869">પà«àª°à«‹àª•à«àª¸à«€ ગોઠવણી .pac સà«àª•à«àª°àª¿àªªà«àªŸ URL નો ઉપયોગ કરવા માટે સેટ છે, નિયત પà«àª°à«‹àª•à«àª¸à«€ સરà«àªµàª°à«àª¸ માટે નહીં.</translation>
+<translation id="4098354747657067197">આગળ છેતરામણી સાઇટ છે</translation>
<translation id="4103249731201008433">ઉપકરણ અનà«àª•à«àª°à«àª®àª¾àª‚ક નંબર અમાનà«àª¯ છે</translation>
<translation id="4103763322291513355">બà«àª²à«‡àª•àª²àª¿àª¸à«àªŸ કરેલા URL ની સૂચિ અને તમારા સિસà«àªŸàª® વà«àª¯àªµàª¸à«àª¥àª¾àªªàª• દà«àªµàª¾àª°àª¾ લાગૠઅનà«àª¯ નીતિઓ જોવા માટે &lt;strong&gt;chrome://policy&lt;/strong&gt; ની મà«àª²àª¾àª•àª¾àª¤ લો.</translation>
<translation id="4110615724604346410">આ સરà«àªµàª° ઠસાબિત કરી શકà«àª¯à«àª‚ નથી કે તે <ph name="DOMAIN" /> છે; તેના સà«àª°àª•à«àª·àª¾ પà«àª°àª®àª¾àª£àªªàª¤à«àª°àª®àª¾àª‚ ભૂલો છે. આ કદાચ કોઈ ખોટી ગોઠવણી અથવા કોઈ હà«àª®àª²àª¾àª–ોર તમારા કનેકà«àª¶àª¨àª¨à«‡ અટકાવી રહà«àª¯à«‹ હોવાને કારણે થયà«àª‚ છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{કોઈ નહીં}=1{1 àªàªªà«àª²àª¿àª•à«‡àª¶àª¨ ($1)}=2{2 àªàªªà«àª²àª¿àª•à«‡àª¶àª¨ ($1, $2)}one{# àªàªªà«àª²àª¿àª•à«‡àª¶àª¨ ($1, $2, $3)}other{# àªàªªà«àª²àª¿àª•à«‡àª¶àª¨ ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">કà«àª°à«‡àª¶à«‡àª¸</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />નેટવરà«àª• ડાયગà«àª¨à«‹àª¸à«àªŸàª¿àª•à«àª¸ ચલાવવાનો પà«àª°àª¯àª¾àª¸ કરો<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">આ સાઇટ પરનà«àª‚ તમારà«àª‚ કનેકà«àª¶àª¨ પૂરà«àª£àªªàª£à«‡ સà«àª°àª•à«àª·àª¿àª¤ નથી</translation>
<translation id="4250680216510889253">નહીં</translation>
<translation id="425582637250725228">તમે કરેલા ફેરફારો સાચવવામાં આવà«àª¯àª¾àª‚ ન હોઇ શકે.</translation>
<translation id="4258748452823770588">ખરાબ હસà«àª¤àª¾àª•à«àª·àª°</translation>
<translation id="4269787794583293679">(કોઇ વપરાશકરà«àª¤àª¾àª¨àª¾àª® નથી)</translation>
+<translation id="4280429058323657511">, સમાપà«àª¤àª¿ તારીખ <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google સલામત બà«àª°àª¾àª‰àªàª¿àª‚ગને તાજેતરમાં <ph name="SITE" /> <ph name="BEGIN_LINK" />હાનિકારક પà«àª°à«‹àª—à«àª°àª¾àª®à«àª¸ મળà«àª¯àª¾àª‚<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">પેરેનà«àªŸ સૂચનો</translation>
<translation id="4304224509867189079">લૉગ ઇન કરો</translation>
<translation id="432290197980158659">સરà«àªµàª°à«‡ àªàªµà«àª‚ પà«àª°àª®àª¾àª£àªªàª¤à«àª° પà«àª°àª¸à«àª¤à«àª¤ કરà«àª¯à«àª‚ કે જે બિલà«àªŸ-ઇન અપવાદો સાથે મેળ ખાતà«àª‚ નથી. તમને સà«àª°àª•à«àª·àª¿àª¤ રાખવા માટે, આ અપેકà«àª·àª¾àª“ અમà«àª• ચોકà«àª•àª¸, ઉચà«àªš-સà«àª°àª•à«àª·àª¾ વેબસાઇટà«àª¸ માટે શામેલ કરવામાં આવી છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">લેખ શોધવામાં નિષà«àª«àª³ થયાં</translation>
+<translation id="4326324639298822553">તમારી સમાપà«àª¤àª¿ તારીખ તપાસો અને ફરી પà«àª°àª¯àª¾àª¸ કરો</translation>
<translation id="4331708818696583467">સà«àª°àª•à«àª·àª¿àª¤ નથી</translation>
+<translation id="4356973930735388585">આ સાઇટ પરના હà«àª®àª²àª¾àª–ોરો તમારા કમà«àªªà«àª¯à«àªŸàª° પર તમારી માહિતી (ઉદાહરણ તરીકે, ફોટા, પાસવરà«àª¡à«àª¸, સંદેશા અને કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡à«àª¸) ને ચોરી શકે કે કાઢી નાખે તેવા જોખમી પà«àª°à«‹àª—à«àª°àª¾àª®à«àª¸àª¨à«‡ ઇનà«àª¸à«àªŸà«‹àª² કરવાનો પà«àª°àª¯àª¾àª¸ કરી શકે છે.</translation>
<translation id="4372948949327679948">અપેકà«àª·àª¿àª¤ <ph name="VALUE_TYPE" /> મૂલà«àª¯.</translation>
<translation id="4381091992796011497">વપરાશકરà«àª¤àª¾àª¨à«àª‚ નામ:</translation>
<translation id="4394049700291259645">અકà«àª·àª® કરો</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">તમે <ph name="DOMAIN" /> પર પહોંચવાનો પà«àª°àª¯àª¤à«àª¨ કરà«àª¯à«‹, પરંતૠસરà«àªµàª°à«‡ અમાનà«àª¯ પà«àª°àª®àª¾àª£àªªàª¤à«àª° પà«àª°àª¸à«àª¤à«àª¤ કરà«àª¯à«àª‚. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">આધà«àª¨àª¿àª• સાઇફર સà«àª¯à«‚ટનો ઉપયોગ કરીને <ph name="DOMAIN" /> સાથેનà«àª‚ તમારà«àª‚ કનેકà«àª¶àª¨ àªàª¨à«àª•à«àª°àª¿àªªà«àªŸ કરાયà«àª‚ છે.</translation>
<translation id="4594403342090139922">&amp;કાઢી નાખવà«àª‚ પૂરà«àªµàªµàª¤à«â€Œ કરો</translation>
+<translation id="4619615317237390068">અનà«àª¯ ઉપકરણોમાંથી ટૅબà«àª¸</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">તમે àªàª•à«àª¸à«àªŸà«‡àª¨à«àª¶àª¨ પૃષà«àª  જોઈ રહà«àª¯àª¾àª‚ છો.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">નીતિઓ ફરીથી લોડ કરો</translation>
<translation id="4728558894243024398">પà«àª²à«‡àªŸàª«à«‹àª°à«àª®</translation>
<translation id="4744603770635761495">અમલ કરવાયોગà«àª¯ પાથ</translation>
+<translation id="4750917950439032686">તમારી માહિતી (ઉદાહરણ તરીકે, પાસવરà«àª¡à«àª¸ અથવા કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡ નંબરà«àª¸) ખાનગી હોય છે જà«àª¯àª¾àª°à«‡ તે આ સાઇટ પર મોકલવામાં આવે.</translation>
<translation id="4756388243121344051">&amp;ઇતિહાસ</translation>
+<translation id="4759118997339041434">ચà«àª•àªµàª£à«€ સà«àªµàª¤àªƒàª­àª°àª£ અકà«àª·àª® કરેલ છે</translation>
<translation id="4764776831041365478"><ph name="URL" /> પરનાં વેબપેજ અસà«àª¥àª¾àª¯à«€ ધોરણે બંધ હોઈ શકે છે અથવા તે કાયમ માટે નવા વેબ સરનામાં પર ખસેડવામાં આવà«àª¯àª¾ હોઈ શકે છે.</translation>
<translation id="4771973620359291008">કોઈ અજà«àªžàª¾àª¤ ભૂલ આવી.</translation>
<translation id="4800132727771399293">તમારી સમાપà«àª¤àª¿ તારીખ અને CVC તપાસો અને ફરીથી પà«àª°àª¯àª¾àª¸ કરો</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">નેટવરà«àª• ભૂલ</translation>
<translation id="4816492930507672669">પૃષà«àª  પર ફિટ</translation>
<translation id="4850886885716139402">જà«àª“</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{અને 1 વધૠવેબ પૃષà«àª }one{અને # વધૠવેબ પૃષà«àª }other{અને # વધૠવેબ પૃષà«àª }}</translation>
<translation id="4923417429809017348">આ પૃષà«àª  કોઈ અજà«àªžàª¾àª¤ ભાષામાંથી <ph name="LANGUAGE_LANGUAGE" /> માં અનà«àªµàª¾àª¦àª¿àª¤ કરવામાં આવà«àª¯à«àª‚ છે</translation>
+<translation id="4923459931733593730">ચà«àª•àªµàª£à«€</translation>
<translation id="4926049483395192435">ઉલà«àª²à«‡àª–િત હોવà«àª‚ આવશà«àª¯àª• છે.</translation>
<translation id="495170559598752135">કà«àª°àª¿àª¯àª¾àª“</translation>
<translation id="4958444002117714549">સૂચિ વિસà«àª¤à«ƒàª¤ કરો</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">ડાઉનલોડ કરી રહà«àª¯à«àª‚ છે</translation>
<translation id="5190835502935405962">બà«àª•àª®àª¾àª°à«àª•à«àª¸ બાર</translation>
<translation id="5199729219167945352">પà«àª°àª¯à«‹àª—à«‹</translation>
-<translation id="5199841536747119669">તમારા સૂચનો અહીં દેખાય છે</translation>
<translation id="5251803541071282808">મેઘ</translation>
<translation id="5277279256032773186">કારà«àª¯ પર Chrome નો ઉપયોગ કરી રહà«àª¯àª¾àª‚ છો? વà«àª¯àªµàª¸àª¾àª¯à«‹ તેમના કરà«àª®àªšàª¾àª°à«€àª“ માટે Chrome સેટિંગà«àª¸àª¨à«‡ સંચાલિત કરી શકે છે. વધૠજાણો</translation>
<translation id="5299298092464848405">ભૂલ વિશà«àª²à«‡àª·àª£ નીતિ</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">કà«àª°à«‡àª¶àª¨à«€ જાણ કરવાનà«àª‚ અકà«àª·àª® કરà«àª¯à«àª‚ છે.</translation>
<translation id="5317780077021120954">સાચવો</translation>
<translation id="5327248766486351172">નામ</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> પરના હà«àª®àª²àª¾àª–ોરો તમારી વà«àª¯àª•à«àª¤àª¿àª—ત માહિતી (ઉદાહરણ તરીકે, પાસવરà«àª¡à«àª¸, ફોન નંબરà«àª¸ અથવા કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡à«àª¸) ને દરà«àª¶àª¾àªµàªµàª¾ અથવા સોફà«àªŸàªµà«‡àª° ઇનà«àª¸à«àªŸà«‹àª² કરવા જેવી જોખમી વસà«àª¤à«àª“ને કરવા માટે તમને છેતરવાનો પà«àª°àª¯àª¾àª¸ કરી શકે છે.</translation>
<translation id="5359637492792381994">આ સરà«àªµàª° ઠસાબિત કરી શકà«àª¯à«àª‚ નથી કે તે <ph name="DOMAIN" /> છે; તેનà«àª‚ સà«àª°àª•à«àª·àª¾ પà«àª°àª®àª¾àª£àªªàª¤à«àª° આ સમયે માનà«àª¯ નથી. આ કદાચ કોઈ ખોટી ગોઠવણી અથવા કોઈ હà«àª®àª²àª¾àª–ોર તમારા કનેકà«àª¶àª¨àª¨à«‡ અટકાવી રહà«àª¯à«‹ હોવાને કારણે થયà«àª‚ છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">નીતિ સેટિંગà«àª¸ સà«àªŸà«‹àª° કરવામાં નિષà«àª«àª³ થયાં</translation>
+<translation id="5386426401304769735">આ સાઇટ માટેની પà«àª°àª®àª¾àª£àªªàª¤à«àª° શà«àª°à«ƒàª‚ખલા SHA-1 નો ઉપયોગ કરીને સહી કરેલ પà«àª°àª®àª¾àª£àªªàª¤à«àª° ધરાવે છે.</translation>
<translation id="5421136146218899937">બà«àª°àª¾àª‰àªàª¿àª‚ગ ડેટા સાફ કરો...</translation>
<translation id="5430298929874300616">બà«àª•àª®àª¾àª°à«àª• દૂર કરો</translation>
<translation id="5431657950005405462">તમારી ફાઇલ મળી ન હતી</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> પરનà«àª‚ àªàª®à«àª¬à«‡àª¡ કરેલ પૃષà«àª  આ કહે છે:</translation>
<translation id="5556459405103347317">ફરિથી લોડ કરો</translation>
<translation id="5565735124758917034">સકà«àª°àª¿àª¯</translation>
+<translation id="5572851009514199876">કૃપા કરીને Chrome ને પà«àª°àª¾àª°àª‚ભ કરો અને સાઇન ઇન કરો જેથી કરીને Chrome તપાસી શકે કે તમને આ સાઇટની àªàª•à«àª¸à«‡àª¸àª¨à«€ મંજૂરી છે કે કેમ.</translation>
+<translation id="5580958916614886209">તમારો સમાપà«àª¤àª¿ મહિનો તપાસો અને ફરી પà«àª°àª¯àª¾àª¸ કરો</translation>
<translation id="560412284261940334">સંચાલન સમરà«àª¥àª¿àª¤ નથી</translation>
<translation id="5610142619324316209">કનેકà«àª¶àª¨ તપાસીને</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> ઠતમને ઘણીબધી વખત રીડાયરેકà«àªŸ કરà«àª¯à«àª‚.</translation>
<translation id="5622887735448669177">શà«àª‚ તમે આ સાઇટ છોડવા માંગો છો?</translation>
<translation id="5629630648637658800">નીતિ સેટિંગà«àª¸ લોડ કરવામાં નિષà«àª«àª³ થયાં</translation>
<translation id="5631439013527180824">અમાનà«àª¯ ઉપકરણ સંચાલન ટોકન</translation>
+<translation id="5669703222995421982">વà«àª¯àª•à«àª¤àª¿àª—ત કરેલ સામગà«àª°à«€ મેળવો</translation>
+<translation id="5675650730144413517">આ પૃષà«àª  કામ કરી રહà«àª¯à«àª‚ નથી</translation>
<translation id="5677928146339483299">અવરોધિત</translation>
<translation id="5694783966845939798">તમે <ph name="DOMAIN" /> પર પહોંચવાનો પà«àª°àª¯àª¤à«àª¨ કરà«àª¯à«‹, પરંતૠસરà«àªµàª°à«‡ નબળા હસà«àª¤àª¾àª•à«àª·àª° અલà«àª—ોરિધમ (જેમ કે SHA-1)નો ઉપયોગ કરીને સહી કરેલà«àª‚ પà«àª°àª®àª¾àª£àªªàª¤à«àª° પà«àª°àª¸à«àª¤à«àª¤ કરà«àª¯à«àª‚. આનો અરà«àª¥ ઠછે કે સરà«àªµàª°à«‡ પà«àª°àª¸à«àª¤à«àª¤ કરેલા સà«àª°àª•à«àª·àª¾ પà«àª°àª®àª¾àª£àªªàª¤à«àª°à«‹ બનાવટી હોઈ શકે છે અને તે સરà«àªµàª° તમારà«àª‚ અપેકà«àª·àª¿àª¤ સરà«àªµàª° ન પણ હોય (તમે કોઈ હà«àª®àª²àª¾àª–ોર સાથે વારà«àª¤àª¾àª²àª¾àªª કરી રહà«àª¯àª¾àª‚ હોઈ શકો છો). <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">આ વેબસાઇટની ઓળખ ચકાસવામાં આવી નથી.</translation>
<translation id="5720705177508910913">વરà«àª¤àª®àª¾àª¨ વપરાશકરà«àª¤àª¾</translation>
-<translation id="572328651809341494">તાજેતરના ટેબà«àª¸</translation>
<translation id="5732392974455271431">તમારા માટે તમારા માતાપિતા તેને અનાવરોધિત કરી શકે છે</translation>
<translation id="5784606427469807560">તમારા કારà«àª¡àª¨à«€ પà«àª·à«àªŸàª¿ કરવામાં àªàª• સમસà«àª¯àª¾ આવી હતી. તમારà«àª‚ ઇનà«àªŸàª°àª¨à«‡àªŸ કનેકà«àª¶àª¨ તપાસો અને ફરીથી પà«àª°àª¯àª¾àª¸ કરો.</translation>
<translation id="5785756445106461925">વળી, આ પૃષà«àª àª®àª¾àª‚ અનà«àª¯ àªàªµàª¾ સાધનો છે જે સà«àª°àª•à«àª·àª¿àª¤ નથી. ટà«àª°àª¾àª‚àªàª¿àªŸàª®àª¾àª‚ હોવા પર અનà«àª¯ લોકો દà«àªµàª¾àª°àª¾ આ સાધનો જોઈ શકાય છે અને પૃષà«àª àª¨à«‹ દેખાવ બદલવા માટે હà«àª®àª²àª¾àª–ોર દà«àªµàª¾àª°àª¾ સંશોધિત કરવામાં આવી શકે છે.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">તમે <ph name="DOMAIN" /> પર પહોંચવાનો પà«àª°àª¯àª¤à«àª¨ કરà«àª¯à«‹, પરંતૠસરà«àªµàª°à«‡ નબળી કી ધરાવતà«àª‚ પà«àª°àª®àª¾àª£àªªàª¤à«àª° પà«àª°àª¸à«àª¤à«àª¤ કરà«àª¯à«àª‚. હà«àª®àª²àª¾àª–ોરે ખાનગી કી તોડી હોઈ શકે છે અને બને કે સરà«àªµàª° તમારà«àª‚ અપેકà«àª·àª¿àª¤ સરà«àªµàª° ન હોય (તમે કોઈ હà«àª®àª²àª¾àª–ોર સાથે વારà«àª¤àª¾àª²àª¾àªª કરી રહà«àª¯àª¾àª‚ હોઈ શકો છો). <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">નવà«àª‚ ફોલà«àª¡àª°</translation>
<translation id="5869405914158311789">આ સાઇટ પર પહોંચી શકાતà«àª‚ નથી</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">આ વેપારી માટે Google Payments દà«àªµàª¾àª°àª¾ આ પà«àª°àª•àª¾àª°àª¨àª¾ કારà«àª¡àª¨à«‡ સમરà«àª¥àª¨ નથી. કૃપા કરીને બીજà«àª‚ કારà«àª¡ પસંદ કરો.</translation>
<translation id="59174027418879706">સકà«àª·àª®</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="5966707198760109579">અઠવાડિયà«àª‚</translation>
<translation id="5967867314010545767">ઇતિહાસમાંથી દૂર કરો</translation>
<translation id="5975083100439434680">àªà«‚મ ઘટાડો</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">પà«àª°àª¯àª¾àª¸ કરો:</translation>
<translation id="6151417162996330722">સરà«àªµàª° પà«àª°àª®àª¾àª£àªªàª¤à«àª° પાસે ખૂબ લાંબી હોય àªàªµà«€ માનà«àª¯àª¤àª¾ અવધિ છે.</translation>
<translation id="6165508094623778733">વધૠજાણો</translation>
+<translation id="6177128806592000436">આ સાઇટ પરનà«àª‚ તમારà«àª‚ કનેકà«àª¶àª¨ સà«àª°àª•à«àª·àª¿àª¤ નથી</translation>
<translation id="6203231073485539293">તમારà«àª‚ ઇનà«àªŸàª°àª¨à«‡àªŸ કનેકà«àª¶àª¨ તપાસો</translation>
<translation id="6218753634732582820">Chromium માંથી સરનામà«àª‚ દૂર કરીàª?</translation>
+<translation id="6251924700383757765">ગોપનીયતા નીતિ</translation>
+<translation id="625755898061068298">તમે આ સાઇટ માટે સà«àª°àª•à«àª·àª¾ ચેતવણીઓ અકà«àª·àª® કરવાનà«àª‚ પસંદ કરà«àª¯à«àª‚ છે.</translation>
<translation id="6259156558325130047">&amp;પà«àª¨àªƒàª•à«àª°àª®àª¾àª‚કિત કરવà«àª‚ ફરી કરો</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> બà«àª•àª®àª¾àª°à«àª•à«àª¸</translation>
<translation id="6264485186158353794">સà«àª°àª•à«àª·àª¾ પર પાછા</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" />, પહોંચવા યોગà«àª¯ નથી.</translation>
<translation id="6321917430147971392">તમારી DNS સેટિંગà«àª¸ તપાસો</translation>
<translation id="6328639280570009161">નેટવરà«àª• પૂરà«àªµàª¾àª¨à«àª®àª¾àª¨àª¨à«‡ અકà«àª·àª® કરવાનો પà«àª°àª¯àª¾àª¸ કરો</translation>
+<translation id="6328786501058569169">આ સાઇટ ભà«àª°àª¾àª®àª• છે</translation>
<translation id="6337534724793800597">નામ દà«àªµàª¾àª°àª¾ નીતિઓને ફિલà«àªŸàª° કરો</translation>
<translation id="6342069812937806050">હમણાં જ</translation>
<translation id="6345221851280129312">અનામ કદ</translation>
@@ -489,6 +519,8 @@
<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="6404511346730675251">બà«àª•àª®àª¾àª°à«àª• સંપાદિત કરો</translation>
+<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> માટે સમાપà«àª¤àª¿ તારીખ અને CVC દાખલ કરો</translation>
<translation id="6414888972213066896">આ પૃષà«àª àª¨à«€ મà«àª²àª¾àª•àª¾àª¤ લો છો તે ઠીક છે કે કેમ તેવà«àª‚ તમે તમારા માતાપિતાને પૂછà«àª¯à«àª‚</translation>
<translation id="6416403317709441254">તમે અતà«àª¯àª¾àª°à«‡ <ph name="SITE" /> ની મà«àª²àª¾àª•àª¾àª¤ લઈ શકતાં નથી કારણ કે વેબસાઇટે સમજાય નહીં તેવા ઓળખપતà«àª° મોકલà«àª¯àª¾àª‚ છે જેની પર Chromium પà«àª°àª•à«àª°àª¿àª¯àª¾ કરી શકતà«àª‚ નથી. નેટવરà«àª• ભૂલો અને હà«àª®àª²àª¾ સામાનà«àª¯ રીતે અસà«àª¥àª¾àª¯à«€ છે, તેથી આ પૃષà«àª  સંભવિત રૂપે પછીથી કારà«àª¯ કરશે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">પà«àª°àª®àª¾àª£àªªàª¤à«àª° રદ કરવામાં આવà«àª¯à«àª‚ છે કે નહીં તે તપાસવામાં અકà«àª·àª® છે.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">અવગણો કારણ કે નીતિ દà«àªµàª¾àª°àª¾ ડિફૉલà«àªŸ શોધ અકà«àª·àª® કરેલી છે.</translation>
<translation id="6462969404041126431">આ સરà«àªµàª° ઠસાબિત કરી શકà«àª¯à«àª‚ નથી કે તે <ph name="DOMAIN" /> છે; તેનà«àª‚ સà«àª°àª•à«àª·àª¾ પà«àª°àª®àª¾àª£àªªàª¤à«àª° રદબાતલ કરà«àª¯à«àª‚ હોઈ શકે છે. આ કદાચ કોઈ ખોટી ગોઠવણી અથવા કોઈ હà«àª®àª²àª¾àª–ોર તમારા કનેકà«àª¶àª¨àª¨à«‡ અટકાવી રહà«àª¯à«‹ હોવાને કારણે થયà«àª‚ છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">ઉપકરણ નીતિઓ</translation>
+<translation id="6477321094435799029">Chrome ને આ પૃષà«àª  પર અસામાનà«àª¯ કોડ મળà«àª¯à«‹ અને તમારી વà«àª¯àª•à«àª¤àª¿àª—ત માહિતી (ઉદાહરણ તરીકે, પાસવરà«àª¡à«àª¸, ફોન નંબરà«àª¸ અને કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡à«àª¸)ની સà«àª°àª•à«àª·àª¾ કરવા માટે તેને અવરોધિત કરેલ છે.</translation>
<translation id="6489534406876378309">કà«àª°à«‡àª¶ અપલોડ કરવાનà«àª‚ શરૂ કરો</translation>
<translation id="6529602333819889595">&amp;કાઢી નાખવà«àª‚ ફરી કરો</translation>
<translation id="6534179046333460208">વાસà«àª¤àªµàª¿àª• વેબ સૂચનો</translation>
<translation id="6550675742724504774">વિકલà«àªªà«‹</translation>
+<translation id="6556239504065605927">સà«àª°àª•à«àª·àª¿àª¤ કનેકà«àª¶àª¨</translation>
<translation id="6563469144985748109">તમારા સંચાલકે હજી સà«àª§à«€ તેને મંજૂર કરેલ નથી</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" /> કરતાં ઓછà«àª‚</translation>
<translation id="6596325263575161958">àªàª¨à«àª•à«àª°àª¿àªªà«àª¶àª¨ વિકલà«àªªà«‹</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">આ નીતિ દૂર કરવામાં આવેલી છે.</translation>
<translation id="6652240803263749613">આ સરà«àªµàª° ઠસાબિત કરી શકà«àª¯à«àª‚ નથી કે તે <ph name="DOMAIN" /> છે; તેનà«àª‚ સà«àª°àª•à«àª·àª¾ પà«àª°àª®àª¾àª£àªªàª¤à«àª° તમારા કમà«àªªà«àª¯à«àªŸàª°àª¨à«€ ઓપરેટિંગ સિસà«àªŸàª® દà«àªµàª¾àª°àª¾ વિશà«àªµàª¸àª¨à«€àª¯ નથી. આ કોઈ ખોટી ગોઠવણીને કારણે થયà«àª‚ હશે અથવા કોઈ હà«àª®àª²àª¾àª–ોર તમારા કનેકà«àª¶àª¨àª¨à«‡ અટકાવી રહà«àª¯à«‹ છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">ફોલà«àª¡àª° સંપાદિત કરો</translation>
-<translation id="6660210980321319655">આપમેળે જાણ કરી <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Chromium માંથી ફોરà«àª® સૂચન દૂર કરીàª?</translation>
<translation id="6685834062052613830">સાઇન આઉટ કરો અને સેટઅપ પૂરà«àª£ કરો</translation>
<translation id="6710213216561001401">પહેલાનà«àª‚</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">નીતિ મૂલà«àª¯</translation>
<translation id="6757797048963528358">તમારà«àª‚ ઉપકરણ નિષà«àª•à«àª°àª¿àª¯ થઈ ગયà«àª‚ હતà«àª‚.</translation>
<translation id="6778737459546443941">તમારા માતાપિતાઠહજી સà«àª§à«€ તેને મંજૂર કરેલ નથી</translation>
+<translation id="6810899417690483278">કસà«àªŸàª®àª¾àª‡àªà«‡àª¶àª¨ ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" /> પરનà«àª‚ વેબપૃષà«àª  હાલમાં અનà«àªªàª²àª¬à«àª§ છે. તે કદાચ ઑવરલોડ થઈ ગયà«àª‚ છે અથવા જાળવણી કારà«àª¯ માટે બંધ કરેલà«àª‚ છે.</translation>
<translation id="6831043979455480757">અનà«àªµàª¾àª¦ કરો</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">સંપરà«àª• માહિતી</translation>
+<translation id="7079718277001814089">આ સાઇટમાં માલવેર છે</translation>
<translation id="7087282848513945231">પરગણà«àª‚</translation>
<translation id="7088615885725309056">વધૠજૂનà«àª‚</translation>
<translation id="7090678807593890770"><ph name="LINK" /> માટે Google પર શોધો</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" />, સà«àª°àª•à«àª·àª¾ માનકોનà«àª‚ પાલન કરતà«àª‚ નથી.</translation>
<translation id="721197778055552897">આ સમસà«àª¯àª¾ વિશે <ph name="BEGIN_LINK" />વધૠજાણો<ph name="END_LINK" />.</translation>
<translation id="7219179957768738017">કનેકà«àª¶àª¨ <ph name="SSL_VERSION" /> નો ઉપયોગ કરે છે.</translation>
+<translation id="724691107663265825">સાઇટ આગળ મૉલવેર ધરાવે છે</translation>
<translation id="724975217298816891">તમારા કારà«àª¡àª¨à«€ વિગતોને અપડેટ કરવા <ph name="CREDIT_CARD" /> માટે સમાપà«àª¤àª¿ તારીખ અને CVC દાખલ કરો. àªàª•àªµàª¾àª° તમે પà«àª·à«àªŸàª¿ કરી લો, તે પછી આ સાઇટ સાથે તમારા કારà«àª¡àª¨à«€ વિગતો શેર કરવામાં આવશે.</translation>
<translation id="725866823122871198"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> પર ખાનગી કનેકà«àª¶àª¨ સà«àª¥àª¾àªªàª¿àª¤ કરી શકાતà«àª‚ નથી કારણ કે તમારા ઉપકરણની તારીખ અને સમય (<ph name="DATE_AND_TIME" />) અયોગà«àª¯ છે.</translation>
<translation id="7269802741830436641">આ વેબ પૃષà«àª àª®àª¾àª‚ àªàª• રીડાયરેકà«àªŸ લૂપ છે</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">રદ કરો</translation>
<translation id="7667346355482952095">પરત થયેલ નીતિ ટોકન ખાલી છે અથવા વરà«àª¤àª®àª¾àª¨ ટોકન સાથે મેળ ખાતà«àª‚ નથી</translation>
<translation id="7668654391829183341">અજà«àªžàª¾àª¤ ઉપકરણ</translation>
+<translation id="7669271284792375604">આ સાઇટ પરના હà«àª®àª²àª¾àª–ોરો તમને તમારા બà«àª°àª¾àª‰àªàª¿àª‚ગ અનà«àª­àªµàª¨à«‡ નà«àª•àª¸àª¾àª¨ પહોંચાડે àªàªµàª¾ પà«àª°à«‹àª—à«àª°àª¾àª®à«àª¸ ઇનà«àª¸à«àªŸà«‰àª² કરવા માટે છેતરવાનો પà«àª°àª¯àª¾àª¸ કરી શકે છે (ઉદાહરણ તરીકે, તમારà«àª‚ હોમપેજ બદલીને અથવા તમે મà«àª²àª¾àª•àª¾àª¤ લો છો તે સાઇટà«àª¸ પર વધૠપડતી જાહેરાતો બતાવીને).</translation>
<translation id="7674629440242451245">શà«àª‚ કૂલ નવી Chrome સà«àªµàª¿àª§àª¾àª“માં રà«àªšàª¿ ધરાવો છો? chrome.com/dev પર અમારી dev ચૅનલ અજમાવી જà«àª“.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /><ph name="SITE" /> પર આગળ વધો (અસલામત)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">કૅશમાંથી આ સાઇટ લોડ કરી શકાતી નથી</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">કનેકà«àª¶àª¨ <ph name="CIPHER" /> નો ઉપયોગ કરીને, સંદેશ ઑથેંટિકેશન માટે <ph name="MAC" /> સાથે અને કી àªàª•à«àª¸à«àªšà«‡àª‚જ તંતà«àª° તરીકે <ph name="KX" /> àªàª¨à«àª•à«àª°àª¿àªªà«àªŸ કરેલà«àª‚ છે, </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="7887683347370398519">તમારà«àª‚ CVC તપાસો અને ફરીથી પà«àª°àª¯àª¾àª¸ કરો</translation>
<translation id="7912024687060120840">ફોલà«àª¡àª°àª®àª¾àª‚:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">સરà«àªµàª°àª¨à«àª‚ પà«àª°àª®àª¾àª£àªªàª¤à«àª° હજી માનà«àª¯ નથી.</translation>
<translation id="7942349550061667556">લાલ</translation>
+<translation id="7947285636476623132">તમારà«àª‚ સમાપà«àª¤àª¿ વરà«àª· તપાસો અને ફરી પà«àª°àª¯àª¾àª¸ કરો</translation>
<translation id="7951415247503192394">(32-બિટ)</translation>
<translation id="7956713633345437162">મોબાઇલ બà«àª•àª®àª¾àª°à«àª•à«àª¸</translation>
<translation id="7961015016161918242">કà«àª¯àª¾àª°à«‡àª¯ નહીં</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">હંમેશા <ph name="ORIGINAL_LANGUAGE" /> નà«àª‚ <ph name="TARGET_LANGUAGE" /> માં ભાષાંતર કરો</translation>
<translation id="7995512525968007366">નિરà«àª¦àª¿àª·à«àªŸ કરાયેલ નથી</translation>
<translation id="8012647001091218357">અમે આ પળે તમારા વાલીઓ સà«àª§à«€ પહોંચી શકà«àª¯àª¾àª‚ નથી. કૃપા કરીને ફરી પà«àª°àª¯àª¾àª¸ કરો.</translation>
+<translation id="8025119109950072390">આ સાઇટ પરના હà«àª®àª²àª¾àª–ોરો તમારી વà«àª¯àª•à«àª¤àª¿àª—ત માહિતી (ઉદાહરણ તરીકે, પાસવરà«àª¡à«àª¸, ફોન નંબરà«àª¸ અથવા કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡à«àª¸) ને દરà«àª¶àª¾àªµàªµàª¾ અથવા સોફà«àªŸàªµà«‡àª° ઇનà«àª¸à«àªŸà«‰àª² કરવા જેવી જોખમી વસà«àª¤à«àª“ને કરવા માટે તમને છેતરવાનો પà«àª°àª¯àª¾àª¸ કરી શકે છે.</translation>
+<translation id="803030522067524905">Google સલામત બà«àª°àª¾àª‰àªàª¿àª‚ગને તાજેતરમાં <ph name="SITE" /> પર ફિશિંગ મળà«àª¯à«àª‚. ફિશિંગ સાઇટà«àª¸ તમને છેતરવા માટે અનà«àª¯ વેબસાઇટà«àª¸ હોવાનો ડોળ કરે છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">આ પૃષà«àª  <ph name="SOURCE_LANGUAGE" /> માં છે. શà«àª‚ તેનો અનà«àªµàª¾àª¦ <ph name="TARGET_LANGUAGE" /> માં કરીàª?</translation>
+<translation id="8041089156583427627">પà«àª°àª¤àª¿àª¸àª¾àª¦ મોકલો</translation>
<translation id="8088680233425245692">લેખ જોવામાં નિષà«àª«àª³ થયાં.</translation>
<translation id="8089520772729574115">1 MB કરતાં ઓછà«àª‚</translation>
<translation id="8091372947890762290">સકà«àª°àª¿àª¯àª¤àª¾ સરà«àªµàª° પર બાકી છે</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719"><ph name="URL" /> પરની ફાઇલ વાંચનયોગà«àª¯ નથી. તે દૂર કરવામાં, ખસેડવામાં આવી હોઈ શકે છે અથવા ફાઇલ પરવાનગીઓ àªàª•à«àª¸à«‡àª¸ કરવાથી અટકાવતી હોઈ શકે છે.</translation>
<translation id="8194797478851900357">&amp;ખસેડવà«àª‚ પૂરà«àªµàªµàª¤à«â€Œ કરો</translation>
<translation id="8201077131113104583">ID "<ph name="EXTENSION_ID" />" સાથેના àªàª•à«àª¸àªŸà«‡àª¨à«àª¶àª¨ માટે અમાનà«àª¯ અપડેટ URL.</translation>
+<translation id="8202097416529803614">ઓરà«àª¡àª°àª¨à«‹ સારાંશ</translation>
<translation id="8218327578424803826">સોંપાયેલ સà«àª¥àª¾àª¨:</translation>
<translation id="8225771182978767009">આ કમà«àªªà«àª¯à«àªŸàª°àª¨à«‡ સેટ કરનાર વà«àª¯àª•à«àª¤àª¿àª આ સાઇટને અવરોધિત કરવાનà«àª‚ પસંદ કરà«àª¯à«àª‚ છે.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">હà«àª®àª²àª¾àª–ોરો હાલમાં <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> પર છે તે તમારા કમà«àªªà«àª¯à«àªŸàª° પર તમારી માહિતી (ઉદાહરણ તરીકે, ફોટા, પાસવરà«àª¡à«àª¸, સંદેશા અને કà«àª°à«‡àª¡àª¿àªŸ કારà«àª¡à«àª¸) ને ચોરી શકે કે કાઢી નાખે તેવા જોખમી પà«àª°à«‹àª—à«àª°àª¾àª®à«àª¸àª¨à«‡ ઇનà«àª¸à«àªŸà«‹àª² કરવાનો પà«àª°àª¯àª¾àª¸ કરી શકે છે.</translation>
<translation id="8241707690549784388">તમારા દà«àªµàª¾àª°àª¾ દાખલ કરાયેલી વપરાયેલી માહિતી માટે આ પાનà«àª‚ તમે જોઈ રહà«àª¯àª¾ છો. તે પૃષà«àª  પર પાછા જવાથી àªàªµà«€ કોઈપણ કà«àª°àª¿àª¯àª¾ ફરીથી થઈ શકે છે જે તમે પહેલા કરી હતી. શà«àª‚ તમે ચાલૠરાખવા માંગો છો?</translation>
<translation id="8249320324621329438">છેલà«àª²à«àª‚ આનયન:</translation>
<translation id="8261506727792406068">કાઢી નાખો</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">સà«àª°à«‹àª¤</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="8349305172487531364">બà«àª•àª®àª¾àª°à«àª•à«àª¸ બાર</translation>
<translation id="8363502534493474904">àªàª°àªªà«àª²à«‡àª¨ મોડ બંધ કરીને</translation>
<translation id="8364627913115013041">સેટ નથી.</translation>
<translation id="8380941800586852976">જોખમી</translation>
<translation id="8382348898565613901">તમારા તાજેતરમાં મà«àª²àª¾àª•àª¾àª¤ લીધેલ બà«àª•àª®àª¾àª°à«àª•à«àª¸ અહીં દેખાય છે</translation>
+<translation id="8398259832188219207"><ph name="UPLOAD_TIME" /> ઠકà«àª°à«‡àª¶ રિપોરà«àªŸ અપલોડ કરà«àª¯à«‹ હતો</translation>
<translation id="8412145213513410671">કà«àª°à«‡àª¶à«‡àª¸ (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">તમારે તે જ પાસફà«àª°à«‡àª બે વાર દાખલ કરવો આવશà«àª¯àª• છે.</translation>
<translation id="8428213095426709021">સેટિંગà«àª¸</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ઠપà«àª°àª¤àª¿àª¸àª¾àª¦ આપવા માટે ઘણો સમય લીધો.</translation>
<translation id="852346902619691059">આ સરà«àªµàª° ઠસાબિત કરી શકà«àª¯à«àª‚ નથી કે તે <ph name="DOMAIN" /> છે; તેનà«àª‚ સà«àª°àª•à«àª·àª¾ પà«àª°àª®àª¾àª£àªªàª¤à«àª° તમારા ઉપકરણની ઓપરેટિંગ સિસà«àªŸàª® દà«àªµàª¾àª°àª¾ વિશà«àªµàª¸àª¨à«€àª¯ નથી. આ કોઈ ખોટી ગોઠવણીને કારણે થયà«àª‚ હશે અથવા કોઈ હà«àª®àª²àª¾àª–ોર તમારા કનેકà«àª¶àª¨àª¨à«‡ અટકાવી રહà«àª¯à«‹ છે. <ph name="BEGIN_LEARN_MORE_LINK" />વધૠજાણો<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Google Payments દà«àªµàª¾àª°àª¾ આ પà«àª°àª•àª¾àª°àª¨àª¾ કારà«àª¡àª¨à«‡ સમરà«àª¥àª¨ નથી. કૃપા કરીને બીજà«àª‚ કારà«àª¡ પસંદ કરો.</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="8553075262323480129">ભાષાંતર નિષà«àª«àª³ રહà«àª¯à«àª‚ કારણ કે પૃષà«àª àª¨à«€ ભાષા નિરà«àª§àª¾àª°àª¿àª¤ થઈ શકી નથી.</translation>
<translation id="8559762987265718583"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> પર ખાનગી કનેકà«àª¶àª¨ સà«àª¥àª¾àªªàª¿àª¤ કરી શકાતà«àª‚ નથી કારણ કે તમારા ઉપકરણની તારીખ અને સમય (<ph name="DATE_AND_TIME" />) અયોગà«àª¯ છે.</translation>
-<translation id="856992080682148">આ સાઇટ માટેનà«àª‚ પà«àª°àª®àª¾àª£àªªàª¤à«àª° 2017 માં સમાપà«àª¤ થાય છે અને પà«àª°àª®àª¾àª£àªªàª¤à«àª° શà«àª°à«ƒàª‚ખલા SHA-1 નો ઉપયોગ કરીને સહી કરેલ પà«àª°àª®àª¾àª£àªªàª¤à«àª° ધરાવે છે.</translation>
<translation id="8571890674111243710">પૃષà«àª àª¨à«‡ <ph name="LANGUAGE" /> માં અનà«àªµàª¾àª¦àª¿àª¤ કરી રહà«àª¯à«àª‚ છે...</translation>
<translation id="859285277496340001">પà«àª°àª®àª¾àª£àªªàª¤à«àª°àª¨à«‡ રદ કરવામાં આવà«àª¯à«àª‚ છે કે નહિ તે તપાસવા માટે કોઈપણ મેકેનિàªàª® નિરà«àª¦àª¿àª·à«àªŸ કરતà«àª‚ નથી.</translation>
<translation id="8620436878122366504">તમારા માતાપિતાઠતેને હજી સà«àª§à«€ મંજૂર કરેલ નથી</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522"><ph name="HOST_NAME" /> નà«àª‚ &lt;abbr id="dnsDefinition"&gt;DNS સરનામà«àª‚&lt;/abbr&gt; શોધી શકાયà«àª‚ નથી. સમસà«àª¯àª¾àª¨à«àª‚ નિદાન કરી રહà«àª¯àª¾àª‚ છીàª.</translation>
<translation id="8790007591277257123">&amp;કાઢી નાખવà«àª‚ ફરી કરો</translation>
<translation id="8798099450830957504">ડિફૉલà«àªŸ</translation>
+<translation id="8800988563907321413">તમારા નજીકના સૂચનો અહીં દેખાય છે</translation>
<translation id="8804164990146287819">ગોપનીયતા નીતિ</translation>
<translation id="8820817407110198400">બà«àª•àª®àª¾àª°à«àª•à«àª¸</translation>
<translation id="8834246243508017242">સંપરà«àª•à«‹àª¨à«‹ ઉપયોગ કરીને સà«àªµàª¤àªƒàª­àª°àª£ સકà«àª·àª® કરો...</translation>
<translation id="883848425547221593">અનà«àª¯ બà«àª•àª®àª¾àª°à«àª•à«àª¸</translation>
+<translation id="884264119367021077">શિપિંગ સરનામà«àª‚</translation>
<translation id="884923133447025588">રદ કરવાની કોઈ મેકેનિàªàª® મળી નથી.</translation>
<translation id="885730110891505394">Google સાથે શેર કરવà«àª‚</translation>
<translation id="8866481888320382733">ભૂલ વિશà«àª²à«‡àª·àª£ નીતિ સેટિંગà«àª¸</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">સરà«àªµàª°àª¨àª¾ પà«àª°àª®àª¾àª£àªªàª¤à«àª°àª¨à«€ સમયસીમા સમાપà«àª¤ થઈ છે.</translation>
<translation id="8987927404178983737">મહિનો</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">સાઇટમાં આગળ હાનિકારક પà«àª°à«‹àª—à«àª°àª¾àª®à«àª¸ છે</translation>
<translation id="9001074447101275817">પà«àª°à«‹àª•à«àª¸à«€ <ph name="DOMAIN" /> ને વપરાશકરà«àª¤àª¾àª¨àª¾àª® અને પાસવરà«àª¡àª¨à«€ જરૂર છે.</translation>
<translation id="901974403500617787">ધà«àªµàªœà«‹ કે જે સિસà«àªŸàª®-વà«àª¯àª¾àªªà«€ લાગૠછે તે ફકà«àª¤ માલિક દà«àªµàª¾àª°àª¾ જ સેટ કરી શકાય છે: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">આ પૃષà«àª àª¨à«‹ <ph name="TARGET_LANGUAGE" /> માં અનà«àªµàª¾àª¦ કરવામાં આવà«àª¯à«‹ છે</translation>
+<translation id="9035022520814077154">સà«àª°àª•à«àª·àª¾ ભૂલ</translation>
<translation id="9038649477754266430">પૃષà«àª à«‹àª¨à«‡ વધૠàªàª¡àªªàª¥à«€ લોડ કરવા માટે પૂરà«àªµàª¾àª¨à«àª®àª¾àª¨ સેવાનો ઉપયોગ કરો</translation>
<translation id="9039213469156557790">વળી, આ પૃષà«àª àª®àª¾àª‚ અનà«àª¯ àªàªµàª¾ સાધનો છે જે સà«àª°àª•à«àª·àª¿àª¤ નથી. ટà«àª°àª¾àª‚àªàª¿àªŸàª®àª¾àª‚ હોવા પર અનà«àª¯ લોકો દà«àªµàª¾àª°àª¾ આ સાધનો જોઈ શકાય છે અને પૃષà«àª àª¨à«‹ વà«àª¯àªµàª¹àª¾àª° બદલવા માટે હà«àª®àª²àª¾àª–ોર દà«àªµàª¾àª°àª¾ સંશોધિત કરવામાં આવી શકે છે.</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> પરનાં હà«àª®àª²àª¾àª–ોરો તમને àªàªµàª¾ પà«àª°à«‹àª—à«àª°àª¾àª®à«àª¸ ઇનà«àª¸à«àªŸà«‹àª² કરવા માટે ભà«àª°àª®àª¿àª¤ કરવાનો પà«àª°àª¯àª¾àª¸ કરી શકે છે જે તમારા બà«àª°àª¾àª‰àªàª¿àª‚ગ અનà«àª­àªµàª¨à«‡ નà«àª•àª¸àª¾àª¨ પહોંચાડે છે (ઉદાહરણ તરીકે, તમારà«àª‚ હોમપેજ બદલીને અથવા તમે મà«àª²àª¾àª•àª¾àª¤ લો છો તે સાઇટà«àª¸ પર વધૠપડતી જાહેરાતો દરà«àª¶àª¾àªµà«€àª¨à«‡).</translation>
<translation id="9050666287014529139">પાસફà«àª°à«‡àª</translation>
<translation id="9065203028668620118">સંપાદન</translation>
<translation id="9068849894565669697">રંગ પસંદ કરો</translation>
<translation id="9076283476770535406">તેમાં વયસà«àª• સામગà«àª°à«€ હોઈ શકે છે</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> પૃષà«àª  કામ કરી રહà«àª¯à«àª‚ નથી</translation>
<translation id="9103872766612412690"><ph name="SITE" /> સામાનà«àª¯ રીતે તમારી માહિતીને સà«àª°àª•à«àª·àª¿àª¤ રાખવા માટે àªàª¨à«àª•à«àª°àª¿àªªà«àª¶àª¨àª¨à«‹ ઉપયોગ કરે છે. જà«àª¯àª¾àª°à«‡ આ સમયે Chromium દà«àªµàª¾àª°àª¾ <ph name="SITE" /> થી કનેકà«àªŸ કરવાનો પà«àª°àª¯àª¾àª¸ થયો, તà«àª¯àª¾àª°à«‡ વેબસાઇટે અસામાનà«àª¯ અને ખોટા ઓળખાણપતà«àª°à«‹àª¨à«‡ પાછા મોકલà«àª¯àª¾àª‚. આવà«àª‚ તà«àª¯àª¾àª°à«‡ થઇ શકે જà«àª¯àª¾àª°à«‡ કોઈ હà«àª®àª²àª¾àª–ોર <ph name="SITE" /> હોવાનો ડોળ કરવાનો પà«àª°àª¯àª¾àª¸ કરી રહà«àª¯à«‹ હોય અથવા કોઈ Wi-Fi સાઇન-ઇન સà«àª•à«àª°à«€àª¨à«‡ કનેકà«àª¶àª¨àª®àª¾àª‚ વિકà«àª·à«‡àªª પાડà«àª¯à«‹ હોય. તમારી માહિતી હજી પણ સà«àª°àª•à«àª·àª¿àª¤ છે કારણ કે Chromium ઠકોઈપણ ડેટા વિનિમય થાય તે પહેલાં જ કનેકà«àª¶àª¨ રોકી દીધà«àª‚.</translation>
<translation id="9137013805542155359">મૂળ બતાવો</translation>
+<translation id="9137248913990643158">આ àªàªªà«àª²àª¿àª•à«‡àª¶àª¨àª¨à«‹ ઉપયોગ કરતાં પહેલાં કૃપા કરીને Chrome ને પà«àª°àª¾àª°àª‚ભ કરો અને સાઇન ઇન કરો.</translation>
<translation id="9148507642005240123">&amp;સંપાદિત કરવà«àª‚ પૂરà«àªµàªµàª¤à«â€Œ કરો</translation>
<translation id="9157595877708044936">સેટિંગ અપ...</translation>
<translation id="9170848237812810038">&amp;પૂરà«àªµàªµàª¤à« કરો</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">ફોરà«àª® સાફ કરો</translation>
<translation id="939736085109172342">નવà«àª‚ ફોલà«àª¡àª°</translation>
<translation id="941721044073577244">àªàªµà«àª‚ લાગે છે કે તમને આ સાઇટની મà«àª²àª¾àª•àª¾àª¤ લેવા માટેની પરવાનગી નથી</translation>
-<translation id="962701380617707048">તમારા કારà«àª¡àª¨à«€ વિગતોને અપડેટ કરવા <ph name="CREDIT_CARD" /> માટે સમાપà«àª¤àª¿ તારીખ અને CVC દાખલ કરો</translation>
<translation id="969892804517981540">આધિકારિક બિલà«àª¡</translation>
<translation id="988159990683914416">વિકાસકરà«àª¤àª¾ બિલà«àª¡</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 aa76db5248b..bfe9c6f8ade 100644
--- a/chromium/components/strings/components_strings_hi.xtb
+++ b/chromium/components/strings/components_strings_hi.xtb
@@ -12,7 +12,7 @@
<translation id="1074497978438210769">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नहीं है</translation>
<translation id="1080116354587839789">चौड़ाई में फ़िट करें</translation>
<translation id="1103523840287552314"><ph name="LANGUAGE" /> का हमेशा अनà¥à¤µà¤¾à¤¦ करें</translation>
-<translation id="1107591249535594099">चेक किठहोने पर, अधिक तेज़ फ़ॉरà¥à¤® भरने के लिठChrome इस डिवाइस पर आपके कारà¥à¤¡ की पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ संगà¥à¤°à¤¹à¥€à¤¤ कर लेगा.</translation>
+<translation id="1107591249535594099">चेक किठहोने पर, अधिक तेज़ फ़ॉरà¥à¤® भरने के लिठChrome इस डिवाइस पर आपके कारà¥à¤¡ की पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ संगà¥à¤°à¤¹à¤¿à¤¤ कर लेगा.</translation>
<translation id="1111153019813902504">हाल के बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="1113869188872983271">&amp;पà¥à¤¨: कà¥à¤°à¤®à¤¿à¤¤ करना वापस लाà¤à¤‚</translation>
<translation id="1126551341858583091">सà¥à¤¥à¤¾à¤¨à¥€à¤¯ जगह का आकार <ph name="CRASH_SIZE" /> है.</translation>
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">वाई-फ़ाई से फिर से कनेकà¥à¤Ÿ करें</translation>
<translation id="1175364870820465910">&amp;पà¥à¤°à¤¿à¤‚ट करें...</translation>
<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="1206967143813997005">नाम के पहले अकà¥à¤·à¤° के गलत हसà¥à¤¤à¤¾à¤•à¥à¤·à¤°</translation>
@@ -45,7 +46,7 @@
<translation id="1375198122581997741">वरà¥à¤¶à¤¨ के बारे में</translation>
<translation id="139305205187523129"><ph name="HOST_NAME" /> ने कोई डेटा नहीं भेजा.</translation>
<translation id="1407135791313364759">सभी खोलें</translation>
-<translation id="1413809658975081374">निजता तà¥à¤°à¥à¤Ÿà¤¿</translation>
+<translation id="1413809658975081374">निजता गड़बड़ी</translation>
<translation id="1426410128494586442">हां</translation>
<translation id="1430915738399379752">पà¥à¤°à¤¿à¤‚ट करें</translation>
<translation id="1442912890475371290"><ph name="BEGIN_LINK" /><ph name="DOMAIN" /> के किसी पेज पर जाने का <ph name="END_LINK" /> पà¥à¤°à¤¯à¤¾à¤¸ अवरोधित किया गया.</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">सà¥à¤¯à¤¾à¤¨</translation>
<translation id="1629803312968146339">कà¥à¤¯à¤¾ आप चाहते हैं कि Chrome इस कारà¥à¤¡ को सहेजे?</translation>
<translation id="1640180200866533862">उपयोगकरà¥à¤¤à¤¾ नीतियां</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />साइट के मà¥à¤–पृषà¥à¤  पर विज़िट करके<ph name="END_LINK" /> देखें.</translation>
<translation id="1644184664548287040">नेटवरà¥à¤• कॉनà¥à¤«à¤¼à¤¿à¤—रेशन अमानà¥à¤¯ है और उसे आयात नहीं किया जा सकेगा.</translation>
<translation id="1644574205037202324">इतिहास</translation>
<translation id="1645368109819982629">असमरà¥à¤¥à¤¿à¤¤ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²</translation>
<translation id="1676269943528358898">आपकी जानकारी की सà¥à¤°à¤•à¥à¤·à¤¾ करने के लिठ<ph name="SITE" /> आमतौर पर à¤à¤¨à¥à¤•à¥à¤°à¤¿à¤ªà¥à¤¶à¤¨ का उपयोग करती है. जब Google Chrome ने इस बार <ph name="SITE" /> से कनेकà¥à¤Ÿ करने का पà¥à¤°à¤¯à¤¾à¤¸ किया, तो वेबसाइट ने असामानà¥à¤¯ और गलत कà¥à¤°à¥‡à¤¡à¥‡à¤‚शियल वापस भेजे. à¤à¤¸à¤¾ तब हो सकता है जब कोई हमलावर <ph name="SITE" /> होने का दावा करने का पà¥à¤°à¤¯à¤¾à¤¸ कर रहा हो या किसी वाई-फ़ाई पà¥à¤°à¤µà¥‡à¤¶ सà¥à¤•à¥à¤°à¥€à¤¨ ने कनेकà¥à¤¶à¤¨ को बाधित कर दिया हो. आपकी जानकारी अभी भी सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ है कà¥à¤¯à¥‹à¤‚कि किसी भी डेटा के आदान-पà¥à¤°à¤¦à¤¾à¤¨ से पहले ही Google Chrome ने कनेकà¥à¤¶à¤¨ को रोक दिया था.</translation>
+<translation id="168328519870909584"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> पर मौजूद हमलावर आपके डिवाइस पर à¤à¤¸à¥‡ खतरनाक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® इंसà¥à¤Ÿà¥‰à¤² करने की कोशिश कर सकते हैं जो आपकी जानकारी (उदाहरण के लिà¤, फ़ोटो, पासवरà¥à¤¡, संदेश और कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) को चà¥à¤°à¤¾ लेते हैं या उसे हटा देते हैं.</translation>
<translation id="168841957122794586">सरà¥à¤µà¤° पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° में कमज़ोर कà¥à¤°à¤¿à¤ªà¥à¤Ÿà¥‹à¤—à¥à¤°à¤¾à¤«à¤¼à¤¿à¤• कà¥à¤‚जी है.</translation>
-<translation id="1701955595840307032">सà¥à¤à¤¾à¤ˆ गई सामगà¥à¤°à¥€ पà¥à¤°à¤¾à¤ªà¥à¤¤ करें</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">आपको <ph name="NAME" /> से इस साइट पर जाने की अनà¥à¤®à¤¤à¤¿ लेनी होगी</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,15 +81,18 @@
<translation id="1753706481035618306">पृषà¥â€à¤  संखà¥â€à¤¯à¤¾</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows नेटवरà¥à¤• निदान चलाकर देखें<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">कृपया अपना समनà¥â€à¤µà¤¯à¤¨ पासफà¥à¤°à¥‡à¤œà¤¼ अपडेट करें.</translation>
+<translation id="1787142507584202372">आपके दà¥à¤µà¤¾à¤°à¤¾ खोले गठटैब, यहां दिखाई देंगे</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग को हाल ही में <ph name="SITE" /> पर <ph name="BEGIN_LINK" />मैलवेयर का पता चला<ph name="END_LINK" /> है. आमतौर पर सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रहने वाली वेबसाइट कभी-कभी मैलवेयर से संकà¥à¤°à¤®à¤¿à¤¤ हो जाती हैं. दà¥à¤°à¥à¤­à¤¾à¤µà¤¨à¤¾à¤ªà¥‚रà¥à¤£ सामगà¥à¤°à¥€ <ph name="SUBRESOURCE_HOST" /> से आती है, जिसे जà¥à¤žà¤¾à¤¤ मैलवेयर वितरक कहा जाता है. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">अमानà¥à¤¯ अनà¥à¤°à¥‹à¤§ या अनà¥à¤°à¥‹à¤§ पैरामीटर</translation>
+<translation id="1834321415901700177">इस साइट में हानिकारक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® हैं</translation>
<translation id="1838667051080421715">आप à¤à¤• वेब पेज का सà¥à¤°à¥‹à¤¤ देख रहे हैं.</translation>
<translation id="1871208020102129563">पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ को फ़िकà¥â€à¤¸à¥â€à¤¡ पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ सरà¥à¤µà¤° का उपयोग करने के लिठसेट किया गया है, .pac सà¥â€à¤•à¥à¤°à¤¿à¤ªà¥â€à¤Ÿ फ़ाइल का उपयोग करने के लिठनहीं.</translation>
<translation id="1883255238294161206">सूची संकà¥à¤·à¤¿à¤ªà¥à¤¤ करें</translation>
<translation id="1898423065542865115">फ़िलà¥à¤Ÿà¤° किया जा रहा है</translation>
<translation id="194030505837763158"><ph name="LINK" /> पर जाà¤à¤‚</translation>
<translation id="1962204205936693436"><ph name="DOMAIN" /> बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
-<translation id="1973335181906896915">कà¥à¤°à¤®à¤¬à¤¦à¥à¤§ करने में तà¥à¤°à¥à¤Ÿà¤¿</translation>
+<translation id="1973335181906896915">कà¥à¤°à¤®à¤¬à¤¦à¥à¤§ करने में गड़बड़ी</translation>
<translation id="1974060860693918893">उनà¥à¤¨à¤¤</translation>
<translation id="2001146170449793414">{COUNT,plural, =1{और 1 अधिक}one{और # अधिक}other{और # अधिक}}</translation>
<translation id="2025186561304664664">पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ सà¥â€à¤µà¤¤: कॉनà¥â€à¤«à¤¼â€à¤¿à¤—र पर सेट है.</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">ज़िप कोड</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 सà¥à¤à¤¾à¤µ}one{# सà¥à¤à¤¾à¤µ}other{# सà¥à¤à¤¾à¤µ}}</translation>
<translation id="2065985942032347596">पà¥à¤°à¤®à¤¾à¤£à¥€à¤•à¤°à¤£ आवशà¥à¤¯à¤•</translation>
-<translation id="2079545284768500474">पूरà¥à¤µà¤µà¤¤à¥ करें</translation>
+<translation id="2079545284768500474">वापस लाà¤à¤‚</translation>
<translation id="20817612488360358">सिसà¥â€à¤Ÿà¤® पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ सेटिंग उपयोग किठजाने के लिठसेट हैं लेकिन कोई सà¥à¤ªà¤·à¥â€à¤Ÿ पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ कॉनà¥à¥žà¤¿à¤—रेशन भी निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ है.</translation>
<translation id="2086652334978798447">Google दà¥à¤µà¤¾à¤°à¤¾ सà¥à¤à¤¾à¤ˆ गई वैयकà¥à¤¤à¤¿à¤•à¥ƒà¤¤ सामगà¥à¤°à¥€ पà¥à¤°à¤¾à¤ªà¥à¤¤ करने के लिà¤, Chrome में पà¥à¤°à¤µà¥‡à¤¶ करें.</translation>
<translation id="2089090684895656482">कम</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">नेटिव कà¥à¤²à¤¾à¤‡à¤‚ट</translation>
<translation id="213826338245044447">मोबाइल बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="2148716181193084225">आज</translation>
-<translation id="2149973817440762519">बà¥à¤•à¤®à¤¾à¤°à¥à¤• संपादित करें</translation>
<translation id="2154054054215849342">आपके डोमेन के लिठसिंक करने की सà¥à¤µà¤¿à¤§à¤¾ उपलबà¥â€à¤§ नहीं है</translation>
<translation id="2166049586286450108">पूरà¥à¤£ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤•à¥€à¤¯ à¤à¤•à¥à¤¸à¥‡à¤¸</translation>
<translation id="2166378884831602661">यह साइट सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ कनेकà¥à¤¶à¤¨ पà¥à¤°à¤¦à¤¾à¤¨ नहीं कर सकती</translation>
@@ -118,7 +122,7 @@
<translation id="2230458221926704099"><ph name="BEGIN_LINK" />निदान à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨<ph name="END_LINK" /> का उपयोग करके अपने कनेकà¥à¤¶à¤¨ को ठीक करें</translation>
<translation id="2239100178324503013">अभी भेजें</translation>
<translation id="225207911366869382">यह मान इस नीति के लिठहटा दिया गया है.</translation>
-<translation id="2262243747453050782">HTTP तà¥à¤°à¥à¤Ÿà¤¿</translation>
+<translation id="2262243747453050782">HTTP गड़बड़ी</translation>
<translation id="2282872951544483773">अनà¥à¤ªà¤²à¤¬à¥à¤§ पà¥à¤°à¤¯à¥‹à¤—</translation>
<translation id="2292556288342944218">आपका इंटरनेट कनेकà¥à¤¶à¤¨ अवरà¥à¤¦à¥à¤§ है</translation>
<translation id="229702904922032456">किसी रूट या मधà¥à¤¯à¤µà¤°à¥à¤¤à¥€ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° की अवधि समापà¥à¤¤ हो गई है.</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">आप इस समय <ph name="SITE" /> पर विज़िट नहीं कर सकते कà¥à¤¯à¥‹à¤‚कि वेबसाइट HSTS का उपयोग करती है. नेटवरà¥à¤• की गड़बड़ियां और हमले आमतौर पर असà¥à¤¥à¤¾à¤¯à¥€ होते हैं, इसलिठसंभवत: यह पेज बाद में काम करेगा. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155"><ph name="ENTRY_INDEX" /> अनà¥à¤•à¥à¤°à¤®à¤£à¤¿à¤•à¤¾ पर अमानà¥à¤¯ बà¥à¤•à¤®à¤¾à¤°à¥à¤• को अनदेखा किया गया</translation>
<translation id="2354001756790975382">अनà¥à¤¯ बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
+<translation id="2355395290879513365">आप इस साइट पर जिन चितà¥à¤°à¥‹à¤‚ को देख रहे हैं, हो सकता है कि वे हमलावरों को दिखाई दें और हमलावर उनà¥à¤¹à¥‡à¤‚ बदलने के लिठआपको भà¥à¤°à¤®à¤¿à¤¤ करें.</translation>
<translation id="2359808026110333948">जारी रखें</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> पर कैपà¥à¤šà¤° की गई ख़राबी रिपोरà¥à¤Ÿ अपलोड नहीं की गई</translation>
<translation id="2367567093518048410">सà¥à¤¤à¤°</translation>
@@ -138,12 +143,13 @@
<translation id="2392959068659972793">कोई भी मान सेट नहीं की गई नीतियां दिखाà¤à¤‚</translation>
<translation id="2396249848217231973">&amp;हटाना वापस लाà¤à¤‚</translation>
<translation id="2455981314101692989">इस वेब पृषà¥â€à¤  पर इस फ़ॉरà¥à¤® को सà¥â€à¤µà¤¤: भरना अकà¥à¤·à¤® किया गया है.</translation>
+<translation id="2460160116472764928">Google सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग को हाल ही में <ph name="SITE" /> पर <ph name="BEGIN_LINK" />मैलवेयर का पता चला<ph name="END_LINK" /> है. आमतौर पर सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रहने वाली वेबसाइट कभी-कभी मैलवेयर से संकà¥à¤°à¤®à¤¿à¤¤ हो जाती हैं. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">भरें</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />नेटवरà¥à¤• निदान चलाकर देखें<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">अमानà¥â€à¤¯ खोज URL.</translation>
<translation id="2491120439723279231">सरà¥à¤µà¤° के पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° में तà¥à¤°à¥à¤Ÿà¤¿à¤¯à¤¾à¤‚ हैं.</translation>
<translation id="2495083838625180221">JSON पारà¥à¤¸à¤°</translation>
-<translation id="2495093607237746763">यदि चेक किया गया हो, तो अधिक तेज़ी से फ़ॉरà¥à¤® भरने के लिठकà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® इस डिवाइस पर आपके कारà¥à¤¡ की कॉपी संगà¥à¤°à¤¹à¥€à¤¤ करेगा.</translation>
+<translation id="2495093607237746763">यदि चेक किया गया हो, तो अधिक तेज़ी से फ़ॉरà¥à¤® भरने के लिठकà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® इस डिवाइस पर आपके कारà¥à¤¡ की कॉपी संगà¥à¤°à¤¹à¤¿à¤¤ करेगा.</translation>
<translation id="2498091847651709837">नया कारà¥à¤¡ सà¥â€à¤•à¥ˆà¤¨ करें</translation>
<translation id="2501278716633472235">वापस जाà¤à¤‚</translation>
<translation id="2515629240566999685">अपने कà¥à¤·à¥‡à¤¤à¥à¤° में सिगà¥à¤¨à¤² की जांच करें</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">कà¥à¤¯à¤¾ आप वाकई अपने इतिहास से इन पृषà¥à¤ à¥‹à¤‚ को हटाना चाहते हैं?</translation>
<translation id="2677748264148917807">छोड़ें</translation>
<translation id="269990154133806163">सरà¥à¤µà¤° ने à¤à¤¸à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° पà¥à¤°à¤¸à¥à¤¤à¥à¤¤ किया है जिसे पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° पारदरà¥à¤¶à¤¿à¤¤à¤¾ नीति का उपयोग करके सारà¥à¤µà¤œà¤¨à¤¿à¤• रूप से पà¥à¤°à¤•à¤Ÿ नहीं किया गया था. कà¥à¤› पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤°à¥‹à¤‚ के लिठयह सà¥à¤¨à¤¿à¤¶à¥à¤šà¤¿à¤¤ करना आवशà¥à¤¯à¤• है कि वे विशà¥à¤µà¤¸à¤¨à¥€à¤¯ हैं और हमलावरों से सà¥à¤°à¤•à¥à¤·à¤¾ करते हैं. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">पठन सूची</translation>
<translation id="2704283930420550640">मान का पà¥à¤°à¤¾à¤°à¥‚प से मिलान नहीं होता.</translation>
<translation id="2704951214193499422">कà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® इस समय आपके कारà¥à¤¡ की पà¥à¤·à¥à¤Ÿà¤¿ नहीं कर सका. कृपया बाद में पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें.</translation>
<translation id="2705137772291741111">इस साइट की सहेजी गई (संचित) कॉपी पढ़ने योगà¥à¤¯ नहीं थी.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">आपने <ph name="DOMAIN" /> पर पहà¥à¤‚चने का पà¥à¤°à¤¯à¤¾à¤¸ किया, लेकिन सरà¥à¤µà¤° दà¥à¤µà¤¾à¤°à¤¾ पà¥à¤°à¤¸à¥à¤¤à¥à¤¤ किठगठपà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° को उसके जारीकरà¥à¤¤à¤¾ ने निरसà¥à¤¤ कर दिया है. इसका अरà¥à¤¥ है कि सरà¥à¤µà¤° दà¥à¤µà¤¾à¤°à¤¾ पà¥à¤°à¤¸à¥à¤¤à¥à¤¤ सà¥à¤°à¤•à¥à¤·à¤¾ कà¥à¤°à¥‡à¤¡à¥‡à¤‚शियल पर बिलà¥à¤•à¥à¤² भी विशà¥à¤µà¤¾à¤¸ नहीं किया जाना चाहिà¤. हो सकता है आप किसी हमलावर से बातचीत कर रहे हों. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">अनà¥à¤®à¤¤à¤¿ मांगें</translation>
<translation id="2713444072780614174">सफ़ेद</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>
@@ -186,9 +194,10 @@
<translation id="2948083400971632585">आप किसी कनेकà¥à¤¶à¤¨ के लिठकॉनà¥à¤«à¤¼à¤¿à¤—र की गई किसी भी पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ को सेटिंग पेज से अकà¥à¤·à¤® कर सकते हैं.</translation>
<translation id="2955913368246107853">खोज बार बंद करें</translation>
<translation id="2958431318199492670">नेटवरà¥à¤• कॉनà¥à¤«à¤¼à¤¿à¤—रेशन ONC मानक का पालन नहीं करता. कॉनà¥à¤«à¤¼à¤¿à¤—रेशन के कà¥à¤› भाग आयात नहीं किठजा सकते हैं.</translation>
+<translation id="29611076221683977"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> पर मौजूद हमलावर आपके Mac पर à¤à¤¸à¥‡ खतरनाक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® इंसà¥à¤Ÿà¥‰à¤² करने की कोशिश कर सकते हैं जो आपकी जानकारी (उदाहरण के लिà¤, फ़ोटो, पासवरà¥à¤¡, संदेश और कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) को चà¥à¤°à¤¾à¤¤à¥‡ हैं या उसे हटा देते हैं.</translation>
<translation id="2969319727213777354">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ कनेकà¥â€à¤¶à¤¨ सà¥â€à¤¥à¤¾à¤ªà¤¿à¤¤ करने के लिà¤, आपकी घड़ी को सही तरीके से सेट किठजाने की आवशà¥â€à¤¯à¤•à¤¤à¤¾ है. à¤à¤¸à¤¾ इसलिठकà¥â€à¤¯à¥‹à¤‚कि वेबसाइटों दà¥à¤µà¤¾à¤°à¤¾ सà¥â€à¤µà¤¯à¤‚ की पहचान करने के लिठउपयोग किठजाने वाले पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° केवल विशिषà¥â€à¤Ÿ समयावधियों के लिठही मानà¥â€à¤¯ होते हैं. चूंकि आपके डिवाइस की घड़ी गलत है, इसलिठGoogle Chrome इन पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤°à¥‹à¤‚ का सतà¥â€à¤¯à¤¾à¤ªà¤¨ नहीं कर सकता.</translation>
<translation id="2972581237482394796">&amp;फिर से करें</translation>
-<translation id="2985306909656435243">यदि सकà¥à¤·à¤® किया गया हो, तो अधिक तेज़ी से फ़ॉरà¥à¤® भरने के लिठकà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® इस डिवाइस पर आपके कारà¥à¤¡ की à¤à¤• कॉपी संगà¥à¤°à¤¹à¥€à¤¤ करेगा.</translation>
+<translation id="2985306909656435243">यदि सकà¥à¤·à¤® किया गया हो, तो अधिक तेज़ी से फ़ॉरà¥à¤® भरने के लिठकà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® इस डिवाइस पर आपके कारà¥à¤¡ की à¤à¤• कॉपी संगà¥à¤°à¤¹à¤¿à¤¤ करेगा.</translation>
<translation id="2991174974383378012">वेबसाइटों के साथ साà¤à¤¾à¤•à¤°à¤£</translation>
<translation id="3005723025932146533">सहेजी गई पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ दिखाà¤à¤‚</translation>
<translation id="3008447029300691911"><ph name="CREDIT_CARD" /> का CVC डालें. आपकी तरफ से पà¥à¤·à¥à¤Ÿà¤¿ हो जाने पर, आपके कारà¥à¤¡ के विवरण इस साइट के साथ साà¤à¤¾ किठजाà¤à¤‚गे.</translation>
@@ -204,7 +213,7 @@
<translation id="3109728660330352905">आपके पास इस पेज को देखने के लिठपà¥à¤°à¤¾à¤§à¤¿à¤•à¤°à¤£ नहीं है.</translation>
<translation id="31207688938192855"><ph name="BEGIN_LINK" />कनेकà¥à¤Ÿà¤¿à¤µà¤¿à¤Ÿà¥€ निदान चलाकर देखें<ph name="END_LINK" />.</translation>
<translation id="3145945101586104090">उतà¥à¤¤à¤° डीकोड करने में विफल</translation>
-<translation id="3150653042067488994">असà¥à¤¥à¤¾à¤¯à¥€ सरà¥à¤µà¤° तà¥à¤°à¥à¤Ÿà¤¿</translation>
+<translation id="3150653042067488994">असà¥à¤¥à¤¾à¤¯à¥€ सरà¥à¤µà¤° गड़बड़ी</translation>
<translation id="3157931365184549694">पà¥à¤¨à¤°à¥à¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करें</translation>
<translation id="3167968892399408617">आपके दà¥à¤µà¤¾à¤°à¤¾ गà¥à¤ªà¥à¤¤ टैब में देखे जाने वाले पेज, आपके दà¥à¤µà¤¾à¤°à¤¾ अपने सभी गà¥à¤ªà¥à¤¤ टैब बंद कर देने के बाद आपके बà¥à¤°à¤¾à¤‰à¥›à¤° के इतिहास, कà¥à¤•à¥€ संगà¥à¤°à¤¹, या खोज इतिहास में नहीं रहेंगे. आपके दà¥à¤µà¤¾à¤°à¤¾ डाउनलोड की गईं सभी फ़ाइलें या बनाठगठबà¥à¤•à¤®à¤¾à¤°à¥à¤• रख लिठजाà¤à¤‚गे.</translation>
<translation id="3169472444629675720">तलाश करें</translation>
@@ -229,10 +238,10 @@
<translation id="3340978935015468852">सेटिंग</translation>
<translation id="3345135638360864351">इस साइट को à¤à¤•à¥â€à¤¸à¥‡à¤¸ करने का आपका अनà¥à¤°à¥‹à¤§ <ph name="NAME" /> को नहीं भेजा जा सका. कृपया पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें.</translation>
<translation id="3355823806454867987">पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सेटिंग बदलें...</translation>
-<translation id="3369192424181595722">घड़ी तà¥à¤°à¥à¤Ÿà¤¿</translation>
+<translation id="3369192424181595722">घड़ी गड़बड़ी</translation>
<translation id="337363190475750230">पà¥à¤°à¤¾à¤µà¤§à¤¾à¤¨ रदà¥à¤¦</translation>
-<translation id="3377188786107721145">नीति पारà¥à¤¸ तà¥à¤°à¥à¤Ÿà¤¿</translation>
-<translation id="3380365263193509176">अजà¥à¤žà¤¾à¤¤ तà¥à¤°à¥à¤Ÿà¤¿</translation>
+<translation id="3377188786107721145">नीति पारà¥à¤¸ गड़बड़ी</translation>
+<translation id="3380365263193509176">अजà¥à¤žà¤¾à¤¤ गड़बड़ी</translation>
<translation id="3380864720620200369">कà¥à¤²à¤¾à¤‡à¤‚ट आईडी:</translation>
<translation id="340013220407300675">हो सकता है हमलावर <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> से आपकी जानकारी चà¥à¤°à¤¾à¤¨à¥‡ का पà¥à¤°à¤¯à¤¾à¤¸ कर रहे हों (उदाहरण के लिठपासवरà¥à¤¡, संदेश या कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡).</translation>
<translation id="3422472998109090673"><ph name="HOST_NAME" /> वरà¥à¤¤à¤®à¤¾à¤¨ में पहà¥à¤‚च योगà¥à¤¯ नहीं है.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">विवरण छà¥à¤ªà¤¾à¤à¤‚</translation>
<translation id="3587482841069643663">सभी</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">सरà¥à¤µà¤°, TLS फिर से मधà¥à¤¯à¤¸à¥à¤¥à¤¤à¤¾ विसà¥à¤¤à¤¾à¤° का समरà¥à¤¥à¤¨ नहीं करता है.</translation>
<translation id="36224234498066874">बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग डेटा साफ़ करें...</translation>
<translation id="362276910939193118">संपूरà¥à¤£ इतिहास दिखाà¤à¤‚</translation>
<translation id="3623476034248543066">मान दिखाà¤à¤‚</translation>
@@ -271,19 +279,20 @@
<translation id="3655670868607891010">यदि आपको यह बार-बार दिखाई दे रहा हो, तो इन <ph name="HELP_LINK" /> को आज़माà¤à¤‚.</translation>
<translation id="3658742229777143148">पà¥à¤¨à¤°à¥€à¤•à¥à¤·à¤£</translation>
<translation id="3678029195006412963">अनà¥à¤°à¥‹à¤§ पर हसà¥à¤¤à¤¾à¤•à¥à¤·à¤° नहीं किया जा सका</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="3696411085566228381">कोई नहीं</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">लोड हो रहा है...</translation>
+<translation id="370665806235115550">लोड हो रही हैं...</translation>
<translation id="3712624925041724820">लाइसेंस समापà¥à¤¤ हो गà¤</translation>
<translation id="3714780639079136834">मोबाइल डेटा या वाई-फ़ाई चालू करें</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />पà¥à¤°à¥‰à¤•à¥à¤¸à¥€, फायरवॉल और DNS कॉनà¥à¥žà¤¿à¤—रेशन की जांच करें<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">यदि आप अपनी सà¥à¤°à¤•à¥à¤·à¤¾ में होने वाले जोखिमों को समà¤à¤¤à¥‡ हैं, तो आप खतरनाक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® निकाले जाने से पहले <ph name="BEGIN_LINK" />इस असà¥à¤°à¤•à¥à¤·à¤¿à¤¤ साइट पर विज़िट<ph name="END_LINK" /> कर सकते हैं.</translation>
<translation id="3739623965217189342">काॅपी किया गया लिंक</translation>
-<translation id="375403751935624634">सरà¥à¤µà¤° तà¥à¤°à¥à¤Ÿà¤¿ के कारण अनà¥à¤µà¤¾à¤¦ विफल.</translation>
+<translation id="375403751935624634">सरà¥à¤µà¤° गड़बड़ी के कारण अनà¥à¤µà¤¾à¤¦ विफल.</translation>
<translation id="3759461132968374835">आपके पास हाल ही में रिपोरà¥à¤Ÿ किठगठकà¥à¤°à¥ˆà¤¶ नहीं हैं. कà¥à¤°à¥ˆà¤¶ रिपोरà¥à¤Ÿà¤¿à¤‚ग अकà¥à¤·à¤® होने के दौरान होने वाले कà¥à¤°à¥ˆà¤¶ यहां दिखाई नहीं देंगे.</translation>
-<translation id="3788090790273268753">इस साइट के लिठपà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° की समय-सीमा 2016 में समापà¥à¤¤ होगी और पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° की शà¥à¤°à¥ƒà¤‚खला में, SHA-1 का उपयोग करके हसà¥à¤¤à¤¾à¤•à¥à¤·à¤° किया गया पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° शामिल है.</translation>
<translation id="382518646247711829">यदि आप पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¤° का उपयोग करते हैं...</translation>
<translation id="3828924085048779000">खाली पासफ़à¥à¤°à¥‡à¤œà¤¼ की अनà¥à¤®à¤¤à¤¿ नहीं है.</translation>
<translation id="3845539888601087042">आपके पà¥à¤°à¤µà¥‡à¤¶ किठगठडिवाइस का इतिहास दिखाया जा रहा है. <ph name="BEGIN_LINK" />अधिक जानें<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">कà¥à¤‚जी "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">कà¥à¤²à¤¾à¤‡à¤‚ट और सरà¥à¤µà¤°, सामानà¥à¤¯ SSL पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤² वरà¥à¤¶à¤¨ या सिफ़र सà¥à¤‡à¤Ÿ का समरà¥à¤¥à¤¨ नहीं करते हैं.</translation>
<translation id="4079302484614802869">पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ कॉनà¥â€à¤«à¤¼à¤¿à¤—रेशन को .pac सà¥â€à¤•à¥à¤°à¤¿à¤ªà¥â€à¤Ÿ URL का उपयोग करने के लिठसेट किया जाता है, फ़िकà¥â€à¤¸à¥â€à¤¡ पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ सरà¥à¤µà¤° के लिठनहीं.</translation>
+<translation id="4098354747657067197">आगे भà¥à¤°à¤¾à¤®à¤• साइट है</translation>
<translation id="4103249731201008433">डिवाइस की कà¥à¤°à¤® संखà¥à¤¯à¤¾ अमानà¥à¤¯ है</translation>
<translation id="4103763322291513355">काली सूची में डाले गठURL तथा आपके सिसà¥à¤Ÿà¤® वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤• दà¥à¤µà¤¾à¤°à¤¾ लागू की गई अनà¥à¤¯ नीतियों को देखने के लिठ&lt;strong&gt;chrome://policy&lt;/strong&gt; पर जाà¤à¤‚.</translation>
<translation id="4110615724604346410">यह सरà¥à¤µà¤° पà¥à¤°à¤®à¤¾à¤£à¤¿à¤¤ नहीं कर सका कि यह <ph name="DOMAIN" /> है; इसके सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° में गड़बड़ियां हैं. à¤à¤¸à¤¾ गलत कॉनà¥à¤«à¤¼à¤¿à¤—रेशन के कारण या किसी हमलावर दà¥à¤µà¤¾à¤°à¤¾ आपके कनेकà¥à¤¶à¤¨ में बाधा डालने के कारण हो सकता है. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{कोई नहीं}=1{1 à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ ($1)}=2{2 à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ ($1, $2)}one{# à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ ($1, $2, $3)}other{# à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">कà¥à¤°à¥ˆà¤¶</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />नेटवरà¥à¤• निदान चलाकर देखें<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">इस साइट से आपका कनेकà¥à¤¶à¤¨ पूरी तरह से सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नहीं है</translation>
<translation id="4250680216510889253">नहीं</translation>
<translation id="425582637250725228">हो सकता है कि आपके दà¥à¤µà¤¾à¤°à¤¾ किठगठबदलाव सहेजे ना जाà¤à¤‚.</translation>
<translation id="4258748452823770588">खराब हसà¥à¤¤à¤¾à¤•à¥à¤·à¤°</translation>
<translation id="4269787794583293679">(कोई उपयोगकरà¥à¤¤à¤¾ नाम नहीं)</translation>
+<translation id="4280429058323657511">, समापà¥à¤¤à¤¿ दिनांक <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग को हाल ही में <ph name="SITE" /> पर <ph name="BEGIN_LINK" />हानिकारक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® मिले हैं<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">अभिभावक सà¥à¤à¤¾à¤µ</translation>
<translation id="4304224509867189079">पà¥à¤°à¤µà¥‡à¤¶ करें</translation>
<translation id="432290197980158659">सरà¥à¤µà¤° ने à¤à¤¸à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° पà¥à¤°à¤¸à¥à¤¤à¥à¤¤ किया जिसका मिलान अंतरà¥à¤¨à¤¿à¤¹à¤¿à¤¤ अपेकà¥à¤·à¤¾à¤“ं से नहीं होता. आपकी सà¥à¤°à¤•à¥à¤·à¤¾ करने के लिà¤, ये अपेकà¥à¤·à¤¾à¤à¤‚ कà¥à¤› निशà¥à¤šà¤¿à¤¤, उचà¥à¤š-सà¥à¤°à¤•à¥à¤·à¤¾ वेबसाइट के लिठशामिल की गई हैं. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">लेख ढूंà¥à¤¨à¥‡ में विफल</translation>
+<translation id="4326324639298822553">अपना समापà¥à¤¤à¤¿ दिनांक जांचें और फिर से कोशिश करें</translation>
<translation id="4331708818696583467">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नहीं है</translation>
+<translation id="4356973930735388585">इस साइट पर मौजूद हमलावर आपके कंपà¥à¤¯à¥‚टर पर à¤à¤¸à¥‡ खतरनाक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® इंसà¥à¤Ÿà¥‰à¤² करने की कोशिश कर सकते हैं जो आपकी जानकारी (उदाहरण के लिà¤, फ़ोटो, पासवरà¥à¤¡, संदेश और कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) चà¥à¤°à¤¾ लेते हैं या उसे हटा देते हैं.</translation>
<translation id="4372948949327679948">अपेकà¥à¤·à¤¿à¤¤ <ph name="VALUE_TYPE" /> मान.</translation>
<translation id="4381091992796011497">उपयोगकरà¥à¤¤à¤¾ नाम:</translation>
<translation id="4394049700291259645">अकà¥à¤·à¤® करें</translation>
@@ -339,7 +354,7 @@
<translation id="4432688616882109544"><ph name="HOST_NAME" /> ने आपका लॉगिन पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° सà¥à¤µà¥€à¤•à¤¾à¤° नहीं किया है या हो सकता है कि पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° उपलबà¥à¤§ नहीं कराया गया हो.</translation>
<translation id="443673843213245140">पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ का उपयोग अकà¥à¤·à¤® है लेकिन कोई सà¥â€à¤ªà¤·à¥à¤Ÿ पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ कॉनà¥à¥žà¤¿à¤—रेशन निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ किया गया है.</translation>
<translation id="4492190037599258964">'<ph name="SEARCH_STRING" />' के खोज परिणाम</translation>
-<translation id="4506176782989081258">सतà¥â€à¤¯à¤¾à¤ªà¤¨ तà¥à¤°à¥à¤Ÿà¤¿: <ph name="VALIDATION_ERROR" /></translation>
+<translation id="4506176782989081258">सतà¥â€à¤¯à¤¾à¤ªà¤¨ गड़बड़ी: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">सिसà¥à¤Ÿà¤® वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤• से संपरà¥à¤• करें</translation>
<translation id="450710068430902550">वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤• के साथ साà¤à¤¾ करना</translation>
<translation id="4522570452068850558">विवरण</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">आपने <ph name="DOMAIN" /> में पहà¥à¤‚चने का पà¥à¤°à¤¯à¤¾à¤¸ किया, लेकिन सरà¥à¤µà¤° ने अमानà¥à¤¯ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° पà¥à¤°à¤¸à¥à¤¤à¥à¤¤ किया. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459"><ph name="DOMAIN" /> से आपके कनेकà¥à¤¶à¤¨ को किसी आधà¥à¤¨à¤¿à¤• सिफ़र सà¥à¤‡à¤Ÿ का उपयोग करके à¤à¤¨à¥â€à¤•à¥à¤°à¤¿à¤ªà¥â€à¤Ÿ किया गया है.</translation>
<translation id="4594403342090139922">&amp;हटाना वापस लाà¤à¤‚</translation>
+<translation id="4619615317237390068">अनà¥à¤¯ डिवाइस के टैब</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">आप à¤à¤• à¤à¤•à¥à¤¸à¤Ÿà¥‡à¤‚शन पेज देख रहे हैं.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,11 +372,14 @@
<translation id="4726672564094551039">नीतियां फिर से लोड करें</translation>
<translation id="4728558894243024398">पà¥à¤²à¥‡à¤Ÿà¤«à¤¼à¥‰à¤°à¥à¤®</translation>
<translation id="4744603770635761495">निषà¥à¤ªà¤¾à¤¦à¤¨-योगà¥à¤¯ पथ</translation>
+<translation id="4750917950439032686">आपकी जानकारी (उदाहरण के लिà¤, पासवरà¥à¤¡ या कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡ नंबर) जब इस साइट पर भेजी जाती है तब वह निजी होती है .</translation>
<translation id="4756388243121344051">&amp;इतिहास</translation>
+<translation id="4759118997339041434">भà¥à¤—तान का ऑटोमैटिक भरना अकà¥à¤·à¤®</translation>
<translation id="4764776831041365478"><ph name="URL" /> पर मौजूद वेबपेज संभवतः असà¥à¤¥à¤¾à¤¯à¥€ रूप से बंद है या उसे सà¥à¤¥à¤¾à¤¯à¥€ रूप से किसी नठवेब पते पर ले जाया गया है.</translation>
-<translation id="4771973620359291008">अजà¥à¤žà¤¾à¤¤ तà¥à¤°à¥à¤Ÿà¤¿ आई.</translation>
+<translation id="4771973620359291008">अजà¥à¤žà¤¾à¤¤ गड़बड़ी आई.</translation>
<translation id="4800132727771399293">अपना अवधि समापà¥â€à¤¤à¤¿ दिनांक और CVC जांचें और पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें</translation>
-<translation id="4813512666221746211">नेटवरà¥à¤• तà¥à¤°à¥à¤Ÿà¤¿</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
+<translation id="4813512666221746211">नेटवरà¥à¤• गड़बड़ी</translation>
<translation id="4816492930507672669">पेज में फ़िट करें</translation>
<translation id="4850886885716139402">देखें</translation>
<translation id="4858792381671956233">आपने अपने अभिभावकों से पूछा था कि इस साइट पर जाना ठीक है या नहीं</translation>
@@ -368,13 +387,14 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{1 और वेब पेज}one{# और वेब पेज}other{# और वेब पेज}}</translation>
<translation id="4923417429809017348">इस पेज का à¤à¤• अजà¥à¤žà¤¾à¤¤ भाषा से <ph name="LANGUAGE_LANGUAGE" /> में अनà¥à¤µà¤¾à¤¦ किया गया है</translation>
+<translation id="4923459931733593730">भà¥à¤—तान</translation>
<translation id="4926049483395192435">निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ किया जाना चाहिà¤.</translation>
<translation id="495170559598752135">कà¥à¤°à¤¿à¤¯à¤¾à¤à¤‚</translation>
<translation id="4958444002117714549">सूची विसà¥à¤¤à¥ƒà¤¤ करें</translation>
<translation id="4962322354953122629">यह सरà¥à¤µà¤° पà¥à¤°à¤®à¤¾à¤£à¤¿à¤¤ नहीं कर सका कि यह <ph name="DOMAIN" /> है; इसका सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° Chrome दà¥à¤µà¤¾à¤°à¤¾ विशà¥à¤µà¤¸à¤¨à¥€à¤¯ नहीं है. à¤à¤¸à¤¾ गलत कॉनà¥à¤«à¤¼à¤¿à¤—रेशन के कारण या किसी हमलावर दà¥à¤µà¤¾à¤°à¤¾ आपके कनेकà¥à¤¶à¤¨ में बाधा डालने के कारण हो सकता है. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="498957508165411911"><ph name="ORIGINAL_LANGUAGE" /> से <ph name="TARGET_LANGUAGE" /> में अनà¥à¤µà¤¾à¤¦ करें?</translation>
<translation id="4989809363548539747">यह पà¥à¤²à¤— इन समरà¥à¤¥à¤¿à¤¤ नहीं है</translation>
-<translation id="5002932099480077015">यदि सकà¥à¤·à¤® किया हà¥à¤† हो, तो Chrome फ़ॉरà¥à¤® को तेज़ी से भरने के लिठइस डिवाइस पर आपके कारà¥à¤¡ की à¤à¤• पà¥à¤°à¤¤à¤¿ संगà¥à¤°à¤¹à¥€à¤¤ करेगा.</translation>
+<translation id="5002932099480077015">यदि सकà¥à¤·à¤® किया हà¥à¤† हो, तो Chrome फ़ॉरà¥à¤® को तेज़ी से भरने के लिठइस डिवाइस पर आपके कारà¥à¤¡ की à¤à¤• पà¥à¤°à¤¤à¤¿ संगà¥à¤°à¤¹à¤¿à¤¤ करेगा.</translation>
<translation id="5019198164206649151">बैकिंग संगà¥à¤°à¤¹ खराब सà¥à¤¥à¤¿à¤¤à¤¿ में है</translation>
<translation id="5023310440958281426">अपने वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤• की नीतियां देखें</translation>
<translation id="5029568752722684782">सà¥â€à¤ªà¤·à¥â€à¤Ÿ पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿</translation>
@@ -388,28 +408,29 @@
<translation id="5095208057601539847">पà¥à¤°à¤¾à¤‚त</translation>
<translation id="5115563688576182185">(64-बिट)</translation>
<translation id="5141240743006678641">समनà¥à¤µà¤¯à¤¿à¤¤ पासवरà¥à¤¡ अपने Google पà¥à¤°à¤®à¤¾à¤£à¤¿à¤•à¤¤à¤¾ के साथ à¤à¤¨à¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ करें</translation>
-<translation id="5145883236150621069">नीति पà¥à¤°à¤¤à¤¿à¤¸à¤¾à¤¦ में तà¥à¤°à¥à¤Ÿà¤¿ कोड मौजूद है</translation>
+<translation id="5145883236150621069">नीति पà¥à¤°à¤¤à¤¿à¤¸à¤¾à¤¦ में गड़बड़ी कोड मौजूद है</translation>
<translation id="5171045022955879922">URL खोजें या लिखें</translation>
<translation id="5172758083709347301">मशीन</translation>
-<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> में नहीं है? इस तà¥à¤°à¥à¤Ÿà¤¿ की रिपोरà¥à¤Ÿ करें</translation>
+<translation id="5179510805599951267"><ph name="ORIGINAL_LANGUAGE" /> में नहीं है? इस गड़बड़ी की रिपोरà¥à¤Ÿ करें</translation>
<translation id="5181140330217080051">डाउनलोड हो रहा है</translation>
<translation id="5190835502935405962">बà¥à¤•à¤®à¤¾à¤°à¥à¤• बार</translation>
<translation id="5199729219167945352">पà¥à¤°à¤¯à¥‹à¤—</translation>
-<translation id="5199841536747119669">आपके सà¥à¤à¤¾à¤µ यहां दिखाई देते हैं</translation>
<translation id="5251803541071282808">कà¥à¤²à¤¾à¤‰à¤¡</translation>
<translation id="5277279256032773186">कारà¥à¤¯à¤¸à¥à¤¥à¤² पर Chrome का उपयोग कर रहे हैं? कंपनियां अपने करà¥à¤®à¤šà¤¾à¤°à¤¿à¤¯à¥‹à¤‚ के लिठChrome सेटिंग पà¥à¤°à¤¬à¤‚धित कर सकती हैं. और जानें</translation>
-<translation id="5299298092464848405">नीति पारà¥à¤¸ करने में तà¥à¤°à¥à¤Ÿà¤¿</translation>
+<translation id="5299298092464848405">नीति पारà¥à¤¸ करने में गड़बड़ी</translation>
<translation id="5300589172476337783">दिखाà¤à¤‚</translation>
<translation id="5308689395849655368">कà¥à¤°à¥ˆà¤¶ की रिपोरà¥à¤Ÿ करना अकà¥à¤·à¤® कर दिया गया है.</translation>
<translation id="5317780077021120954">सहेजें</translation>
<translation id="5327248766486351172">नाम</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> पर मौजूद हमलावर आपको धोखा देकर आपसे कà¥à¤› जोखिम वाला काम करा सकते हैं, जैसे सॉफ़à¥à¤Ÿà¤µà¥‡à¤¯à¤° इंसà¥à¤Ÿà¥‰à¤² करना या आपकी वà¥à¤¯à¤•à¥à¤¤à¤¿à¤—त जानकारी (उदाहरण के लिà¤, पासवरà¥à¤¡, फ़ोन नंबर या कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) पà¥à¤°à¤•à¤Ÿ करना.</translation>
<translation id="5359637492792381994">यह सरà¥à¤µà¤° पà¥à¤°à¤®à¤¾à¤£à¤¿à¤¤ नहीं कर सका कि यह <ph name="DOMAIN" /> है; इसका सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° इस समय मानà¥à¤¯ नहीं है. à¤à¤¸à¤¾ गलत कॉनà¥à¤«à¤¼à¤¿à¤—रेशन के कारण या किसी हमलावर दà¥à¤µà¤¾à¤°à¤¾ आपके कनेकà¥à¤¶à¤¨ में बाधा डालने के कारण हो सकता है. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
-<translation id="536296301121032821">नीति सेटिंग संगà¥à¤°à¤¹à¥€à¤¤ करने में विफल</translation>
+<translation id="536296301121032821">नीति सेटिंग संगà¥à¤°à¤¹à¤¿à¤¤ करने में विफल</translation>
+<translation id="5386426401304769735">इस साइट की पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° शà¥à¤°à¥ƒà¤‚खला में, SHA-1 का उपयोग करके हसà¥à¤¤à¤¾à¤•à¥à¤·à¤° किया गया पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° शामिल है.</translation>
<translation id="5421136146218899937">बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग डेटा साफ़ करें...</translation>
<translation id="5430298929874300616">बà¥à¤•à¤®à¤¾à¤°à¥à¤• निकालें</translation>
<translation id="5431657950005405462">आपकी फ़ाइल नहीं मिली</translation>
<translation id="5435775191620395718">इस डिवाइस का इतिहास दिखाया जा रहा है. <ph name="BEGIN_LINK" />अधिक जानें<ph name="END_LINK" />.</translation>
-<translation id="5439770059721715174">"<ph name="ERROR_PATH" />" पर सà¥à¤•à¥€à¤®à¤¾ सतà¥à¤¯à¤¾à¤ªà¤¨ तà¥à¤°à¥à¤Ÿà¤¿: <ph name="ERROR" /></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="TOTAL_MATCHCOUNT" /> में से <ph name="ACTIVE_MATCH" /></translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> पर à¤à¤®à¥â€à¤¬à¥‡à¤¡ किठगठपृषà¥â€à¤  का कहना है:</translation>
<translation id="5556459405103347317">पà¥à¤¨: लोड करें</translation>
<translation id="5565735124758917034">सकà¥à¤°à¤¿à¤¯</translation>
+<translation id="5572851009514199876">कृपया Chrome शà¥à¤°à¥‚ करके उसमें पà¥à¤°à¤µà¥‡à¤¶ करें ताकि Chrome देख सके कि कà¥à¤¯à¤¾ आपके पास यह साइट à¤à¤•à¥à¤¸à¥‡à¤¸ करने की अनà¥à¤®à¤¤à¤¿ है.</translation>
+<translation id="5580958916614886209">अपना समापà¥à¤¤à¤¿ माह जांचें और फिर से कोशिश करें</translation>
<translation id="560412284261940334">पà¥à¤°à¤¬à¤‚धन समरà¥à¤¥à¤¿à¤¤ नहीं</translation>
<translation id="5610142619324316209">कनेकà¥à¤¶à¤¨ की जांच करें</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> ने आपको कई बार रीडायरेकà¥à¤Ÿ किया है.</translation>
<translation id="5622887735448669177">कà¥à¤¯à¤¾ आप इस साइट को छोड़ना चाहते हैं?</translation>
<translation id="5629630648637658800">नीति सेटिंग लोड करने में विफल</translation>
<translation id="5631439013527180824">अमानà¥à¤¯ डिवाइस पà¥à¤°à¤¬à¤‚धन टोकन</translation>
+<translation id="5669703222995421982">खास आपके लिठबनी सामगà¥à¤°à¥€ पाà¤à¤‚</translation>
+<translation id="5675650730144413517">यह पेज काम नहीं कर रहा है</translation>
<translation id="5677928146339483299">अवरोधित</translation>
<translation id="5694783966845939798">आपने <ph name="DOMAIN" /> पर पहà¥à¤‚चने का पà¥à¤°à¤¯à¤¾à¤¸ किया, लेकिन सरà¥à¤µà¤° ने कमज़ोर हसà¥à¤¤à¤¾à¤•à¥à¤·à¤° à¤à¤²à¥à¤—ोरिदम के उपयोग से हसà¥à¤¤à¤¾à¤•à¥à¤·à¤°à¤¿à¤¤ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° (जैसे कि SHA-1) पà¥à¤°à¤¸à¥à¤¤à¥à¤¤ किया. इसका अरà¥à¤¥ है कि सरà¥à¤µà¤° दà¥à¤µà¤¾à¤°à¤¾ पà¥à¤°à¤¸à¥à¤¤à¥à¤¤ सà¥à¤°à¤•à¥à¤·à¤¾ कà¥à¤°à¥‡à¤¡à¥‡à¤‚शियल नकली हो सकते हैं और हो सकता है कि सरà¥à¤µà¤° आपका अपेकà¥à¤·à¤¿à¤¤ सरà¥à¤µà¤° ना हो (हो सकता है आप किसी हमलावर से बातचीत कर रहे हों). <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">इस वेबसाइट की पहचान सतà¥à¤¯à¤¾à¤ªà¤¿à¤¤ नहीं की गई है.</translation>
<translation id="5720705177508910913">वरà¥à¤¤à¤®à¤¾à¤¨ उपयोगकरà¥à¤¤à¤¾</translation>
-<translation id="572328651809341494">हाल ही के टैब</translation>
<translation id="5732392974455271431">आपके अभिभावक इसे आपके लिठअनवरोधित कर सकते हैं</translation>
<translation id="5784606427469807560">आपके कारà¥à¤¡ की पà¥à¤·à¥à¤Ÿà¤¿ करते समय समसà¥â€à¤¯à¤¾ हà¥à¤ˆ. अपना इंटरनेट कनेकà¥â€à¤¶à¤¨ जांचें और पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें.</translation>
<translation id="5785756445106461925">इसके अतिरिकà¥à¤¤, इस पेज में à¤à¤¸à¥‡ अनà¥à¤¯ संसाधन भी शामिल हैं, जो सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नहीं हैं. टà¥à¤°à¤¾à¤‚ज़िट में होने के दौरान ये संसाधन अनà¥à¤¯ लोगों दà¥à¤µà¤¾à¤°à¤¾ देखे जा सकते हैं और पेज का सà¥à¤µà¤°à¥‚प बदलने के लिठकिसी हमवलावर दà¥à¤µà¤¾à¤°à¤¾ इनमें बदलाव किठजा सकते हैं.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">आपने <ph name="DOMAIN" /> तक पहà¥à¤‚चने का पà¥à¤°à¤¯à¤¾à¤¸ किया, लेकिन सरà¥à¤µà¤° ने कमज़ोर कà¥à¤‚जी वाला पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° पà¥à¤°à¤¸à¥â€à¤¤à¥à¤¤ किया. संभवत: हमलावर ने निजी कà¥à¤‚जी का पता लगा लिया है और हो सकता है कि सरà¥à¤µà¤° आपका अपेकà¥à¤·à¤¿à¤¤ सरà¥à¤µà¤° ना हो (हो सकता है कि आप किसी हमलावर से बातचीत कर रहे हों). <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">नया फ़ोलà¥à¤¡à¤°</translation>
<translation id="5869405914158311789">इस साइट तक नहीं पहà¥à¤‚चा जा सकता</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">कारà¥à¤¡ का यह पà¥à¤°à¤•à¤¾à¤° Google पेमेंटà¥à¤¸ दà¥à¤µà¤¾à¤°à¤¾ इस वà¥à¤¯à¤¾à¤ªà¤¾à¤°à¥€ के लिठसमरà¥à¤¥à¤¿à¤¤ नहीं है. कृपया कोई भिनà¥à¤¨ कारà¥à¤¡ चà¥à¤¨à¥‡à¤‚.</translation>
<translation id="59174027418879706">सकà¥à¤·à¤® किया गया</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="5966707198760109579">सपà¥à¤¤à¤¾à¤¹</translation>
<translation id="5967867314010545767">इतिहास से निकालें</translation>
<translation id="5975083100439434680">ज़ूम आउट</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">यह आज़माकर देखें:</translation>
<translation id="6151417162996330722">सरà¥à¤µà¤° पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° की मानà¥â€à¤¯à¤¤à¤¾ अवधि बहà¥à¤¤ लंबी है.</translation>
<translation id="6165508094623778733">अधिक जानें</translation>
+<translation id="6177128806592000436">इस साइट से आपका कनेकà¥â€à¤¶à¤¨ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नहीं है</translation>
<translation id="6203231073485539293">अपना इंटरनेट कनेकà¥à¤¶à¤¨ जांचें</translation>
<translation id="6218753634732582820">कà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® से पता निकालें?</translation>
+<translation id="6251924700383757765">निजता नीति</translation>
+<translation id="625755898061068298">आपने इस साइट के लिठसà¥à¤°à¤•à¥à¤·à¤¾ चेतावनियों को अकà¥à¤·à¤® करना चà¥à¤¨à¤¾ है.</translation>
<translation id="6259156558325130047">&amp;पà¥à¤¨: कà¥à¤°à¤®à¤¿à¤¤ करना फिर से करें</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="6264485186158353794">सà¥à¤°à¤•à¥à¤·à¤¾ पर वापस</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> तक नहीं पहà¥à¤‚चा जा सकता.</translation>
<translation id="6321917430147971392">अपनी DNS सेटिंग जांचें</translation>
<translation id="6328639280570009161">नेटवरà¥à¤• पूरà¥à¤µà¤¾à¤¨à¥à¤®à¤¾à¤¨ को अकà¥à¤·à¤® करके देखें</translation>
+<translation id="6328786501058569169">यह साइट भà¥à¤°à¤¾à¤®à¤• है</translation>
<translation id="6337534724793800597">नाम के अनà¥à¤¸à¤¾à¤° नीतियां फ़िलà¥à¤Ÿà¤° करें</translation>
<translation id="6342069812937806050">अभी</translation>
<translation id="6345221851280129312">अजà¥à¤žà¤¾à¤¤ आकार</translation>
@@ -489,6 +519,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{1 अनà¥â€à¤¯ सà¥à¤à¤¾à¤µ}one{# अनà¥â€à¤¯ सà¥à¤à¤¾à¤µ}other{# अनà¥â€à¤¯ सà¥à¤à¤¾à¤µ}}</translation>
<translation id="6387478394221739770">बेहतरीन नई Chrome सà¥à¤µà¤¿à¤§à¤¾à¤“ं में रूचि है? chrome.com/beta पर हमारा बीटा चैनल आज़माà¤à¤‚.</translation>
<translation id="6389758589412724634">यह वेबपेज दिखाते समय कà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® में जगह नहीं बची.</translation>
+<translation id="6404511346730675251">बà¥à¤•à¤®à¤¾à¤°à¥à¤• संपादित करें</translation>
+<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> का समापà¥à¤¤à¤¿ दिनांक और CVC डालें</translation>
<translation id="6414888972213066896">आपने अपने अभिभावक से पूछा था कि इस साइट पर जाना ठीक है या नहीं</translation>
<translation id="6416403317709441254">आप इस समय <ph name="SITE" /> पर विज़िट नहीं कर सकते कà¥à¤¯à¥‹à¤‚कि वेबसाइट ने à¤à¤¸à¥‡ अवà¥à¤¯à¤µà¤¸à¥à¤¥à¤¿à¤¤ कà¥à¤°à¥‡à¤¡à¥‡à¤‚शियल भेजे हैं जिनà¥à¤¹à¥‡à¤‚ कà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® संसाधित नहीं कर सकता. नेटवरà¥à¤• की गड़बड़ियां और हमले आमतौर पर असà¥à¤¥à¤¾à¤¯à¥€ होते हैं, इसलिठसंभवत: पेज बाद में काम करेगा. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° को रदà¥à¤¦ किया गया है या नहीं यह जांच करने में असमररà¥à¤¥.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">नीति के दà¥à¤µà¤¾à¤°à¤¾ डिफ़ॉलà¥â€à¤Ÿ खोज अकà¥à¤·à¤® कर दिठजाने के कारण उपेकà¥à¤·à¤¿à¤¤.</translation>
<translation id="6462969404041126431">यह सरà¥à¤µà¤° पà¥à¤°à¤®à¤¾à¤£à¤¿à¤¤ नहीं कर सका कि यह <ph name="DOMAIN" /> है; हो सकता है इसका सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° निरसà¥à¤¤ कर दिया गया हो. à¤à¤¸à¤¾ गलत कॉनà¥à¤«à¤¼à¤¿à¤—रेशन के कारण या किसी हमलावर दà¥à¤µà¤¾à¤°à¤¾ आपके कनेकà¥à¤¶à¤¨ में बाधा डालने के कारण हो सकता है. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">डिवाइस नीतियां</translation>
+<translation id="6477321094435799029">Chrome को इस पेज पर असामानà¥à¤¯ कोड मिला था और उसने आपकी वà¥à¤¯à¤•à¥à¤¤à¤¿à¤—त जानकारी (उदाहरण के लिà¤, पासवरà¥à¤¡, फ़ोन नंबर और कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) की सà¥à¤°à¤•à¥à¤·à¤¾ करने के लिठउसे अवरà¥à¤¦à¥à¤§ कर दिया है.</translation>
<translation id="6489534406876378309">कà¥à¤°à¥ˆà¤¶ अपलोड करना पà¥à¤°à¤¾à¤°à¤‚भ करें</translation>
<translation id="6529602333819889595">&amp;हटाना फिर से करें</translation>
<translation id="6534179046333460208">जीता-जागता वेब के सà¥à¤à¤¾à¤µ</translation>
<translation id="6550675742724504774">विकलà¥à¤ª</translation>
+<translation id="6556239504065605927">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ कनेकà¥à¤¶à¤¨</translation>
<translation id="6563469144985748109">आपके पà¥à¤°à¤¬à¤‚धक ने अभी तक इसकी सà¥à¤µà¥€à¤•à¥ƒà¤¤à¤¿ नहीं दी है</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" /> से कम</translation>
<translation id="6596325263575161958">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ तरीका विकलà¥à¤ª</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">यह नीति हटा दी गई है.</translation>
<translation id="6652240803263749613">यह सरà¥à¤µà¤° पà¥à¤°à¤®à¤¾à¤£à¤¿à¤¤ नहीं कर सका कि यह <ph name="DOMAIN" /> है; इसका सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° आपके कंपà¥à¤¯à¥‚टर के ऑपरेटिंग सिसà¥à¤Ÿà¤® दà¥à¤µà¤¾à¤°à¤¾ विशà¥à¤µà¤¸à¤¨à¥€à¤¯ नहीं है. à¤à¤¸à¤¾ गलत कॉनà¥à¤«à¤¼à¤¿à¤—रेशन के कारण या किसी हमलावर दà¥à¤µà¤¾à¤°à¤¾ आपके कनेकà¥à¤¶à¤¨ में बाधा डालने के कारण हो सकता है. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">फ़ोलà¥â€à¤¡à¤° संपादित करें</translation>
-<translation id="6660210980321319655"><ph name="CRASH_TIME" /> बजे अपने आप रिपोरà¥à¤Ÿ की गई</translation>
<translation id="6671697161687535275">कà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® से फ़ॉरà¥à¤® सà¥à¤à¤¾à¤µ निकालें?</translation>
<translation id="6685834062052613830">पà¥à¤°à¤¸à¥à¤¥à¤¾à¤¨ करें और सेटअप पूरा करें</translation>
<translation id="6710213216561001401">पिछला</translation>
@@ -518,10 +551,11 @@
<translation id="6711464428925977395">पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¤° के साथ कà¥à¤› गलत है या पता गलत है.</translation>
<translation id="6727102863431372879">सेट करें</translation>
<translation id="6731320287533051140">{COUNT,plural, =0{कोई नहीं}=1{1 आइटम}one{# आइटम}other{# आइटम}}</translation>
-<translation id="674375294223700098">अजà¥à¤žà¤¾à¤¤ सरà¥à¤µà¤° पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° तà¥à¤°à¥à¤Ÿà¤¿.</translation>
+<translation id="674375294223700098">अजà¥à¤žà¤¾à¤¤ सरà¥à¤µà¤° पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° गड़बड़ी.</translation>
<translation id="6753269504797312559">नीति मान</translation>
<translation id="6757797048963528358">आपका डिवाइस निषà¥à¤•à¥à¤°à¤¿à¤¯ हो गया है.</translation>
<translation id="6778737459546443941">आपके अभिभावक ने अभी तक इसकी सà¥à¤µà¥€à¤•à¥ƒà¤¤à¤¿ नहीं दी है</translation>
+<translation id="6810899417690483278">कसà¥à¤Ÿà¤®à¤¾à¤‡à¤œà¤¼à¥‡à¤¶à¤¨ आईडी</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" /> पर वेबपेज वरà¥à¤¤à¤®à¤¾à¤¨ में अनà¥à¤ªà¤²à¤¬à¥à¤§ है. संभवतः यह ओवरलोड हो गया है या रखरखाव के लिठबंद कर दिया है.</translation>
<translation id="6831043979455480757">अनà¥à¤µà¤¾à¤¦ करें</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">संपरà¥à¤• जानकारी</translation>
+<translation id="7079718277001814089">इस साइट में मैलवेयर है</translation>
<translation id="7087282848513945231">काउंटी</translation>
<translation id="7088615885725309056">इससे पà¥à¤°à¤¾à¤¨à¥‡</translation>
<translation id="7090678807593890770"><ph name="LINK" /> के लिठGoogle में खोज करें</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> सà¥à¤°à¤•à¥à¤·à¤¾ मानकों का पालन नहीं करता.</translation>
<translation id="721197778055552897">इस समसà¥à¤¯à¤¾ के बारे में <ph name="BEGIN_LINK" />अधिक जानें<ph name="END_LINK" />.</translation>
<translation id="7219179957768738017">कनेकà¥â€à¤¶à¤¨ <ph name="SSL_VERSION" /> का उपयोग करता है.</translation>
+<translation id="724691107663265825">साइट में आगे मैलवेयर हैं</translation>
<translation id="724975217298816891">अपने कारà¥à¤¡ विवरण अपडेट करने के लिठ<ph name="CREDIT_CARD" /> का समय समापà¥à¤¤à¤¿ दिनांक और CVC डालें. आपकी तरफ से पà¥à¤·à¥à¤Ÿà¤¿ हो जाने पर, आपके कारà¥à¤¡ के विवरण इस साइट के साथ साà¤à¤¾ किठजाà¤à¤‚गे.</translation>
<translation id="725866823122871198"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> से à¤à¤• निजी कनेकà¥â€à¤¶à¤¨ सà¥â€à¤¥à¤¾à¤ªà¤¿à¤¤ नहीं किया जा सकता कà¥â€à¤¯à¥‹à¤‚कि आपके कंपà¥â€à¤¯à¥‚टर का दिनांक और समय (<ph name="DATE_AND_TIME" />) गलत है.</translation>
<translation id="7269802741830436641">इस वेबपेज में à¤à¤• रीडायरेकà¥à¤Ÿ लूप है</translation>
@@ -609,6 +646,7 @@
<translation id="7658239707568436148">अभी नहीं</translation>
<translation id="7667346355482952095">वापस लौटा हà¥à¤† नीति टोकन खाली है या उसका मिलान वरà¥à¤¤à¤®à¤¾à¤¨ टोकन से नहीं होता</translation>
<translation id="7668654391829183341">अजà¥à¤žà¤¾à¤¤ डिवाइस</translation>
+<translation id="7669271284792375604">इस साइट पर मौजूद हमलावर आपके बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग अनà¥à¤­à¤µ को हानि पहà¥à¤‚चा सकने वाले पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® इंसà¥à¤Ÿà¥‰à¤² करने के लिठआपको भà¥à¤°à¤®à¤¿à¤¤ कर सकते हैं (उदाहरण के लिà¤, आपके मà¥à¤–पृषà¥à¤  को बदलकर या आपकी विज़िट की जा रहीं साइट पर अतिरिकà¥à¤¤ विजà¥à¤žà¤¾à¤ªà¤¨ दिखाकर).</translation>
<translation id="7674629440242451245">शानदार नई Chrome सà¥à¤µà¤¿à¤§à¤¾à¤“ं में रूचि है? तो chrome.com/dev पर हमारा डेव चैनल आज़माà¤à¤‚.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /><ph name="SITE" /> में आगे बढ़ें (असà¥à¤°à¤•à¥à¤·à¤¿à¤¤)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">इस साइट को संचय से लोड नहीं किया जा सकता</translation>
@@ -624,14 +662,17 @@
<translation id="7800304661137206267">कनेकà¥à¤¶à¤¨ को संदेश पà¥à¤°à¤®à¤¾à¤£à¥€à¤•à¤°à¤£ के लिठ<ph name="MAC" /> और मà¥à¤–à¥à¤¯ à¤à¤•à¥à¤¸à¤šà¥‡à¤‚ज कà¥à¤°à¤¿à¤¯à¤¾à¤µà¤¿à¤§à¤¿ के रूप में <ph name="KX" /> के साथ, <ph name="CIPHER" /> का उपयोग करते हà¥à¤ à¤à¤¨à¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ किया गया है.</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="7887683347370398519">अपना CVC जांचें और पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें</translation>
<translation id="7912024687060120840">इस फ़ोलà¥à¤¡à¤° में:</translation>
<translation id="7935318582918952113">DOM डिसà¥à¤Ÿà¤¿à¤²à¤°</translation>
<translation id="7938958445268990899">सरà¥à¤µà¤° का पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° अभी तक मानà¥à¤¯ नहीं है.</translation>
<translation id="7942349550061667556">लाल</translation>
+<translation id="7947285636476623132">अपना समापà¥à¤¤à¤¿ वरà¥à¤· जांचें और फिर से कोशिश करें</translation>
<translation id="7951415247503192394">(32-बिट)</translation>
<translation id="7956713633345437162">मोबाइल बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="7961015016161918242">कभी नहीं</translation>
@@ -639,7 +680,10 @@
<translation id="7983301409776629893">हमेशा <ph name="ORIGINAL_LANGUAGE" /> से <ph name="TARGET_LANGUAGE" /> में अनà¥à¤µà¤¾à¤¦ करें</translation>
<translation id="7995512525968007366">निरà¥à¤¦à¤¿à¤·à¥â€à¤Ÿ नहीं किया गया</translation>
<translation id="8012647001091218357">हम इस समय आपके अभिभावकों तक नहीं पहà¥à¤‚च पा रहे हैं. कृपया पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें.</translation>
+<translation id="8025119109950072390">इस साइट पर मौजूद हमलावर आपको धोखा देकर आपसे कà¥à¤› जोखिम वाला काम करा सकते हैं, जैसे सॉफ़à¥à¤Ÿà¤µà¥‡à¤¯à¤° इंसà¥à¤Ÿà¥‰à¤² करना या आपकी वà¥à¤¯à¤•à¥à¤¤à¤¿à¤—त जानकारी (उदाहरण के लिà¤, पासवरà¥à¤¡, फ़ोन नंबर या कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) पà¥à¤°à¤•à¤Ÿ करना.</translation>
+<translation id="803030522067524905">Google सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग को हाल ही में <ph name="SITE" /> पर फ़िशिंग का पता चला है. आपको भà¥à¤°à¤®à¤¿à¤¤ करने के लिठफ़िशिंग साइट अनà¥à¤¯ वेबसाइट होने का दावा करती हैं. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">यह पेज <ph name="SOURCE_LANGUAGE" /> में है. इसका <ph name="TARGET_LANGUAGE" /> में अनà¥à¤µà¤¾à¤¦ करें?</translation>
+<translation id="8041089156583427627">सà¥à¤à¤¾à¤µ भेजें</translation>
<translation id="8088680233425245692">लेख देखने में विफल रहा.</translation>
<translation id="8089520772729574115">1 MB से कम</translation>
<translation id="8091372947890762290">सरà¥à¤µà¤° पर सकà¥à¤°à¤¿à¤¯à¤£ लंबित है</translation>
@@ -650,9 +694,11 @@
<translation id="8150722005171944719"><ph name="URL" /> पर मौजूद फ़ाइल पढ़ने योगà¥à¤¯ नहीं है. हो सकता है कि इसे निकाल दिया गया हो, कहीं ले जाया गया हो, या फ़ाइल की अनà¥à¤®à¤¤à¤¿à¤¯à¤¾à¤‚ पहà¥à¤‚च रोक रही हों.</translation>
<translation id="8194797478851900357">&amp;ले जाना वापस लाà¤à¤‚</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" आईडी वाले à¤à¤•à¥â€à¤¸à¤Ÿà¥‡à¤‚शन का अमानà¥â€à¤¯ अपडेट URL.</translation>
+<translation id="8202097416529803614">आदेश सारांश</translation>
<translation id="8218327578424803826">सौंपा गया सà¥â€à¤¥à¤¾à¤¨:</translation>
<translation id="8225771182978767009">जिस वà¥à¤¯à¤•à¥à¤¤à¤¿ ने इस कंपà¥à¤¯à¥‚टर को सेट किया है, उसने इस साइट को अवरà¥à¤¦à¥à¤§ करना चà¥à¤¨à¤¾ है.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> पर मौजूद हमलावर आपके कंपà¥à¤¯à¥‚टर पर à¤à¤¸à¥‡ खतरनाक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® इंसà¥à¤Ÿà¥‰à¤² करने की कोशिश कर सकते हैं जो आपकी जानकारी (उदाहरण के लिà¤, फ़ोटो, पासवरà¥à¤¡, संदेश और कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) चà¥à¤°à¤¾ लेते हैं या उसे हटा देते हैं.</translation>
<translation id="8241707690549784388">आपके दà¥à¤µà¤¾à¤°à¤¾ खोजे जा रहे पेज ने आपके दà¥à¤µà¤¾à¤°à¤¾ पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ की गई जानकारी का उपयोग किया है. उस पेज पर वापस जाने से आपके दà¥à¤µà¤¾à¤°à¤¾ की गई किसी कà¥à¤°à¤¿à¤¯à¤¾ को दोहराने की आवशà¥à¤¯à¤•à¤¤à¤¾ हो सकती है. कà¥à¤¯à¤¾ आप जारी रखना चाहते हैं?</translation>
<translation id="8249320324621329438">पिछली बार पà¥à¤°à¤¾à¤ªà¥à¤¤ किया गया:</translation>
<translation id="8261506727792406068">हटाà¤à¤‚</translation>
@@ -661,11 +707,13 @@
<translation id="8294431847097064396">सà¥à¤°à¥‹à¤¤</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="8349305172487531364">बà¥à¤•à¤®à¤¾à¤°à¥à¤• बार</translation>
<translation id="8363502534493474904">हवाई जहाज़ मोड बंद करें</translation>
<translation id="8364627913115013041">सेट नहीं है.</translation>
<translation id="8380941800586852976">खतरनाक</translation>
<translation id="8382348898565613901">हाल ही में विज़िट किठगठआपके बà¥à¤•à¤®à¤¾à¤°à¥à¤• यहां दिखाई देते हैं</translation>
+<translation id="8398259832188219207">ख़राबी रिपोरà¥à¤Ÿ <ph name="UPLOAD_TIME" /> पर अपलोड की गई</translation>
<translation id="8412145213513410671">कà¥à¤°à¥ˆà¤¶ (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">आपको वही पासफ़à¥à¤°à¥‡à¥› दोबारा दरà¥à¤œ करना होगा.</translation>
<translation id="8428213095426709021">सेटिंगà¥à¤¸</translation>
@@ -677,9 +725,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ने पà¥à¤°à¤¤à¤¿à¤¸à¤¾à¤¦ देने में अतà¥à¤¯à¤§à¤¿à¤• समय लिया.</translation>
<translation id="852346902619691059">यह सरà¥à¤µà¤° पà¥à¤°à¤®à¤¾à¤£à¤¿à¤¤ नहीं कर सका कि यह <ph name="DOMAIN" /> है; इसका सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° आपके कंपà¥à¤¯à¥‚टर के ऑपरेटिंग सिसà¥à¤Ÿà¤® दà¥à¤µà¤¾à¤°à¤¾ विशà¥à¤µà¤¸à¤¨à¥€à¤¯ नहीं है. à¤à¤¸à¤¾ गलत कॉनà¥à¤«à¤¼à¤¿à¤—रेशन के कारण या किसी हमलावर दà¥à¤µà¤¾à¤°à¤¾ आपके कनेकà¥à¤¶à¤¨ में बाधा डालने के कारण हो सकता है. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जानें<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">कारà¥à¤¡ का यह पà¥à¤°à¤•à¤¾à¤° Google पेमेंटà¥à¤¸ दà¥à¤µà¤¾à¤°à¤¾ समरà¥à¤¥à¤¿à¤¤ नहीं है. कृपया कोई भिनà¥à¤¨ कारà¥à¤¡ चà¥à¤¨à¥‡à¤‚.</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="8553075262323480129">अनà¥à¤µà¤¾à¤¦ विफल हो गया कà¥à¤¯à¥‹à¤‚कि पेज की भाषा निरà¥à¤§à¤¾à¤°à¤¿à¤¤ नहीं की जा सकी.</translation>
<translation id="8559762987265718583"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> से à¤à¤• निजी कनेकà¥â€à¤¶à¤¨ सà¥â€à¤¥à¤¾à¤ªà¤¿à¤¤ नहीं किया जा सकता कà¥â€à¤¯à¥‹à¤‚कि आपके डिवाइस का दिनांक और समय (<ph name="DATE_AND_TIME" />) गलत है.</translation>
-<translation id="856992080682148">इस साइट के लिठपà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° की समय-सीमा 2017 या उसके बाद समापà¥à¤¤ होगी और पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° की शà¥à¤°à¥ƒà¤‚खला में, SHA-1 का उपयोग करके हसà¥à¤¤à¤¾à¤•à¥à¤·à¤° किया गया पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° शामिल है.</translation>
<translation id="8571890674111243710">पेज का अनà¥à¤µà¤¾à¤¦ <ph name="LANGUAGE" /> में कर रहा है...</translation>
<translation id="859285277496340001">पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° यह जांचने के लिठकोई तंतà¥à¤° निरà¥à¤¦à¤¿à¤·à¥â€à¤Ÿ नहीं करता कि इसे रदà¥à¤¦ कर दिया गया है या नहीं.</translation>
<translation id="8620436878122366504">आपके अभिभावकों ने अभी तक इसकी सà¥à¤µà¥€à¤•à¥ƒà¤¤à¤¿ नहीं दी है</translation>
@@ -692,13 +740,15 @@
<translation id="8740359287975076522"><ph name="HOST_NAME" /> का &lt;abbr id="dnsDefinition"&gt;DNS पता&lt;/abbr&gt; पà¥à¤°à¤¾à¤ªà¥à¤¤ नहीं किया जा सका. समसà¥à¤¯à¤¾ का निदान किया जा रहा है.</translation>
<translation id="8790007591277257123">&amp;हटाना फिर से करें</translation>
<translation id="8798099450830957504">सामानà¥à¤¯</translation>
+<translation id="8800988563907321413">आपके आस-पास के सà¥à¤à¤¾à¤µ यहां दिखाई देंगे</translation>
<translation id="8804164990146287819">निजता नीति</translation>
<translation id="8820817407110198400">बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="8834246243508017242">संपरà¥à¤•à¥‹à¤‚ का ऑटोमैटिक भरना सकà¥à¤·à¤® करें…</translation>
<translation id="883848425547221593">अनà¥à¤¯ बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
+<translation id="884264119367021077">शिपिंग पता</translation>
<translation id="884923133447025588">कोई निरसà¥à¤¤à¥€à¤•à¤°à¤£ पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ पà¥à¤°à¤¾à¤ªà¥à¤¤ नहीं हà¥à¤ˆ.</translation>
<translation id="885730110891505394">Google के साथ साà¤à¤¾ करना</translation>
-<translation id="8866481888320382733">नीति सेटिंग पारà¥à¤¸ करने में तà¥à¤°à¥à¤Ÿà¤¿</translation>
+<translation id="8866481888320382733">नीति सेटिंग पारà¥à¤¸ करने में गड़बड़ी</translation>
<translation id="8866959479196209191">इस पृषà¥â€à¤  का कहना है:</translation>
<translation id="8870413625673593573">हाल ही में बंद किठगà¤</translation>
<translation id="8876793034577346603">नेटवरà¥à¤• कॉनà¥à¤«à¤¼à¤¿à¤—रेशन पारà¥à¤¸ होने में विफल रहा.</translation>
@@ -713,18 +763,21 @@
<translation id="8971063699422889582">सरà¥à¤µà¤° के पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° की समय-सीमा समापà¥à¤¤ हो चà¥à¤•à¥€ है.</translation>
<translation id="8987927404178983737">माह</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">साइट में आगे हानिकारक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® हैं</translation>
<translation id="9001074447101275817">पà¥à¤°à¥‰à¤•à¥â€à¤¸à¥€ <ph name="DOMAIN" /> के लिठउपयोगकरà¥à¤¤à¤¾ नाम और पासवरà¥à¤¡ की आवशà¥à¤¯à¤•à¤¤à¤¾ है.</translation>
<translation id="901974403500617787">सिसà¥à¤Ÿà¤®-वà¥à¤¯à¤¾à¤ªà¥€ रूप से लागू होने वाले फ़à¥à¤²à¥ˆà¤— केवल मालिक दà¥à¤µà¤¾à¤°à¤¾ ही सेट किठजा सकते हैं: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">इस पेज का <ph name="TARGET_LANGUAGE" /> में अनà¥à¤µà¤¾à¤¦ कर दिया गया है</translation>
+<translation id="9035022520814077154">सà¥à¤°à¤•à¥à¤·à¤¾ गड़बड़ी</translation>
<translation id="9038649477754266430">अधिक तेज़ी से पेज लोड करने के लिठकिसी पूरà¥à¤µà¤¾à¤¨à¥à¤®à¤¾à¤¨ सेवा का उपयोग करें</translation>
<translation id="9039213469156557790">इसके अतिरिकà¥à¤¤, इस पेज में à¤à¤¸à¥‡ अनà¥à¤¯ संसाधन भी शामिल हैं, जो सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नहीं हैं. टà¥à¤°à¤¾à¤‚ज़िट में होने के दौरान ये संसाधन अनà¥à¤¯ लोगों दà¥à¤µà¤¾à¤°à¤¾ देखे जा सकते हैं और पेज का वà¥à¤¯à¤µà¤¹à¤¾à¤° बदलने के लिठकिसी हमवलावर दà¥à¤µà¤¾à¤°à¤¾ इनमें बदलाव किठजा सकते हैं.</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> पर मौजूद हमलावर आपको à¤à¤¸à¥‡ पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® इंसà¥à¤Ÿà¥‰à¤² करने के लिठभà¥à¤°à¤®à¤¿à¤¤ कर सकते हैं जिनसे आपके बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग अनà¥à¤­à¤µ को हानि पहà¥à¤‚च सकती है (उदाहरण के लिà¤, आपके मà¥à¤–à¥à¤¯à¤ªà¥ƒà¤·à¥à¤  को बदलकर या आपके दà¥à¤µà¤¾à¤°à¤¾ देखी जाने वाली वेबसाइटों पर अतिरिकà¥à¤¤ विजà¥à¤žà¤¾à¤ªà¤¨ दिखाकर).</translation>
<translation id="9050666287014529139">पासफ़à¥à¤°à¥‡à¤œà¤¼</translation>
<translation id="9065203028668620118">संपादित करें</translation>
<translation id="9068849894565669697">रंग चà¥à¤¨à¥‡à¤‚</translation>
<translation id="9076283476770535406">इसमें वयसà¥à¤• सामगà¥à¤°à¥€ हो सकती है</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> पेज काम नहीं कर रहा है</translation>
<translation id="9103872766612412690"><ph name="SITE" /> आपकी जानकारी की सà¥à¤°à¤•à¥à¤·à¤¾ करने के लिठआमतौर पर à¤à¤¨à¥à¤•à¥à¤°à¤¿à¤ªà¥à¤¶à¤¨ का उपयोग करती है. जब कà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® ने इस बार <ph name="SITE" /> से कनेकà¥à¤Ÿ करने का पà¥à¤°à¤¯à¤¾à¤¸ किया, तो वेबसाइट ने असामानà¥à¤¯ और गलत कà¥à¤°à¥‡à¤¡à¥‡à¤‚शियल वापस भेजे. à¤à¤¸à¤¾ तब हो सकता है जब कोई हमलावर <ph name="SITE" /> होने का दावा करने का पà¥à¤°à¤¯à¤¾à¤¸ कर रहा हो या किसी वाई-फ़ाई पà¥à¤°à¤µà¥‡à¤¶ सà¥à¤•à¥à¤°à¥€à¤¨ ने कनेकà¥à¤¶à¤¨ को बाधित कर दिया हो. आपकी जानकारी अभी भी सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ है कà¥à¤¯à¥‹à¤‚कि किसी भी डेटा के आदान-पà¥à¤°à¤¦à¤¾à¤¨ से पहले ही कà¥à¤°à¥‹à¤®à¤¿à¤¯à¤® ने कनेकà¥à¤¶à¤¨ को रोक दिया था.</translation>
<translation id="9137013805542155359">मूल दिखाà¤à¤‚</translation>
+<translation id="9137248913990643158">इस à¤à¤ªà¥à¤²à¤¿à¤•à¥‡à¤¶à¤¨ का उपयोग करने से पहले कृपया Chrome शà¥à¤°à¥‚ करके उसमें पà¥à¤°à¤µà¥‡à¤¶ करें.</translation>
<translation id="9148507642005240123">&amp;संपादन वापस लाà¤à¤‚</translation>
<translation id="9157595877708044936">सेट अप कर रहा है...</translation>
<translation id="9170848237812810038">&amp;पूरà¥à¤µà¤µà¤¤à¥ करें</translation>
@@ -737,7 +790,6 @@
<translation id="935608979562296692">फ़ॉरà¥à¤® साफ़ करें</translation>
<translation id="939736085109172342">नया फ़ोलà¥à¤¡à¤°</translation>
<translation id="941721044073577244">à¤à¤¸à¤¾ लगता है कि आपको इस साइट पर जाने की अनà¥à¤®à¤¤à¤¿ नहीं है</translation>
-<translation id="962701380617707048">अपने कारà¥à¤¡ विवरण अपडेट करने के लिठ<ph name="CREDIT_CARD" /> का समय समापà¥à¤¤à¤¿ दिनांक और CVC डालें</translation>
<translation id="969892804517981540">आधिकारिक बिलà¥à¤¡</translation>
<translation id="988159990683914416">डेवलपर बिलà¥à¤¡</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 75d8470788e..14f9da966e7 100644
--- a/chromium/components/strings/components_strings_hr.xtb
+++ b/chromium/components/strings/components_strings_hr.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Ponovo se povežite s Wi-Fi mrežom</translation>
<translation id="1175364870820465910">&amp;Ispis...</translation>
<translation id="1181037720776840403">Ukloni</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Automatski prijavi<ph name="END_WHITEPAPER_LINK" /> Googleu pojedinosti o mogućim sigurnosnim incidentima. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Sljedeće</translation>
<translation id="1201895884277373915">Više s ove web-lokacije</translation>
<translation id="1206967143813997005">Potpis inicijalima nije ispravan</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cijan</translation>
<translation id="1629803312968146339">Želite li da Chrome spremi tu karticu?</translation>
<translation id="1640180200866533862">KorisniÄka pravila</translation>
+<translation id="1640244768702815859">PokuÅ¡ajte <ph name="BEGIN_LINK" />otvoriti poÄetnu stranicu web-lokacije<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Mrežna konfiguracija nije važeća i nije ju bilo moguće uvesti.</translation>
<translation id="1644574205037202324">Povijest</translation>
<translation id="1645368109819982629">Protokol nije podržan</translation>
<translation id="1676269943528358898"><ph name="SITE" /> obiÄno upotrebljava enkripciju radi zaÅ¡tite vaÅ¡ih podataka. Prilikom ovog pokuÅ¡aja povezivanja Google Chromea s web-lokacijom <ph name="SITE" /> ta je web-lokacija vratila neuobiÄajene i netoÄne vjerodajnice. To može znaÄiti da se neki napadaÄ pokuÅ¡ava predstaviti kao <ph name="SITE" /> ili je zaslon za prijavu na Wi-Fi prekinuo vezu. VaÅ¡i su podaci joÅ¡ uvijek sigurni jer je Google Chrome zaustavio povezivanje prije razmjene podataka.</translation>
+<translation id="168328519870909584">NapadaÄi koji se trenutaÄno nalaze na <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogu pokuÅ¡ati instalirati opasne aplikacije na vaÅ¡ ureÄ‘aj radi kraÄ‘e ili brisanja vaÅ¡ih podataka (na primjer, fotografija, zaporki, poruka i brojeva kreditnih kartica).</translation>
<translation id="168841957122794586">Certifikat poslužitelja sadrži slab kriptografski kljuÄ!</translation>
-<translation id="1701955595840307032">Prikaz predloženog sadržaja</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264"><ph name="NAME" /> mora dopustiti da posjetiš tu web-lokaciju</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Broj stranice</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Pokušajte pokrenuti Mrežnu dijagnostiku sustava Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Ažurirajte zaporku za sinkronizaciju.</translation>
+<translation id="1787142507584202372">Ovdje se prikazuju vaše otvorene kartice</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google sigurno pregledavanje nedavno je <ph name="BEGIN_LINK" />otkrilo zlonamjerni softver<ph name="END_LINK" /> na web-lokaciji <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 poznati distributer zlonamjernog softvera. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Nevažeći zahtjev ili parametri zahtjeva</translation>
+<translation id="1834321415901700177">Ova web-lokacija sadrži štetne programe</translation>
<translation id="1838667051080421715">Gledate izvor web-stranice.</translation>
<translation id="1871208020102129563">Proxy poslužitelj postavljen je na upotrebu fiksnih proxy poslužitelja, a ne URL .pac skripte.</translation>
<translation id="1883255238294161206">Sažmi popis</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Lokalni klijent</translation>
<translation id="213826338245044447">Mobilne oznake</translation>
<translation id="2148716181193084225">Danas</translation>
-<translation id="2149973817440762519">Uredi oznaku</translation>
<translation id="2154054054215849342">Sinkronizacija nije dostupna za vašu domenu</translation>
<translation id="2166049586286450108">Potpuni administratorski pristup</translation>
<translation id="2166378884831602661">Web-lokacija ne može pružiti sigurnu vezu</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">TrenutaÄno ne možete otvoriti <ph name="SITE" /> jer web-lokacija upotrebljava HSTS. Mrežne pogreÅ¡ke i napadi uglavnom su privremeni, tako da će ta stranica vjerojatno kasnije funkcionirati. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Zanemarena nevažeća oznaka u indeksu <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Druge oznake</translation>
+<translation id="2355395290879513365">NapadaÄi možda mogu vidjeti slike koje gledate na ovoj web-lokaciji i izmijeniti ih kako bi vas prevarili.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Prikaži pravila bez postavljenih vrijednosti</translation>
<translation id="2396249848217231973">&amp;Poništi brisanje</translation>
<translation id="2455981314101692989">Ova web-stranica ima onemogućeno automatsko popunjavanje za taj obrazac.</translation>
+<translation id="2460160116472764928">Google sigurno pregledavanje nedavno je <ph name="BEGIN_LINK" />otkrilo zlonamjerni softver<ph name="END_LINK" /> na web-lokaciji <ph name="SITE" />. Web-lokacije koje su inaÄe sigurne ponekad mogu biti zaražene zlonamjernim softverom. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Ispuni</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />pokrenuti Mrežnu dijagnostiku<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Nevažeći URL pretraživanja.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Jeste li sigurni da te stranice želite izbrisati iz Vaše povijesti?</translation>
<translation id="2677748264148917807">Napusti</translation>
<translation id="269990154133806163">Poslužitelj je prikazao certifikat koji nije javno otkriven putem pravila Transparentnost certifikata. Taj se zahtjev postavlja za neke certifikate radi provjere njihove pouzdanosti i zaštite od napada. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte više<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">Popis za Äitanje</translation>
<translation id="2704283930420550640">Vrijednost ne odgovara formatu.</translation>
<translation id="2704951214193499422">Chromium nije uspio potvrditi vašu karticu. Pokušajte ponovo kasnije.</translation>
<translation id="2705137772291741111">Spremljena (predmemorirana) kopija web-lokacije nije Äitljiva.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">PokuÅ¡ali ste otvoriti <ph name="DOMAIN" />, ali izdavaÄ je povukao certifikat koji je poslužitelj prikazao. To znaÄi da sigurnosne vjerodajnice koje je poslužitelj prikazao nikako ne biste trebali smatrati pouzdanima. Možda komunicirate s napadaÄem. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Traži dopuštenje</translation>
<translation id="2713444072780614174">Bijela</translation>
+<translation id="2720342946869265578">U blizini</translation>
<translation id="2721148159707890343">Zahtjev je uspio</translation>
<translation id="2728127805433021124">Certifikat poslužitelja potpisan je slabim algoritmom potpisa.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />pokrenuti Dijagnostiku povezivosti<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Možete onemogućiti sve proxyje konfigurirane za vezu sa stranice postavki.</translation>
<translation id="2955913368246107853">Zatvori traku za traženje</translation>
<translation id="2958431318199492670">Mrežna konfiguracija nije u skladu sa standardima ONC. Dijelove konfiguracije nije moguće uvesti.</translation>
+<translation id="29611076221683977">NapadaÄi koji se trenutaÄno nalaze na <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).</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="2985306909656435243">Ako je to omogućeno, Chromium će pohraniti kopiju vaše kartice na uređaj radi bržeg ispunjavanja obrazaca.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Sakrij detalje</translation>
<translation id="3587482841069643663">Sve</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Poslužitelj ne podržava proširenje TLS ponovljenog pregovaranja.</translation>
<translation id="36224234498066874">Obriši podatke o pregledavanju...</translation>
<translation id="362276910939193118">Pokaži cijelu povijest</translation>
<translation id="3623476034248543066">Prikaži vrijednost</translation>
@@ -270,6 +278,7 @@
<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="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>
@@ -279,10 +288,10 @@
<translation id="3712624925041724820">Licence su potrošene</translation>
<translation id="3714780639079136834">ukljuÄite mobilne podatke ili Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />provjerite proxy, vatrozid i konfiguraciju DNS-a<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Ako ste svjesni sigurnosnih rizika, možete <ph name="BEGIN_LINK" />posjetiti tu nesigurnu web-lokaciju<ph name="END_LINK" /> prije uklanjanja opasnih programa.</translation>
<translation id="3739623965217189342">Veza koju ste kopirali</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="3788090790273268753">Certifikat za ovu web-lokaciju istjeÄe 2016., a lanac certifikata sadrži certifikat s SHA-1 potpisom.</translation>
<translation id="382518646247711829">Ako upotrebljavate proxy poslužitelj...</translation>
<translation id="3828924085048779000">Prazne zaporke nisu dopuštene.</translation>
<translation id="3845539888601087042">Prikazuje se povijest s uređaja na kojima ste prijavljeni. <ph name="BEGIN_LINK" />Saznajte više<ph name="END_LINK" />.</translation>
@@ -304,6 +313,7 @@
<translation id="4058922952496707368">Stavka "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Klijent i poslužitelj ne podržavaju uobiÄajenu verziju SSL protokola ili paket Å¡ifri.</translation>
<translation id="4079302484614802869">Konfiguracija proxy poslužitelja postavljena je za upotrebu URL-a .pac skripte, a ne fiksnih proxy poslužitelja.</translation>
+<translation id="4098354747657067197">Pred vama je obmanjujuća web-lokacija</translation>
<translation id="4103249731201008433">Serijski broj uređaja nije važeći</translation>
<translation id="4103763322291513355">Posjetite &lt;strong&gt;chrome://policy&lt;/strong&gt; da biste vidjeli popis nedopuštenih URL-ova i druga pravila koja je nametnuo vaš administrator sustava.</translation>
<translation id="4110615724604346410">Poslužitelj nije mogao dokazati da je to <ph name="DOMAIN" /> jer sigurnosni certifikat sadrži pogreške. Razlog može biti pogrešna konfiguracija ili napad na vašu vezu. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte više<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -321,15 +331,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ništa}=1{1 aplikacija ($1)}=2{2 aplikacije ($1, $2)}one{# aplikacija ($1, $2, $3)}few{# aplikacije ($1, $2, $3)}other{# aplikacija ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Padovi programa</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Pokušajte pokrenuti Mrežnu dijagnostiku<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Veza s ovom web-lokacijom nije potpuno sigurna</translation>
<translation id="4250680216510889253">Ne</translation>
<translation id="425582637250725228">Unesene promjene ne mogu se spremiti.</translation>
<translation id="4258748452823770588">Potpis nije valjan</translation>
<translation id="4269787794583293679">(Nema korisniÄkog imena)</translation>
+<translation id="4280429058323657511">, istek <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google sigurno pregledavanje nedavno je <ph name="BEGIN_LINK" />pronašlo štetne programe<ph name="END_LINK" /> na web-lokaciji <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte više<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Nadređeni prijedlozi</translation>
<translation id="4304224509867189079">Prijavi se</translation>
<translation id="432290197980158659">Poslužitelj je prikazao certifikat koji ne odgovara ugraÄ‘enim oÄekivanjima. Ta su oÄekivanja ukljuÄena za odreÄ‘ene web-lokacije visoke razine sigurnosti radi vaÅ¡e zaÅ¡tite. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Članak nije pronađen</translation>
+<translation id="4326324639298822553">Provjerite datum isteka, pa pokušajte ponovo</translation>
<translation id="4331708818696583467">Nije sigurno</translation>
+<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="4381091992796011497">Ime korisnika:</translation>
<translation id="4394049700291259645">Onemogući</translation>
@@ -347,6 +362,7 @@
<translation id="4589078953350245614">Pokušali ste otvoriti <ph name="DOMAIN" />, ali poslužitelj je prikazao nevažeći certifikat. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte više<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Kartice s drugih uređaja</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Gledate stranicu proširenja.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -355,10 +371,13 @@
<translation id="4726672564094551039">Ponovo uÄitaj pravila</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4744603770635761495">Izvršna putanja</translation>
+<translation id="4750917950439032686">Vaši podaci (na primjer, zaporke i brojevi kreditnih kartica) privatni su kada se šalju na tu web-lokaciju.</translation>
<translation id="4756388243121344051">&amp;Povijest</translation>
+<translation id="4759118997339041434">Automatsko popunjavanje podataka o plaćanju onemogućeno je</translation>
<translation id="4764776831041365478">Web-stranica na adresi <ph name="URL" /> možda privremeno nije dostupna ili je trajno preseljena na novu web-adresu.</translation>
<translation id="4771973620359291008">Došlo je do nepoznate pogreške.</translation>
<translation id="4800132727771399293">Provjerite datum isteka i CVC pa pokušajte ponovo</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Pogreška mreže</translation>
<translation id="4816492930507672669">Prilagodi stranici</translation>
<translation id="4850886885716139402">Prikaz</translation>
@@ -367,6 +386,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Radnje</translation>
<translation id="4958444002117714549">Proširi popis</translation>
@@ -394,7 +414,6 @@
<translation id="5181140330217080051">Preuzimanje</translation>
<translation id="5190835502935405962">Traka oznaka</translation>
<translation id="5199729219167945352">Eksperimenti</translation>
-<translation id="5199841536747119669">Ovdje će se prikazivati prijedlozi za vas</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="5299298092464848405">Pogreška u pravilu analize</translation>
@@ -402,8 +421,10 @@
<translation id="5308689395849655368">Onemogućeno je izvješćivanje o padu.</translation>
<translation id="5317780077021120954">Spremi</translation>
<translation id="5327248766486351172">Naziv</translation>
+<translation id="5337705430875057403">NapadaÄi na <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 (na primjer, zaporki, telefonskih brojeva ili kreditnih kartica).</translation>
<translation id="5359637492792381994">Poslužitelj nije mogao dokazati da je to <ph name="DOMAIN" /> jer sigurnosni certifikat trenutaÄno nije važeći. Razlog može biti pogreÅ¡na konfiguracija ili napad na vaÅ¡u vezu. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Pohrana postavki pravila nije uspjela</translation>
+<translation id="5386426401304769735">Lanac certifikata za ovu web-lokaciju sadrži certifikat s SHA-1 potpisom.</translation>
<translation id="5421136146218899937">Obriši podatke pregledavanja...</translation>
<translation id="5430298929874300616">Ukloni oznaku</translation>
<translation id="5431657950005405462">Datoteka nije pronađena</translation>
@@ -424,17 +445,20 @@
<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="5565735124758917034">Aktivno</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>
+<translation id="5580958916614886209">Provjerite mjesec isteka, pa pokušajte ponovo</translation>
<translation id="560412284261940334">Upravljanje nije podržano</translation>
<translation id="5610142619324316209">provjerite vezu</translation>
<translation id="5617949217645503996">Host <ph name="HOST_NAME" /> preusmjerio vas je previše puta.</translation>
<translation id="5622887735448669177">Želite li zatvoriti tu web-lokaciju?</translation>
<translation id="5629630648637658800">UÄitavanje postavki pravila nije uspjelo</translation>
<translation id="5631439013527180824">Token za upravljanje uređajem nije važeći</translation>
+<translation id="5669703222995421982">Predlaganje sadržaja</translation>
+<translation id="5675650730144413517">Stranica ne funkcionira</translation>
<translation id="5677928146339483299">Blokirano</translation>
<translation id="5694783966845939798">PokuÅ¡ali ste otvoriti <ph name="DOMAIN" />, ali je poslužitelj pružio certifikat potpisan slabim algoritmom potpisa (kao Å¡to je SHA-1). To znaÄi da su sigurnosne vjerodajnice koje je poslužitelj pružio možda krivotvorene, a poslužitelj možda nije onaj koji oÄekujete (možda komunicirate s napadaÄem). <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Identitet ove web lokacije nije ovjeren.</translation>
<translation id="5720705177508910913">TrenutaÄni korisnik:</translation>
-<translation id="572328651809341494">Nedavne kartice</translation>
<translation id="5732392974455271431">Tvoji je roditelji mogu deblokirati</translation>
<translation id="5784606427469807560">Pojavio se problem prilikom potvrđivanja kartice. Provjerite internetsku vezu i pokušajte ponovo.</translation>
<translation id="5785756445106461925">Nadalje, ova stranica sadrži druge resurse koji nisu sigurni. Te resurse mogu vidjeti drugi tijekom prijenosa i napadaÄ ih može izmijeniti kako bi promijenio izgled stranice.</translation>
@@ -443,6 +467,7 @@
<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="5843436854350372569">PokuÅ¡ali ste otvoriti <ph name="DOMAIN" />, ali poslužitelj je prikazao certifikat sa slabim kljuÄem. Privatni je kljuÄ možda otkriven, a poslužitelj možda nije onaj koji oÄekujete (možda komunicirate s napadaÄem). <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Nova mapa</translation>
<translation id="5869405914158311789">Web-lokacija ne može se dohvatiti</translation>
@@ -452,6 +477,7 @@
<translation id="59107663811261420">Google Payments ne podržava tu vrstu kartice za ovog trgovca. Odaberite neku drugu karticu.</translation>
<translation id="59174027418879706">Omogućeno</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="5966707198760109579">Tjedan</translation>
<translation id="5967867314010545767">Ukloni iz povijesti</translation>
<translation id="5975083100439434680">Smanji</translation>
@@ -470,8 +496,11 @@
<translation id="614940544461990577">Pokušajte sljedeće:</translation>
<translation id="6151417162996330722">Certifikat poslužitelja ima predugo razdoblje valjanosti.</translation>
<translation id="6165508094623778733">Saznajte više</translation>
+<translation id="6177128806592000436">Veza s web-lokacijom nije sigurna</translation>
<translation id="6203231073485539293">Provjerite internetsku vezu</translation>
<translation id="6218753634732582820">Želite li ukloniti adresu iz Chromiuma?</translation>
+<translation id="6251924700383757765">Pravila o privatnosti</translation>
+<translation id="625755898061068298">Onemogućili ste sigurnosna upozorenja za tu web-lokaciju.</translation>
<translation id="6259156558325130047">&amp;Ponovi promjenu rasporeda</translation>
<translation id="6263376278284652872">Oznake domene <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Natrag u sigurnost</translation>
@@ -480,6 +509,7 @@
<translation id="6305205051461490394">Web-lokacija <ph name="URL" /> nije dostupna.</translation>
<translation id="6321917430147971392">Provjerite postavke DNS-a</translation>
<translation id="6328639280570009161">Pokušajte onemogućiti predviđanja mreže</translation>
+<translation id="6328786501058569169">Ova je web-lokacija obmanjujuća</translation>
<translation id="6337534724793800597">Filtriranje pravila prema nazivu</translation>
<translation id="6342069812937806050">Samo sad</translation>
<translation id="6345221851280129312">nepoznata veliÄina</translation>
@@ -488,6 +518,8 @@
<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="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>
<translation id="6416403317709441254">TrenutaÄno ne možete otvoriti <ph name="SITE" /> jer je web-lokacija poslala kodirane vjerodajnice koje Chromium ne može obraditi. Mrežne pogreÅ¡ke i napadi uglavnom su privremeni, tako da će ta stranica vjerojatno kasnije funkcionirati. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Nije moguće provjeriti je li certifikat opozvan.</translation>
@@ -497,10 +529,12 @@
<translation id="6458467102616083041">Zanemareno jer je zadano pretraživanje onemogućeno pravilom.</translation>
<translation id="6462969404041126431">Poslužitelj ne može dokazati da je to <ph name="DOMAIN" /> jer bi sigurnosni certifikat mogao biti opozvan. Razlog može biti pogrešna konfiguracija ili napad na vašu vezu. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte više<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Ponovi brisanje</translation>
<translation id="6534179046333460208">Prijedlozi FiziÄkog weba</translation>
<translation id="6550675742724504774">Opcije</translation>
+<translation id="6556239504065605927">Sigurna veza</translation>
<translation id="6563469144985748109">Voditelj je još nije odobrio</translation>
<translation id="6593753688552673085">manje od <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opcije Å¡ifriranja</translation>
@@ -509,7 +543,6 @@
<translation id="6644283850729428850">Ovo je pravilo zastarjelo.</translation>
<translation id="6652240803263749613">Poslužitelj nije mogao dokazati da je to <ph name="DOMAIN" /> jer operativni sustav raÄunala sigurnosni certifikat ne smatra pouzdanim. Razlog može biti pogreÅ¡na konfiguracija ili napad na vaÅ¡u vezu. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte viÅ¡e<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Uređivanje mape</translation>
-<translation id="6660210980321319655">Automatski prijavljeno <ph name="CRASH_TIME" /></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>
@@ -521,6 +554,7 @@
<translation id="6753269504797312559">Vrijednost pravila</translation>
<translation id="6757797048963528358">Uređaj je u stanju mirovanja.</translation>
<translation id="6778737459546443941">Roditelj je još nije odobrio</translation>
+<translation id="6810899417690483278">ID prilagođavanja</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Web-stranica na <ph name="URL" /> trenutno nije dostupna. Možda je preopterećena ili iskljuÄena zbog održavanja.</translation>
<translation id="6831043979455480757">Prevedi</translation>
@@ -541,6 +575,8 @@
<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>
+<translation id="7064851114919012435">Podaci za kontakt</translation>
+<translation id="7079718277001814089">Ova web-lokacija sadrži zlonamjerni softver</translation>
<translation id="7087282848513945231">Županija</translation>
<translation id="7088615885725309056">Starije</translation>
<translation id="7090678807593890770">Potražite upit <ph name="LINK" /> na Googleu</translation>
@@ -555,6 +591,7 @@
<translation id="7210863904660874423">Host <ph name="HOST_NAME" /> ne pridržava se sigurnosnih standarda.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Saznajte više<ph name="END_LINK" /> o ovom problemu.</translation>
<translation id="7219179957768738017">Veza upotrebljava <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Web-lokacija pred vama sadrži zlonamjerni softver</translation>
<translation id="724975217298816891">Unesite datum isteka i CVC za karticu <ph name="CREDIT_CARD" /> da biste ažurirali podatke o kartici. Nakon što ih potvrdite, podaci o kartici dijelit će se s ovom web-lokacijom.</translation>
<translation id="725866823122871198">Sigurnu vezu s domenom <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> nije moguće uspostaviti jer datum i vrijeme na raÄunalu (<ph name="DATE_AND_TIME" />) nisu toÄni.</translation>
<translation id="7269802741830436641">Ova web-stranica ima petlju za preusmjeravanje</translation>
@@ -610,6 +647,7 @@ Psst! Sljedeći bi vam put mogao koristiti anonimni naÄin <ph name="SHORTCUT_KE
<translation id="7658239707568436148">Odustani</translation>
<translation id="7667346355482952095">Vraćeni token pravila prazan je ili ne odgovara trenutaÄnom tokenu</translation>
<translation id="7668654391829183341">Nepoznati uređaj</translation>
+<translation id="7669271284792375604">NapadaÄi na ovoj web-lokaciji mogu vas pokuÅ¡ati navesti na instaliranje programa koji smanjuju kvalitetu pregledavanja interneta (npr. promjenom poÄetne stranice ili prikazivanjem dodatnih oglasa na web-lokacijama koje posjetite).</translation>
<translation id="7674629440242451245">Zanimaju li vas nove, kul znaÄajke preglednika Chrome? Isprobajte naÅ¡ razvojni kanal na stranici chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Idi na web-lokaciju <ph name="SITE" /> (nije sigurno)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Web-lokacija se ne može uÄitati iz predmemorije</translation>
@@ -625,14 +663,17 @@ Psst! Sljedeći bi vam put mogao koristiti anonimni naÄin <ph name="SHORTCUT_KE
<translation id="7800304661137206267">Veza je Å¡ifrirana pomoću <ph name="CIPHER" />, za provjeru autentiÄnosti poruke koristi se <ph name="MAC" />, a <ph name="KX" /> služi za mehanizam razmjene kljuÄeva.</translation>
<translation id="780301667611848630">Ne, hvala</translation>
<translation id="7805768142964895445">Status</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Želite li s Chromea ukloniti prijedlog za obrasce?</translation>
<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="7887683347370398519">Provjerite CVC i pokušajte ponovo</translation>
<translation id="7912024687060120840">U mapi:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Certifkat poslužitelja još nije valjan.</translation>
<translation id="7942349550061667556">Crvena</translation>
+<translation id="7947285636476623132">Provjerite godinu isteka, pa pokušajte ponovo</translation>
<translation id="7951415247503192394">(32-bitni)</translation>
<translation id="7956713633345437162">Mobilne oznake</translation>
<translation id="7961015016161918242">Nikad</translation>
@@ -640,7 +681,10 @@ Psst! Sljedeći bi vam put mogao koristiti anonimni naÄin <ph name="SHORTCUT_KE
<translation id="7983301409776629893">Uvijek prevedi <ph name="ORIGINAL_LANGUAGE" /> na <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Nije navedeno</translation>
<translation id="8012647001091218357">Nismo uspjeli stupiti u kontakt s tvojim roditeljima. Pokušaj ponovo.</translation>
+<translation id="8025119109950072390">NapadaÄi na ovoj web-lokaciji 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 kreditnih kartica).</translation>
+<translation id="803030522067524905">Google sigurno pregledavanje nedavno je otkrilo krađu identiteta na web-lokaciji <ph name="SITE" />. Web-lokacije za krađu identiteta lažno se predstavljaju kao druge web-lokacije kako bi vas prevarile. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte više<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Jezik ove stranice jest <ph name="SOURCE_LANGUAGE" />. Želite li je prevesti na <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Slanje povratnih informacija</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>
@@ -651,9 +695,11 @@ Psst! Sljedeći bi vam put mogao koristiti anonimni naÄin <ph name="SHORTCUT_KE
<translation id="8150722005171944719">Datoteka na adresi <ph name="URL" /> nije Äitljiva. Možda je uklonjena ili premjeÅ¡tena ili dozvole datoteka sprjeÄavaju pristup.</translation>
<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="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="8230421197304563332">NapadaÄi koji se trenutaÄno nalaze na <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).</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="8249320324621329438">Zadnje dohvaćanje:</translation>
<translation id="8261506727792406068">Izbriši</translation>
@@ -662,11 +708,13 @@ Psst! Sljedeći bi vam put mogao koristiti anonimni naÄin <ph name="SHORTCUT_KE
<translation id="8294431847097064396">Izvor</translation>
<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="8349305172487531364">Traka oznaka</translation>
<translation id="8363502534493474904">iskljuÄite naÄin rada u zrakoplovu</translation>
<translation id="8364627913115013041">Nije postavljeno.</translation>
<translation id="8380941800586852976">Opasno</translation>
<translation id="8382348898565613901">Ovdje će se prikazivati oznake koje ste nedavno posjetili</translation>
+<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="8428213095426709021">Postavke</translation>
@@ -678,9 +726,9 @@ Psst! Sljedeći bi vam put mogao koristiti anonimni naÄin <ph name="SHORTCUT_KE
<translation id="8498891568109133222">Hostu <ph name="HOST_NAME" /> bilo je potrebno previše vremena za odgovor.</translation>
<translation id="852346902619691059">Poslužitelj nije mogao dokazati da je to <ph name="DOMAIN" /> jer operativni sustav uređaja sigurnosni certifikat ne smatra pouzdanim. Razlog može biti pogrešna konfiguracija ili napad na vašu vezu. <ph name="BEGIN_LEARN_MORE_LINK" />Saznajte više<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Google Payments ne podržava tu vrstu kartice. Odaberite neku drugu karticu.</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="8553075262323480129">Prijevod nije uspio jer nije bilo moguće odrediti jezik stranice.</translation>
<translation id="8559762987265718583">Sigurnu vezu s domenom <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> nije moguće uspostaviti jer datum i vrijeme (<ph name="DATE_AND_TIME" />) na vaÅ¡em ureÄ‘aju nisu toÄni.</translation>
-<translation id="856992080682148">Certifikat za ovu web-lokaciju istjeÄe 2017. ili kasnije, a lanac certifikata sadrži certifikat s SHA-1 potpisom.</translation>
<translation id="8571890674111243710">Prijevod stranice na <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Certifikat ne navodi mehanizam za provjeru svojeg opoziva.</translation>
<translation id="8620436878122366504">Roditelji je još nisu odobrili</translation>
@@ -693,10 +741,12 @@ Psst! Sljedeći bi vam put mogao koristiti anonimni naÄin <ph name="SHORTCUT_KE
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;DNS adresa&lt;/abbr&gt; hosta <ph name="HOST_NAME" /> nije pronađena. U tijeku je dijagnosticiranje problema.</translation>
<translation id="8790007591277257123">&amp;Ponovi brisanje</translation>
<translation id="8798099450830957504">Zadano</translation>
+<translation id="8800988563907321413">Ovdje se prikazuju prijedlozi u blizini za vas</translation>
<translation id="8804164990146287819">Pravila o privatnosti</translation>
<translation id="8820817407110198400">Knjižne oznake</translation>
<translation id="8834246243508017242">Omogući Automatsko popunjavanje pomoću Kontakata...</translation>
<translation id="883848425547221593">Druge oznake</translation>
+<translation id="884264119367021077">Adresa za dostavu</translation>
<translation id="884923133447025588">Nije pronađen mehanizam za opoziv.</translation>
<translation id="885730110891505394">Dijeljenje s Googleom</translation>
<translation id="8866481888320382733">Pogreška pri analizi postavki pravila</translation>
@@ -714,19 +764,22 @@ Psst! Sljedeći bi vam put mogao koristiti anonimni naÄin <ph name="SHORTCUT_KE
<translation id="8971063699422889582">Istekao je certifikat poslužitelja.</translation>
<translation id="8987927404178983737">Mjesec</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Sljedeća web-lokacija sadrži štetne programe</translation>
<translation id="9001074447101275817">Proxy <ph name="DOMAIN" /> zahtijeva korisniÄko ime i zaporku.</translation>
<translation id="901974403500617787">Oznake koje se primjenjuju na razini sustava može postaviti samo vlasnik: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Ova je stranica prevedena na <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Sigurnosna pogreška</translation>
<translation id="9038649477754266430">Upotreba usluge predviÄ‘anja za brže uÄitavanje stranica</translation>
<translation id="9039213469156557790">Nadalje, ova stranica sadrži druge resurse koji nisu sigurni. Te resurse mogu vidjeti drugi tijekom prijenosa i napadaÄ ih može izmijeniti kako bi promijenio ponaÅ¡anje stranice.</translation>
+<translation id="9040185888511745258">NapadaÄi na web-lokaciji <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogu vas pokuÅ¡ati navesti na instaliranje programa koji smanjuju kvalitetu pregledavanja interneta (na primjer, promjenom poÄetne stranice ili prikazivanjem dodatnih oglasa na web-lokacijama koje posjetite).</translation>
<translation id="9050666287014529139">Zaporka</translation>
<translation id="9065203028668620118">Uredi</translation>
<translation id="9068849894565669697">Odaberite boju</translation>
<translation id="9076283476770535406">Možda ima sadržaj za odrasle</translation>
-<translation id="9092364396508701805">Stranica hosta <ph name="HOST_NAME" /> ne funkcionira</translation>
<translation id="9103872766612412690"><ph name="SITE" /> obiÄno upotrebljava enkripciju radi zaÅ¡tite vaÅ¡ih podataka. Prilikom ovog pokuÅ¡aja povezivanja Chromiuma s web-lokacijom <ph name="SITE" /> ta je web-lokacija vratila neuobiÄajene
i netoÄne vjerodajnice. To može znaÄiti da se neki napadaÄ pokuÅ¡ava predstaviti kao <ph name="SITE" /> ili je zaslon za prijavu na Wi-Fi prekinuo vezu. VaÅ¡i su podaci joÅ¡ uvijek sigurni jer je Chromium zaustavio povezivanje prije razmjene podataka.</translation>
<translation id="9137013805542155359">Prikaži original</translation>
+<translation id="9137248913990643158">Pokrenite Chrome i prijavite se na njega da biste mogli upotrebljavati tu aplikaciju.</translation>
<translation id="9148507642005240123">&amp;Poništi uređivanje</translation>
<translation id="9157595877708044936">Postavljanje...</translation>
<translation id="9170848237812810038">&amp;Poništi</translation>
@@ -739,7 +792,6 @@ i netoÄne vjerodajnice. To može znaÄiti da se neki napadaÄ pokuÅ¡ava predsta
<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="962701380617707048">Unesite datum isteka i CVC za karticu <ph name="CREDIT_CARD" /> da biste ažurirali podatke o kartici</translation>
<translation id="969892804517981540">Službeni sastavak</translation>
<translation id="988159990683914416">Sastavak razvojnog programera</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 1ab0359b417..af367ed6d7d 100644
--- a/chromium/components/strings/components_strings_hu.xtb
+++ b/chromium/components/strings/components_strings_hu.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Újracsatlakozás Wi-Fi-hálózathoz</translation>
<translation id="1175364870820465910">&amp;Nyomtatás...</translation>
<translation id="1181037720776840403">Eltávolítás</translation>
+<translation id="1184214524891303587">A lehetséges biztonsági események adatainak <ph name="BEGIN_WHITEPAPER_LINK" />automatikus jelentése<ph name="END_WHITEPAPER_LINK" /> a Google-nak. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Következő</translation>
<translation id="1201895884277373915">Továbbiak erről a webhelyről</translation>
<translation id="1206967143813997005">Hibás alapértelmezett aláírás</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cián</translation>
<translation id="1629803312968146339">Szeretné, hogy a Chrome mentse ezt a kártyát?</translation>
<translation id="1640180200866533862">Felhasználói házirendek</translation>
+<translation id="1640244768702815859">Próbálja meg <ph name="BEGIN_LINK" />megnyitni a webhely főoldalát<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">A hálózati konfiguráció érvénytelen és nem importálható.</translation>
<translation id="1644574205037202324">Előzmények</translation>
<translation id="1645368109819982629">Nem támogatott protokoll</translation>
<translation id="1676269943528358898">A(z) <ph name="SITE" /> webhely rendes esetben titkosítást alkalmaz az Ön adatainak védelme érdekében. Amikor a Google Chrome most csatlakozni próbált, a(z) <ph name="SITE" /> webhely szokatlan és helytelen hitelesítési adatokat küldött vissza. Ez olyankor fordulhat elő, amikor egy támadó megpróbálja magát kiadni a(z) <ph name="SITE" /> webhelynek, vagy valamilyen Wi-Fi-bejelentkezési képernyő megszakította a kapcsolatot. Adatai továbbra is biztonságban vannak, mivel a Google Chrome még azt megelőzően megszakította a kapcsolatot, hogy bármiféle adatcserére sor kerülhetett volna.</translation>
+<translation id="168328519870909584">Előfordulhat, hogy a(z) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webhely támadói veszélyes alkalmazásokat kísérelnek meg telepíteni eszközére, amelyek ellopják vagy törlik adatait (például fotóit, jelszavait, üzeneteit és hitelkártyaadatait).</translation>
<translation id="168841957122794586">A szervertanúsítvány gyenge titkosítási kulcsot tartalmaz.</translation>
-<translation id="1701955595840307032">Javasolt tartalmak fogadása</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">A webhely felkereséséhez <ph name="NAME" /> engedélyére van szükség</translation>
<translation id="1734864079702812349">American Express</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Oldalszám</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Próbálkozzon a Windows Hálózati diagnosztika futtatásával<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Kérjük, frissítse szinkronizálási összetett jelszavát.</translation>
+<translation id="1787142507584202372">A megnyitott lapok helye</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">A Google Biztonságos Böngészés funkciója nemrég <ph name="BEGIN_LINK" />rosszindulatú programot észlelt<ph name="END_LINK" /> a(z) <ph name="SITE" /> webhelyen. A normál esetben biztonságos webhelyek néha rosszindulatú programokkal fertőzöttek. A rosszindulatú tartalom az ilyen programok következő ismert terjesztőjétől származik: <ph name="SUBRESOURCE_HOST" />. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Érvénytelen kérés vagy kérésparaméter</translation>
+<translation id="1834321415901700177">A webhely ártalmas programokat tartalmaz</translation>
<translation id="1838667051080421715">Jelenleg egy weboldal forrását látja.</translation>
<translation id="1871208020102129563">A proxy fix proxyszerverek használatára van beállítva, nem pedig .pac típusú szkript URL címének használatára.</translation>
<translation id="1883255238294161206">Lista bezárása</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Natív kliens</translation>
<translation id="213826338245044447">Mobil könyvjelzők</translation>
<translation id="2148716181193084225">Ma</translation>
-<translation id="2149973817440762519">Könyvjelző szerkesztése</translation>
<translation id="2154054054215849342">A szinkronizálás az Ön domainjén nem áll rendelkezésre</translation>
<translation id="2166049586286450108">Teljes rendszergazdai hozzáférés</translation>
<translation id="2166378884831602661">A webhely nem képes biztonságos kapcsolatot nyújtani</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">A(z) <ph name="SITE" /> webhelyet pillanatnyilag nem tudja felkeresni, mert a webhely HSTS-t használ. A hálózati hibák és támadások rendszerint átmenetiek, ezért az említett oldal később valószínűleg már működni fog. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Mellőzött érvénytelen könyvjelző a(z) <ph name="ENTRY_INDEX" />. indexnél</translation>
<translation id="2354001756790975382">További könyvjelzők</translation>
+<translation id="2355395290879513365">A felhasználók esetleg láthatják a webhelyen éppen megtekintett képeit, és a képeket módosítva félrevezethetik Önt.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Beállított értékkel nem rendelkező házirendek megjelenítése</translation>
<translation id="2396249848217231973">&amp;Törlés visszavonása</translation>
<translation id="2455981314101692989">A weboldal letiltotta az automatikus kitöltést erre az űrlapra.</translation>
+<translation id="2460160116472764928">A Google Biztonságos Böngészés funkciója nemrég <ph name="BEGIN_LINK" />rosszindulatú programot észlelt<ph name="END_LINK" /> a(z) <ph name="SITE" /> webhelyen. A rendes esetben biztonságos webhelyek néha rosszindulatú programokkal fertőzöttek. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Kitöltés</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Hálózati diagnosztika futtatása<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Érvénytelen keresési URL</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Biztosan törölni szeretné ezeket az oldalakat az előzmények közül?</translation>
<translation id="2677748264148917807">Lap elhagyása</translation>
<translation id="269990154133806163">A szerver olyan tanúsítványt mutatott be, amelyet nem A tanúsítványok átláthatósága keretrendszer segítségével hoztak nyilvánosságra. Ez egyes tanúsítványoknál követelmény annak biztosítása érdekében, hogy az adott tanúsítványok megbízhatók, és védelmet nyújtanak a támadók ellen. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Olvasási lista</translation>
<translation id="2704283930420550640">Az érték nem egyezik a formátummal.</translation>
<translation id="2704951214193499422">A Chromium ez alkalommal nem tudta ellenőrizni az Ön kártyáját. Próbálja újra később.</translation>
<translation id="2705137772291741111">A webhely mentett (gyorsítótárazott) példánya nem olvasható.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">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 tanúsítványában egyáltalán nem lehet megbízni. Lehet, hogy támadóval áll kapcsolatban. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Engedély kérése</translation>
<translation id="2713444072780614174">Fehér</translation>
+<translation id="2720342946869265578">A közelben</translation>
<translation id="2721148159707890343">Sikeres kérés</translation>
<translation id="2728127805433021124">A szerver tanúsítványa gyenge aláírási algoritmussal van aláírva.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Kapcsolódási diagnosztika futtatása<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Bármelyik kapcsolatlétesítésre használt proxyt letilthatja a beállítások oldalon.</translation>
<translation id="2955913368246107853">Keresősáv bezárása</translation>
<translation id="2958431318199492670">A hálózati konfiguráció nem felel meg az ONC szabványnak. A konfiguráció egyes részeit nem lehet importálni.</translation>
+<translation id="29611076221683977">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 Mac számítógépére, amelyek ellopják vagy törlik adatait (például fotóit, jelszavait, üzeneteit és hitelkártyaadatait).</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="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>
@@ -257,7 +266,6 @@
<translation id="3586931643579894722">Részletek elrejtése</translation>
<translation id="3587482841069643663">Mind</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">A szerver nem támogatja a TLS renegotiation bővítményt.</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>
@@ -269,19 +277,20 @@
<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="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="3696411085566228381">nincs</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="370665806235115550">Betöltés...</translation>
<translation id="3712624925041724820">Az engedélyek elfogytak</translation>
<translation id="3714780639079136834">A mobiladatok vagy a Wi-Fi bekapcsolása</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />A proxy, a tűzfal és a DNS-konfiguráció ellenőrzése<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">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 a nem biztonságos webhelyet<ph name="END_LINK" />.</translation>
<translation id="3739623965217189342">Ãtmásolt 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="3788090790273268753">A webhely tanúsítványa 2016-ban jár le, a tanúsítványlánc pedig tartalmaz egy SHA-1 titkosítással aláírt tanúsítványt.</translation>
<translation id="382518646247711829">Ha proxyszervert használ...</translation>
<translation id="3828924085048779000">Az üres összetett jelszó nem engedélyezett.</translation>
<translation id="3845539888601087042">Előzmények megjelenítése bejelentkezett eszközeiről. <ph name="BEGIN_LINK" />További információ<ph name="END_LINK" />.</translation>
@@ -303,6 +312,7 @@
<translation id="4058922952496707368">"<ph name="SUBKEY" />" kulcs: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Az ügyfél és a szerver nem támogat ugyanolyan SSL-protokollverziót vagy rejtjelezési csomagot.</translation>
<translation id="4079302484614802869">A proxykonfiguráció a .pac típusú szkript URL-cím, nem pedig a fix proxyszerverek használatára van beállítva.</translation>
+<translation id="4098354747657067197">Megtévesztő webhely megnyitására készül</translation>
<translation id="4103249731201008433">Az eszköz sorozatszáma érvénytelen</translation>
<translation id="4103763322291513355">Látogasson el a &lt;strong&gt;chrome://policy&lt;/strong&gt; oldalra a feketelistán lévő URL-ek és egyéb, a rendszergazda által előírt szabályok megtekintéséhez.</translation>
<translation id="4110615724604346410">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 támadó térítette el az Ön kapcsolódását. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -320,15 +330,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{nincsen}=1{1 alkalmazás ($1)}=2{2 alkalmazás ($1, $2)}other{# alkalmazás ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Összeomlások</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Próbálkozzon a Hálózati diagnosztika futtatásával<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Kapcsolata a webhellyel nem teljesen biztonságos</translation>
<translation id="4250680216510889253">Nem</translation>
<translation id="425582637250725228">Előfordulhat, hogy módosításait nem menti a rendszer.</translation>
<translation id="4258748452823770588">Rossz aláírás</translation>
<translation id="4269787794583293679">(Nincs felhasználónév)</translation>
+<translation id="4280429058323657511">, lejár: <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">A Google Biztonságos Böngészés funkciója nemrég <ph name="BEGIN_LINK" />ártalmas programokat talált<ph name="END_LINK" /> a(z) <ph name="SITE" /> webhelyen. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Szülői javaslatok</translation>
<translation id="4304224509867189079">Bejelentkezés</translation>
<translation id="432290197980158659">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 vonatkozóan találhatók meg az Ön védelmének érdekében. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Nem sikerült megtalálni a cikket</translation>
+<translation id="4326324639298822553">Ellenőrizze a lejárati dátumot, majd próbálja újra</translation>
<translation id="4331708818696583467">Nem biztonságos</translation>
+<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="4381091992796011497">Felhasználónév:</translation>
<translation id="4394049700291259645">Kikapcsolás</translation>
@@ -346,6 +361,7 @@
<translation id="4589078953350245614">Megpróbálta elérni a(z) <ph name="DOMAIN" /> webhelyet, de a szerver érvénytelen tanúsítványt mutatott be. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Más eszközök lapjai</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Egy bővítményoldalt tekint meg.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -354,10 +370,13 @@
<translation id="4726672564094551039">Házirendek újratöltése</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4744603770635761495">Végrehajtható fájl útvonala</translation>
+<translation id="4750917950439032686">Adatai (például jelszava vagy hitelkártyaszáma) nem láthatók más számára, amikor a rendszer elküldi őket a webhelynek.</translation>
<translation id="4756388243121344051">&amp;Előzmények</translation>
+<translation id="4759118997339041434">A fizetési adatok automatikus kitöltése ki van kapcsolva</translation>
<translation id="4764776831041365478">A <ph name="URL" /> weboldal lehet, hogy ideiglenesen nem érhető el, vagy véglegesen új címre költözött.</translation>
<translation id="4771973620359291008">Ismeretlen hiba történt.</translation>
<translation id="4800132727771399293">Ellenőrizze a lejárati dátumot és a CVC-t, majd próbálja újra</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Hálózati hiba</translation>
<translation id="4816492930507672669">Igazítás az oldalmérethez</translation>
<translation id="4850886885716139402">Nézet</translation>
@@ -366,6 +385,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> és <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{és egy további weboldal}other{és # további weboldal}}</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="495170559598752135">Műveletek</translation>
<translation id="4958444002117714549">Lista részletes nézete</translation>
@@ -393,7 +413,6 @@
<translation id="5181140330217080051">Letöltés</translation>
<translation id="5190835502935405962">Könyvjelzősáv</translation>
<translation id="5199729219167945352">Kísérletek</translation>
-<translation id="5199841536747119669">A javaslatok helye</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="5299298092464848405">Irányelv-előfeldolgozási hiba</translation>
@@ -401,8 +420,10 @@
<translation id="5308689395849655368">A hibabejelentés ki van kapcsolva.</translation>
<translation id="5317780077021120954">Mentés</translation>
<translation id="5327248766486351172">Név</translation>
+<translation id="5337705430875057403">A(z) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webhely támadói megpróbálhatják becsapni Önt, hogy például veszélyes szoftvert telepítsen vagy felfedje személyes adatait (jelszavát, telefonszámát, hitelkártyaszámát stb.).</translation>
<translation id="5359637492792381994">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványa jelenleg nem érvényes. Ennek oka lehet konfigurációs hiba, vagy hogy támadó térítette el az Ön kapcsolatát. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Az irányelv-beállítások tárolása sikertelen</translation>
+<translation id="5386426401304769735">A webhely tanúsítványlánca SHA-1 titkosítással aláírt tanúsítványt tartalmaz.</translation>
<translation id="5421136146218899937">Böngészési adatok törlése...</translation>
<translation id="5430298929874300616">Könyvjelző törlése</translation>
<translation id="5431657950005405462">A fájl nem található</translation>
@@ -423,17 +444,20 @@
<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="5565735124758917034">Aktív</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>
+<translation id="5580958916614886209">Ellenőrizze a lejárati hónapot, majd próbálja újra</translation>
<translation id="560412284261940334">A kezelés nem támogatott</translation>
<translation id="5610142619324316209">A kapcsolat ellenőrzése</translation>
<translation id="5617949217645503996">A(z) <ph name="HOST_NAME" /> túl sokszor irányította át.</translation>
<translation id="5622887735448669177">Szeretné elhagyni ezt a webhelyet?</translation>
<translation id="5629630648637658800">Az irányelv-beállítások betöltése sikertelen</translation>
<translation id="5631439013527180824">Érvénytelen eszközkezelési token</translation>
+<translation id="5669703222995421982">Személyre szabott tartalmak fogadása</translation>
+<translation id="5675650730144413517">Az oldal nem működik</translation>
<translation id="5677928146339483299">Letiltva</translation>
<translation id="5694783966845939798">Megpróbálta elérni a(z) <ph name="DOMAIN" /> webhelyet, de a szerver gyenge aláírási algoritmust (pl. SHA-1-et) használó tanúsítványt mutatott be. Ez alapján elképzelhető, hogy a szerver által megadott biztonsági tanúsítványt hamisították, és a szerver nem az, amelyikre számított (lehet, hogy éppen a támadóval áll kapcsolatban). <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">A webhely valódiságát nem ellenőriztük.</translation>
<translation id="5720705177508910913">Jelenlegi felhasználó</translation>
-<translation id="572328651809341494">Nemrég megnyitott lapok</translation>
<translation id="5732392974455271431">A letiltást a szüleid oldhatják fel</translation>
<translation id="5784606427469807560">A kártya ellenőrzése során hiba történt. Ellenőrizze az internetkapcsolatot, majd próbálkozzon újra.</translation>
<translation id="5785756445106461925">Emellett az oldal azonban más forrásokat is tartalmaz, amelyek nem biztonságosak. Ezeket a forrásokat mások is megtekinthetik átvitel közben, és megváltoztatásukkal a támadók módosíthatják az oldal viselkedését.</translation>
@@ -442,6 +466,7 @@
<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="5843436854350372569">Megpróbálta elérni a(z) <ph name="DOMAIN" /> webhelyet, de a szerver gyenge kulccsal rendelkező tanúsítványt mutatott be. Előfordulhat, hogy támadó törte fel a privát kulcsot, és lehet, hogy a szerver nem a várt kiszolgáló (lehet, hogy Ön a támadóval áll kapcsolatban). <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Új mappa</translation>
<translation id="5869405914158311789">A webhely nem érhető el</translation>
@@ -451,6 +476,7 @@
<translation id="59107663811261420">A Google Payments ennél a kereskedőnél nem támogatja az ilyen típusú kártyákat. Kérjük, válasszon másik kártyát.</translation>
<translation id="59174027418879706">Engedélyezve</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="5966707198760109579">Hét</translation>
<translation id="5967867314010545767">Eltávolítás az előzmények közül</translation>
<translation id="5975083100439434680">Kicsinyítés</translation>
@@ -469,8 +495,11 @@
<translation id="614940544461990577">Próbálja ki a következőket:</translation>
<translation id="6151417162996330722">A szervertanúsítvány érvényességi ideje túl hosszú.</translation>
<translation id="6165508094623778733">További információ</translation>
+<translation id="6177128806592000436">Kapcsolata a webhellyel nem biztonságos</translation>
<translation id="6203231073485539293">Ellenőrizze az internetkapcsolatot</translation>
<translation id="6218753634732582820">Eltávolítja a címet a Chromiumból?</translation>
+<translation id="6251924700383757765">Adatvédelmi irányelvek</translation>
+<translation id="625755898061068298">Úgy döntött, hogy letiltja a biztonsági figyelmeztetéseket ezen a webhelyen.</translation>
<translation id="6259156558325130047">&amp;Ãtrendezés újra</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> könyvjelzők</translation>
<translation id="6264485186158353794">Vissza a biztonsághoz</translation>
@@ -479,6 +508,7 @@
<translation id="6305205051461490394">A(z) <ph name="URL" /> nem érhető el.</translation>
<translation id="6321917430147971392">Ellenőrizze a DNS-beállításokat</translation>
<translation id="6328639280570009161">Próbálkozzon a hálózati előrejelzések kikapcsolásával</translation>
+<translation id="6328786501058569169">A webhely megtévesztő</translation>
<translation id="6337534724793800597">Házirendek szűrése név szerint</translation>
<translation id="6342069812937806050">Éppen most</translation>
<translation id="6345221851280129312">ismeretlen méret</translation>
@@ -487,6 +517,8 @@
<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="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>
<translation id="6416403317709441254">Pillanatnyilag nem tudja felkeresni a(z) <ph name="SITE" /> webhelyet, mivel az olyan titkosított hitelesítési adatokat küldött, amelyeket a Chromium nem tud feldolgozni. A hálózati hibák és támadások rendszerint átmenetiek, ezért az említett oldal később valószínűleg már működni fog. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Nem lehet ellenőrizni, hogy a tanúsítványt visszavonták-e.</translation>
@@ -496,10 +528,12 @@
<translation id="6458467102616083041">A rendszer figyelmen kívül hagyja, mivel az alapértelmezett keresést házirend tiltja le.</translation>
<translation id="6462969404041126431">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványát visszavonhatták. Ennek oka lehet konfigurációs hiba, vagy hogy támadó térítette el az Ön kapcsolódását. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Törlés újra</translation>
<translation id="6534179046333460208">Fizikai web – javaslatok</translation>
<translation id="6550675742724504774">Beállítások</translation>
+<translation id="6556239504065605927">Biztonságos kapcsolat</translation>
<translation id="6563469144985748109">A kezelő még nem hagyta jóvá</translation>
<translation id="6593753688552673085">kevesebb, mint <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Titkosítási lehetőségek</translation>
@@ -508,7 +542,6 @@
<translation id="6644283850729428850">Ez a házirend már elavult.</translation>
<translation id="6652240803263749613">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványa a számítógép operációs rendszere szerint nem megbízható. Ennek oka lehet konfigurációs hiba, vagy hogy támadó térítette el az Ön kapcsolódását. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Mappa szerkesztése</translation>
-<translation id="6660210980321319655">Automatikusan jelentve ekkor: <ph name="CRASH_TIME" /></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>
@@ -520,6 +553,7 @@
<translation id="6753269504797312559">Házirend értéke</translation>
<translation id="6757797048963528358">Eszköze alvó üzemmódba váltott.</translation>
<translation id="6778737459546443941">A szülő még nem hagyta jóvá</translation>
+<translation id="6810899417690483278">Testreszabás-azonosító</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">A(z) <ph name="URL" /> helyen lévő weboldal pillanatnyilag nem érhető el. Lehetséges, hogy túlterhelt, vagy karbantartás miatt nem működik.</translation>
<translation id="6831043979455480757">Fordítás</translation>
@@ -540,6 +574,8 @@
<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>
+<translation id="7064851114919012435">Kapcsolatfelvételi adatok</translation>
+<translation id="7079718277001814089">A webhely rosszindulatú programokat tartalmaz</translation>
<translation id="7087282848513945231">Megye</translation>
<translation id="7088615885725309056">Régebbi</translation>
<translation id="7090678807593890770">Keresés a Google-on a következőre: <ph name="LINK" /></translation>
@@ -554,6 +590,7 @@
<translation id="7210863904660874423">A(z) <ph name="HOST_NAME" /> nem felel meg a biztonsági szabványoknak.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />További információ<ph name="END_LINK" /> erről a hibáról.</translation>
<translation id="7219179957768738017">A kapcsolat a következőt használja: <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">A megnyíló oldal rosszindulatú programot tartalmaz</translation>
<translation id="724975217298816891">Adja meg a(z) <ph name="CREDIT_CARD" /> kártya lejárati dátumát és CVC-kódját. Az ellenőrzést követően a böngésző megosztja kártyaadatait ezzel a webhellyel.</translation>
<translation id="725866823122871198">Nem hozható létre privát kapcsolat a következővel: <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, mert a számítógép dátum- és időbeállítása helytelen (<ph name="DATE_AND_TIME" />).</translation>
<translation id="7269802741830436641">Ez a weboldal átirányítási körbe került</translation>
@@ -609,6 +646,7 @@ Pszt! Az inkognitómód (<ph name="SHORTCUT_KEY" />) hasznos lehet a következő
<translation id="7658239707568436148">Mégse</translation>
<translation id="7667346355482952095">A visszaadott irányelvtoken üres vagy nem egyezik az aktuális tokennel</translation>
<translation id="7668654391829183341">Ismeretlen eszköz</translation>
+<translation id="7669271284792375604">A webhelyen lévő támadók megpróbálhatják csellel rávenni Önt olyan programok telepítésére, amelyek károsak a böngészési élmény szempontjából (például módosítják a kezdőlapot, vagy plusz hirdetéseket jelenítenek meg a felkeresett webhelyeken).</translation>
<translation id="7674629440242451245">Érdekli néhány remek új Chrome-funkció? Próbálja ki a fejlesztői csatornánkat a chrome.com/dev webhelyen.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Tovább a(z) <ph name="SITE" /> webhelyre (nem biztonságos)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">A webhely nem tölthető be a gyorsítótárból</translation>
@@ -624,14 +662,17 @@ Pszt! Az inkognitómód (<ph name="SHORTCUT_KEY" />) hasznos lehet a következő
<translation id="7800304661137206267">A kapcsolat a(z) <ph name="CIPHER" /> használatával lett kódolva, <ph name="MAC" /> algoritmussal az üzenethitelesítéshez és <ph name="KX" /> algoritmussal a kulcscserélő folyamathoz.</translation>
<translation id="780301667611848630">Köszönöm, nem</translation>
<translation id="7805768142964895445">Ãllapot</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Eltávolítja a javaslatot a Chrome-ból?</translation>
<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="7887683347370398519">Ellenőrizze a CVC-t, majd próbálja újra</translation>
<translation id="7912024687060120840">Ebben a mappában:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">A szerver tanúsítványa még nem érvényes.</translation>
<translation id="7942349550061667556">Piros</translation>
+<translation id="7947285636476623132">Ellenőrizze a lejárati évet, majd próbálja újra</translation>
<translation id="7951415247503192394">(32 bites)</translation>
<translation id="7956713633345437162">Mobil könyvjelzők</translation>
<translation id="7961015016161918242">Soha</translation>
@@ -639,7 +680,10 @@ Pszt! Az inkognitómód (<ph name="SHORTCUT_KEY" />) hasznos lehet a következő
<translation id="7983301409776629893">A(z) <ph name="ORIGINAL_LANGUAGE" /> nyelvű szövegeket mindig fordítsa <ph name="TARGET_LANGUAGE" /> nyelvre</translation>
<translation id="7995512525968007366">Nincs megadva</translation>
<translation id="8012647001091218357">Jelenleg nem tudjuk elérni szüleidet. Próbálkozz újra.</translation>
+<translation id="8025119109950072390">A webhely támadói megpróbálhatják csellel rávenni Önt, hogy például telepítsen egy veszélyes szoftvert, vagy felfedje személyes adatait (jelszavát, telefonszámát, hitelkártyaszámát stb.).</translation>
+<translation id="803030522067524905">A Google Biztonságos Böngészés funkciója nemrég adathalászatot észlelt a következő webhelyen: <ph name="SITE" />. Az adathalász webhelyek más webhelynek tettetik magukat, hogy félrevezessék Önt. <ph name="BEGIN_LEARN_MORE_LINK" />További információ.<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">Ez az oldal <ph name="SOURCE_LANGUAGE" /> nyelven van. Lefordítja <ph name="TARGET_LANGUAGE" /> nyelvre?</translation>
+<translation id="8041089156583427627">Visszajelzés küldése</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>
@@ -650,9 +694,11 @@ Pszt! Az inkognitómód (<ph name="SHORTCUT_KEY" />) hasznos lehet a következő
<translation id="8150722005171944719">A fájl (<ph name="URL" />) nem olvasható. Lehet, hogy eltávolították, áthelyezték, vagy a fájlengedélyek megakadályozzák a hozzáférést.</translation>
<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="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="8230421197304563332">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 adatait (például fotóit, jelszavait, üzeneteit és hitelkártyaadatait).</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="8249320324621329438">Utolsó lekérés:</translation>
<translation id="8261506727792406068">Törlés</translation>
@@ -661,11 +707,13 @@ Pszt! Az inkognitómód (<ph name="SHORTCUT_KEY" />) hasznos lehet a következő
<translation id="8294431847097064396">Forrás</translation>
<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="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>
<translation id="8380941800586852976">Veszélyes</translation>
<translation id="8382348898565613901">A közelmúltban megnyitott könyvjelzők helye</translation>
+<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="8428213095426709021">Beállítások</translation>
@@ -677,9 +725,9 @@ Pszt! Az inkognitómód (<ph name="SHORTCUT_KEY" />) hasznos lehet a következő
<translation id="8498891568109133222">A(z) <ph name="HOST_NAME" /> túl hosszú ideje nem válaszol.</translation>
<translation id="852346902619691059">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványa az eszköz operációs rendszere szerint nem megbízható. Ennek oka lehet konfigurációs hiba, vagy hogy támadó térítette el az Ön kapcsolódását. <ph name="BEGIN_LEARN_MORE_LINK" />További információ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">A Google Payments nem támogatja az ilyen típusú kártyákat. Kérjük, válasszon egy másikat.</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="8553075262323480129">A fordítás nem sikerült, mivel az oldal nyelvét nem lehet megállapítani.</translation>
<translation id="8559762987265718583">Nem hozható létre privát kapcsolat a következővel: <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, mert az eszköz dátum- és időbeállítása helytelen (<ph name="DATE_AND_TIME" />).</translation>
-<translation id="856992080682148">A webhely tanúsítványa 2017-ben vagy később jár le, a tanúsítványlánc pedig tartalmaz egy SHA-1 titkosítással aláírt tanúsítványt.</translation>
<translation id="8571890674111243710">Oldal fordítása erre a nyelvre: <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Ez a tanúsítvány nem határoz meg olyan mechanizmust, amely ellenőrizné, hogy visszavonták-e.</translation>
<translation id="8620436878122366504">A szüleid még nem hagyták jóvá</translation>
@@ -692,10 +740,12 @@ Pszt! Az inkognitómód (<ph name="SHORTCUT_KEY" />) hasznos lehet a következő
<translation id="8740359287975076522">A(z) <ph name="HOST_NAME" /> &lt;abbr id="dnsDefinition"&gt;DNS-címe&lt;/abbr&gt; nem található. A probléma diagnosztizálása folyamatban van.</translation>
<translation id="8790007591277257123">&amp;Törlés újra</translation>
<translation id="8798099450830957504">Alapértelmezett</translation>
+<translation id="8800988563907321413">A közeli javaslatok helye</translation>
<translation id="8804164990146287819">Adatvédelmi irányelvek</translation>
<translation id="8820817407110198400">Könyvjelzők</translation>
<translation id="8834246243508017242">Az Ismerősöket felhasználó Automatikus kitöltés engedélyezése…</translation>
<translation id="883848425547221593">Egyéb könyvjelzők</translation>
+<translation id="884264119367021077">Szállítási cím</translation>
<translation id="884923133447025588">Nem található visszavonási mechanizmus.</translation>
<translation id="885730110891505394">Megosztás a Google-lal</translation>
<translation id="8866481888320382733">Irányelv-beállítások előfeldolgozási hibája</translation>
@@ -713,18 +763,21 @@ Pszt! Az inkognitómód (<ph name="SHORTCUT_KEY" />) hasznos lehet a következő
<translation id="8971063699422889582">A szerver tanúsítványa lejárt.</translation>
<translation id="8987927404178983737">hónap</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">A felkeresni kívánt webhely ártalmas programokat tartalmaz</translation>
<translation id="9001074447101275817">A(z) <ph name="DOMAIN" /> proxy felhasználónevet és jelszót kér.</translation>
<translation id="901974403500617787">Rendszerszinten érvényes jelölőket csak a tulajdonos (<ph name="OWNER_EMAIL" />) állíthat be.</translation>
<translation id="9020542370529661692">Az oldalt lefordítottuk <ph name="TARGET_LANGUAGE" /> nyelvre.</translation>
+<translation id="9035022520814077154">Biztonsági hiba</translation>
<translation id="9038649477754266430">A várható kifejezés szolgáltatás használata az oldalak gyorsabb betöltése érdekében</translation>
<translation id="9039213469156557790">Emellett az oldal más forrásokat is tartalmaz, amelyek nem biztonságosak. Ezeket a forrásokat mások is megtekinthetik átvitel közben, és megváltoztatásukkal a támadók módosíthatják az oldal viselkedését.</translation>
+<translation id="9040185888511745258">A(z) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webhelyen lévő támadók megpróbálhatják rávenni Önt olyan programok telepítésére, amelyek károsak a böngészési élmény szempontjából (például módosítják a kezdőlapot vagy plusz hirdetéseket jelenítenek meg a felkeresett webhelyeken).</translation>
<translation id="9050666287014529139">Összetett jelszó</translation>
<translation id="9065203028668620118">Szerkesztés</translation>
<translation id="9068849894565669697">Szín kiválasztása</translation>
<translation id="9076283476770535406">Lehet, hogy felnőtt tartalommal rendelkezik</translation>
-<translation id="9092364396508701805">A(z) <ph name="HOST_NAME" /> oldal nem működik</translation>
<translation id="9103872766612412690">A(z) <ph name="SITE" /> webhely rendes esetben titkosítást alkalmaz az Ön adatainak védelme érdekében. Amikor a Chromium most csatlakozni próbált, a(z) <ph name="SITE" /> webhely szokatlan és helytelen hitelesítési adatokat küldött vissza.Ez olyankor fordulhat elő, amikor egy támadó megpróbálja magát kiadni a(z) <ph name="SITE" /> webhelynek, vagy valamilyen Wi-Fi-bejelentkezési képernyő megszakította a kapcsolatot. Adatai továbbra is biztonságban vannak, mivel a Chromium még azt megelőzően megszakította a kapcsolatot, hogy bármiféle adatcserére sor kerülhetett volna.</translation>
<translation id="9137013805542155359">Eredeti megjelenítése</translation>
+<translation id="9137248913990643158">Indítsa el a Chrome böngészőt és jelentkezzen be az alkalmazás használata előtt.</translation>
<translation id="9148507642005240123">&amp;Szerkesztés visszavonása</translation>
<translation id="9157595877708044936">Előkészítés...</translation>
<translation id="9170848237812810038">&amp;Visszavonás</translation>
@@ -737,7 +790,6 @@ Pszt! Az inkognitómód (<ph name="SHORTCUT_KEY" />) hasznos lehet a következő
<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="962701380617707048">A kártyaadatok frissítése érdekében adja meg a(z) <ph name="CREDIT_CARD" /> kártya lejárati dátumát és CVC-kódját</translation>
<translation id="969892804517981540">Hivatalos verzió</translation>
<translation id="988159990683914416">Fejlesztői változat</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 284d6a88073..f8bdd0fef6f 100644
--- a/chromium/components/strings/components_strings_id.xtb
+++ b/chromium/components/strings/components_strings_id.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Menyambungkan ulang ke Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Cetak...</translation>
<translation id="1181037720776840403">Hapus</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Otomatis laporkan<ph name="END_WHITEPAPER_LINK" /> detail kemungkinan insiden keamanan ke Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Berikutnya</translation>
<translation id="1201895884277373915">Lainnya dari situs ini</translation>
<translation id="1206967143813997005">Tanda tangan awal tidak valid</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Sian</translation>
<translation id="1629803312968146339">Ingin Chrome menyimpan kartu ini?</translation>
<translation id="1640180200866533862">Kebijakan pengguna</translation>
+<translation id="1640244768702815859">Coba <ph name="BEGIN_LINK" />buka beranda situs<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Konfigurasi cadangan tidak valid dan tidak dapat diimpor.</translation>
<translation id="1644574205037202324">Riwayat</translation>
<translation id="1645368109819982629">Protokol yang tidak didukung</translation>
<translation id="1676269943528358898"><ph name="SITE" /> biasanya menggunakan enkripsi untuk melindungi informasi Anda. Saat Google Chrome mencoba menyambung ke <ph name="SITE" /> kali ini, situs web mengembalikan kredensial yang salah dan tidak biasa. Hal ini dapat terjadi jika ada penyerang yang berpura-pura menjadi <ph name="SITE" />, atau layar masuk Wi-Fi mengganggu sambungan. Informasi Anda masih aman karena Google Chrome menghentikan sambungan sebelum terjadi pertukaran data apa pun.</translation>
+<translation id="168328519870909584">Saat ini penyerang yang berada di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin berusaha memasang program berbahaya di perangkat Anda yang dapat mencuri atau menghapus informasi (misalnya, foto, sandi, pesan, dan kartu kredit).</translation>
<translation id="168841957122794586">Sertifikat server berisi kunci kriptografis yang lemah.</translation>
-<translation id="1701955595840307032">Dapatkan konten yang disarankan</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Anda memerlukan izin dari <ph name="NAME" /> untuk mengunjungi situs ini</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Nomor laman</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Coba jalankan Diagnostik Jaringan Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Perbarui frasa sandi sinkronisasi Anda.</translation>
+<translation id="1787142507584202372">Tab yang terbuka muncul di sini</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google Penjelajahan Aman baru-baru ini <ph name="BEGIN_LINK" />mendeteksi perangkat lunak perusak<ph name="END_LINK" /> di <ph name="SITE" />. Situs web yang biasanya aman terkadang dapat terinfeksi perangkat lunak perusak. Konten berbahaya tersebut berasal dari <ph name="SUBRESOURCE_HOST" />, sebuah distributor perangkat lunak perusak ternama. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Permintaan atau parameter permintaan tidak valid</translation>
+<translation id="1834321415901700177">Situs ini berisi program yang berbahaya</translation>
<translation id="1838667051080421715">Anda sedang melihat sumber laman web.</translation>
<translation id="1871208020102129563">Proxy disetel untuk menggunakan server proxy tetap, bukan URL skrip .pac.</translation>
<translation id="1883255238294161206">Ciutkan daftar</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">Kode pos</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 saran}other{# saran}}</translation>
<translation id="2065985942032347596">Diperlukan Otentikasi</translation>
-<translation id="2079545284768500474">Batalkan</translation>
+<translation id="2079545284768500474">Urungkan</translation>
<translation id="20817612488360358">Setelan proxy sistem disetel untuk digunakan namun konfigurasi proxy eksplisit juga ditentukan.</translation>
<translation id="2086652334978798447">Untuk mendapatkan konten hasil personalisasi yang disarankan oleh Google, masuk ke Chrome.</translation>
<translation id="2089090684895656482">Lebih Sedikit</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Bookmark Seluler</translation>
<translation id="2148716181193084225">Hari ini</translation>
-<translation id="2149973817440762519">Edit Bookmark</translation>
<translation id="2154054054215849342">Sinkronisasi tidak tersedia untuk domain Anda</translation>
<translation id="2166049586286450108">Akses Penuh Admin</translation>
<translation id="2166378884831602661">Situs ini tidak dapat menyediakan sambungan aman</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Saat ini, Anda tidak dapat mengunjungi <ph name="SITE" /> karena situs web tersebut menggunakan HSTS. Kesalahan dan serangan jaringan biasanya bersifat sementara, jadi laman ini mungkin akan bekerja nanti. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Mengabaikan bookmark tidak valid di indeks <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Bookmark lain</translation>
+<translation id="2355395290879513365">Penyerang mungkin dapat melihat gambar yang sedang Anda lihat di situs dan mengelabui Anda dengan memodifikasinya.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Tampilkan kebijakan tanpa nilai yang disetel</translation>
<translation id="2396249848217231973">&amp;Urungkan penghapusan</translation>
<translation id="2455981314101692989">Laman web ini telah menonaktifkan pengisian otomatis untuk formulir ini.</translation>
+<translation id="2460160116472764928">Google Penjelajahan Aman baru-baru ini <ph name="BEGIN_LINK" />mendeteksi perangkat lunak perusak<ph name="END_LINK" /> di <ph name="SITE" />. Situs web yang biasanya aman terkadang dapat terinfeksi perangkat lunak perusak. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Isi</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Jalankan Diagnostik Jaringan<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL penelusuran tidak valid.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Yakin ingin menghapus laman ini dari riwayat?</translation>
<translation id="2677748264148917807">Keluar</translation>
<translation id="269990154133806163">Server menunjukkan sertifikat yang tidak diungkapkan secara publik menggunakan kebijakan Transparansi Sertifikat. Ini adalah persyaratan untuk beberapa sertifikat, guna memastikan bahwa sertifikat tersebut dapat dipercaya dan melindungi dari penyerang. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Daftar Bacaan</translation>
<translation id="2704283930420550640">Nilai tidak sesuai format.</translation>
<translation id="2704951214193499422">Saat ini Chromium tidak dapat mengonfirmasi kartu. Coba lagi nanti.</translation>
<translation id="2705137772291741111">Salinan tersimpan (dalam cache) situs ini tidak dapat dibaca.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Anda berusaha menjangkau <ph name="DOMAIN" />, tetapi sertifikat yang ditunjukkan oleh server telah dicabut oleh penerbitnya. Artinya, kredensial keamanan yang ditunjukkan server tidak dapat dipercaya. Anda mungkin sedang berkomunikasi dengan penyerang. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Minta izin</translation>
<translation id="2713444072780614174">Putih</translation>
+<translation id="2720342946869265578">Di Sekitar</translation>
<translation id="2721148159707890343">Permintaan berhasil</translation>
<translation id="2728127805433021124">Sertifikat server ditandai menggunakan algoritme yang lemah.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Jalankan Diagnostik Konektivitas<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Anda dapat menonaktifkan proxy apa pun yang dikonfigurasi untuk sambungan dari laman setelan.</translation>
<translation id="2955913368246107853">Tutup bilah cari</translation>
<translation id="2958431318199492670">Konfigurasi jaringan tidak mematuhi standar ONC. Bagian dari konfigurasi mungkin tidak diimpor.</translation>
+<translation id="29611076221683977">Saat ini penyerang yang berada pada <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin berusaha memasang program berbahaya di Mac yang dapat mencuri atau menghapus informasi Anda (misalnya, foto, sandi, pesan, dan kartu kredit).</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="2985306909656435243">Jika diaktifkan, Chromium akan menyimpan salinan kartu Anda di perangkat ini untuk pengisian formulir yang lebih cepat.</translation>
@@ -254,12 +263,11 @@
<translation id="3549761410225185768"><ph name="NUM_TABS_MORE" /> lainnya...</translation>
<translation id="3555561725129903880">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya berasal dari <ph name="DOMAIN2" />. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau ada penyerang yang mencegat sambungan Anda. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="3556433843310711081">Pengelola dapat membuka blokirnya untuk Anda</translation>
-<translation id="3566021033012934673">Sambungan Anda tidak pribadi</translation>
+<translation id="3566021033012934673">Koneksi Anda tidak pribadi</translation>
<translation id="3583757800736429874">&amp;Ulangi Pemindahan</translation>
<translation id="3586931643579894722">Sembunyikan detail</translation>
<translation id="3587482841069643663">Semua</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Server tidak mendukung ekstensi negosiasi ulang TLS.</translation>
<translation id="36224234498066874">Hapus Data Perambanan...</translation>
<translation id="362276910939193118">Tampilkan Riwayat Lengkap</translation>
<translation id="3623476034248543066">Tampilkan nilai</translation>
@@ -271,19 +279,20 @@
<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="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="3696411085566228381">tidak ada</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">Membuka...</translation>
+<translation id="370665806235115550">Memuat...</translation>
<translation id="3712624925041724820">Lisensi habis</translation>
<translation id="3714780639079136834">Aktifkan data seluler atau Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Periksa proxy, firewall, dan konfigurasi DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Jika Anda memahami risiko terhadap keamanan, Anda dapat <ph name="BEGIN_LINK" />mengunjungi situs tidak aman ini<ph name="END_LINK" /> sebelum program berbahaya tersebut dibuang.</translation>
<translation id="3739623965217189342">Tautan yang Anda salin</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="3788090790273268753">Sertifikat untuk situs ini berakhir masa berlakunya pada tahun 2016, dan rantai sertifikat tersebut berisi sertifikat yang ditandatangani menggunakan SHA-1.</translation>
<translation id="382518646247711829">Jika Anda menggunakan server proxy...</translation>
<translation id="3828924085048779000">Frasa sandi kosong tidak dibolehkan.</translation>
<translation id="3845539888601087042">Menampilkan riwayat dari perangkat yang Anda gunakan untuk masuk. <ph name="BEGIN_LINK" />Pelajari lebih lanjut<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Kunci "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Klien dan server tidak mendukung versi protokol SSL umum atau cipher suite.</translation>
<translation id="4079302484614802869">Konfigurasi proxy disetel untuk menggunakan URL skrip .pac, bukan server proxy yang tetap.</translation>
+<translation id="4098354747657067197">Situs yang akan dibuka berisi penipuan</translation>
<translation id="4103249731201008433">Nomor seri perangkat tidak valid</translation>
<translation id="4103763322291513355">Kunjungi &lt;strong&gt;chrome://policy&lt;/strong&gt; untuk melihat daftar URL yang masuk daftar hitam dan kebijakan lain yang diterapkan oleh administrator sistem Anda.</translation>
<translation id="4110615724604346410">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya berisi berbagai kesalahan. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau ada penyerang yang mencegat sambungan Anda. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{tidak ada}=1{1 aplikasi ($1)}=2{2 aplikasi ($1, $2)}other{# aplikasi ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Kerusakan</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Coba jalankan Diagnostik Jaringan<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Sambungan ke situs ini tidak sepenuhnya aman</translation>
<translation id="4250680216510889253">Tidak</translation>
<translation id="425582637250725228">Perubahan yang Anda lakukan mungkin tidak disimpan.</translation>
<translation id="4258748452823770588">Tanda tangan salah</translation>
<translation id="4269787794583293679">(Tidak ada nama pengguna)</translation>
+<translation id="4280429058323657511">, kedaluwarsa <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google Penjelajahan Aman baru-baru ini <ph name="BEGIN_LINK" />menemukan program berbahaya<ph name="END_LINK" /> di <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Saran induk</translation>
<translation id="4304224509867189079">Log In</translation>
<translation id="432290197980158659">Server menunjukkan sertifikat yang tidak sesuai dengan perkiraan yang ada. Perkiraan ini disertakan untuk situs web tertentu dengan keamanan tingkat tinggi untuk melindungi Anda. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Gagal menemukan artikel</translation>
+<translation id="4326324639298822553">Periksa tanggal kedaluwarsa dan coba lagi</translation>
<translation id="4331708818696583467">Tidak Aman</translation>
+<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="4381091992796011497">Nama Pengguna:</translation>
<translation id="4394049700291259645">Nonaktifkan</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Anda berusaha menjangkau <ph name="DOMAIN" />, tetapi server menunjukkan sertifikat yang tidak valid. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Tab dari perangkat lain</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Anda sedang melihat laman ekstensi.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Muat ulang kebijakan</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4744603770635761495">Jalur Yang Dapat Dijalankan</translation>
+<translation id="4750917950439032686">Informasi Anda (misalnya, sandi atau nomor kartu kredit) bersifat pribadi saat dikirimkan ke situs ini.</translation>
<translation id="4756388243121344051">&amp;Riwayat</translation>
+<translation id="4759118997339041434">Isiotomatis pembayaran dinonaktifkan</translation>
<translation id="4764776831041365478">Laman web di <ph name="URL" /> mungkin sedang tidak aktif untuk sementara atau dipindahkan secara permanen ke alamat web baru.</translation>
<translation id="4771973620359291008">Terjadi kesalahan yang tidak diketahui.</translation>
<translation id="4800132727771399293">Periksa tanggal masa berlaku habis dan CVC, lalu coba lagi</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Kesalahan jaringan</translation>
<translation id="4816492930507672669">Paskan dengan halaman</translation>
<translation id="4850886885716139402">Lihat</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{dan 1 laman web lainnya}other{dan # laman web lainnya}}</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="495170559598752135">Tindakan</translation>
<translation id="4958444002117714549">Luaskan daftar</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Mendownload</translation>
<translation id="5190835502935405962">Bilah Bookmark</translation>
<translation id="5199729219167945352">Eksperimen</translation>
-<translation id="5199841536747119669">Saran Anda ditampilkan di sini</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="5299298092464848405">Kebijakan kesalahan penguraian</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Pelaporan kondisi ngadat dinonaktifkan.</translation>
<translation id="5317780077021120954">Simpan</translation>
<translation id="5327248766486351172">Nama</translation>
+<translation id="5337705430875057403">Penyerang di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> dapat mengelabui Anda agar melakukan hal berbahaya seperti memasang perangkat lunak atau mengungkap informasi pribadi Anda (misalnya, sandi, nomor telepon, atau kartu kredit).</translation>
<translation id="5359637492792381994">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya tidak valid untuk saat ini. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau ada penyerang yang mencegat sambungan Anda. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Gagal menyimpan setelan kebijakan</translation>
+<translation id="5386426401304769735">Rantai sertifikat untuk situs ini berisi sertifikat yang ditandatangani menggunakan SHA-1.</translation>
<translation id="5421136146218899937">Hapus data penjelajahan...</translation>
<translation id="5430298929874300616">Buang bookmark</translation>
<translation id="5431657950005405462">File Anda tidak ditemukan</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Laman tersemat di <ph name="SITE" /> menyatakan:</translation>
<translation id="5556459405103347317">Muat ulang</translation>
<translation id="5565735124758917034">Aktif</translation>
+<translation id="5572851009514199876">Mulai dan login ke Chrome agar Chrome dapat memeriksa apakah Anda diizinkan untuk mengakses situs ini atau tidak.</translation>
+<translation id="5580958916614886209">Periksa bulan kedaluwarsa dan coba lagi</translation>
<translation id="560412284261940334">Pengelolaan tidak didukung</translation>
<translation id="5610142619324316209">Periksa sambungan</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> terlalu sering mengalihkan Anda.</translation>
<translation id="5622887735448669177">Ingin keluar dari situs ini?</translation>
<translation id="5629630648637658800">Gagal memuat setelan kebijakan</translation>
<translation id="5631439013527180824">Token pengelolaan perangkat tidak valid</translation>
+<translation id="5669703222995421982">Mendapatkan konten hasil personalisasi</translation>
+<translation id="5675650730144413517">Halaman ini tidak berfungsi</translation>
<translation id="5677928146339483299">Dicekal</translation>
<translation id="5694783966845939798">Anda berusaha menjangkau <ph name="DOMAIN" />, tetapi server menunjukkan sertifikat yang ditandatangani menggunakan algoritme tanda tangan yang lemah (seperti SHA-1). Artinya, kredensial keamanan yang ditunjukkan server mungkin telah dipalsukan, dan server tersebut mungkin bukan server yang diharapkan (Anda mungkin sedang berkomunikasi dengan penyerang). <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Identitas situs Web ini belum diverifikasi.</translation>
<translation id="5720705177508910913">Pengguna saat ini</translation>
-<translation id="572328651809341494">Tab baru-baru ini</translation>
<translation id="5732392974455271431">Orang tua dapat membuka blokirnya untukmu</translation>
<translation id="5784606427469807560">Terjadi masalah saat mengonfirmasi kartu. Periksa sambungan internet Anda dan coba lagi.</translation>
<translation id="5785756445106461925">Selain itu, laman ini berisi sumber daya lainnya yang tidak aman. Sumber daya ini dapat dilihat oleh orang lain saat transit dan dapat dimodifikasi oleh penyerang untuk mengubah tampilan perangkat.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Anda mencoba menjangkau <ph name="DOMAIN" />, tetapi server menunjukkan sertifikat yang berisi kunci lemah. Penyerang mungkin telah merusak kunci pribadi dan server tersebut mungkin bukan server yang diharapkan (Anda mungkin sedang berkomunikasi dengan penyerang). <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Folder Baru</translation>
<translation id="5869405914158311789">Situs ini tidak dapat dijangkau</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Kartu jenis ini tidak didukung oleh Google Payments untuk pedagang ini. Pilih kartu lain.</translation>
<translation id="59174027418879706">Diaktifkan</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="5966707198760109579">Minggu</translation>
<translation id="5967867314010545767">Hapus dari riwayat</translation>
<translation id="5975083100439434680">Perkecil</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Coba:</translation>
<translation id="6151417162996330722">Sertifikat server memiliki masa berlaku yang terlalu panjang.</translation>
<translation id="6165508094623778733">Pelajari lebih lanjut</translation>
+<translation id="6177128806592000436">Sambungan Anda ke situs ini tidak aman</translation>
<translation id="6203231073485539293">Periksa sambungan internet Anda</translation>
<translation id="6218753634732582820">Hapus alamat dari Chromium?</translation>
+<translation id="6251924700383757765">Kebijakan privasi</translation>
+<translation id="625755898061068298">Anda telah memilih untuk menonaktifkan peringatan keamanan untuk situs ini.</translation>
<translation id="6259156558325130047">&amp;Ulangi Pengaturan Ulang</translation>
<translation id="6263376278284652872">Bookmark <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Kembali ke keamanan</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> tidak dapat dijangkau.</translation>
<translation id="6321917430147971392">Periksa setelan DNS Anda</translation>
<translation id="6328639280570009161">Coba nonaktifkan prediksi jaringan</translation>
+<translation id="6328786501058569169">Situs ini bersifat menipu</translation>
<translation id="6337534724793800597">Filter kebijakan menurut nama</translation>
<translation id="6342069812937806050">Baru saja</translation>
<translation id="6345221851280129312">ukuran tak diketahui</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Saat ini, Anda tidak dapat mengunjungi <ph name="SITE" /> karena situs web ini mengirimkan kredensial acak yang tidak dapat diproses oleh Chromium. Kesalahan dan serangan jaringan biasanya bersifat sementara, jadi laman ini mungkin akan bekerja nanti. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Tidak dapat memeriksa apakah sertifikat telah ditarik.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Diabaikan karena penelusuran default dinonaktifkan oleh kebijakan.</translation>
<translation id="6462969404041126431">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya mungkin sudah dicabut. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau ada penyerang yang mencegat sambungan Anda. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Ulangi Penghapusan</translation>
<translation id="6534179046333460208">Saran Web Fisik</translation>
<translation id="6550675742724504774">Opsi</translation>
+<translation id="6556239504065605927">Sambungan aman</translation>
<translation id="6563469144985748109">Pengelola Anda belum menyetujuinya</translation>
<translation id="6593753688552673085">kurang dari <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opsi enkripsi</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Kebijakan ini telah usang.</translation>
<translation id="6652240803263749613">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya tidak dipercaya oleh sistem operasi komputer. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau ada penyerang yang mencegat sambungan Anda. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Edit Folder</translation>
-<translation id="6660210980321319655">Otomatis dilaporkan pada <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Hapus saran formulir dari Chromium?</translation>
<translation id="6685834062052613830">Keluar dan selesaikan penyiapan</translation>
<translation id="6710213216561001401">Sebelumnya</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Nilai kebijakan</translation>
<translation id="6757797048963528358">Perangkat Anda sedang dalam mode tidur.</translation>
<translation id="6778737459546443941">Orang tuamu belum menyetujuinya</translation>
+<translation id="6810899417690483278">ID Penyesuaian</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Saat ini laman web di <ph name="URL" /> tidak tersedia. Mungkin laman web sedang kelebihan muatan atau terganggu karena ada perawatan.</translation>
<translation id="6831043979455480757">Terjemahkan</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Info kontak</translation>
+<translation id="7079718277001814089">Situs ini berisi malware</translation>
<translation id="7087282848513945231">County</translation>
<translation id="7088615885725309056">Lawas</translation>
<translation id="7090678807593890770">Telusuri <ph name="LINK" /> di Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> tidak mematuhi standar keamanan.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Selengkapnya<ph name="END_LINK" /> tentang masalah ini.</translation>
<translation id="7219179957768738017">Koneksi menggunakan <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Situs yang akan dibuka berisi perangkat lunak perusak</translation>
<translation id="724975217298816891">Masukkan tanggal habis masa berlaku dan CVC untuk <ph name="CREDIT_CARD" /> guna memperbarui detail kartu. Setelah mengonfirmasi, detail kartu Anda akan dibagikan dengan situs ini.</translation>
<translation id="725866823122871198">Sambungan pribadi ke <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> tidak dapat dibuat karena tanggal dan waktu (<ph name="DATE_AND_TIME" />) komputer Anda tidak benar.</translation>
<translation id="7269802741830436641">Laman web ini memiliki simpul pengalihan</translation>
@@ -611,6 +648,7 @@ Ssst! <ph name="SHORTCUT_KEY" /> mode penyamaran mungkin berguna suatu saat nant
<translation id="7658239707568436148">Batal</translation>
<translation id="7667346355482952095">Token kebijakan yang dikembalikan kosong atau tidak cocok dengan token saat ini</translation>
<translation id="7668654391829183341">Perangkat tidak dikenal</translation>
+<translation id="7669271284792375604">Penyerang di situs ini mungkin berusaha mengelabui Anda agar memasang program yang dapat membahayakan pengalaman menjelajah Anda (misalnya dengan mengubah beranda Anda atau menayangkan iklan ekstra pada situs yang dikunjungi).</translation>
<translation id="7674629440242451245">Tertarik dengan fitur Chrome baru yang keren? Coba saluran dev kami di chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Lanjutkan ke <ph name="SITE" /> (tidak aman)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Situs ini tidak dapat dimuat dari cache</translation>
@@ -626,14 +664,17 @@ Ssst! <ph name="SHORTCUT_KEY" /> mode penyamaran mungkin berguna suatu saat nant
<translation id="7800304661137206267">Sambungan dienkripsi menggunakan <ph name="CIPHER" />, dengan <ph name="MAC" /> untuk autentikasi pesan dan <ph name="KX" /> sebagai mekanisme pertukaran kunci.</translation>
<translation id="780301667611848630">Lain kali</translation>
<translation id="7805768142964895445">Status</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Hapus sebagai saran dari Chrome?</translation>
<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="7887683347370398519">Periksa CVC dan coba lagi</translation>
<translation id="7912024687060120840">Dalam Folder:</translation>
<translation id="7935318582918952113">Penyaring DOM</translation>
<translation id="7938958445268990899">Sertifikat server belum valid.</translation>
<translation id="7942349550061667556">Merah</translation>
+<translation id="7947285636476623132">Periksa tahun kedaluwarsa dan coba lagi</translation>
<translation id="7951415247503192394">(32 bit)</translation>
<translation id="7956713633345437162">Bookmark seluler</translation>
<translation id="7961015016161918242">Tidak pernah</translation>
@@ -641,7 +682,10 @@ Ssst! <ph name="SHORTCUT_KEY" /> mode penyamaran mungkin berguna suatu saat nant
<translation id="7983301409776629893">Selalu terjemahkan <ph name="ORIGINAL_LANGUAGE" /> ke <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Tidak Ditentukan</translation>
<translation id="8012647001091218357">Orang tua Anda saat ini tidak dapat dihubungi. Coba lagi.</translation>
+<translation id="8025119109950072390">Penyerang di situs ini dapat mengelabui Anda agar melakukan hal yang berbahaya seperti memasang software atau mengungkap informasi pribadi Anda (misalnya sandi, nomor telepon, atau kartu kredit).</translation>
+<translation id="803030522067524905">Google Penjelajahan Aman baru-baru ini mendeteksi aktivitas phishing di <ph name="SITE" />. Situs phishing berpura-pura menjadi situs lain untuk menipu Anda. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Laman ini berbahasa <ph name="SOURCE_LANGUAGE" />. Terjemahkan ke <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Kirim Masukan</translation>
<translation id="8088680233425245692">Gagal melihat artikel.</translation>
<translation id="8089520772729574115">kurang dari 1 MB</translation>
<translation id="8091372947890762290">Aktivasi ditunda di server</translation>
@@ -652,9 +696,11 @@ Ssst! <ph name="SHORTCUT_KEY" /> mode penyamaran mungkin berguna suatu saat nant
<translation id="8150722005171944719">File pada <ph name="URL" /> tidak dapat dibaca. File mungkin telah dihapus, dipindahkan, atau izin file mungkin mencegah akses.</translation>
<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="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="8230421197304563332">Saat ini penyerang yang berada pada <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin berusaha memasang program berbahaya di komputer Anda yang dapat mencuri dan menghapus informasi Anda (misalnya, foto, sandi, pesan, dan kartu kredit).</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="8249320324621329438">Terakhir diambil:</translation>
<translation id="8261506727792406068">Hapus</translation>
@@ -663,11 +709,13 @@ Ssst! <ph name="SHORTCUT_KEY" /> mode penyamaran mungkin berguna suatu saat nant
<translation id="8294431847097064396">Sumber</translation>
<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="8349305172487531364">Bilah bookmark</translation>
<translation id="8363502534493474904">Nonaktifkan mode pesawat</translation>
<translation id="8364627913115013041">Tidak disetel.</translation>
<translation id="8380941800586852976">Berbahaya</translation>
<translation id="8382348898565613901">Bookmark yang baru-baru ini dikunjungi ditampilkan di sini</translation>
+<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="8428213095426709021">Setelan</translation>
@@ -679,9 +727,9 @@ Ssst! <ph name="SHORTCUT_KEY" /> mode penyamaran mungkin berguna suatu saat nant
<translation id="8498891568109133222"><ph name="HOST_NAME" /> membutuhkan terlalu banyak waktu untuk merespons.</translation>
<translation id="852346902619691059">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya tidak dipercaya oleh sistem operasi perangkat. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau ada penyerang yang mencegat sambungan Anda. <ph name="BEGIN_LEARN_MORE_LINK" />Pelajari lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Kartu jenis ini tidak didukung oleh Google Payments. Pilih kartu lain.</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="8553075262323480129">Terjemahan gagal karena bahasa laman tidak dapat ditentukan.</translation>
<translation id="8559762987265718583">Sambungan pribadi ke <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> tidak dapat dibuat karena tanggal dan waktu (<ph name="DATE_AND_TIME" />) perangkat tidak benar.</translation>
-<translation id="856992080682148">Sertifikat untuk situs ini berakhir masa berlakunya pada tahun 2017 atau lebih lama, dan rantai sertifikat tersebut berisi sertifikat yang ditandatangani menggunakan SHA-1.</translation>
<translation id="8571890674111243710">Menerjemahkan laman ke <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Sertifikat tidak menetapkan mekanisme untuk memeriksa apakah sertifikat telah ditarik.</translation>
<translation id="8620436878122366504">Orang tuamu belum menyetujuinya</translation>
@@ -694,10 +742,12 @@ Ssst! <ph name="SHORTCUT_KEY" /> mode penyamaran mungkin berguna suatu saat nant
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;Alamat DNS&lt;/abbr&gt; <ph name="HOST_NAME" /> tidak dapat ditemukan. Mendiagnosis masalah.</translation>
<translation id="8790007591277257123">&amp;Ulangi penghapusan</translation>
<translation id="8798099450830957504">Default</translation>
+<translation id="8800988563907321413">Saran terdekat muncul di sini</translation>
<translation id="8804164990146287819">Kebijakan Privasi</translation>
<translation id="8820817407110198400">Bookmark</translation>
<translation id="8834246243508017242">Aktifkan IsiOtomatis menggunakan Kontak...</translation>
<translation id="883848425547221593">Bookmark Lain</translation>
+<translation id="884264119367021077">Alamat pengiriman</translation>
<translation id="884923133447025588">Tidak ditemukan mekanisme pembatalan.</translation>
<translation id="885730110891505394">Berbagi dengan Google</translation>
<translation id="8866481888320382733">Kesalahan saat menguraikan setelan kebijakan</translation>
@@ -715,18 +765,21 @@ Ssst! <ph name="SHORTCUT_KEY" /> mode penyamaran mungkin berguna suatu saat nant
<translation id="8971063699422889582">Sertifikat server telah kedaluwarsa.</translation>
<translation id="8987927404178983737">Bulan</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Situs yang akan dibuka berisi program berbahaya</translation>
<translation id="9001074447101275817">Proxy <ph name="DOMAIN" /> memerlukan nama pengguna dan sandi.</translation>
<translation id="901974403500617787">Tanda yang berlaku bagi sistem secara luas hanya dapat disetel oleh pemilik: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Laman ini telah diterjemahkan ke <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Kesalahan keamanan</translation>
<translation id="9038649477754266430">Gunakan layanan prediksi agar laman dimuat dengan lebih cepat</translation>
<translation id="9039213469156557790">Selain itu, laman ini berisi sumber daya lainnya yang tidak aman. Sumber daya ini dapat dilihat oleh orang lain saat transit dan dapat dimodifikasi oleh penyerang untuk mengubah perilaku laman.</translation>
+<translation id="9040185888511745258">Penyerang di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin berusaha mengelabui Anda agar memasang program yang membahayakan pengalaman penjelajahan (misalnya, dengan mengubah beranda Anda atau menayangkan iklan ekstra pada situs yang Anda kunjungi).</translation>
<translation id="9050666287014529139">Frasa sandi</translation>
<translation id="9065203028668620118">Edit</translation>
<translation id="9068849894565669697">Pilih warna</translation>
<translation id="9076283476770535406">Situs mungkin berisi konten dewasa</translation>
-<translation id="9092364396508701805">Laman <ph name="HOST_NAME" /> tidak bekerja</translation>
<translation id="9103872766612412690"><ph name="SITE" /> biasanya menggunakan enkripsi untuk melindungi informasi Anda. Saat Chromium mencoba menyambung ke <ph name="SITE" /> kali ini, situs web mengembalikan kredensial yang salah dan tidak biasa. Hal ini dapat terjadi jika ada penyerang yang berpura-pura menjadi <ph name="SITE" />, atau layar masuk Wi-Fi mengganggu sambungan. Informasi Anda masih aman karena Chromium menghentikan sambungan sebelum terjadi pertukaran data apa pun.</translation>
<translation id="9137013805542155359">Perlihatkan laman asli</translation>
+<translation id="9137248913990643158">Mulai dan login ke Chrome sebelum menggunakan aplikasi ini.</translation>
<translation id="9148507642005240123">&amp;Urungkan pengeditan</translation>
<translation id="9157595877708044936">Menyiapkan...</translation>
<translation id="9170848237812810038">&amp;Urung</translation>
@@ -739,7 +792,6 @@ Ssst! <ph name="SHORTCUT_KEY" /> mode penyamaran mungkin berguna suatu saat nant
<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="962701380617707048">Masukkan tanggal habis masa berlaku dan CVC untuk <ph name="CREDIT_CARD" /> guna memperbarui detail kartu Anda</translation>
<translation id="969892804517981540">Pembuatan Resmi</translation>
<translation id="988159990683914416">Buatan Pengembang</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 569c9587d6b..590429413be 100644
--- a/chromium/components/strings/components_strings_it.xtb
+++ b/chromium/components/strings/components_strings_it.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Riconnessione alla rete Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Stampa...</translation>
<translation id="1181037720776840403">Rimuovi</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Segnala automaticamente<ph name="END_WHITEPAPER_LINK" /> a Google i dettagli dei possibili problemi di sicurezza. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Avanti</translation>
<translation id="1201895884277373915">Altri dal sito</translation>
<translation id="1206967143813997005">Firma iniziale non valida</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Ciano</translation>
<translation id="1629803312968146339">Vuoi che Chrome salvi questa carta?</translation>
<translation id="1640180200866533862">Criteri utente</translation>
+<translation id="1640244768702815859">Prova a <ph name="BEGIN_LINK" />visitare la home page del sito<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">La configurazione di rete non è valida e non può essere importata.</translation>
<translation id="1644574205037202324">Cronologia</translation>
<translation id="1645368109819982629">Protocollo non supportato</translation>
<translation id="1676269943528358898"><ph name="SITE" /> in genere utilizza la crittografia per proteggere le tue informazioni. Questa volta, quando Google Chrome ha provato a connettersi a <ph name="SITE" />, il sito web ha restituito credenziali insolite e sbagliate. È possibile che un malintenzionato stia cercando di spacciarsi per il sito <ph name="SITE" /> oppure che una schermata di accesso alla rete Wi-Fi abbia interrotto la connessione. Le tue informazioni sono ancora al sicuro perché Google Chrome ha interrotto la connessione prima che avvenissero scambi di dati.</translation>
+<translation id="168328519870909584">I malintenzionati attualmente sul sito <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> potrebbero tentare di installare sul tuo dispositivo app pericolose che scoprono o eliminano i tuoi dati (ad esempio foto, password, messaggi e carte di credito).</translation>
<translation id="168841957122794586">Il certificato del server contiene una chiave crittografica debole.</translation>
-<translation id="1701955595840307032">Ricevi contenuti suggeriti</translation>
<translation id="1710259589646384581">Sistema operativo</translation>
<translation id="1721312023322545264">Ti occorre l'autorizzazione di <ph name="NAME" /> per poter visitare il sito</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Numero di pagina</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Prova a eseguire lo strumento Diagnostica di rete Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Aggiorna la tua passphrase di sincronizzazione.</translation>
+<translation id="1787142507584202372">Le tue schede aperte vengono visualizzate qui</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">La funzione Navigazione sicura di Google di recente ha <ph name="BEGIN_LINK" />rilevato malware<ph name="END_LINK" /> 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. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Richiesta o parametri della richiesta non validi</translation>
+<translation id="1834321415901700177">Il sito contiene programmi dannosi.</translation>
<translation id="1838667051080421715">È visualizzata la sorgente di una pagina web.</translation>
<translation id="1871208020102129563">L'impostazione del proxy prevede l'utilizzo di server proxy fissi, non di un URL script .pac.</translation>
<translation id="1883255238294161206">Comprimi elenco</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Preferiti su disp. mobili</translation>
<translation id="2148716181193084225">Oggi</translation>
-<translation id="2149973817440762519">Modifica Preferito</translation>
<translation id="2154054054215849342">Il servizio di sincronizzazione non è disponibile per il tuo dominio</translation>
<translation id="2166049586286450108">Accesso amministrativo completo</translation>
<translation id="2166378884831602661">Il sito non può fornire una connessione protetta</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Al momento non puoi visitare il sito <ph name="SITE" /> perché utilizza HSTS. In genere gli errori di rete e gli attacchi sono temporanei, pertanto questa pagina potrebbe funzionare più tardi. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Preferito non valido dell'indice <ph name="ENTRY_INDEX" /> ignorato</translation>
<translation id="2354001756790975382">Altri Preferiti</translation>
+<translation id="2355395290879513365">Gli utenti malintenzionati potrebbero riuscire a vedere le immagini che visualizzi in questo sito e ingannarti modificandole.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Mostra norme senza valori</translation>
<translation id="2396249848217231973">&amp;Annulla eliminazione</translation>
<translation id="2455981314101692989">La pagina web ha disattivato la compilazione automatica per questo modulo.</translation>
+<translation id="2460160116472764928">La funzione Navigazione sicura di Google di recente ha <ph name="BEGIN_LINK" />rilevato malware<ph name="END_LINK" /> sul sito <ph name="SITE" />. I siti web che in genere sono sicuri a volte vengono infettati da malware. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Compila</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Eseguire lo strumento Diagnostica di rete<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL ricerca non valido.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Eliminare le pagine dalla cronologia?</translation>
<translation id="2677748264148917807">Esci</translation>
<translation id="269990154133806163">Il server ha presentato un certificato che non è stato reso pubblico tramite le norme di Certificate Transparency, la cui applicazione costituisce un requisito di alcuni certificati al fine di garantire la loro attendibilità nonché la sicurezza nei confronti dei malintenzionati. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Elenco di lettura</translation>
<translation id="2704283930420550640">Il valore non corrisponde al formato.</translation>
<translation id="2704951214193499422">Al momento non è possibile confermare la carta in Chromium. Riprova più tardi.</translation>
<translation id="2705137772291741111">La copia del sito salvata (nella cache) era illeggibile.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Hai tentato di accedere a <ph name="DOMAIN" />, tuttavia 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 malintenzionato. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Chiedi autorizzazione</translation>
<translation id="2713444072780614174">Bianco</translation>
+<translation id="2720342946869265578">Qui vicino</translation>
<translation id="2721148159707890343">Richiesta riuscita</translation>
<translation id="2728127805433021124">Il certificato del server è stato firmato utilizzando un algoritmo di firma debole.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Eseguire lo strumento Diagnostica della connettività<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Puoi disattivare tutti i proxy configurati per una connessione dalla pagina delle impostazioni.</translation>
<translation id="2955913368246107853">Chiudi la barra di ricerca</translation>
<translation id="2958431318199492670">La configurazione di rete non è conforme allo standard ONC. Parti della configurazione potrebbero non essere importate.</translation>
+<translation id="29611076221683977">I malintenzionati attualmente sul sito <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> potrebbero tentare di installare sul tuo Mac programmi pericolosi che scoprono o eliminano i tuoi dati (ad esempio foto, password, messaggi e carte di credito).</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="2985306909656435243">Se questa opzione viene attivata, Chromium memorizza una copia della carta sul dispositivo per velocizzare la compilazione dei moduli.</translation>
@@ -256,7 +265,6 @@
<translation id="3586931643579894722">Nascondi dettagli</translation>
<translation id="3587482841069643663">Tutti</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Il server non supporta l'estensione di rinegoziazione TLS.</translation>
<translation id="36224234498066874">Cancella dati di navigazione...</translation>
<translation id="362276910939193118">Mostra cronologia completa</translation>
<translation id="3623476034248543066">Mostra valore</translation>
@@ -268,6 +276,7 @@
<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="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>
@@ -277,10 +286,10 @@
<translation id="3712624925041724820">Licenze esaurite</translation>
<translation id="3714780639079136834">Attivare la rete dati mobile o Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Controllare la configurazione del proxy, firewall e DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Se sei consapevole dei rischi per la tua sicurezza, potresti <ph name="BEGIN_LINK" />visitare questo sito non sicuro<ph name="END_LINK" /> senza aspettare che vengano rimossi i programmi pericolosi.</translation>
<translation id="3739623965217189342">Link che hai copiato</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="3788090790273268753">Il certificato di questo sito scade nel 2016 e la catena di certificati contiene un certificato che è stato firmato utilizzando SHA-1.</translation>
<translation id="382518646247711829">Se utilizzi un server proxy...</translation>
<translation id="3828924085048779000">Non è consentita una passphrase vuota.</translation>
<translation id="3845539888601087042">È visualizzata la cronologia dei dispositivi su cui hai eseguito l'accesso. <ph name="BEGIN_LINK" />Ulteriori informazioni<ph name="END_LINK" /></translation>
@@ -302,6 +311,7 @@
<translation id="4058922952496707368">Chiave "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Il client e il server non supportano un pacchetto di crittografia o una versione del protocollo SSL comuni.</translation>
<translation id="4079302484614802869">L'impostazione della configurazione proxy prevede l'utilizzo di un URL script .pac, non di server proxy fissi.</translation>
+<translation id="4098354747657067197">Sito ingannevole in vista</translation>
<translation id="4103249731201008433">Il numero di serie del dispositivo non è valido</translation>
<translation id="4103763322291513355">Visita &lt;strong&gt;chrome://policy&lt;/strong&gt; per visualizzare l'elenco di URL inseriti nella blacklist e altre norme applicate dall'amministratore di sistema.</translation>
<translation id="4110615724604346410">Questo server non è riuscito a verificare che si tratti 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. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -319,15 +329,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{nessuna}=1{1 app ($ 1)}=2{2 app ($ 1, $ 2)}other{# app ($ 1, $ 2, $ 3)}}</translation>
<translation id="4220128509585149162">Arresti anomali</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Prova a eseguire lo strumento Diagnostica di rete<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">La connessione a questo sito non è completamente protetta</translation>
<translation id="4250680216510889253">No</translation>
<translation id="425582637250725228">Le modifiche apportate potrebbero non essere salvate.</translation>
<translation id="4258748452823770588">Firma errata</translation>
<translation id="4269787794583293679">(Nessun nome utente)</translation>
+<translation id="4280429058323657511">, scad.: <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">La funzione Navigazione sicura di Google di recente ha <ph name="BEGIN_LINK" />rilevato programmi dannosi<ph name="END_LINK" /> sul sito <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Suggerimenti per i genitori</translation>
<translation id="4304224509867189079">Accedi</translation>
<translation id="432290197980158659">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. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Impossibile trovare l'articolo</translation>
+<translation id="4326324639298822553">Controlla la data di scadenza e riprova</translation>
<translation id="4331708818696583467">Non sicuro</translation>
+<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="4381091992796011497">Nome utente:</translation>
<translation id="4394049700291259645">Disabilita</translation>
@@ -345,6 +360,7 @@
<translation id="4589078953350245614">Hai tentato di accedere a <ph name="DOMAIN" />, tuttavia il server ha presentato un certificato non valido. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Schede di altri dispositivi</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">È visualizzata la pagina di un'estensione.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -353,10 +369,13 @@
<translation id="4726672564094551039">Ricarica norme</translation>
<translation id="4728558894243024398">Piattaforma</translation>
<translation id="4744603770635761495">Percorso eseguibile</translation>
+<translation id="4750917950439032686">Le tue informazioni (ad esempio password o numeri di carte di credito) restano private quando vengono inviate a questo sito.</translation>
<translation id="4756388243121344051">&amp;Cronologia</translation>
+<translation id="4759118997339041434">Compilazione automatica del pagamento disabilitata</translation>
<translation id="4764776831041365478">La pagina web all'indirizzo <ph name="URL" /> potrebbe essere temporaneamente non disponibile oppure è stata permanentemente spostata a un nuovo indirizzo web.</translation>
<translation id="4771973620359291008">Si è verificato un errore sconosciuto.</translation>
<translation id="4800132727771399293">Controlla la data di scadenza e il codice CVC, poi riprova</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Errore di rete</translation>
<translation id="4816492930507672669">Adatta alla pagina</translation>
<translation id="4850886885716139402">Visualizza</translation>
@@ -365,6 +384,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{e un'altra pagina web}other{e altre # pagine web}}</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="495170559598752135">Azioni</translation>
<translation id="4958444002117714549">Espandi elenco</translation>
@@ -392,7 +412,6 @@
<translation id="5181140330217080051">Download in corso</translation>
<translation id="5190835502935405962">Barra dei Preferiti</translation>
<translation id="5199729219167945352">Esperimenti</translation>
-<translation id="5199841536747119669">I suggerimenti vengono visualizzati qui</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="5299298092464848405">Errore durante l'analisi del criterio</translation>
@@ -400,8 +419,10 @@
<translation id="5308689395849655368">La segnalazione degli arresti anomali è disattivata.</translation>
<translation id="5317780077021120954">Salva</translation>
<translation id="5327248766486351172">Nome</translation>
+<translation id="5337705430875057403">I malintenzionati sul sito <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> potrebbero indurti con l'inganno a effettuare operazioni pericolose, come installare software o fornire i tuoi dati personali (ad esempio password, numeri di telefono o carte di credito).</translation>
<translation id="5359637492792381994">Questo server non è riuscito a verificare che si tratti di <ph name="DOMAIN" />; il relativo certificato di sicurezza al momento non è valido. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Archiviazione delle impostazioni criterio non riuscita</translation>
+<translation id="5386426401304769735">Il certificato di questo sito contiene un certificato che è stato firmato utilizzando SHA-1.</translation>
<translation id="5421136146218899937">Cancella dati di navigazione...</translation>
<translation id="5430298929874300616">Rimuovi preferito</translation>
<translation id="5431657950005405462">Il file non è stato trovato</translation>
@@ -422,17 +443,20 @@
<translation id="5544037170328430102">Una pagina incorporata in <ph name="SITE" /> dice:</translation>
<translation id="5556459405103347317">Ricarica</translation>
<translation id="5565735124758917034">Attivo</translation>
+<translation id="5572851009514199876">Accedi a Chrome per consentire al browser di verificare che tu sia autorizzato ad accedere a questo sito.</translation>
+<translation id="5580958916614886209">Controlla il mese di scadenza e riprova</translation>
<translation id="560412284261940334">Gestione non supportata</translation>
<translation id="5610142619324316209">Verificare la connessione</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> ti ha reindirizzato troppe volte.</translation>
<translation id="5622887735448669177">Vuoi uscire da questo sito?</translation>
<translation id="5629630648637658800">Caricamento delle impostazioni criterio non riuscito</translation>
<translation id="5631439013527180824">Token di gestione del dispositivo non valido</translation>
+<translation id="5669703222995421982">Ricevi contenuti suggeriti appositamente per te</translation>
+<translation id="5675650730144413517">La pagina non funziona</translation>
<translation id="5677928146339483299">Bloccati</translation>
<translation id="5694783966845939798">Hai tentato di accedere a <ph name="DOMAIN" />, tuttavia il server ha presentato un certificato firmato utilizzando un algoritmo di firma debole (ad esempio, SHA-1). Ciò significa che le credenziali di sicurezza presentate dal server potrebbero essere state falsificate e il server potrebbe non essere quello previsto (è possibile che tu stia comunicando con un malintenzionato). <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">L'identità di questo sito web non è stata verificata.</translation>
<translation id="5720705177508910913">Utente corrente</translation>
-<translation id="572328651809341494">Schede recenti</translation>
<translation id="5732392974455271431">I tuoi genitori possono sbloccarlo per te</translation>
<translation id="5784606427469807560">Si è verificato un problema durante la conferma della carta. Controlla la connessione Internet e riprova.</translation>
<translation id="5785756445106461925">Inoltre, questa pagina include altre risorse che non sono sicure. Tali risorse possono essere visualizzate da altri durante il transito dei dati e possono essere modificate da un utente malintenzionato al fine di modificare l'aspetto della pagina.</translation>
@@ -441,6 +465,7 @@
<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="5843436854350372569">Hai tentato di accedere a <ph name="DOMAIN" />, tuttavia il server ha presentato un certificato contenente una chiave debole. Un malintenzionato potrebbe avere decifrato la chiave privata e il server potrebbe non essere quello previsto (è possibile che tu stia comunicando con un malintenzionato). <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Nuova cartella</translation>
<translation id="5869405914158311789">Impossibile raggiungere il sito</translation>
@@ -450,6 +475,7 @@
<translation id="59107663811261420">Questo tipo di carta non è supportato in Google Payments per questo commerciante. Seleziona un'altra carta.</translation>
<translation id="59174027418879706">Attiva</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="5966707198760109579">Settimana</translation>
<translation id="5967867314010545767">Rimuovi da cronologia</translation>
<translation id="5975083100439434680">Diminuisci lo zoom</translation>
@@ -467,8 +493,11 @@
<translation id="614940544461990577">Prova a:</translation>
<translation id="6151417162996330722">Il certificato del server ha un periodo di validità troppo lungo.</translation>
<translation id="6165508094623778733">Ulteriori informazioni</translation>
+<translation id="6177128806592000436">La tua connessione a questo sito non è protetta</translation>
<translation id="6203231073485539293">Controlla la connessione a Internet</translation>
<translation id="6218753634732582820">Rimuovere l'indirizzo da Chromium?</translation>
+<translation id="6251924700383757765">Norme sulla privacy</translation>
+<translation id="625755898061068298">Hai scelto di disattivare gli avvisi di sicurezza per questo sito.</translation>
<translation id="6259156558325130047">&amp;Ripeti ridisposizione</translation>
<translation id="6263376278284652872">Segnalibri di <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Torna nell'area protetta</translation>
@@ -477,6 +506,7 @@
<translation id="6305205051461490394"><ph name="URL" /> non è raggiungibile.</translation>
<translation id="6321917430147971392">Controlla le impostazioni DNS</translation>
<translation id="6328639280570009161">Prova a disattivare la previsione della rete</translation>
+<translation id="6328786501058569169">Questo sito è ingannevole</translation>
<translation id="6337534724793800597">Filtra i criteri per nome</translation>
<translation id="6342069812937806050">In questo momento</translation>
<translation id="6345221851280129312">dimensioni sconosciute</translation>
@@ -485,6 +515,8 @@
<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="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>
<translation id="6416403317709441254">Al momento non puoi visitare il sito <ph name="SITE" /> perché ha inviato credenziali criptate che Chromium non è in grado di elaborare. In genere gli errori di rete e gli attacchi sono temporanei, pertanto questa pagina potrebbe funzionare più tardi. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Impossibile controllare se il certificato è stato revocato.</translation>
@@ -494,10 +526,12 @@
<translation id="6458467102616083041">Ignorato perché la ricerca predefinita è stata disattivata secondo la norma.</translation>
<translation id="6462969404041126431">Questo server non è riuscito a verificare che si tratti di <ph name="DOMAIN" />; il relativo certificato di sicurezza potrebbe essere stato revocato. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Ripeti eliminazione</translation>
<translation id="6534179046333460208">Suggerimenti relativi al Physical Web</translation>
<translation id="6550675742724504774">Opzioni</translation>
+<translation id="6556239504065605927">Connessione protetta</translation>
<translation id="6563469144985748109">Il tuo gestore non ha ancora approvato la richiesta</translation>
<translation id="6593753688552673085">meno di <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opzioni di crittografia</translation>
@@ -506,7 +540,6 @@
<translation id="6644283850729428850">Questa norma è obsoleta.</translation>
<translation id="6652240803263749613">Questo server non è riuscito a verificare che si tratti di <ph name="DOMAIN" />; il relativo certificato di sicurezza non è considerato attendibile dal sistema operativo del computer. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Modifica cartella</translation>
-<translation id="6660210980321319655">Data/ora arresto anomalo: <ph name="CRASH_TIME" /></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>
@@ -518,6 +551,7 @@
<translation id="6753269504797312559">Valore norma</translation>
<translation id="6757797048963528358">Il dispositivo è entrato in modalità sospensione.</translation>
<translation id="6778737459546443941">Il tuo genitore non ha ancora approvato la richiesta</translation>
+<translation id="6810899417690483278">ID personalizzazione</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">La pagina web all'indirizzo <ph name="URL" /> non è al momento disponibile. Potrebbe essere sovraccarica o non disponibile per manutenzione.</translation>
<translation id="6831043979455480757">Traduci</translation>
@@ -538,6 +572,8 @@
<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>
+<translation id="7064851114919012435">Informazioni di contatto</translation>
+<translation id="7079718277001814089">Questo sito contiene malware</translation>
<translation id="7087282848513945231">Contea</translation>
<translation id="7088615885725309056">Meno recente</translation>
<translation id="7090678807593890770">Cerca <ph name="LINK" /> con Google</translation>
@@ -552,6 +588,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> non è conforme agli standard di sicurezza.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Ulteriori informazioni<ph name="END_LINK" /> su questo problema.</translation>
<translation id="7219179957768738017">La connessione utilizza <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Il sito che stai per visitare contiene malware</translation>
<translation id="724975217298816891">Inserisci la data di scadenza e il codice CVC della carta <ph name="CREDIT_CARD" /> per aggiornare i relativi dettagli. Dopo essere stati confermati, i dettagli della carta saranno condivisi con questo sito.</translation>
<translation id="725866823122871198">Impossibile stabilire una connessione privata con il sito <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> perché data e ora del computer (<ph name="DATE_AND_TIME" />) sono sbagliate.</translation>
<translation id="7269802741830436641">La pagina web ha generato un loop di reindirizzamento</translation>
@@ -607,6 +644,7 @@ Psst! La prossima volta potrebbe esserti utile la modalità di navigazione in in
<translation id="7658239707568436148">Annulla</translation>
<translation id="7667346355482952095">Il token della norma restituito è vuoto o non corrisponde al token corrente</translation>
<translation id="7668654391829183341">Dispositivo sconosciuto</translation>
+<translation id="7669271284792375604">I malintenzionati su questo sito potrebbero cercare di indurti con l'inganno a installare programmi che danneggiano la tua navigazione (ad esempio cambiando la tua pagina iniziale o mostrando annunci extra sui siti che visiti).</translation>
<translation id="7674629440242451245">Ti interessano le nuove e straordinarie funzioni di Chrome? Prova il nostro canale Dev all'indirizzo chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Procedi su <ph name="SITE" /> (non sicuro)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Impossibile caricare il sito dalla cache</translation>
@@ -622,14 +660,17 @@ Psst! La prossima volta potrebbe esserti utile la modalità di navigazione in in
<translation id="7800304661137206267">La connessione è stata criptata utilizzando <ph name="CIPHER" />, con <ph name="MAC" /> per l'autenticazione dei messaggi e <ph name="KX" /> come meccanismo principale di scambio delle chiavi.</translation>
<translation id="780301667611848630">No grazie</translation>
<translation id="7805768142964895445">Stato</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Rimuovere il suggerimento per i moduli da Chrome?</translation>
<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="7887683347370398519">Controlla il tuo codice CVC e riprova</translation>
<translation id="7912024687060120840">Nella cartella:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Il certificato del server non è ancora valido.</translation>
<translation id="7942349550061667556">Rosso</translation>
+<translation id="7947285636476623132">Controlla l'anno di scadenza e riprova</translation>
<translation id="7951415247503192394">(a 32 bit)</translation>
<translation id="7956713633345437162">Preferiti su disp. mobili</translation>
<translation id="7961015016161918242">Mai</translation>
@@ -637,7 +678,10 @@ Psst! La prossima volta potrebbe esserti utile la modalità di navigazione in in
<translation id="7983301409776629893">Traduci sempre <ph name="ORIGINAL_LANGUAGE" /> in <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Non specificato</translation>
<translation id="8012647001091218357">In questo momento, non è possibile raggiungere i tuoi genitori. Riprova.</translation>
+<translation id="8025119109950072390">I malintenzionati su questo sito potrebbero indurti con l'inganno a effettuare operazioni pericolose, come installare software o fornire i tuoi dati personali (ad esempio password, numeri di telefono o carte di credito).</translation>
+<translation id="803030522067524905">La funzione Navigazione sicura di Google di recente ha rilevato phishing sul sito <ph name="SITE" />. I siti di phishing fingono di essere altri siti web per ingannarti. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Questa pagina è in <ph name="SOURCE_LANGUAGE" />. Tradurla in <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Invia feedback</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>
@@ -648,9 +692,11 @@ Psst! La prossima volta potrebbe esserti utile la modalità di navigazione in in
<translation id="8150722005171944719">Il file all'indirizzo <ph name="URL" /> non è leggibile. Potrebbe essere stato rimosso, spostato oppure delle autorizzazioni del file potrebbero impedire l'accesso.</translation>
<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="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="8230421197304563332">I malintenzionati attualmente sul sito <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 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="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="8249320324621329438">Ultimo recupero:</translation>
<translation id="8261506727792406068">Elimina</translation>
@@ -659,11 +705,13 @@ Psst! La prossima volta potrebbe esserti utile la modalità di navigazione in in
<translation id="8294431847097064396">Origine</translation>
<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="8349305172487531364">Barra dei Preferiti</translation>
<translation id="8363502534493474904">Disattivare la modalità aereo</translation>
<translation id="8364627913115013041">Non impostata.</translation>
<translation id="8380941800586852976">Pericolosa</translation>
<translation id="8382348898565613901">I preferiti visitati di recente vengono visualizzati qui</translation>
+<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="8428213095426709021">Impostazioni</translation>
@@ -675,9 +723,9 @@ Psst! La prossima volta potrebbe esserti utile la modalità di navigazione in in
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ha impiegato troppo tempo a rispondere.</translation>
<translation id="852346902619691059">Questo server non è riuscito a verificare che si tratti 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. <ph name="BEGIN_LEARN_MORE_LINK" />Ulteriori informazioni<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Questo tipo di carta non è supportato in Google Payments. Seleziona un'altra carta.</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="8553075262323480129">La traduzione non è riuscita perché non è stato possibile determinare la lingua della pagina.</translation>
<translation id="8559762987265718583">Impossibile stabilire una connessione privata con il sito <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> perché data e ora del dispositivo (<ph name="DATE_AND_TIME" />) sono sbagliate.</translation>
-<translation id="856992080682148">Il certificato di questo sito scade nel 2017 o oltre e la catena di certificati contiene un certificato che è stato firmato utilizzando SHA-1.</translation>
<translation id="8571890674111243710">Traduzione della pagina in <ph name="LANGUAGE" /> in corso...</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>
@@ -690,10 +738,12 @@ Psst! La prossima volta potrebbe esserti utile la modalità di navigazione in in
<translation id="8740359287975076522">Impossibile trovare l'&lt;abbr id="dnsDefinition"&gt;indirizzo DNS&lt;/abbr&gt; di <ph name="HOST_NAME" />. Stiamo analizzando il problema.</translation>
<translation id="8790007591277257123">&amp;Ripeti eliminazione</translation>
<translation id="8798099450830957504">Predefinito</translation>
+<translation id="8800988563907321413">I suggerimenti Qui vicino vengono visualizzati qui</translation>
<translation id="8804164990146287819">Norme sulla privacy</translation>
<translation id="8820817407110198400">Preferiti</translation>
<translation id="8834246243508017242">Attiva Compilazione automatica utilizzando Contatti…</translation>
<translation id="883848425547221593">Altri Preferiti</translation>
+<translation id="884264119367021077">Indirizzo di spedizione</translation>
<translation id="884923133447025588">Nessun sistema di revoca trovato.</translation>
<translation id="885730110891505394">Condivisione con Google</translation>
<translation id="8866481888320382733">Errore durante l'analisi delle impostazioni criterio</translation>
@@ -711,18 +761,21 @@ Psst! La prossima volta potrebbe esserti utile la modalità di navigazione in in
<translation id="8971063699422889582">Il certificato del server è scaduto.</translation>
<translation id="8987927404178983737">Mese</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Il sito che stai per visitare contiene programmi dannosi</translation>
<translation id="9001074447101275817">Il proxy <ph name="DOMAIN" /> richiede un nome utente e una password.</translation>
<translation id="901974403500617787">I contrassegni che si applicano a livello di sistema possono essere impostati solo dal proprietario: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Questa pagina è stata tradotta in <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Errore di sicurezza</translation>
<translation id="9038649477754266430">Utilizza un servizio di previsione per velocizzare il caricamento delle pagine</translation>
<translation id="9039213469156557790">Inoltre, questa pagina include altre risorse che non sono sicure. Tali risorse possono essere visualizzate da altri durante il transito dei dati e possono essere modificate da un utente malintenzionato al fine di modificare il comportamento della pagina.</translation>
+<translation id="9040185888511745258">I malintenzionati su <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> potrebbero cercare di indurti con l'inganno a installare programmi che danneggiano la tua navigazione (ad esempio cambiando la tua pagina iniziale o mostrando annunci extra sui siti che visiti).</translation>
<translation id="9050666287014529139">Passphrase</translation>
<translation id="9065203028668620118">Modifica</translation>
<translation id="9068849894565669697">Seleziona colore</translation>
<translation id="9076283476770535406">Può includere contenuti per adulti</translation>
-<translation id="9092364396508701805">La pagina <ph name="HOST_NAME" /> non funziona</translation>
<translation id="9103872766612412690"><ph name="SITE" /> in genere utilizza la crittografia per proteggere le tue informazioni. Questa volta, quando Chromium ha provato a connettersi a <ph name="SITE" />, il sito web ha restituito credenziali insolite e sbagliate. È possibile che un malintenzionato stia cercando di spacciarsi per il sito <ph name="SITE" /> oppure che una schermata di accesso alla rete Wi-Fi abbia interrotto la connessione. Le tue informazioni sono ancora al sicuro perché Chromium ha interrotto la connessione prima che avvenissero scambi di dati.</translation>
<translation id="9137013805542155359">Mostra originale</translation>
+<translation id="9137248913990643158">Accedi a Chrome prima di usare questa app.</translation>
<translation id="9148507642005240123">&amp;Annulla modifica</translation>
<translation id="9157595877708044936">Configurazione in corso...</translation>
<translation id="9170848237812810038">&amp;Annulla</translation>
@@ -735,7 +788,6 @@ Psst! La prossima volta potrebbe esserti utile la modalità di navigazione in in
<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="962701380617707048">Inserisci la data di scadenza e il codice CVC della carta <ph name="CREDIT_CARD" /> per aggiornare i relativi dettagli</translation>
<translation id="969892804517981540">Build ufficiale</translation>
<translation id="988159990683914416">Build</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 1821e3d1b0c..f20c62e401b 100644
--- a/chromium/components/strings/components_strings_iw.xtb
+++ b/chromium/components/strings/components_strings_iw.xtb
@@ -16,7 +16,7 @@
<translation id="1111153019813902504">סימניות ×חרונות</translation>
<translation id="1113869188872983271">&amp;ביטול של שינוי סדר</translation>
<translation id="1126551341858583091">גודל ×”×חסון המקומי ×”×•× <ph name="CRASH_SIZE" />.</translation>
-<translation id="112840717907525620">הקובץ השמור של המדיניות תקין</translation>
+<translation id="112840717907525620">המטמון של המדיניות תקין</translation>
<translation id="113188000913989374"><ph name="SITE" /> ×ומר:</translation>
<translation id="1132774398110320017">â€×”גדרות מילוי ×וטומטי של Chrome...</translation>
<translation id="1152921474424827756">גש ×ל <ph name="BEGIN_LINK" />עותק בקובץ שמור<ph name="END_LINK" /> של <ph name="URL" /></translation>
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">â€×œ×”תחבר מחדש ×ל ×”-Wi-Fi</translation>
<translation id="1175364870820465910">הדפס...</translation>
<translation id="1181037720776840403">הסר</translation>
+<translation id="1184214524891303587">â€<ph name="BEGIN_WHITEPAPER_LINK" />שלח ב×ופן ×וטומטי<ph name="END_WHITEPAPER_LINK" /> ×ל Google דיווח על בעיות ×בטחה ×פשריות. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">הב×</translation>
<translation id="1201895884277373915">עוד מ×תר ×–×”</translation>
<translation id="1206967143813997005">חתימה ר×שונית ×œ× ×—×•×§×™×ª</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">צי×ן</translation>
<translation id="1629803312968146339">â€×”×× ×ª×¨×¦×” ש-Chrome ישמור ×ת הכרטיס ×”×–×”?</translation>
<translation id="1640180200866533862">מדיניות משתמשי×</translation>
+<translation id="1640244768702815859">נסה <ph name="BEGIN_LINK" />להיכנס לדף הבית של ×”×תר<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">תצורת הרשת ××™× ×” חוקית ×•×œ× × ×™×ª×Ÿ ×œ×™×™×‘× ×ותה.</translation>
<translation id="1644574205037202324">היסטוריה</translation>
<translation id="1645368109819982629">פרוטוקול ×œ× × ×ª×ž×š</translation>
<translation id="1676269943528358898">â€×”×תר <ph name="SITE" /> משתמש בדרך כלל בהצפנה כדי להגן על המידע שלך. ×›×שר Google Chrome ניסה ×”×¤×¢× ×œ×”×ª×—×‘×¨ ל-<ph name="SITE" />, ×”×תר שלח חזרה ××™×©×•×¨×™× ×—×¨×™×’×™× ×•×©×’×•×™×™×. ייתכן שתוקף מנסה להתחזות ל×תר <ph name="SITE" />, ×ו שמסך כניסה ל-Wi-Fi הפריע לחיבור. המידע שלך עדיין מ×ובטח מכיוון ש-Google Chrome הפסיק ×ת החיבור לפני חילופי הנתוני×.</translation>
+<translation id="168328519870909584">×ª×•×§×¤×™× ×”× ×ž×¦××™× ×›×¢×ª ב-<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ×¢×©×•×™×™× ×œ× ×¡×•×ª להתקין במכשיר שלך ×פליקציות מסוכנות שגונבות ×ו מוחקות מידע (לדוגמה, תמונות, סיסמ×ות, הודעות וכרטיסי ×שר××™).</translation>
<translation id="168841957122794586">×ישור השרת מכיל מפתח הצפנה חלש.</translation>
-<translation id="1701955595840307032">קבל הצעות לתוכן</translation>
<translation id="1710259589646384581">מערכת הפעלה</translation>
<translation id="1721312023322545264">עליך לפנות ×ל <ph name="NAME" /> לקבלת הרש××” לביקור ב×תר ×”×–×”</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">מספר דף</translation>
<translation id="1768211456781949159">â€<ph name="BEGIN_LINK" />נסה להפעיל ×ת ×בחון הרשת של Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">עדכן ×ת משפט-הסיסמה של הסינכרון.</translation>
+<translation id="1787142507584202372">×›×ן מופיעות הכרטיסיות ש×תה פותח</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">â€×’לישה בטוחה של Google <ph name="BEGIN_LINK" />זיהתה ל×חרונה תוכנה זדונית<ph name="END_LINK" /> ב-<ph name="SITE" />. ×œ×¢×™×ª×™× ×§×•×¨×” ש××ª×¨×™× ×‘×˜×•×—×™× × ×“×‘×§×™× ×‘×ª×•×›× ×” זדונית. מקור התוכן הזדוני ×”×•× <ph name="SUBRESOURCE_HOST" />. זהו מפיץ ידוע של תוכנה זדונית. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">הבקשה ×ו ×”×¤×¨×ž×˜×¨×™× ×©×œ הבקשה ××™× × ×—×•×§×™×™×</translation>
+<translation id="1834321415901700177">×”×תר ×”×–×” מכיל תוכניות מזיקות</translation>
<translation id="1838667051080421715">×תה מציג ×ת המקור של דף ×ינטרנט.</translation>
<translation id="1871208020102129563">â€×©×¨×ª ×”-Proxy מוגדר להשתמש בשרתי Proxy קבועי×, ×œ× ×‘×›×ª×•×‘×ª ×תר של סקריפט ‎.Pac</translation>
<translation id="1883255238294161206">כווץ רשימה</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">מספר / מיקוד</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{הצעה ×חת}two{שתי הצעות}many{# הצעות}other{# הצעות}}</translation>
<translation id="2065985942032347596">נדרש ×ימות</translation>
-<translation id="2079545284768500474">בטל</translation>
+<translation id="2079545284768500474">בטל פעולה</translation>
<translation id="20817612488360358">â€× ×§×‘×¢ שימוש בהגדרות שרת Proxy של מערכת ×ך בנוסף מצוינת ×’× ×ª×¦×•×¨×” מפורשת של שרת Proxy.</translation>
<translation id="2086652334978798447">â€×›×“×™ לקבל מ-Google הצעות לתוכן מות×× ×ישית, היכנס ×ל Chrome.</translation>
<translation id="2089090684895656482">פחות</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">סימניות לנייד</translation>
<translation id="2148716181193084225">היו×</translation>
-<translation id="2149973817440762519">ערוך סימניות</translation>
<translation id="2154054054215849342">סנכרון ×ינו זמין בדומיין שלך</translation>
<translation id="2166049586286450108">גישה מל××” של מנהל המערכת</translation>
<translation id="2166378884831602661">×תר ×–×” ×œ× ×™×›×•×œ לספק חיבור מ×ובטח</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">â€×œ× ניתן לבקר עכשיו ב×תר <ph name="SITE" /> מכיוון ×©×”×•× ×ž×©×ª×ž×© ב-HSTSâ€. שגי×ות רשת ותקיפות מתרחשות בדרך כלל לזמן מוגבל, ולכן סביר להניח שדף ×–×” יחזור לפעול מ×וחר יותר. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">המערכת התעלמה מסימניה ×œ× ×—×•×§×™×ª ב×ינדקס <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">סימניות ×חרות</translation>
+<translation id="2355395290879513365">ייתכן ×©×ª×•×§×¤×™× ×™×•×›×œ×• לר×ות ×ת התמונות שבהן ×תה צופה ב×תר ×–×”, ול×חר מכן ×”× ×™× ×¡×• להונות ×ותך על ידי שינוי התמונות.</translation>
<translation id="2359808026110333948">המשך</translation>
<translation id="2365563543831475020">דוח הקריסה שתועד ב-<ph name="CRASH_TIME" /> ×œ× ×”×•×¢×œ×”</translation>
<translation id="2367567093518048410">רמה</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">הצגת מדיניות ×œ×œ× ×¢×¨×š מוגדר</translation>
<translation id="2396249848217231973">&amp;ביטול מחיקה</translation>
<translation id="2455981314101692989">דף ×ינטרנט ×–×” השבית מילוי ×וטומטי של טופס ×–×”.</translation>
+<translation id="2460160116472764928">â€×’לישה בטוחה של Google <ph name="BEGIN_LINK" />זיהתה ל×חרונה תוכנה זדונית<ph name="END_LINK" /> ב-<ph name="SITE" />. ×œ×¢×™×ª×™× ×§×•×¨×” ש××ª×¨×™× ×‘×˜×•×—×™× × ×“×‘×§×™× ×‘×ª×•×›× ×” זדונית. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">מל×</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />מפעיל ×ת ×בחון הרשת<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">כתובת ×תר ×œ× ×—×•×§×™×ª של חיפוש</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">×”×× ×תה בטוח שברצונך להסיר ×“×¤×™× ×לו מההיסטוריה שלך?</translation>
<translation id="2677748264148917807">צ×</translation>
<translation id="269990154133806163">השרת הציג ×ישור ×©×œ× × ×—×©×£ ב×ופן ציבורי על פי מדיניות שקיפות ×”×ישורי×. החשיפה הזו ×”×™× ×“×¨×™×©×ª חובה בחלק מה×ישורי×, כדי להבטיח ×©×”× ×ž×”×™×ž× ×™× ×•×›×“×™ להגן מפני תוקפי×. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">רשימת קרי××”</translation>
<translation id="2704283930420550640">הערך ×œ× ×ª×•×× ×œ×¤×•×¨×ž×˜.</translation>
<translation id="2704951214193499422">â€×œ-Chromium ×ין כרגע ×פשרות ל×שר ×ת הכרטיס. נסה שוב מ×וחר יותר.</translation>
<translation id="2705137772291741111">העותק השמור (בקובץ השמור) של ×”×תר ×”×–×” ×”×™×” בלתי קרי×.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">ניסית להגיע ×ל <ph name="DOMAIN" />, ×ך ×”×ישור שהשרת הציג בוטל על-ידי המנפיק שלו. פירוש הדבר ×”×•× ×©×ין לתת ×מון ב×ישורי ×”×בטחה שהשרת הציג. ייתכן ש×תה מתקשר ×¢× ×ª×•×§×£. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">בקש רשות</translation>
<translation id="2713444072780614174">לבן</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">â€× ×™×ª×Ÿ להשבית כל שרת proxy המוגדר לחיבור מדף ההגדרות.</translation>
<translation id="2955913368246107853">סגור ×ת חלונית החיפוש</translation>
<translation id="2958431318199492670">â€×ª×¦×•×¨×ª הרשת ××™× ×” תו×מת לתקן ONC. ייתכן ×©×—×œ×§×™× ×ž×”×ª×¦×•×¨×” ×œ× ×™×™×›×œ×œ×• בייבו×.</translation>
+<translation id="29611076221683977">â€×ª×•×§×¤×™× הנמצ××™× ×›×¢×ª ב-<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ×¢×©×•×™×™× ×œ× ×¡×•×ª להתקין ב-Mac שלך תכניות מסוכנות שגונבות ×ו מוחקות מידע (לדוגמה, תמונות, סיסמ×ות, הודעות וכרטיסי ×שר××™).</translation>
<translation id="2969319727213777354">â€×›×“×™ ליצור חיבור מ×ובטח, השעון צריך להיות מוגדר כהלכה. הסיבה לכך ×”×™× ×©×”××™×©×•×¨×™× ×©×‘××ž×¦×¢×•×ª× ××ª×¨×™× ×ž×–×”×™× ×ת ×¢×¦×ž× ×ª×§×¤×™× ×¨×§ למשך פרקי זמן מסוימי×. מ×חר שהשעון במכשיר שלך שגוי, Google Chrome ×œ× ×™×›×•×œ ל×מת ×ת ×”××™×©×•×¨×™× ×”×לה.</translation>
<translation id="2972581237482394796">&amp;בצע שנית</translation>
<translation id="2985306909656435243">â€×× ×”×פשרות תופעל, Chromium ×™×חסן עותק של הכרטיס שלך במכשיר ×”×–×” למילוי מהיר יותר של טפסי×.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">הסתר פרטי×</translation>
<translation id="3587482841069643663">הכל</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">â€×”שרת ×ינו תומך בתוסף TLS לניהול ×ž×©× ×•×ž×ª×Ÿ חוזר.</translation>
<translation id="36224234498066874">נקה נתוני גלישה...</translation>
<translation id="362276910939193118">הצג ×ת כל ההיסטוריה</translation>
<translation id="3623476034248543066">הצג ערך</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">×× ×תה רו××” ×–×ת ×œ×¢×ª×™× ×§×¨×•×‘×•×ª, נסה ×ת ×”<ph name="HELP_LINK" /> ×”×לה.</translation>
<translation id="3658742229777143148">גרסה קודמת</translation>
<translation id="3678029195006412963">×œ× × ×™×ª×Ÿ ×”×™×” ×œ×—×ª×•× ×¢×œ הבקשה</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">×ין מספיק רישיונות</translation>
<translation id="3714780639079136834">â€×œ×”פעיל × ×ª×•× ×™× ×œ× ×™×™×“ ×ו ×ת ×”-Wi-Fi</translation>
<translation id="3717027428350673159">â€<ph name="BEGIN_LINK" />לבדוק ×ת תצורת ×”-DNS, חומת ×”×ש ושרת ×”-Proxy<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">×× ×תה מבין ×ת ×”×¡×™×›×•× ×™× ×‘× ×•×’×¢ ל×בטחה שלך, תוכל <ph name="BEGIN_LINK" />להיכנס ל×תר ×œ× ×‘×˜×•×— ×–×”<ph name="END_LINK" /> לפני הסרת התכניות המסוכנות.</translation>
<translation id="3739623965217189342">קישור שהעתקת</translation>
<translation id="375403751935624634">×”×ª×¨×’×•× × ×›×©×œ עקב שגי×ת שרת.</translation>
<translation id="3759461132968374835">×œ× ×”×ª×§×‘×œ×• ×“×™×•×•×—×™× ×¢×œ קריסות ל×חרונה. קריסות שהתרחשו בזמן ש×פשרות הדיווח על קריסות היתה מושבתת ×œ× ×™×•×¤×™×¢×• ×›×ן.</translation>
-<translation id="3788090790273268753">â€×ª×•×§×£ ×”×ישור של ×”×תר ×”×–×” יפוג ב-2016 ושרשרת ×”××™×©×•×¨×™× ×ž×›×™×œ×” ×ישור ×©× ×—×ª× ×‘×מצעות SHA-1.</translation>
<translation id="382518646247711829">â€×× ×תה משתמש בשרת Proxy...</translation>
<translation id="3828924085048779000">×ין ×פשרות להשתמש במשפט-סיסמה ריק.</translation>
<translation id="3845539888601087042">מציג היסטוריה ×ž×”×ž×›×©×™×¨×™× ×©×‘×”× ×תה מחובר לחשבון. <ph name="BEGIN_LINK" />למידע נוסף<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">מפתח "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">â€×”לקוח והשרת ××™× × ×ª×•×ž×›×™× ×‘×’×¨×¡×” נפוצה של פרוטוקול SSL ×ו בחבילת צפני×.</translation>
<translation id="4079302484614802869">â€×ª×¦×•×¨×ª ×”-Proxy מוגדרת להשתמש בכתובת ×תר של סקריפט מסוג ‎.Pac ×•×œ× ×‘×©×¨×ª×™ Proxy קבועי×.</translation>
+<translation id="4098354747657067197">זהירות, ×תר מטעה</translation>
<translation id="4103249731201008433">המספר הסידורי של המכשיר ×ינו חוקי</translation>
<translation id="4103763322291513355">â€×”יכנס לכתובת &lt;strong&gt;chrome://policy&lt;/strong&gt; כדי לר×ות רשימה של כתובות ××ª×¨×™× ×©× ×ž× ×¢×” ××œ×™×”× ×”×’×™×©×”, כמו ×’× ×ª×§× ×•× ×™× ××—×¨×™× ×©× ×כפו על ידי מנהל המערכת שלך.</translation>
<translation id="4110615724604346410">השרת ×”×–×” ×œ× ×”×¦×œ×™×— להוכיח ×©×”×•× <ph name="DOMAIN" />; ×ישור ×”×בטחה שלו מכיל שגי×ות. ייתכן שהסיבה לכך ×”×™× ×”×’×“×¨×” שגויה ×ו שתוקף מיירט ×ת החיבור שלך. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{לל×}=1{×פליקציה ×חת ($1)}=2{שתי ×פליקציות ($1, $2)}many{# ×פליקציות ($1, $2, $3)}other{# ×פליקציות ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">קריסה</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />נסה להפעיל ×ת ×בחון הרשת<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">החיבור שלך ל×תר ×”×–×” ×œ× ×ž×ובטח בצורה מל××”</translation>
<translation id="4250680216510889253">ל×</translation>
<translation id="425582637250725228">ייתכן ×©×”×©×™× ×•×™×™× ×©×‘×™×¦×¢×ª ×œ× ×™×™×©×ž×¨×•.</translation>
<translation id="4258748452823770588">חתימה שגויה</translation>
<translation id="4269787794583293679">(×ין ×©× ×ž×©×ª×ž×©)</translation>
+<translation id="4280429058323657511">, בתוקף עד <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">â€×”גלישה הבטוחה של Google <ph name="BEGIN_LINK" />מצ××” ל×חרונה תוכניות מזיקות<ph name="END_LINK" /> ב-<ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">הצעות להורי×</translation>
<translation id="4304224509867189079">היכנס</translation>
<translation id="432290197980158659">השרת הציג ×ישור ש×ינו עומד בציפיות המובנות במערכת. הציפיות ×”×לה מוגדרות על מנת ל×פשר גישה רק ×ל ××ª×¨×™× ×‘×¢×œ×™ רמת ×בטחה גבוהה כדי לשמור על ביטחונך. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">×œ× × ×™×ª×Ÿ ×”×™×” ×œ×ž×¦×•× ×ת הפריט</translation>
+<translation id="4326324639298822553">בדוק ×ת ת×ריך התפוגה ונסה שוב</translation>
<translation id="4331708818696583467">×œ× ×ž×ובטח</translation>
+<translation id="4356973930735388585">×ª×•×§×¤×™× ×‘×תר ×”×–×” ×¢×©×•×™×™× ×œ× ×¡×•×ª להתקין במחשב שלך תוכניות מסוכנות שגונבות ×ו מוחקות מידע שלך (לדוגמה: תמונות, סיסמ×ות, הודעות ופרטי כרטיסי ×שר××™).</translation>
<translation id="4372948949327679948">צפוי ערך מסוג <ph name="VALUE_TYPE" />.</translation>
<translation id="4381091992796011497">×©× ×ž×©×ª×ž×©:</translation>
<translation id="4394049700291259645">השבת</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">ניסית להגיע ×ל <ph name="DOMAIN" />, ×ך השרת הציג ×ישור ×œ× ×—×•×§×™. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">החיבור שלך ×ל <ph name="DOMAIN" /> מוצפן ב×מצעות חבילת צופן מתקדמת.</translation>
<translation id="4594403342090139922">&amp;ביטול מחיקה</translation>
+<translation id="4619615317237390068">כרטיסיות ×ž×ž×›×©×™×¨×™× ×חרי×</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">×תה צופה בדף של תוסף.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,7 +372,9 @@
<translation id="4726672564094551039">טען מדיניות מחדש</translation>
<translation id="4728558894243024398">פלטפורמה</translation>
<translation id="4744603770635761495">נתיב להפעלה</translation>
+<translation id="4750917950439032686">×”×¤×¨×˜×™× ×©×œ×š (כמו סיסמ×ות ×ו מספרי כרטיסי ×שר××™) × ×©×œ×—×™× ×œ×תר ×”×–×” במצב פרטי.</translation>
<translation id="4756388243121344051">&amp;היסטוריה</translation>
+<translation id="4759118997339041434">המילוי ×”×וטומטי של ×ª×©×œ×•×ž×™× ×”×•×©×‘×ª</translation>
<translation id="4764776831041365478">ייתכן שדף ×”×ינטרנט בכתובת <ph name="URL" /> ×ינו פעיל זמנית, ×ו שהועבר לכתובת ×ינטרנט חדשה לצמיתות.</translation>
<translation id="4771973620359291008">â€×ירעה שגי××” ×œ× ×ž×•×›×¨×ª.
@@ -364,6 +382,7 @@
1240185477879256427×תר
Del</translation>
<translation id="4800132727771399293">â€×‘דוק ×ת ת×ריך התפוגה ו×ת ×”-CVC ונסה שוב</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">שגי×ת רשת</translation>
<translation id="4816492930507672669">הת×מה לדף</translation>
<translation id="4850886885716139402">הצג</translation>
@@ -372,6 +391,7 @@ Del</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ועוד דף ×ינטרנט ×חד}two{ועוד # דפי ×ינטרנט}many{ועוד # דפי ×ינטרנט}other{ועוד # דפי ×ינטרנט}}</translation>
<translation id="4923417429809017348">דף ×–×” ×ª×•×¨×’× ×ž×©×¤×” ×œ× ×™×“×•×¢×” ל<ph name="LANGUAGE_LANGUAGE" /></translation>
+<translation id="4923459931733593730">תשלו×</translation>
<translation id="4926049483395192435">יש לציין ערך זה.</translation>
<translation id="495170559598752135">פעולות</translation>
<translation id="4958444002117714549">הרחב רשימה</translation>
@@ -399,7 +419,6 @@ Del</translation>
<translation id="5181140330217080051">מוריד</translation>
<translation id="5190835502935405962">סרגל הסימניות</translation>
<translation id="5199729219167945352">ניסויי×</translation>
-<translation id="5199841536747119669">הצעות עבורך מופיעות ×›×ן</translation>
<translation id="5251803541071282808">ענן</translation>
<translation id="5277279256032773186">â€×ž×©×ª×ž×© ב-Chrome בעבודה? ×¢×¡×§×™× ×™×›×•×œ×™× ×œ× ×”×œ ×ת ההגדרות של Chrome עבור העובדי×. למידע נוסף</translation>
<translation id="5299298092464848405">שגי××” בניתוח המדיניות</translation>
@@ -407,8 +426,10 @@ Del</translation>
<translation id="5308689395849655368">דיווח קריסות מושבת.</translation>
<translation id="5317780077021120954">שמור</translation>
<translation id="5327248766486351172">ש×</translation>
+<translation id="5337705430875057403">×ª×•×§×¤×™× ×‘-<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ×¢×©×•×™×™× ×œ×’×¨×•× ×œ×š לבצע פעולות מסוכנות, כמו התקנת תוכנה ×ו חשיפה של מידע ×ישי (למשל: סיסמ×ות, מספרי טלפון ×ו כרטיסי ×שר××™).</translation>
<translation id="5359637492792381994">השרת ×”×–×” ×œ× ×”×¦×œ×™×— להוכיח ×©×”×•× <ph name="DOMAIN" />; ×ישור ×”×בטחה שלו ×ינו חוקי כעת. ייתכן שהסיבה לכך ×”×™× ×”×’×“×¨×” שגויה ×ו שתוקף מיירט ×ת החיבור שלך. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">×חסון הגדרות המדיניות נכשל</translation>
+<translation id="5386426401304769735">â€×©×¨×©×¨×ª ×”××™×©×•×¨×™× ×©×œ ×”×תר ×”×–×” כוללת ×ישור ×©× ×—×ª× ×‘×מצעות SHA-1.</translation>
<translation id="5421136146218899937">נקה נתוני גלישה...</translation>
<translation id="5430298929874300616">הסר סימניה</translation>
<translation id="5431657950005405462">הקובץ ×œ× × ×ž×¦×</translation>
@@ -430,17 +451,20 @@ Del</translation>
<translation id="5544037170328430102">דף המוטמע ב-<ph name="SITE" /> ×ומר:</translation>
<translation id="5556459405103347317">טען שוב</translation>
<translation id="5565735124758917034">פעיל</translation>
+<translation id="5572851009514199876">â€×ª×—ילה היכנס לחשבונך ב-Chrome כדי ל×פשר ל-Chrome לבדוק ×× ×™×© לך הרש××” לגשת ל×תר ×”×–×”.</translation>
+<translation id="5580958916614886209">בדוק ×ת חודש התפוגה ונסה שוב</translation>
<translation id="560412284261940334">ניהול ×ינו נתמך</translation>
<translation id="5610142619324316209">לבדוק ×ת החיבור</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> הפנה ×ותך מחדש ×¤×¢×ž×™× ×¨×‘×•×ª מדי.</translation>
<translation id="5622887735448669177">×”×× ×‘×¨×¦×•× ×š לעזוב ×ת ×”×תר?</translation>
<translation id="5629630648637658800">טעינת הגדרות המדיניות נכשלה</translation>
<translation id="5631439013527180824">×סימון ניהול המכשיר ×ינו חוקי</translation>
+<translation id="5669703222995421982">הת×מה ×ישית של תוכן</translation>
+<translation id="5675650730144413517">הדף ×”×–×” ×œ× ×¢×•×‘×“</translation>
<translation id="5677928146339483299">במצב חסו×</translation>
<translation id="5694783966845939798">â€× ×™×¡×™×ª להיכנס ×ל <ph name="DOMAIN" />, ×בל השרת הציג ×ישור ×©× ×—×ª× ×‘×מצעות ××œ×’×•×¨×™×ª× ×—×ª×™×ž×” ברמת ×בטחה חלשה (כגון SHA-1). כלומר, ייתכן ש×ישורי ×”×בטחה שהשרת הציג מזויפי×, ×•×©×œ× ×ž×“×•×‘×¨ בשרת שציפית לו (ייתכן ש×תה מתקשר ×¢× ×ª×•×§×£). <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">הזהות של ×תר ×–×” ×œ× ×ומתה.</translation>
<translation id="5720705177508910913">משתמש נוכחי:</translation>
-<translation id="572328651809341494">כרטיסיות ×חרונות</translation>
<translation id="5732392974455271431">×”×”×•×¨×™× ×©×œ×š ×™×›×•×œ×™× ×œ×‘×˜×œ בשבילך ×ת החסימה</translation>
<translation id="5784606427469807560">הייתה בעיה ב×ישור הכרטיס. בדוק ×ת החיבור ל×ינטרנט ונסה שוב.</translation>
<translation id="5785756445106461925">כמו כן, דף ×–×” כולל מש××‘×™× × ×•×¡×¤×™× ×©××™× × ×ž×ובטחי×. ×’×•×¨×ž×™× ××—×¨×™× ×¢×œ×•×œ×™× ×œ×¨×ות ×ת המש××‘×™× ×”×לה במהלך העברת×, ותוקף עלול לשנות ××•×ª× ×‘×ופן שישנה ×ת מר××” הדף.</translation>
@@ -449,6 +473,7 @@ Del</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="5843436854350372569">ניסית להגיע ×ל <ph name="DOMAIN" />, ×בל השרת הציג ×ישור המכיל מפתח חלש. ייתכן שתוקף פרץ ×ת המפתח הפרטי וזהו ×ינו השרת ש×תה מצפה לו (ייתכן ש×תה מתקשר ×¢× ×ª×•×§×£). <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">תיקייה חדשה</translation>
<translation id="5869405914158311789">×œ× × ×™×ª×Ÿ לגשת ל×תר ×”×–×”</translation>
@@ -458,6 +483,7 @@ Del</translation>
<translation id="59107663811261420">â€×¡×•×’ הכרטיס ×”×–×” ×ינו נתמך על ידי Google Payments ×צל הסוחר ×”×–×”. בחר כרטיס ×חר.</translation>
<translation id="59174027418879706">מופעל</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="5966707198760109579">שבוע</translation>
<translation id="5967867314010545767">הסר מההיסטוריה</translation>
<translation id="5975083100439434680">התרחק</translation>
@@ -476,8 +502,11 @@ Del</translation>
<translation id="614940544461990577">נסה:</translation>
<translation id="6151417162996330722">תקופת התוקף של ×ישור השרת ×רוכה מדי.</translation>
<translation id="6165508094623778733">למידע נוסף</translation>
+<translation id="6177128806592000436">החיבור שלך ל×תר ×”×–×” ×œ× ×ž×ובטח</translation>
<translation id="6203231073485539293">בדוק ×ת חיבור ×”×ינטרנט</translation>
<translation id="6218753634732582820">â€×”×× ×œ×”×¡×™×¨ מ-Chromium ×ת הכתובת?</translation>
+<translation id="6251924700383757765">מדיניות פרטיות</translation>
+<translation id="625755898061068298">בחרת להשבית ×ת ×זהרות ×”×בטחה ל×תר ×”×–×”.</translation>
<translation id="6259156558325130047">&amp;ביצוע מחדש של שינוי סדר</translation>
<translation id="6263376278284652872">סימניות <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">חזרה לחוף מבטחי×</translation>
@@ -486,6 +515,7 @@ Del</translation>
<translation id="6305205051461490394">×œ× × ×™×ª×Ÿ לגשת ×ל <ph name="URL" />.</translation>
<translation id="6321917430147971392">â€×‘דוק ×ת הגדרות ×”-DNS</translation>
<translation id="6328639280570009161">נסה להשבית ×ת חיזוי הרשת</translation>
+<translation id="6328786501058569169">×”×תר ×”×–×” מטעה</translation>
<translation id="6337534724793800597">סנן מדיניות לפי ש×</translation>
<translation id="6342069812937806050">זה עתה</translation>
<translation id="6345221851280129312">גודל ×œ× ×™×“×•×¢</translation>
@@ -494,6 +524,8 @@ Del</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="6404511346730675251">ערוך סימניה</translation>
+<translation id="6410264514553301377">הזן ×ת ת×ריך התפוגה ו×ת קוד ×”×ימות של <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">ש×לת ×חד מהוריך ×× ×תה יכול לבקר ב×תר ×”×–×”</translation>
<translation id="6416403317709441254">â€×œ× ניתן לבקר עכשיו ב-<ph name="SITE" /> מכיוון שה×תר שלח ××™×©×•×¨×™× ×œ× ×ª×§×™× ×™× ×©-Chromium ×œ× ×ž×¦×œ×™×— לעבד. שגי×ות רשת ותקיפות מתרחשות בדרך כלל לזמן מוגבל, ולכן סביר להניח שדף ×–×” יחזור לפעול מ×וחר יותר. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">×œ× × ×™×ª×Ÿ לבדוק ×× ×”×ישור נשלל.</translation>
@@ -503,10 +535,12 @@ Del</translation>
<translation id="6458467102616083041">המערכת התעלמה מערך מדיניות ×–×”, ×ž×©×•× ×©×œ×¤×™ המדיניות, חיפוש ברירת המחדל מושבת.</translation>
<translation id="6462969404041126431">השרת ×”×–×” ×œ× ×”×¦×œ×™×— להוכיח ×©×”×•× <ph name="DOMAIN" />; ייתכן ש×ישור ×”×בטחה שלו בוטל. ייתכן שהסיבה לכך ×”×™× ×”×’×“×¨×” שגויה ×ו שתוקף מיירט ×ת החיבור שלך. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">מדיניות המכשיר</translation>
+<translation id="6477321094435799029">â€Chrome ×–×™×”×” קוד חריג בדף ×”×–×” ×•×—×¡× ×ותו כדי להגן על המידע הפרטי שלך (כגון סיסמ×ות, מספרי טלפון ומספרי כרטיסי ×שר××™).</translation>
<translation id="6489534406876378309">התחל להעלות קריסות</translation>
<translation id="6529602333819889595">&amp;ביצוע מחדש של מחיקה</translation>
<translation id="6534179046333460208">הצעות ל×ינטרנט הווירטופיזי</translation>
<translation id="6550675742724504774">×פשרויות</translation>
+<translation id="6556239504065605927">חיבור מ×ובטח</translation>
<translation id="6563469144985748109">המנהל שלך עדיין ×œ× ×ישר ×–×ת</translation>
<translation id="6593753688552673085">פחות מ-<ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">×פשרויות הצפנה</translation>
@@ -515,7 +549,6 @@ Del</translation>
<translation id="6644283850729428850">מדיניות זו ××™× ×” בתוקף.</translation>
<translation id="6652240803263749613">השרת ×”×–×” ×œ× ×”×¦×œ×™×— להוכיח ×©×”×•× <ph name="DOMAIN" />; מערכת ההפעלה של המחשב ×œ× ×¡×•×ž×›×ª על ×ישור ×”×בטחה שלו. ייתכן שהסיבה לכך ×”×™× ×”×’×“×¨×” שגויה ×ו שתוקף מיירט ×ת החיבור שלך. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">ערוך תיקייה</translation>
-<translation id="6660210980321319655">דיווח ×וטומטי <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">â€×”×× ×œ×”×¡×™×¨ מ-Chromium הצעות לטפסי×?</translation>
<translation id="6685834062052613830">×¦× ×•×”×©×œ× ×ת ההגדרה</translation>
<translation id="6710213216561001401">הקוד×</translation>
@@ -527,6 +560,7 @@ Del</translation>
<translation id="6753269504797312559">ערך מדיניות</translation>
<translation id="6757797048963528358">המכשיר עבר למצב שינה.</translation>
<translation id="6778737459546443941">ההורה שש×לת עדיין ×œ× ×ישר ×–×ת</translation>
+<translation id="6810899417690483278">מזהה של הת×מה ×ישית</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">דף ×”×ינטרנט <ph name="URL" /> ×ינו זמין כעת. ייתכן ×©×”×•× ×‘×¢×•×ž×¡ יתר ×ו שהושבת לצורך תחזוקה.</translation>
<translation id="6831043979455480757">תרג×</translation>
@@ -547,6 +581,8 @@ Del</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>
+<translation id="7064851114919012435">×¤×¨×˜×™× ×œ×™×¦×™×¨×ª קשר</translation>
+<translation id="7079718277001814089">×”×תר ×”×–×” מכיל תוכנה זדונית</translation>
<translation id="7087282848513945231">מחוז (בבריטניה וב×ירלנד)</translation>
<translation id="7088615885725309056">ישן יותר</translation>
<translation id="7090678807593890770">â€×—פש ב-Google ×ת <ph name="LINK" /></translation>
@@ -561,6 +597,7 @@ Del</translation>
<translation id="7210863904660874423"><ph name="HOST_NAME" /> ×œ× ×¤×•×¢×œ בהת×× ×œ×ª×§× ×™ ×”×בטחה.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />מידע נוסף<ph name="END_LINK" /> ×‘× ×•×©× ×–×”.</translation>
<translation id="7219179957768738017">החיבור משתמש ב-<ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">×”×תר שלפניך מכיל תוכנה זדונית</translation>
<translation id="724975217298816891">הזן ×ת ת×ריך התפוגה ו×ת קוד ×”×ימות של <ph name="CREDIT_CARD" /> כדי לעדכן ×ת פרטי הכרטיס. ברגע שת×שר, פרטי הכרטיס שלך ישותפו ×¢× ×”×תר ×”×–×”.</translation>
<translation id="725866823122871198">×œ× × ×™×ª×Ÿ ליצור חיבור פרטי ×ל <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> מפני שהת×ריך והשעה (<ph name="DATE_AND_TIME" />) במחשב שלך שגויי×.</translation>
<translation id="7269802741830436641">דף ×ינטרנט ×–×” מכיל לול×ת כתובת ×תר להפניה מחדש</translation>
@@ -616,9 +653,10 @@ Del</translation>
<translation id="7658239707568436148">ביטול</translation>
<translation id="7667346355482952095">×סימון המדיניות שהוחזר ×”×•× ×¨×™×§ ×ו ש×ינו תו×× ×œ×סימון הנוכחי</translation>
<translation id="7668654391829183341">מכשיר ×œ× ×™×“×•×¢</translation>
+<translation id="7669271284792375604">×ª×•×§×¤×™× ×‘×תר ×”×–×” ×¢×©×•×™×™× ×œ×’×¨×•× ×œ×š, בדרכי מרמה, להתקין תוכניות שיפגעו בחוויית הגלישה שלך (לדוגמה, על ידי שינוי דף הבית ×ו הצגת מודעות נוספות ב××ª×¨×™× ×©×‘×”× ×תה מבקר).</translation>
<translation id="7674629440242451245">â€×ž×¢×•× ×™×™×Ÿ בתכונות חדשות ומגניבות של Chrome? נסה ×ת הערוץ שלנו ×œ×ž×¤×ª×—×™× ×‘×›×ª×•×‘×ª chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />המשך ×ל <ph name="SITE" /> (×œ× ×‘×˜×•×—)<ph name="END_LINK" /></translation>
-<translation id="7716424297397655342">×œ× × ×™×ª×Ÿ לטעון ×ת ×”×תר ×”×–×” מהקובץ השמור</translation>
+<translation id="7716424297397655342">×œ× × ×™×ª×Ÿ לטעון ×ת ×”×תר ×”×–×” מהמטמון</translation>
<translation id="7733391738235763478">(<ph name="NUMBER_VISITS" />)</translation>
<translation id="7752995774971033316">×œ×œ× × ×™×”×•×œ</translation>
<translation id="7755287808199759310">×חד ×ž×”×”×•×¨×™× ×©×œ×š יכול לבטל בשבילך ×ת החסימה</translation>
@@ -631,14 +669,17 @@ Del</translation>
<translation id="7800304661137206267">החיבור מוצפן ב×מצעות <ph name="CIPHER" /> ×¢× <ph name="MAC" /> ל×ימות הודעות ×•×¢× <ph name="KX" /> בתור מנגנון להחלפת מפתחות.</translation>
<translation id="780301667611848630">×œ× ×ª×•×“×”</translation>
<translation id="7805768142964895445">סטטוס</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">â€×”×× ×œ×”×¡×™×¨ מ-Chrome הצעות בשביל טפסי×?</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="7887683347370398519">â€×‘דוק ×ת ×”-CVC ונסה שוב</translation>
<translation id="7912024687060120840">בתיקייה:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">×ישור השרת עדיין ×œ× ×‘×ª×•×§×£.</translation>
<translation id="7942349550061667556">×דו×</translation>
+<translation id="7947285636476623132">בדוק ×ת שנת התפוגה ונסה שוב</translation>
<translation id="7951415247503192394">(32 סיביות)</translation>
<translation id="7956713633345437162">סימניות לנייד</translation>
<translation id="7961015016161918242">××£ פע×</translation>
@@ -646,7 +687,10 @@ Del</translation>
<translation id="7983301409776629893">×ª×¨×’× ×ª×ž×™×“ <ph name="ORIGINAL_LANGUAGE" /> ל<ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">×œ× ×¦×•×™×Ÿ</translation>
<translation id="8012647001091218357">×œ× ×”×¦×œ×—× ×• ליצור קשר ×¢× ×”×”×•×¨×™× ×©×œ×š. נסה שוב מ×וחר יותר.</translation>
+<translation id="8025119109950072390">×ª×•×§×¤×™× ×‘×תר ×”×–×” ×¢×©×•×™×™× ×œ×’×¨×•× ×œ×š, בדרכי מרמה, לבצע פעולות מסוכנות כמו התקנת תוכנה ×ו חשיפה של מידע ×ישי (לדוגמה: סיסמ×ות, מספרי טלפון ×ו פרטי כרטיסי ×שר××™).</translation>
+<translation id="803030522067524905">â€×”גלישה הבטוחה של Google זיהתה ל×חרונה דיוג ב×תר <ph name="SITE" />. ×תרי דיוג ×ž×ª×—×–×™× ×œ××ª×¨×™× ××—×¨×™× ×›×“×™ להטעות ×ותך. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">דף ×–×” מוצג ב<ph name="SOURCE_LANGUAGE" />. ×”×× ×œ×ª×¨×’× ×ותו ל<ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">שלח משוב</translation>
<translation id="8088680233425245692">הצגת הפריט נכשלה.</translation>
<translation id="8089520772729574115">â€×¤×—ות מ-‎1 MB</translation>
<translation id="8091372947890762290">ההפעלה ממתינה בשרת</translation>
@@ -657,9 +701,11 @@ Del</translation>
<translation id="8150722005171944719">הקובץ ×”× ×ž×¦× ×‘-<ph name="URL" /> ×œ× × ×™×ª×Ÿ לקרי××”. ייתכן שהקובץ הוסר ×ו הועבר, ×ו שהרש×ות הקובץ מונעות גישה ×ליו.</translation>
<translation id="8194797478851900357">&amp;ביטול העברה</translation>
<translation id="8201077131113104583">כתובת ×תר ×œ× ×—×•×§×™×ª לעדכון עבור תוסף ×¢× ×”×ž×–×”×” "<ph name="EXTENSION_ID" />".</translation>
+<translation id="8202097416529803614">×¡×™×›×•× ×”×–×ž× ×”</translation>
<translation id="8218327578424803826">×ž×™×§×•× ×ž×•×§×¦×”:</translation>
<translation id="8225771182978767009">×”××“× ×©×”×’×“×™×¨ ×ת המחשב ×”×–×” בחר ×œ×—×¡×•× ×ת ×”×תר ×”×–×”.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">×ª×•×§×¤×™× ×”× ×ž×¦××™× ×›×¢×ª ב-<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ×¢×©×•×™×™× ×œ× ×¡×•×ª להתקין במחשב שלך תכניות מסוכנות שגונבות ×ו מוחקות מידע שלך (לדוגמה, תמונות, סיסמ×ות, הודעות וכרטיסי ×שר××™).</translation>
<translation id="8241707690549784388">הדף ש×תה מחפש השתמש במידע שהזנת. החזרה לדף ×–×” עלולה ×œ×’×¨×•× ×œ×›×¤×™×œ×•×ª בפעולות שביצעת. ×”×× ×‘×¨×¦×•× ×š להמשיך?</translation>
<translation id="8249320324621329438">×וחזר ל×חרונה:</translation>
<translation id="8261506727792406068">מחק</translation>
@@ -668,11 +714,13 @@ Del</translation>
<translation id="8294431847097064396">מקור</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="8349305172487531364">סרגל סימניות</translation>
<translation id="8363502534493474904">לכבות ×ת מצב הטיסה</translation>
<translation id="8364627913115013041">×œ× ×ž×•×’×“×¨.</translation>
<translation id="8380941800586852976">מסוכן</translation>
<translation id="8382348898565613901">הסימניות ש×ליהן נכנסת ל×חרונה מופיעות ×›×ן</translation>
+<translation id="8398259832188219207">דוח קריסה הועלה ב-<ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">קריסות (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">עליך להזין ×ת ×ותו משפט-סיסמה פעמיי×.</translation>
<translation id="8428213095426709021">הגדרות</translation>
@@ -684,9 +732,9 @@ Del</translation>
<translation id="8498891568109133222">ל-<ph name="HOST_NAME" /> נדרש זמן רב מדי להגיב.</translation>
<translation id="852346902619691059">השרת ×”×–×” ×œ× ×”×¦×œ×™×— להוכיח ×©×”×•× <ph name="DOMAIN" />; מערכת ההפעלה של המכשיר ×œ× ×¡×•×ž×›×ª על ×ישור ×”×בטחה שלו. ייתכן שהסיבה לכך ×”×™× ×”×’×“×¨×” שגויה ×ו שתוקף מיירט ×ת החיבור שלך. <ph name="BEGIN_LEARN_MORE_LINK" />למידע נוסף<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">â€×¡×•×’ הכרטיס ×”×–×” ×ינו נתמך על ידי Google Payments. בחר כרטיס ×חר.</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="8553075262323480129">×”×ª×¨×’×•× × ×›×©×œ כיוון ×©×œ× ×”×™×™×ª×” ×פשרות לקבוע ×ת שפת הדף.</translation>
<translation id="8559762987265718583">×œ× × ×™×ª×Ÿ ליצור חיבור פרטי ×ל <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> מפני שהת×ריך והשעה (<ph name="DATE_AND_TIME" />) במכשיר שלך שגויי×.</translation>
-<translation id="856992080682148">â€×ª×•×§×£ ×”×ישור של ×”×תר ×”×–×” יפוג ב-2017 ×ו מ×וחר יותר ושרשרת ×”××™×©×•×¨×™× ×ž×›×™×œ×” ×ישור ×©× ×—×ª× ×‘×מצעות SHA-1.</translation>
<translation id="8571890674111243710">×ž×ª×¨×’× ×“×£ ל<ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">×”×ישור ×ינו מציין מנגנון הבודק ×× ×”×•× × ×©×œ×œ.</translation>
<translation id="8620436878122366504">×”×”×•×¨×™× ×©×œ×š עדיין ×œ× ×ישרו ×–×ת</translation>
@@ -699,10 +747,12 @@ Del</translation>
<translation id="8740359287975076522">â€×œ× ניתן ×”×™×” ×œ×ž×¦×•× ×ת &lt;abbr id="dnsDefinition"&gt;כתובת ×”-DNS&lt;/abbr&gt; של <ph name="HOST_NAME" />. מ×בחן ×ת הבעיה.</translation>
<translation id="8790007591277257123">&amp;ביצוע מחדש של מחיקה</translation>
<translation id="8798099450830957504">ברירת מחדל</translation>
+<translation id="8800988563907321413">×›×ן מופיעות הצעות עבורך למקומות קרובי×</translation>
<translation id="8804164990146287819">מדיניות פרטיות</translation>
<translation id="8820817407110198400">סימניות</translation>
<translation id="8834246243508017242">הפעל מילוי ×וטומטי ב×מצעות '×נשי קשר'…</translation>
<translation id="883848425547221593">סימניות ×חרות</translation>
+<translation id="884264119367021077">כתובת למשלוח</translation>
<translation id="884923133447025588">×œ× × ×ž×¦× ×ž× ×’× ×•×Ÿ ביטול</translation>
<translation id="885730110891505394">â€×©×™×ª×•×£ ×¢× Google</translation>
<translation id="8866481888320382733">שגי××” בניתוח הגדרות המדיניות</translation>
@@ -720,18 +770,21 @@ Del</translation>
<translation id="8971063699422889582">פג תוקפו של ×ישור השרת.</translation>
<translation id="8987927404178983737">חודש</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">×”×תר ש×תה עומד לעבור ×ליו מכיל תוכניות מזיקות</translation>
<translation id="9001074447101275817">â€×©×¨×ª ×”-proxy â€<ph name="DOMAIN" /> מצריך ×©× ×ž×©×ª×ž×© וסיסמה.</translation>
<translation id="901974403500617787">רק ×”×‘×¢×œ×™× ×™×›×•×œ להגדיר ×¡×™×ž×•× ×™× ×”×—×œ×™× ×¢×œ כל המערכת: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">הדף ×”×–×” ×ª×•×¨×’× ×œ<ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">שגי×ת ×בטחה</translation>
<translation id="9038649477754266430">השתמש בשירות חיזוי כדי לטעון ×“×¤×™× ×ž×”×¨ יותר</translation>
<translation id="9039213469156557790">כמו כן, דף ×–×” כולל מש××‘×™× × ×•×¡×¤×™× ×©××™× × ×ž×ובטחי×. ×’×•×¨×ž×™× ××—×¨×™× ×¢×œ×•×œ×™× ×œ×¨×ות ×ת המש××‘×™× ×”×לה במהלך העברת×, ותוקף עלול לשנות ××•×ª× ×‘×ופן שישנה ×ת התנהגות הדף.</translation>
+<translation id="9040185888511745258">×ª×•×§×¤×™× ×‘-<ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ×¢×©×•×™×™× ×œ× ×¡×•×ª ×œ×”×¢×¨×™× ×¢×œ×™×š ×•×œ×’×¨×•× ×œ×š להתקין תוכניות שיפגעו בחוויית הגלישה שלך (לדוגמה, על ידי שינוי דף הבית ×ו הצגת מודעות נוספות ב××ª×¨×™× ×©×‘×”× ×תה מבקר).</translation>
<translation id="9050666287014529139">משפט-סיסמה</translation>
<translation id="9065203028668620118">ערוך</translation>
<translation id="9068849894565669697">בחירת צבע</translation>
<translation id="9076283476770535406">ייתכן שה×תר מכיל תוכן למבוגרי×</translation>
-<translation id="9092364396508701805">הדף של <ph name="HOST_NAME" /> ×œ× ×¤×•×¢×œ</translation>
<translation id="9103872766612412690">â€×”×תר <ph name="SITE" /> משתמש בדרך כלל בהצפנה כדי להגן על המידע שלך. ×›×שר Chromium ניסה ×”×¤×¢× ×œ×”×ª×—×‘×¨ ל-<ph name="SITE" />, ×”×תר שלח חזרה ××™×©×•×¨×™× ×—×¨×™×’×™× ×•×©×’×•×™×™×. ייתכן שתוקף מנסה להתחזות ל×תר <ph name="SITE" />, ×ו שמסך כניסה ל-Wi-Fi הפריע לחיבור. המידע שלך עדיין מ×ובטח מכיוון ש-Chromium הפסיק ×ת החיבור לפני חילופי הנתוני×.</translation>
<translation id="9137013805542155359">הצג מקור</translation>
+<translation id="9137248913990643158">â€×”יכנס לחשבונך ב-Chrome לפני שתשתמש ב×פליקציה הזו.</translation>
<translation id="9148507642005240123">&amp;ביטול עריכה</translation>
<translation id="9157595877708044936">מגדיר...</translation>
<translation id="9170848237812810038">&amp;ביטול</translation>
@@ -744,7 +797,6 @@ Del</translation>
<translation id="935608979562296692">נקה ×ת הטופס</translation>
<translation id="939736085109172342">תיקייה חדשה</translation>
<translation id="941721044073577244">נר××” ש×ין לך הרש××” לבקר ב×תר ×”×–×”</translation>
-<translation id="962701380617707048">הזן ×ת ת×ריך התפוגה ו×ת קוד ×”×ימות של <ph name="CREDIT_CARD" /> כדי לעדכן ×ת פרטי הכרטיס</translation>
<translation id="969892804517981540">גירסה רשמית</translation>
<translation id="988159990683914416">גירסת מפתחי×</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 420ffd9940d..20d7fe0ffc3 100644
--- a/chromium/components/strings/components_strings_ja.xtb
+++ b/chromium/components/strings/components_strings_ja.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Wi-Fi ã«å†åº¦æŽ¥ç¶šã™ã‚‹</translation>
<translation id="1175364870820465910">å°åˆ·(&amp;P)...</translation>
<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="1206967143813997005">最åˆã®ç½²åã«å•é¡ŒãŒã‚ã‚Šã¾ã™</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">シアン</translation>
<translation id="1629803312968146339">Chrome ã«ã“ã®ã‚«ãƒ¼ãƒ‰ã‚’ä¿å­˜ã—ã¾ã™ã‹ï¼Ÿ</translation>
<translation id="1640180200866533862">ユーザー ãƒãƒªã‚·ãƒ¼</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />サイトã®ãƒ›ãƒ¼ãƒ ãƒšãƒ¼ã‚¸ã«ã‚¢ã‚¯ã‚»ã‚¹<ph name="END_LINK" />ã—ã¦ã¿ã¦ãã ã•ã„。</translation>
<translation id="1644184664548287040">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®šãŒç„¡åŠ¹ãªãŸã‚インãƒãƒ¼ãƒˆã§ãã¾ã›ã‚“ã§ã—ãŸã€‚</translation>
<translation id="1644574205037202324">履歴</translation>
<translation id="1645368109819982629">サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ãªã„プロトコルã§ã™</translation>
<translation id="1676269943528358898"><ph name="SITE" /> ã§ã¯é€šå¸¸ã€æš—å·åŒ–ã—ã¦æƒ…報をä¿è­·ã—ã¦ã„ã¾ã™ã€‚今回ã€Google Chrome ã‹ã‚‰ <ph name="SITE" /> ã¸ã®æŽ¥ç¶šè©¦è¡Œæ™‚ã«ã€ã“ã®ã‚¦ã‚§ãƒ–サイトã‹ã‚‰ã„ã¤ã‚‚ã¨ã¯ç•°ãªã‚‹èª¤ã£ãŸèªè¨¼æƒ…å ±ãŒè¿”ã•ã‚Œã¾ã—ãŸã€‚悪æ„ã®ã‚るユーザー㌠<ph name="SITE" /> ã«ãªã‚Šã™ã¾ãã†ã¨ã—ã¦ã„ã‚‹ã‹ã€Wi-Fi ログイン画é¢ã§æŽ¥ç¶šãŒä¸­æ–­ã•ã‚ŒãŸå¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚データã®ã‚„ã‚Šå–ã‚ŠãŒè¡Œã‚れるå‰ã« Google Chrome ã«ã‚ˆã£ã¦æŽ¥ç¶šãŒåœæ­¢ã•ã‚ŒãŸãŸã‚ã€æƒ…å ±ã¯å¼•ã続ãä¿è­·ã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
+<translation id="168328519870909584"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ã§ã¯ç¾åœ¨ã€æ‚ªæ„ã®ã‚るユーザーãŒã€ãŠä½¿ã„ã®ãƒ‡ãƒã‚¤ã‚¹ã«å±é™ºãªã‚¢ãƒ—リ(写真ã€ãƒ‘スワードã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カード番å·ãªã©ã‚’ç›—ã¿å–ã‚‹ã‹å‰Šé™¤ã™ã‚‹ã‚¢ãƒ—リ)をインストールã—よã†ã¨ã—ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="168841957122794586">サーãƒãƒ¼è¨¼æ˜Žæ›¸ã«è„†å¼±ãªæš—å·éµãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚</translation>
-<translation id="1701955595840307032">ãŠã™ã™ã‚コンテンツを表示</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">ã“ã®ã‚µã‚¤ãƒˆã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã«ã¯ <ph name="NAME" /> ã•ã‚“ã®è¨±å¯ãŒå¿…è¦ã§ã™</translation>
<translation id="1734864079702812349">AMEX</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">ページ番å·</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨ºæ–­ãƒ„ールを実行ã—ã¦ã¿ã¦ãã ã•ã„<ph name="END_LINK" />。</translation>
<translation id="1783075131180517613">åŒæœŸãƒ‘スフレーズを更新ã—ã¦ãã ã•ã„。</translation>
+<translation id="1787142507584202372">最近開ã„ãŸã‚¿ãƒ–ãŒã“ã“ã«è¡¨ç¤ºã•ã‚Œã¾ã™</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883"><ph name="SITE" /> ã§ã¯æœ€è¿‘ã€Google セーフ ブラウジングã«ã‚ˆã‚Šã€<ph name="BEGIN_LINK" />ä¸æ­£ãªã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ãŒæ¤œå‡º<ph name="END_LINK" />ã•ã‚Œã¾ã—ãŸã€‚通常ã¯å®‰å…¨ãªã‚¦ã‚§ãƒ–サイトã§ã‚ã£ã¦ã‚‚ã€ä¸æ­£ãªã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã«æ„ŸæŸ“ã—ã¦ã„ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚今回ã®æ‚ªæ„ã®ã‚るコンテンツã¯ã€ä¸æ­£ãªã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã®æ—¢çŸ¥ã®é…布元ã§ã‚る「<ph name="SUBRESOURCE_HOST" />ã€ã‹ã‚‰é€ã‚‰ã‚Œã¦ãã¾ã—ãŸã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">無効ãªãƒªã‚¯ã‚¨ã‚¹ãƒˆã¾ãŸã¯ãƒªã‚¯ã‚¨ã‚¹ãƒˆ パラメータã§ã™</translation>
+<translation id="1834321415901700177">ã“ã®ã‚µã‚¤ãƒˆã«ã¯æœ‰å®³ãªãƒ—ログラムãŒå«ã¾ã‚Œã¦ã„ã¾ã™</translation>
<translation id="1838667051080421715">ウェブページã®ã‚½ãƒ¼ã‚¹ã‚’表示ã—ã¦ã„ã¾ã™ã€‚</translation>
<translation id="1871208020102129563">プロキシ㯠.pac スクリプト URL ã§ã¯ãªã固定プロキシ サーãƒãƒ¼ã‚’使用ã™ã‚‹ã‚ˆã†ã«è¨­å®šã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="1883255238294161206">リストを折りãŸãŸã‚€</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">郵便番å·</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 件ã®å€™è£œ}other{# 件ã®å€™è£œ}}</translation>
<translation id="2065985942032347596">èªè¨¼ãŒå¿…è¦</translation>
-<translation id="2079545284768500474">å–り消ã™</translation>
+<translation id="2079545284768500474">å…ƒã«æˆ»ã™</translation>
<translation id="20817612488360358">システム プロキシ設定を使用ã™ã‚‹ã‚ˆã†ã«è¨­å®šã•ã‚Œã¦ã„ã¾ã™ãŒã€æ˜Žç¤ºçš„ãªãƒ—ロキシã®è¨­å®šã‚‚指定ã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="2086652334978798447">ユーザーã«åˆã‚ã›ãŸ Google ã‹ã‚‰ã®ãŠã™ã™ã‚コンテンツを表示ã™ã‚‹ã«ã¯ã€Chrome ã«ãƒ­ã‚°ã‚¤ãƒ³ã—ã¾ã™ã€‚</translation>
<translation id="2089090684895656482">一部表示</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">ãƒã‚¤ãƒ†ã‚£ãƒ– クライアント</translation>
<translation id="213826338245044447">モãƒã‚¤ãƒ«ã®ãƒ–ックマーク</translation>
<translation id="2148716181193084225">今日</translation>
-<translation id="2149973817440762519">ブックマークを編集</translation>
<translation id="2154054054215849342">ãŠä½¿ã„ã®ãƒ‰ãƒ¡ã‚¤ãƒ³ã§ã¯åŒæœŸæ©Ÿèƒ½ã‚’ã”利用ã„ãŸã ã‘ã¾ã›ã‚“</translation>
<translation id="2166049586286450108">ã™ã¹ã¦ã®ãƒ‡ãƒ¼ã‚¿ã¸ã®ç®¡ç†è€…アクセス</translation>
<translation id="2166378884831602661">ã“ã®ã‚µã‚¤ãƒˆã¯å®‰å…¨ã«æŽ¥ç¶šã§ãã¾ã›ã‚“</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836"><ph name="SITE" /> ã§ã¯ HSTS ãŒä½¿ç”¨ã•ã‚Œã¦ã„ã‚‹ãŸã‚ã€ç¾åœ¨ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“。通常ã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ エラーやä¸æ­£ãªæ“作ã¯ä¸€æ™‚çš„ãªã‚‚ã®ã§ã™ã€‚å°‘ã—時間をãŠãã¨ã€ã¾ãŸãƒšãƒ¼ã‚¸ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãるよã†ã«ãªã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">インデックス <ph name="ENTRY_INDEX" /> ã®ç„¡åŠ¹ãªãƒ–ックマークを無視ã—ã¾ã—ãŸ</translation>
<translation id="2354001756790975382">ãã®ä»–ã®ãƒ–ックマーク</translation>
+<translation id="2355395290879513365">ã“ã®ã‚µã‚¤ãƒˆã§ç›®ã«ã™ã‚‹ç”»åƒã¯ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦å·®ã—替ãˆã‚‰ã‚ŒãŸã‚‚ã®ã§ã‚ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="2359808026110333948">続行</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> ã«ä½œæˆã•ã‚ŒãŸã‚¯ãƒ©ãƒƒã‚·ãƒ¥ レãƒãƒ¼ãƒˆã¯ã‚¢ãƒƒãƒ—ロードã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ</translation>
<translation id="2367567093518048410">レベル</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">値ãŒè¨­å®šã•ã‚Œã¦ã„ãªã„ãƒãƒªã‚·ãƒ¼ã‚’表示ã™ã‚‹</translation>
<translation id="2396249848217231973">削除ã®å–り消ã—(&amp;U)</translation>
<translation id="2455981314101692989">ã“ã®ã‚¦ã‚§ãƒ–ページã§ã¯ã€ãƒ•ã‚©ãƒ¼ãƒ ã¸ã®è‡ªå‹•å…¥åŠ›ãŒç„¡åŠ¹ã«ãªã£ã¦ã„ã¾ã™ã€‚</translation>
+<translation id="2460160116472764928"><ph name="SITE" /> ã§ã¯æœ€è¿‘ã€Google セーフ ブラウジングã«ã‚ˆã‚Šã€<ph name="BEGIN_LINK" />ä¸æ­£ãªã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ãŒæ¤œå‡º<ph name="END_LINK" />ã•ã‚Œã¾ã—ãŸã€‚通常ã¯å®‰å…¨ãªã‚¦ã‚§ãƒ–サイトã§ã‚ã£ã¦ã‚‚ã€ä¸æ­£ãªã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã«æ„ŸæŸ“ã—ã¦ã„ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">入力ã™ã‚‹</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨ºæ–­ãƒ„ールを実行ã™ã‚‹<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">検索 URL ãŒç„¡åŠ¹ã§ã™ã€‚</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">履歴ã‹ã‚‰ã“れらã®ãƒšãƒ¼ã‚¸ã‚’削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ</translation>
<translation id="2677748264148917807">ã“ã®ãƒšãƒ¼ã‚¸ã‚’離れる</translation>
<translation id="269990154133806163">サーãƒãƒ¼ã‹ã‚‰æ示ã•ã‚ŒãŸè¨¼æ˜Žæ›¸ã¯ã€è¨¼æ˜Žæ›¸ã®é€æ˜Žæ€§ãƒãƒªã‚·ãƒ¼ã‚’介ã—ã¦å…¬é–‹ã•ã‚Œã¦ã„ã¾ã›ã‚“。一部ã®è¨¼æ˜Žæ›¸ã¯ã€ä¿¡é ¼æ€§ã®ç¢ºä¿ã¨æ”»æ’ƒè€…ã‹ã‚‰ã®ä¿è­·ã®ãŸã‚ã€è¨¼æ˜Žæ›¸ã®é€æ˜Žæ€§ãƒãƒªã‚·ãƒ¼ã‚’介ã—ã¦å…¬é–‹ã•ã‚Œã‚‹ã“ã¨ãŒè¦ä»¶ã¨ãªã£ã¦ã„ã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">リーディング リスト</translation>
<translation id="2704283930420550640">値ãŒæœ‰åŠ¹ãªå½¢å¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“。</translation>
<translation id="2704951214193499422">Chromium ã§ã‚«ãƒ¼ãƒ‰ã‚’確èªã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã—ã°ã‚‰ãã—ã¦ã‹ã‚‰ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。</translation>
<translation id="2705137772291741111">ã“ã®ã‚µã‚¤ãƒˆã®ä¿å­˜ï¼ˆã‚­ãƒ£ãƒƒã‚·ãƒ¥ï¼‰ã•ã‚ŒãŸã‚³ãƒ”ーを読ã¿å–ã‚Œã¾ã›ã‚“ã§ã—ãŸã€‚</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082"><ph name="DOMAIN" /> ã«ã‚¢ã‚¯ã‚»ã‚¹ã—よã†ã¨ã—ã¾ã—ãŸãŒã€ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰æ示ã•ã‚ŒãŸè¨¼æ˜Žæ›¸ã¯ç™ºè¡Œå…ƒã«ã‚ˆã‚Šå–り消ã•ã‚Œã¦ã„ã¾ã™ã€‚ã“ã‚Œã¯ã€ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰æ示ã•ã‚ŒãŸã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£èªè¨¼æƒ…å ±ãŒä¿¡é ¼ã§ããªã„ã“ã¨ã‚’示ã—ã¦ãŠã‚Šã€æ‚ªæ„ã®ã‚るユーザーã¨é€šä¿¡ã—よã†ã¨ã—ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">権é™ã‚’リクエスト</translation>
<translation id="2713444072780614174">白</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">接続用ã«è¨­å®šã•ã‚ŒãŸãƒ—ロキシã¯ã€è¨­å®šãƒšãƒ¼ã‚¸ã§ç„¡åŠ¹ã«ã§ãã¾ã™ã€‚</translation>
<translation id="2955913368246107853">検索ãƒãƒ¼ã‚’é–‰ã˜ã‚‹</translation>
<translation id="2958431318199492670">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨­å®šãŒ ONC 標準ã«æº–æ‹ ã—ã¦ã„ã¾ã›ã‚“。設定ã®ä¸€éƒ¨ãŒã‚¤ãƒ³ãƒãƒ¼ãƒˆã•ã‚Œãªã„å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
+<translation id="29611076221683977"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ã§ã¯ç¾åœ¨ã€æ‚ªæ„ã®ã‚るユーザーãŒã€ãŠä½¿ã„ã® Mac ã«å±é™ºãªãƒ—ログラム(写真ã€ãƒ‘スワードã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カード番å·ãªã©ã‚’ç›—ã¿å–ã‚‹ã‹å‰Šé™¤ã™ã‚‹ãƒ—ログラム)をインストールã—よã†ã¨ã—ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="2969319727213777354">安全ãªæŽ¥ç¶šã‚’確立ã™ã‚‹ã«ã¯ã€æ™‚計ãŒæ­£ã—ã設定ã•ã‚Œã¦ã„ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ã“ã‚Œã¯ã€ã‚¦ã‚§ãƒ–サイトãŒè‡ªèº«ã‚’証明ã™ã‚‹ãŸã‚ã«ä½¿ç”¨ã™ã‚‹è¨¼æ˜Žæ›¸ã«ã¯æœ‰åŠ¹æœŸé™ãŒã‚ã‚‹ãŸã‚ã§ã™ã€‚デãƒã‚¤ã‚¹ã®æ™‚計ãŒæ­£ã—ããªã„ãŸã‚ã€Google Chrome ã§ã“れらã®è¨¼æ˜Žæ›¸ã‚’確èªã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。</translation>
<translation id="2972581237482394796">ã‚„ã‚Šç›´ã—(&amp;R)</translation>
<translation id="2985306909656435243">有効ã«ã™ã‚‹ã¨ã€Chromium ãŒã‚«ãƒ¼ãƒ‰æƒ…報をã“ã®ç«¯æœ«ã«ä¿å­˜ã™ã‚‹ãŸã‚フォームã«ã™ã°ã‚„ã入力ã§ãるよã†ã«ãªã‚Šã¾ã™ã€‚</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">詳細をéžè¡¨ç¤º</translation>
<translation id="3587482841069643663">ã™ã¹ã¦</translation>
<translation id="3600246354004376029"><ph name="TITLE" />ã€<ph name="DOMAIN" />ã€<ph name="TIME" /></translation>
-<translation id="3609138628363401169">ã“ã®ã‚µãƒ¼ãƒãƒ¼ã§ã¯ TLS ã®å†äº¤æ¸‰æ‹¡å¼µãŒã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã›ã‚“。</translation>
<translation id="36224234498066874">閲覧履歴を消去...</translation>
<translation id="362276910939193118">全履歴を表示</translation>
<translation id="3623476034248543066">値を表示</translation>
@@ -271,19 +279,20 @@
<translation id="3655670868607891010">ã“ã®ã‚¨ãƒ©ãƒ¼ãŒé »ç¹ã«è¡¨ç¤ºã•ã‚Œã‚‹å ´åˆã¯ã€ã“ã¡ã‚‰ã®<ph name="HELP_LINK" />ã‚’ãŠè©¦ã—ãã ã•ã„。</translation>
<translation id="3658742229777143148">変更履歴</translation>
<translation id="3678029195006412963">リクエストã«ç½²åã§ãã¾ã›ã‚“ã§ã—ãŸ</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="3696411085566228381">ãªã—</translation>
<translation id="3704609568417268905"><ph name="TIME" />ã€<ph name="TITLE" />(<ph name="DOMAIN" />)を<ph name="BOOKMARKED" /></translation>
-<translation id="370665806235115550">読ã¿è¾¼ã¿ä¸­...</translation>
+<translation id="370665806235115550">読ã¿è¾¼ã‚“ã§ã„ã¾ã™...</translation>
<translation id="3712624925041724820">ライセンスを使ã„切りã¾ã—ãŸ</translation>
<translation id="3714780639079136834">モãƒã‚¤ãƒ«ãƒ‡ãƒ¼ã‚¿ã¾ãŸã¯ Wi-Fi を有効ã«ã™ã‚‹</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />プロキシã€ãƒ•ã‚¡ã‚¤ã‚¢ã‚¦ã‚©ãƒ¼ãƒ«ã€DNS ã®è¨­å®šã‚’確èªã™ã‚‹<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">å±é™ºãªãƒ—ログラムãŒå‰Šé™¤ã•ã‚Œã‚‹ã‚ˆã‚Šå‰ã«<ph name="BEGIN_LINK" />ã“ã®å®‰å…¨ã§ãªã„サイトã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹<ph name="END_LINK" />å ´åˆã¯ã€ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ä¸Šã®ãƒªã‚¹ã‚¯ã«ã¤ã„ã¦ã”承知ãŠããã ã•ã„。</translation>
<translation id="3739623965217189342">コピーã—ãŸãƒªãƒ³ã‚¯</translation>
<translation id="375403751935624634">サーãƒãƒ¼ エラーã®ãŸã‚翻訳ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚</translation>
<translation id="3759461132968374835">最近発生ã—ãŸéšœå®³ã¯ã‚ã‚Šã¾ã›ã‚“。障害レãƒãƒ¼ãƒˆãŒç„¡åŠ¹ã«ãªã£ã¦ã„ã‚‹ã¨ãã«ç™ºç”Ÿã—ãŸéšœå®³ã¯ã€ã“ã“ã«ã¯è¡¨ç¤ºã•ã‚Œã¾ã›ã‚“。</translation>
-<translation id="3788090790273268753">ã“ã®ã‚µã‚¤ãƒˆã®è¨¼æ˜Žæ›¸ã¯ 2016 å¹´ã¾ã§æœ‰åŠ¹ã§ã€è¨¼æ˜Žæ›¸ãƒã‚§ãƒ¼ãƒ³ã«ã¯ SHA-1 を使ã£ã¦ç½²åã•ã‚ŒãŸè¨¼æ˜Žæ›¸ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="382518646247711829">プロキシ サーãƒãƒ¼ã‚’使用ã—ã¦ã„ã‚‹å ´åˆ...</translation>
<translation id="3828924085048779000">パスフレーズã¯å¿…ãšæŒ‡å®šã—ã¦ãã ã•ã„。</translation>
<translation id="3845539888601087042">ログインã—ã¦ã„る端末ã®å±¥æ­´ã‚’表示ã—ã¦ã„ã¾ã™ã€‚<ph name="BEGIN_LINK" />詳細<ph name="END_LINK" /></translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">キー「<ph name="SUBKEY" />ã€: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">クライアントã¨ã‚µãƒ¼ãƒãƒ¼ã§ã€å…±é€šã® SSL プロトコル ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¾ãŸã¯æš—å·ã‚¹ã‚¤ãƒ¼ãƒˆãŒã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã›ã‚“。</translation>
<translation id="4079302484614802869">プロキシã¯å›ºå®šãƒ—ロキシ サーãƒãƒ¼ã§ã¯ãªã .pac スクリプト URL を使用ã™ã‚‹ã‚ˆã†ã«è¨­å®šã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
+<translation id="4098354747657067197">å½ã®ã‚µã‚¤ãƒˆã«ã‚¢ã‚¯ã‚»ã‚¹ã—よã†ã¨ã—ã¦ã„ã¾ã™</translation>
<translation id="4103249731201008433">デãƒã‚¤ã‚¹ã®ã‚·ãƒªã‚¢ãƒ«ç•ªå·ãŒç„¡åŠ¹ã§ã™</translation>
<translation id="4103763322291513355">&lt;strong&gt;chrome://policy&lt;/strong&gt; ã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã¦ã€ãƒ–ラックリストã«ç™»éŒ²ã•ã‚Œã¦ã„ã‚‹ URL ã¨ã‚·ã‚¹ãƒ†ãƒ ç®¡ç†è€…ãŒè¨­å®šã—ãŸä»–ã®ãƒãƒªã‚·ãƒ¼ã‚’確èªã§ãã¾ã™ã€‚</translation>
<translation id="4110615724604346410">ã“ã®ã‚µãƒ¼ãƒãƒ¼ãŒ <ph name="DOMAIN" /> ã§ã‚ã‚‹ã“ã¨ã‚’確èªã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã“ã®ã‚µãƒ¼ãƒãƒ¼ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨¼æ˜Žæ›¸ã«ã¯ã‚¨ãƒ©ãƒ¼ãŒã‚ã‚Šã¾ã™ã€‚原因ã¨ã—ã¦ã€è¨­å®šãŒä¸é©åˆ‡ã§ã‚ã‚‹ã‹ã€æ‚ªæ„ã®ã‚るユーザーãŒæŽ¥ç¶šã‚’妨害ã—ã¦ã„ã‚‹ã“ã¨ãŒè€ƒãˆã‚‰ã‚Œã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ãªã—}=1{1 個ã®ã‚¢ãƒ—リ($1)}=2{2 個ã®ã‚¢ãƒ—リ($1ã€$2)}other{# 個ã®ã‚¢ãƒ—リ($1ã€$2ã€$3)}}</translation>
<translation id="4220128509585149162">クラッシュ</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯è¨ºæ–­ãƒ„ールを実行ã—ã¦ã¿ã¦ãã ã•ã„<ph name="END_LINK" />。</translation>
+<translation id="4250431568374086873">ã“ã®ã‚µã‚¤ãƒˆã¸ã®æŽ¥ç¶šã¯å®Œå…¨ã«ã¯ä¿è­·ã•ã‚Œã¦ã„ã¾ã›ã‚“</translation>
<translation id="4250680216510889253">ã„ã„ãˆ</translation>
<translation id="425582637250725228">è¡Œã£ãŸå¤‰æ›´ãŒä¿å­˜ã•ã‚Œãªã„å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="4258748452823770588">ç½²åãŒä¸é©åˆ‡ã§ã™</translation>
<translation id="4269787794583293679">(ユーザーåãªã—)</translation>
+<translation id="4280429058323657511">ã€æœ‰åŠ¹æœŸé™: <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969"><ph name="SITE" /> ã§ã¯æœ€è¿‘ã€Google セーフ ブラウジングã«ã‚ˆã‚Š<ph name="BEGIN_LINK" />有害ãªãƒ—ログラムãŒæ¤œå‡º<ph name="END_LINK" />ã•ã‚Œã¾ã—ãŸã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">ä¿è­·è€…ã‹ã‚‰ã®ãŠã™ã™ã‚</translation>
<translation id="4304224509867189079">ログイン</translation>
<translation id="432290197980158659">サーãƒãƒ¼ã®æ示ã—ãŸè¨¼æ˜Žæ›¸ãŒã€çµ„ã¿è¾¼ã¾ã‚Œã¦ã„る想定ã®è¨¼æ˜Žæ›¸ã¨ä¸€è‡´ã—ã¾ã›ã‚“。ã“れらã®æƒ³å®šã®è¨¼æ˜Žæ›¸ã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ä¿è­·ã®ãŸã‚ã€ç‰¹å®šã®å®‰å…¨æ€§ã®é«˜ã„ウェブサイトã«ã¤ã„ã¦ç”¨æ„ã•ã‚Œã¦ã„ã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">記事ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ</translation>
+<translation id="4326324639298822553">有効期é™ã®ã€Œæ—¥ã€ã‚’確èªã—ã¦ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„</translation>
<translation id="4331708818696583467">ä¿è­·ã•ã‚Œã¦ã„ãªã„通信</translation>
+<translation id="4356973930735388585">ã“ã®ã‚µã‚¤ãƒˆã‚’利用ã™ã‚‹ã¨ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦ã€å±é™ºãªãƒ—ログラム(写真ã€ãƒ‘スワードã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カード番å·ãªã©ã®æƒ…報を盗ã¿å–ã‚‹ã‹å‰Šé™¤ã™ã‚‹ãƒ—ログラム)ãŒãŠä½¿ã„ã®ãƒ‘ソコンã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="4372948949327679948"><ph name="VALUE_TYPE" /> 値ãŒæƒ³å®šã•ã‚Œã¾ã™ã€‚</translation>
<translation id="4381091992796011497">ユーザーå:</translation>
<translation id="4394049700291259645">無効ã«ã™ã‚‹</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614"><ph name="DOMAIN" /> ã«ã‚¢ã‚¯ã‚»ã‚¹ã—よã†ã¨ã—ã¾ã—ãŸãŒã€ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ç„¡åŠ¹ãªè¨¼æ˜Žæ›¸ãŒæ示ã•ã‚Œã¾ã—ãŸã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4592951414987517459"><ph name="DOMAIN" /> ã¸ã®æŽ¥ç¶šã¯æ–°ã—ã„æš—å·ã‚¹ã‚¤ãƒ¼ãƒˆã«ã‚ˆã‚Šæš—å·åŒ–ã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="4594403342090139922">削除ã®å–り消ã—(&amp;U)</translation>
+<translation id="4619615317237390068">ä»–ã®ãƒ‡ãƒã‚¤ã‚¹ã‹ã‚‰ã®ã‚¿ãƒ–</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">拡張機能ã®ãƒšãƒ¼ã‚¸ã‚’表示ã—ã¦ã„ã¾ã™ã€‚</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">ãƒãƒªã‚·ãƒ¼ã‚’å†èª­ã¿è¾¼ã¿</translation>
<translation id="4728558894243024398">プラットフォーム</translation>
<translation id="4744603770635761495">実行ファイルã®ãƒ‘ス</translation>
+<translation id="4750917950439032686">ãŠå®¢æ§˜ãŒã“ã®ã‚µã‚¤ãƒˆã«é€ä¿¡ã—ãŸæƒ…報(パスワードã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カード番å·ãªã©ï¼‰ãŒç¬¬ä¸‰è€…ã«è¦‹ã‚‰ã‚Œã‚‹ã“ã¨ã¯ã‚ã‚Šã¾ã›ã‚“。</translation>
<translation id="4756388243121344051">履歴(&amp;H)</translation>
+<translation id="4759118997339041434">ãŠæ”¯æ‰•ã„情報ã®è‡ªå‹•å…¥åŠ›ã¯ç„¡åŠ¹ã«ãªã£ã¦ã„ã¾ã™</translation>
<translation id="4764776831041365478"><ph name="URL" /> ã®ã‚¦ã‚§ãƒ–ページã¯ä¸€æ™‚çš„ã«åœæ­¢ã—ã¦ã„ã‚‹ã‹ã€æ–°ã—ã„ウェブアドレスã«ç§»å‹•ã—ãŸå¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="4771973620359291008">ä¸æ˜Žãªã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚</translation>
<translation id="4800132727771399293">有効期é™ã¨ CVC を確èªã—ã¦ã‹ã‚‰ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ エラー</translation>
<translation id="4816492930507672669">ページサイズã«åˆã‚ã›ã‚‹</translation>
<translation id="4850886885716139402">表示</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />ã€<ph name="TYPE_2" />ã€<ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ä»– 1 件ã®ã‚¦ã‚§ãƒ–ページ}other{ä»– # 件ã®ã‚¦ã‚§ãƒ–ページ}}</translation>
<translation id="4923417429809017348">ã“ã®ãƒšãƒ¼ã‚¸ã¯ã€ä¸æ˜Žãªè¨€èªžã‹ã‚‰<ph name="LANGUAGE_LANGUAGE" />ã«ç¿»è¨³ã•ã‚Œã¾ã—ãŸã€‚</translation>
+<translation id="4923459931733593730">ãŠæ”¯æ‰•ã„</translation>
<translation id="4926049483395192435">指定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="495170559598752135">æ“作</translation>
<translation id="4958444002117714549">リストを展開ã™ã‚‹</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">ダウンロードã—ã¦ã„ã¾ã™</translation>
<translation id="5190835502935405962">ブックマーク ãƒãƒ¼</translation>
<translation id="5199729219167945352">試験é‹ç”¨æ©Ÿèƒ½</translation>
-<translation id="5199841536747119669">ãŠã™ã™ã‚ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒã“ã“ã«è¡¨ç¤ºã•ã‚Œã¾ã™</translation>
<translation id="5251803541071282808">クラウド</translation>
<translation id="5277279256032773186">会社㧠Chrome を使用ã™ã‚‹å ´åˆã¯ã€å¾“業員用㫠Chrome ã®è¨­å®šã‚’管ç†ã§ãã¾ã™ã€‚詳細</translation>
<translation id="5299298092464848405">ãƒãƒªã‚·ãƒ¼ã®è§£æžä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">障害レãƒãƒ¼ãƒˆãŒç„¡åŠ¹ã«ãªã£ã¦ã„ã¾ã™ã€‚</translation>
<translation id="5317780077021120954">ä¿å­˜</translation>
<translation id="5327248766486351172">åå‰</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ã§ã¯ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦ã€ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã‚„個人情報(パスワードã€é›»è©±ç•ªå·ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カードãªã©ï¼‰ã®å…¥åŠ›ã¨ã„ã£ãŸå±é™ºãªæ“作を行ã†ã‚ˆã†èª˜å°Žã•ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="5359637492792381994">ã“ã®ã‚µãƒ¼ãƒãƒ¼ãŒ <ph name="DOMAIN" /> ã§ã‚ã‚‹ã“ã¨ã‚’確èªã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã“ã®ã‚µãƒ¼ãƒãƒ¼ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨¼æ˜Žæ›¸ã¯ç¾åœ¨æœ‰åŠ¹ã§ã¯ã‚ã‚Šã¾ã›ã‚“。設定ãŒä¸é©åˆ‡ã‹ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦æŽ¥ç¶šãŒå¦¨å®³ã•ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">ãƒãƒªã‚·ãƒ¼è¨­å®šã‚’ä¿å­˜ã§ãã¾ã›ã‚“ã§ã—ãŸ</translation>
+<translation id="5386426401304769735">ã“ã®ã‚µã‚¤ãƒˆã®è¨¼æ˜Žæ›¸ãƒã‚§ãƒ¼ãƒ³ã«ã¯ SHA-1 を使ã£ã¦ç½²åã•ã‚ŒãŸè¨¼æ˜Žæ›¸ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="5421136146218899937">閲覧履歴データã®æ¶ˆåŽ»...</translation>
<translation id="5430298929874300616">ブックマークを削除</translation>
<translation id="5431657950005405462">ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> ã«åŸ‹ã‚è¾¼ã¾ã‚Œã¦ã„るページã®å†…容:</translation>
<translation id="5556459405103347317">å†èª­ã¿è¾¼ã¿</translation>
<translation id="5565735124758917034">有効</translation>
+<translation id="5572851009514199876">ã“ã®ã‚µã‚¤ãƒˆã¸ã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©ãŒã‚ã‚‹ã‹ã©ã†ã‹ã‚’ Chrome ã§ç¢ºèªã§ãるよã†ã«ã€Chrome ã‚’èµ·å‹•ã—ã¦ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ãã ã•ã„。</translation>
+<translation id="5580958916614886209">有効期é™ã®ã€Œæœˆã€ã‚’確èªã—ã¦ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„</translation>
<translation id="560412284261940334">管ç†ã¯ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã›ã‚“</translation>
<translation id="5610142619324316209">接続を確èªã™ã‚‹</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> ã§ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆãŒç¹°ã‚Šè¿”ã—è¡Œã‚ã‚Œã¾ã—ãŸã€‚</translation>
<translation id="5622887735448669177">ã“ã®ã‚µã‚¤ãƒˆã‚’離れã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ</translation>
<translation id="5629630648637658800">ãƒãƒªã‚·ãƒ¼è¨­å®šã‚’読ã¿è¾¼ã‚ã¾ã›ã‚“ã§ã—ãŸ</translation>
<translation id="5631439013527180824">無効ãªãƒ‡ãƒã‚¤ã‚¹ç®¡ç†ãƒˆãƒ¼ã‚¯ãƒ³ã§ã™</translation>
+<translation id="5669703222995421982">自分å‘ã‘ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を表示</translation>
+<translation id="5675650730144413517">ã“ã®ãƒšãƒ¼ã‚¸ã¯å‹•ä½œã—ã¦ã„ã¾ã›ã‚“</translation>
<translation id="5677928146339483299">ブロック</translation>
<translation id="5694783966845939798"><ph name="DOMAIN" /> ã«ã‚¢ã‚¯ã‚»ã‚¹ã—よã†ã¨ã—ã¾ã—ãŸãŒã€ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰æ示ã•ã‚ŒãŸè¨¼æ˜Žæ›¸ãŒè„†å¼±ãªç½²åアルゴリズム(SHA-1 ãªã©ï¼‰ã‚’使用ã—ã¦ç½²åã•ã‚Œã¦ã„ã¾ã™ã€‚サーãƒãƒ¼ã‹ã‚‰æ示ã•ã‚ŒãŸã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£èªè¨¼æƒ…å ±ã¯å½è£…ã•ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã€ã‚¢ã‚¯ã‚»ã‚¹ã—よã†ã¨ã—ãŸã‚µãƒ¼ãƒãƒ¼ã¨ã¯ç•°ãªã‚‹ã‚µãƒ¼ãƒãƒ¼ã§ã‚ã‚‹ãŠãã‚ŒãŒã‚ã‚Šã¾ã™ï¼ˆæ‚ªæ„ã®ã‚るユーザーã¨é€šä¿¡ã—ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ï¼‰ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">ã“ã®ã‚¦ã‚§ãƒ–サイト㮠ID ã¯ç¢ºèªã•ã‚Œã¦ã„ã¾ã›ã‚“。</translation>
<translation id="5720705177508910913">ç¾åœ¨ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼</translation>
-<translation id="572328651809341494">最近使ã£ãŸã‚¿ãƒ–</translation>
<translation id="5732392974455271431">ブロックã®è§£é™¤ã¯ä¿è­·è€…ãŒè¡Œã†ã“ã¨ãŒã§ãã¾ã™</translation>
<translation id="5784606427469807560">カードã®ç¢ºèªä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚インターãƒãƒƒãƒˆæŽ¥ç¶šã‚’確èªã—ã¦ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。</translation>
<translation id="5785756445106461925">加ãˆã¦ã€ã“ã®ãƒšãƒ¼ã‚¸ã«ã¯å®‰å…¨ã§ãªã„ä»–ã®ãƒªã‚½ãƒ¼ã‚¹ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ã“ã®ãƒªã‚½ãƒ¼ã‚¹ã¯é€ä¿¡ä¸­ã«ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰è¦‹ã‚‰ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ã¾ãŸã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦æ”¹å¤‰ã•ã‚Œãƒšãƒ¼ã‚¸ã®è¦‹ãŸç›®ãŒå¤‰ã‚ã‚‹å¯èƒ½æ€§ã‚‚ã‚ã‚Šã¾ã™ã€‚</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569"><ph name="DOMAIN" /> ã«ã‚¢ã‚¯ã‚»ã‚¹ã—よã†ã¨ã—ã¾ã—ãŸãŒã€ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰æ示ã•ã‚ŒãŸè¨¼æ˜Žæ›¸ã«ã¯è„†å¼±ãªæš—å·éµãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚悪æ„ã®ã‚るユーザーã«ã‚ˆã£ã¦ç§˜å¯†éµãŒç ´ã‚‰ã‚ŒãŸå¯èƒ½æ€§ãŒã‚ã‚Šã€ã‚¢ã‚¯ã‚»ã‚¹ã—よã†ã¨ã—ãŸã‚µãƒ¼ãƒãƒ¼ã¨ã¯ç•°ãªã‚‹ã‚µãƒ¼ãƒãƒ¼ã§ã‚ã‚‹ãŠãã‚ŒãŒã‚ã‚Šã¾ã™ï¼ˆæ‚ªæ„ã®ã‚るユーザーã¨é€šä¿¡ã—ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ï¼‰ã€‚<ph name="BEGIN_LEARN_MORE_LINK" /><ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">æ–°ã—ã„フォルダ</translation>
<translation id="5869405914158311789">ã“ã®ã‚µã‚¤ãƒˆã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">ã“ã®è²©å£²è€…ã«å¯¾ã—ã€ã“ã®ç¨®é¡žã®ã‚«ãƒ¼ãƒ‰ã¯ Google ペイメントã§ã”利用ã„ãŸã ã‘ã¾ã›ã‚“。別ã®ã‚«ãƒ¼ãƒ‰ã‚’é¸æŠžã—ã¦ãã ã•ã„。</translation>
<translation id="59174027418879706">有効</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="5966707198760109579">週</translation>
<translation id="5967867314010545767">履歴ã‹ã‚‰å‰Šé™¤</translation>
<translation id="5975083100439434680">縮å°ã™ã‚‹</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">次をãŠè©¦ã—ãã ã•ã„:</translation>
<translation id="6151417162996330722">サーãƒãƒ¼è¨¼æ˜Žæ›¸ã®æœ‰åŠ¹æœŸé™ãŒé•·ã™ãŽã¾ã™ã€‚</translation>
<translation id="6165508094623778733">詳ã—ã見る</translation>
+<translation id="6177128806592000436">ã“ã®ã‚µã‚¤ãƒˆã¸ã®æŽ¥ç¶šã¯ä¿è­·ã•ã‚Œã¦ã„ã¾ã›ã‚“</translation>
<translation id="6203231073485539293">インターãƒãƒƒãƒˆæŽ¥ç¶šã‚’確èªã—ã¦ãã ã•ã„</translation>
<translation id="6218753634732582820">Chromium ã‹ã‚‰ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ</translation>
+<translation id="6251924700383757765">プライãƒã‚·ãƒ¼ ãƒãƒªã‚·ãƒ¼</translation>
+<translation id="625755898061068298">ã“ã®ã‚µã‚¤ãƒˆã«é–¢ã™ã‚‹ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è­¦å‘Šã‚’無効ã«ã™ã‚‹ã‚ˆã†ã«æŒ‡å®šã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="6259156558325130047">é †åºå¤‰æ›´ã®ã‚„ã‚Šç›´ã—(&amp;R)</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> ã®ãƒ–ックマーク</translation>
<translation id="6264485186158353794">セキュリティã§ä¿è­·ã•ã‚ŒãŸãƒšãƒ¼ã‚¸ã«æˆ»ã‚‹</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“。</translation>
<translation id="6321917430147971392">DNS 設定を確èªã—ã¦ãã ã•ã„</translation>
<translation id="6328639280570009161">ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯äºˆæ¸¬ã‚’無効ã«ã—ã¦ã¿ã¦ãã ã•ã„</translation>
+<translation id="6328786501058569169">ã“ã‚Œã¯å½ã®ã‚µã‚¤ãƒˆã§ã™</translation>
<translation id="6337534724793800597">ãƒãƒªã‚·ãƒ¼ã‚’åå‰ã§ãƒ•ã‚£ãƒ«ã‚¿</translation>
<translation id="6342069812937806050">ãŸã£ãŸä»Š</translation>
<translation id="6345221851280129312">ä¸æ˜Žãªã‚µã‚¤ã‚º</translation>
@@ -489,6 +519,8 @@
<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="6404511346730675251">ブックマークを編集</translation>
+<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> ã®æœ‰åŠ¹æœŸé™ã¨ CVC を入力</translation>
<translation id="6414888972213066896">ã“ã®ã‚µã‚¤ãƒˆã‚’é–‹ã„ã¦ã‚‚よã„ã‹ã®å•ã„åˆã‚ã›ã‚’ä¿è­·è€…ã«é€ä¿¡ã—ã¾ã—ãŸ</translation>
<translation id="6416403317709441254"><ph name="SITE" /> ã‹ã‚‰é€ä¿¡ã•ã‚ŒãŸæš—å·åŒ–済ã¿ã®èªè¨¼æƒ…報を Chromium ã§å‡¦ç†ã§ããªã„ãŸã‚ã€ç¾åœ¨ã“ã®ã‚¦ã‚§ãƒ–サイトã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“。通常ã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ エラーやä¸æ­£ãªæ“作ã¯ä¸€æ™‚çš„ãªã‚‚ã®ã§ã™ã€‚å°‘ã—時間をãŠãã¨ã€ã¾ãŸãƒšãƒ¼ã‚¸ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãるよã†ã«ãªã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">証明書ãŒå–り消ã•ã‚ŒãŸã‹ã©ã†ã‹ã‚’確èªã§ãã¾ã›ã‚“。</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">ãƒãƒªã‚·ãƒ¼ã«ã‚ˆã£ã¦ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®æ¤œç´¢ãŒç„¡åŠ¹ã«ã•ã‚Œã¦ã„ã‚‹ãŸã‚無視ã•ã‚Œã¾ã™ã€‚</translation>
<translation id="6462969404041126431">ã“ã®ã‚µãƒ¼ãƒãƒ¼ãŒ <ph name="DOMAIN" /> ã§ã‚ã‚‹ã“ã¨ã‚’確èªã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã“ã®ã‚µãƒ¼ãƒãƒ¼ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨¼æ˜Žæ›¸ã¯å–り消ã•ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚原因ã¨ã—ã¦ã€è¨­å®šãŒä¸é©åˆ‡ã§ã‚ã‚‹ã‹ã€æ‚ªæ„ã®ã‚るユーザーãŒæŽ¥ç¶šã‚’妨害ã—ã¦ã„ã‚‹ã“ã¨ãŒè€ƒãˆã‚‰ã‚Œã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="647261751007945333">デãƒã‚¤ã‚¹ ãƒãƒªã‚·ãƒ¼</translation>
+<translation id="6477321094435799029">ã“ã®ãƒšãƒ¼ã‚¸ã§é€šå¸¸ã¨ç•°ãªã‚‹ã‚³ãƒ¼ãƒ‰ã‚’検出ã—ãŸãŸã‚ã€å€‹äººæƒ…報(例: パスワードã€é›»è©±ç•ªå·ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カード番å·ï¼‰ã‚’ä¿è­·ã™ã‚‹ãŸã‚ã«ã€ãƒšãƒ¼ã‚¸ã‚’ブロックã—ã¾ã—ãŸã€‚</translation>
<translation id="6489534406876378309">クラッシュã®ã‚¢ãƒƒãƒ—ロードを開始</translation>
<translation id="6529602333819889595">削除ã®ã‚„ã‚Šç›´ã—(&amp;R)</translation>
<translation id="6534179046333460208">フィジカル ウェブã‹ã‚‰ã® URL</translation>
<translation id="6550675742724504774">オプション</translation>
+<translation id="6556239504065605927">ä¿è­·ã•ã‚ŒãŸæŽ¥ç¶š</translation>
<translation id="6563469144985748109">管ç†è€…ãŒã¾ã ã‚µã‚¤ãƒˆã‚’é–‹ãã“ã¨ã‚’許å¯ã—ã¦ã„ã¾ã›ã‚“</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" /> 未満</translation>
<translation id="6596325263575161958">æš—å·åŒ–オプション</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">ã“ã®ãƒãƒªã‚·ãƒ¼ã¯å»ƒæ­¢ã•ã‚Œã¾ã—ãŸã€‚</translation>
<translation id="6652240803263749613">ã“ã®ã‚µãƒ¼ãƒãƒ¼ãŒ <ph name="DOMAIN" /> ã§ã‚ã‚‹ã“ã¨ã‚’確èªã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã“ã®ã‚µãƒ¼ãƒãƒ¼ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨¼æ˜Žæ›¸ã¯ã€ã”使用ã®ãƒ‘ソコンã®ã‚ªãƒšãƒ¬ãƒ¼ãƒ†ã‚£ãƒ³ã‚° システムã«ã‚ˆã£ã¦ä¿¡é ¼ã•ã‚Œã¦ã„ã‚‹ã‚‚ã®ã§ã¯ã‚ã‚Šã¾ã›ã‚“。原因ã¨ã—ã¦ã€è¨­å®šãŒä¸é©åˆ‡ã§ã‚ã‚‹ã‹ã€æ‚ªæ„ã®ã‚るユーザーãŒæŽ¥ç¶šã‚’妨害ã—ã¦ã„ã‚‹ã“ã¨ãŒè€ƒãˆã‚‰ã‚Œã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">フォルダã®ç·¨é›†</translation>
-<translation id="6660210980321319655"><ph name="CRASH_TIME" /> ã«ãƒ¬ãƒãƒ¼ãƒˆã‚’自動é€ä¿¡ã—ã¾ã—ãŸ</translation>
<translation id="6671697161687535275">Chromium ã‹ã‚‰å€™è£œã‚’削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ</translation>
<translation id="6685834062052613830">ログアウトã—ã¦è¨­å®šã‚’完了ã—ã¦ãã ã•ã„</translation>
<translation id="6710213216561001401">å‰ã¸</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">ãƒãƒªã‚·ãƒ¼ã®å€¤</translation>
<translation id="6757797048963528358">デãƒã‚¤ã‚¹ãŒã‚¹ãƒªãƒ¼ãƒ—状態ã§ã™ã€‚</translation>
<translation id="6778737459546443941">ä¿è­·è€…ãŒã¾ã ã‚µã‚¤ãƒˆã‚’é–‹ãã“ã¨ã‚’許å¯ã—ã¦ã„ã¾ã›ã‚“</translation>
+<translation id="6810899417690483278">カスタム ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">ウェブページ(<ph name="URL" />)ã¯ç¾åœ¨è¡¨ç¤ºã§ãã¾ã›ã‚“。アクセスãŒé›†ä¸­ã—ã¦ã„ã‚‹ã‹ã€ãƒ¡ãƒ³ãƒ†ãƒŠãƒ³ã‚¹ã®ãŸã‚ã«åœæ­¢ã—ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="6831043979455480757">翻訳</translation>
@@ -542,6 +576,8 @@
<translation id="7012363358306927923">中国銀è¯</translation>
<translation id="7012372675181957985">Google アカウントã§ã®ä»–ã®å½¢å¼ã®é–²è¦§å±¥æ­´ãŒ <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> ã«æ®‹ã‚‹ã“ã¨ãŒã‚ã‚Šã¾ã™</translation>
<translation id="7029809446516969842">パスワード</translation>
+<translation id="7064851114919012435">連絡先情報</translation>
+<translation id="7079718277001814089">ã“ã®ã‚µã‚¤ãƒˆã«ã¯ä¸æ­£ãªã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ãŒå«ã¾ã‚Œã¦ã„ã¾ã™</translation>
<translation id="7087282848513945231">郡</translation>
<translation id="7088615885725309056">å¤ã„</translation>
<translation id="7090678807593890770"><ph name="LINK" /> ã‚’ Google ã§æ¤œç´¢ã—ã¦ãã ã•ã„</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> ã§ã¯ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¦æ ¼ãŒéµå®ˆã•ã‚Œã¦ã„ã¾ã›ã‚“。</translation>
<translation id="721197778055552897">ã“ã®å•é¡Œã®è©³ç´°ã«ã¤ã„ã¦ã¯ã€<ph name="BEGIN_LINK" />ã“ã¡ã‚‰<ph name="END_LINK" />ã‚’ã”覧ãã ã•ã„。</translation>
<translation id="7219179957768738017">ã“ã®æŽ¥ç¶šã«ã¯ <ph name="SSL_VERSION" /> を使用ã—ã¦ã„ã¾ã™ã€‚</translation>
+<translation id="724691107663265825">アクセス先ã®ã‚µã‚¤ãƒˆã§ä¸æ­£ãªã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã‚’検出ã—ã¾ã—ãŸ</translation>
<translation id="724975217298816891">カードã®è©³ç´°ã‚’æ›´æ–°ã™ã‚‹ã«ã¯ <ph name="CREDIT_CARD" /> ã®æœ‰åŠ¹æœŸé™ã¨ CVC を入力ã—ã¾ã™ã€‚確èªã‚’è¡Œã†ã¨ã€ã‚«ãƒ¼ãƒ‰ã®è©³ç´°ãŒã“ã®ã‚µã‚¤ãƒˆã¨å…±æœ‰ã•ã‚Œã¾ã™ã€‚</translation>
<translation id="725866823122871198">パソコンã®æ—¥æ™‚(<ph name="DATE_AND_TIME" />)ãŒæ­£ã—ããªã„ãŸã‚ã€<ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ã¸ã®ãƒ—ライベート接続を確立ã§ãã¾ã›ã‚“。</translation>
<translation id="7269802741830436641">ã“ã®ã‚¦ã‚§ãƒ–ページã«ã¯ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆ ループãŒå«ã¾ã‚Œã¦ã„ã¾ã™</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">キャンセル</translation>
<translation id="7667346355482952095">è¿”ã•ã‚ŒãŸãƒãƒªã‚·ãƒ¼ã®ãƒˆãƒ¼ã‚¯ãƒ³ãŒç©ºã§ã‚ã‚‹ã‹ã€ç¾åœ¨ã®ãƒˆãƒ¼ã‚¯ãƒ³ã¨ä¸€è‡´ã—ã¾ã›ã‚“</translation>
<translation id="7668654391829183341">ä¸æ˜Žãªãƒ‡ãƒã‚¤ã‚¹</translation>
+<translation id="7669271284792375604">ã“ã®ã‚µã‚¤ãƒˆã‚’利用ã™ã‚‹ã¨ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦ã€é–²è¦§æ™‚ã®ã‚¨ã‚¯ã‚¹ãƒšãƒªã‚¨ãƒ³ã‚¹ã‚’æãªã†ãƒ—ログラム(ホームページを改ã–ã‚“ã™ã‚‹ã€ã‚¢ã‚¯ã‚»ã‚¹å…ˆã®ã‚µã‚¤ãƒˆã«è¿½åŠ ã®åºƒå‘Šã‚’表示ã™ã‚‹ãªã©ã®ãƒ—ログラム)をインストールã™ã‚‹ã‚ˆã†èª˜å°Žã•ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="7674629440242451245">Chrome ã®æ–°ã—ã„機能ã«é–¢å¿ƒã‚’ãŠæŒã¡ã§ã—ãŸã‚‰ã€chrome.com/dev ã‹ã‚‰ Dev ãƒãƒ£ãƒ³ãƒãƒ«ã‚’ãŠè©¦ã—ãã ã•ã„。</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /><ph name="SITE" /> ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ï¼ˆå®‰å…¨ã§ã¯ã‚ã‚Šã¾ã›ã‚“)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">ã“ã®ã‚µã‚¤ãƒˆã‚’キャッシュã‹ã‚‰èª­ã¿è¾¼ã‚€ã“ã¨ãŒã§ãã¾ã›ã‚“</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">ã“ã®æŽ¥ç¶šã¯ <ph name="CIPHER" /> ã§æš—å·åŒ–ã•ã‚Œã¦ãŠã‚Šã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸èªè¨¼ã«ã¯ <ph name="MAC" />ã€éµäº¤æ›ãƒ¡ã‚«ãƒ‹ã‚ºãƒ ã«ã¯ <ph name="KX" /> ãŒä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã™ã€‚</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="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="7887683347370398519">CVC を確èªã—ã¦ã‹ã‚‰ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„</translation>
<translation id="7912024687060120840">フォルダã®å†…容:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">サーãƒãƒ¼ã®è¨¼æ˜Žæ›¸ãŒæœ‰åŠ¹ã«ãªã£ã¦ã„ã¾ã›ã‚“。</translation>
<translation id="7942349550061667556">赤</translation>
+<translation id="7947285636476623132">有効期é™ã®ã€Œå¹´ã€ã‚’確èªã—ã¦ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„</translation>
<translation id="7951415247503192394">(32 ビット)</translation>
<translation id="7956713633345437162">モãƒã‚¤ãƒ«ã®ãƒ–ックマーク</translation>
<translation id="7961015016161918242">使用ã—ãªã„</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">常ã«<ph name="ORIGINAL_LANGUAGE" />ã‹ã‚‰<ph name="TARGET_LANGUAGE" />ã«ç¿»è¨³ã™ã‚‹</translation>
<translation id="7995512525968007366">指定ãªã—</translation>
<translation id="8012647001091218357">ç¾åœ¨ã€ä¿è­·è€…ã«ãŸãšã­ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。もã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。</translation>
+<translation id="8025119109950072390">ã“ã®ã‚µã‚¤ãƒˆã‚’利用ã™ã‚‹ã¨ã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦ã€ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã‚„個人情報(例: パスワードã€é›»è©±ç•ªå·ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カード番å·ï¼‰ã®å…¥åŠ›ãªã©ã®å±é™ºãªæ“作を行ã†ã‚ˆã†èª˜å°Žã•ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
+<translation id="803030522067524905"><ph name="SITE" /> ã§ã¯æœ€è¿‘ã€Google セーフ ブラウジングã«ã‚ˆã‚Šã€ãƒ•ã‚£ãƒƒã‚·ãƒ³ã‚°è¡Œç‚ºãŒæ¤œå‡ºã•ã‚Œã¾ã—ãŸã€‚フィッシング サイトã¯ã€ä»–ã®ã‚¦ã‚§ãƒ–サイトã«ãªã‚Šã™ã¾ã—ã¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’欺ã“ã†ã¨ã™ã‚‹ã‚µã‚¤ãƒˆã§ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">ã“ã®ãƒšãƒ¼ã‚¸ã®è¨€èªžã¯<ph name="SOURCE_LANGUAGE" />ã§ã™ã€‚<ph name="TARGET_LANGUAGE" />ã«ç¿»è¨³ã—ã¾ã™ã‹ï¼Ÿ</translation>
+<translation id="8041089156583427627">フィードãƒãƒƒã‚¯ã‚’é€ä¿¡</translation>
<translation id="8088680233425245692">記事を表示ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚</translation>
<translation id="8089520772729574115">1 MB 未満</translation>
<translation id="8091372947890762290">サーãƒãƒ¼ã§æœ‰åŠ¹åŒ–ãŒä¿ç•™ã«ãªã£ã¦ã„ã¾ã™</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719"><ph name="URL" /> ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’読むã“ã¨ãŒã§ãã¾ã›ã‚“。削除ã•ã‚ŒãŸã‹ç§»å‹•ã•ã‚ŒãŸå¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ファイルã«å¯¾ã™ã‚‹ã‚¢ã‚¯ã‚»ã‚¹æ¨©ãŒãªã„å ´åˆã‚‚ã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="8194797478851900357">移動ã®å–り消ã—(&amp;U)</translation>
<translation id="8201077131113104583">ID「<ph name="EXTENSION_ID" />ã€ã®æ‹¡å¼µæ©Ÿèƒ½ã«å¯¾ã™ã‚‹ç„¡åŠ¹ãªæ›´æ–° URL ã§ã™ã€‚</translation>
+<translation id="8202097416529803614">ã”注文概è¦</translation>
<translation id="8218327578424803826">割り当ã¦ã‚‰ã‚ŒãŸå ´æ‰€:</translation>
<translation id="8225771182978767009">ã“ã®ã‚µã‚¤ãƒˆã¯ã€ã“ã®ãƒ‘ソコンを設定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã‚ˆã£ã¦ãƒ–ロックã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />ã€<ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ã§ã¯ç¾åœ¨ã€æ‚ªæ„ã®ã‚るユーザーãŒã€ãŠä½¿ã„ã®ãƒ‘ソコンã«å±é™ºãªãƒ—ログラム(写真ã€ãƒ‘スワードã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã€ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆ カード番å·ãªã©ã‚’ç›—ã¿å–ã‚‹ã‹å‰Šé™¤ã™ã‚‹ãƒ—ログラム)をインストールã—よã†ã¨ã—ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="8241707690549784388">検索ã—ã¦ã„るページã¯ã€å…¥åŠ›ã—ãŸæƒ…報を使用ã—ã¦ã„ã¾ã™ã€‚ã“ã®ãƒšãƒ¼ã‚¸ã«æˆ»ã£ãŸå ´åˆã€æ“作ã®ã‚„ã‚Šç›´ã—ãŒç™ºç”Ÿã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚続行ã—ã¾ã™ã‹ï¼Ÿ</translation>
<translation id="8249320324621329438">å‰å›žã®å–å¾—:</translation>
<translation id="8261506727792406068">削除</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">ソース</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="8349305172487531364">ブックマーク ãƒãƒ¼</translation>
<translation id="8363502534493474904">機内モードをオフã«ã™ã‚‹</translation>
<translation id="8364627913115013041">未設定</translation>
<translation id="8380941800586852976">å±é™º</translation>
<translation id="8382348898565613901">最近アクセスã—ãŸãƒ–ックマークãŒã“ã“ã«è¡¨ç¤ºã•ã‚Œã¾ã™</translation>
+<translation id="8398259832188219207">クラッシュ レãƒãƒ¼ãƒˆãŒ <ph name="UPLOAD_TIME" /> ã«ã‚¢ãƒƒãƒ—ロードã•ã‚Œã¾ã—ãŸ</translation>
<translation id="8412145213513410671">障害数(<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">åŒã˜ãƒ‘スフレーズを 2 回入力ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="8428213095426709021">設定</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ã‹ã‚‰ã®å¿œç­”時間ãŒé•·ã™ãŽã¾ã™ã€‚</translation>
<translation id="852346902619691059">ã“ã®ã‚µãƒ¼ãƒãƒ¼ãŒ <ph name="DOMAIN" /> ã§ã‚ã‚‹ã“ã¨ã‚’確èªã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã“ã®ã‚µãƒ¼ãƒãƒ¼ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨¼æ˜Žæ›¸ã¯ã€ã”使用ã®ãƒ‡ãƒã‚¤ã‚¹ã®ã‚ªãƒšãƒ¬ãƒ¼ãƒ†ã‚£ãƒ³ã‚° システムã«ã‚ˆã£ã¦ä¿¡é ¼ã•ã‚Œã¦ã„ã‚‹ã‚‚ã®ã§ã¯ã‚ã‚Šã¾ã›ã‚“。原因ã¨ã—ã¦ã€è¨­å®šãŒä¸é©åˆ‡ã§ã‚ã‚‹ã‹ã€æ‚ªæ„ã®ã‚るユーザーãŒæŽ¥ç¶šã‚’妨害ã—ã¦ã„ã‚‹ã“ã¨ãŒè€ƒãˆã‚‰ã‚Œã¾ã™ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />詳細<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">ã“ã®ç¨®é¡žã®ã‚«ãƒ¼ãƒ‰ã¯ Google ペイメントã§ã”利用ã„ãŸã ã‘ã¾ã›ã‚“。別ã®ã‚«ãƒ¼ãƒ‰ã‚’é¸æŠžã—ã¦ãã ã•ã„。</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="8553075262323480129">ページã®è¨€èªžã‚’検出ã§ããªã„ãŸã‚翻訳ã§ãã¾ã›ã‚“。</translation>
<translation id="8559762987265718583">デãƒã‚¤ã‚¹ã®æ—¥æ™‚(<ph name="DATE_AND_TIME" />)ãŒæ­£ã—ããªã„ãŸã‚ã€<ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ã¸ã®ãƒ—ライベート接続を確立ã§ãã¾ã›ã‚“。</translation>
-<translation id="856992080682148">ã“ã®ã‚µã‚¤ãƒˆã®è¨¼æ˜Žæ›¸ã¯ 2017 å¹´ã¾ãŸã¯ãれ以é™ã¾ã§æœ‰åŠ¹ã§ã€è¨¼æ˜Žæ›¸ãƒã‚§ãƒ¼ãƒ³ã«ã¯ SHA-1 を使ã£ã¦ç½²åã•ã‚ŒãŸè¨¼æ˜Žæ›¸ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="8571890674111243710">ページを<ph name="LANGUAGE" />ã«ç¿»è¨³ã—ã¦ã„ã¾ã™...</translation>
<translation id="859285277496340001">ã“ã®è¨¼æ˜Žæ›¸ã«ã¯ã€å–り消ã•ã‚ŒãŸã‹ã©ã†ã‹ã‚’確èªã™ã‚‹æ–¹æ³•ãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。</translation>
<translation id="8620436878122366504">ä¿è­·è€…ãŒã¾ã ã‚µã‚¤ãƒˆã‚’é–‹ãã“ã¨ã‚’許å¯ã—ã¦ã„ã¾ã›ã‚“</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522"><ph name="HOST_NAME" /> ã® &lt;abbr id="dnsDefinition"&gt;DNS アドレス&lt;/abbr&gt;ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚å•é¡Œã‚’診断ã—ã¦ã„ã¾ã™ã€‚</translation>
<translation id="8790007591277257123">削除ã®ã‚„ã‚Šç›´ã—(&amp;R)</translation>
<translation id="8798099450830957504">既定</translation>
+<translation id="8800988563907321413">周辺ã®ãŠã™ã™ã‚ã®å ´æ‰€ãŒã“ã“ã«è¡¨ç¤ºã•ã‚Œã¾ã™</translation>
<translation id="8804164990146287819">プライãƒã‚·ãƒ¼ ãƒãƒªã‚·ãƒ¼</translation>
<translation id="8820817407110198400">ブックマーク</translation>
<translation id="8834246243508017242">連絡先ã‹ã‚‰ã®è‡ªå‹•å…¥åŠ›ã‚’有効ã«ã™ã‚‹...</translation>
<translation id="883848425547221593">ãã®ä»–ã®ãƒ–ックマーク</translation>
+<translation id="884264119367021077">é…é€å…ˆä½æ‰€</translation>
<translation id="884923133447025588">å–り消ã—機構ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。</translation>
<translation id="885730110891505394">Google ã¨ã®å…±æœ‰</translation>
<translation id="8866481888320382733">ãƒãƒªã‚·ãƒ¼è¨­å®šã®è§£æžä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">サーãƒãƒ¼ã®è¨¼æ˜Žæ›¸ã®æœ‰åŠ¹æœŸé™ãŒåˆ‡ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="8987927404178983737">月</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">ã“ã®å…ˆã®ã‚µã‚¤ãƒˆã«ã¯æœ‰å®³ãªãƒ—ログラムãŒã‚ã‚Šã¾ã™</translation>
<translation id="9001074447101275817">プロキシ <ph name="DOMAIN" /> ã«ã¯ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¨ãƒ‘スワードを指定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚</translation>
<translation id="901974403500617787">システム全体ã«é©ç”¨ã•ã‚Œã‚‹ãƒ•ãƒ©ã‚°ã¯æ‰€æœ‰è€…(<ph name="OWNER_EMAIL" />)ã®ã¿ãŒè¨­å®šã§ãã¾ã™ã€‚</translation>
<translation id="9020542370529661692">ã“ã®ãƒšãƒ¼ã‚¸ã¯<ph name="TARGET_LANGUAGE" />ã«ç¿»è¨³ã•ã‚Œã¦ã„ã¾ã™</translation>
+<translation id="9035022520814077154">セキュリティ エラー</translation>
<translation id="9038649477754266430">予測サービスを使用ã—ã¦ãƒšãƒ¼ã‚¸ã‚’より迅速ã«èª­ã¿è¾¼ã‚€</translation>
<translation id="9039213469156557790">加ãˆã¦ã€ã“ã®ãƒšãƒ¼ã‚¸ã«ã¯å®‰å…¨ã§ãªã„ä»–ã®ãƒªã‚½ãƒ¼ã‚¹ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚ã“ã®ãƒªã‚½ãƒ¼ã‚¹ã¯é€ä¿¡ä¸­ã«ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰è¦‹ã‚‰ã‚Œã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚ã¾ãŸã€æ‚ªæ„ã®ã‚るユーザーã«ã‚ˆã£ã¦æ”¹å¤‰ã•ã‚Œãƒšãƒ¼ã‚¸ã®å‹•ä½œãŒå¤‰ã‚ã‚‹å¯èƒ½æ€§ã‚‚ã‚ã‚Šã¾ã™ã€‚</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ã®æ”»æ’ƒè€…ãŒã€é–²è¦§ç’°å¢ƒã‚’æãªã†ãƒ—ログラムをインストールã•ã›ã‚ˆã†ã¨ã—ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ï¼ˆãƒ›ãƒ¼ãƒ ãƒšãƒ¼ã‚¸ã‚’改ã–ã‚“ã™ã‚‹ã€ã‚¢ã‚¯ã‚»ã‚¹å…ˆã®ã‚µã‚¤ãƒˆã«è¿½åŠ ã®åºƒå‘Šã‚’表示ã™ã‚‹ãªã©ï¼‰ã€‚</translation>
<translation id="9050666287014529139">パスフレーズ</translation>
<translation id="9065203028668620118">編集</translation>
<translation id="9068849894565669697">色ã®é¸æŠž</translation>
<translation id="9076283476770535406">æˆäººå‘ã‘コンテンツãŒå«ã¾ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> ページã¯æ©Ÿèƒ½ã—ã¦ã„ã¾ã›ã‚“</translation>
<translation id="9103872766612412690"><ph name="SITE" /> ã§ã¯é€šå¸¸ã€æš—å·åŒ–ã—ã¦æƒ…報をä¿è­·ã—ã¦ã„ã¾ã™ã€‚今回ã€Chromium ã‹ã‚‰ <ph name="SITE" /> ã¸ã®æŽ¥ç¶šè©¦è¡Œæ™‚ã«ã€ã“ã®ã‚¦ã‚§ãƒ–サイトã‹ã‚‰ã„ã¤ã‚‚ã¨ã¯ç•°ãªã‚‹èª¤ã£ãŸèªè¨¼æƒ…å ±ãŒè¿”ã•ã‚Œã¾ã—ãŸã€‚悪æ„ã®ã‚るユーザー㌠<ph name="SITE" /> ã«ãªã‚Šã™ã¾ãã†ã¨ã—ã¦ã„ã‚‹ã‹ã€Wi-Fi ログイン画é¢ã§æŽ¥ç¶šãŒä¸­æ–­ã•ã‚ŒãŸå¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚データã®ã‚„ã‚Šå–ã‚ŠãŒè¡Œã‚れるå‰ã« Chromium ã«ã‚ˆã£ã¦æŽ¥ç¶šãŒåœæ­¢ã•ã‚ŒãŸãŸã‚ã€æƒ…å ±ã¯å¼•ã続ãä¿è­·ã•ã‚Œã¦ã„ã¾ã™ã€‚</translation>
<translation id="9137013805542155359">原文ã®ãƒšãƒ¼ã‚¸ã‚’表示</translation>
+<translation id="9137248913990643158">ã“ã®ã‚¢ãƒ—リを使用ã™ã‚‹ã«ã¯ã€Chrome ã‚’èµ·å‹•ã—ã¦ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ãã ã•ã„。</translation>
<translation id="9148507642005240123">編集ã®å–り消ã—(&amp;U)</translation>
<translation id="9157595877708044936">セットアップ中...</translation>
<translation id="9170848237812810038">å–消(&amp;U)</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">フォームをクリア</translation>
<translation id="939736085109172342">æ–°ã—ã„フォルダ</translation>
<translation id="941721044073577244">ã“ã®ã‚µã‚¤ãƒˆã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ãŒè¨±å¯ã•ã‚Œã¦ã„ãªã„よã†ã§ã™</translation>
-<translation id="962701380617707048">カードã®è©³ç´°ã‚’æ›´æ–°ã™ã‚‹ã«ã¯ <ph name="CREDIT_CARD" /> ã®æœ‰åŠ¹æœŸé™ã¨ CVC を入力ã—ã¾ã™</translation>
<translation id="969892804517981540">Official Build</translation>
<translation id="988159990683914416">Developer Build</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 9df13870c9d..bdc6a1875dd 100644
--- a/chromium/components/strings/components_strings_kn.xtb
+++ b/chromium/components/strings/components_strings_kn.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">ವೈ-ಫೈಗೆ ಮರà³à²¸à²‚ಪರà³à²•à²¿à²¸à²²à²¾à²—à³à²¤à³à²¤à²¿à²¦à³†</translation>
<translation id="1175364870820465910">&amp;ಮà³à²¦à³à²°à²¿à²¸à²¿...</translation>
<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="1206967143813997005">ತಪà³à²ªà³ ಪà³à²°à²¾à²°à²‚ಭಿಕ ಸಹಿ</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">ಹಸಿರà³à²¨à³€à²²à²¿</translation>
<translation id="1629803312968146339">ಈ ಕಾರà³à²¡à³ ಅನà³à²¨à³ Chrome ಉಳಿಸಬೇಕೆಂದೠನೀವೠಬಯಸà³à²µà²¿à²°à²¾?</translation>
<translation id="1640180200866533862">ಬಳಕೆದಾರನ ನೀತಿಗಳà³</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />ಸೈಟà³â€Œà²¨ ಮà³à²–ಪà³à²Ÿà²•à³à²•à³† ಭೇಟಿ ನೀಡಲà³<ph name="END_LINK" /> ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿.</translation>
<translation id="1644184664548287040">ನೆಟà³â€Œà²µà²°à³à²•à³ ಕಾನà³à²«à²¿à²—ರೇಶನà³â€Œ ಅಮಾನà³à²¯à²µà²¾à²—ಿದೆ ಹಾಗೂ ಆಮದೠಮಾಡಲಾಗà³à²µà³à²¦à²¿à²²à³à²².</translation>
<translation id="1644574205037202324">ಇತಿಹಾಸ</translation>
<translation id="1645368109819982629">ಬೆಂಬಲವಿಲà³à²²à²¦ ಪà³à²°à³†à³‚ಟೋಕಾಲà³</translation>
<translation id="1676269943528358898"><ph name="SITE" /> ಸಾಮಾನà³à²¯à²µà²¾à²—ಿ ನಿಮà³à²® ಮಾಹಿತಿಯನà³à²¨à³ ಸಂರಕà³à²·à²¿à²¸à²²à³ ಎನà³â€Œà²•à³à²°à²¿à²ªà³à²¶à²¨à³ ಪà³à²°à²¯à³‹à²œà²¨à²µà²¨à³à²¨à³ ಬಳಸಿಕೊಳà³à²³à³à²¤à³à²¤à²¦à³†. ಈ ಸಂದರà³à²­à²¦à²²à³à²²à²¿ Google Chrome <ph name="SITE" /> ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²—ೆ ಸಂಪರà³à²•à²¿à²¸à²²à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿à²¦à²¾à²—, ಆ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œâ€Œ ಅಸಹಜ ಮತà³à²¤à³ ತಪà³à²ªà³ ರà³à²œà³à²µà²¾à²¤à³à²—ಳನà³à²¨à³ ಹಿಂತಿರà³à²—ಿಸಿದೆ. ದಾಳಿಕೋರರೠ<ph name="SITE" /> ರೂಪದಲà³à²²à²¿ ಸೋಗೠಹಾಕಲೠಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à³à²¤à³à²¤à²¿à²°à³à²µà²¾à²— ಅಥವಾ ವೈ-ಫೈ ಸೈನà³-ಇನೠಪರದೆಯೠಸಂಪರà³à²•à²•à³à²•à³† ಅಡà³à²¡à²¿à²¯à³à²‚ಟೠಮಾಡಿದಾಗ ಇದೠಕಂಡà³à²¬à²°à²¬à²¹à³à²¦à³. ಯಾವà³à²¦à³‡ ಡೇಟಾವನà³à²¨à³ ವಿನಿಮಯ ಮಾಡಿಕೊಳà³à²³à³à²µ ಮೊದಲೇ Google Chrome ಸಂಪರà³à²• ಕಡಿತಗೊಳಿಸಿರà³à²µ ಕಾರಣ, ನಿಮà³à²® ಮಾಹಿತಿ ಈಗಲೂ ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿದೆ.</translation>
+<translation id="168328519870909584"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ನಲà³à²²à²¿à²°à³à²µ ದಾಳಿಕೋರರೠನಿಮà³à²® ಕಂಪà³à²¯à³‚ಟರà³â€Œà²¨à²²à³à²²à²¿à²°à³à²µ ಮಾಹಿತಿ (ಉದಾಹರಣೆಗೆ, ಫೋಟೋಗಳà³, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳೠಮತà³à²¤à³ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³ ಮಾಹಿತಿಗಳà³) ಕದಿಯಲೠಇಲà³à²²à²µà³‡ ಅಳಿಸಲೠಅಪಾಯಕಾರಿ ಪà³à²°à³‹à²—à³à²°à²¾à²‚ಗಳನà³à²¨à³ ಸà³à²¥à²¾à²ªà²¿à²¸à²²à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³.</translation>
<translation id="168841957122794586">ಸರà³à²µà²°à³ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà³ ದà³à²°à³à²¬à²² ಕà³à²°à²¿à²ªà³à²Ÿà³‹à²—à³à²°à²¾à²«à²¿à²•à³ ಕೀಯನà³à²¨à³ ಹೊಂದಿದೆ.</translation>
-<translation id="1701955595840307032">ಸಲಹೆ ನೀಡಿದ ವಿಷಯ ಪಡೆದà³à²•à³Šà²³à³à²³à²¿</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">ಈ ಸೈಟà³â€Œà²—ೆ ಭೇಟಿ ನೀಡಲೠನಿಮಗೆ <ph name="NAME" /> ಅವರ ಅನà³à²®à²¤à²¿à²¯ ಅಗತà³à²¯à²µà²¿à²°à³à²¤à³à²¤à²¦à³†</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">ಪà³à²Ÿ ಸಂಖà³à²¯à³†</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows ನೆಟà³â€Œà²µà²°à³à²•à³ ಡಯಾಗà³à²¨à²¾à²¸à³à²Ÿà²¿à²•à³à²¸à³â€Œ ರನೠಮಾಡಲೠಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿â€Œ<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">ದಯವಿಟà³à²Ÿà³ ನಿಮà³à²® ಸಿಂಕೠಪಾಸà³â€Œà²«à³à²°à³‡à²¸à³ ಅನà³à²¨à³ ನವೀಕರಿಸಿ.</translation>
+<translation id="1787142507584202372">ನಿಮà³à²® ತೆರೆಯಲಾದ ಟà³à²¯à²¾à²¬à³â€Œà²—ಳೠಇಲà³à²²à²¿ ಗೋಚರಿಸà³à²¤à³à²¤à²¦à³†</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google ಸà³à²°à²•à³à²·à²¿à²¤ ಬà³à²°à³Œà²¸à³ ಮಾಡà³à²µà²¿à²•à³† ಇತà³à²¤à³€à²šà³†à²—ೆ <ph name="SITE" /> ನಲà³à²²à²¿ <ph name="BEGIN_LINK" />ಮಾಲà³â€Œà²µà³‡à²°à³ ಪತà³à²¤à³†à²¹à²šà³à²šà²¿à²¦à³†<ph name="END_LINK" />. ಸಾಮಾನà³à²¯à²µà²¾à²—ಿ ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿರà³à²µ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²—ಳೠಕೆಲವೊಮà³à²®à³† ಮಾಲà³â€Œà²µà³‡à²°à³ ದಾಳಿಗೆ ತà³à²¤à³à²¤à²¾à²—ಿರà³à²¤à³à²¤à²µà³†. ದà³à²°à³à²¦à³à²¦à³‡à²¶à²ªà³‚ರಿತ ವಿಷಯವೠಚಿರಪರಿಚಿತ ಮಾಲà³â€Œà²µà³‡à²°à³ <ph name="SUBRESOURCE_HOST" /> ವಿತರಕರಿಂದ ಬರà³à²¤à³à²¤à²µà³†. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">ಅಮಾನà³à²¯à²µà²¾à²¦ ವಿನಂತಿ ಅಥವಾ ವಿನಂತಿ ಪà³à²¯à²¾à²°à²¾à²®à³€à²Ÿà²°à³â€Œà²—ಳà³</translation>
+<translation id="1834321415901700177">ಈ ಸೈಟೠಹಾನಿಕಾರಕ ಪà³à²°à³†à³‚ೕಗà³à²°à²¾à²‚ಗಳನà³à²¨à³ ಹೊಂದಿದೆ</translation>
<translation id="1838667051080421715">ನೀವೠವೆಬೠಪà³à²Ÿà²¦ ಮೂಲವನà³à²¨à³ ವೀಕà³à²·à²¿à²¸à³à²¤à³à²¤à²¿à²°à³à²µà²¿à²°à²¿.</translation>
<translation id="1871208020102129563">.pac ಸà³à²•à³à²°à²¿à²ªà³à²Ÿà³ URL ಅಲà³à²²à²¦à³†, ನಿಗಧಿತ ಪà³à²°à²¾à²•à³à²¸à²¿ ಸರà³à²µà²°à³â€Œà²—ಳನà³à²¨à³ ಬಳಸಲೠಪà³à²°à²¾à²•à³à²¸à²¿à²¯à²¨à³à²¨à³ ಹೊಂದಿಸಲಾಗಿದೆ.</translation>
<translation id="1883255238294161206">ಪಟà³à²Ÿà²¿à²¯à²¨à³à²¨à³ ಸಂಕà³à²šà²¿à²¸à²¿</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">ಸà³à²¥à²³à³€à²¯ ಗà³à²°à²¾à²¹à²•</translation>
<translation id="213826338245044447">ಮೊಬೈಲೠಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳà³</translation>
<translation id="2148716181193084225">ಇಂದà³</translation>
-<translation id="2149973817440762519">ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³ ಎಡಿಟೠಮಾಡಿ</translation>
<translation id="2154054054215849342">ಸಿಂಕೠಸೇವೆಯೠನಿಮà³à²® ಡೊಮೇನà³â€Œà²—ೆ ಲಭà³à²¯à²µà²¿à²²à³à²²</translation>
<translation id="2166049586286450108">ಪೂರà³à²£ ನಿರà³à²µà²¾à²¹à²• ಪà³à²°à²µà³‡à²¶</translation>
<translation id="2166378884831602661">ಈ ಸೈಟà³â€Œà²—ೆ ಸà³à²°à²•à³à²·à²¿à²¤ ಸಂಪರà³à²•à²µà²¨à³à²¨à³ ಒದಗಿಸಲಾಗà³à²µà³à²¦à²¿à²²à³à²²</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">ವೆಬà³â€Œà²¸à³ˆà²Ÿà³ HSTS ಬಳಸà³à²µ ಕಾರಣ ಇದೀಗ ನೀವೠ<ph name="SITE" /> ಗೆ ಭೇಟಿ ಮಾಡಲೠಸಾಧà³à²¯à²µà²¿à²²à³à²². ನೆಟà³â€Œà²µà²°à³à²•à³ ದೋಷಗಳೠಮತà³à²¤à³ ದಾಳಿಗಳೠಸಾಮಾನà³à²¯à²µà²¾à²—ಿ ತಾತà³à²•à²¾à²²à²¿à²•à²µà²¾à²—ಿರà³à²¤à³à²¤à²µà³†, ಹೀಗಾಗಿ ಈ ಪà³à²Ÿà²µà³ ನಂತರ ಕಾರà³à²¯à²¨à²¿à²°à³à²µà²¹à²¿à²¸à²¬à²¹à³à²¦à³. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">ಸೂಚà³à²¯à²‚ಕದಲà³à²²à²¿ <ph name="ENTRY_INDEX" /> ನಿರà³à²²à²•à³à²·à²¿à²¸à²²à²¾à²¦ ಅಮಾನà³à²¯ ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œ</translation>
<translation id="2354001756790975382">ಇತರ ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳà³</translation>
+<translation id="2355395290879513365">ಈ ಸೈಟà³â€Œà²¨à²²à³à²²à²¿ ನೀವೠನೋಡà³à²¤à³à²¤à²¿à²°à³à²µ ಚಿತà³à²°à²—ಳನà³à²¨à³ ವೀಕà³à²·à²¿à²¸à²²à³ ಮತà³à²¤à³ ಅವà³à²—ಳನà³à²¨à³ ಮಾರà³à²ªà²¡à²¿à²¸à³à²µ ಮೂಲಕ ನಿಮà³à²®à²¨à³à²¨à³ ವಂಚಿಸಲೠದಾಳಿಕೋರರಿಗೆ ಸಾಧà³à²¯à²µà²¾à²—à³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³.</translation>
<translation id="2359808026110333948">ಮà³à²‚ದà³à²µà²°à²¿à²¸à³</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> ನಲà³à²²à²¿ ಸೆರೆಹಿಡಿಯಲಾದ ಕà³à²°à³à²¯à²¾à²¶à³ ವರದಿಯನà³à²¨à³ ಅಪà³â€Œà²²à³‹à²¡à³ ಮಾಡಲಾಗಿಲà³à²²</translation>
<translation id="2367567093518048410">ಹಂತ</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">ಯಾವà³à²¦à³‡ ಮೌಲà³à²¯ ಹೊಂದಿಸಿಲà³à²²à²¦ ನೀತಿಗಳನà³à²¨à³ ತೋರಿಸಿ</translation>
<translation id="2396249848217231973">&amp;ಅಳಿಸà³à²µà³à²¦à²¨à³à²¨à³ ರದà³à²¦à³à²—ೊಳಿಸà³</translation>
<translation id="2455981314101692989">ಈ ಫಾರà³à²®à³ ಅನà³à²¨à³ ಭರà³à²¤à²¿ ಮಾಡà³à²µ ಸಲà³à²µà²¾à²—ಿ ಈ ವೆಬà³â€Œà²ªà³à²Ÿà²µà²¨à³à²¨à³ ಸà³à²µà²¯à²‚ಚಾಲಿತವಾಗಿ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಲಾಗಿದೆ.</translation>
+<translation id="2460160116472764928">Google ಸà³à²°à²•à³à²·à²¿à²¤ ಬà³à²°à³Œà²¸à³ ಮಾಡà³à²µà²¿à²•à³† ಇತà³à²¤à³€à²šà³†à²—ೆ <ph name="SITE" /> ನಲà³à²²à²¿ <ph name="BEGIN_LINK" />ಮಾಲà³â€Œà²µà³‡à²°à³ ಪತà³à²¤à³†à²¹à²šà³à²šà²¿à²¦à³†<ph name="END_LINK" />. ಸಾಮಾನà³à²¯à²µà²¾à²—ಿ ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿರà³à²µ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²—ಳೠಕೆಲವೊಮà³à²®à³† ಮಾಲà³â€Œà²µà³‡à²°à³ ದಾಳಿಗೆ ತà³à²¤à³à²¤à²¾à²—ಿರà³à²¤à³à²¤à²µà³†. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">ಭರà³à²¤à²¿ ಮಾಡà³</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />ನೆಟà³â€Œà²µà²°à³à²•à³ ಡಯಾಗà³à²¨à²¾à²¸à³à²Ÿà²¿à²•à³à²¸à³â€Œ ರನೠಆಗà³à²¤à³à²¤à²¿à²¦à³†<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">ಅಮಾನà³à²¯à²µà²¾à²¦ ಹà³à²¡à³à²•à²¾à²Ÿ URL.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">ನಿಮà³à²® ಇತಿಹಾಸದಿಂದ ನೀವೠಈ ಪà³à²Ÿà²—ಳನà³à²¨à³ ಖಚಿತವಾಗಿ ಅಳಿಸಲೠಬಯಸà³à²¤à³à²¤à²¿à²¦à³à²¦à³€à²°à²¾?</translation>
<translation id="2677748264148917807">ತೊರೆಯಿರಿ</translation>
<translation id="269990154133806163">ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²° ಪಾರದರà³à²¶à²•à²¤à³† ನೀತಿಯನà³à²¨à³ ಬಳಸಿಕೊಂಡೠಸಾರà³à²µà²œà²¨à²¿à²•à²µà²¾à²—ಿ ಬಹಿರಂಗಗೊಳಿಸದ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಸರà³à²µà²°à³ ಪà³à²°à²¸à³à²¤à³à²¤à²ªà²¡à²¿à²¸à²¿à²¦à³†. ಇದೠಅವà³à²—ಳೠವಿಶà³à²µà²¾à²¸à²¾à²°à³à²¹à²µà²¾à²—ಿವೆ ಮತà³à²¤à³ ಆಕà³à²°à²®à²£à²•à²¾à²°à²° ವಿರà³à²¦à³à²§ ರಕà³à²·à²£à³†à²¯à²¨à³à²¨à³ ನೀಡà³à²¤à³à²¤à²µà³† ಎಂಬà³à²¦à²¨à³à²¨à³ ಖಚಿತಪಡಿಸಿಕೊಳà³à²³à²²à³ ಕೆಲವೠಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²—ಳಿಗೆ ಅಗತà³à²¯à²µà²¾à²—ಿದೆ. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">ಓದà³à²µ ಪಟà³à²Ÿà²¿</translation>
<translation id="2704283930420550640">ಮೌಲà³à²¯à²µà³ ಸà³à²µà²°à³‚ಪಕà³à²•à³† ಹೊಂದಿಕೆಯಾಗà³à²µà³à²¦à²¿à²²à³à²².</translation>
<translation id="2704951214193499422">ಈ ಸಮಯದಲà³à²²à²¿ Chromium ಗೆ ನಿಮà³à²® ಕಾರà³à²¡à³ ಖಚಿತಪಡಿಸಲೠಸಾಧà³à²¯à²µà²¾à²—ಲಿಲà³à²². ದಯವಿಟà³à²Ÿà³ ನಂತರ ಮತà³à²¤à³† ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿.</translation>
<translation id="2705137772291741111">ಈ ಸೈಟà³â€Œà²¨ ಉಳಿಸಿದ (ಸಂಗà³à²°à²¹à²µà²¾à²—ಿರà³à²µ) ನಕಲನà³à²¨à³ ಓದಲಾಗà³à²¤à³à²¤à²¿à²²à³à²².</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">ನೀವೠ<ph name="DOMAIN" /> ತಲà³à²ªà²²à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿à²°à³à²µà²¿à²°à²¿, ಆದರೆ ಸರà³à²µà²°à³ ನೀಡಿದ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಅದನà³à²¨à³ ನೀಡಿದವರೠಹಿಂತೆಗೆದà³à²•à³Šà²‚ಡಿದà³à²¦à²¾à²°à³†. ಇದರರà³à²¥ ಸರà³à²µà²°à³ ಒದಗಿಸಿದ ಸà³à²°à²•à³à²·à²¤à²¾ ರà³à²œà³à²µà²¾à²¤à³à²—ಳನà³à²¨à³ ಯಾವ ಕಾರಣಕà³à²•à³‚ ನಂಬಲಾಗà³à²µà³à²¦à²¿à²²à³à²². ನೀವೠಆಕà³à²°à²®à²£à²•à²¾à²°à²° ಜೊತೆಗೆ ಸಂವಹನ ಮಾಡà³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">ಅನà³à²®à²¤à²¿ ಕೇಳಿ</translation>
<translation id="2713444072780614174">ಬಿಳಿ</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">ಸಂಪರà³à²•à²•à³à²•à²¾à²—ಿ ಕಾನà³à²«à²¿à²—ರೠಮಾಡಲಾಗಿರà³à²µ ಯಾವà³à²¦à³‡ ಪà³à²°à²¾à²•à³à²¸à²¿à²—ಳನà³à²¨à³ ನೀವೠಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳ ಪà³à²Ÿà²¦à²¿à²‚ದ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಬಹà³à²¦à³.</translation>
<translation id="2955913368246107853">ಹà³à²¡à³à²•à²¿ ಬಾರೠಅನà³à²¨à³ ಮà³à²šà³à²šà²¿</translation>
<translation id="2958431318199492670">ONC ಪà³à²°à²®à²¾à²£à²¿à²¤à²•à³à²•à³† ನೆಟà³â€Œà²µà²°à³à²•à³ ಕಾನà³à²«à²¿à²—ರೇಶನೠಅನà³à²¸à²°à²£à³†à²¯à²¾à²—à³à²µà³à²¦à²¿à²²à³à²². ಕಾನà³à²«à²¿à²—ರೇಶನà³â€Œà²¨ ಭಾಗಗಳನà³à²¨à³ ಆಮದೠಮಾಡಲಾಗದಿರಬಹà³à²¦à³.</translation>
+<translation id="29611076221683977"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ನಲà³à²²à²¿à²°à³à²µ ದಾಳಿಕೋರರೠನಿಮà³à²® ಕಂಪà³à²¯à³‚ಟರà³â€Œà²¨à²²à³à²²à²¿à²°à³à²µ ಮಾಹಿತಿ (ಉದಾಹರಣೆಗೆ, ಫೋಟೋಗಳà³, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳೠಮತà³à²¤à³ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³ ಮಾಹಿತಿಗಳà³) ಕದಿಯಲೠಇಲà³à²²à²µà³‡ ಅಳಿಸಲೠಅಪಾಯಕಾರಿ ಪà³à²°à³‹à²—à³à²°à²¾à²‚ಗಳನà³à²¨à³ ಸà³à²¥à²¾à²ªà²¿à²¸à²²à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³.</translation>
<translation id="2969319727213777354">ಸà³à²°à²•à³à²·à²¿à²¤ ಸಂಪರà³à²•à²µà²¨à³à²¨à³ ಸà³à²¥à²¾à²ªà²¿à²¸à²²à³, ನಿಮà³à²® ಗಡಿಯಾರವನà³à²¨à³ ಸರಿಯಾಗಿ ಹೊಂದಿಸಬೇಕಾದ ಅಗತà³à²¯à²µà²¿à²¦à³†. ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²—ಳೠತಮà³à²®à²¨à³à²¨à³ ಗà³à²°à³à²¤à²¿à²¸à²²à³ ಬಳಸà³à²µ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²—ಳೠನಿರà³à²¦à²¿à²·à³à²Ÿ ಅವಧಿಗಳಲà³à²²à²¿ ಮಾತà³à²° ಮಾನà³à²¯à²µà²¾à²—ಿರà³à²µ ಕಾರಣ ಹೀಗಾಗà³à²¤à³à²¤à²¦à³†. ನಿಮà³à²® ಸಾಧನದ ಗಡಿಯಾರವೠತಪà³à²ªà²¾à²—ಿರà³à²µ ಕಾರಣ, Google Chrome ಗೆ ಈ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²—ಳನà³à²¨à³ ಪರಿಶೀಲಿಸಲೠಸಾಧà³à²¯à²µà²¾à²—à³à²µà³à²¦à²¿à²²à³à²².</translation>
<translation id="2972581237482394796">&amp;ಮತà³à²¤à³†à²®à²¾à²¡à³</translation>
<translation id="2985306909656435243">ಸಕà³à²°à²¿à²¯à²—ೊಳಿಸಿದà³à²¦à²°à³†, ವೇಗವಾಗಿ ಫಾರà³à²®à³ ಭರà³à²¤à²¿ ಮಾಡಲೠChromium ಈ ಸಾಧನದಲà³à²²à²¿ ನಿಮà³à²® ಕಾರà³à²¡à³â€Œà²¨ ಪà³à²°à²¤à²¿à²¯à²¨à³à²¨à³ ಸಂಗà³à²°à²¹à²¿à²¸à³à²¤à³à²¤à²¦à³†.</translation>
@@ -257,7 +266,6 @@
<translation id="3586931643579894722">ವಿವರಗಳನà³à²¨à³ ಮರೆಮಾಡಿ</translation>
<translation id="3587482841069643663">ಎಲà³à²²</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">ಸರà³à²µà²°à³ TLS ಮರà³à²¸à²‚ಧಾನ ವಿಸà³à²¤à²°à²£à³†à²¯à²¨à³à²¨à³ ಬೆಂಬಲಿಸà³à²µà³à²¦à²¿à²²à³à²².</translation>
<translation id="36224234498066874">ಬà³à²°à³Œà²¸à³ ಆಗà³à²¤à³à²¤à²¿à²°à³à²µ ಡೇಟಾವನà³à²¨à³ ತೆರವà³à²—ೊಳಿಸಿ...</translation>
<translation id="362276910939193118">ಪೂರà³à²£ ಇತಿಹಾಸ ತೋರಿಸಿ</translation>
<translation id="3623476034248543066">ಮೌಲà³à²¯à²µà²¨à³à²¨à³ ತೋರಿಸಿ</translation>
@@ -268,19 +276,20 @@
<translation id="3655670868607891010">ಇದೠನಿಮಗೆ ಪದೇ ಪದೇ ಎದà³à²°à²¾à²—à³à²¤à³à²¤à²¿à²¦à³à²¦à²°à³†, <ph name="HELP_LINK" /> ಇವà³à²—ಳನà³à²¨à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿.</translation>
<translation id="3658742229777143148">ಪರಿಷà³à²•à²°à²£à³†</translation>
<translation id="3678029195006412963">ವಿನಂತಿಗೆ ಸಹಿ ಮಾಡಲಾಗà³à²µà³à²¦à²¿à²²à³à²²</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="3696411085566228381">ಯಾವà³à²¦à³‚ ಇಲà³à²²</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">ಲೋಡೠಮಾಡಲಾಗà³à²¤à³à²¤à²¿à²¦à³†...</translation>
+<translation id="370665806235115550">ಲೋಡೠಆಗà³à²¤à³à²¤à²¿à²¦à³†...</translation>
<translation id="3712624925041724820">ಪರವಾನಗಿಗಳೠಬರಿದಾಗಿವೆ</translation>
<translation id="3714780639079136834">ಮೊಬೈಲೠಡೇಟಾ ಅಥವಾ ವೈ-ಫೈ ಆನೠಮಾಡಲಾಗà³à²¤à³à²¤à²¿à²¦à³†</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />ಪà³à²°à²¾à²•à³à²¸à²¿, ಫೈರà³â€Œà²µà²¾à²²à³ ಮತà³à²¤à³ DNS ಕಾನà³à²«à²¿à²—ರೇಶನà³â€Œâ€Œ ಪರಿಶೀಲಿಸಲಾಗà³à²¤à³à²¤à²¿à²¦à³†<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">ನಿಮà³à²® ಸà³à²°à²•à³à²·à²¤à³† ಅಪಾಯಗಳೠನಿಮಗೆ ಅರà³à²¥à²µà²¾à²—ಿದà³à²¦à²°à³†, ಅಪಾಯಕಾರಿ ಪà³à²°à³‹à²—à³à²°à²¾à²‚ಗಳನà³à²¨à³ ತೆಗೆದà³à²¹à²¾à²•à³à²µà³à²¦à²•à³à²•à³‚ ಮೊದಲೠನೀವೠ<ph name="BEGIN_LINK" />ಈ ಅಸà³à²°à²•à³à²·à²¿à²¤ ಸೈಟà³â€Œà²—ೆ ಭೇಟಿ ನೀಡಬಹà³à²¦à³<ph name="END_LINK" />.</translation>
<translation id="3739623965217189342">ನೀವೠನಕಲಿಸಿದ ಲಿಂಕà³</translation>
<translation id="375403751935624634">ಸರà³à²µà²°à³ ದೋಷದ ಕಾರಣ ಅನà³à²µà²¾à²¦à²µà³ ವಿಫಲವಾಗಿದೆ.</translation>
<translation id="3759461132968374835">ಇತà³à²¤à³€à²šà³†à²—ೆ ನೀವೠಯಾವà³à²¦à³‡ ಕà³à²°â€à³à²¯à²¾à²¶à³â€Œà²—ಳನà³à²¨à³ ವರದಿ ಮಾಡಿಲà³à²². ಕà³à²°â€à³à²¯à²¾à²¶à³â€Œâ€Œ ಅನà³à²¨à³ ವರದಿಮಾಡà³à²µà²¿à²•à³†à²¯à²¨à³à²¨à³ ಉಂಟಾಗಿರà³à²µ ಕà³à²°â€à³à²¯à²¾à²¶à³â€Œà²—ಳೠಇಲà³à²²à²¿ ಗೋಚರಿಸà³à²µà³à²¦à²¿à²²à³à²².</translation>
-<translation id="3788090790273268753">ಈ ಸೈಟà³â€Œà²—ೆ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²¦ ಅವಧಿಯೠ2016 ರಲà³à²²à²¿ ಮà³à²•à³à²¤à²¾à²¯à²—ೊಳà³à²³à³à²¤à³à²¤à²¦à³† ಮತà³à²¤à³ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²° ಸರಣಿಯೠSHA-1 ಬಳಸಿಕೊಂಡೠಸಹಿ ಮಾಡಲಾದ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಒಳಗೊಂಡಿರà³à²¤à³à²¤à²¦à³†.</translation>
<translation id="382518646247711829">ನೀವೠಪà³à²°à²¾à²•à³à²¸à²¿ ಸರà³à²µà²°à³ ಬಳಸಿದರೆ...</translation>
<translation id="3828924085048779000">ಖಾಲಿ ಪಾಸà³â€Œà²«à³à²°à³‡à²¸à³ ಅನà³à²¨à³ ಅನà³à²®à²¤à²¿à²¸à³à²µà³à²¦à²¿à²²à³à²².</translation>
<translation id="3845539888601087042">ನಿಮà³à²® ಸೈನà³-ಇನೠಮಾಡಿದ ಸಾಧನಗಳಿಂದ ಇತಿಹಾಸವನà³à²¨à³ ತೋರಿಸಲಾಗà³à²¤à³à²¤à²¿à²¦à³†. <ph name="BEGIN_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LINK" />.</translation>
@@ -302,6 +311,7 @@
<translation id="4058922952496707368">ಕೀ "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">ಸಾಮಾನà³à²¯ SSL ಪà³à²°à³Šà²Ÿà³‹à²•à²¾à²²à³ ಆವೃತà³à²¤à²¿ ಅಥವಾ ಸೈಫರೠಸà³à²¯à³‚ಟೠಅನà³à²¨à³ ಕà³à²²à³ˆà²‚ಟೠಮತà³à²¤à³ ಸರà³à²µà²°à³ ಬೆಂಬಲಿಸà³à²µà³à²¦à²¿à²²à³à²².</translation>
<translation id="4079302484614802869">ಪà³à²°à²¾à²•à³à²¸à²¿ ಕಾನà³à²«à²¿à²—ರೇಶನೠಅನà³à²¨à³ .pac ಸà³à²•à³à²°à²¿à²ªà³à²Ÿà³ URL ಬಳಸà³à²µà²‚ತೆ ಹೊಂದಿಸಲಾಗಿದೆ, ಹೊಂದಿಸಿದ ಪà³à²°à²¾à²•à³à²¸à²¿ ಸರà³à²µà²°à³â€Œà²—ಳಲà³à²².</translation>
+<translation id="4098354747657067197">ವಂಚನೆಯ ಸೈಟೠಮà³à²‚ದಿದೆ</translation>
<translation id="4103249731201008433">ಸಾಧನದ ಸರಣಿಯ ಸಂಖà³à²¯à³† ಅಮಾನà³à²¯à²µà²¾à²—ಿದೆ</translation>
<translation id="4103763322291513355">ನಿಮà³à²® ಸಿಸà³à²Ÿà²‚ ನಿರà³à²µà²¾à²¹à²•à²°à³ ವಿಧಿಸಿರà³à²µ ಕಪà³à²ªà³à²ªà²Ÿà³à²Ÿà²¿à²¯ URLಗಳೠಮತà³à²¤à³ ಇತರ ನೀತಿಗಳನà³à²¨à³ ವೀಕà³à²·à²¿à²¸à²²à³ &lt;strong&gt;chrome://policy&lt;/strong&gt; ಗೆ ಭೇಟಿ ನೀಡಿ.</translation>
<translation id="4110615724604346410">ಈ ಸರà³à²µà²°à³ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬà³à²¦à²¨à³à²¨à³ ಸಾಬೀತà³à²ªà²¡à²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¾à²—ಲಿಲà³à²²; ಅದರ ಸà³à²°à²•à³à²·à²¤à²¾ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà³ ದೋಷಗಳನà³à²¨à³ ಹೊಂದಿರಬಹà³à²¦à³. ಇದೠತಪà³à²ªà²¾à²¦ ಕಾನà³à²«à²¿à²—ರೇಶನà³â€Œà²¨à²¿à²‚ದ ಅಥವಾ ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ನಿಮà³à²® ಸಂಪರà³à²•à²¦à²²à³à²²à²¿ ಒಳನà³à²¸à³à²³à²¿à²°à³à²µà³à²¦à²°à²¿à²‚ದ ಆಗಿರಬಹà³à²¦à³. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -319,15 +329,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ಯಾವà³à²¦à³‚ ಇಲà³à²²}=1{1 ಅಪà³à²²à²¿à²•à³‡à²¶à²¨à³â€Œ ($1)}=2{2 ಅಪà³à²²à²¿à²•à³‡à²¶à²¨à³â€Œà²—ಳೠ($1, $2)}one{# ಅಪà³à²²à²¿à²•à³‡à²¶à²¨à³â€Œà²—ಳೠ($1, $2, $3)}other{# ಅಪà³à²²à²¿à²•à³‡à²¶à²¨à³â€Œà²—ಳೠ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">ವಿಫಲತೆಗಳà³</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />ನೆಟà³â€Œà²µà²°à³à²•à³ ಡಯಾಗà³à²¨à²¾à²¸à³à²Ÿà²¿à²•à³à²¸à³â€Œ ರನೠಮಾಡಲೠಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">ಈ ಸೈಟà³â€Œà²—ೆ ನಿಮà³à²® ಸಂಪರà³à²•à²µà³ ಸಂಪೂರà³à²£à²µà²¾à²—ಿ ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿಲà³à²²</translation>
<translation id="4250680216510889253">ಇಲà³à²²</translation>
<translation id="425582637250725228">ನೀವೠಮಾಡಿದ ಬದಲಾವಣೆಗಳನà³à²¨à³ ಉಳಿಸಲಾಗದೇ ಇರಬಹà³à²¦à³.</translation>
<translation id="4258748452823770588">ತಪà³à²ªà²¾à²¦ ಸಹಿ</translation>
<translation id="4269787794583293679">(ಯಾವà³à²¦à³ ಬಳಕೆದಾರಹೆಸರಿಲà³à²²)</translation>
+<translation id="4280429058323657511">, ಅವಧಿ ಮà³à²•à³à²¤à²¾à²¯ <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google ಸà³à²°à²•à³à²·à²¿à²¤ ಬà³à²°à³Œà²¸à³ ಮಾಡà³à²µà²¿à²•à³† ಇತà³à²¤à³€à²šà³†à²—ೆ <ph name="SITE" /> ನಲà³à²²à²¿ <ph name="BEGIN_LINK" />ಹಾನಿಕಾರಕ ಪà³à²°à³‹à²—à³à²°à²¾à²‚ಗಳನà³à²¨à³ ಪತà³à²¤à³†à²¹à²šà³à²šà²¿à²¦à³†<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">ಪೋಷಕ ಸಲಹೆಗಳà³</translation>
<translation id="4304224509867189079">ಲಾಗೠಇನà³</translation>
<translation id="432290197980158659">ಅಂತರà³â€Œà²¨à²¿à²°à³à²®à²¿à²¤ ನಿರೀಕà³à²·à³†à²—ಳಿಗೆ ಹೊಂದಿಕೆಯಾಗದ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಸರà³à²µà²°à³ ಪà³à²°à²¸à³à²¤à³à²¤à²ªà²¡à²¿à²¸à²¿à²¦à³†. ಈ ನಿರೀಕà³à²·à³†à²—ಳೠನಿಮà³à²®à²¨à³à²¨à³ ರಕà³à²·à²¿à²¸à³à²µ ಸಲà³à²µà²¾à²—ಿ ಕೆಲವೠಉನà³à²¨à²¤ ಸà³à²°à²•à³à²·à²¤à²¾ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²—ಳನà³à²¨à³ ಒಳಗೊಂಡಿವೆ. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">ಲೇಖನ ಕಂಡà³à²¬à²°à²²à²¿à²²à³à²²</translation>
+<translation id="4326324639298822553">ನಿಮà³à²® ಮà³à²•à³à²¤à²¾à²¯ ದಿನಾಂಕವನà³à²¨à³ ಪರಿಶೀಲಿಸಿ ಹಾಗೂ ಮತà³à²¤à³† ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿</translation>
<translation id="4331708818696583467">ಸà³à²°à²•à³à²·à²¿à²¤à²µà²²à³à²²</translation>
+<translation id="4356973930735388585">ಈ ಸೈಟà³â€Œà²¨à²²à³à²²à²¿à²°à³à²µ ದಾಳಿಕೋರರೠನಿಮà³à²® ಕಂಪà³à²¯à³‚ಟರà³â€Œà²¨à²²à³à²²à²¿à²°à³à²µ ಮಾಹಿತಿ (ಉದಾಹರಣೆಗೆ, ಫೋಟೋಗಳà³, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳೠಮತà³à²¤à³ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³ ಮಾಹಿತಿಗಳà³) ಕದಿಯಲೠಇಲà³à²²à²µà³‡ ಅಳಿಸಲೠಅಪಾಯಕಾರಿ ಪà³à²°à³‹à²—à³à²°à²¾à²‚ಗಳನà³à²¨à³ ಸà³à²¥à²¾à²ªà²¿à²¸à²²à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³.</translation>
<translation id="4372948949327679948">ನಿರೀಕà³à²·à²¿à²¤ <ph name="VALUE_TYPE" /> ಮೌಲà³à²¯.</translation>
<translation id="4381091992796011497">ಬಳಕೆದಾರ ಹೆಸರೠ:</translation>
<translation id="4394049700291259645">ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಿ</translation>
@@ -345,6 +360,7 @@
<translation id="4589078953350245614">ನೀವೠ<ph name="DOMAIN" /> ತಲà³à²ªà²²à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿à²°à³à²µà²¿à²°à²¿, ಆದರೆ ಮಾನà³à²¯à²µà²²à³à²²à²¦ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಸರà³à²µà²°à³ ಪà³à²°à²¸à³à²¤à³à²¤à²ªà²¡à²¿à²¸à²¿à²¦à³†. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">ಆಧà³à²¨à²¿à²• ಸೈಫರೠಸೂಟೠಬಳಸà³à²µ ಮೂಲಕ <ph name="DOMAIN" /> ಗೆ ನಿಮà³à²® ಸಂಪರà³à²•à²µà²¨à³à²¨à³ ಎನà³â€Œà²•à³à²°à²¿à²ªà³à²Ÿà³ ಮಾಡಲಾಗಿದೆ.</translation>
<translation id="4594403342090139922">&amp;ಅಳಿಸà³à²µà³à²¦à²¨à³à²¨à³ ರದà³à²¦à³à²—ೊಳಿಸಿ</translation>
+<translation id="4619615317237390068">ಇತರ ಸಾಧನಗಳಿಂದ ಟà³à²¯à²¾à²¬à³â€Œà²—ಳà³</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">ನೀವೠವಿಸà³à²¤à²°à²£à³†à²¯ ಪà³à²Ÿà²µà²¨à³à²¨à³ ವೀಕà³à²·à²¿à²¸à³à²¤à³à²¤à²¿à²°à³à²µà²¿à²°à²¿.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -353,10 +369,13 @@
<translation id="4726672564094551039">ನೀತಿಗಳನà³à²¨à³ ಮರà³à²²à³‹à²¡à³ ಮಾಡಿ</translation>
<translation id="4728558894243024398">ಪà³à²²à²¾à²Ÿà³â€Œà²«à²¾à²°à³à²®à³</translation>
<translation id="4744603770635761495">ಪà³à²°à²¦à²°à³à²¶à²¨à²—ೊಳà³à²³à³à²µà²‚ತಹ ಹಾದಿ</translation>
+<translation id="4750917950439032686">ಈ ಸೈಟà³â€Œà²—ೆ ನಿಮà³à²® ಮಾಹಿತಿಯನà³à²¨à³ ಕಳà³à²¹à²¿à²¸à²¿à²¦à²¾à²— ಅದೠ(ಉದಾಹರಣೆಗೆ, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳೠಅಥವಾ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³ ಸಂಖà³à²¯à³†à²—ಳà³) ಖಾಸಗಿಯಾಗಿರà³à²¤à³à²¤à²¦à³†.</translation>
<translation id="4756388243121344051">&amp;ಇತಿಹಾಸ</translation>
+<translation id="4759118997339041434">ಪಾವತಿ ಸà³à²µà²¯à²‚ ಭರà³à²¤à²¿ ಮಾಡà³à²µà²¿à²•à³†à²¯à²¨à³à²¨à³ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಲಾಗಿದೆ</translation>
<translation id="4764776831041365478"><ph name="URL" /> ನಲà³à²²à²¿à²°à³à²µ ವೆಬà³â€Œà²ªà³à²Ÿà²µà³ ತಾತà³à²•à²¾à²²à²¿à²•à²µà²¾à²—ಿ ಕಾರà³à²¯à²¨à²¿à²°à³à²µà²¹à²¿à²¸à²¦à³‡ ಇರಬಹà³à²¦à³ ಅಥವಾ ಅದನà³à²¨à³ ಶಾಶà³à²µà²¤à²µà²¾à²—ಿ ಹೊಸ ವೆಬೠವಿಳಾಸಕà³à²•à³† ಸರಿಸಲಾಗಿರಬಹà³à²¦à³.</translation>
<translation id="4771973620359291008">ಅಪರಿಚಿತ ದೋಷವೊಂದೠಎದà³à²°à²¾à²—ಿದೆ.</translation>
<translation id="4800132727771399293">ನಿಮà³à²® ಮà³à²•à³à²¤à²¾à²¯à²¦ ದಿನಾಂಕ ಮತà³à²¤à³ CVC ಪರಿಶೀಲಿಸಿ ಹಾಗೂ ಮತà³à²¤à³† ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">ನೆಟà³â€Œà²µà²°à³à²•à³ ದೋಷ</translation>
<translation id="4816492930507672669">ಪà³à²Ÿà²•à³à²•à³† ಹೊಂದಿಸà³</translation>
<translation id="4850886885716139402">ವೀಕà³à²·à²£à³†</translation>
@@ -365,6 +384,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ಮತà³à²¤à³ 1 ಹೆಚà³à²šà²¿à²¨ ವೆಬೠಪà³à²Ÿ}one{ಮತà³à²¤à³ # ಹೆಚà³à²šà²¿à²¨ ವೆಬೠಪà³à²Ÿà²—ಳà³}other{ಮತà³à²¤à³ # ಹೆಚà³à²šà²¿à²¨ ವೆಬೠಪà³à²Ÿà²—ಳà³}}</translation>
<translation id="4923417429809017348">ಗೊತà³à²¤à²¿à²²à³à²²à²¦ ಭಾಷೆಯಿಂದ <ph name="LANGUAGE_LANGUAGE" /> ಗೆ ಈ ಪà³à²Ÿà²µà²¨à³à²¨à³ ಭಾಷಾಂತರಿಸಲಾಗಿದೆ</translation>
+<translation id="4923459931733593730">ಪಾವತಿ</translation>
<translation id="4926049483395192435">ನಿರà³à²¦à²¿à²·à³à²Ÿà²ªà²¡à²¿à²¸à²¬à³‡à²•à²¾à²—ಿದೆ.</translation>
<translation id="495170559598752135">ಕà³à²°à²¿à²¯à³†à²—ಳà³</translation>
<translation id="4958444002117714549">ಪಟà³à²Ÿà²¿à²¯à²¨à³à²¨à³ ವಿಸà³à²¤à²°à²¿à²¸à²¿</translation>
@@ -392,7 +412,6 @@
<translation id="5181140330217080051">ಡೌನà³â€Œà²²à³‹à²¡à³ ಆಗà³à²¤à³à²¤à²¿à²¦à³†</translation>
<translation id="5190835502935405962">ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳ ಬಾರà³</translation>
<translation id="5199729219167945352">ಪà³à²°à²¯à³‹à²—ಗಳà³</translation>
-<translation id="5199841536747119669">ನಿಮà³à²® ಸಲಹೆಗಳೠಇಲà³à²²à²¿ ಕಾಣಿಸಿಕೊಳà³à²³à³à²¤à³à²¤à²µà³†</translation>
<translation id="5251803541071282808">ಮೇಘ</translation>
<translation id="5277279256032773186">ಕೆಲಸದಲà³à²²à²¿ Chrome ಬಳಸà³à²¤à³à²¤à²¿à²°à³à²µà²¿à²°à²¾? ತಮà³à²® ಉದà³à²¯à³‹à²—ಿಗಳಿಗಾಗಿ ವà³à²¯à²µà²¹à²¾à²°à²—ಳಲà³à²²à²¿ Chrome ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳೠನಿರà³à²µà²¹à²¿à²¸à²¬à²¹à³à²¦à³. ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ</translation>
<translation id="5299298092464848405">ನೀತಿಯ ಪಾರà³à²¸à²¿à²‚ಗà³â€Œà²¨à²²à³à²²à²¿ ದೋಷ</translation>
@@ -400,8 +419,10 @@
<translation id="5308689395849655368">ಕà³à²°â€à³à²¯à²¾à²¶à³â€Œâ€Œ ವರದಿಯನà³à²¨à³ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಲಾಗಿದೆ.</translation>
<translation id="5317780077021120954">ಉಳಿಸà³</translation>
<translation id="5327248766486351172">ಹೆಸರà³</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ನಲà³à²²à²¿à²¨ ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ಸಾಫà³à²Ÿà³â€Œà²µà³‡à²°à³ ಸà³à²¥à²¾à²ªà²¿à²¸à³à²µà²¿à²•à³† ಅಥವಾ ನಿಮà³à²® ವೈಯಕà³à²¤à²¿à²• ಮಾಹಿತಿಯನà³à²¨à³ ಬಹಿರಂಗ ಪಡಿಸà³à²µà²‚ತಹ ಅಪಾಯಕಾರಿಯಾಗಿ à²à²¨à²¾à²¦à²°à³‚ ಮಾಡà³à²µà²‚ತಹ ಮೋಸವನà³à²¨à³ ಮಾಡಬಹà³à²¦à³ (ಉದಾಹರಣೆಗೆ, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳà³, ಫೋನà³â€Œ ಸಂಖà³à²¯à³†à²—ಳà³, ಅಥವಾ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³â€Œà²—ಳà³).</translation>
<translation id="5359637492792381994">ಈ ಸರà³à²µà²°à³ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬà³à²¦à²¨à³à²¨à³ ಸಾಬೀತà³à²ªà²¡à²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¾à²—ಲಿಲà³à²²; ಅದರ ಸà³à²°à²•à³à²·à²¤à²¾ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà³ ಈ ಸಮಯದಲà³à²²à²¿ ಮಾನà³à²¯à²µà²¾à²—ಿಲà³à²². ಇದೠತಪà³à²ªà²¾à²¦ ಕಾನà³à²«à²¿à²—ರೇಶನà³â€Œà²¨à²¿à²‚ದ ಅಥವಾ ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ನಿಮà³à²® ಸಂಪರà³à²•à²¦à²²à³à²²à²¿ ಒಳನà³à²¸à³à²³à²¿à²°à³à²µà³à²¦à²°à²¿à²‚ದ ಆಗಿರಬಹà³à²¦à³. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">ನೀತಿಯ ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳನà³à²¨à³ ಸಂಗà³à²°à²¹à²¿à²¸à³à²µà²²à³à²²à²¿ ವಿಫಲವಾಗಿದೆ</translation>
+<translation id="5386426401304769735">ಈ ಸೈಟà³â€Œà²—ೆ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²° ಸರಣಿಯೠSHA-1 ಬಳಸಿಕೊಂಡೠಸಹಿ ಮಾಡಲಾದ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಒಳಗೊಂಡಿರà³à²¤à³à²¤à²¦à³†.</translation>
<translation id="5421136146218899937">ಬà³à²°à³Œà²¸à²¿à²‚ಗೠಡೇಟಾವನà³à²¨à³ ತೆರವà³à²—ೊಳಿಸಿ...</translation>
<translation id="5430298929874300616">ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œ ತೆಗೆದà³à²¹à²¾à²•à²¿</translation>
<translation id="5431657950005405462">ನಿಮà³à²® ಫೈಲೠಕಂಡà³à²¬à²‚ದಿಲà³à²²</translation>
@@ -422,17 +443,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> ನಲà³à²²à²¿ ಎಂಬೆಡೠಮಾಡಲಾದ ಪà³à²Ÿà²µà³ ಹೀಗೆ ಹೇಳà³à²¤à³à²¤à²¦à³†:</translation>
<translation id="5556459405103347317">ಮರà³à²²à³‹à²¡à³â€Œ</translation>
<translation id="5565735124758917034">ಸಕà³à²°à²¿à²¯</translation>
+<translation id="5572851009514199876">ದಯವಿಟà³à²Ÿà³ Chrome ಪà³à²°à²¾à²°à²‚ಭಿಸಿ ಮತà³à²¤à³ ಸೈನೠಇನೠಮಾಡಿ ಈ ಮೂಲಕ ಈ ಸೈಟà³â€Œà²—ೆ ಪà³à²°à²µà³‡à²¶à²¿à²¸à²²à³ ನಿಮಗೆ ಅನà³à²®à²¤à²¿à²¸à²²à²¾à²—ಿದೆಯೇ ಎಂಬà³à²¦à²¨à³à²¨à³ Chrome ಪರಿಶೀಲಿಸಬಹà³à²¦à³.</translation>
+<translation id="5580958916614886209">ನಿಮà³à²® ಮà³à²•à³à²¤à²¾à²¯ ತಿಂಗಳನà³à²¨à³ ಪರಿಶೀಲಿಸಿ ಹಾಗೂ ಮತà³à²¤à³† ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿</translation>
<translation id="560412284261940334">ನಿರà³à²µà²¾à²¹à²• ಬೆಂಬಲಿಸà³à²µà³à²¦à²¿à²²à³à²²</translation>
<translation id="5610142619324316209">ಸಂಪರà³à²•à²µà²¨à³à²¨à³ ಪರಿಶೀಲಿಸಲಾಗà³à²¤à³à²¤à²¿à²¦à³†</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> ನಿಮà³à²®à²¨à³à²¨à³ ತೀರಾ ಹೆಚà³à²šà³ ಬಾರಿ ಮರà³à²¨à²¿à²°à³à²¦à³‡à²¶à²¿à²¸à²¿à²¦à³†.</translation>
<translation id="5622887735448669177">ನೀವೠಈ ಸೈಟೠತೊರೆಯಲೠಬಯಸà³à²¤à³à²¤à³€à²°à²¾?</translation>
<translation id="5629630648637658800">ನೀತಿಯ ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳನà³à²¨à³ ಲೋಡೠಮಾಡà³à²µà²²à³à²²à²¿ ವಿಫಲವಾಗಿದೆ</translation>
<translation id="5631439013527180824">ಅಮಾನà³à²¯à²µà²¾à²¦ ಸಾಧನ ನಿರà³à²µà²¹à²£à³† ಟೋಕನà³</translation>
+<translation id="5669703222995421982">ವೈಯಕà³à²¤à³€à²•à²°à²¿à²¸à²²à²¾à²¦ ವಿಷಯವನà³à²¨à³ ಪಡೆಯಿರಿ</translation>
+<translation id="5675650730144413517">ಈ ಪà³à²Ÿ ಕಾರà³à²¯à²¨à²¿à²°à³à²µà²¹à²¿à²¸à³à²¤à³à²¤à²¿à²²à³à²²</translation>
<translation id="5677928146339483299">ನಿರà³à²¬à²‚ಧಿಸಲಾಗಿದೆ</translation>
<translation id="5694783966845939798">ನೀವೠ<ph name="DOMAIN" /> ತಲà³à²ªà²²à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿à²°à³à²µà²¿à²°à²¿, ಆದರೆ ದà³à²°à³à²¬à²² ಸಹಿ ಅಲà³à²—ಾರಿದಮೠಬಳಸಿಕೊಂಡೠಸಹಿ ಮಾಡಿದ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಸರà³à²µà²°à³ ಪà³à²°à²¸à³à²¤à³à²¤à²ªà²¡à²¿à²¸à²¿à²¦à³† (SHA-1 ನಂತೆಯೇ). ಇದರರà³à²¥ ಸರà³à²µà²°à³ ಪà³à²°à²¸à³à²¤à³à²¤à²ªà²¡à²¿à²¸à²¿à²¦ ಸà³à²°à²•à³à²·à²¤à²¾ ರà³à²œà³à²µà²¾à²¤à³à²—ಳೠನಕಲಿ ಆಗಿರಬಹà³à²¦à³ ಮತà³à²¤à³ ನೀವೠನಿರೀಕà³à²·à²¿à²¸à²¿à²¦ ಸರà³à²µà²°à³ ಅದಾಗಿರದೇ ಇರಬಹà³à²¦à³ (ನೀವೠಆಕà³à²°à²®à²£à²•à²¾à²°à²° ಜೊತೆಗೆ ಸಂವಹನ ಮಾಡà³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³). <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">ಈ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²¨ ಗà³à²°à³à²¤à²¿à²¸à³à²µà²¿à²•à³†à²¯à²¨à³à²¨à³ ಇನà³à²¨à³‚ ಪರಿಶೀಲಿಸಲಾಗಿಲà³à²².</translation>
<translation id="5720705177508910913">ಪà³à²°à²¸à³à²¤à³à²¤ ಬಳಕೆದಾರ</translation>
-<translation id="572328651809341494">ಇತà³à²¤à³€à²šà²¿à²¨ ಟà³à²¯à²¾à²¬à³â€Œà²—ಳà³</translation>
<translation id="5732392974455271431">ನಿಮà³à²® ಪೋಷಕರೠನಿಮಗಾಗಿ ಅದನà³à²¨à³ ಅನಿರà³à²¬à²‚ಧಿಸಬಹà³à²¦à²¾à²—ಿದೆ</translation>
<translation id="5784606427469807560">ನಿಮà³à²® ಕಾರà³à²¡à³ ಅನà³à²¨à³ ದೃಢೀಕರಿಸà³à²µà²²à³à²²à²¿ ಸಮಸà³à²¯à³† ಇದೆ. ನಿಮà³à²® ಇಂಟರà³à²¨à³†à²Ÿà³ ಸಂಪರà³à²•à²µà²¨à³à²¨à³ ಪರಿಶೀಲಿಸಿ ಹಾಗೂ ಮತà³à²¤à³† ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿.</translation>
<translation id="5785756445106461925">ಅಲà³à²²à²¦à³‡, ಸà³à²°à²•à³à²·à²¿à²¤à²µà²²à³à²²à²¦ ಸಂಪನà³à²®à³‚ಲಗಳನà³à²¨à³ ಈ ಪà³à²Ÿ ಒಳಗೊಂಡಿದೆ. ಪà³à²°à²¯à²¾à²£à²¦ ಸಂದರà³à²­à²¦à²²à³à²²à²¿ ಈ ಸಂಪನà³à²®à³‚ಲಗಳನà³à²¨à³ ಇತರರೂ ವೀಕà³à²·à²¿à²¸à²¬à²¹à³à²¦à²¾à²—ಿದೆ ಮತà³à²¤à³ ಪà³à²Ÿà²¦ ನೋಟವೇ ಬದಲಾಗà³à²µà²‚ತೆ ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ಅದನà³à²¨à³ ತಿದà³à²¦à²¬à²¹à³à²¦à²¾à²—ಿದೆ.</translation>
@@ -441,6 +465,7 @@
<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="5843436854350372569">ನೀವೠ<ph name="DOMAIN" /> ತಲà³à²ªà²²à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿à²°à³à²µà²¿à²°à²¿, ಆದರೆ ದà³à²°à³à²¬à²² ಕೀಯನà³à²¨à³ ಹೊಂದಿರà³à²µ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಸರà³à²µà²°à³ ಪà³à²°à²¸à³à²¤à³à²¤à²ªà²¡à²¿à²¸à²¿à²¦à³†. ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ಖಾಸಗಿ ಕೀಯನà³à²¨à³ ನಾಶ ಪಡಿಸಿರಬಹà³à²¦à³ ಮತà³à²¤à³ ನೀವೠನಿರೀಕà³à²·à²¿à²¸à²¿à²¦ ಸರà³à²µà²°à³ ಅದಾಗಿರದೇ ಇರಬಹà³à²¦à³ (ನೀವೠಆಕà³à²°à²®à²£à²•à²¾à²°à²° ಜೊತೆಗೆ ಸಂವಹನ ಮಾಡà³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³). <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">ಹೊಸ ಫೋಲà³à²¡à²°à³</translation>
<translation id="5869405914158311789">ಈ ಸೈಟೠತಲà³à²ªà²²à²¾à²—à³à²µà³à²¦à²¿à²²à³à²²</translation>
@@ -450,6 +475,7 @@
<translation id="59107663811261420">ಈ ವರà³à²¤à²•à²°à²¿à²—ೆ Google Payments ನಿಂದ ಈ ಪà³à²°à²•à²¾à²°à²¦ ಕಾರà³à²¡à³ ಬೆಂಬಲಿತವಾಗಿಲà³à²². ದಯವಿಟà³à²Ÿà³ ಬೇರೆಯ ಕಾರà³à²¡à³ ಆಯà³à²•à³†à²®à²¾à²¡à²¿.</translation>
<translation id="59174027418879706">ಸಕà³à²°à²¿à²¯à²—ೊಳಿಸಲಾಗಿದೆ</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="5966707198760109579">ವಾರ</translation>
<translation id="5967867314010545767">ಇತಿಹಾಸದಿಂದ ತೆಗೆದà³à²¹à²¾à²•à²¿</translation>
<translation id="5975083100439434680">à²à³‚ಮೠಔಟà³</translation>
@@ -458,7 +484,7 @@
<translation id="6008256403891681546">JCB</translation>
<translation id="6016158022840135739">{COUNT,plural, =1{ಪà³à²Ÿ 1}one{ಪà³à²Ÿ #}other{ಪà³à²Ÿ #}}</translation>
<translation id="6017514345406065928">ಹಸಿರà³</translation>
-<translation id="6040143037577758943">ಮà³à²šà³à²šà³</translation>
+<translation id="6040143037577758943">ಮà³à²šà³à²šà²¿à²°à²¿</translation>
<translation id="604124094241169006">ಸà³à²µà²¯à²‚ಚಾಲಿತ</translation>
<translation id="6042308850641462728">ಇನà³à²¨à²·à³à²Ÿà³</translation>
<translation id="6060685159320643512">ಜಾಗà³à²°à²¤à³†, ಈ ಪà³à²°à²¯à³‹à²—ಗಳೠವಿಫಲವಾಗಬಹà³à²¦à³</translation>
@@ -467,8 +493,11 @@
<translation id="614940544461990577">ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿:</translation>
<translation id="6151417162996330722">ಸರà³à²µà²°à³ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà³ ತà³à²‚ಬಾ ಉದà³à²¦à²µà²¾à²¦ ವಾಯಿದೆ ಅವಧಿಯನà³à²¨à³ ಹೊಂದಿದೆ.</translation>
<translation id="6165508094623778733">ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ</translation>
+<translation id="6177128806592000436">ಈ ಸೈಟà³â€Œà²—ೆ ನಿಮà³à²® ಸಂಪರà³à²•à²µà³ ಸà³à²°à²•à³à²·à²¿à²¤à²µà²¾à²—ಿಲà³à²²</translation>
<translation id="6203231073485539293">ನಿಮà³à²® ಇಂಟರà³à²¨à³†à²Ÿà³ ಸಂಪರà³à²•à²µà²¨à³à²¨à³ ಪರಿಶೀಲಿಸಿ</translation>
<translation id="6218753634732582820">Chromium ನಿಂದ ವಿಳಾಸವನà³à²¨à³ ತೆಗೆದà³à²¹à²¾à²•à³à²µà³à²¦à³‡?</translation>
+<translation id="6251924700383757765">ಗೌಪà³à²¯à²¤à²¾ ನೀತಿ</translation>
+<translation id="625755898061068298">ಈ ಸೈಟà³â€Œà²—ೆ ನೀವೠಸà³à²°à²•à³à²·à²¤à³† ಎಚà³à²šà²°à²¿à²•à³†à²—ಳನà³à²¨à³ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಲೠಆಯà³à²•à³† ಮಾಡಿರà³à²µà²¿à²°à²¿.</translation>
<translation id="6259156558325130047">&amp;ಮರà³à²•à³à²°à²®à²—ೊಳಿಸà³à²µà³à²¦à²¨à³à²¨à³ ಮತà³à²¤à³†à²®à²¾à²¡à³</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳà³</translation>
<translation id="6264485186158353794">ಸà³à²°à²•à³à²·à²¤à³†à²—ೆ ಹಿಂದಿರà³à²—ಿ</translation>
@@ -477,6 +506,7 @@
<translation id="6305205051461490394"><ph name="URL" /> ತಲà³à²ªà²²à²¾à²—à³à²µà³à²¦à²¿à²²à³à²².</translation>
<translation id="6321917430147971392">ನಿಮà³à²® DNS ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳನà³à²¨à³ ಪರಿಶೀಲಿಸಿ</translation>
<translation id="6328639280570009161">ನೆಟà³â€Œà²µà²°à³à²•à³ ಮà³à²¨à³à²¸à³‚ಚನೆಯನà³à²¨à³ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಲೠಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿</translation>
+<translation id="6328786501058569169">ಈ ಸೈಟೠಮೋಸಗೊಳಿಸà³à²µà³à²¦à²¾à²—ಿದೆ</translation>
<translation id="6337534724793800597">ಹೆಸರಿನ ಪà³à²°à²•à²¾à²°à²µà²¾à²—ಿ ನೀತಿಗಳನà³à²¨à³ ಫಿಲà³à²Ÿà²°à³ ಮಾಡಿ</translation>
<translation id="6342069812937806050">ಇದೀಗ</translation>
<translation id="6345221851280129312">ಅಪರಿಚಿತ ಗಾತà³à²°</translation>
@@ -485,6 +515,8 @@
<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="6404511346730675251">ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳನà³à²¨à³ ಎಡಿಟೠಮಾಡಿ</translation>
+<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> ಗೆ ಮà³à²•à³à²¤à²¾à²¯ ದಿನಾಂಕ ಮತà³à²¤à³ ಸಿವಿಸಿ ಅನà³à²¨à³ ನಮೂದಿಸಿ</translation>
<translation id="6414888972213066896">ಈ ಸೈಟà³â€Œà²—ೆ ಭೇಟಿ ನೀಡà³à²µà³à²¦à³ ಸರಿಯೇ ಎಂದೠನೀವೠನಿಮà³à²® ಪೋಷಕರನà³à²¨à³ ಕೇಳಿರà³à²µà²¿à²°à²¿</translation>
<translation id="6416403317709441254">Chromium ಪà³à²°à²•à³à²°à²¿à²¯à³†à²—ೊಳಿಸಲೠಸಾಧà³à²¯à²µà²¿à²²à³à²²à²¦ ಅವà³à²¯à²µà²¸à³à²¥à²¿à²¤ ರà³à²œà³à²µà²¾à²¤à³à²—ಳನà³à²¨à³ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³ ಕಳà³à²¹à²¿à²¸à²¿à²¦ ಕಾರಣ ಇದೀಗ ನೀವೠ<ph name="SITE" /> ಭೇಟಿ ಮಾಡಲೠಸಾಧà³à²¯à²µà²¿à²²à³à²². ನೆಟà³â€Œà²µà²°à³à²•à³ ದೋಷಗಳೠಮತà³à²¤à³ ದಾಳಿಗಳೠಸಾಮಾನà³à²¯à²µà²¾à²—ಿ ತಾತà³à²•à²¾à²²à²¿à²•à²µà²¾à²—ಿರà³à²¤à³à²¤à²µà³†, ಹೀಗಾಗಿ ಈ ಪà³à²Ÿà²µà³ ನಂತರ ಕಾರà³à²¯à²¨à²¿à²°à³à²µà²¹à²¿à²¸à²¬à²¹à³à²¦à³. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಹಿಂತೆಗೆದà³à²•à³Šà²³à³à²³à²²à²¾à²—ಿದೆಯೆ ಎಂಬà³à²¦à²¨à³à²¨à³ ಪರಿಶೀಲಿಸಲೠಸಾಧà³à²¯à²µà²¿à²²à³à²².</translation>
@@ -494,10 +526,12 @@
<translation id="6458467102616083041">ಡಿಫಾಲà³à²Ÿà³ ಹà³à²¡à³à²•à²¾à²Ÿà²µà²¨à³à²¨à³ ನೀತಿಯಿಂದ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಲಾಗಿರà³à²µà³à²¦à²°à²¿à²‚ದ ನಿರà³à²²à²•à³à²·à²¿à²¸à²²à²¾à²—ಿದೆ.</translation>
<translation id="6462969404041126431">ಈ ಸರà³à²µà²°à³ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬà³à²¦à²¨à³à²¨à³ ಸಾಬೀತà³à²ªà²¡à²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¾à²—ಲಿಲà³à²²; ಅದರ ಸà³à²°à²•à³à²·à²¤à²¾ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಹಿಂಪಡೆದà³à²•à³Šà²‚ಡಿರಬಹà³à²¦à³. ಇದೠತಪà³à²ªà²¾à²¦ ಕಾನà³à²«à²¿à²—ರೇಶನà³â€Œà²¨à²¿à²‚ದ ಅಥವಾ ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ನಿಮà³à²® ಸಂಪರà³à²•à²¦à²²à³à²²à²¿ ಒಳನà³à²¸à³à²³à²¿à²°à³à²µà³à²¦à²°à²¿à²‚ದ ಆಗಿರಬಹà³à²¦à³. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">ಸಾಧನ ನೀತಿಗಳà³</translation>
+<translation id="6477321094435799029">ಈ ಪà³à²Ÿà²¦à²²à³à²²à²¿ ಅಸಹಜ ಕೋಡೠಅನà³à²¨à³ Chrome ಪತà³à²¤à³†à²¹à²šà³à²šà²¿à²¦à³† ಮತà³à²¤à³ ನಿಮà³à²® ವೈಯಕà³à²¤à²¿à²• ಮಾಹಿತಿಯನà³à²¨à³ (ಉದಾಹರಣೆಗೆ, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳà³, ಫೋನà³â€Œ ಸಂಖà³à²¯à³†à²—ಳೠಮತà³à²¤à³ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³â€Œà²—ಳà³) ರಕà³à²·à²¿à²¸à²²à³ ಅದನà³à²¨à³ ನಿರà³à²¬à²‚ಧಿಸಿದೆ.</translation>
<translation id="6489534406876378309">ವಿಫಲತೆಗಳನà³à²¨à³ ಅಪà³â€Œà²²à³‹à²¡à³â€Œ ಮಾಡà³à²µà³à²¦à²¨à³à²¨à³ ಪà³à²°à²¾à²°à²‚ಭಿಸà³</translation>
<translation id="6529602333819889595">&amp;ಅಳಿಸà³à²µà³à²¦à²¨à³à²¨à³ ಮತà³à²¤à³†à²®à²¾à²¡à³</translation>
<translation id="6534179046333460208">ಭೌತಿಕ ವೆಬೠಸಲಹೆಗಳà³</translation>
<translation id="6550675742724504774">ಆಯà³à²•à³†à²—ಳà³</translation>
+<translation id="6556239504065605927">ಸà³à²°à²•à³à²·à²¿à²¤ ಸಂಪರà³à²•</translation>
<translation id="6563469144985748109">ನಿಮà³à²® ಮà³à²¯à²¾à²¨à³†à³•à²œà²°à³ ಇನà³à²¨à³‚ ಇದನà³à²¨à³ ಅಂಗೀಕರಿಸಿಲà³à²²</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" /> ಗಿಂತ ಕಡಿಮೆ</translation>
<translation id="6596325263575161958">ಎನà³â€Œà²•à³à²°à²¿à²«à³à²¶à²¨à³ ಆಯà³à²•à³†à²—ಳà³</translation>
@@ -506,7 +540,6 @@
<translation id="6644283850729428850">ಈ ನೀತಿಯನà³à²¨à³ ವಿನಂತಿಸಲಾಗಿದೆ.</translation>
<translation id="6652240803263749613">ಈ ಸರà³à²µà²°à³ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬà³à²¦à²¨à³à²¨à³ ಸಾಬೀತà³à²ªà²¡à²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¾à²—ಲಿಲà³à²²; ಅದರ ಸà³à²°à²•à³à²·à²¤à²¾ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà³ ನಿಮà³à²® ಕಂಪà³à²¯à³‚ಟರà³â€Œà²¨ ಆಪರೇಟಿಂಗೠಸಿಸà³à²Ÿà²‚ನಿಂದ ವಿಶà³à²µà²¾à²¸à²¹à³Šà²‚ದಿಲà³à²². ಇದೠತಪà³à²ªà²¾à²¦ ಕಾನà³à²«à²¿à²—ರೇಶನà³â€Œà²¨à²¿à²‚ದ ಅಥವಾ ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ನಿಮà³à²® ಸಂಪರà³à²•à²¦à²²à³à²²à²¿ ಒಳನà³à²¸à³à²³à²¿à²°à³à²µà³à²¦à²°à²¿à²‚ದ ಆಗಿರಬಹà³à²¦à³. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">ಫೋಲà³à²¡à²°à³ ಎಡಿಟೠಮಾಡಿ</translation>
-<translation id="6660210980321319655"><ph name="CRASH_TIME" /> ಸಮಯಕà³à²•à³† ಸà³à²µà²¯à²‚ಚಾಲಿತವಾಗಿ ವರದಿ ಮಾಡಲಾಗಿದೆ</translation>
<translation id="6671697161687535275">Chromium ನಿಂದ ಫಾರà³à²®à³ ಸಲಹೆಯನà³à²¨à³ ತೆಗೆದà³à²¹à²¾à²•à³à²µà³à²¦à³‡?</translation>
<translation id="6685834062052613830">ಸೈನà³â€Œ ಔಟà³â€Œ ಮಾಡಿ ಹಾಗೂ ಸೆಟಪೠಪೂರà³à²£à²—ೊಳಿಸಿ</translation>
<translation id="6710213216561001401">ಹಿಂದೆ</translation>
@@ -518,6 +551,7 @@
<translation id="6753269504797312559">ನೀತಿ ಮೌಲà³à²¯</translation>
<translation id="6757797048963528358">ನಿಮà³à²® ಸಾಧನವೠನಿದà³à²°à²¾à²µà²¸à³à²¥à³†à²—ೆ ಹೋಗಿದೆ.</translation>
<translation id="6778737459546443941">ನಿಮà³à²® ಪೋಷಕರೠಇನà³à²¨à³‚ ಇದನà³à²¨à³ ಅಂಗೀಕರಿಸಿಲà³à²²</translation>
+<translation id="6810899417690483278">ಕಸà³à²Ÿà²®à³ˆà²¸à³‡à²¶à²¨à³ à²à²¡à²¿</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" /> ರಲà³à²²à²¿ ವೆಬà³â€Œà²ªà³à²Ÿà²µà³ ಪà³à²°à²¸à³à²¤à³à²¤ ಲಭà³à²¯à²µà²¿à²²à³à²². ನಿರà³à²µà²¹à²£à³†à²—ಾಗಿ ಇದೠಓವರೠಲೋಡೠಆಗಿರಬಹà³à²¦à³ ಅಥವಾ ಸà³à²¥à²—ಿತಗೊಂಡಿರಬಹà³à²¦à³. </translation>
<translation id="6831043979455480757">ಅನà³à²µà²¾à²¦à²¿à²¸à³</translation>
@@ -538,6 +572,8 @@
<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>
+<translation id="7064851114919012435">ಸಂಪರà³à²• ಮಾಹಿತಿ</translation>
+<translation id="7079718277001814089">ಈ ಸೈಟೠಮಾಲà³â€Œâ€Œà²µà³‡à²°à³ ಹೊಂದಿದೆ</translation>
<translation id="7087282848513945231">ರಾಷà³à²Ÿà³à²°</translation>
<translation id="7088615885725309056">ಹಳೆಯದà³</translation>
<translation id="7090678807593890770"><ph name="LINK" /> ಗೆ Google ಹà³à²¡à³à²•à²¾à²Ÿ</translation>
@@ -552,6 +588,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> ಭದà³à²°à²¤à³† ಮಾನದಂಡಗಳನà³à²¨à³ ಅನà³à²¸à²°à²¿à²¸à³à²¤à³à²¤à²¿à²²à³à²².</translation>
<translation id="721197778055552897">ಈ ತೊಂದರೆಯ ಬಗà³à²—ೆ <ph name="BEGIN_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LINK" />.</translation>
<translation id="7219179957768738017">ಸಂಪರà³à²•à²µà³ <ph name="SSL_VERSION" /> ಅನà³à²¨à³ ಬಳಸà³à²¤à³à²¤à²¦à³†.</translation>
+<translation id="724691107663265825">ಮà³à²‚ದಿರà³à²µ ಸೈಟà³â€Œà²¨à²²à³à²²à²¿ ಮಾಲà³â€Œà²µà³‡à²°à³ ಇದೆ</translation>
<translation id="724975217298816891">ನಿಮà³à²® ಕಾರà³à²¡à³â€Œ ವಿವರಗಳನà³à²¨à³ ಅಪà³â€Œà²¡à³‡à²Ÿà³â€Œ ಮಾಡಲೠ<ph name="CREDIT_CARD" /> ಗೆ ಮà³à²•à³à²¤à²¾à²¯ ದಿನಾಂಕ ಮತà³à²¤à³ CVC ಅನà³à²¨à³ ನಮೂದಿಸಿ. ನೀವೠಒಮà³à²®à³† ಖಚಿತಪಡಿಸಿದರೆ, ನಿಮà³à²® ಕಾರà³à²¡à³ ವಿವರಗಳನà³à²¨à³ ಈ ಸೈಟೠಜೊತೆಗೆ ಹಂಚಿಕೊಳà³à²³à²²à²¾à²—à³à²¤à³à²¤à²¦à³†.</translation>
<translation id="725866823122871198">ನಿಮà³à²® ಕಂಪà³à²¯à³‚ಟರà³â€Œà²¨ ದಿನಾಂಕ ಮತà³à²¤à³ ಸಮಯ (<ph name="DATE_AND_TIME" />) ತಪà³à²ªà²¾à²—ಿರà³à²µà³à²¦à²°à²¿à²‚ದ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ಗೆ ಖಾಸಗಿ ಸಂಪರà³à²•à²µà²¨à³à²¨à³ ಸà³à²¥à²¾à²ªà²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¿à²²à³à²².</translation>
<translation id="7269802741830436641">ಈ ವೆಬà³â€Œà²ªà³à²Ÿà²µà³ ಮರà³à²¨à²¿à²°à³à²¦à³‡à²¶à²¿à²¸à³à²µà²¿à²•à³† ಲೂಪೠಅನà³à²¨à³ ಹೊಂದಿದೆ</translation>
@@ -607,6 +644,7 @@
<translation id="7658239707568436148">ರದà³à²¦à³à²®à²¾à²¡à²¿</translation>
<translation id="7667346355482952095">ಹಿಂತಿರà³à²—ಿಸಲಾದ ನೀತಿಯ ಟೋಕನà³â€Œ ಖಾಲಿ ಇದೆ ಅಥವಾ ಪà³à²°à²¸à³à²¤à³à²¤ ಟೋಕನà³â€Œà²—ೆ ಹೊಂದಾಣಿಕೆಯಾಗà³à²µà³à²¦à²¿à²²à³à²²</translation>
<translation id="7668654391829183341">ಅಪರಿಚಿತ ಸಾಧನ</translation>
+<translation id="7669271284792375604">ನಿಮà³à²® ಬà³à²°à³Œà²¸à²¿à²‚ಗà³â€Œ ಅನà³à²­à²µà²µà²¨à³à²¨à³ ಹಾನಿಮಾಡಲೠಸà³à²¥à²¾à²ªà²¿à²¸à²²à²¾à²—à³à²µ ಪà³à²°à³‹à²—à³à²°à²¾à²‚ಗಳಲà³à²²à²¿ ನಿಮà³à²®à²¨à³à²¨à³ ವಂಚಿಸಲೠಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ಈ ಸೈಟà³â€Œ ಮೇಲೆ ದಾಳಿ ಮಾಡಬಹà³à²¦à³ (ಉದಾಹರಣೆಗೆ, ನಿಮà³à²® ಮà³à²–ಪà³à²Ÿà²µà²¨à³à²¨à³ ಬದಲಾಯಿಸಲಾಗà³à²¤à³à²¤à²¦à³† ಅಥವಾ ನೀವೠಭೇಟಿ ನೀಡà³à²µ ಸೈಟà³â€Œà²—ಳಲà³à²²à²¿ ಹೆಚà³à²šà²¿à²¨ ಜಾಹೀರಾತà³à²—ಳನà³à²¨à³ ತೋರಿಸಲಾಗà³à²¤à³à²¤à²¦à³†).</translation>
<translation id="7674629440242451245">ಉತà³à²¤à²®à²µà²¾à²¦ ಹೊಸ Chrome ವೈಶಿಷà³à²Ÿà³à²¯à²—ಳಲà³à²²à²¿ ಆಸಕà³à²¤à²¿ ಇದೆಯೇ? chrome.com/dev ನಲà³à²²à²¿ ನಮà³à²® dev ಚಾನಲೠಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /><ph name="SITE" /> ಗೆ (ಅಸà³à²°à²•à³à²·à²¿à²¤) ಮà³à²‚ದà³à²µà²°à³†à²¸à³<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">ಸಂಗà³à²°à²¹à²¦à²¿à²‚ದ ಈ ಸೈಟೠಲೋಡೠಮಾಡಲಾಗà³à²µà³à²¦à²¿à²²à³à²²</translation>
@@ -622,14 +660,17 @@
<translation id="7800304661137206267">ಸಂದೇಶದ ದೃಢೀಕರಣಕà³à²•à²¾à²—ಿ <ph name="MAC" /> ಮತà³à²¤à³ <ph name="KX" /> ನಂತೆ ಕೀಲಿ ವಿನಿಮಯದ ಯಾಂತà³à²°à²¿à²•à²¤à³† ಜೊತೆಗೆ <ph name="CIPHER" /> ಅನà³à²¨à³ ಬಳಸಿ ಸಂಪರà³à²•à²µà²¨à³à²¨à³ ಎನà³â€Œà²•à³à²°à²¿à²ªà³à²Ÿà³ ಮಾಡಲಾಗಿದೆ.</translation>
<translation id="780301667611848630">ಬೇಡ, ಧನà³à²¯à²µà²¾à²¦à²—ಳà³</translation>
<translation id="7805768142964895445">ಸà³à²¥à²¿à²¤à²¿</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Chrome ನಿಂದ ಫಾರà³à²®à³ ಸಲಹೆಯನà³à²¨à³ ತೆಗೆದà³à²¹à²¾à²•à³à²µà³à²¦à³‡?</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="7887683347370398519">ನಿಮà³à²® CVC ಅನà³à²¨à³ ಪರಿಶೀಲಿಸಿ ಹಾಗೂ ಮತà³à²¤à³† ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿</translation>
<translation id="7912024687060120840">ಫೋಲà³à²¡à²°à³â€Œà²¨à²²à³à²²à²¿:</translation>
<translation id="7935318582918952113">DOM ಡಿಸà³à²Ÿà²¿à²²à²°à³</translation>
<translation id="7938958445268990899">ಸರà³à²µà²°à³â€Œà²¨ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà²¨à³à²¨à³ ಇನà³à²¨à³‚ ಮಾನà³à²¯à²—ೊಳಿಸಿಲà³à²².</translation>
<translation id="7942349550061667556">ಕೆಂಪà³</translation>
+<translation id="7947285636476623132">ನಿಮà³à²® ಮà³à²•à³à²¤à²¾à²¯ ವರà³à²·à²µà²¨à³à²¨à³ ಪರಿಶೀಲಿಸಿ ಮತà³à²¤à³ ಪà³à²¨à²ƒ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿</translation>
<translation id="7951415247503192394">(32-ಬಿಟà³)</translation>
<translation id="7956713633345437162">ಮೊಬೈಲೠಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳà³</translation>
<translation id="7961015016161918242">ಎಂದಿಗೂ ಇಲà³à²²</translation>
@@ -637,7 +678,10 @@
<translation id="7983301409776629893">ಯಾವಾಗಲೂ <ph name="ORIGINAL_LANGUAGE" /> ಅನà³à²¨à³ <ph name="TARGET_LANGUAGE" /> ಗೆ ಅನà³à²µà²¾à²¦à²¿à²¸à²¿</translation>
<translation id="7995512525968007366">ನಿರà³à²¦à²¿à²·à³à²Ÿà²ªà²¡à²¿à²¸à²²à²¾à²—ಿಲà³à²²</translation>
<translation id="8012647001091218357">ಈ ಕà³à²·à²£à²¦à²²à³à²²à²¿ ನಿಮà³à²® ಪೋಷಕರನà³à²¨à³ ತಲà³à²ªà²²à³ ನಮಗೆ ಸಾಧà³à²¯à²µà²¾à²—ಲಿಲà³à²². ಮತà³à²¤à³† ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à²¿.</translation>
+<translation id="8025119109950072390">ಈ ಸೈಟà³â€Œà²¨à²²à³à²²à²¿à²¨ ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ಸಾಫà³à²Ÿà³â€Œà²µà³‡à²°à³ ಸà³à²¥à²¾à²ªà²¿à²¸à³à²µà²¿à²•à³† ಅಥವಾ ನಿಮà³à²® ವೈಯಕà³à²¤à²¿à²• ಮಾಹಿತಿಯನà³à²¨à³ ಬಹಿರಂಗ ಪಡಿಸà³à²µà²‚ತಹ ಅಪಾಯಕಾರಿಯಾಗಿ à²à²¨à²¾à²¦à²°à³‚ ಮಾಡà³à²µà²‚ತಹ ಮೋಸವನà³à²¨à³ ಮಾಡಬಹà³à²¦à³ (ಉದಾಹರಣೆಗೆ, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳà³, ಫೋನà³â€Œ ಸಂಖà³à²¯à³†à²—ಳೠಅಥವಾ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³â€Œà²—ಳà³).</translation>
+<translation id="803030522067524905">Google ಸà³à²°à²•à³à²·à²¿à²¤ ಬà³à²°à³Œà²¸à³ ಮಾಡà³à²µà²¿à²•à³† ಇತà³à²¤à³€à²šà³†à²—ೆ <ph name="SITE" /> ನಲà³à²²à²¿ ಫಿಶಿಂಗೠಪತà³à²¤à³†à²¹à²šà³à²šà²¿à²¦à³†. ಫಿಶಿಂಗೠಸೈಟà³â€Œà²—ಳೠನಿಮà³à²®à²¨à³à²¨à³ ಮೋಸಗೊಳಿಸಲೠಇತರ ವೆಬà³â€Œà²¸à³ˆà²Ÿà³â€Œà²—ಳಂತೆ ಸೋಗೠಹಾಕà³à²¤à³à²¤à²µà³†. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">ಈ ಪà³à²Ÿà²µà³ <ph name="SOURCE_LANGUAGE" /> ನಲà³à²²à²¿ ಇದೆ. ಇದನà³à²¨à³ <ph name="TARGET_LANGUAGE" /> ಗೆ ಅನà³à²µà²¾à²¦à²¿à²¸à³à²µà³à²¦à³‡?</translation>
+<translation id="8041089156583427627">ಪà³à²°à²¤à²¿à²•à³à²°à²¿à²¯à³† ಕಳà³à²¹à²¿à²¸à²¿</translation>
<translation id="8088680233425245692">ಲೇಖನವನà³à²¨à³ ವೀಕà³à²·à²¿à²¸à²²à³ ವಿಫಲವಾಗಿದೆ.</translation>
<translation id="8089520772729574115">1 MB ಗಿಂತ ಕಡಿಮೆ</translation>
<translation id="8091372947890762290">ಸರà³à²µà²°à³â€Œà²¨à²²à³à²²à²¿ ಸಕà³à²°à²¿à²¯à²¤à³† ಬಾಕಿ ಉಳಿದಿದೆ</translation>
@@ -648,9 +692,11 @@
<translation id="8150722005171944719"><ph name="URL" /> ನಲà³à²²à²¿à²¨ ಫೈಲೠಓದà³à²µà²‚ತಿರà³à²µà³à²¦à²¿à²²à³à²². ಇದನà³à²¨à³ ತೆಗೆದà³à²¹à²¾à²•à²¬à²¹à³à²¦à³, ಚಲಿಸಬಹà³à²¦à³, ಅಥವಾ ಫೈಲೠಅನà³à²®à²¤à²¿à²—ಳೠಪà³à²°à²µà³‡à²¶à²µà²¨à³à²¨à³ ತಡೆಗಟà³à²Ÿà³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³.</translation>
<translation id="8194797478851900357">&amp;ಸರಿಸà³à²µà³à²¦à²¨à³à²¨à³ ರದà³à²¦à³à²—ೊಳಿಸà³</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" ID ಜೊತೆಗಿನ ವಿಸà³à²¤à²°à²£à³†à²—ೆ ಅಮಾನà³à²¯à²µà²¾à²¦ ಅಪà³â€Œà²¡à³‡à²Ÿà³â€Œâ€Œ URL.</translation>
+<translation id="8202097416529803614">ಆರà³à²¡à²°à³ ಸಾರಾಂಶ</translation>
<translation id="8218327578424803826">ನಿಯೋಜಿಸಲಾದ ಸà³à²¥à²³:</translation>
<translation id="8225771182978767009">ಈ ಕಂಪà³à²¯à³‚ಟರೠಹೊಂದಿಸಿರà³à²µ ವà³à²¯à²•à³à²¤à²¿à²¯à³ ಈ ಸೈಟೠನಿರà³à²¬à²‚ಧಿಸಲೠಆಯà³à²•à³†à²®à²¾à²¡à²¿à²¦à³à²¦à²¾à²°à³†.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ನಲà³à²²à²¿à²°à³à²µ ದಾಳಿಕೋರರೠನಿಮà³à²® ಕಂಪà³à²¯à³‚ಟರà³â€Œà²¨à²²à³à²²à²¿à²°à³à²µ ಮಾಹಿತಿ (ಉದಾಹರಣೆಗೆ, ಫೋಟೋಗಳà³, ಪಾಸà³â€Œà²µà²°à³à²¡à³â€Œà²—ಳೠಮತà³à²¤à³ ಕà³à²°à³†à²¡à²¿à²Ÿà³ ಕಾರà³à²¡à³ ಮಾಹಿತಿಗಳà³) ಕದಿಯಲೠಇಲà³à²²à²µà³‡ ಅಳಿಸಲೠಅಪಾಯಕಾರಿ ಪà³à²°à³‹à²—à³à²°à²¾à²‚ಗಳನà³à²¨à³ ಸà³à²¥à²¾à²ªà²¿à²¸à²²à³ ಪà³à²°à²¯à²¤à³à²¨à²¿à²¸à³à²¤à³à²¤à²¿à²°à²¬à²¹à³à²¦à³.</translation>
<translation id="8241707690549784388">ನೀವೠಎದà³à²°à³ ನೋಡà³à²¤à³à²¤à²¿à²°à³à²µ ಪà³à²Ÿ ನೀವೠನಮೂದಿಸಿದ ಮಾಹಿತಿಯನà³à²¨à³ ಬಳಸಿದೆ. ಆ ಪà³à²Ÿà²•à³à²•à³† ಹಿಂದಿರà³à²—à³à²µà³à²¦à²°à²¿à²‚ದ ನೀವೠಮಾಡಿದ ಯಾವà³à²¦à³‡ ಕà³à²°à²¿à²¯à³† ಪà³à²¨à²°à²¾à²µà²°à³à²¤à²¿à²¸à³à²µà²‚ತೆ ಮಾಡà³à²¤à³à²¤à²¦à³†. ನೀವೠಮà³à²‚ದà³à²µà²°à²¿à²¸à²²à³ ಬಯಸà³à²¤à³à²¤à³€à²°à²¾?</translation>
<translation id="8249320324621329438">ಕಳೆದ ಬಾರಿ ಪಡೆದಿರà³à²µà³à²¦à³:</translation>
<translation id="8261506727792406068">ಅಳಿಸà³</translation>
@@ -659,11 +705,13 @@
<translation id="8294431847097064396">ಮೂಲ</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="8349305172487531364">ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳ ಬಾರà³</translation>
<translation id="8363502534493474904">à²à²°à³â€Œà²ªà³à²²à³‡à²¨à³ ಮೋಡೠಆಫà³â€Œ ಮಾಡಲಾಗà³à²¤à³à²¤à²¿à²¦à³†</translation>
<translation id="8364627913115013041">ಹೊಂದಿಸಿಲà³à²².</translation>
<translation id="8380941800586852976">ಅಪಾಯಕಾರಿ</translation>
<translation id="8382348898565613901">ನೀವೠಇತà³à²¤à³€à²šà²¿à²—ೆ ಭೇಟಿ ನೀಡಿದ ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳೠಇಲà³à²²à²¿ ಕಾಣಿಸಿಕೊಳà³à²³à³à²¤à³à²¤à²µà³†</translation>
+<translation id="8398259832188219207"><ph name="UPLOAD_TIME" /> ಸಮಯಕà³à²•à³† ಕà³à²°à³à²¯à²¾à²¶à³ ವರದಿಯನà³à²¨à³ ಅಪà³â€Œà²²à³‹à²¡à³ ಮಾಡಲಾಗಿದೆ</translation>
<translation id="8412145213513410671">(<ph name="CRASH_COUNT" />) ಕà³à²°â€à³à²¯à²¾à²¶à³â€Œà²—ಳೠ</translation>
<translation id="8412392972487953978">ನೀವೠಒಂದೇ ರೀತಿಯ ಪಾಸà³â€Œà²«à³à²°à³‡à²¸à³ ಅನà³à²¨à³ ಎರಡೠಬಾರಿ ನಮೂದಿಸಬೇಕà³.</translation>
<translation id="8428213095426709021">ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳà³</translation>
@@ -675,9 +723,9 @@
<translation id="8498891568109133222">ಪà³à²°à²¤à²¿à²•à³à²°à²¿à²¯à²¿à²¸à²²à³ <ph name="HOST_NAME" /> ಹೆಚà³à²šà³ ಸಮಯ ತೆಗೆದà³à²•à³Šà²‚ಡಿದೆ.</translation>
<translation id="852346902619691059">ಈ ಸರà³à²µà²°à³ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬà³à²¦à²¨à³à²¨à³ ಸಾಬೀತà³à²ªà²¡à²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¾à²—ಲಿಲà³à²²; ಅದರ ಸà³à²°à²•à³à²·à²¤à²¾ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà³ ನಿಮà³à²® ಸಾಧನದ ಆಪರೇಟಿಂಗೠಸಿಸà³à²Ÿà²‚ನಿಂದ ವಿಶà³à²µà²¾à²¸à²¹à³Šà²‚ದಿಲà³à²². ಇದೠತಪà³à²ªà²¾à²¦ ಕಾನà³à²«à²¿à²—ರೇಶನà³â€Œà²¨à²¿à²‚ದ ಅಥವಾ ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ನಿಮà³à²® ಸಂಪರà³à²•à²¦à²²à³à²²à²¿ ಒಳನà³à²¸à³à²³à²¿à²°à³à²µà³à²¦à²°à²¿à²‚ದ ಆಗಿರಬಹà³à²¦à³. <ph name="BEGIN_LEARN_MORE_LINK" />ಇನà³à²¨à²·à³à²Ÿà³ ತಿಳಿಯಿರಿ<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">ಈ ಪà³à²°à²•à²¾à²°à²¦ ಕಾರà³à²¡à³ ಅನà³à²¨à³ Google Payments ಬೆಂಬಲಿಸà³à²µà³à²¦à²¿à²²à³à²². ದಯವಿಟà³à²Ÿà³ ಬೇರೆ ಕಾರà³à²¡à³ ಆಯà³à²•à³†à²®à²¾à²¡à²¿.</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="8553075262323480129">ಪà³à²Ÿà²¦ ಭಾಷೆಯನà³à²¨à³ ಗà³à²°à³à²¤à²¿à²¸à²²à³ ಅಸಾಧà³à²¯à²µà²¾à²¦ ಕಾರಣ ಭಾಷಾಂತರವೠವಿಫಲವಾಗಿದೆ.</translation>
<translation id="8559762987265718583"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ಗೆ ಖಾಸಗಿ ಸಂಪರà³à²•à²µà²¨à³à²¨à³ ಸà³à²¥à²¾à²ªà²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¿à²²à³à²² à²à²•à³†à²‚ದರೆ ನಿಮà³à²® ಸಾಧನದ ದಿನಾಂಕ ಮತà³à²¤à³ ಸಮಯ (<ph name="DATE_AND_TIME" />) ತಪà³à²ªà²¾à²—ಿದೆ.</translation>
-<translation id="856992080682148">ಈ ಸೈಟà³â€Œà²—ಾಗಿ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²¦ ಅವಧಿಯೠ2017 ಅಥವಾ ನಂತರದಲà³à²²à²¿ ಮà³à²•à³à²¤à²¾à²¯à²—ೊಳà³à²³à³à²¤à³à²¤à²¦à³† ಮತà³à²¤à³ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²° ಸರಣಿಯೠSHA-1 ಬಳಸಿಕೊಂಡೠಸಹಿ ಮಾಡಲಾದ ಪà³à²°à²®à²¾à²£à²ªà²¤à³à²° ಒಳಗೊಂಡಿರà³à²¤à³à²¤à²¦à³†.</translation>
<translation id="8571890674111243710"><ph name="LANGUAGE" /> ಗೆ ಪà³à²Ÿà²µà²¨à³à²¨à³ ಭಾಷಾಂತರಿಸಲಾಗà³à²¤à³à²¤à²¿à²¦à³†...</translation>
<translation id="859285277496340001">ಇದನà³à²¨à³ ರದà³à²¦à³à²®à²¾à²¡à²²à²¾à²—ಿದೆಯೆ ಎಂದೠಪರಿಶೀಲಿಸಲೠಪà³à²°à²®à²¾à²£à²ªà²¤à³à²°à²µà³ ಯಾಂತà³à²°à³€à²•à²°à²£à²µà²¨à³à²¨à³ ನಿರà³à²¦à²¿à²·à³à²Ÿà²ªà²¡à²¿à²¸à²¿à²²à³à²².</translation>
<translation id="8620436878122366504">ನಿಮà³à²® ಪೋಷಕರೠಇನà³à²¨à³‚ ಇದನà³à²¨à³ ಅಂಗೀಕರಿಸಿಲà³à²²</translation>
@@ -690,10 +738,12 @@
<translation id="8740359287975076522"><ph name="HOST_NAME" /> ನ &lt;abbr id="dnsDefinition"&gt;DNS ವಿಳಾಸ&lt;/abbr&gt; ಕಂಡà³à²¬à²°à²²à²¿à²²à³à²². ಸಮಸà³à²¯à³†à²¯à²¨à³à²¨à³ ಪತà³à²¤à³†à²¹à²šà³à²šà²²à²¾à²—à³à²¤à³à²¤à²¿à²¦à³†.</translation>
<translation id="8790007591277257123">&amp;ಅಳಿಸà³à²µà³à²¦à²¨à³à²¨à³ ಮತà³à²¤à³†à²®à²¾à²¡à³</translation>
<translation id="8798099450830957504">ಡಿಫಾಲà³à²Ÿà³</translation>
+<translation id="8800988563907321413">ನಿಮà³à²® ಸಮೀಪದ ವೆಬೠಪà³à²Ÿà²¦ ಸಲಹೆಗಳೠಇಲà³à²²à²¿ ಗೋಚರಿಸà³à²¤à³à²¤à²µà³†</translation>
<translation id="8804164990146287819">ಗೌಪà³à²¯à²¤à²¾ ನೀತಿ</translation>
<translation id="8820817407110198400">ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳà³</translation>
<translation id="8834246243508017242">ಸಂಪರà³à²•à²—ಳನà³à²¨à³ ಬಳಸಿಕೊಂಡೠಸà³à²µà²¯à²‚ತà³à²‚ಬà³à²µà²¿à²•à³†à²¯à²¨à³à²¨à³ ಸಕà³à²°à²¿à²¯à²—ೊಳಿಸà³â€¦</translation>
<translation id="883848425547221593">ಇತರ ಬà³à²•à³â€Œà²®à²¾à²°à³à²•à³â€Œà²—ಳà³:</translation>
+<translation id="884264119367021077">ಶಿಪà³à²ªà²¿à²‚ಗà³â€Œ ವಿಳಾಸ</translation>
<translation id="884923133447025588">ವಿಫಲವಾದ ಕಾರà³à²¯à²¤à²‚ತà³à²° ಪತà³à²¤à³†à²¯à²¾à²—ಿಲà³à²².</translation>
<translation id="885730110891505394">Google ಜೊತೆಗೆ ಹಂಚಿಕೊಳà³à²³à³à²µà³à²¦à³</translation>
<translation id="8866481888320382733">ನೀತಿಯ ಸೆಟà³à²Ÿà²¿à²‚ಗà³â€Œà²—ಳನà³à²¨à³ ಪಾಸೠಮಾಡà³à²µà²²à³à²²à²¿ ದೋಷ</translation>
@@ -711,18 +761,21 @@
<translation id="8971063699422889582">ಸರà³à²µà²°à³â€Œà²¨ ಪà³à²°à²•à²®à²¾à²£à²ªà²¤à³à²°à²¦ ಅವಧಿ ಮà³à²•à³à²¤à²¾à²¯à²—ೊಂಡಿದೆ.</translation>
<translation id="8987927404178983737">ತಿಂಗಳà³</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">ಈ ಮà³à²‚ದಕà³à²•à³† ಸೈಟೠಹಾನಿಕಾರಕ ಪà³à²°à³†à³‚ೕಗà³à²°à²¾à²‚ಗಳನà³à²¨à³ ಹೊಂದಿದೆ</translation>
<translation id="9001074447101275817"><ph name="DOMAIN" /> ಪà³à²°à²¾à²•à³à²¸à²¿à²—ೆ ಬಳಕೆದಾರಹೆಸರೠಮತà³à²¤à³ ಪಾಸà³â€Œà²µà²°à³à²¡à³ ಅಗತà³à²¯à²µà²¿à²°à³à²¤à³à²¤à²¦à³†.</translation>
<translation id="901974403500617787">ಸಿಸà³à²Ÿà²‚ನಾದà³à²¯à²‚ತ ಅನà³à²µà²¯à²µà²¾à²—à³à²µ ಫà³à²²à³à²¯à²¾à²—à³â€Œà²—ಳನà³à²¨à³ ಮಾಲೀಕರಿಂದ ಮಾತà³à²° ಹೊಂದಿಸಲೠಸಾಧà³à²¯: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">ಈ ಪà³à²Ÿà²µà²¨à³à²¨à³ <ph name="TARGET_LANGUAGE" /> ಗೆ ಅನà³à²µà²¾à²¦à²¿à²¸à²²à²¾à²—ಿದೆ</translation>
+<translation id="9035022520814077154">ಭದà³à²°à²¤à²¾ ದೋಷ</translation>
<translation id="9038649477754266430">ಪà³à²Ÿà²—ಳನà³à²¨à³ ಹೆಚà³à²šà³ ವೇಗವಾಗಿ ಲೋಡೠಮಾಡಲೠಮà³à²¨à³à²¨à³‹à²Ÿà²—ಳನà³à²¨à³ ಬಳಸಿ</translation>
<translation id="9039213469156557790">ಅಲà³à²²à²¦à³‡, ಸà³à²°à²•à³à²·à²¿à²¤à²µà²²à³à²²à²¦ ಸಂಪನà³à²®à³‚ಲಗಳನà³à²¨à³ ಈ ಪà³à²Ÿ ಹೊಂದಿದೆ. ಸà³à²¥à²¿à²¤à³à²¯à²‚ತರಗೊಳà³à²³à³à²µ ಸಂದರà³à²­à²¦à²²à³à²²à²¿ ಈ ಸಂಪನà³à²®à³‚ಲಗಳನà³à²¨à³ ಇತರರೂ ವೀಕà³à²·à²¿à²¸à²¬à²¹à³à²¦à²¾à²—ಿದೆ ಮತà³à²¤à³ ಪà³à²Ÿà²¦ ಹೊರನೋಟವೇ ಬದಲಾಗà³à²µà²‚ತೆ ಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ ಅದನà³à²¨à³ ತಿದà³à²¦à²¬à²¹à³à²¦à²¾à²—ಿದೆ.</translation>
+<translation id="9040185888511745258">ನಿಮà³à²® ಬà³à²°à³Œà²¸à²¿à²‚ಗà³â€Œ ಅನà³à²­à²µà²µà²¨à³à²¨à³ ಹಾನಿಮಾಡಲೠಸà³à²¥à²¾à²ªà²¿à²¸à²²à²¾à²—à³à²µ ಪà³à²°à³‹à²—à³à²°à²¾à²‚ಗಳಲà³à²²à²¿ ನಿಮà³à²®à²¨à³à²¨à³ ವಂಚಿಸಲೠಆಕà³à²°à²®à²£à²•à²¾à²°à²°à³ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ಮೇಲೆ ದಾಳಿ ಮಾಡಬಹà³à²¦à³ (ಉದಾಹರಣೆಗೆ, ನಿಮà³à²® ಮà³à²–ಪà³à²Ÿà²µà²¨à³à²¨à³ ಬದಲಾಯಿಸಲಾಗà³à²¤à³à²¤à²¦à³† ಅಥವಾ ನೀವೠಭೇಟಿ ನೀಡà³à²µ ಸೈಟà³â€Œà²—ಳಲà³à²²à²¿ ಹೆಚà³à²šà²¿à²¨ ಜಾಹೀರಾತà³à²—ಳನà³à²¨à³ ತೋರಿಸಲಾಗà³à²¤à³à²¤à²¦à³†).</translation>
<translation id="9050666287014529139">ಪಾಸà³â€Œà²«à³à²°à³‡à²¸à³</translation>
<translation id="9065203028668620118">ಎಡಿಟà³</translation>
<translation id="9068849894565669697">ಬಣà³à²£à²µà²¨à³à²¨à³ ಆಯà³à²•à³†à²®à²¾à²¡à²¿</translation>
<translation id="9076283476770535406">ಇದೠಪà³à²°à²¬à³à²¦à³à²§ ವಿಷಯವನà³à²¨à³ ಹೊಂದಿರಬಹà³à²¦à³</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> ಪà³à²Ÿ ಕಾರà³à²¯à²¨à²¿à²°à³à²µà²¹à²¿à²¸à³à²¤à³à²¤à²¿à²²à³à²²</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>
<translation id="9148507642005240123">&amp;ಸಂಪಾದಿಸà³à²µà³à²¦à²¨à³à²¨à³ ರದà³à²¦à³à²—ೊಳಿಸಿ</translation>
<translation id="9157595877708044936">ಹೊಂದಿಸಲಾಗà³à²¤à³à²¤à²¿à²¦à³†...</translation>
<translation id="9170848237812810038">&amp;ರದà³à²¦à³à²®à²¾à²¡à³</translation>
@@ -735,7 +788,6 @@
<translation id="935608979562296692">ಫಾರà³à²®à³ ತೆರವà³à²—ೊಳಿಸಿ</translation>
<translation id="939736085109172342">ಹೊಸ ಫೋಲà³à²¡à²°à³</translation>
<translation id="941721044073577244">ಈ ಸೈಟà³â€Œà²—ೆ ಭೇಟಿ ನೀಡಲೠನಿಮಗೆ ಅನà³à²®à²¤à²¿ ಇಲà³à²² ಎಂದೠತೋರà³à²¤à³à²¤à²¿à²¦à³†</translation>
-<translation id="962701380617707048">ನಿಮà³à²® ಕಾರà³à²¡à³â€Œ ವಿವರಗಳನà³à²¨à³ ಅಪà³â€Œà²¡à³‡à²Ÿà³â€Œ ಮಾಡಲೠ<ph name="CREDIT_CARD" /> ಗೆ ಮà³à²•à³à²¤à²¾à²¯ ದಿನಾಂಕ ಮತà³à²¤à³ CVC ಅನà³à²¨à³ ನಮೂದಿಸಿ</translation>
<translation id="969892804517981540">ಅಧಿಕೃತವಾಗಿ ನಿರà³à²®à²¿à²¸à²¿</translation>
<translation id="988159990683914416">ಡೆವಲಪರೠಬಿಲà³à²¡à³</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 4c6319afbcf..65a8fd27f0b 100644
--- a/chromium/components/strings/components_strings_ko.xtb
+++ b/chromium/components/strings/components_strings_ko.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Wi-Fiì— ë‹¤ì‹œ ì—°ê²°</translation>
<translation id="1175364870820465910">ì¸ì‡„(&amp;P)</translation>
<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="1206967143813997005">ìž˜ëª»ëœ ì´ˆê¸° 서명</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">ì²­ë¡ìƒ‰</translation>
<translation id="1629803312968146339">Chromeì—ì„œ ì´ ì¹´ë“œë¥¼ 저장하ë„ë¡ í•˜ì‹œê² ìŠµë‹ˆê¹Œ?</translation>
<translation id="1640180200866533862">ì‚¬ìš©ìž ì •ì±…</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />사ì´íŠ¸ì˜ 홈페ì´ì§€ë¥¼ 방문<ph name="END_LINK" />í•´ 보세요.</translation>
<translation id="1644184664548287040">ë„¤íŠ¸ì›Œí¬ êµ¬ì„±ì´ ìž˜ëª»ë˜ì–´ 가져올 수 없습니다.</translation>
<translation id="1644574205037202324">방문 기ë¡</translation>
<translation id="1645368109819982629">지ì›ë˜ì§€ 않는 프로토콜</translation>
<translation id="1676269943528358898"><ph name="SITE" />ì—서는 ì‚¬ìš©ìž ì •ë³´ë¥¼ 보호하기 위해 ì¼ë°˜ì ìœ¼ë¡œ 암호화를 사용합니다. ì´ë²ˆì— Chromeì—ì„œ <ph name="SITE" />ì— ì—°ê²°ì„ ì‹œë„í–ˆì„ ë•Œ 웹사ì´íŠ¸ì—ì„œ 비정ìƒì ì´ê³  ìž˜ëª»ëœ ì‚¬ìš©ìž ì¸ì¦ 정보를 반환했습니다. ì´ëŠ” 공격ìžê°€ <ph name="SITE" />ì¸ ê²ƒì²˜ëŸ¼ 가장하려고 하거나 Wi-Fi ë¡œê·¸ì¸ í™”ë©´ì´ ì—°ê²°ì„ ë°©í•´í–ˆê¸° ë•Œë¬¸ì¼ ìˆ˜ 있습니다. ë°ì´í„° êµí™˜ì´ ë°œìƒí•˜ê¸° ì „ì— Chromeì—ì„œ ì—°ê²°ì„ ì¤‘ë‹¨í–ˆê¸° ë•Œë¬¸ì— ì‚¬ìš©ìž ì •ë³´ëŠ” 안전합니다.</translation>
+<translation id="168328519870909584">현재 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ì˜ ê³µê²©ìžê°€ 사용 ì¤‘ì¸ ê¸°ê¸°ì— ì‚¬ìš©ìž ì •ë³´(예: 사진, 비밀번호, 메시지, ì‹ ìš©ì¹´ë“œ)를 ë„용하거나 삭제하는 위험한 ì•±ì„ ì„¤ì¹˜í•˜ë ¤ê³  ì‹œë„í•  수 있습니다.</translation>
<translation id="168841957122794586">서버 ì¸ì¦ì„œì— ì•ˆì „ì„±ì´ ë‚®ì€ ì•”í˜¸í™” 키가 í¬í•¨ë˜ì–´ 있습니다.</translation>
-<translation id="1701955595840307032">추천 콘í…츠 받기</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">ì´ ì‚¬ì´íŠ¸ë¥¼ 방문하려면 <ph name="NAME" />님으로부터 ê¶Œí•œì„ ë°›ì•„ì•¼ 합니다.</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">페ì´ì§€ 번호</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows ë„¤íŠ¸ì›Œí¬ ì§„ë‹¨ í”„ë¡œê·¸ëž¨ì„ ì‹¤í–‰<ph name="END_LINK" />í•´ 보세요.</translation>
<translation id="1783075131180517613">ë™ê¸°í™” 암호를 ì—…ë°ì´íŠ¸í•˜ì„¸ìš”.</translation>
+<translation id="1787142507584202372">열린 íƒ­ì´ ì—¬ê¸°ì— í‘œì‹œë©ë‹ˆë‹¤.</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google 세ì´í”„ 브ë¼ìš°ì§•ì´ 최근 <ph name="SITE" />ì—ì„œ <ph name="BEGIN_LINK" />멀웨어를 ê°ì§€<ph name="END_LINK" />했습니다. ì•ˆì „í•˜ë˜ ì›¹ì‚¬ì´íŠ¸ë„ ë©€ì›¨ì–´ì— ê°ì—¼ë˜ëŠ” 경우가 있습니다. 악성 콘í…ì¸ ì˜ ì¶œì²˜ëŠ” 알려진 멀웨어 ë°°í¬ìžì¸ <ph name="SUBRESOURCE_HOST" />입니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">ìž˜ëª»ëœ ìš”ì²­ ë˜ëŠ” 요청 매개변수</translation>
+<translation id="1834321415901700177">ì´ ì‚¬ì´íŠ¸ì— 유해한 í”„ë¡œê·¸ëž¨ì´ ìžˆìŠµë‹ˆë‹¤.</translation>
<translation id="1838667051080421715">지금 웹페ì´ì§€ì˜ 소스를 ë³´ê³  있습니다.</translation>
<translation id="1871208020102129563">프ë¡ì‹œê°€ .pac 스í¬ë¦½íŠ¸ URLì´ ì•„ë‹Œ ê³ ì • 프ë¡ì‹œ 서버를 사용하ë„ë¡ ì„¤ì •ë©ë‹ˆë‹¤.</translation>
<translation id="1883255238294161206">접기 목ë¡</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">우편번호</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{제안 1개}other{제안 #개}}</translation>
<translation id="2065985942032347596">ì¸ì¦ í•„ìš”</translation>
-<translation id="2079545284768500474">실행 취소</translation>
+<translation id="2079545284768500474">실행취소</translation>
<translation id="20817612488360358">시스템 프ë¡ì‹œ ì„¤ì •ì´ ì‚¬ìš©í•˜ë„ë¡ ì„¤ì •ë˜ì—ˆì§€ë§Œ ëª…ì‹œì  í”„ë¡ì‹œ ì„¤ì •ë„ ì§€ì •ë˜ì–´ 있습니다.</translation>
<translation id="2086652334978798447">Googleì—ì„œ 추천한 맞춤설정 콘í…츠를 받으려면 Chromeì— ë¡œê·¸ì¸í•©ë‹ˆë‹¤.</translation>
<translation id="2089090684895656482">숨기기</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">네ì´í‹°ë¸Œ í´ë¼ì´ì–¸íŠ¸</translation>
<translation id="213826338245044447">ëª¨ë°”ì¼ ë¶ë§ˆí¬</translation>
<translation id="2148716181193084225">오늘</translation>
-<translation id="2149973817440762519">ë¶ë§ˆí¬ 수정</translation>
<translation id="2154054054215849342">ë„ë©”ì¸ì— 대해 ë™ê¸°í™”를 사용할 수 없습니다.</translation>
<translation id="2166049586286450108">ì „ì²´ ê´€ë¦¬ìž ì•¡ì„¸ìŠ¤</translation>
<translation id="2166378884831602661">사ì´íŠ¸ì— 보안 ì—°ê²°í•  수 ì—†ìŒ</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836"><ph name="SITE" />ì—ì„œ HSTS를 사용하고 있기 ë•Œë¬¸ì— ë°©ë¬¸í•  수 없습니다. ë„¤íŠ¸ì›Œí¬ ì˜¤ë¥˜ì™€ ê³µê²©ì€ ëŒ€ë¶€ë¶„ ì¼ì‹œì ì´ë¯€ë¡œ ìž ì‹œ 후 페ì´ì§€ê°€ ì •ìƒí™”ë  ê²ƒìž…ë‹ˆë‹¤. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155"><ph name="ENTRY_INDEX" /> 색ì¸ì˜ ìž˜ëª»ëœ ë¶ë§ˆí¬ 무시ë¨</translation>
<translation id="2354001756790975382">기타 ë¶ë§ˆí¬</translation>
+<translation id="2355395290879513365">공격ìžëŠ” 사용ìžê°€ ì´ ì‚¬ì´íŠ¸ì—ì„œ ë³´ê³  있는 ì´ë¯¸ì§€ë¥¼ ë³¼ 수 있으며 ì´ë¯¸ì§€ë¥¼ 수정하여 사용ìžë¥¼ ì†ì¼ 수 있습니다.</translation>
<translation id="2359808026110333948">계ì†</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" />ì— ìº¡ì²˜ëœ ë¹„ì •ìƒ ì¢…ë£Œ 보고서가 업로드ë˜ì§€ 않았습니다.</translation>
<translation id="2367567093518048410">수준</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">ê°’ì´ ì„¤ì •ë˜ì§€ ì•Šì€ ì •ì±… 표시</translation>
<translation id="2396249848217231973">삭제 실행 취소(&amp;U)</translation>
<translation id="2455981314101692989">ì´ ì›¹íŽ˜ì´ì§€ì—서는 ì´ ì–‘ì‹ì— 대한 ìžë™ì™„성 ê¸°ëŠ¥ì„ ì‚¬ìš©í•  수 없습니다.</translation>
+<translation id="2460160116472764928">Google 세ì´í”„ 브ë¼ìš°ì§•ì´ 최근 <ph name="SITE" />ì—ì„œ <ph name="BEGIN_LINK" />멀웨어를 ê°ì§€<ph name="END_LINK" />했습니다. ì•ˆì „í•˜ë˜ ì›¹ì‚¬ì´íŠ¸ë„ ë©€ì›¨ì–´ì— ê°ì—¼ë˜ëŠ” 경우가 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">ìž…ë ¥</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />ë„¤íŠ¸ì›Œí¬ ì§„ë‹¨ 프로그램 실행<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">검색 URLì´ ìž˜ëª»ë¨</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">방문 기ë¡ì—ì„œ ì´ íŽ˜ì´ì§€ë¥¼ 삭제하시겠습니까?</translation>
<translation id="2677748264148917807">나가기</translation>
<translation id="269990154133806163">서버ì—ì„œ ì¸ì¦ì„œ 투명성 ì •ì±…ì„ ì‚¬ìš©í•˜ì—¬ ê³µê°œëœ ì¸ì¦ì„œê°€ ì•„ë‹Œ 다른 비공개 ì¸ì¦ì„œë¥¼ 제시했습니다. ì¸ì¦ì„œ 투명성 ì •ì±…ì€ ì¸ì¦ì„œê°€ 신뢰할 수 있으며 공격ìžë¡œë¶€í„° 사용ìžë¥¼ 보호하고 있ìŒì„ 보장하기 위한 것으로 ì¼ë¶€ ì¸ì¦ì„œì˜ 경우 필수사항입니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">ì½ê¸° 목ë¡</translation>
<translation id="2704283930420550640">ê°’ì´ í˜•ì‹ê³¼ ì¼ì¹˜í•˜ì§€ 않습니다.</translation>
<translation id="2704951214193499422">현재 Chromiumì—ì„œ 카드를 확ì¸í•  수 없습니다. ë‚˜ì¤‘ì— ë‹¤ì‹œ ì‹œë„í•´ 주세요.</translation>
<translation id="2705137772291741111">사ì´íŠ¸ì˜ 저장ëœ(ìºì‹œëœ) ì‚¬ë³¸ì„ ì½ì„ 수 없습니다.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082"><ph name="DOMAIN" />ì— ì ‘ì†í•˜ë ¤ 했으나 발행기관ì—ì„œ 서버가 제시한 ì¸ì¦ì„œë¥¼ í기했습니다. ì´ëŠ” 서버가 제시한 보안 ìžê²©ì¦ëª… 정보는 절대 신뢰할 수 ì—†ìŒì„ ì˜ë¯¸í•©ë‹ˆë‹¤. 현재 해커와 통신 ì¤‘ì¼ ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">권한 요청</translation>
<translation id="2713444072780614174">í°ìƒ‰</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">설정 페ì´ì§€ì˜ ì—°ê²°ì„ êµ¬ì„±í•˜ëŠ” 프ë¡ì‹œë¥¼ 사용 중지할 수 있습니다.</translation>
<translation id="2955913368246107853">검색 바 닫기</translation>
<translation id="2958431318199492670">ë„¤íŠ¸ì›Œí¬ ì„¤ì •ì´ ONC í‘œì¤€ì„ ì¤€ìˆ˜í•˜ì§€ 않습니다. ì¼ë¶€ ì„¤ì •ì„ ê°€ì ¸ì˜¬ 수 없습니다.</translation>
+<translation id="29611076221683977">현재 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ì˜ ê³µê²©ìžê°€ ì‚¬ìš©ìž ì •ë³´(예: 사진, 비밀번호, 메시지, ì‹ ìš©ì¹´ë“œ)를 ë„용하거나 삭제하는 위험한 í”„ë¡œê·¸ëž¨ì„ Macì— ì„¤ì¹˜í•˜ë ¤ê³  ì‹œë„í•  수 있습니다.</translation>
<translation id="2969319727213777354">보안 ì—°ê²°ì„ ì„¤ì •í•˜ë ¤ë©´ 시계가 올바로 설정ë˜ì–´ 있어야 합니다. 웹사ì´íŠ¸ê°€ ìžì‹ ì„ ì‹ë³„하는 ë° ì‚¬ìš©í•˜ëŠ” ì¸ì¦ì„œëŠ” 특정 기간ì—만 유효하기 때문입니다. ê¸°ê¸°ì˜ ì‹œê³„ê°€ 잘못 설정ë˜ì–´ Chromeì—ì„œ ì´ ì¸ì¦ì„œë¥¼ 확ì¸í•  수 없습니다.</translation>
<translation id="2972581237482394796">다시 실행(&amp;R)</translation>
<translation id="2985306909656435243">ì„ íƒí•˜ë©´ ì´ ê¸°ê¸°ì— ì¹´ë“œ ì‚¬ë³¸ì´ ì €ìž¥ë˜ì–´ Chromiumì—ì„œ ì–‘ì‹ì„ ë” ë¹ ë¥´ê²Œ 작성할 수 있습니다.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">세부정보 숨기기</translation>
<translation id="3587482841069643663">ì „ì²´</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">서버ì—ì„œ TLS ìž¬í˜‘ìƒ ìˆ˜ì •ì„ ì§€ì›í•˜ì§€ 않습니다.</translation>
<translation id="36224234498066874">ì¸í„°ë„· 사용정보 ì‚­ì œ...</translation>
<translation id="362276910939193118">방문 ê¸°ë¡ ì „ì²´ 보기</translation>
<translation id="3623476034248543066">값 표시</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">ì´ ë©”ì‹œì§€ê°€ ìžì£¼ 표시ëœë‹¤ë©´ 다ìŒì„ ì‹œë„í•´ 보세요. <ph name="HELP_LINK" /></translation>
<translation id="3658742229777143148">개정</translation>
<translation id="3678029195006412963">ìš”ì²­ì„ ì„œëª…í•  수 ì—†ìŒ</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">ë¼ì´ì„ ìŠ¤ 만료ë¨</translation>
<translation id="3714780639079136834">ëª¨ë°”ì¼ ë°ì´í„° ë˜ëŠ” Wi-Fi 사용 설정</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />프ë¡ì‹œ, 방화벽, DNS 설정 확ì¸<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">보안 관련 ìœ„í—˜ì„ ì´í•´í•œë‹¤ë©´ 위험한 í”„ë¡œê·¸ëž¨ì´ ì‚­ì œë˜ê¸° ì „ì— <ph name="BEGIN_LINK" />안전하지 ì•Šì€ ì‚¬ì´íŠ¸<ph name="END_LINK" />ì— ë°©ë¬¸í•´ë„ ë©ë‹ˆë‹¤.</translation>
<translation id="3739623965217189342">복사한 ë§í¬</translation>
<translation id="375403751935624634">서버 오류가 ë°œìƒí•˜ì—¬ 번역하지 못했습니다.</translation>
<translation id="3759461132968374835">ìµœê·¼ì— ë³´ê³ ëœ ë¹„ì •ìƒ ì¢…ë£Œê°€ 없습니다. ë¹„ì •ìƒ ì¢…ë£Œ 보고를 사용 ì¤‘ì§€í–ˆì„ ë•Œ ë°œìƒí•œ ë¹„ì •ìƒ ì¢…ë£ŒëŠ” ì—¬ê¸°ì— í‘œì‹œë˜ì§€ 않습니다.</translation>
-<translation id="3788090790273268753">ì´ ì‚¬ì´íŠ¸ì˜ ì¸ì¦ì„œëŠ” 2016ë…„ì— ë§Œë£Œë˜ë©° ì¸ì¦ì„œ ì²´ì¸ì—는 SHA-1ì„ ì‚¬ìš©í•˜ì—¬ ì„œëª…ëœ ì¸ì¦ì„œê°€ í¬í•¨ë˜ì–´ 있습니다.</translation>
<translation id="382518646247711829">프ë¡ì‹œ 서버를 사용하는 경우</translation>
<translation id="3828924085048779000">암호를 빈 칸으로 ë‘어서는 안 ë©ë‹ˆë‹¤.</translation>
<translation id="3845539888601087042">로그ì¸í•œ ê¸°ê¸°ì˜ ë°©ë¬¸ 기ë¡ì„ 표시합니다. <ph name="BEGIN_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LINK" /></translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">'<ph name="SUBKEY" />' 키: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">í´ë¼ì´ì–¸íŠ¸ì™€ 서버가 ì¼ë°˜ì ì¸ SSL 프로토콜 버전 ë˜ëŠ” 암호화 ì œí’ˆêµ°ì„ ì§€ì›í•˜ì§€ 않습니다.</translation>
<translation id="4079302484614802869">프ë¡ì‹œ ì„¤ì •ì´ ê³ ì • 프ë¡ì‹œ 서버가 ì•„ë‹Œ .pac 스í¬ë¦½íŠ¸ URLì„ ì‚¬ìš©í•˜ë„ë¡ ì„¤ì •ë©ë‹ˆë‹¤.</translation>
+<translation id="4098354747657067197">사기성 사ì´íŠ¸ 주ì˜</translation>
<translation id="4103249731201008433">기기 ì¼ë ¨ë²ˆí˜¸ê°€ 잘못ë¨</translation>
<translation id="4103763322291513355">ì°¨ë‹¨ëœ URL ë° ì‹œìŠ¤í…œ 관리ìžê°€ 설정한 기타 ì •ì±… 목ë¡ì„ 확ì¸í•˜ë ¤ë©´ &lt;strong&gt;chrome://policy&lt;/strong&gt;를 방문하세요.</translation>
<translation id="4110615724604346410">보안 ì¸ì¦ì„œì— 오류가 있어서 서버 ë„ë©”ì¸ì´ <ph name="DOMAIN" />ìž„ì„ ì¦ëª…í•  수 없습니다. ì„¤ì •ì´ ìž˜ëª»ë˜ì—ˆê±°ë‚˜ 해커가 ì—°ê²°ì„ ê°€ë¡œì±„ê³  있기 ë•Œë¬¸ì¼ ìˆ˜ 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ì—†ìŒ}=1{앱 1ê°œ($1)}=2{앱 2ê°œ($1, $2)}other{앱 #ê°œ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">ë¹„ì •ìƒ ì¢…ë£Œ</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />ë„¤íŠ¸ì›Œí¬ ì§„ë‹¨ í”„ë¡œê·¸ëž¨ì„ ì‹¤í–‰<ph name="END_LINK" />í•´ 보세요.</translation>
+<translation id="4250431568374086873">ì´ ì‚¬ì´íŠ¸ì— 대한 ì—°ê²°ì€ ì™„ë²½í•˜ê²Œ ë³´ì•ˆì´ ë˜ì§€ 않습니다.</translation>
<translation id="4250680216510889253">아니요</translation>
<translation id="425582637250725228">ë³€ê²½ì‚¬í•­ì´ ì €ìž¥ë˜ì§€ ì•Šì„ ìˆ˜ 있습니다.</translation>
<translation id="4258748452823770588">ìž˜ëª»ëœ ì„œëª…</translation>
<translation id="4269787794583293679">(ì‚¬ìš©ìž ì´ë¦„ ì—†ìŒ)</translation>
+<translation id="4280429058323657511">, ë§Œë£Œì¼ <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google 세ì´í”„ 브ë¼ìš°ì§•ì´ ìµœê·¼ì— <ph name="SITE" />ì—ì„œ <ph name="BEGIN_LINK" />유해한 í”„ë¡œê·¸ëž¨ì„ ì°¾ì•˜ìŠµë‹ˆë‹¤<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">ìƒìœ„ 추천</translation>
<translation id="4304224509867189079">로그ì¸</translation>
<translation id="432290197980158659">서버가 ë‚´ìž¥ëœ ê¸°ì¤€ì— ì¼ì¹˜í•˜ì§€ 않는 ì¸ì¦ì„œë¥¼ 제시했습니다. ì´ëŸ¬í•œ ê¸°ì¤€ì€ ë³´ì•ˆì´ ì—„ê²©í•œ 특정 웹사ì´íŠ¸ì—ì„œ 사용ìžë¥¼ 보호하기 위해 ì ìš©ë©ë‹ˆë‹¤. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">ë„움ë§ì„ 찾지 못했습니다.</translation>
+<translation id="4326324639298822553">만료ì¼ì„ 확ì¸í•œ 후 다시 ì‹œë„í•´ 주세요.</translation>
<translation id="4331708818696583467">안전하지 ì•ŠìŒ</translation>
+<translation id="4356973930735388585">ì´ ì‚¬ì´íŠ¸ì˜ 공격ìžê°€ ì‚¬ìš©ìž ì •ë³´(예: 사진, 비밀번호, 메시지, ì‹ ìš©ì¹´ë“œ)를 ë„용하거나 삭제하는 위험한 í”„ë¡œê·¸ëž¨ì„ ì»´í“¨í„°ì— ì„¤ì¹˜í•˜ë ¤ê³  ì‹œë„í•  수 있습니다.</translation>
<translation id="4372948949327679948">ì˜ˆìƒ <ph name="VALUE_TYPE" /> 값입니다.</translation>
<translation id="4381091992796011497">ì‚¬ìš©ìž ì´ë¦„:</translation>
<translation id="4394049700291259645">사용 중지</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614"><ph name="DOMAIN" />ì— ì ‘ì†í•˜ë ¤ê³  ì‹œë„했지만 서버ì—ì„œ ìž˜ëª»ëœ ì¸ì¦ì„œë¥¼ 제시했습니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4592951414987517459"><ph name="DOMAIN" />ì— ëŒ€í•œ ì—°ê²°ì€ ìµœì‹  암호화 ê¸°ìˆ ì„ ì‚¬ìš©í•˜ì—¬ 암호화ë©ë‹ˆë‹¤.</translation>
<translation id="4594403342090139922">삭제 실행 취소(&amp;U)</translation>
+<translation id="4619615317237390068">다른 ê¸°ê¸°ì˜ íƒ­</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">지금 확장 프로그램 페ì´ì§€ë¥¼ ë³´ê³  있습니다.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">정책 새로고침</translation>
<translation id="4728558894243024398">플랫í¼</translation>
<translation id="4744603770635761495">실행 가능 경로</translation>
+<translation id="4750917950439032686">비밀번호나 ì‹ ìš©ì¹´ë“œ 번호 ë“±ì˜ ì •ë³´ëŠ” 비공개 ìƒíƒœë¡œ ì´ ì‚¬ì´íŠ¸ì— 전송ë©ë‹ˆë‹¤.</translation>
<translation id="4756388243121344051">방문 기ë¡(&amp;H)</translation>
+<translation id="4759118997339041434">ê²°ì œ ìžë™ì™„성 사용 중지</translation>
<translation id="4764776831041365478"><ph name="URL" />ì˜ ì›¹íŽ˜ì´ì§€ê°€ ì¼ì‹œì ìœ¼ë¡œ 다운ë˜ì—ˆê±°ë‚˜ 새 웹 주소로 완전히 ì´ë™í–ˆì„ 수 있습니다.</translation>
<translation id="4771973620359291008">ì•Œ 수 없는 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤.</translation>
<translation id="4800132727771399293">유효기간과 CVC를 확ì¸í•œ 후 다시 ì‹œë„í•´ 주세요.</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">ë„¤íŠ¸ì›Œí¬ ì˜¤ë¥˜</translation>
<translation id="4816492930507672669">페ì´ì§€ 맞춤</translation>
<translation id="4850886885716139402">보기</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{외 웹페ì´ì§€ 1ê°œ}other{외 웹페ì´ì§€ #ê°œ}}</translation>
<translation id="4923417429809017348">페ì´ì§€ê°€ ì•Œ 수 없는 언어ì—ì„œ <ph name="LANGUAGE_LANGUAGE" />(으)ë¡œ 번역ë˜ì—ˆìŠµë‹ˆë‹¤.</translation>
+<translation id="4923459931733593730">결제</translation>
<translation id="4926049483395192435">지정해야 합니다.</translation>
<translation id="495170559598752135">ìž‘ì—…</translation>
<translation id="4958444002117714549">펼치기 목ë¡</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">다운로드 중</translation>
<translation id="5190835502935405962">ë¶ë§ˆí¬ë°”</translation>
<translation id="5199729219167945352">실험실 기능</translation>
-<translation id="5199841536747119669">추천 í•­ëª©ì´ ì—¬ê¸°ì— í‘œì‹œë©ë‹ˆë‹¤.</translation>
<translation id="5251803541071282808">í´ë¼ìš°ë“œ</translation>
<translation id="5277279256032773186">ì§ìž¥ì—ì„œ Chromeì„ ì‚¬ìš©í•˜ì‹œë‚˜ìš”? ê¸°ì—…ì€ ì§ì›ì˜ Chrome ì„¤ì •ì„ ê´€ë¦¬í•  수 있습니다. ìžì„¸ížˆ 알아보기</translation>
<translation id="5299298092464848405">ì •ì±…ì„ íŒŒì‹±í•˜ëŠ” 중 오류 ë°œìƒ</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">ë¹„ì •ìƒ ì¢…ë£Œ ë³´ê³ ê°€ 사용 중지ë˜ì—ˆìŠµë‹ˆë‹¤.</translation>
<translation id="5317780077021120954">저장</translation>
<translation id="5327248766486351172">ì´ë¦„</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 공격ìžê°€ 소프트웨어를 설치하거나 ê°œì¸ì •ë³´(예: 비밀번호, 전화번호, ì‹ ìš©ì¹´ë“œ ì •ë³´)를 공개하는 ë“±ì˜ ìœ„í—˜í•œ í–‰ë™ì„ 하ë„ë¡ ì‚¬ìš©ìžë¥¼ ì†ì¼ 수 있습니다.</translation>
<translation id="5359637492792381994">현재 ì„œë²„ì˜ ë³´ì•ˆ ì¸ì¦ì„œê°€ 유효하지 ì•Šì•„ 서버 ë„ë©”ì¸ì´ <ph name="DOMAIN" />ìž„ì„ ì¦ëª…í•  수 없습니다. ì„¤ì •ì´ ìž˜ëª»ë˜ì—ˆê±°ë‚˜ 해커가 ì—°ê²°ì„ ê°€ë¡œì±„ê³  있기 ë•Œë¬¸ì¼ ìˆ˜ 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">정책 설정 저장 실패</translation>
+<translation id="5386426401304769735">ì´ ì‚¬ì´íŠ¸ì˜ ì¸ì¦ì„œ ì²´ì¸ì€ SHA-1ì„ ì‚¬ìš©í•˜ì—¬ ì„œëª…ëœ ì¸ì¦ì„œë¥¼ í¬í•¨í•©ë‹ˆë‹¤.</translation>
<translation id="5421136146218899937">ì¸í„°ë„· 사용 ê¸°ë¡ ì‚­ì œ...</translation>
<translation id="5430298929874300616">ë¶ë§ˆí¬ ì‚­ì œ</translation>
<translation id="5431657950005405462">파ì¼ì„ ì°¾ì„ ìˆ˜ ì—†ìŒ</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102"><ph name="SITE" />ì— ì‚½ìž…ëœ íŽ˜ì´ì§€ ë‚´ìš©:</translation>
<translation id="5556459405103347317">새로고침</translation>
<translation id="5565735124758917034">활성</translation>
+<translation id="5572851009514199876">ì´ ì‚¬ì´íŠ¸ì— 액세스할 수 있는지 확ì¸í•  수 있ë„ë¡ Chromeì„ ì‹œìž‘í•˜ê³  로그ì¸í•˜ì„¸ìš”.</translation>
+<translation id="5580958916614886209">유효기간 ì›”ì„ í™•ì¸í•œ 후 다시 ì‹œë„í•´ 주세요.</translation>
<translation id="560412284261940334">관리가 지ì›ë˜ì§€ ì•ŠìŒ</translation>
<translation id="5610142619324316209">ì—°ê²° 확ì¸</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" />ì—ì„œ 리디렉션한 횟수가 너무 많습니다.</translation>
<translation id="5622887735448669177">ì´ ì‚¬ì´íŠ¸ì—ì„œ 나가시겠습니까?</translation>
<translation id="5629630648637658800">정책 설정 로드 실패</translation>
<translation id="5631439013527180824">ìž˜ëª»ëœ ê¸°ê¸° 관리 토í°</translation>
+<translation id="5669703222995421982">내게 맞는 콘í…츠 추천 받기</translation>
+<translation id="5675650730144413517">페ì´ì§€ê°€ ìž‘ë™í•˜ì§€ 않습니다.</translation>
<translation id="5677928146339483299">차단ë¨</translation>
<translation id="5694783966845939798"><ph name="DOMAIN" />ì— ì ‘ì†í•˜ë ¤ 했으나 서버ì—ì„œ ì•ˆì „ì„±ì´ ë‚®ì€ ì„œëª… ì•Œê³ ë¦¬ì¦˜ì„ ì‚¬ìš©í•˜ì—¬ ì„œëª…ëœ ì¸ì¦ì„œë¥¼ 제시했습니다. ì´ëŠ” 서버ì—ì„œ 제시한 보안 ì‚¬ìš©ìž ì¸ì¦ ì •ë³´ê°€ 위조ë˜ì—ˆì„ 수 있으며 서버를 가장한 공격ìžì™€ 통신 ì¤‘ì¼ ìˆ˜ 있ìŒì„ ì˜ë¯¸í•©ë‹ˆë‹¤. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">ì´ ì›¹ì‚¬ì´íŠ¸ì˜ 주소가 확ì¸ë˜ì§€ 않았습니다.</translation>
<translation id="5720705177508910913">현재 사용ìž</translation>
-<translation id="572328651809341494">최근 탭</translation>
<translation id="5732392974455271431">ë¶€ëª¨ë‹˜ì´ ì°¨ë‹¨ 해제할 수 있습니다.</translation>
<translation id="5784606427469807560">ì¹´ë“œ í™•ì¸ ì¤‘ì— ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. ì¸í„°ë„· ì—°ê²°ì„ í™•ì¸í•˜ê³  다시 ì‹œë„하세요.</translation>
<translation id="5785756445106461925">ë˜í•œ ì´ íŽ˜ì´ì§€ì—는 안전하지 ì•Šì€ ë‹¤ë¥¸ 리소스가 í¬í•¨ë˜ì–´ 있습니다. ì´ëŸ¬í•œ 리소스는 전송 ì¤‘ì— ë‹¤ë¥¸ ì‚¬ëžŒì´ ë³¼ 수 있으며 페ì´ì§€ì˜ ëª¨ì–‘ì„ ë³€ê²½í•˜ê¸° 위해 공격ìžê°€ 수정할 수 있습니다.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569"><ph name="DOMAIN" />ì— ì ‘ì†í•˜ë ¤ 했으나 서버가 ì•ˆì „ì„±ì´ ë‚®ì€ í‚¤ê°€ í¬í•¨ëœ ì¸ì¦ì„œë¥¼ 제시했습니다. 공격ìžê°€ 비공개 키를 ì†ìƒì‹œì¼°ì„ 수 있으며 서버를 가장한 공격ìžì™€ 통신 ì¤‘ì¼ ìˆ˜ 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">새 í´ë”</translation>
<translation id="5869405914158311789">사ì´íŠ¸ì— ì—°ê²°í•  수 ì—†ìŒ</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">ì´ íŒë§¤ìžì˜ Google Paymentsì—ì„œ 지ì›í•˜ì§€ 않는 ì¹´ë“œ 유형입니다. 다른 카드를 ì„ íƒí•´ 주세요.</translation>
<translation id="59174027418879706">사용 설정ë¨</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="5966707198760109579">주</translation>
<translation id="5967867314010545767">기ë¡ì—ì„œ ì‚­ì œ</translation>
<translation id="5975083100439434680">축소</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">다ìŒì„ ì‹œë„:</translation>
<translation id="6151417162996330722">서버 ì¸ì¦ì„œì˜ 유효 ê¸°ê°„ì´ ë„ˆë¬´ ê¹ë‹ˆë‹¤.</translation>
<translation id="6165508094623778733">ìžì„¸ížˆ 알아보기</translation>
+<translation id="6177128806592000436">ì´ ì‚¬ì´íŠ¸ì— 대한 ì—°ê²°ì€ ì•ˆì „í•˜ì§€ 않습니다.</translation>
<translation id="6203231073485539293">ì¸í„°ë„· ì—°ê²°ì„ í™•ì¸í•˜ì„¸ìš”.</translation>
<translation id="6218753634732582820">Chromiumì—ì„œ 주소를 삭제하시겠습니까?</translation>
+<translation id="6251924700383757765">ê°œì¸ì •ë³´ì²˜ë¦¬ë°©ì¹¨</translation>
+<translation id="625755898061068298">ì´ ì‚¬ì´íŠ¸ì— 대한 보안 경고를 사용하지 ì•Šë„ë¡ ì„ íƒí–ˆìŠµë‹ˆë‹¤.</translation>
<translation id="6259156558325130047">재정렬 다시 실행(&amp;R)</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> ë¶ë§ˆí¬</translation>
<translation id="6264485186158353794">안전 페ì´ì§€ë¡œ ëŒì•„가기</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" />ì— ì—°ê²°í•  수 없습니다.</translation>
<translation id="6321917430147971392">DNS 설정 확ì¸</translation>
<translation id="6328639280570009161">ë„¤íŠ¸ì›Œí¬ ì˜ˆì¸¡ì„ ì‚¬ìš© 중지해 보세요.</translation>
+<translation id="6328786501058569169">사기성 사ì´íŠ¸</translation>
<translation id="6337534724793800597">ì´ë¦„별로 ì •ì±… í•„í„°ë§</translation>
<translation id="6342069812937806050">완료ë¨</translation>
<translation id="6345221851280129312">í¬ê¸°ë¥¼ ì•Œ 수 ì—†ìŒ</translation>
@@ -489,6 +519,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{그 외 제안 1건}other{그 외 제안 #건}}</translation>
<translation id="6387478394221739770">Chromeì˜ ë©‹ì§„ 새 ê¸°ëŠ¥ì— ê´€ì‹¬ì´ ìžˆìœ¼ì‹­ë‹ˆê¹Œ? chrome.com/beta 페ì´ì§€ì—ì„œ 베타 채ë„ì„ ë°©ë¬¸í•´ 보세요.</translation>
<translation id="6389758589412724634">ì´ ì›¹íŽ˜ì´ì§€ë¥¼ 표시하려고 했으나 Chromium 메모리가 부족합니다.</translation>
+<translation id="6404511346730675251">ë¶ë§ˆí¬ 수정</translation>
+<translation id="6410264514553301377"><ph name="CREDIT_CARD" />ì˜ ë§Œë£Œì¼ê³¼ CVC를 입력하세요.</translation>
<translation id="6414888972213066896">ì´ ì‚¬ì´íŠ¸ë¥¼ ë°©ë¬¸í•´ë„ ê´œì°®ì€ì§€ 부모님께 문ì˜í–ˆìŠµë‹ˆë‹¤.</translation>
<translation id="6416403317709441254">현재 <ph name="SITE" />ì—ì„œ 처리할 수 없는 ì•”í˜¸í™”ëœ ìžê²©ì¦ëª… 정보를 전송했기 ë•Œë¬¸ì— ë°©ë¬¸í•  수 없습니다. ë„¤íŠ¸ì›Œí¬ ì˜¤ë¥˜ì™€ ê³µê²©ì€ ëŒ€ë¶€ë¶„ ì¼ì‹œì ì´ë¯€ë¡œ ìž ì‹œ 후 페ì´ì§€ê°€ ì •ìƒí™”ë  ê²ƒìž…ë‹ˆë‹¤. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">ì¸ì¦ì„œê°€ 취소ë˜ì—ˆëŠ”지 확ì¸í•  수 없습니다.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">ì •ì±…ì— ì˜í•´ 기본 ê²€ìƒ‰ì˜ ì‚¬ìš©ì´ ì¤‘ì§€ë˜ì—ˆê¸° ë•Œë¬¸ì— ë¬´ì‹œë¨</translation>
<translation id="6462969404041126431">보안 ì¸ì¦ì„œê°€ 취소ë˜ì—ˆì„ 수 있어서 서버 ë„ë©”ì¸ì´ <ph name="DOMAIN" />ìž„ì„ ì¦ëª…í•  수 없습니다. ì„¤ì •ì´ ìž˜ëª»ë˜ì—ˆê±°ë‚˜ 해커가 ì—°ê²°ì„ ê°€ë¡œì±„ê³  있기 ë•Œë¬¸ì¼ ìˆ˜ 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="647261751007945333">기기 정책</translation>
+<translation id="6477321094435799029">Chromeì´ ì´ íŽ˜ì´ì§€ì—ì„œ 비정ìƒì ì¸ 코드를 ê°ì§€í–ˆìœ¼ë©° ê°œì¸ì •ë³´(예: 비밀번호, 전화번호, ì‹ ìš©ì¹´ë“œ) 보호를 위해 차단했습니다.</translation>
<translation id="6489534406876378309">ë¹„ì •ìƒ ì¢…ë£Œ 업로드 시작하기</translation>
<translation id="6529602333819889595">삭제 다시 실행(&amp;R)</translation>
<translation id="6534179046333460208">피지컬 웹 제안</translation>
<translation id="6550675742724504774">옵션</translation>
+<translation id="6556239504065605927">보안 연결</translation>
<translation id="6563469144985748109">관리ìžê°€ ì•„ì§ ìŠ¹ì¸í•˜ì§€ 않았습니다.</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" /> 미만</translation>
<translation id="6596325263575161958">암호화 옵션</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">ì´ ì •ì±…ì€ ì‚¬ìš©ë˜ì§€ 않습니다.</translation>
<translation id="6652240803263749613">ì»´í“¨í„°ì˜ ìš´ì˜ì²´ì œì—ì„œ 보안 ì¸ì¦ì„œë¥¼ 신뢰하지 ì•Šì•„ì„œ 서버 ë„ë©”ì¸ì´ <ph name="DOMAIN" />ìž„ì„ ì¦ëª…í•  수 없습니다. ì„¤ì •ì´ ìž˜ëª»ë˜ì—ˆê±°ë‚˜ 해커가 ì—°ê²°ì„ ê°€ë¡œì±„ê³  있기 ë•Œë¬¸ì¼ ìˆ˜ 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">í´ë” 수정</translation>
-<translation id="6660210980321319655"><ph name="CRASH_TIME" />ì— ìžë™ìœ¼ë¡œ ì‹ ê³ ë¨</translation>
<translation id="6671697161687535275">Chromiumì—ì„œ ìžë™ì™„성 항목 ì¶”ì²œì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?</translation>
<translation id="6685834062052613830">로그아웃 후 설정 완료</translation>
<translation id="6710213216561001401">ì´ì „</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">ì •ì±… ê°’</translation>
<translation id="6757797048963528358">기기가 절전 모드 ìƒíƒœìž…니다.</translation>
<translation id="6778737459546443941">ë¶€ëª¨ë‹˜ì´ ì•„ì§ ìŠ¹ì¸í•˜ì§€ 않았습니다.</translation>
+<translation id="6810899417690483278">맞춤설정 ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" />ì˜ ì›¹íŽ˜ì´ì§€ëŠ” 현재 사용할 수 없습니다. 웹페ì´ì§€ê°€ 오버로드ë˜ì—ˆê±°ë‚˜ 유지보수를 위해 다운ë˜ì—ˆì„ 수 있습니다.</translation>
<translation id="6831043979455480757">번역</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">ì—°ë½ì²˜ ì •ë³´</translation>
+<translation id="7079718277001814089">ì´ ì‚¬ì´íŠ¸ì— 멀웨어가 있습니다.</translation>
<translation id="7087282848513945231">ì¹´ìš´í‹°</translation>
<translation id="7088615885725309056">다ìŒ</translation>
<translation id="7090678807593890770">Googleì—ì„œ <ph name="LINK" /> 검색</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" />ì—ì„œ 보안 ê¸°ì¤€ì„ ì¤€ìˆ˜í•˜ì§€ 않습니다.</translation>
<translation id="721197778055552897">ì´ ë¬¸ì œì— ëŒ€í•´ <ph name="BEGIN_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LINK" /></translation>
<translation id="7219179957768738017"><ph name="SSL_VERSION" /> 연결입니다.</translation>
+<translation id="724691107663265825">ë‹¤ìŒ ì‚¬ì´íŠ¸ì— 멀웨어가 있습니다.</translation>
<translation id="724975217298816891">ì¹´ë“œ 세부정보를 ì—…ë°ì´íŠ¸í•˜ë ¤ë©´ <ph name="CREDIT_CARD" /> ì¹´ë“œì˜ ë§Œë£Œì¼ê³¼ CVC를 입력하세요. 카드를 확ì¸í•˜ë©´ ì¹´ë“œ 세부정보가 ì´ ì‚¬ì´íŠ¸ì™€ 공유ë©ë‹ˆë‹¤.</translation>
<translation id="725866823122871198">ì»´í“¨í„°ì˜ ë‚ ì§œì™€ 시간(<ph name="DATE_AND_TIME" />)ì´ ìž˜ëª»ë˜ì–´ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />ì— ëŒ€í•œ 비공개 ì—°ê²°ì„ ì„¤ì •í•  수 없습니다.</translation>
<translation id="7269802741830436641">ì´ ì›¹íŽ˜ì´ì§€ì— 리디렉션 순환 오류가 있습니다</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">취소</translation>
<translation id="7667346355482952095">ë°˜í™˜ëœ ì •ì±… 토í°ì´ 비었거나 현재 토í°ê³¼ ì¼ì¹˜í•˜ì§€ ì•ŠìŒ</translation>
<translation id="7668654391829183341">알 수 없는 기기</translation>
+<translation id="7669271284792375604">ì´ ì‚¬ì´íŠ¸ì˜ 공격ìžê°€ ì¸í„°ë„· 사용 í™˜ê²½ì— ì•…ì˜í–¥ì„ 미치는 í”„ë¡œê·¸ëž¨ì„ ì„¤ì¹˜í•˜ë„ë¡ ì†ìž„수(예를 들어, 방문하는 사ì´íŠ¸ì˜ 홈페ì´ì§€ë¥¼ 변경하거나 추가로 광고를 표시)를 ì‹œë„í•  수 있습니다.</translation>
<translation id="7674629440242451245">Chromeì˜ ë©‹ì§„ 새 ê¸°ëŠ¥ì— ê´€ì‹¬ì´ ìžˆìœ¼ì‹­ë‹ˆê¹Œ? chrome.com/dev 페ì´ì§€ì—ì„œ ê°œë°œìž ì±„ë„ì— ë°©ë¬¸í•´ 보세요.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /><ph name="SITE" />(안전하지 ì•ŠìŒ)<ph name="END_LINK" />(으)ë¡œ ì´ë™</translation>
<translation id="7716424297397655342">ì´ ì‚¬ì´íŠ¸ë¥¼ ìºì‹œì—ì„œ 로드할 수 ì—†ìŒ</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">메시지 ì¸ì¦(<ph name="MAC" />)ê³¼ 키 êµí™˜ 메커니즘(<ph name="KX" />)ì„ ì„¤ì •í•˜ê³  <ph name="CIPHER" />ì„(를) 사용하여 ì—°ê²°ì´ ì•”í˜¸í™”ë˜ì–´ 있습니다.</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="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="7887683347370398519">CVC를 확ì¸í•œ 후 다시 ì‹œë„하세요.</translation>
<translation id="7912024687060120840">í´ë”:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">서버 ì¸ì¦ì„œê°€ 유효하지 않습니다.</translation>
<translation id="7942349550061667556">빨간색</translation>
+<translation id="7947285636476623132">유효기간 ì—°ë„를 확ì¸í•œ 후 다시 ì‹œë„í•´ 주세요.</translation>
<translation id="7951415247503192394">(32비트)</translation>
<translation id="7956713633345437162">ëª¨ë°”ì¼ ë¶ë§ˆí¬</translation>
<translation id="7961015016161918242">사용하지 ì•ŠìŒ</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893"><ph name="ORIGINAL_LANGUAGE" />를 í•­ìƒ <ph name="TARGET_LANGUAGE" />ë¡œ 번역</translation>
<translation id="7995512525968007366">지정ë˜ì§€ ì•ŠìŒ</translation>
<translation id="8012647001091218357">현재 부모님께 ì—°ë½í•  수 없습니다. ë‚˜ì¤‘ì— ë‹¤ì‹œ ì‹œë„í•´ 주세요.</translation>
+<translation id="8025119109950072390">ì´ ì‚¬ì´íŠ¸ì˜ 공격ìžê°€ 소프트웨어를 설치하거나 ê°œì¸ì •ë³´(예: 비밀번호, 전화번호, ì‹ ìš©ì¹´ë“œ)를 공개하는 ë“±ì˜ ìœ„í—˜í•œ í–‰ë™ì„ 하ë„ë¡ ì‚¬ìš©ìžë¥¼ ì†ì¼ 수 있습니다.</translation>
+<translation id="803030522067524905">Google 세ì´í”„ 브ë¼ìš°ì§•ì´ 최근 <ph name="SITE" />ì—ì„œ í”¼ì‹±ì„ ê°ì§€í–ˆìŠµë‹ˆë‹¤. 피싱 사ì´íŠ¸ëŠ” 사용ìžë¥¼ ì†ì´ê¸° 위해 다른 웹사ì´íŠ¸ì¸ 것처럼 위장합니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">ì´ íŽ˜ì´ì§€ëŠ” <ph name="SOURCE_LANGUAGE" />ë¡œ ë˜ì–´ 있습니다. <ph name="TARGET_LANGUAGE" />ë¡œ 번역하시겠습니까?</translation>
+<translation id="8041089156583427627">ì˜ê²¬ 보내기</translation>
<translation id="8088680233425245692">ê¸€ì„ ì¡°íšŒí•˜ì§€ 못했습니다.</translation>
<translation id="8089520772729574115">1MB 미만</translation>
<translation id="8091372947890762290">활성화 ìš”ì²­ì´ ì„œë²„ì—ì„œ 대기 중</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719"><ph name="URL" />ì˜ íŒŒì¼ì„ ì½ì„ 수 없습니다. ì‚­ì œ ë˜ëŠ” ì´ë™ë˜ì—ˆê±°ë‚˜ íŒŒì¼ ì‚¬ìš© ê¶Œí•œì´ ì•¡ì„¸ìŠ¤ë¥¼ 차단할 ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤.</translation>
<translation id="8194797478851900357">ì´ë™ 실행 취소(&amp;U)</translation>
<translation id="8201077131113104583">IDê°€ '<ph name="EXTENSION_ID" />'ì¸ í™•ìž¥ í”„ë¡œê·¸ëž¨ì— ëŒ€í•œ ìž˜ëª»ëœ ì—…ë°ì´íŠ¸ URL</translation>
+<translation id="8202097416529803614">주문 요약</translation>
<translation id="8218327578424803826">ì§€ì •ëœ ìœ„ì¹˜:</translation>
<translation id="8225771182978767009">컴퓨터를 설정한 사용ìžê°€ ì´ ì‚¬ì´íŠ¸ë¥¼ 차단했습니다.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">현재 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ì˜ ê³µê²©ìžê°€ ì‚¬ìš©ìž ì •ë³´(예: 사진, 비밀번호, 메시지, ì‹ ìš©ì¹´ë“œ)를 ë„용하거나 삭제하는 위험한 í”„ë¡œê·¸ëž¨ì„ ì»´í“¨í„°ì— ì„¤ì¹˜í•˜ë ¤ê³  ì‹œë„í•  수 있습니다.</translation>
<translation id="8241707690549784388">찾고 있는 페ì´ì§€ì—ì„œ 사용ìžê°€ 입력한 정보를 사용했습니다. 해당 페ì´ì§€ë¡œ ëŒì•„가면 기존 ìž‘ì—…ì„ ë°˜ë³µí•  수 있습니다. 계ì†í•˜ì‹œê² ìŠµë‹ˆê¹Œ?</translation>
<translation id="8249320324621329438">마지막으로 가져온 시간:</translation>
<translation id="8261506727792406068">삭제</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">출처</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="8349305172487531364">ë¶ë§ˆí¬ë°”</translation>
<translation id="8363502534493474904">비행기 모드 사용 중지</translation>
<translation id="8364627913115013041">설정 안ë¨</translation>
<translation id="8380941800586852976">위험</translation>
<translation id="8382348898565613901">ìµœê·¼ì— ë°©ë¬¸í•œ ë¶ë§ˆí¬ê°€ ì—¬ê¸°ì— í‘œì‹œë©ë‹ˆë‹¤.</translation>
+<translation id="8398259832188219207">ë¹„ì •ìƒ ì¢…ë£Œ 보고서가 <ph name="UPLOAD_TIME" />ì— ì—…ë¡œë“œë¨</translation>
<translation id="8412145213513410671">ë¹„ì •ìƒ ì¢…ë£Œ(<ph name="CRASH_COUNT" />회)</translation>
<translation id="8412392972487953978">ë™ì¼í•œ 암호를 ë‘ ë²ˆ 입력해야 합니다.</translation>
<translation id="8428213095426709021">설정</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" />ì—ì„œ ì‘답하는 ë° ì‹œê°„ì´ ë„ˆë¬´ 오래 걸립니다.</translation>
<translation id="852346902619691059">ê¸°ê¸°ì˜ ìš´ì˜ì²´ì œì—ì„œ 보안 ì¸ì¦ì„œë¥¼ 신뢰하지 ì•Šì•„ì„œ 서버 ë„ë©”ì¸ì´ <ph name="DOMAIN" />ìž„ì„ ì¦ëª…í•  수 없습니다. ì„¤ì •ì´ ìž˜ëª»ë˜ì—ˆê±°ë‚˜ 해커가 ì—°ê²°ì„ ê°€ë¡œì±„ê³  있기 ë•Œë¬¸ì¼ ìˆ˜ 있습니다. <ph name="BEGIN_LEARN_MORE_LINK" />ìžì„¸ížˆ 알아보기<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Google Paymentsì—ì„œ 지ì›í•˜ì§€ 않는 ì¹´ë“œ 유형입니다. 다른 카드를 ì„ íƒí•´ 주세요.</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="8553075262323480129">페ì´ì§€ì˜ 언어를 ê²°ì •í•  수 없으므로 번역하지 못했습니다.</translation>
<translation id="8559762987265718583">ê¸°ê¸°ì˜ ë‚ ì§œì™€ 시간(<ph name="DATE_AND_TIME" />)ì´ ìž˜ëª»ë˜ì–´ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />ì— ëŒ€í•œ 비공개 ì—°ê²°ì„ ì„¤ì •í•  수 없습니다.</translation>
-<translation id="856992080682148">ì´ ì‚¬ì´íŠ¸ì˜ ì¸ì¦ì„œëŠ” 2017ë…„ ë˜ëŠ” ê·¸ ì´í›„ì— ë§Œë£Œë˜ë©° ì¸ì¦ì„œ ì²´ì¸ì— SHA-1ì„ ì‚¬ìš©í•˜ì—¬ ì„œëª…ëœ ì¸ì¦ì„œê°€ í¬í•¨ë˜ì–´ 있습니다.</translation>
<translation id="8571890674111243710">페ì´ì§€ë¥¼ <ph name="LANGUAGE" />(으)ë¡œ 번역 중...</translation>
<translation id="859285277496340001">ì¸ì¦ì„œëŠ” 취소 여부를 확ì¸í•˜ëŠ” ë§¤ì»¤ë‹ˆì¦˜ì„ ì§€ì •í•˜ì§€ 않습니다.</translation>
<translation id="8620436878122366504">ë¶€ëª¨ë‹˜ì´ ì•„ì§ ìŠ¹ì¸í•˜ì§€ 않았습니다.</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522"><ph name="HOST_NAME" />ì˜ &lt;abbr id="dnsDefinition"&gt;DNS 주소&lt;/abbr&gt;를 ì°¾ì„ ìˆ˜ 없습니다. 문제를 진단하는 중입니다.</translation>
<translation id="8790007591277257123">삭제 다시 실행(&amp;R)</translation>
<translation id="8798099450830957504">기본값</translation>
+<translation id="8800988563907321413">근처 추천 í•­ëª©ì´ ì—¬ê¸°ì— í‘œì‹œë©ë‹ˆë‹¤.</translation>
<translation id="8804164990146287819">ê°œì¸ì •ë³´ì²˜ë¦¬ë°©ì¹¨</translation>
<translation id="8820817407110198400">ë¶ë§ˆí¬</translation>
<translation id="8834246243508017242">주소ë¡ì„ 사용해 ìžë™ì™„성 기능 사용 설정…</translation>
<translation id="883848425547221593">기타 ë¶ë§ˆí¬</translation>
+<translation id="884264119367021077">배송지 주소</translation>
<translation id="884923133447025588">í기 ë§¤ì»¤ë‹ˆì¦˜ì„ ì°¾ì„ ìˆ˜ 없습니다.</translation>
<translation id="885730110891505394">Google과 공유</translation>
<translation id="8866481888320382733">ì •ì±… ì„¤ì •ì„ íŒŒì‹±í•˜ëŠ” 중 오류 ë°œìƒ</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">서버 ì¸ì¦ì„œê°€ 만료ë˜ì—ˆìŠµë‹ˆë‹¤.</translation>
<translation id="8987927404178983737">ì›”</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">방문하려는 사ì´íŠ¸ì— 유해한 í”„ë¡œê·¸ëž¨ì´ ìžˆìŠµë‹ˆë‹¤.</translation>
<translation id="9001074447101275817">프ë¡ì‹œ <ph name="DOMAIN" />ì— ì‚¬ìš©ìž ì´ë¦„ ë° ë¹„ë°€ë²ˆí˜¸ë¥¼ 입력해야 합니다.</translation>
<translation id="901974403500617787">시스템 ì „ì²´ì— ì ìš©ë˜ëŠ” 플래그는 소유ìž(<ph name="OWNER_EMAIL" />)만 설정할 수 있습니다.</translation>
<translation id="9020542370529661692">ì´ íŽ˜ì´ì§€ëŠ” <ph name="TARGET_LANGUAGE" />ë¡œ 번역ë˜ì—ˆìŠµë‹ˆë‹¤.</translation>
+<translation id="9035022520814077154">보안 오류</translation>
<translation id="9038649477754266430">빠른 페ì´ì§€ 로드를 위해 ì˜ˆìƒ ê²€ìƒ‰ì–´ 서비스 사용</translation>
<translation id="9039213469156557790">ë˜í•œ ì´ íŽ˜ì´ì§€ì—는 안전하지 ì•Šì€ ë‹¤ë¥¸ 리소스가 í¬í•¨ë˜ì–´ 있습니다. ì´ëŸ¬í•œ 리소스는 전송 ì¤‘ì— ë‹¤ë¥¸ ì‚¬ëžŒì´ ë³¼ 수 있으며 페ì´ì§€ì˜ ìž‘ë™ì„ 변경하기 위해 공격ìžê°€ 수정할 수 있습니다.</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />ì— ìžˆëŠ” 해커가 브ë¼ìš°ì € í™˜ê²½ì— ì•…ì˜í–¥ì„ 미치는 í”„ë¡œê·¸ëž¨ì„ ì„¤ì¹˜í•˜ë„ë¡ ì†ìž„수(예를 들어, 방문하는 사ì´íŠ¸ì˜ 홈페ì´ì§€ë¥¼ 변경하거나 추가로 광고를 표시)를 ì‹œë„í•  수 있습니다.</translation>
<translation id="9050666287014529139">암호</translation>
<translation id="9065203028668620118">수정</translation>
<translation id="9068849894565669697">ìƒ‰ìƒ ì„ íƒ</translation>
<translation id="9076283476770535406">성ì¸ìš© 콘í…츠가 í¬í•¨ë˜ì–´ ìžˆì„ ìˆ˜ 있습니다.</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> 페ì´ì§€ê°€ ìž‘ë™í•˜ì§€ ì•ŠìŒ</translation>
<translation id="9103872766612412690"><ph name="SITE" />ì—서는 ì‚¬ìš©ìž ì •ë³´ë¥¼ 보호하기 위해 ì¼ë°˜ì ìœ¼ë¡œ 암호화를 사용합니다. ì´ë²ˆì— Chromiumì—ì„œ <ph name="SITE" />ì— ì—°ê²°ì„ ì‹œë„í–ˆì„ ë•Œ 웹사ì´íŠ¸ì—ì„œ 비정ìƒì ì´ê³  ìž˜ëª»ëœ ì‚¬ìš©ìž ì¸ì¦ 정보를 반환했습니다. ì´ëŠ” 공격ìžê°€ <ph name="SITE" />ì¸ ê²ƒì²˜ëŸ¼ 가장하려고 하거나 Wi-Fi ë¡œê·¸ì¸ í™”ë©´ì´ ì—°ê²°ì„ ë°©í•´í–ˆê¸° ë•Œë¬¸ì¼ ìˆ˜ 있습니다. ë°ì´í„° êµí™˜ì´ ë°œìƒí•˜ê¸° ì „ì— Chromiumì—ì„œ ì—°ê²°ì„ ì¤‘ë‹¨í–ˆìœ¼ë¯€ë¡œ ì‚¬ìš©ìž ì •ë³´ëŠ” 안전합니다.</translation>
<translation id="9137013805542155359">ì›ë³¸ 보기</translation>
+<translation id="9137248913990643158">ì´ ì•±ì„ ì‚¬ìš©í•˜ê¸° ì „ì— Chromeì„ ì‹œìž‘í•˜ê³  로그ì¸í•˜ì„¸ìš”.</translation>
<translation id="9148507642005240123">수정 실행 취소(&amp;U)</translation>
<translation id="9157595877708044936">설정 중...</translation>
<translation id="9170848237812810038">실행 취소(&amp;U)</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">ì–‘ì‹ ì§€ìš°ê¸°</translation>
<translation id="939736085109172342">새 í´ë”</translation>
<translation id="941721044073577244">ì´ ì‚¬ì´íŠ¸ë¥¼ 방문할 수 있는 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤.</translation>
-<translation id="962701380617707048">ì¹´ë“œ 세부정보를 ì—…ë°ì´íŠ¸í•˜ë ¤ë©´ <ph name="CREDIT_CARD" /> ì¹´ë“œì˜ ë§Œë£Œì¼ê³¼ CVC를 입력하세요.</translation>
<translation id="969892804517981540">ê³µì‹ ë¹Œë“œ</translation>
<translation id="988159990683914416">ê°œë°œìž ë¹Œë“œ</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 162a47ec003..4211df5cccd 100644
--- a/chromium/components/strings/components_strings_lt.xtb
+++ b/chromium/components/strings/components_strings_lt.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Iš naujo prisijungti prie „Wi-Fi“</translation>
<translation id="1175364870820465910">&amp;Spausdinti...</translation>
<translation id="1181037720776840403">Pašalinti</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Automatiškai pateikti<ph name="END_WHITEPAPER_LINK" /> išsamią informaciją apie galimas saugos problemas sistemoje „Google“. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Toliau</translation>
<translation id="1201895884277373915">Daugiau iš šios svetainės</translation>
<translation id="1206967143813997005">Netinkamas pirminis parašas</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Žydra</translation>
<translation id="1629803312968146339">Ar norite, kad „Chrome“ išsaugotų šią kortelę?</translation>
<translation id="1640180200866533862">Naudotojo politika</translation>
+<translation id="1640244768702815859">Pabandykite <ph name="BEGIN_LINK" />apsilankyti pagrindiniame svetainÄ—s puslapyje<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Tinklo konfigūracija netinkama ir jos neįmanoma importuoti.</translation>
<translation id="1644574205037202324">Istorija</translation>
<translation id="1645368109819982629">Nepalaikomas protokolas</translation>
<translation id="1676269943528358898">Svetainėje <ph name="SITE" /> įprastai naudojama šifruotė informacijai apsaugoti. Šį kartą „Google Chrome“ bandant prisijungti prie <ph name="SITE" />, ji pateikė neįprastus ir netinkamus prisijungimo duomenis. Gali būti, kad užpuolėjas bando apsimesti svetaine <ph name="SITE" /> arba „Wi-Fi“ prisijungimo ekrane nutrūko ryšys. Jūsų informacija vis tiek liko apsaugota, nes „Google Chrome“ sustabdė prisijungimą prieš apsikeitimą bet kokiais duomenimis.</translation>
+<translation id="168328519870909584">Šiuo metu svetainės <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> užpuolėjai gali jūsų įrenginyje bandyti įdiegti pavojingas programas, kurios vagia arba ištrina informaciją (pvz., nuotraukas, slaptažodžius, pranešimus ir kredito kortelių numerius).</translation>
<translation id="168841957122794586">Serverio sertifikate yra nesudÄ—tingas kriptografinis raktas.</translation>
-<translation id="1701955595840307032">Siūlomo turinio gavimas</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Turite gauti <ph name="NAME" /> leidimÄ… apsilankyti Å¡ioje svetainÄ—je</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Puslapio numeris</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Pabandykite paleisti „Windows Network Diagnostics“<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Atnaujinkite sinchronizavimo slaptafrazÄ™.</translation>
+<translation id="1787142507584202372">Atidaryti skirtukai bus rodomi Äia</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">„Google“ saugaus narÅ¡ymo funkcija <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Å¡kÄ… turinį platina <ph name="SUBRESOURCE_HOST" /> – žinomas kenkÄ—jiÅ¡kų programų platintojas. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Netinkama užklausa arba jos parametrai</translation>
+<translation id="1834321415901700177">Šioje svetainėje yra kenkėjiškų programų</translation>
<translation id="1838667051080421715">Peržiūrite tinklalapio šaltinį.</translation>
<translation id="1871208020102129563">Įgaliotasis serveris nustatytas naudoti fiksuotų įgaliotųjų serverių, o ne .pac scenarijaus URL.</translation>
<translation id="1883255238294161206">Sutraukti sąrašą</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Žymės mobiliesiems</translation>
<translation id="2148716181193084225">Å iandien</translation>
-<translation id="2149973817440762519">Redaguoti žymes</translation>
<translation id="2154054054215849342">Sinchronizavimo paslauga nepasiekiama jūsų domenui</translation>
<translation id="2166049586286450108">VisateisÄ— administratoriaus prieiga</translation>
<translation id="2166378884831602661">Ši svetainė negali užtikrinti saugaus ryšio</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Šiuo metu negalite apsilankyti <ph name="SITE" />, nes svetainėje naudojama HSTS. Tinklo klaidos ir išpuoliai dažniausiai yra laikini, todėl šis puslapis tikriausiai veiks vėliau. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Nepaisoma netinkama žymė indekse <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Kitos žymės</translation>
+<translation id="2355395290879513365">Užpuolikai galbūt galės matyti, kuriuos šios svetainės vaizdus peržiūrite, ir juos pakeis siekdami jus suklaidinti.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Rodyti politikÄ… su nenustatyta verte</translation>
<translation id="2396249848217231973">&amp;Anuliuoti ištrynimą</translation>
<translation id="2455981314101692989">Šiame tinklalapyje neleidžiamas automatinis šios formos pildymas.</translation>
+<translation id="2460160116472764928">„Google“ saugaus narÅ¡ymo funkcija 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. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Užpildyti</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Paleistas įrankis „Windows Network Diagnostics“<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Netinkamas paieškos URL.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Ar tikrai norite pašalinti šiuos puslapius iš savo istorijos?</translation>
<translation id="2677748264148917807">IÅ¡eiti</translation>
<translation id="269990154133806163">Serveris pateikė sertifikatą, kuris nebuvo viešai atskleistas naudojant sertifikato skaidrumo politiką. Tai yra tam tikriems sertifikatams taikomas reikalavimas, siekiant užtikrinti, kad jie patikimi ir apsaugo nuo užpuolikų. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Skait. sąraš.</translation>
<translation id="2704283930420550640">VertÄ— neatitinka formato.</translation>
<translation id="2704951214193499422">Šiuo metu „Chromium“ negali patvirtinti jūsų kortelės. Vėliau bandykite dar kartą.</translation>
<translation id="2705137772291741111">IÅ¡saugotos (talpykloje esanÄios) Å¡ios svetainÄ—s kopijos negalima skaityti.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Bandėte pasiekti <ph name="DOMAIN" />, bet sertifikatą, kurį pateikė serveris, anuliavo jo išdavėjas. Tai reiškia, kad serverio pateikti saugos prisijungimo duomenys yra visiškai nepatikimi. Gali būti, kad bendraujate su užpuoliku. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Prašyti leidimo</translation>
<translation id="2713444072780614174">Balta</translation>
+<translation id="2720342946869265578">Netoliese</translation>
<translation id="2721148159707890343">Užklausa sėkminga</translation>
<translation id="2728127805433021124">Serverio sertifikatas pasirašytas naudojant nepatikimą parašo algoritmą.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Paleistas ryšio diagnostikos įrankis<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Galite neleisti visų tarpinių serverių, jungimasis prie kurių sukonfigūruotas nustatymų puslapyje.</translation>
<translation id="2955913368246107853">Uždaryti paieškos juostą</translation>
<translation id="2958431318199492670">Tinklo konfigūracija neatitinka ONC standarto. Kai kurių konfigūracijos dalių neįmanoma importuoti.</translation>
+<translation id="29611076221683977">Šiuo metu svetainės <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> užpuolėjai 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ų numerius).</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="2985306909656435243">Jei Å¡is nustatymas įgalintas, „Chromium“ iÅ¡saugos kortelÄ—s kopijÄ… įrenginyje, kad galÄ—tumÄ—te greiÄiau užpildyti formas.</translation>
@@ -260,7 +269,6 @@
<translation id="3586931643579894722">Slėpti išsamią informaciją</translation>
<translation id="3587482841069643663">Visi</translation>
<translation id="3600246354004376029">„<ph name="TITLE" />“, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Serveris nepalaiko TLS pakartotinių derybų plėtinio.</translation>
<translation id="36224234498066874">Išvalyti naršymo duomenis...</translation>
<translation id="362276910939193118">Rodyti visÄ… istorijÄ…</translation>
<translation id="3623476034248543066">Rodyti vertÄ™</translation>
@@ -272,6 +280,7 @@
<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="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>
@@ -281,10 +290,10 @@
<translation id="3712624925041724820">Licencijos baigÄ—si</translation>
<translation id="3714780639079136834">Įjungti mobiliojo ryšio duomenis arba „Wi-Fi“</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Patikrinti tarpinio serverio, užkardos ir DNS konfigūraciją<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Jei suprantate, kokia rizika gali kilti jūsų saugai, galite <ph name="BEGIN_LINK" />apsilankyti šioje nesaugioje svetainėje<ph name="END_LINK" />, kol iš jos dar nepašalintos pavojingos programos.</translation>
<translation id="3739623965217189342">Nukopijuota nuoroda</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="3788090790273268753">Šios svetainės sertifikato galiojimo laikas baigiasi 2016 m., o sertifikatų grandinėje yra sertifikatas, pasirašytas naudojant SHA-1.</translation>
<translation id="382518646247711829">Jei naudojate tarpinį serverį…</translation>
<translation id="3828924085048779000">Neleidžiama naudoti tuÅ¡Äios slaptafrazÄ—s.</translation>
<translation id="3845539888601087042">Rodoma įrenginių, kuriuose esate prisijungę, istorija. <ph name="BEGIN_LINK" />Sužinokite daugiau<ph name="END_LINK" />.</translation>
@@ -306,6 +315,7 @@
<translation id="4058922952496707368">Raktas „<ph name="SUBKEY" />“: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Kliento programa ir serveris nepalaiko įprasto SSL protokolo versijos ar šifruotojo programų komplekto.</translation>
<translation id="4079302484614802869">Įgaliotojo serverio konfigūracijoje nustatyta naudoti .pac scenarijaus URL, o ne fiksuotus įgaliotuosius serverius.</translation>
+<translation id="4098354747657067197">Ketinate apsilankyti apgaulingoje svetainÄ—je</translation>
<translation id="4103249731201008433">Netinkamas įrenginio serijos numeris</translation>
<translation id="4103763322291513355">Apsilankykite &lt;strong&gt;chrome://policy&lt;/strong&gt;, kad peržiūrėtumėte į juodąjį sąrašą įtrauktų URL sąrašą ir kitą politiką, kurią priverstinai paleido sistemos administratorius.</translation>
<translation id="4110615724604346410">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; jo saugos sertifikate yra klaidų. Taip galėjo būti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užpuoliko. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -323,15 +333,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{nėra}=1{1 programa („$1“)}=2{2 programos („$1“, „$2“)}one{# programa („$1“, „$2“, „$3“)}few{# programos („$1“, „$2“, „$3“)}many{# programos („$1“, „$2“, „$3“)}other{# programų („$1“, „$2“, „$3“)}}</translation>
<translation id="4220128509585149162">Gedimai</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Pabandykite paleisti „Windows Network Diagnostics“<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Ryšys su šia svetaine nėra visiškai saugus</translation>
<translation id="4250680216510889253">Ne</translation>
<translation id="425582637250725228">Atlikti pakeitimai gali nebūti išsaugoti.</translation>
<translation id="4258748452823770588">Netinkamas parašas</translation>
<translation id="4269787794583293679">(NÄ—ra naudotojo vardo)</translation>
+<translation id="4280429058323657511">, gal. pab. <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">„Google“ saugaus naršymo funkcija neseniai <ph name="BEGIN_LINK" />aptiko žalingų programų<ph name="END_LINK" /> svetainėje <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Pasiūlymai tėvams</translation>
<translation id="4304224509867189079">Prisijungti</translation>
<translation id="432290197980158659">Serveris pateikė sertifikatą, kuris neatitinka integruotų reikalavimų. Šie reikalavimai taikomi tam tikrose aukšto saugos lygio svetainėse, kad būtumėte saugūs. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Nepavyko rasti straipsnio</translation>
+<translation id="4326324639298822553">Patikrinkite galiojimo pabaigos datÄ… ir bandykite dar kartÄ…</translation>
<translation id="4331708818696583467">Nesaugi</translation>
+<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="4381091992796011497">Naudotojo vardas:</translation>
<translation id="4394049700291259645">Neleisti</translation>
@@ -349,6 +364,7 @@
<translation id="4589078953350245614">Bandėte pasiekti <ph name="DOMAIN" />, bet serveris pateikė netinkamą sertifikatą. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Skirtukai iš kitų įrenginių</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Peržiūrite plėtinio puslapį.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -357,10 +373,13 @@
<translation id="4726672564094551039">Iš naujo įkelti politiką</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4744603770635761495">Vykdomasis kelias</translation>
+<translation id="4750917950439032686">Ä® Å¡iÄ… svetainÄ™ siunÄiama informacija (pvz., slaptažodžiai arba kredito kortelių numeriai) yra privati.</translation>
<translation id="4756388243121344051">&amp;Istorija</translation>
+<translation id="4759118997339041434">Mokėjimo automatinis pildymas išjungtas</translation>
<translation id="4764776831041365478">Tinklalapis šiuo adresu <ph name="URL" /> gali laikinai neveikti arba visam laikui būti perkeltas kitu žiniatinklio adresu.</translation>
<translation id="4771973620359291008">Įvyko nežinoma klaida.</translation>
<translation id="4800132727771399293">Patikrinkite kortelÄ—s galiojimo pabaigos datÄ… bei saugos kodÄ… (CVC) ir bandykite dar kart</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Tinklo klaida</translation>
<translation id="4816492930507672669">Pritaikyti pagal puslapį</translation>
<translation id="4850886885716139402">Žiūrėti</translation>
@@ -369,6 +388,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Veiksmai</translation>
<translation id="4958444002117714549">Išskleisti sąrašą</translation>
@@ -396,7 +416,6 @@
<translation id="5181140330217080051">AtsisiunÄiama</translation>
<translation id="5190835502935405962">Žymių juosta</translation>
<translation id="5199729219167945352">Bandymai</translation>
-<translation id="5199841536747119669">JÅ«sų pasiÅ«lymai rodomi Äia</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="5299298092464848405">Analizuojant politiką įvyko klaida</translation>
@@ -404,8 +423,10 @@
<translation id="5308689395849655368">StrigÄių ataskaitų teikimas neleidžiamas.</translation>
<translation id="5317780077021120954">IÅ¡saugoti</translation>
<translation id="5327248766486351172">Pavadinimas</translation>
+<translation id="5337705430875057403">Svetainės <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> užpuolėjai gali bandyti apgaulingai priversti atlikti pavojingus veiksmus, pvz., įdiegti programinę įrangą ar atskleisti asmeninę informaciją (pvz., slaptažodžius, telefonų numerius ar kredito kortelių informaciją).</translation>
<translation id="5359637492792381994">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; jo saugos sertifikatas šiuo metu negalioja. Taip gali būti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užpuoliko. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Išsaugant politikos nustatymus įvyko klaida</translation>
+<translation id="5386426401304769735">Šios svetainės sertifikatų grandinėje yra sertifikatas, pasirašytas naudojant SHA-1.</translation>
<translation id="5421136146218899937">Išvalyti naršymo duomenis...</translation>
<translation id="5430298929874300616">Pašalinti žymę</translation>
<translation id="5431657950005405462">Failas nerastas</translation>
@@ -426,17 +447,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> įterptame puslapyje sakoma:</translation>
<translation id="5556459405103347317">Įkelti iš naujo</translation>
<translation id="5565735124758917034">Aktyvus</translation>
+<translation id="5572851009514199876">Pirmiausia prisijunkite prie „Chrome“, kad „Chrome“ galėtų patikrinti, ar jums leidžiama pasiekti šią svetainę.</translation>
+<translation id="5580958916614886209">Patikrinkite galiojimo pabaigos mėnesį ir bandykite dar kartą</translation>
<translation id="560412284261940334">Tvarkymas nepalaikomas</translation>
<translation id="5610142619324316209">Patikrinti ryšį</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> buvote per daug kartų peradresuoti.</translation>
<translation id="5622887735448669177">Ar norite išeiti iš šios svetainės?</translation>
<translation id="5629630648637658800">Įkeliant politikos nustatymus įvyko klaida</translation>
<translation id="5631439013527180824">Netinkamas įrenginio tvarkymo prieigos raktas</translation>
+<translation id="5669703222995421982">Suasmeninto turinio gavimas</translation>
+<translation id="5675650730144413517">Å is puslapis neveikia</translation>
<translation id="5677928146339483299">Užblokuota</translation>
<translation id="5694783966845939798">Bandėte pasiekti <ph name="DOMAIN" />, bet serveris pateikė sertifikatą, kuris pasirašytas naudojant nesudėtingą parašo algoritmą (pvz., SHA-1). Tai reiškia, kad serverio pateikti saugos prisijungimo duomenys galėjo būti suklastoti ir serveris gali būti ne tas, kurio tikėjotės (gali būti, kad bendraujate su užpuoliku). <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Å io tinklalapio tapatybÄ— nenustatyta.</translation>
<translation id="5720705177508910913">Dabartinis naudotojas</translation>
-<translation id="572328651809341494">Naujausi skirtukai</translation>
<translation id="5732392974455271431">Jūsų tėvai gali atblokuoti ją už jus</translation>
<translation id="5784606427469807560">Patvirtinant kortelę kilo problema. Patikrinkite interneto ryšį ir bandykite dar kartą.</translation>
<translation id="5785756445106461925">Be to, šiame puslapyje yra kitų nesaugių išteklių. Perduodant duomenis šiuos išteklius gali peržiūrėti kiti asmenys ir keisti atakuojanti programa, siekianti pakeisti puslapio išvaizdą.</translation>
@@ -445,6 +469,7 @@
<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="5843436854350372569">Bandėte pasiekti <ph name="DOMAIN" />, bet serveris pateikė nesudėtingą raktą turintį sertifikatą. Užpuolikas galėjo užvaldyti privatų raktą, o šis serveris gali būti ne tas, kurio tikėjotės (gali būti, kad bendraujate su užpuoliku). <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Naujas aplankas</translation>
<translation id="5869405914158311789">Nepavyksta pasiekti Å¡ios svetainÄ—s</translation>
@@ -454,6 +479,7 @@
<translation id="59107663811261420">„Google Payments“ nepalaiko šio tipo kortelės naudojantis šio prekybininko paslaugomis. Pasirinkite kitą kortelę.</translation>
<translation id="59174027418879706">Įgalinta</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="5966707198760109579">SavaitÄ—</translation>
<translation id="5967867314010545767">Pašalinti iš istorijos</translation>
<translation id="5975083100439434680">Tolinti</translation>
@@ -472,8 +498,11 @@
<translation id="614940544461990577">Pabandykite atlikti toliau nurodytus veiksmus.</translation>
<translation id="6151417162996330722">Serverio sertifikato galiojimo laikotarpis per ilgas.</translation>
<translation id="6165508094623778733">Sužinokite daugiau</translation>
+<translation id="6177128806592000436">Ryšys su šia svetaine nėra saugus</translation>
<translation id="6203231073485539293">Patikrinkite interneto ryšį</translation>
<translation id="6218753634732582820">Pašalinti adresą iš „Chromium“?</translation>
+<translation id="6251924700383757765">Privatumo politika</translation>
+<translation id="625755898061068298">Pasirinkote išjungti šios svetainės saugos įspėjimus.</translation>
<translation id="6259156558325130047">&amp;Pertvarkyti dar kartÄ…</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> žymės</translation>
<translation id="6264485186158353794">Grįžti prie saugumo</translation>
@@ -482,6 +511,7 @@
<translation id="6305205051461490394"><ph name="URL" /> nepasiekiama.</translation>
<translation id="6321917430147971392">Patikrinkite DNS nustatymus</translation>
<translation id="6328639280570009161">Bandykite neleisti tinklo numatymo</translation>
+<translation id="6328786501058569169">Å i svetainÄ— yra apgaulinga</translation>
<translation id="6337534724793800597">Filtruoti politikÄ… pagal pavadinimÄ…</translation>
<translation id="6342069812937806050">KÄ… tik</translation>
<translation id="6345221851280129312">nežinomas dydis</translation>
@@ -490,6 +520,8 @@
<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="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>
<translation id="6416403317709441254">Šiuo metu negalite apsilankyti <ph name="SITE" />, nes svetainė atsiuntė užšifruotus prisijungimo duomenis, kurių „Chromium“ negali apdoroti. Tinklo klaidos ir išpuoliai dažniausiai yra laikini, todėl šis puslapis tikriausiai veiks vėliau. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Nepavyksta patikrinti, ar sertifikatas buvo panaikintas.</translation>
@@ -499,10 +531,12 @@
<translation id="6458467102616083041">Nepaisoma, nes numatytoji paieška neleidžiama pagal politiką.</translation>
<translation id="6462969404041126431">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; gali būti, kad jo saugos sertifikatas atšauktas. Taip galėjo nutikti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užpuoliko. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;IÅ¡trinti dar kartÄ…</translation>
<translation id="6534179046333460208">Fizinio žiniatinklio pasiūlymai</translation>
<translation id="6550675742724504774">Parinktys</translation>
+<translation id="6556239504065605927">Saugus ryšys</translation>
<translation id="6563469144985748109">Jūsų valdytojas dar jos nepatvirtino</translation>
<translation id="6593753688552673085">mažiau nei <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Å ifravimo parinktys</translation>
@@ -511,7 +545,6 @@
<translation id="6644283850729428850">Å i politika nepatvirtinta.</translation>
<translation id="6652240803263749613">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; kompiuterio operacinė sistema negali pasitikėti jo saugos sertifikatu. Taip galėjo būti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užpuoliko. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Redaguoti aplankÄ…</translation>
-<translation id="6660210980321319655">Automatiškai pranešta <ph name="CRASH_TIME" /></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>
@@ -523,6 +556,7 @@
<translation id="6753269504797312559">Politikos vertÄ—</translation>
<translation id="6757797048963528358">Įjungta įrenginio miego būsena.</translation>
<translation id="6778737459546443941">Jūsų tėtis ar mama dar jos nepatvirtino</translation>
+<translation id="6810899417690483278">Tinkinimo ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Šiuo metu <ph name="URL" /> tinklalapis negalimas. Jis gali būti per daug apkrautas arba neveikiantis dėl techninės priežiūros.</translation>
<translation id="6831043979455480757">VertÄ—jas</translation>
@@ -543,6 +577,8 @@
<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>
+<translation id="7064851114919012435">KontaktinÄ— informacija</translation>
+<translation id="7079718277001814089">Šioje svetainėje yra kenkėjiškų programų</translation>
<translation id="7087282848513945231">Apygarda</translation>
<translation id="7088615885725309056">AnkstesnÄ—</translation>
<translation id="7090678807593890770">Sistemoje „Google“ atlikite paiešką pagal užklausą „<ph name="LINK" />“</translation>
@@ -557,6 +593,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> nesilaikoma saugos standartų.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Sužinokite daugiau<ph name="END_LINK" /> apie šią problemą.</translation>
<translation id="7219179957768738017">Ryšiui naudojama <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Svetainėje, kurioje ketinate apsilankyti, yra kenkėjiškų programų</translation>
<translation id="724975217298816891">Jei norite atnaujinti išsamią kortelės informaciją, įveskite „<ph name="CREDIT_CARD" />“ galiojimo pabaigos datą ir kortelės saugos kodą (CVC). Kai patvirtinsite, išsami kortelės informacija bus bendrinama su šia svetaine.</translation>
<translation id="725866823122871198">Nepavyksta užmegzti privataus ryšio su <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, nes kompiuterio data ir laikas (<ph name="DATE_AND_TIME" />) yra netinkami.</translation>
<translation id="7269802741830436641">Å iam tinklalapiui taikomas peradresavimo ciklas</translation>
@@ -612,6 +649,7 @@ Ei! Kitą kartą gali būti naudinga naudoti inkognito režimą (<ph name="SHORT
<translation id="7658239707568436148">Atšaukti</translation>
<translation id="7667346355482952095">Sugrąžintas politikos prieigos raktas yra tuÅ¡Äias arba neatitinka dabartinio prieigos rakto</translation>
<translation id="7668654391829183341">Nežinomas įrenginys</translation>
+<translation id="7669271284792375604">Å ios svetainÄ—s užgrobÄ—jai gali bandyti apgaule priversti jus įdiegti narÅ¡ymo funkcijas trikdanÄių programų (pvz., pakeitÄ™ pagrindinį puslapį ar rodydami papildomų skelbimų svetainÄ—se, kuriose lankotÄ—s).</translation>
<translation id="7674629440242451245">Domina naujos „Chrome“ funkcijos? Išbandykite mūsų kuriamą kanalą adresu chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Eiti į svetainę <ph name="SITE" /> (nesaugu)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Nepavyksta įkelti šios svetainės iš talpyklos</translation>
@@ -627,14 +665,17 @@ Ei! Kitą kartą gali būti naudinga naudoti inkognito režimą (<ph name="SHORT
<translation id="7800304661137206267">Ryšys užšifruotas naudojant <ph name="CIPHER" /> su <ph name="MAC" /> pranešimo tapatybei nustatyti ir <ph name="KX" /> kaip pagrindinį mainų mechanizmą.</translation>
<translation id="780301667611848630">AÄiÅ«, ne</translation>
<translation id="7805768142964895445">BÅ«sena</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Pašalinti formos pasiūlymą iš „Chrome“?</translation>
<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="7887683347370398519">Patikrinkite kortelÄ—s saugos kodÄ… (CVC) ir bandykite dar kartÄ…</translation>
<translation id="7912024687060120840">Aplanke:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Serverio sertifikatas dar negalioja.</translation>
<translation id="7942349550061667556">Raudona</translation>
+<translation id="7947285636476623132">Patikrinkite galiojimo pabaigos metus ir bandykite dar kartÄ…</translation>
<translation id="7951415247503192394">(32 bitų)</translation>
<translation id="7956713633345437162">Žymės mobiliesiems</translation>
<translation id="7961015016161918242">Niekada</translation>
@@ -642,7 +683,10 @@ Ei! Kitą kartą gali būti naudinga naudoti inkognito režimą (<ph name="SHORT
<translation id="7983301409776629893">Visada versti iš <ph name="ORIGINAL_LANGUAGE" /> į <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Nenurodytas</translation>
<translation id="8012647001091218357">Šiuo metu nepavyko susisiekti su jūsų tėvais. Bandykite dar kartą.</translation>
+<translation id="8025119109950072390">Šios svetainės užpuolėjai gali bandyti apgaulingai priversti atlikti pavojingus veiksmus, pvz., įdiegti programinę įrangą ar atskleisti asmens informaciją (pvz., slaptažodžius, telefonų numerius ar kredito kortelių informaciją).</translation>
+<translation id="803030522067524905">„Google“ saugaus narÅ¡ymo funkcija neseniai aptiko, kad svetainÄ—je <ph name="SITE" /> sukÄiaujama. SukÄiavimo svetainÄ—s apsimeta kitomis svetainÄ—mis, kad jus apgautų. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Šis puslapis yra <ph name="SOURCE_LANGUAGE" /> k. Išversti į <ph name="TARGET_LANGUAGE" /> k.?</translation>
+<translation id="8041089156583427627">Siųsti atsiliepimą</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>
@@ -653,9 +697,11 @@ Ei! Kitą kartą gali būti naudinga naudoti inkognito režimą (<ph name="SHORT
<translation id="8150722005171944719"><ph name="URL" /> nurodyto failo negalima skaityti. Gali būti, kad jis pašalintas, perkeltas arba neleidžiama prieiga prie jo dėl failo leidimų.</translation>
<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="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="8230421197304563332">Šiuo metu svetainės <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 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="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="8249320324621329438">Paskutinį kartą gauta:</translation>
<translation id="8261506727792406068">Panaikinti</translation>
@@ -664,11 +710,13 @@ Ei! Kitą kartą gali būti naudinga naudoti inkognito režimą (<ph name="SHORT
<translation id="8294431847097064396">Å altinis</translation>
<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="8349305172487531364">Žymių juosta</translation>
<translation id="8363502534493474904">Išjungti lėktuvo režimą</translation>
<translation id="8364627913115013041">Nenustatyta.</translation>
<translation id="8380941800586852976">Pavojingas</translation>
<translation id="8382348898565613901">Čia rodomos žymės, kurias naudojote pastaruoju metu</translation>
+<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="8428213095426709021">Nustatymai</translation>
@@ -680,9 +728,9 @@ Ei! Kitą kartą gali būti naudinga naudoti inkognito režimą (<ph name="SHORT
<translation id="8498891568109133222">Per ilgai laukta <ph name="HOST_NAME" /> atsako.</translation>
<translation id="852346902619691059">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; įrenginio operacinė sistema negali pasitikėti jo saugos sertifikatu. Taip galėjo būti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užpuoliko. <ph name="BEGIN_LEARN_MORE_LINK" />Sužinokite daugiau<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">„Google Payments“ nepalaiko šio tipo kortelės. Pasirinkite kitą kortelę.</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="8553075262323480129">IÅ¡versti negalima, nes nepavyko nustatyti puslapio kalbos.</translation>
<translation id="8559762987265718583">Nepavyksta užmegzti privataus ryšio su <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, nes įrenginio data ir laikas (<ph name="DATE_AND_TIME" />) yra netinkami.</translation>
-<translation id="856992080682148">Šios svetainės sertifikato galiojimo laikas baigiasi 2017 m. ar vėliau, o sertifikatų grandinėje yra sertifikatas, pasirašytas naudojant SHA-1.</translation>
<translation id="8571890674111243710">Puslapis verÄiamas į <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Sertifikatas nenurodo mechanizmo, skirto patikrinti, ar jis buvo panaikintas.</translation>
<translation id="8620436878122366504">Jūsų tėvai dar jos nepatvirtino</translation>
@@ -695,10 +743,12 @@ Ei! Kitą kartą gali būti naudinga naudoti inkognito režimą (<ph name="SHORT
<translation id="8740359287975076522">Nepavyko rasti <ph name="HOST_NAME" /> &lt;abbr id="dnsDefinition"&gt;DNS adreso&lt;/abbr&gt;. Nustatoma problema.</translation>
<translation id="8790007591277257123">&amp;IÅ¡trinti dar kartÄ…</translation>
<translation id="8798099450830957504">Numatytasis</translation>
+<translation id="8800988563907321413">Netoliese esantys pasiÅ«lymai jums rodomi Äia</translation>
<translation id="8804164990146287819">Privatumo politika</translation>
<translation id="8820817407110198400">Žymės</translation>
<translation id="8834246243508017242">Įgalinti automatinį pildymą naudojant kontaktus…</translation>
<translation id="883848425547221593">Kitos žymės</translation>
+<translation id="884264119367021077">Siuntimo adresas</translation>
<translation id="884923133447025588">Nerasta atšaukimo mechanizmo.</translation>
<translation id="885730110891505394">Bendrinimas su „Google“</translation>
<translation id="8866481888320382733">Analizuojant politikos nustatymus įvyko klaida</translation>
@@ -716,18 +766,21 @@ Ei! Kitą kartą gali būti naudinga naudoti inkognito režimą (<ph name="SHORT
<translation id="8971063699422889582">BaigÄ—si serverio sertifikato galiojimo laikas.</translation>
<translation id="8987927404178983737">MÄ—nuo</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Pateiktoje svetainėje yra kenkėjiškų programų</translation>
<translation id="9001074447101275817">Tarpiniame serveryje <ph name="DOMAIN" /> būtina įvesti naudotojo vardą ir slaptažodį.</translation>
<translation id="901974403500617787">Visoje sistemoje taikomas žymas gali nustatyti tik savininkas: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Šis puslapis išverstas į <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Saugos klaida</translation>
<translation id="9038649477754266430">Naudokite numatymo paslaugÄ…, kad puslapiai bÅ«tų įkeliami greiÄiau</translation>
<translation id="9039213469156557790">Be to, šiame puslapyje yra kitų nesaugių išteklių. Perduodant duomenis šiuos išteklius gali peržiūrėti kiti asmenys ir keisti atakuojanti programa, siekianti pakeisti puslapio veikimą.</translation>
+<translation id="9040185888511745258">UžgrobÄ—jai <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> gali bandyti apgaule priversti jus įdiegti narÅ¡ymo funkcijas trikdanÄių programų (pvz., pakeitÄ™ pagrindinį puslapį ar rodydami papildomų skelbimų svetainÄ—se, kuriose lankotÄ—s).</translation>
<translation id="9050666287014529139">SlaptafrazÄ—</translation>
<translation id="9065203028668620118">Redaguoti</translation>
<translation id="9068849894565669697">Pasirinkite spalvÄ…</translation>
<translation id="9076283476770535406">Joje gali būti suaugusiesiems skirto turinio</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> puslapis neveikia</translation>
<translation id="9103872766612412690">Svetainėje <ph name="SITE" /> įprastai naudojama šifruotė informacijai apsaugoti. Šį kartą „Chromium“ bandant prisijungti prie <ph name="SITE" />, ji pateikė neįprastus ir netinkamus prisijungimo duomenis. Gali būti, kad užpuolėjas bando apsimesti svetaine <ph name="SITE" /> arba „Wi-Fi“ prisijungimo ekrane nutrūko ryšys. Jūsų informacija vis tiek liko apsaugota, nes „Chromium“ sustabdė prisijungimą prieš apsikeitimą bet kokiais duomenimis.</translation>
<translation id="9137013805542155359">Rodyti originalÄ…</translation>
+<translation id="9137248913990643158">Prieš naudodami šią programą prisijunkite prie „Chrome“.</translation>
<translation id="9148507642005240123">&amp;Anuliuoti redagavimÄ…</translation>
<translation id="9157595877708044936">Nustatoma...</translation>
<translation id="9170848237812810038">&amp;Atšaukti</translation>
@@ -740,7 +793,6 @@ Ei! Kitą kartą gali būti naudinga naudoti inkognito režimą (<ph name="SHORT
<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="962701380617707048">Jei norite atnaujinti išsamią kortelės informaciją, įveskite „<ph name="CREDIT_CARD" />“ galiojimo pabaigos datą ir kortelės saugos kodą (CVC)</translation>
<translation id="969892804517981540">Oficialiai pagaminta</translation>
<translation id="988159990683914416">Vykdymo programa sukurta</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 c18a3bd7912..f6fa6cf3c6a 100644
--- a/chromium/components/strings/components_strings_lv.xtb
+++ b/chromium/components/strings/components_strings_lv.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">AtkÄrtoti izveidojiet savienojumu ar Wi-Fi tÄ«klu.</translation>
<translation id="1175364870820465910">DrukÄt...</translation>
<translation id="1181037720776840403">Noņemt</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />AutomÄtiski nosÅ«tÄ«t<ph name="END_WHITEPAPER_LINK" /> Google serveriem informÄciju par iespÄ“jamÄm droÅ¡Ä«bas problÄ“mÄm. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">NÄkamais</translation>
<translation id="1201895884277373915">VairÄk no Å¡Ä«s vietnes</translation>
<translation id="1206967143813997005">SÄkotnÄ“jais paraksts nav derÄ«gs</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">CiÄnzila</translation>
<translation id="1629803312968146339">Vai vÄ“laties, lai Chrome saglabÄtu Å¡o karti?</translation>
<translation id="1640180200866533862">LietotÄja politikas</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />ApmeklÄ“jiet vietnes sÄkumlapu<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">TÄ«kla konfigurÄcija nav derÄ«ga, un to nevarÄ“ja importÄ“t.</translation>
<translation id="1644574205037202324">VÄ“sture</translation>
<translation id="1645368109819982629">Neatbalstīts protokols</translation>
<translation id="1676269943528358898">VietnÄ“ <ph name="SITE" /> informÄcijas aizsargÄÅ¡anai parasti tiek izmantota Å¡ifrÄ“Å¡ana. Kad pÄrlÅ«kÄ Google Chrome tika mÄ“Ä£inÄts izveidot savienojumu ar vietni <ph name="SITE" />, Å¡oreiz tÄ nosÅ«tÄ«ja neparastus un nepareizus akreditÄcijas datus. IespÄ“jams, tas notika, jo uzbrucÄ“js mÄ“Ä£inÄja uzdoties par vietni <ph name="SITE" />, vai arÄ« Wi-Fi pierakstÄ«Å¡anÄs ekrÄns pÄrtrauc savienojumu. JÅ«su informÄcija joprojÄm ir droÅ¡Ä«bÄ, jo pÄrlÅ«ks Google Chrome pÄrtrauca savienojumu, pirms tika veikta jebkÄdu datu apmaiņa.</translation>
+<translation id="168328519870909584">UzbrucÄ“ji, kuri paÅ¡laik atrodas vietnÄ“ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, iespÄ“jams, mÄ“Ä£inÄs jÅ«su ierÄ«cÄ“ instalÄ“t bÄ«stamas lietotnes, kuras zog vai dzÄ“Å¡ jÅ«su informÄciju (piemÄ“ram, fotoattÄ“lus, paroles, ziņojumus un kredÄ«tkarÅ¡u informÄciju).</translation>
<translation id="168841957122794586">Servera sertifikÄts ietver vÄju kriptogrÄfisko atslÄ“gu.</translation>
-<translation id="1701955595840307032">Iegūt ieteikto saturu</translation>
<translation id="1710259589646384581">OperÄ“tÄjsistÄ“ma</translation>
<translation id="1721312023322545264">Jums ir nepieciešama atļauja no <ph name="NAME" />, lai apmeklētu šo vietni</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Lapas numurs</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Mēģiniet palaist Windows tīkla diagnostiku<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Atjauniniet savu sinhronizÄcijas ieejas frÄzi.</translation>
+<translation id="1787142507584202372">Å eit tiks parÄdÄ«tas jÅ«su atvÄ“rtÄs cilnes</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Nesen Google droÅ¡ajÄ pÄrlÅ«koÅ¡anÄ <ph name="BEGIN_LINK" />tika konstatÄ“ta ļaunprÄtÄ«ga programmatÅ«ra<ph name="END_LINK" /> vietnÄ“ <ph name="SITE" />. Vietnes, kas parasti ir droÅ¡as, dažkÄrt var tikt inficÄ“tas ar ļaunprÄtÄ«gu programmatÅ«ru. Ä»aunprÄtÄ«gÄ programmatÅ«ra nÄk no <ph name="SUBRESOURCE_HOST" />, kas ir zinÄms ļaunprÄtÄ«gas programmatÅ«ras izplatÄ«tÄjs. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">PieprasÄ«jums vai tÄ parametri nebija derÄ«gi.</translation>
+<translation id="1834321415901700177">Å Ä« vietne satur kaitnieciskas programmas</translation>
<translation id="1838667051080421715">JÅ«s skatÄt tÄ«mekļa lapas avotu.</translation>
<translation id="1871208020102129563">Starpniekserveris ir iestatīts, lai tas lietotu fiksētus starpniekserverus, nevis .pac skripta URL.</translation>
<translation id="1883255238294161206">Sakļaut sarakstu</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">MobilÄs grÄmatzÄ«mes</translation>
<translation id="2148716181193084225">Å odien</translation>
-<translation id="2149973817440762519">Rediģēt grÄmatzÄ«mes</translation>
<translation id="2154054054215849342">SinhronizÄcija jÅ«su domÄ“nam nav pieejama.</translation>
<translation id="2166049586286450108">Pilna administratora piekļuve</translation>
<translation id="2166378884831602661">Šī vietne nevar garantēt drošu savienojumu</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Å obrÄ«d nevarat apmeklÄ“t vietni <ph name="SITE" />, jo tajÄ tiek izmantota politika HSTS. TÄ«kla kļūdas un uzbrukumi parasti ir Ä«slaicÄ«gi, tÄpÄ“c Å¡Ä« lapa vÄ“lÄk, visticamÄk, darbosies. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">NederÄ«ga grÄmatzÄ«me ignorÄ“ta <ph name="ENTRY_INDEX" />. rÄdÄ«tÄjÄ</translation>
<translation id="2354001756790975382">Citas grÄmatzÄ«mes</translation>
+<translation id="2355395290879513365">UzbrucÄ“ji var skatÄ«t attÄ“lus, kurus skatÄt Å¡ajÄ vietnÄ“, un apmÄnÄ«t jÅ«s, pÄrveidojot Å¡os attÄ“lus.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">RÄdÄ«t politikas, kuru vÄ“rtÄ«ba nav iestatÄ«ta</translation>
<translation id="2396249848217231973">&amp;Atsaukt dzēšanu</translation>
<translation id="2455981314101692989">Å ajÄ tÄ«mekļa lapÄ ir atspÄ“jota Å¡Ä«s veidlapas automÄtiskÄ aizpilde.</translation>
+<translation id="2460160116472764928">Nesen Google droÅ¡ajÄ pÄrlÅ«koÅ¡anÄ <ph name="BEGIN_LINK" />tika konstatÄ“ta ļaunprÄtÄ«ga programmatÅ«ra<ph name="END_LINK" /> vietnÄ“ <ph name="SITE" />. Vietnes, kuras parasti ir droÅ¡as, var tikt inficÄ“tas ar ļaunprÄtÄ«gu programmatÅ«ru. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Aizpildīt</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Palaist tīkla diagnostiku<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Nederīgs meklēšanas URL.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Vai tieÅ¡Äm vÄ“laties dzÄ“st Å¡Ä«s lapas no savas vÄ“stures?</translation>
<translation id="2677748264148917807">Iziet</translation>
<translation id="269990154133806163">Serveris uzrÄdÄ«ja sertifikÄtu, kas netika publiski atklÄts, izmantojot Certificate Transparency politiku. Å Ä« ir prasÄ«ba noteiktiem sertifikÄtiem, lai nodroÅ¡inÄtu to uzticamÄ«bu un aizsardzÄ«bu pret uzbrucÄ“jiem. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Atvērt sarakstu</translation>
<translation id="2704283930420550640">VÄ“rtÄ«ba neatbilst formÄtam.</translation>
<translation id="2704951214193499422">PÄrlÅ«kÄ Chromium paÅ¡laik nevar apstiprinÄt jÅ«su karti. LÅ«dzu, vÄ“lÄk mÄ“Ä£iniet vÄ“lreiz.</translation>
<translation id="2705137772291741111">Nevar nolasÄ«t Å¡Ä«s vietnes saglabÄto (keÅ¡atmiÅ†Ä ievietoto) kopiju.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">JÅ«s mÄ“Ä£inÄjÄt sasniegt domÄ“nu <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 nemaz nav uzticami. IespÄ“jams, jÅ«s sazinÄties ar uzbrucÄ“ju. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Lūgt atļauju</translation>
<translation id="2713444072780614174">Balta</translation>
+<translation id="2720342946869265578">TuvumÄ</translation>
<translation id="2721148159707890343">Pieprasījums bija veiksmīgs.</translation>
<translation id="2728127805433021124">Servera sertifikÄts ir parakstÄ«ts, izmantojot vÄju paraksta algoritmu.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Palaist savienojamības diagnostiku<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">IestatÄ«jumu lapÄ varat atspÄ“jot jebkurus starpniekserverus, kas konfigurÄ“ti savienojuma izveidei.</translation>
<translation id="2955913368246107853">Aizvērt atrašanas joslu</translation>
<translation id="2958431318199492670">TÄ«kla konfigurÄcija neatbilst standartam ONC. IespÄ“jams, konfigurÄcijas daļas netiks importÄ“tas.</translation>
+<translation id="29611076221683977">PaÅ¡laik vietnÄ“ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> esoÅ¡ie uzbrucÄ“ji jÅ«su Mac 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="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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Slēpt detaļas</translation>
<translation id="3587482841069643663">Visi</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Serveris neatbalsta TLS pÄrskatÄ«Å¡anas paplaÅ¡inÄjumu.</translation>
<translation id="36224234498066874">DzÄ“st pÄrlÅ«koÅ¡anas datus...</translation>
<translation id="362276910939193118">RÄdÄ«t pilnu vÄ“sturi</translation>
<translation id="3623476034248543066">RÄdÄ«t vÄ“rtÄ«bu</translation>
@@ -270,6 +278,7 @@
<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="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>
@@ -279,10 +288,10 @@
<translation id="3712624925041724820">Nav pietiekami daudz licenÄu.</translation>
<translation id="3714780639079136834">Ieslēdziet mobilo datu savienojumu vai Wi-Fi.</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />PÄrbaudiet starpniekserveri, ugunsmÅ«ri un DNS konfigurÄciju<ph name="END_LINK" />.</translation>
+<translation id="3736520371357197498">Ja apzinÄties droÅ¡Ä«bas risku, varat <ph name="BEGIN_LINK" />apmeklÄ“t Å¡o nedroÅ¡o vietni<ph name="END_LINK" />, pirms ir noņemtas bÄ«stamÄs programmas.</translation>
<translation id="3739623965217189342">JÅ«su kopÄ“tÄ saite</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="3788090790273268753">Å Ä«s vietnes sertifikÄta derÄ«gums beidzas 2016. gadÄ, un sertifikÄtu Ä·Ä“dÄ“ ir iekļauts sertifikÄts, kas ir parakstÄ«ts, izmantojot SHA-1.</translation>
<translation id="382518646247711829">Ja izmantojat starpniekserveri...</translation>
<translation id="3828924085048779000">TukÅ¡a ieejas frÄze nav atļauta.</translation>
<translation id="3845539888601087042">Tiek rÄdÄ«ta vÄ“sture no ierÄ«cÄ“m, kurÄs esat pierakstÄ«jies. <ph name="BEGIN_LINK" />Uzziniet vairÄk<ph name="END_LINK" />.</translation>
@@ -304,6 +313,7 @@
<translation id="4058922952496707368">Atslēga <ph name="SUBKEY" />: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Klients un serveris neatbalsta bieži lietoto SSL protokola versiju vai šifra komplektu.</translation>
<translation id="4079302484614802869">Starpniekserveris ir iestatīts, lai tas lietotu .pac skripta URL, nevis fiksētus starpniekserverus.</translation>
+<translation id="4098354747657067197">Maldinoša vietne</translation>
<translation id="4103249731201008433">Ierīces sērijas numurs nav derīgs.</translation>
<translation id="4103763322291513355">ApmeklÄ“jiet vietni &lt;strong&gt;chrome://policy&lt;/strong&gt;, lai skatÄ«tu melnajÄ sarakstÄ iekļautos vietrÄžus URL, kÄ arÄ« citas politikas, ko noteicis sistÄ“mas administrators.</translation>
<translation id="4110615724604346410">Å is serveris nevarÄ“ja pierÄdÄ«t, ka tas ir domÄ“ns <ph name="DOMAIN" />; tÄ droÅ¡Ä«bas sertifikÄtÄ ir kļūdas. Å Ä« problÄ“ma var rasties nepareizas konfigurÄcijas dēļ vai tÄdēļ, ka kÄds uzbrucÄ“js ir pÄrtvÄ“ris jÅ«su savienojumu. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -321,15 +331,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{nav}=1{1 lietotne ($1)}=2{2 lietotnes ($1, $2)}zero{# lietotnes ($1, $2, $3)}one{# lietotne ($1, $2, $3)}other{# lietotnes ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">AvÄrijas</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Mēģiniet palaist tīkla diagnostiku<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Jūsu savienojums ar šo vietni nav pavisam drošs.</translation>
<translation id="4250680216510889253">NÄ“</translation>
<translation id="425582637250725228">VeiktÄs izmaiņas, iespÄ“jams, netiks saglabÄtas.</translation>
<translation id="4258748452823770588">Paraksts nav derīgs.</translation>
<translation id="4269787794583293679">(Nav lietotÄjvÄrda)</translation>
+<translation id="4280429058323657511">, derīga līdz <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google droÅ¡ajÄ pÄrlÅ«koÅ¡anÄ nesen <ph name="BEGIN_LINK" />tika atrastas kaitÄ«gas programmas<ph name="END_LINK" /> vietnÄ“ <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">VecÄku ieteikumi</translation>
<translation id="4304224509867189079">PieteikÅ¡anÄs</translation>
<translation id="432290197980158659">Serveris uzrÄdÄ«ja sertifikÄtu, kas neatbilst iebÅ«vÄ“tajÄm prasÄ«bÄm. Å Ä«s prasÄ«bas ir paredzÄ“tas noteiktÄm augsta droÅ¡Ä«bas lÄ«meņa vietnÄ“m, lai jÅ«s aizsargÄtu. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Rakstu neizdevÄs atrast.</translation>
+<translation id="4326324639298822553">PÄrbaudiet derÄ«guma termiņa datumu un mÄ“Ä£iniet vÄ“lreiz.</translation>
<translation id="4331708818696583467">Nav droši</translation>
+<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="4381091992796011497">LietotÄjvÄrds:</translation>
<translation id="4394049700291259645">Atspējot</translation>
@@ -347,6 +362,7 @@
<translation id="4589078953350245614">JÅ«s mÄ“Ä£inÄjÄt sasniegt domÄ“nu <ph name="DOMAIN" />, taÄu serveris uzrÄdÄ«ja nederÄ«gu sertifikÄtu. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Cilnes no citÄm ierÄ«cÄ“m</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">JÅ«s skatÄt paplaÅ¡inÄjumu lapu.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -355,10 +371,13 @@
<translation id="4726672564094551039">AtkÄrtoti ielÄdÄ“t politikas</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4744603770635761495">IzpildÄms ceļš</translation>
+<translation id="4750917950439032686">JÅ«su informÄcija (piemÄ“ram, paroles vai kredÄ«tkarÅ¡u numuri) ir privÄta, kad tÄ tiek nosÅ«tÄ«ta uz Å¡o vietni.</translation>
<translation id="4756388243121344051">VÄ“sture</translation>
+<translation id="4759118997339041434">MaksÄjumu automÄtiska aizpilde ir atspÄ“jota</translation>
<translation id="4764776831041365478">TÄ«mekļa lapa vietnÄ“ <ph name="URL" /> var Ä«slaicÄ«gi nebÅ«t pieejama, vai tÄ var bÅ«t pÄrvietota uz jaunu tÄ«mekļa adresi.</translation>
<translation id="4771973620359291008">RadÄs nezinÄma kļūda.</translation>
<translation id="4800132727771399293">PÄrbaudiet derÄ«guma termiņu un CVC kodu un mÄ“Ä£iniet vÄ“lreiz.</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Tīkla kļūda</translation>
<translation id="4816492930507672669">IetilpinÄt lapÄ</translation>
<translation id="4850886885716139402">Skatīt</translation>
@@ -367,6 +386,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Darbības</translation>
<translation id="4958444002117714549">Izvērst sarakstu</translation>
@@ -394,7 +414,6 @@
<translation id="5181140330217080051">Notiek lejupielÄde</translation>
<translation id="5190835502935405962">GrÄmatzÄ«mju josla</translation>
<translation id="5199729219167945352">Eksperimenti</translation>
-<translation id="5199841536747119669">Ieteikumi tiek parÄdÄ«ti Å¡eit</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="5299298092464848405">ParsÄ“jot politiku, radÄs kļūda.</translation>
@@ -402,8 +421,10 @@
<translation id="5308689395849655368">AvÄriju pÄrskatu izveide ir atspÄ“jota.</translation>
<translation id="5317780077021120954">SaglabÄt</translation>
<translation id="5327248766486351172">Nosaukums</translation>
+<translation id="5337705430875057403">UzbrucÄ“ji vietnÄ“ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> var pamudinÄt jÅ«s veikt bÄ«stamas darbÄ«bas, piemÄ“ram, instalÄ“t programmatÅ«ru vai atklÄt savu personas informÄciju (t.i., paroles, tÄlruņa numurus vai informÄciju par kredÄ«tkartÄ“m).</translation>
<translation id="5359637492792381994">Å is serveris nevarÄ“ja pierÄdÄ«t, ka tas ir domÄ“ns <ph name="DOMAIN" />; tÄ droÅ¡Ä«bas sertifikÄts Å¡obrÄ«d nav uzticams. Å Ä« problÄ“ma var rasties nepareizas konfigurÄcijas dēļ vai tÄdēļ, ka kÄds uzbrucÄ“js ir pÄrtvÄ“ris jÅ«su savienojumu.<ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">NeizdevÄs saglabÄt politikas iestatÄ«jumus.</translation>
+<translation id="5386426401304769735">Å Ä«s vietnes sertifikÄtu Ä·Ä“dÄ“ ir iekļauts sertifikÄts, kas ir parakstÄ«ts, izmantojot SHA-1.</translation>
<translation id="5421136146218899937">NotÄ«rÄ«t pÄrlÅ«koÅ¡anas datus</translation>
<translation id="5430298929874300616">Noņemt grÄmatzÄ«mi</translation>
<translation id="5431657950005405462">Fails nav atrasts</translation>
@@ -424,17 +445,20 @@
<translation id="5544037170328430102">VietnÄ“ <ph name="SITE" /> iegultÄ lapÄ ir rakstÄ«ts:</translation>
<translation id="5556459405103347317">PÄrlÄdÄ“t</translation>
<translation id="5565735124758917034">Aktīvs</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>
+<translation id="5580958916614886209">PÄrbaudiet derÄ«guma termiņa mÄ“nesi un mÄ“Ä£iniet vÄ“lreiz.</translation>
<translation id="560412284261940334">PÄrvaldÄ«Å¡ana netiek atbalstÄ«ta.</translation>
<translation id="5610142619324316209">PÄrbaudiet savienojumu.</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> novirzÄ«ja pÄrÄk daudz reižu.</translation>
<translation id="5622887735448669177">Vai vēlaties pamest šo vietni?</translation>
<translation id="5629630648637658800">NeizdevÄs ielÄdÄ“t politikas iestatÄ«jumus.</translation>
<translation id="5631439013527180824">IerÄ«ces pÄrvaldÄ«bas marÄ·ieris nav derÄ«gs.</translation>
+<translation id="5669703222995421982">Saņemiet personalizētu saturu</translation>
+<translation id="5675650730144413517">Å Ä« lapa nedarbojas</translation>
<translation id="5677928146339483299">BloÄ·Ä“ts</translation>
<translation id="5694783966845939798">JÅ«s mÄ“Ä£inÄjÄt sasniegt domÄ“nu <ph name="DOMAIN" />, bet serveris uzrÄdÄ«ja sertifikÄtu, kas ir parakstÄ«ts, izmantojot vÄju paraksta algoritmu (piemÄ“ram, SHA-1). Tas nozÄ«mÄ“, ka servera norÄdÄ«tie droÅ¡Ä«bas akreditÄcijas dati var bÅ«t viltoti un Å¡is serveris var nebÅ«t tas serveris, kuru mÄ“Ä£inÄt sasniegt (iespÄ“jams, jÅ«s sazinÄties ar uzbrucÄ“ju). <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">TÄ«mekļa vietnes identitÄte nav apstiprinÄta.</translation>
<translation id="5720705177508910913">PaÅ¡reizÄ“jais lietotÄjs</translation>
-<translation id="572328651809341494">Nesen atvērtas cilnes</translation>
<translation id="5732392974455271431">Lai atbloÄ·Ä“tu, vÄ“rsieties pie vecÄkiem</translation>
<translation id="5784606427469807560">Apstiprinot karti, radÄs problÄ“ma. PÄrbaudiet interneta savienojumu un mÄ“Ä£iniet vÄ“lreiz.</translation>
<translation id="5785756445106461925">TurklÄt Å¡ajÄ lapÄ ir citi resursi, kas nav droÅ¡i. KamÄ“r Å¡ie resursi tiek pÄrsÅ«tÄ«ti, tos var aplÅ«kot citi, kÄ arÄ« uzbrucÄ“js var tos pÄrveidot, lai mainÄ«tu lapas izskatu.</translation>
@@ -443,6 +467,7 @@
<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="5843436854350372569">JÅ«s mÄ“Ä£inÄjÄt sasniegt domÄ“nu <ph name="DOMAIN" />, taÄu serveris uzrÄdÄ«ja sertifikÄtu ar nedroÅ¡u atslÄ“gu. IespÄ“jams, uzbrucÄ“js ir uzlauzis privÄto atslÄ“gu un Å¡is serveris var nebÅ«t tas serveris, kuru mÄ“Ä£inÄt sasniegt (iespÄ“jams, jÅ«s sazinÄties ar uzbrucÄ“ju). <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Jauna mape</translation>
<translation id="5869405914158311789">Å Ä« vietne nav sasniedzama</translation>
@@ -452,6 +477,7 @@
<translation id="59107663811261420">Å is kartes veids Å¡im tirgotÄjam pakalpojumÄ Google Payments netiek atbalstÄ«ts. LÅ«dzu, atlasiet citu karti.</translation>
<translation id="59174027418879706">Iespējots</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="5966707198760109579">Nedēļa</translation>
<translation id="5967867314010545767">Noņemt no vēstures</translation>
<translation id="5975083100439434680">TÄlinÄt</translation>
@@ -470,8 +496,11 @@
<translation id="614940544461990577">Veiciet tÄlÄk norÄdÄ«tÄs darbÄ«bas.</translation>
<translation id="6151417162996330722">Å Ä« servera sertifikÄta derÄ«guma periods ir pÄrÄk ilgs.</translation>
<translation id="6165508094623778733">Uzziniet vairÄk</translation>
+<translation id="6177128806592000436">Savienojums ar šo vietni nav drošs.</translation>
<translation id="6203231073485539293">Interneta savienojuma pÄrbaude</translation>
<translation id="6218753634732582820">Vai noņemt adresi no pÄrlÅ«ka Chromium?</translation>
+<translation id="6251924700383757765">KonfidencialitÄtes politika</translation>
+<translation id="625755898061068298">JÅ«s izvÄ“lÄ“jÄties atspÄ“jot droÅ¡Ä«bas brÄ«dinÄjumus Å¡ai vietnei.</translation>
<translation id="6259156558325130047">&amp;PÄrkÄrtoÅ¡anas atsaukuma atcelÅ¡ana</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> grÄmatzÄ«mes</translation>
<translation id="6264485186158353794">Atpakaļ droÅ¡Ä«bÄ</translation>
@@ -480,6 +509,7 @@
<translation id="6305205051461490394">Vietne <ph name="URL" /> nav sasniedzama.</translation>
<translation id="6321917430147971392">SistÄ“mas DNS iestatÄ«jumu pÄrbaude</translation>
<translation id="6328639280570009161">Tīkla prognožu atspējošana</translation>
+<translation id="6328786501058569169">Å Ä« vietne ir krÄpnieciska</translation>
<translation id="6337534724793800597">Filtrēt politikas pēc nosaukuma</translation>
<translation id="6342069812937806050">Tikko</translation>
<translation id="6345221851280129312">nezinÄms lielums</translation>
@@ -488,6 +518,8 @@
<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="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>
<translation id="6416403317709441254">Å obrÄ«d nevarat apmeklÄ“t vietni <ph name="SITE" />, jo no tÄs tika nosÅ«tÄ«ti sajaukti akreditÄcijas dati, kurus nevar apstrÄdÄt pÄrlÅ«kÄ Chromium. TÄ«kla kļūdas un brÄ«dinÄjumi parasti ir Ä«slaicÄ«gi, tÄpÄ“c Å¡Ä« lapa vÄ“lÄk, visticamÄk, darbosies. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Nevar pÄrbaudÄ«t, vai sertifikÄts ir atsaukts.</translation>
@@ -497,10 +529,12 @@
<translation id="6458467102616083041">Ignorēta, jo ar politiku ir atspējota noklusējuma meklēšana.</translation>
<translation id="6462969404041126431">Å is serveris nevarÄ“ja pierÄdÄ«t, ka tas ir domÄ“ns <ph name="DOMAIN" />; tÄ droÅ¡Ä«bas sertifikÄts, iespÄ“jams, ir atsaukts. Å Ä« problÄ“ma var rasties nepareizas konfigurÄcijas dēļ vai tÄdēļ, ka kÄds uzbrucÄ“js ir pÄrtvÄ“ris jÅ«su savienojumu. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Dzēšanas atsaukuma atcelšana</translation>
<translation id="6534179046333460208">FiziskÄ tÄ«mekļa ieteikumi</translation>
<translation id="6550675742724504774">Opcijas</translation>
+<translation id="6556239504065605927">Drošs savienojums</translation>
<translation id="6563469144985748109">JÅ«su vadÄ«tÄjs vÄ“l nav to apstiprinÄjis</translation>
<translation id="6593753688552673085">mazÄk nekÄ <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Šifrēšanas opcijas</translation>
@@ -509,7 +543,6 @@
<translation id="6644283850729428850">Å Ä« politika ir izbeigta.</translation>
<translation id="6652240803263749613">Å is serveris nevarÄ“ja pierÄdÄ«t, ka tas ir domÄ“ns <ph name="DOMAIN" />; tÄ droÅ¡Ä«bas sertifikÄts nav uzticams jÅ«su datora operÄ“tÄjsistÄ“mÄ. Å Ä« problÄ“ma var rasties nepareizas konfigurÄcijas dēļ vai tÄdēļ, ka kÄds uzbrucÄ“js ir pÄrtvÄ“ris jÅ«su savienojumu. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Mapes rediģēšana</translation>
-<translation id="6660210980321319655">AutomÄtiski ziņots: <ph name="CRASH_TIME" /></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>
@@ -521,6 +554,7 @@
<translation id="6753269504797312559">Politikas vērtība</translation>
<translation id="6757797048963528358">IerÄ«ce tika pÄrslÄ“gta miega režīmÄ.</translation>
<translation id="6778737459546443941">Neviens no jÅ«su vecÄkiem vÄ“l nav to apstiprinÄjis</translation>
+<translation id="6810899417690483278">PielÄgoÅ¡anas ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" /> tÄ«mekļa lapa paÅ¡laik nav pieejama. TÄ var bÅ«t pÄrslogota vai nedarbojas uzturÄ“Å¡anas darbu dēļ.</translation>
<translation id="6831043979455480757">Tulkot</translation>
@@ -541,6 +575,8 @@
<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>
+<translation id="7064851114919012435">KontaktinformÄcija</translation>
+<translation id="7079718277001814089">Å Ä« vietne satur ļaunprÄtÄ«gu programmatÅ«ru</translation>
<translation id="7087282848513945231">GrÄfiste</translation>
<translation id="7088615885725309056">VecÄka</translation>
<translation id="7090678807593890770">Veiciet Google meklÄ“Å¡anu, izmantojot vaicÄjumu “<ph name="LINK" />â€</translation>
@@ -555,6 +591,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> neatbilst drošības standartiem.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Uzziniet vairÄk<ph name="END_LINK" /> par Å¡o problÄ“mu.</translation>
<translation id="7219179957768738017">Savienojums izmanto <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">VietnÄ“ ir ļaunprÄtÄ«ga programmatÅ«ra</translation>
<translation id="724975217298816891">Lai atjauninÄtu kartes informÄciju, ievadiet kredÄ«tkartes <ph name="CREDIT_CARD" /> derÄ«guma termiņu un CVC. PÄ“c apstiprinÄÅ¡anas kartes informÄcija tiks kopÄ«gota ar Å¡o vietni.</translation>
<translation id="725866823122871198">Nevar izveidot privÄtu savienojumu ar <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, jo jÅ«su datora datums un laiks (<ph name="DATE_AND_TIME" />) nav pareizs.</translation>
<translation id="7269802741830436641">TÄ«mekļa lapai ir pÄrvirzes cilpa</translation>
@@ -610,6 +647,7 @@ NÄkamreiz jums varÄ“tu noderÄ“t inkognito režīms (<ph name="SHORTCUT_KEY" />)
<translation id="7658239707568436148">Atcelt</translation>
<translation id="7667346355482952095">Atgrieztais politikas marķieris ir tukšs vai neatbilst pašreizējam marķierim</translation>
<translation id="7668654391829183341">NezinÄma ierÄ«ce</translation>
+<translation id="7669271284792375604">UzbrucÄ“ji Å¡ajÄ vietnÄ“ var mudinÄt jÅ«s uz tÄdu programmu instalÄ“Å¡anu, kuras traucÄ“ pÄrlÅ«koÅ¡anu (piemÄ“ram, mainot sÄkumlapu vai apmeklÄ“tajÄs vietnÄ“s rÄdot papildu reklÄmas).</translation>
<translation id="7674629440242451245">Vai jÅ«s interesÄ“ jaunas Chrome funkcijas? IzmÄ“Ä£iniet izstrÄdÄtÄja versiju vietnÄ“ chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Apmeklēt vietni <ph name="SITE" /> (nav droša)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Å o vietni nevar ielÄdÄ“t no keÅ¡atmiņas</translation>
@@ -625,14 +663,17 @@ NÄkamreiz jums varÄ“tu noderÄ“t inkognito režīms (<ph name="SHORTCUT_KEY" />)
<translation id="7800304661137206267">Savienojums ir Å¡ifrÄ“ts, izmantojot <ph name="CIPHER" />, ar <ph name="MAC" /> ziņojumu autentifikÄcijai un <ph name="KX" /> kÄ atslÄ“gu apmaiņas mehÄnismu.</translation>
<translation id="780301667611848630">NÄ“, paldies</translation>
<translation id="7805768142964895445">Statuss</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Vai noņemt veidlapas ieteikumu no pÄrlÅ«ka Chrome?</translation>
<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="7887683347370398519">PÄrbaudiet CVC kodu un mÄ“Ä£iniet vÄ“lreiz.</translation>
<translation id="7912024687060120840">Mapē:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Servera sertifikÄts vÄ“l nav apstiprinÄts.</translation>
<translation id="7942349550061667556">Sarkana</translation>
+<translation id="7947285636476623132">PÄrbaudiet derÄ«guma termiņa gadu un mÄ“Ä£iniet vÄ“lreiz.</translation>
<translation id="7951415247503192394">(32 bitu)</translation>
<translation id="7956713633345437162">MobilÄs grÄmatzÄ«mes</translation>
<translation id="7961015016161918242">Nekad</translation>
@@ -640,7 +681,10 @@ NÄkamreiz jums varÄ“tu noderÄ“t inkognito režīms (<ph name="SHORTCUT_KEY" />)
<translation id="7983301409776629893">VienmÄ“r tulkot no <ph name="ORIGINAL_LANGUAGE" /> valodas <ph name="TARGET_LANGUAGE" /> valodÄ</translation>
<translation id="7995512525968007366">Nav norÄdÄ«ts</translation>
<translation id="8012647001091218357">MÄ“s nevarÄ“jÄm sasniegt jÅ«su vecÄkus. LÅ«dzu, mÄ“Ä£iniet vÄ“lreiz.</translation>
+<translation id="8025119109950072390">UzbrucÄ“ji Å¡ajÄ vietnÄ“ var mudinÄt jÅ«s veikt bÄ«stamas darbÄ«bas, piemÄ“ram, instalÄ“t programmatÅ«ru vai atklÄt savu personas informÄciju (piemÄ“ram, paroles, tÄlruņa numurus vai informÄciju par kredÄ«tkartÄ“m).</translation>
+<translation id="803030522067524905">Nesen Google droÅ¡ajÄ pÄrlÅ«koÅ¡anÄ tika konstatÄ“ta pikÅ¡Ä·erÄ“Å¡ana vietnÄ“ <ph name="SITE" />. PikÅ¡Ä·erÄ“Å¡anas vietnes uzdodas par citÄm vietnÄ“m, lai jÅ«s maldinÄtu. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Å Ä« lapas saturs ir Å¡ÄdÄ valodÄ: <ph name="SOURCE_LANGUAGE" />. Vai tulkot Å¡ÄdÄ valodÄ: <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Sūtīt atsauksmes</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>
@@ -651,9 +695,11 @@ NÄkamreiz jums varÄ“tu noderÄ“t inkognito režīms (<ph name="SHORTCUT_KEY" />)
<translation id="8150722005171944719">VietnÄ“ <ph name="URL" /> esoÅ¡o failu nevar nolasÄ«t. IespÄ“jams, tas ir noņemts vai pÄrvietots vai piekļuvei nepiecieÅ¡amas atļaujas.</translation>
<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="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="8230421197304563332">PaÅ¡laik vietnÄ“ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 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="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="8249320324621329438">PÄ“dÄ“jÄs pirmsielÄdes laiks:</translation>
<translation id="8261506727792406068">Dzēst</translation>
@@ -662,11 +708,13 @@ NÄkamreiz jums varÄ“tu noderÄ“t inkognito režīms (<ph name="SHORTCUT_KEY" />)
<translation id="8294431847097064396">Avots</translation>
<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="8349305172487531364">GrÄmatzÄ«mju josla</translation>
<translation id="8363502534493474904">Izslēdziet lidojuma režīmu.</translation>
<translation id="8364627913115013041">Nav iestatīta.</translation>
<translation id="8380941800586852976">BÄ«stama</translation>
<translation id="8382348898565613901">Nesen izmantotÄs grÄmatzÄ«mes tiek parÄdÄ«tas Å¡eit</translation>
+<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="8428213095426709021">Iestatījumi</translation>
@@ -678,9 +726,9 @@ NÄkamreiz jums varÄ“tu noderÄ“t inkognito režīms (<ph name="SHORTCUT_KEY" />)
<translation id="8498891568109133222">Vietne <ph name="HOST_NAME" /> pÄrÄk ilgi nereaģēja.</translation>
<translation id="852346902619691059">Å is serveris nevarÄ“ja pierÄdÄ«t, ka tas ir domÄ“ns <ph name="DOMAIN" />; tÄ droÅ¡Ä«bas sertifikÄts nav uzticams jÅ«su ierÄ«ces operÄ“tÄjsistÄ“mÄ. Å Ä« problÄ“ma var rasties nepareizas konfigurÄcijas dēļ vai tÄdēļ, ka kÄds uzbrucÄ“js ir pÄrtvÄ“ris jÅ«su savienojumu. <ph name="BEGIN_LEARN_MORE_LINK" />Uzziniet vairÄk<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Å is kartes veids netiek atbalstÄ«ts pakalpojumÄ Google Payments. LÅ«dzu, atlasiet citu karti.</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="8553075262323480129">TulkoÅ¡ana neizdevÄs, jo lapas valoda nav nosakÄma.</translation>
<translation id="8559762987265718583">Nevar izveidot privÄtu savienojumu ar <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, jo jÅ«su ierÄ«ces datums un laiks (<ph name="DATE_AND_TIME" />) nav pareizs.</translation>
-<translation id="856992080682148">Å Ä«s vietnes sertifikÄta derÄ«gums beidzas 2017. gadÄ vai vÄ“lÄk, un sertifikÄtu Ä·Ä“dÄ“ ir iekļauts sertifikÄts, kas ir parakstÄ«ts, izmantojot SHA-1.</translation>
<translation id="8571890674111243710">Notiek lapas tulkošana uz <ph name="LANGUAGE" /> valodu...</translation>
<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>
@@ -693,10 +741,12 @@ NÄkamreiz jums varÄ“tu noderÄ“t inkognito režīms (<ph name="SHORTCUT_KEY" />)
<translation id="8740359287975076522">Nevarēja atrast <ph name="HOST_NAME" /> &lt;abbr id="dnsDefinition"&gt;DNS adresi&lt;/abbr&gt;. Notiek problēmas diagnosticēšana.</translation>
<translation id="8790007591277257123">&amp;Atcelt dzēšanas atsaukšanu</translation>
<translation id="8798099450830957504">Noklusējums</translation>
+<translation id="8800988563907321413">Å eit tiks parÄdÄ«ti funkcijas TuvumÄ ieteikumi</translation>
<translation id="8804164990146287819">KonfidencialitÄtes politika</translation>
<translation id="8820817407110198400">GrÄmatzÄ«mes</translation>
<translation id="8834246243508017242">IespÄ“jot automÄtisko aizpildi, izmantojot kontaktpersonas...</translation>
<translation id="883848425547221593">Citas grÄmatzÄ«mes</translation>
+<translation id="884264119367021077">PiegÄdes adrese</translation>
<translation id="884923133447025588">Nav atrasts atsaukÅ¡anas mehÄnisms.</translation>
<translation id="885730110891505394">Kopīgošana ar Google</translation>
<translation id="8866481888320382733">ParsÄ“jot politikas iestatÄ«jumus, radÄs kļūda.</translation>
@@ -714,18 +764,21 @@ NÄkamreiz jums varÄ“tu noderÄ“t inkognito režīms (<ph name="SHORTCUT_KEY" />)
<translation id="8971063699422889582">Servera sertifikÄtam ir beidzies derÄ«guma termiņš.</translation>
<translation id="8987927404178983737">MÄ“nesis</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Vietnē, kura tiks atvērta, ir kaitīgas programmas</translation>
<translation id="9001074447101275817">Starpniekserveris <ph name="DOMAIN" /> pieprasa lietotÄjvÄrdu un paroli.</translation>
<translation id="901974403500617787">Atzīmes, kas attiecas uz visu sistēmu, var iestatīt tikai īpašnieks: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Å Ä« lapa ir tulkota <ph name="TARGET_LANGUAGE" /> valodÄ.</translation>
+<translation id="9035022520814077154">Drošības kļūda</translation>
<translation id="9038649477754266430">Ieteikumu pakalpojuma izmantoÅ¡ana ÄtrÄkai lapu ielÄdei</translation>
<translation id="9039213469156557790">TurklÄt Å¡ajÄ lapÄ ir citi resursi, kas nav droÅ¡i. KamÄ“r Å¡ie resursi tiek pÄrsÅ«tÄ«ti, tos var aplÅ«kot citi, kÄ arÄ« uzbrucÄ“js var tos pÄrveidot, lai mainÄ«tu lapas uzvedÄ«bu.</translation>
+<translation id="9040185888511745258">UzbrucÄ“ji vietnÄ“ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, iespÄ“jams, mudinÄs jÅ«s uz tÄdu programmu instalÄ“Å¡anu, kuras traucÄ“ pÄrlÅ«koÅ¡anu (piemÄ“ram, mainot sÄkumlapu vai apmeklÄ“tajÄs vietnÄ“s rÄdot papildu reklÄmas).</translation>
<translation id="9050666287014529139">Ieejas frÄze</translation>
<translation id="9065203028668620118">Labot</translation>
<translation id="9068849894565669697">KrÄsas izvÄ“le</translation>
<translation id="9076283476770535406">Vietnē var būt pieaugušajiem paredzēts saturs</translation>
-<translation id="9092364396508701805">Vietnes <ph name="HOST_NAME" /> lapa nedarbojas</translation>
<translation id="9103872766612412690">VietnÄ“ <ph name="SITE" /> informÄcijas aizsargÄÅ¡anai parasti tiek izmantota Å¡ifrÄ“Å¡ana. Kad pÄrlÅ«kÄ Chromium tika mÄ“Ä£inÄts izveidot savienojumu ar vietni <ph name="SITE" />, Å¡oreiz tÄ nosÅ«tÄ«ja neparastus un nepareizus akreditÄcijas datus. IespÄ“jams, tas notika, jo uzbrucÄ“js mÄ“Ä£inÄja uzdoties par vietni <ph name="SITE" />, vai arÄ« Wi-Fi pierakstÄ«Å¡anÄs ekrÄns pÄrtrauca savienojumu. JÅ«su informÄcija joprojÄm ir droÅ¡Ä«bÄ, jo pÄrlÅ«ks Chromium pÄrtrauca savienojumu, pirms tika veikta jebkÄdu datu apmaiņa.</translation>
<translation id="9137013805542155359">RÄdÄ«t oriÄ£inÄlo</translation>
+<translation id="9137248913990643158">Pirms Å¡Ä«s lietotnes izmantoÅ¡anas, lÅ«dzu, palaidiet pÄrlÅ«ku Chrome un pierakstieties tajÄ.</translation>
<translation id="9148507642005240123">&amp;Atsaukt labojumu</translation>
<translation id="9157595877708044936">Notiek uzstÄdÄ«Å¡ana...</translation>
<translation id="9170848237812810038">&amp;Atsaukt</translation>
@@ -738,7 +791,6 @@ NÄkamreiz jums varÄ“tu noderÄ“t inkognito režīms (<ph name="SHORTCUT_KEY" />)
<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="962701380617707048">Lai atjauninÄtu kartes informÄciju, ievadiet kredÄ«tkartes <ph name="CREDIT_CARD" /> derÄ«guma termiņu un CVC</translation>
<translation id="969892804517981540">OficiÄlÄ UzbÅ«ve</translation>
<translation id="988159990683914416">AttÄ«stÄ«tÄja konstrukcija</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 3bf456bb724..033f843c10f 100644
--- a/chromium/components/strings/components_strings_ml.xtb
+++ b/chromium/components/strings/components_strings_ml.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Wi-Fi-ലേകàµà´•àµ വീണàµà´Ÿàµà´‚ കണകàµà´±àµà´±àµà´šàµ†à´¯àµà´¯àµà´¨àµà´¨àµ</translation>
<translation id="1175364870820465910">&amp;à´…à´šàµà´šà´Ÿà´¿à´•àµà´•àµ‚...</translation>
<translation id="1181037720776840403">നീകàµà´•à´‚ചെയàµà´¯àµ‚</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />à´¸àµà´°à´•àµà´·à´¯àµ† ബാധികàµà´•à´¾àµ» സാധàµà´¯à´¤à´¯àµà´³àµà´³ കാരàµà´¯à´™àµà´™à´³àµà´Ÿàµ† വിശദാംശങàµà´™àµ¾<ph name="END_WHITEPAPER_LINK" /> à´¸àµà´µà´¯à´®àµ‡à´µ Google-ൽ റിപàµà´ªàµ‹àµ¼à´Ÿàµà´Ÿàµà´šàµ†à´¯àµà´¯àµà´¨àµà´¨àµ. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">à´…à´Ÿàµà´¤àµà´¤à´¤àµ</translation>
<translation id="1201895884277373915">à´ˆ സൈറàµà´±à´¿àµ½ നിനàµà´¨àµà´‚ കൂടàµà´¤àµ½</translation>
<translation id="1206967143813997005">à´ªàµà´°à´¾à´°à´‚à´­ സിഗàµà´¨àµ‡à´šàµà´šàµ¼ ശരിയലàµà´²</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">സിയാൻ</translation>
<translation id="1629803312968146339">Chrome à´ˆ കാർഡൠസംരകàµà´·à´¿à´•àµà´•à´£àµ‹?</translation>
<translation id="1640180200866533862">ഉപയോകàµà´¤àµƒ നയങàµà´™àµ¾</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />സൈറàµà´±à´¿à´¨àµà´±àµ† ഹോം പേജൠസനàµà´¦àµ¼à´¶à´¿à´•àµà´•àµà´¨àµà´¨à´¤àµ<ph name="END_LINK" /> പരീകàµà´·à´¿à´šàµà´šàµà´¨àµ‹à´•àµà´•àµ‚.</translation>
<translation id="1644184664548287040">നെറàµà´±àµâ€Œà´µàµ¼à´•àµà´•àµ കോൺഫിഗറേഷൻ അസാധàµà´µà´¾à´¯à´¤à´¿à´¨à´¾àµ½ ഇമàµà´ªàµ‹àµ¼à´Ÿàµà´Ÿàµà´šàµ†à´¯àµà´¯à´¾àµ» à´•à´´à´¿à´žàµà´žà´¿à´²àµà´².</translation>
<translation id="1644574205037202324">à´šà´°à´¿à´¤àµà´°à´‚</translation>
<translation id="1645368109819982629">à´ªàµà´°àµ‡à´¾à´Ÿàµà´Ÿàµ‡à´¾à´•àµà´•àµ‡à´¾àµ¾ പിനàµà´¤àµà´£à´¯àµâ€Œà´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´²</translation>
<translation id="1676269943528358898">നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ പരിരകàµà´·à´¿à´•àµà´•à´¾àµ» സാധാരണയായി <ph name="SITE" />, എൻകàµà´°à´¿à´ªàµâ€Œà´·àµ» ഉപയോഗികàµà´•àµà´¨àµà´¨àµ. ഇപàµà´ªàµ‹àµ¾ <ph name="SITE" /> സൈറàµà´±à´¿à´²àµ‡à´•àµà´•àµ കണകàµâ€Œà´±àµà´±àµà´šàµ†à´¯àµà´¯à´¾àµ» Google Chrome à´¶àµà´°à´®à´¿à´šàµà´šà´ªàµà´ªàµ‹àµ¾, അസാധാരണമായതàµà´‚ തെറàµà´±à´¾à´¯à´¤àµà´®à´¾à´¯ à´•àµà´°àµ†à´¡àµ»à´·àµà´¯à´²àµà´•àµ¾ വെബàµâ€Œà´¸àµˆà´±àµà´±àµ തിരികെ അയചàµà´šàµ. ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿, <ph name="SITE" /> à´Žà´¨àµà´¨à´¤à´¾à´¯à´¿ ഭാവികàµà´•à´¾àµ» à´¶àµà´°à´®à´¿à´•àµà´•àµà´®àµà´ªàµ‹à´´àµ‹ Wi-Fi സൈൻ ഇൻ à´¸àµâ€Œà´•àµà´°àµ€àµ», കണകàµà´·à´¨àµ† തടസàµà´¸à´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´®àµà´ªàµ‹à´´àµ‹ ആണൠഇങàµà´™à´¨àµ† സംഭവികàµà´•à´¾à´¨à´¿à´Ÿà´¯àµà´³àµà´³à´¤àµ. à´à´¤àµ†à´™àµà´•à´¿à´²àµà´‚ ഡാറàµà´± കൈമാറàµà´¨àµà´¨à´¤à´¿à´¨àµà´®àµà´®àµà´ªàµ Google Chrome കണകàµà´·àµ» അവസാനിപàµà´ªà´¿à´šàµà´šà´¤à´¿à´¨à´¾àµ½, നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ à´¤àµà´Ÿàµ¼à´¨àµà´¨àµà´‚ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´¯à´¿à´°à´¿à´•àµà´•àµà´‚.</translation>
+<translation id="168328519870909584"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ† ആകàµà´°à´®à´£à´•à´¾à´°à´¿ നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, ഫോടàµà´Ÿàµ‹à´•àµ¾, പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾, സനàµà´¦àµ‡à´¶à´™àµà´™àµ¾, à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡàµà´•àµ¾ പോലàµà´³àµà´³à´µ) മോഷàµâ€Œà´Ÿà´¿à´•àµà´•à´¾à´¨àµ‹ ഇലàµà´²à´¾à´¤à´¾à´•àµà´•à´¾à´¨àµ‹ ഉപകരണതàµà´¤à´¿àµ½ അപകടകരമായ ആപàµà´ªàµâ€Œà´¸àµ ഇൻസàµà´±àµà´±à´¾àµ¾ ചെയàµà´¯à´¾àµ» à´¶àµà´°à´®à´¿à´šàµà´šàµ‡à´•àµà´•à´¾à´‚.</translation>
<translation id="168841957122794586">സെർവർ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¿àµ½ ഒരൠദàµàµ¼à´¬à´²à´®à´¾à´¯ ഗൂഢഭാഷ കീ ഉൾപàµà´ªàµ†à´Ÿàµà´¨àµà´¨àµ.</translation>
-<translation id="1701955595840307032">നിർദàµà´¦àµ‡à´¶à´¿à´šàµà´š ഉളàµà´³à´Ÿà´•àµà´•à´‚ à´¸àµà´µà´¨àµà´¤à´®à´¾à´•àµà´•àµà´•</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">à´ˆ സൈറàµà´±àµ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•à´¾àµ» നിങàµà´™àµ¾à´•àµà´•àµ <ph name="NAME" /> à´Žà´¨àµà´¨à´¯à´¾à´³à´¿àµ½ നിനàµà´¨àµà´³àµà´³ à´…à´¨àµà´®à´¤à´¿ ആവശàµà´¯à´®à´¾à´£àµ</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">പേജൠനമàµà´ªàµ¼</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows നെറàµà´±àµâ€Œà´µàµ¼à´•àµà´•àµ ഡയഗണോസàµâ€Œà´±àµà´±à´¿à´•àµâ€Œà´¸àµ റൺ ചെയàµâ€Œà´¤àµà´¨àµ‹à´•àµà´•àµ‚<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">നിങàµà´™à´³àµà´Ÿàµ† സമനàµà´µà´¯ പാസàµâ€Œà´«àµà´°àµ‡à´¸àµ ദയവായി à´…à´ªàµâ€Œà´¡àµ‡à´±àµà´±àµ ചെയàµà´¯àµà´•.</translation>
+<translation id="1787142507584202372">നിങàµà´™àµ¾ നിലവിൽ à´¤àµà´±à´¨àµà´¨à´¿à´Ÿàµà´Ÿàµà´³àµà´³ ടാബàµà´•àµ¾ ഇവിടെ ദൃശàµà´¯à´®à´¾à´•àµà´‚</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883"><ph name="SITE" /> à´Žà´¨àµà´¨à´¤à´¿àµ½ Google à´¸àµà´°à´•àµà´·à´¿à´¤ à´¬àµà´°àµ—സിംഗൠഈയിടെ <ph name="BEGIN_LINK" />മാൽവേർ à´•à´£àµà´Ÿàµ†à´¤àµà´¤à´¿<ph name="END_LINK" /> . സാധാരണ നിലയിൽ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´¯ വെബàµà´¸àµˆà´±àµà´±àµà´•à´³à´¿àµ½ ചിലപàµà´ªàµ‹àµ¾ മാൽവേർ ഉണàµà´Ÿà´¾à´¯àµ‡à´•àµà´•à´¾à´‚. അറിയപàµà´ªàµ†à´Ÿàµà´¨àµà´¨ മാൽ‌വേർ വിതരണകàµà´•à´¾à´°à´¾à´¯ <ph name="SUBRESOURCE_HOST" /> à´Žà´¨àµà´¨à´¤à´¿àµ½ നിനàµà´¨à´¾à´£àµ ദോഷകരമായ ഉളàµà´³à´Ÿà´•àµà´•à´‚ വരàµà´¨àµà´¨à´¤àµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">à´…à´­àµà´¯àµ¼à´¤àµà´¥à´¨ à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ à´…à´­àµà´¯àµ¼à´¤àµà´¥à´¨ പാരാമീറàµà´±à´±àµà´•àµ¾ അസാധàµà´µà´¾à´£àµ</translation>
+<translation id="1834321415901700177">à´ˆ സൈറàµà´±à´¿àµ½ ദോഷകരമായ à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•àµ¾ à´…à´Ÿà´™àµà´™à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ</translation>
<translation id="1838667051080421715">നിങàµà´™àµ¾ ഒരൠവെബൠപേജിനàµà´±àµ† ഉറവിടമാണൠകാണàµà´¨àµà´¨à´¤àµ.</translation>
<translation id="1871208020102129563">à´¸àµà´¥à´¿à´°à´®à´¾à´¯ à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ സെർവറàµà´•àµ¾ ഉപയോഗികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´¯à´¿ à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ സജàµà´œàµ€à´•à´°à´¿à´šàµà´šà´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ, ഒരൠ.pac à´¸àµâ€Œà´•àµà´°à´¿à´ªàµà´±àµà´±àµ URL ഉപയോഗികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´²àµà´².</translation>
<translation id="1883255238294161206">ലിസàµà´±àµà´±àµ à´šàµà´°àµà´•àµà´•àµà´•</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">തപാൽ കോഡàµ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{ഒരൠനിർദàµà´¦àµ‡à´¶à´‚}other{# നിർദàµà´¦àµ‡à´¶à´™àµà´™àµ¾}}</translation>
<translation id="2065985942032347596">ആധികാരികത ആവശàµà´¯à´®à´¾à´£àµ</translation>
-<translation id="2079545284768500474">പൂരàµâ€â€Œà´µàµà´µà´¾à´µà´¸àµà´¥à´¯à´¿à´²à´¾à´•àµà´•àµà´•</translation>
+<translation id="2079545284768500474">പഴയപടിയാകàµà´•àµà´•</translation>
<translation id="20817612488360358">സിസàµà´±àµà´±à´‚ à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ à´•àµà´°à´®àµ€à´•à´°à´£à´‚ ഉപയോഗികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´¯à´¿ സജàµà´œà´®à´¾à´•àµà´•à´¿, പകàµà´·àµ† ഒരൠസàµâ€Œà´ªà´·àµâ€Œà´Ÿà´®à´¾à´¯ à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ കോൺഫിഗറേഷനàµà´‚ അതോടൊപàµà´ªà´‚ നിർദàµà´¦àµ‡à´¶à´¿à´šàµà´šà´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ.</translation>
<translation id="2086652334978798447">Google നിർദàµà´¦àµ‡à´¶à´¿à´šàµà´š, à´µàµà´¯à´•àµà´¤à´¿à´ªà´°à´®à´¾à´•àµà´•à´¿à´¯ ഉളàµà´³à´Ÿà´•àµà´•à´‚ à´¸àµà´µà´¨àµà´¤à´®à´¾à´•àµà´•à´¾àµ», Chrome-ൽ സൈൻ ഇൻ ചെയàµà´¯àµà´•.</translation>
<translation id="2089090684895656482">à´•àµà´±à´šàµà´šàµ</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">നേറàµà´±àµ€à´µàµ à´•àµà´²à´¯à´¨àµâ€à´±àµ</translation>
<translation id="213826338245044447">മൊബൈൽ à´¬àµà´•àµà´•àµâ€Œà´®à´¾àµ¼à´•àµà´•àµà´•àµ¾</translation>
<translation id="2148716181193084225">ഇനàµà´¨àµ</translation>
-<translation id="2149973817440762519">à´¬àµà´•àµà´®à´¾à´°àµâ€à´•àµà´•àµ à´Žà´¡à´¿à´±àµà´±àµ ചെയàµà´¯àµà´•</translation>
<translation id="2154054054215849342">നിങàµà´™à´³àµà´Ÿàµ† ഡൊമെയàµâ€Œà´¨à´¿à´¨àµ വേണàµà´Ÿà´¿ സമനàµà´µà´¯à´‚ ലഭàµà´¯à´®à´²àµà´²</translation>
<translation id="2166049586286450108">പൂർണàµà´£à´®à´¾à´¯ à´…à´¡àµâ€Œà´®à´¿àµ» ആകàµâ€Œà´¸à´¸àµà´¸àµ</translation>
<translation id="2166378884831602661">à´ˆ സൈറàµà´±à´¿à´¨àµ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´¯ കണകàµà´·àµ» നൽകാനാകിലàµà´²</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">വെബàµà´¸àµˆà´±àµà´±àµ HSTS ഉപയോഗികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¾àµ½ നിങàµà´™àµ¾à´•àµà´•àµ ഇപàµà´ªàµ‹àµ¾ <ph name="SITE" /> സനàµà´¦àµ¼à´¶à´¿à´•àµà´•à´¾à´¨à´¾à´•à´¿à´²àµà´². നെറàµà´±àµâ€Œà´µàµ¼à´•àµà´•àµ പിശകàµà´•à´³àµà´‚ ആകàµà´°à´®à´£à´™àµà´™à´³àµà´‚ സാധാരണയായി താൽകàµà´•à´¾à´²à´¿à´• à´ªàµà´°à´¶àµâ€Œà´¨à´™àµà´™à´³à´¾à´¯à´¤à´¿à´¨à´¾àµ½ à´ˆ പേജൠമികàµà´•à´µà´¾à´±àµà´‚ പിനàµà´¨àµ€à´Ÿàµ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¿à´•àµà´•àµà´‚. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155"><ph name="ENTRY_INDEX" /> സൂചികയിൽ അസാധàµà´µà´¾à´¯ à´¬àµà´•àµà´•àµâ€Œà´®à´¾àµ¼à´•àµà´•àµ അവഗണിചàµà´šàµ</translation>
<translation id="2354001756790975382">മറàµà´±àµ à´¬àµà´•àµâ€Œà´®à´¾à´°àµâ€à´•àµà´•àµà´•à´³àµâ€</translation>
+<translation id="2355395290879513365">നിങàµà´™àµ¾ à´ˆ സൈറàµà´±à´¿àµ½ തിരയàµà´¨àµà´¨ à´šà´¿à´¤àµà´°à´™àµà´™àµ¾ കാണാനàµà´‚ അവയിൽ മാറàµà´±à´‚ വരàµà´¤àµà´¤à´¿ നിങàµà´™à´³àµ† കബളിപàµà´ªà´¿à´•àµà´•à´¾à´¨àµà´‚ à´…à´•àµà´°à´®à´•à´¾à´°à´¿à´•àµ¾à´•àµà´•àµ à´•à´´à´¿à´žàµà´žàµ‡à´•àµà´•à´¾à´‚.</translation>
<translation id="2359808026110333948">à´¤àµà´Ÿà´°àµ‚</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" />-നൠകàµà´¯à´¾à´ªàµâ€Œà´šàµà´šàµ¼ ചെയàµâ€Œà´¤ à´•àµà´°à´¾à´·àµ റിപàµà´ªàµ‹àµ¼à´Ÿàµà´Ÿàµ à´…à´ªàµâ€Œà´²àµ‹à´¡àµà´šàµ†à´¯àµâ€Œà´¤à´¿à´Ÿàµà´Ÿà´¿à´²àµà´²</translation>
<translation id="2367567093518048410">നില</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">മൂലàµà´¯à´®àµŠà´¨àµà´¨àµà´‚ സജàµà´œà´®à´¾à´•àµà´•à´¾à´¤àµà´¤ നയങàµà´™àµ¾ കാണികàµà´•àµà´•</translation>
<translation id="2396249848217231973">&amp;ഇലàµà´²à´¾à´¤à´¾à´•àµà´•àµ½ പഴയപടിയാകàµà´•àµà´•</translation>
<translation id="2455981314101692989">à´ˆ വെബàµà´ªàµ‡à´œàµ à´ˆ ഫോമിനായàµà´³àµà´³ à´¸àµà´µà´ªàµà´°àµ‡à´°à´¿à´¤ à´«à´¿à´²àµà´²à´¿à´‚ഗിനെ à´…à´ªàµà´°à´¾à´ªàµà´¤à´®à´¾à´•àµà´•à´¿.</translation>
+<translation id="2460160116472764928"><ph name="SITE" /> à´Žà´¨àµà´¨à´¤à´¿àµ½ Google à´¸àµà´°à´•àµà´·à´¿à´¤ à´¬àµà´°àµ—സിംഗൠഈയിടെ <ph name="BEGIN_LINK" />മാൽവേർ à´•à´£àµà´Ÿàµ†à´¤àµà´¤à´¿<ph name="END_LINK" />. സാധാരണ നിലയിൽ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´¯ വെബàµà´¸àµˆà´±àµà´±àµà´•à´³à´¿àµ½ ചിലപàµà´ªàµ‹àµ¾ മാൽവേർ ഉണàµà´Ÿà´¾à´¯à´¿à´°à´¿à´•àµà´•à´¾à´‚. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">പൂരിപàµà´ªà´¿à´•àµà´•àµà´•</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />നെറàµà´±àµâ€Œà´µàµ¼à´•àµà´•àµ ഡയഗണോസàµâ€Œà´±àµà´±à´¿à´•àµâ€Œà´¸àµ റൺ ചെയàµà´¯àµà´¨àµà´¨àµ<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">തിരയൽ URL അസാധàµà´µà´¾à´£àµ.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">നിങàµà´™à´³àµà´Ÿàµ† à´šà´°à´¿à´¤àµà´°à´¤àµà´¤à´¿à´²àµâ€ നിനàµà´¨àµà´‚ à´ˆ പേജàµà´•à´³àµâ€ മായàµà´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ നിങàµà´™à´³àµâ€ താലàµà´ªà´°àµà´¯à´®àµà´£àµà´Ÿàµ‹?</translation>
<translation id="2677748264148917807">ഉപേകàµà´·à´¿à´•àµà´•àµà´•</translation>
<translation id="269990154133806163">സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ à´¸àµà´¤à´¾à´°àµà´¯à´¤à´¾ നയം ഉപയോഗിചàµà´šàµ സെർവർ, à´Žà´²àµà´²à´¾à´µàµ¼à´•àµà´•àµà´®à´¾à´¯à´¿ വെളàµà´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿à´¯à´¿à´Ÿàµà´Ÿà´¿à´²àµà´²à´¾à´¤àµà´¤ ഒരൠസർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ ലഭàµà´¯à´®à´¾à´•àµà´•à´¿. à´šà´¿à´² സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµà´•àµ¾ വിശàµà´µà´¾à´¸à´¯àµ‹à´—àµà´¯à´®à´¾à´£àµ†à´¨àµà´¨àµà´‚ à´…à´•àµà´°à´®à´•à´¾à´°à´¿à´•à´³à´¿àµ½ നിനàµà´¨àµà´‚ പരിരകàµà´·à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¾à´£àµ†à´¨àµà´¨àµà´‚ ഉറപàµà´ªà´¾à´•àµà´•à´¾àµ» അവയàµâ€Œà´•àµà´•àµ ഇതൠആവശàµà´¯à´®à´¾à´£àµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">വായനാ ലിസàµà´±àµà´±àµ</translation>
<translation id="2704283930420550640">മൂലàµà´¯à´‚ ഫോർമാറàµà´±àµà´®à´¾à´¯à´¿ പൊരàµà´¤àµà´¤à´ªàµà´ªàµ†à´Ÿàµà´¨àµà´¨à´¿à´²àµà´².</translation>
<translation id="2704951214193499422">Chromium-à´¤àµà´¤à´¿à´¨àµ ഇപàµà´ªàµ‹àµ¾ നിങàµà´™à´³àµà´Ÿàµ† കാർഡൠസàµà´¥à´¿à´°àµ€à´•à´°à´¿à´•àµà´•à´¾à´¨à´¾à´¯à´¿à´²àµà´². പിനàµà´¨àµ€à´Ÿàµ വീണàµà´Ÿàµà´‚ à´¶àµà´°à´®à´¿à´•àµà´•àµà´•.</translation>
<translation id="2705137772291741111">à´ˆ സൈറàµà´±à´¿à´¨àµà´±àµ† സംരകàµà´·à´¿à´šàµà´š (കാഷെ ചെയàµâ€Œà´¤) പതിപàµà´ªàµ വായികàµà´•à´¾à´¨à´¾à´•à´¾à´¤àµà´¤à´¤à´¾à´£àµ.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">നിങàµà´™àµ¾â€Œ <ph name="DOMAIN" /> ഡൊമെയàµâ€Œà´¨à´¿àµ½â€Œ à´Žà´¤àµà´¤à´¾à´¨àµâ€â€Œ à´¶àµà´°à´®à´¿à´šàµà´šàµ, പകàµà´·àµ‡ സെർവർ‌ നൽകിയ സർ‌ടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¿à´¨àµ† അതിനàµà´±àµ† ഇഷàµà´¯àµ‚വർ റദàµà´¦à´¾à´•àµà´•à´¿. സെർവർ നൽ‌കിയ à´¸àµà´°à´•àµà´·à´¾ à´•àµà´°àµ†à´¡à´¨àµâ€â€Œà´·àµà´¯à´²àµà´•àµ¾ തികചàµà´šàµà´‚ വിശàµà´µà´¾â€à´¸à´¯àµ‹à´—àµà´¯à´®à´²àµà´² à´Žà´¨àµà´¨à´¾à´£àµ ഇതിനരàµâ€â€Œà´¤àµà´¥à´‚. നിങàµà´™àµ¾ ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿à´¯àµà´®à´¾à´¯à´¿à´Ÿàµà´Ÿà´¾à´•à´¾à´‚ ആശയവിനിമയം നടതàµà´¤àµà´¨àµà´¨à´¤àµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">à´…à´¨àµà´®à´¤à´¿ ചോദികàµà´•àµà´•</translation>
<translation id="2713444072780614174">വെളàµà´³</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">കണകàµà´·à´¨à´¾à´¯à´¿ കോൺഫിഗർ ചെയàµâ€Œà´¤ à´à´¤àµŠà´°àµ à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿à´•à´³àµà´‚ à´•àµà´°à´®àµ€à´•à´°à´£à´™àµà´™àµ¾ പേജിൽ നിനàµà´¨àµ നിങàµà´™àµ¾à´•àµà´•àµ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´°à´¹à´¿à´¤à´®à´¾à´•àµà´•à´¾à´¨à´¾à´•àµà´‚.</translation>
<translation id="2955913368246107853">ഫൈനàµâ€à´¡àµ ബാരàµâ€ à´…à´Ÿà´¯àµà´•àµà´•àµà´•</translation>
<translation id="2958431318199492670">നെറàµà´±àµâ€Œà´µàµ¼à´•àµà´•àµ കോൺഫിഗറേഷൻ ONC à´¸àµà´±àµà´±à´¾àµ»à´¡àµ‡àµ¼à´¡à´¿à´¨àµ à´…à´¨àµà´¸àµƒà´¤à´®à´¾à´¯à´¿ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¿à´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´². കോൺഫിഗറേഷൻ ഭാഗങàµà´™àµ¾ ഇമàµà´ªàµ‹àµ¼à´Ÿàµà´Ÿàµà´šàµ†à´¯àµâ€Œà´¤àµ‡à´•àµà´•à´¿à´²àµà´².</translation>
+<translation id="29611076221683977"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ† നിലവിലàµà´³àµà´³ à´…à´•àµà´°à´®à´•à´¾à´°à´¿à´•àµ¾ നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ ഇലàµà´²à´¾à´¤à´¾à´•àµà´•à´¾à´¨àµ‹ മോഷàµâ€Œà´Ÿà´¿à´•àµà´•à´¾à´¨àµ‹ ഇടയàµà´³àµà´³ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, ഫോടàµà´Ÿàµ‹à´•àµ¾, പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾, സനàµà´¦àµ‡à´¶à´™àµà´™àµ¾, à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡàµà´•àµ¾ à´®àµà´¤à´²à´¾à´¯à´µ) അപകടകരമായ à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•àµ¾ Mac-ൽ ഇൻസàµâ€Œà´±àµà´±à´¾à´³àµà´šàµ†à´¯àµà´¯à´¾àµ» à´¶àµà´°à´®à´¿à´šàµà´šàµ‡à´•àµà´•à´¾à´‚.</translation>
<translation id="2969319727213777354">ഒരൠസàµà´°à´•àµà´·à´¿à´¤ കണകàµà´·àµ» à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ, നിങàµà´™à´³àµà´Ÿàµ† à´•àµà´²àµ‹à´•àµà´•àµ ശരിയായി സജàµà´œàµ€à´•à´°à´¿à´•àµà´•àµ‡à´£àµà´Ÿà´¤àµà´£àµà´Ÿàµ. വെബàµâ€Œà´¸àµˆà´±àµà´±àµà´•àµ¾ à´¸àµà´µà´¯à´‚ തിരിചàµà´šà´±à´¿à´¯àµà´¨àµà´¨à´¤à´¿à´¨àµ ഉപയോഗികàµà´•àµà´¨àµà´¨ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµà´•àµ¾, നിർദàµà´¦à´¿à´·àµâ€Œà´Ÿ സമയ പരിധിയിൽ മാതàµà´°à´‚ സാധàµà´¤à´¯àµà´³àµà´³à´¤à´¿à´¨à´¾à´²à´¾à´£à´¿à´¤àµ. നിങàµà´™à´³àµà´Ÿàµ† ഉപകരണതàµà´¤à´¿à´¨àµà´±àµ† à´•àµà´²àµ‹à´•àµà´•àµ തെറàµà´±à´¾à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¾àµ½, Google Chrome-നൠഈ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµà´•àµ¾ പരിശോധിചàµà´šàµà´±à´ªàµà´ªà´¿à´•àµà´•à´¾à´¨à´¾à´µà´¿à´²àµà´².</translation>
<translation id="2972581237482394796">&amp;വീണàµà´Ÿàµà´‚ ചെയàµà´¯àµà´•</translation>
<translation id="2985306909656435243">à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´•àµà´·à´®à´®à´¾à´•àµà´•à´¿à´¯àµ†à´™àµà´•à´¿àµ½, വേഗതàµà´¤à´¿àµ½ ഫോം പൂരിപàµà´ªà´¿à´•àµà´•à´¾àµ» Chromium à´ˆ ഉപകരണതàµà´¤à´¿àµ½ നിങàµà´™à´³àµà´Ÿàµ† കാർഡിനàµà´±àµ† ഒരൠപകർപàµà´ªàµ സൂകàµà´·à´¿à´•àµà´•àµà´‚.</translation>
@@ -258,7 +267,6 @@
<translation id="3586931643579894722">വിശദാംശങàµà´™àµ¾ മറയàµâ€Œà´•àµà´•àµà´•â€â€Œ</translation>
<translation id="3587482841069643663">à´Žà´²àµà´²à´¾à´‚</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">സെരàµâ€â€Œà´µà´°àµâ€â€Œ TLS à´ªàµà´¨à´°à´¾à´²àµ‹à´šà´¨ വിപàµà´²àµ€à´•à´°à´£à´¤àµà´¤àµ† പിനàµà´¤àµà´£à´¯àµâ€Œà´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´².</translation>
<translation id="36224234498066874">à´¬àµà´°àµŒà´¸à´¿à´‚ഗൠഡാറàµà´± മായàµâ€Œà´•àµà´•àµà´•...</translation>
<translation id="362276910939193118">à´®àµà´´àµà´µà´¨àµâ€ à´šà´°à´¿à´¤àµà´°à´µàµà´‚ കാണികàµà´•àµà´•</translation>
<translation id="3623476034248543066">മൂലàµà´¯à´‚ കാണികàµà´•àµà´•</translation>
@@ -270,6 +278,7 @@
<translation id="3655670868607891010">നിങàµà´™àµ¾ ഇതൠപതിവായി കാണàµà´¨àµà´¨àµà´£àµà´Ÿàµ†à´™àµà´•à´¿àµ½, à´ˆ <ph name="HELP_LINK" /> പരീകàµà´·à´¿à´•àµà´•àµ‚.</translation>
<translation id="3658742229777143148">à´ªàµà´¨à´°à´µà´²àµ‹à´•à´¨à´‚</translation>
<translation id="3678029195006412963">à´…à´­àµà´¯àµ¼à´¤àµà´¥à´¨ സൈൻ ചെയàµà´¯à´¾à´¨à´¾à´¯à´¿à´²àµà´²</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>
@@ -279,10 +288,10 @@
<translation id="3712624925041724820">ലൈസൻസàµà´•àµ¾ കാലഹരണപàµà´ªàµ†à´Ÿàµà´Ÿàµ</translation>
<translation id="3714780639079136834">മൊബൈൽ ഡാറàµà´± à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ Wi-Fi ഓണാകàµà´•àµà´¨àµà´¨àµ</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿à´¯àµà´‚ ഫയർവാളàµà´‚ DNS കോൺഫിഗറേഷനàµà´‚ പരിശോധികàµà´•àµà´¨àµà´¨àµ<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">നിങàµà´™à´³àµà´Ÿàµ† à´¸àµà´°à´•àµà´·à´¯àµ† ബാധിചàµà´šàµ‡à´•àµà´•à´¾à´µàµà´¨àµà´¨ അപകട സാധàµà´¯à´¤à´•à´³àµ†à´•àµà´•àµà´±à´¿à´šàµà´šàµ മനസàµà´¸à´¿à´²à´¾à´•àµà´•àµà´•à´¯à´¾à´£àµ†à´™àµà´•à´¿àµ½à´ªàµà´ªàµ‹à´²àµà´‚, à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•àµ¾ നീകàµà´•à´‚ചെയàµà´¯àµà´¨àµà´¨à´¤à´¿à´¨àµ à´®àµà´®àµà´ªàµ <ph name="BEGIN_LINK" />à´ˆ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´²àµà´²à´¾à´¤àµà´¤ സൈറàµà´±àµ<ph name="END_LINK" /> നിങàµà´™àµ¾à´•àµà´•àµ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•à´¾à´‚ (à´¶àµà´ªà´¾àµ¼à´¶à´šàµ†à´¯àµà´¯àµà´¨àµà´¨à´¿à´²àµà´²).</translation>
<translation id="3739623965217189342">നിങàµà´™àµ¾ പകർതàµà´¤à´¿à´¯ ലിങàµà´•àµ</translation>
<translation id="375403751935624634">ഒരൠസെരàµâ€à´µà´°àµâ€ പിശകൠകാരണം വിവരàµâ€â€Œà´¤àµà´¤à´¨à´‚ പരാജയപàµà´ªàµ†à´Ÿàµà´Ÿàµ.</translation>
<translation id="3759461132968374835">നിങàµà´™à´³àµâ€à´•àµà´•àµ സമീപകാലതàµà´¤àµ റിപàµà´ªàµ‹à´°àµâ€à´Ÿàµà´Ÿàµà´šàµ†à´¯àµà´¤ à´•àµà´°à´¾à´·àµà´•à´³àµŠà´¨àµà´¨àµà´®à´¿à´²àµà´². à´•àµà´°à´¾à´·àµ റിപàµà´ªàµ‹à´°àµâ€à´Ÿàµà´Ÿàµà´šàµ†à´¯àµà´¯àµà´¨àµà´¨ സമയതàµà´¤àµ സംഭവിചàµà´š à´•àµà´°à´¾à´·àµà´•à´³àµ† à´…à´ªàµà´°à´¾à´ªàµà´¤à´®à´¾à´•àµà´•à´¿, ഇവിടെ ദൃശàµà´¯à´®à´¾à´•à´¿à´²àµà´².</translation>
-<translation id="3788090790273268753">à´ˆ സൈറàµà´±à´¿à´¨à´¾à´¯àµà´³àµà´³ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ 2016-ൽ കാലഹരണപàµà´ªàµ†à´Ÿàµà´‚ à´’à´ªàµà´ªà´‚ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ ചെയിനിൽ SHA-1 ഉപയോഗിചàµà´šàµ സൈൻ ചെയàµâ€Œà´¤ ഒരൠസർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ à´…à´Ÿà´™àµà´™à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´•à´¯àµà´‚ ചെയàµà´¯àµà´¨àµà´¨àµ.</translation>
<translation id="382518646247711829">നിങàµà´™àµ¾ ഒരൠപàµà´°àµ‹à´•àµâ€Œà´¸à´¿ സെർവർ ഉപയോഗികàµà´•àµà´¨àµà´¨àµ†à´™àµà´•à´¿àµ½...</translation>
<translation id="3828924085048779000">ശൂനàµà´¯ പാസàµà´«àµà´°àµ†à´¯àµà´¸àµ à´…à´¨àµà´µà´¦à´¨àµ€à´¯à´®à´²àµà´².</translation>
<translation id="3845539888601087042">നിങàµà´™àµ¾ സൈൻ ഇൻ ചെയàµâ€Œà´¤à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨ ഉപകരണങàµà´™à´³à´¿àµ½ നിനàµà´¨àµà´³àµà´³ à´šà´°à´¿à´¤àµà´°à´‚ കാണികàµà´•àµà´¨àµà´¨àµ. <ph name="BEGIN_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LINK" />.</translation>
@@ -304,6 +313,7 @@
<translation id="4058922952496707368">കീ "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">ഒരൠസാധാരണ SSL à´ªàµà´°àµ‹à´Ÿàµà´Ÿàµ‹à´•àµà´•àµ‹àµ¾ പതിപàµà´ªà´¿à´¨àµ†à´¯àµ‹ സൈഫർ à´¸àµà´¯àµ‚à´Ÿàµà´Ÿà´¿à´¨àµ†à´¯àµ‹ à´•àµà´²à´¯à´¨àµà´±àµà´‚ സെർവറàµà´‚ പിനàµà´¤àµà´£à´¯àµâ€Œà´•àµà´•à´¿à´²àµà´².</translation>
<translation id="4079302484614802869">à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ കോൺഫിഗറേഷൻ .pac à´¸àµâ€Œà´•àµà´°à´¿à´ªàµà´±àµà´±àµ URL ഉപയോഗികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´¯à´¿ സജàµà´œàµ€à´•à´°à´¿à´šàµà´šà´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ, à´¸àµà´¥à´¿à´°à´®à´¾à´¯ à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ സെർവറàµà´•àµ¾ ഉപയോഗികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´¯à´²àµà´².</translation>
+<translation id="4098354747657067197">വഞàµà´šà´¨à´¾à´ªà´°à´®à´¾à´¯ സൈറàµà´±àµ ഉണàµà´Ÿàµ</translation>
<translation id="4103249731201008433">ഉപകരണ സീരിയൽ നമàµà´ªàµ¼ അസാധàµà´µà´¾à´£àµ</translation>
<translation id="4103763322291513355">à´¬àµà´²à´¾à´•àµà´•àµâ€Œà´²à´¿à´¸àµà´±àµà´±à´¿àµ½à´ªàµà´ªàµ†à´Ÿàµà´Ÿ URL-à´•à´³àµà´Ÿàµ† ലിസàµà´±àµà´±àµà´‚ നിങàµà´™à´³àµà´Ÿàµ† സിസàµà´±àµà´±à´‚ à´…à´¡àµâ€Œà´®à´¿à´¨à´¿à´¸àµâ€Œà´Ÿàµà´°àµ‡à´±àµà´±àµ¼ നടപàµà´ªà´¿à´²à´¾à´•àµà´•à´¿à´¯ മറàµà´±àµ നയങàµà´™à´³àµà´‚ കാണàµà´¨àµà´¨à´¤à´¿à´¨àµ &lt;strong&gt;chrome://policy&lt;/strong&gt; സനàµà´¦àµ¼à´¶à´¿à´•àµà´•àµà´•.</translation>
<translation id="4110615724604346410">à´ˆ സെർവറിനൠഇതൠ<ph name="DOMAIN" /> ഡൊമെയàµâ€Œà´¨à´¾à´£àµ†à´¨àµà´¨àµ തെളിയികàµà´•à´¾à´¨à´¾à´¯à´¿à´²àµà´²; അതിനàµà´±àµ† à´¸àµà´°à´•àµà´·à´¾ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¿àµ½ പിശകàµà´•à´³àµà´£àµà´Ÿàµ. തെറàµà´±à´¾à´¯ കോൺഫിഗറേഷൻ കാരണമോ ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿ നിങàµà´™à´³àµà´Ÿàµ† കണകàµà´·à´¨àµ† തടസàµà´¸à´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´²àµ‹ ആയിരികàµà´•à´¾à´‚ ഇതൠസംഭവിചàµà´šà´¤àµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -321,15 +331,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{à´’à´¨àµà´¨àµà´®à´¿à´²àµà´²}=1{ഒരൠആപàµà´ªàµ ($1)}=2{2 ആപàµâ€Œà´¸àµ ($1, $2)}other{# ആപàµâ€Œà´¸àµ ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">à´•àµà´°à´¾à´·àµà´•à´³àµâ€</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />നെറàµà´±àµâ€Œà´µàµ¼à´•àµà´•àµ ഡയഗണോസàµâ€Œà´±àµà´±à´¿à´•àµâ€Œà´¸àµ റൺ ചെയàµâ€Œà´¤àµà´¨àµ‹à´•àµà´•àµ‚<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">à´ˆ സൈറàµà´±à´¿à´²àµà´³àµà´³ നിങàµà´™à´³àµà´Ÿàµ† കണകàµà´·àµ» പൂർണàµà´£à´®à´¾à´¯àµà´‚ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´²àµà´²</translation>
<translation id="4250680216510889253">ഇലàµà´²</translation>
<translation id="425582637250725228">നിങàµà´™àµ¾ വരàµà´¤àµà´¤à´¿à´¯ മാറàµà´±à´™àµà´™àµ¾ സംരകàµà´·à´¿à´•àµà´•à´ªàµà´ªàµ†à´Ÿàµà´Ÿà´¿à´°à´¿à´•àµà´•à´¾à´¨à´¿à´Ÿà´¯à´¿à´²àµà´².</translation>
<translation id="4258748452823770588">മോശം സിഗàµâ€Œà´¨àµ‡à´šàµà´šàµ¼</translation>
<translation id="4269787794583293679">(ഉപയോകàµà´¤àµƒà´¨à´¾à´®à´®à´¿à´²àµà´²)</translation>
+<translation id="4280429058323657511">, <ph name="EXPIRATION_DATE_ABBR" />-നൠകാലാവധി തീരàµà´¨àµà´¨àµ</translation>
+<translation id="4295944351946828969"><ph name="SITE" /> à´Žà´¨àµà´¨ സൈറàµà´±à´¿àµ½ Google à´¸àµà´°à´•àµà´·à´¿à´¤ à´¬àµà´°àµ—സിംഗൠഈയിടെ <ph name="BEGIN_LINK" />ദോഷകരമായ à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•àµ¾ à´•à´£àµà´Ÿàµ†à´¤àµà´¤à´¿<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">പാരനàµà´±àµ നിർദàµà´¦àµ‡à´¶à´™àµà´™àµ¾</translation>
<translation id="4304224509867189079">ലോഗൠഇനàµâ€ ചെയàµà´¯àµà´•</translation>
<translation id="432290197980158659">സെർവർ നൽകിയ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ à´…à´¨àµà´¤àµ¼à´¨à´¿àµ¼à´®àµà´®à´¿à´¤ ആവശàµà´¯à´•à´¤à´•à´³àµà´®à´¾à´¯à´¿ യോജികàµà´•àµà´¨àµà´¨à´¿à´²àµà´². നിങàµà´™à´³àµ† പരിരകàµà´·à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´¯à´¿ ഉയർനàµà´¨ à´¸àµà´°à´•àµà´·à´¯àµà´³àµà´³ à´šà´¿à´² വെബàµà´¸àµˆà´±àµà´±àµà´•àµ¾à´•àµà´•à´¾à´¯à´¾à´£àµ à´ˆ ആവശàµà´¯à´•à´¤à´•à´³àµ† ഉൾപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨à´¤àµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">ലേഖനം à´•à´£àµà´Ÿàµ†à´¤àµà´¤àµà´¨àµà´¨à´¤àµ പരാജയപàµà´ªàµ†à´Ÿàµà´Ÿàµ</translation>
+<translation id="4326324639298822553">കാലാവധി തീരàµà´¨àµà´¨ തീയതി പരിശോധിചàµà´šàµ വീണàµà´Ÿàµà´‚ à´¶àµà´°à´®à´¿à´šàµà´šàµà´¨àµ‹à´•àµà´•àµ‚</translation>
<translation id="4331708818696583467">à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´²àµà´²</translation>
+<translation id="4356973930735388585">à´ˆ സൈറàµà´±à´¿à´²àµ† ആകàµà´°à´®à´£à´•à´¾à´°à´¿à´•àµ¾ നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ മോഷàµâ€Œà´Ÿà´¿à´•àµà´•à´¾à´¨àµ‹ ഇലàµà´²à´¾à´¤à´¾à´•àµà´•à´¾à´¨àµ‹ ഇടയàµà´³àµà´³ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, ഫോടàµà´Ÿàµ‹à´•àµ¾, പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾, സനàµà´¦àµ‡à´¶à´™àµà´™àµ¾, à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡàµà´•àµ¾ à´Žà´¨àµà´¨à´¿à´µ) അപകടകരമായ à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•àµ¾ à´•à´®àµà´ªàµà´¯àµ‚à´Ÿàµà´Ÿà´±à´¿àµ½ ഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµà´¯à´¾àµ» à´¶àµà´°à´®à´¿à´šàµà´šàµ‡à´•àµà´•à´¾à´‚.</translation>
<translation id="4372948949327679948">à´ªàµà´°à´¤àµ€à´•àµà´·à´¿à´šàµà´š <ph name="VALUE_TYPE" /> മൂലàµà´¯à´‚.</translation>
<translation id="4381091992796011497">ഉപയോകàµà´¤àµƒ നാമം:</translation>
<translation id="4394049700291259645">à´…à´ªàµà´°à´¾à´ªàµâ€Œà´¤à´®à´¾à´•àµà´•àµà´•</translation>
@@ -347,6 +362,7 @@
<translation id="4589078953350245614">നിങàµà´™àµ¾ <ph name="DOMAIN" /> ഡൊമെയàµâ€Œà´¨à´¿àµ½ à´Žà´¤àµà´¤à´¾àµ» à´¶àµà´°à´®à´¿à´šàµà´šàµ†à´™àµà´•à´¿à´²àµà´‚ സെർവർ അസാധàµà´µà´¾à´¯ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¾à´£àµ നൽകിയതàµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459"><ph name="DOMAIN" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ‡à´•àµà´•àµà´³àµà´³ നിങàµà´™à´³àµà´Ÿàµ† കണകàµà´·à´¨àµ† ആധàµà´¨à´¿à´• സൈഫർ à´¸àµà´¯àµ‚à´Ÿàµà´Ÿàµ ഉപയോഗിചàµà´šàµ എൻകàµà´°à´¿à´ªàµà´±àµà´±àµà´šàµ†à´¯àµâ€Œà´¤à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ.</translation>
<translation id="4594403342090139922">&amp;ഇലàµà´²à´¾à´¤à´¾à´•àµà´•àµà´¨àµà´¨à´¤àµ പഴയപടിയാകàµà´•àµà´•</translation>
+<translation id="4619615317237390068">മറàµà´±àµ ഉപകരണങàµà´™à´³à´¿àµ½ നിനàµà´¨àµà´³àµà´³ ടാബàµà´•àµ¾</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">നിങàµà´™àµ¾ വിപàµà´²àµ€à´•à´°à´£ പേജാണൠകാണàµà´¨àµà´¨à´¤àµ.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -355,10 +371,13 @@
<translation id="4726672564094551039">നയങàµà´™àµ¾ വീണàµà´Ÿàµà´‚ ലോഡàµà´šàµ†à´¯àµà´¯àµà´•</translation>
<translation id="4728558894243024398">à´ªàµà´²à´¾à´±àµà´±àµà´«àµ‹à´‚</translation>
<translation id="4744603770635761495">നിരàµâ€à´µàµà´µà´¹à´¿à´•àµà´•à´¾à´µàµà´¨àµà´¨ പാത</translation>
+<translation id="4750917950439032686">നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, പാസàµâ€Œà´µàµ‡à´¡àµà´•à´³àµ‹ à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡൠനമàµà´ªà´±àµà´•à´³àµ‹) à´ˆ സൈറàµà´±à´¿à´²àµ‡à´•àµà´•àµ അയചàµà´šàµ à´•à´´à´¿à´žàµà´žà´¾àµ½ പിനàµà´¨àµ†à´¯à´¤àµ à´¸àµà´µà´•à´¾à´°àµà´¯à´®à´¾à´¯à´¿à´°à´¿à´•àµà´•àµà´‚.</translation>
<translation id="4756388243121344051">&amp;à´šà´°à´¿à´¤àµà´°à´‚</translation>
+<translation id="4759118997339041434">പേയàµâ€Œà´®àµ†à´¨àµà´±àµ à´¸àµà´µà´¯à´®àµ‡à´µ പൂരിപàµà´ªà´¿à´•àµà´•àµ½ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´°à´¹à´¿à´¤à´®à´¾à´•àµà´•à´¿</translation>
<translation id="4764776831041365478"><ph name="URL" /> ലെ വെബàµâ€Œà´ªàµ‡à´œàµ താലàµâ€â€Œà´•àµà´•à´¾à´²à´¿à´•à´®à´¾à´¯à´¿ à´ªàµà´°à´µà´°àµâ€â€Œà´¤àµà´¤à´¨à´°à´¹à´¿à´¤à´®à´¾à´¯à´¿à´°à´¿à´•àµà´•à´¾à´‚, à´…à´²àµà´²àµ†à´™àµà´•à´¿à´²àµâ€â€Œ ഒരൠപàµà´¤à´¿à´¯ വെബൠവിലാസതàµà´¤à´¿à´²àµ‡à´•àµà´•àµ ശാശàµà´µà´¤à´®à´¾à´¯à´¿ നീകàµà´•à´‚ചെയàµà´¯à´ªàµà´ªàµ†à´Ÿàµà´Ÿà´¿à´°à´¿à´•àµà´•à´¾à´‚.</translation>
<translation id="4771973620359291008">ഒരൠഅജàµà´žà´¾à´¤ പിശകൠസംഭവിചàµà´šàµ.</translation>
<translation id="4800132727771399293">നിങàµà´™à´³àµà´Ÿàµ† കാലഹരണ തീയതിയàµà´‚ CVC à´¯àµà´‚ പരിശോധിചàµà´šàµ വീണàµà´Ÿàµà´‚ à´¶àµà´°à´®à´¿à´•àµà´•àµà´•</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">നെറàµà´±àµâ€Œà´µà´°àµâ€à´•àµà´•àµ പിശകàµ</translation>
<translation id="4816492930507672669">പേജിനൠയàµà´•àµà´¤à´®à´¾à´•àµà´•àµà´•</translation>
<translation id="4850886885716139402">കാണàµà´•</translation>
@@ -367,6 +386,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ഒരൠവെബàµâ€Œà´ªàµ‡à´œàµ കൂടിയàµà´£àµà´Ÿàµ}other{# വെബàµâ€Œà´ªàµ‡à´œàµà´•àµ¾ കൂടിയàµà´£àµà´Ÿàµ}}</translation>
<translation id="4923417429809017348">à´ˆ പേജിനെ അറിയപàµà´ªàµ†à´Ÿà´¾à´¤àµà´¤ ഒരൠഭാഷയിലàµâ€â€Œ നിനàµà´¨àµà´‚ <ph name="LANGUAGE_LANGUAGE" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ‡à´•àµà´•àµ വിവരàµâ€â€Œà´¤àµà´¤à´¨à´‚ ചെയàµà´¤àµ</translation>
+<translation id="4923459931733593730">പേയàµâ€Œà´®àµ†à´¨àµà´±àµ രീതി</translation>
<translation id="4926049483395192435">à´µàµà´¯à´•àµà´¤à´®à´¾à´•àµà´•àµ‡à´£àµà´Ÿà´¤à´¾à´£àµ.</translation>
<translation id="495170559598752135">à´ªàµà´°à´µà´°àµâ€à´¤àµà´¤à´¨à´™àµà´™à´³àµâ€</translation>
<translation id="4958444002117714549">ലിസàµà´±àµà´±àµ വിപàµà´²àµ€à´•à´°à´¿à´•àµà´•àµà´•</translation>
@@ -394,7 +414,6 @@
<translation id="5181140330217080051">ഡൗൺലോഡàµà´šàµ†à´¯àµà´¯àµà´¨àµà´¨àµ</translation>
<translation id="5190835502935405962">à´¬àµà´•àµà´•àµâ€Œà´®à´¾à´°àµâ€â€Œà´•àµà´•àµà´•à´³àµâ€â€Œ ബാരàµâ€â€Œ</translation>
<translation id="5199729219167945352">പരീകàµà´·à´£à´™àµà´™à´³àµâ€</translation>
-<translation id="5199841536747119669">നിങàµà´™à´³àµà´Ÿàµ† നിർദàµà´¦àµ‡à´¶à´™àµà´™àµ¾ ഇവിടെ ദൃശàµà´¯à´®à´¾à´•àµà´‚</translation>
<translation id="5251803541071282808">à´•àµà´²àµ—à´¡àµ</translation>
<translation id="5277279256032773186">ജോലിസàµà´¥à´²à´¤àµà´¤àµà´³àµà´³ Chrome ഉപയോഗികàµà´•àµà´•à´¯à´¾à´£àµ‹? ബിസിനസൠസàµà´¥à´¾à´ªà´¨à´™àµà´™àµ¾à´•àµà´•àµ അവരàµà´Ÿàµ† ജീവനകàµà´•à´¾àµ¼à´•àµà´•àµ വേണàµà´Ÿà´¿ Chrome à´•àµà´°à´®àµ€à´•à´°à´£à´‚ മാനേജàµà´šàµ†à´¯àµà´¯à´¾à´¨à´¾à´•àµà´‚. കൂടàµà´¤à´²à´±à´¿à´¯àµà´•</translation>
<translation id="5299298092464848405">നയം പാഴàµâ€Œà´¸àµà´šàµ†à´¯àµà´¯àµà´¨àµà´¨à´¤à´¿àµ½ പിശകàµ</translation>
@@ -402,8 +421,10 @@
<translation id="5308689395849655368">à´•àµà´°à´¾à´·àµ റിപàµà´ªàµ‹à´°àµâ€à´Ÿàµà´Ÿàµà´šàµ†à´¯àµà´¯à´²àµâ€ à´…à´ªàµà´°à´¾à´ªàµà´¤à´®à´¾à´•àµà´•à´¿.</translation>
<translation id="5317780077021120954">സംരകàµà´·à´¿à´•àµà´•àµà´•</translation>
<translation id="5327248766486351172">പേരàµ</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ† ആകàµà´°à´®à´£à´•à´¾à´°à´¿à´•àµ¾ സോഫàµà´±àµà´±àµâ€Œà´µàµ†à´¯àµ¼ ഇൻസàµà´±àµà´±à´¾à´³àµà´šàµ†à´¯àµà´¯àµà´¨àµà´¨à´¤àµ‹ à´¸àµà´µà´•à´¾à´°àµà´¯ വിവരങàµà´™àµ¾ വെളിപàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´¨àµà´¨à´¤àµ‹ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾, ഫോൺ നമàµà´ªà´±àµà´•àµ¾, à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡàµà´•àµ¾ à´¤àµà´Ÿà´™àµà´™à´¿à´¯ വിവരങàµà´™àµ¾) പോലàµà´³àµà´³ അപകടകരമായ കാരàµà´¯à´™àµà´™àµ¾ ചെയàµà´¯àµà´¨àµà´¨à´¤à´¿à´¨àµ നിങàµà´™à´³àµ† കബളിപàµà´ªà´¿à´•àµà´•à´¾à´‚.</translation>
<translation id="5359637492792381994">à´ˆ സെർവറിനൠഇതൠ<ph name="DOMAIN" /> ഡൊമെയàµâ€Œà´¨à´¾à´£àµ†à´¨àµà´¨àµ തെളിയികàµà´•à´¾à´¨à´¾à´¯à´¿à´²àµà´²; അതിനàµà´±àµ† à´¸àµà´°à´•àµà´·à´¾ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ ഇപàµà´ªàµ‹àµ¾ സാധàµà´µà´¾à´¯à´¤à´²àµà´². തെറàµà´±à´¾à´¯ കോൺഫിഗറേഷൻ കാരണമോ ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿ നിങàµà´™à´³àµà´Ÿàµ† കണകàµà´·à´¨àµ† തടസàµà´¸à´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´²àµ‹ ആയിരികàµà´•à´¾à´‚ ഇതൠസംഭവിചàµà´šà´¤àµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">നയ à´•àµà´°à´®àµ€à´•à´°à´£à´™àµà´™àµ¾ സംഭരികàµà´•àµà´¨àµà´¨à´¤à´¿àµ½ പരാജയപàµà´ªàµ†à´Ÿàµà´Ÿàµ</translation>
+<translation id="5386426401304769735">à´ˆ സൈറàµà´±à´¿à´¨àµà´±àµ† സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ ചെയിനിൽ SHA-1 ഉപയോഗിചàµà´šàµ സൈൻ ചെയàµâ€Œà´¤ ഒരൠസർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ à´…à´Ÿà´™àµà´™à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ.</translation>
<translation id="5421136146218899937">à´¬àµà´°àµ—സിംഗൠഡാറàµà´± മായàµâ€Œà´•àµà´•àµà´•...</translation>
<translation id="5430298929874300616">à´¬àµà´•àµà´•àµâ€Œà´®à´¾àµ¼à´•àµà´•àµ നീകàµà´•à´‚ചെയàµà´¯àµà´•</translation>
<translation id="5431657950005405462">നിങàµà´™à´³àµà´Ÿàµ† ഫയൽ à´•à´£àµà´Ÿàµ†à´¤àµà´¤à´¿à´¯à´¿à´²àµà´²</translation>
@@ -424,17 +445,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> സൈറàµà´±à´¿à´²àµ† ഒരൠഎംബഡൠചെയàµâ€Œà´¤ പേജൠപറയàµà´¨àµà´¨à´¤àµ:</translation>
<translation id="5556459405103347317">വീണàµà´Ÿàµà´‚ ലോഡàµà´šàµ†à´¯àµà´¯àµà´•</translation>
<translation id="5565735124758917034">സജീവമാണàµ</translation>
+<translation id="5572851009514199876">ആരംഭിചàµà´šàµ Chrome-ൽ സൈൻ ഇൻ ചെയàµà´¯àµà´¨àµà´¨à´¤à´¿à´²àµ‚ടെ, നിങàµà´™àµ¾à´•àµà´•àµ à´ˆ സൈറàµà´±àµ ആകàµâ€Œà´¸à´¸àµ ചെയàµà´¯à´¾àµ» à´…à´¨àµà´µà´¾à´¦à´®àµà´£àµà´Ÿàµ‹à´¯àµ†à´¨àµà´¨àµ Chrome-നൠപരിശോധികàµà´•à´¾à´¨à´¾à´µàµà´‚.</translation>
+<translation id="5580958916614886209">കാലാവധി തീരàµà´¨àµà´¨ മാസം പരിശോധിചàµà´šàµ വീണàµà´Ÿàµà´‚ à´¶àµà´°à´®à´¿à´šàµà´šàµà´¨àµ‹à´•àµà´•àµ‚</translation>
<translation id="560412284261940334">മാനേജàµà´®àµ†à´¨àµà´±àµ പിനàµà´¤àµà´£à´¯àµâ€Œà´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´²</translation>
<translation id="5610142619324316209">കണകàµà´·àµ» പരിശോധികàµà´•àµà´¨àµà´¨àµ</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" />, നിരവധി തവണ നിങàµà´™à´³àµ† റീഡയറകàµâ€Œà´Ÿàµà´šàµ†à´¯àµâ€Œà´¤àµ.</translation>
<translation id="5622887735448669177">നിങàµà´™àµ¾à´•àµà´•àµ à´ˆ സൈറàµà´±àµ വിടണോ?</translation>
<translation id="5629630648637658800">നയ à´•àµà´°à´®àµ€à´•à´°à´£à´™àµà´™àµ¾ ലോഡàµà´šàµ†à´¯àµà´¯àµà´¨àµà´¨à´¤à´¿àµ½ പരാജയപàµà´ªàµ†à´Ÿàµà´Ÿàµ</translation>
<translation id="5631439013527180824">ഉപകരണ മാനേജàµà´®àµ†à´¨àµà´±àµ ടോകàµà´•àµº അസാധàµà´µà´¾à´£àµ</translation>
+<translation id="5669703222995421982">à´µàµà´¯à´•àµà´¤à´¿à´ªà´°à´®à´¾à´•àµà´•à´¿à´¯ ഉളàµà´³à´Ÿà´•àµà´•à´‚ à´¸àµà´µà´¨àµà´¤à´®à´¾à´•àµà´•àµà´•</translation>
+<translation id="5675650730144413517">à´ˆ പേജൠപàµà´°à´µàµ¼à´¤àµà´¤à´¿à´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´²</translation>
<translation id="5677928146339483299">തടഞàµà´žàµ</translation>
<translation id="5694783966845939798">നിങàµà´™à´³àµâ€â€Œ <ph name="DOMAIN" /> ഡൊമെയàµâ€Œàµ» ആകàµâ€Œà´¸à´¸àµ ചെയàµà´¯à´¾àµ» à´¶àµà´°à´®à´¿à´šàµà´šàµ, à´Žà´¨àµà´¨à´¾àµ½ à´¦àµàµ¼à´¬à´²à´®à´¾à´¯ സിഗàµâ€Œà´¨àµ‡à´šàµà´šà´°àµâ€â€Œ à´…à´²àµâ€â€Œà´—രിതം (SHA-1 പോലàµà´³àµà´³à´¤àµ) ഉപയോഗിചàµà´šàµ സൈൻ ചെയàµâ€Œà´¤ സരàµâ€â€Œà´Ÿàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¾à´£àµ സെരàµâ€â€Œà´µà´°àµâ€â€Œ നലàµâ€â€Œà´•à´¿à´¯à´¤àµ. ഇതിനരàµâ€â€Œà´¤àµà´¥à´‚ സെരàµâ€â€Œà´µà´°àµâ€â€Œ നലàµâ€â€Œà´•à´¿à´¯ à´¸àµà´°à´•àµà´·à´¾ à´•àµà´°àµ†à´¡à´¨àµâ€â€Œà´·àµà´¯à´²àµà´•à´³àµâ€â€Œ à´µàµà´¯à´¾à´œà´®à´¾à´•à´¾à´®àµ†à´¨àµà´¨àµà´‚ സെരàµâ€â€Œà´µà´°àµâ€â€Œ നിങàµà´™à´³àµâ€â€Œ à´ªàµà´°à´¤àµ€à´•àµà´·à´¿à´šàµà´š സെരàµâ€â€Œà´µà´±à´¾à´¯à´¿à´°à´¿à´•àµà´•à´¿à´²àµà´²àµ†à´¨àµà´¨àµà´®à´¾à´£àµ (നിങàµà´™à´³àµâ€â€Œ ആകàµà´°à´®à´£à´•à´¾à´°à´¿à´¯àµà´®à´¾à´¯à´¿à´Ÿàµà´Ÿà´¾à´•à´¾à´‚ ആശയവിനിമയം നടതàµà´¤àµà´¨àµà´¨à´¤àµ). <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">à´ˆ വെബàµà´¸àµˆà´±àµà´±à´¿à´¨àµà´±àµ† à´µàµà´¯à´•àµà´¤à´¿à´¤àµà´µà´‚ പരിശോധിചàµà´šà´¿à´Ÿàµà´Ÿà´¿à´²àµà´².</translation>
<translation id="5720705177508910913">നിലവിലെ ഉപയോകàµà´¤à´¾à´µàµ</translation>
-<translation id="572328651809341494">à´…à´Ÿàµà´¤àµà´¤à´¿à´Ÿàµ†à´¯àµà´³àµà´³ ടാബàµà´•àµ¾</translation>
<translation id="5732392974455271431">നിങàµà´™àµ¾à´•àµà´•àµ വേണàµà´Ÿà´¿ ഇതൠഅൺബàµà´²àµ‹à´•àµà´•àµà´šàµ†à´¯àµà´¯à´¾àµ» à´°à´•àµà´·à´¿à´¤à´¾à´•àµà´•àµ¾à´•àµà´•àµ à´•à´´à´¿à´¯àµà´‚</translation>
<translation id="5784606427469807560">നിങàµà´™à´³àµà´Ÿàµ† കാർഡൠസàµà´¥à´¿à´°àµ€à´•à´°à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿àµ½ à´ªàµà´°à´¶àµâ€Œà´¨à´®àµà´£àµà´Ÿà´¾à´¯à´¿. ഇനàµà´±àµ¼à´¨àµ†à´±àµà´±àµ കണകàµà´·àµ» പരിശോധിചàµà´šàµ, വീണàµà´Ÿàµà´‚ à´¶àµà´°à´®à´¿à´•àµà´•àµà´•.</translation>
<translation id="5785756445106461925">കൂടാതെ, à´ˆ പേജിൽ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´²àµà´²à´¾à´¤àµà´¤ മറàµà´±àµ ഉറവിടങàµà´™àµ¾ ഉൾപàµà´ªàµ†à´Ÿàµà´¨àµà´¨àµ. à´ˆ ഉറവിടങàµà´™àµ¾ കൈമാറàµà´¨àµà´¨à´¤à´¿à´¨à´¿à´Ÿàµ† മറàµà´±àµà´³àµà´³à´µàµ¼à´•àµà´•àµ കാണാനàµà´‚ പേജിനàµà´±àµ† രൂപം മാറàµà´±àµà´¨àµà´¨ തരതàµà´¤à´¿àµ½ ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿à´¯àµâ€Œà´•àµà´•àµ പരിഷàµâ€Œà´•àµà´•à´°à´¿à´•àµà´•à´¾à´¨àµà´®à´¾à´¯àµ‡à´•àµà´•àµà´‚.</translation>
@@ -443,6 +467,7 @@
<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="5843436854350372569">നിങàµà´™à´³àµâ€â€Œ <ph name="DOMAIN" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµâ€ à´Žà´¤àµà´¤à´¾à´¨àµâ€â€Œ à´¶àµà´°à´®à´¿à´šàµà´šàµ, പകàµà´·àµ† സെർവർ ഹാജരാകàµà´•à´¿à´¯ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¿àµ½ à´¦àµàµ¼à´¬à´²à´®à´¾à´¯ കീ ഉൾപàµà´ªàµ†à´Ÿàµà´¨àµà´¨àµ. ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿ à´¸àµà´µà´•à´¾à´°àµà´¯ കീ നശിപàµà´ªà´¿à´šàµà´šà´¿à´°à´¿à´•àµà´•à´¾à´‚, കൂടാതെ സെർവർ നിങàµà´™àµ¾ à´ªàµà´°à´¤àµ€à´•àµà´·à´¿à´šàµà´š സെർവർ ആയിരികàµà´•à´¿à´²àµà´² (നിങàµà´™àµ¾ ആശയവിനിമയം നടതàµà´¤àµà´¨àµà´¨à´¤àµ ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿à´¯àµà´®à´¾à´¯à´¿à´Ÿàµà´Ÿà´¾à´•à´¾à´‚). <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">à´ªàµà´¤à´¿à´¯ ഫോളàµâ€à´¡à´°àµâ€</translation>
<translation id="5869405914158311789">à´ˆ സൈറàµà´±àµ ലഭàµà´¯à´®à´¾à´•àµà´•à´¾à´¨à´¾à´•àµà´¨àµà´¨à´¿à´²àµà´²</translation>
@@ -452,6 +477,7 @@
<translation id="59107663811261420">à´ˆ à´µàµà´¯à´¾à´ªà´°à´¿à´¯àµâ€Œà´•àµà´•à´¾à´¯à´¿ à´ˆ തരതàµà´¤à´¿à´²àµà´³àµà´³ കാർഡൠGoogle പേയàµâ€Œà´®àµ†à´¨àµà´±àµ പിനàµà´¤àµà´£à´¯àµà´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´². മറàµà´±àµŠà´°àµ കാർഡൠതിരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•.</translation>
<translation id="59174027418879706">à´ªàµà´°à´¾à´ªàµâ€Œà´¤à´®à´¾à´•àµà´•à´¿</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="5966707198760109579">ആഴàµâ€Œà´š</translation>
<translation id="5967867314010545767">à´šà´°à´¿à´¤àµà´°à´¤àµà´¤à´¿àµ½ നിനàµà´¨àµà´‚ നീകàµà´•à´‚ചെയàµà´¯àµà´•</translation>
<translation id="5975083100439434680">സൂം ഔടàµà´Ÿàµ</translation>
@@ -470,8 +496,11 @@
<translation id="614940544461990577">പരീകàµà´·à´¿à´šàµà´šàµà´¨àµ‹à´•àµà´•àµ‚:</translation>
<translation id="6151417162996330722">സെർവർ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¿à´¨àµ ദൈർഘàµà´¯à´®àµ‡à´±à´¿à´¯ ഒരൠകാലയളവൠഉണàµà´Ÿàµ.</translation>
<translation id="6165508094623778733">കൂടàµà´¤àµ½â€ മനസിലാകàµà´•àµà´•</translation>
+<translation id="6177128806592000436">à´ˆ സൈറàµà´±à´¿à´²àµ‡à´•àµà´•àµà´³àµà´³ നിങàµà´™à´³àµà´Ÿàµ† കണകàµà´·àµ» à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´²àµà´²</translation>
<translation id="6203231073485539293">നിങàµà´™à´³àµà´Ÿàµ† ഇനàµà´±àµ¼à´¨àµ†à´±àµà´±àµ കണകàµà´·àµ» പരിശോധികàµà´•àµà´•</translation>
<translation id="6218753634732582820">Chromium-à´¤àµà´¤à´¿àµ½ നിനàµà´¨àµ വിലാസം നീകàµà´•à´‚ചെയàµà´¯à´£àµ‹?</translation>
+<translation id="6251924700383757765">à´¸àµà´µà´•à´¾à´°àµà´¯à´¤ നയം</translation>
+<translation id="625755898061068298">നിങàµà´™àµ¾ à´ˆ സൈറàµà´±à´¿à´¨àµà´±àµ† à´¸àµà´°à´•àµà´·à´¾ à´®àµà´¨àµà´¨à´±à´¿à´¯à´¿à´ªàµà´ªàµà´•àµ¾ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´°à´¹à´¿à´¤à´®à´¾à´•àµà´•à´¾àµ» തീരàµà´®à´¾à´¨à´¿à´šàµà´šàµ.</translation>
<translation id="6259156558325130047">&amp;à´ªàµà´¨à´ƒà´•àµà´°à´®àµ€à´•à´°à´¿à´•àµà´•àµà´¨àµà´¨à´¤àµ വീണàµà´Ÿàµà´‚ ചെയàµà´¯àµà´•</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> à´¬àµà´•àµà´•àµâ€Œà´®à´¾àµ¼à´•àµà´•àµà´•àµ¾</translation>
<translation id="6264485186158353794">à´¸àµà´°à´•àµà´·à´¯à´¿à´²àµ‡à´•àµà´•àµ</translation>
@@ -480,6 +509,7 @@
<translation id="6305205051461490394"><ph name="URL" /> ലഭàµà´¯à´®à´²àµà´².</translation>
<translation id="6321917430147971392">നിങàµà´™à´³àµà´Ÿàµ† DNS à´•àµà´°à´®àµ€à´•à´°à´£à´™àµà´™àµ¾ പരിശോധികàµà´•àµà´•</translation>
<translation id="6328639280570009161">നെറàµà´±àµâ€Œà´µàµ¼à´•àµà´•àµ à´ªàµà´°à´µà´šà´¨à´‚ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´°à´¹à´¿à´¤à´®à´¾à´•àµà´•à´¾àµ» à´¶àµà´°à´®à´¿à´•àµà´•àµà´•</translation>
+<translation id="6328786501058569169">à´ˆ സൈറàµà´±àµ വഞàµà´šà´¨à´¾à´ªà´°à´®à´¾à´¯à´¤à´¾à´£àµ</translation>
<translation id="6337534724793800597">പേരിനàµà´±àµ† à´•àµà´°à´®à´¤àµà´¤à´¿àµ½ നയങàµà´™àµ¾ ഫിൽടàµà´Ÿàµ¼ ചെയàµà´¯àµà´•</translation>
<translation id="6342069812937806050">ഇപàµà´ªàµ‹à´³àµâ€â€Œ</translation>
<translation id="6345221851280129312">വലàµà´ªàµà´ªà´‚ à´Žà´¤àµà´°à´¯à´¾à´£àµ†à´¨àµà´¨àµ അറിയിലàµà´²</translation>
@@ -488,6 +518,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{മറàµà´±àµŠà´°àµ നിർദàµà´¦àµ‡à´¶à´‚}other{മറàµà´±àµ # നിർദàµà´¦àµ‡à´¶à´™àµà´™àµ¾}}</translation>
<translation id="6387478394221739770">Chrome-à´¨àµà´±àµ† രസകരമായ à´ªàµà´¤à´¿à´¯ സവിശേഷതകളിൽ താൽപàµà´ªà´°àµà´¯à´®àµà´£àµà´Ÿàµ‹? chrome.com/beta-യിൽ à´žà´™àµà´™à´³àµà´Ÿàµ† ബീറàµà´± ചാനൽ പരീകàµà´·à´¿à´•àµà´•àµà´•.</translation>
<translation id="6389758589412724634">à´ˆ വെബàµâ€Œà´ªàµ‡à´œàµ à´ªàµà´°à´¦àµ¼à´¶à´¿à´ªàµà´ªà´¿à´•àµà´•à´¾àµ» à´¶àµà´°à´®à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¿à´Ÿà´¯à´¿àµ½ Chromium-à´¤àµà´¤à´¿à´¨àµà´±àµ† മെമàµà´®à´±à´¿ നിറഞàµà´žàµ.</translation>
+<translation id="6404511346730675251">à´¬àµà´•àµà´•àµâ€Œà´®à´¾àµ¼à´•àµà´•àµ à´Žà´¡à´¿à´±àµà´±àµà´šàµ†à´¯àµà´¯àµà´•</translation>
+<translation id="6410264514553301377"><ph name="CREDIT_CARD" />-à´¨àµà´±àµ† കാലാവധി തീരàµà´¨àµà´¨ തീയതിയàµà´‚ CVC-à´¯àµà´‚ നൽകàµà´•</translation>
<translation id="6414888972213066896">à´ˆ സൈറàµà´±àµ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ നിങàµà´™àµ¾ à´°à´•àµà´·à´¿à´¤à´¾à´µà´¿à´¨àµ‹à´Ÿàµ à´…à´¨àµà´®à´¤à´¿ ആവശàµà´¯à´ªàµà´ªàµ†à´Ÿàµà´Ÿàµ</translation>
<translation id="6416403317709441254">Chrome-നൠപàµà´°àµ‹à´¸à´¸àµà´¸àµà´šàµ†à´¯àµà´¯à´¾à´¨à´¾à´•à´¾à´¤àµà´¤ രൂപമാറàµà´±à´‚ വരàµà´¤àµà´¤à´¿à´¯ à´•àµà´°àµ†à´¡àµ»à´·àµà´¯à´²àµà´•àµ¾ വെബàµà´¸àµˆà´±àµà´±àµ അയയàµâ€Œà´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¾àµ½ നിങàµà´™àµ¾à´•àµà´•à´¿à´ªàµà´ªàµ‹àµ¾ <ph name="SITE" /> സൈറàµà´±àµ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•à´¾à´¨à´¾à´•à´¿à´²àµà´². നെറàµà´±àµâ€Œà´µàµ¼à´•àµà´•àµ പിശകàµà´•à´³àµà´‚ ആകàµà´°à´®à´£à´™àµà´™à´³àµà´‚ സാധാരണയായി താൽകàµà´•à´¾à´²à´¿à´• à´ªàµà´°à´¶àµâ€Œà´¨à´™àµà´™à´³à´¾à´¯à´¤à´¿à´¨à´¾àµ½ à´ˆ പേജൠമികàµà´•à´µà´¾à´±àµà´‚ പിനàµà´¨àµ€à´Ÿàµ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¿à´•àµà´•àµà´‚. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">സരàµâ€à´Ÿàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ അസാധàµà´µà´¾à´•àµà´•à´¿à´¯àµ‹ à´Žà´¨àµà´¨àµ പരിശോധികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´¯à´¿à´²àµà´².</translation>
@@ -497,10 +529,12 @@
<translation id="6458467102616083041">à´¸àµà´¥à´¿à´°à´¸àµà´¥à´¿à´¤à´¿ തിരയൽ നയ à´ªàµà´°à´•à´¾à´°à´‚ ദൃശàµà´¯à´®à´¾à´¯à´¤à´¿à´¨à´¾àµ½ അവഗണികàµà´•à´ªàµà´ªàµ†à´Ÿàµà´Ÿàµ.</translation>
<translation id="6462969404041126431">à´ˆ സെർവറിനൠഇതൠ<ph name="DOMAIN" /> ഡൊമെയàµâ€Œà´¨à´¾à´£àµ†à´¨àµà´¨àµ തെളിയികàµà´•à´¾à´¨à´¾à´¯à´¿à´²àµà´²; അതിനàµà´±àµ† à´¸àµà´°à´•àµà´·à´¾ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ റദàµà´¦à´¾à´•àµà´•à´¿à´¯à´¿à´°à´¿à´•àµà´•à´¾à´‚. തെറàµà´±à´¾à´¯ കോൺഫിഗറേഷൻ കാരണമോ ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿ നിങàµà´™à´³àµà´Ÿàµ† കണകàµà´·à´¨àµ† തടസàµà´¸à´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´²àµ‹ ആയിരികàµà´•à´¾à´‚ ഇതൠസംഭവിചàµà´šà´¤àµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">ഉപകരണ നയങàµà´™àµ¾</translation>
+<translation id="6477321094435799029">Chrome, à´ˆ പേജിൽ അസാധാരണമായ കോഡൠകണàµà´Ÿàµ†à´¤àµà´¤à´¿à´¯à´¤à´¿à´¨à´¾àµ½ നിങàµà´™à´³àµà´Ÿàµ† à´µàµà´¯à´•àµà´¤à´¿à´—à´¤ വിവരങàµà´™àµ¾ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, പാസàµâ€Œà´µàµ‡à´¡àµà´•à´³àµà´‚ ഫോൺ നമàµà´ªà´±àµà´•à´³àµà´‚ à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡàµà´•à´³àµà´‚ പോലàµà´³àµà´³à´µ) പരിരകàµà´·à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ അതിനെ à´¬àµà´²àµ‹à´•àµà´•àµà´šàµ†à´¯àµâ€Œà´¤àµ.</translation>
<translation id="6489534406876378309">à´•àµà´°à´¾à´·àµà´•àµ¾ à´…à´ªàµâ€Œà´²àµ‹à´¡àµà´šàµ†à´¯àµà´¯àµà´¨àµà´¨à´¤àµ ആരംഭികàµà´•àµà´•</translation>
<translation id="6529602333819889595">&amp;ഇലàµà´²à´¾à´¤à´¾à´•àµà´•àµà´¨àµà´¨à´¤àµ വീണàµà´Ÿàµà´‚ ചെയàµà´¯àµà´•</translation>
<translation id="6534179046333460208">ഫിസികàµà´•àµ½ വെബൠനിർദàµà´¦àµ‡à´¶à´™àµà´™àµ¾</translation>
<translation id="6550675742724504774">à´“à´ªàµà´·à´¨àµà´•àµ¾</translation>
+<translation id="6556239504065605927">കണകàµà´·àµ» à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´£àµ</translation>
<translation id="6563469144985748109">നിങàµà´™à´³àµà´Ÿàµ† മാനേജർ ഇതàµà´µà´°àµ† അംഗീകാരം നൽകിയിടàµà´Ÿà´¿à´²àµà´²</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" />-യിൽ താഴെ</translation>
<translation id="6596325263575161958">എൻകàµà´°à´¿à´ªàµâ€Œà´·àµ» à´“à´ªàµâ€Œà´·à´¨àµà´•àµ¾</translation>
@@ -509,7 +543,6 @@
<translation id="6644283850729428850">à´ˆ നയം ഒഴിവാകàµà´•à´¿.</translation>
<translation id="6652240803263749613">à´ˆ സെർവറിനൠഇതൠ<ph name="DOMAIN" /> ഡൊമെയàµâ€Œà´¨à´¾à´£àµ†à´¨àµà´¨àµ തെളിയികàµà´•à´¾à´¨à´¾à´¯à´¿à´²àµà´²; അതിനàµà´±àµ† à´¸àµà´°à´•àµà´·à´¾ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¿à´¨àµ† നിങàµà´™à´³àµà´Ÿàµ† à´•à´®àµà´ªàµà´¯àµ‚à´Ÿàµà´Ÿà´±à´¿à´¨àµà´±àµ† à´“à´ªàµà´ªà´±àµ‡à´±àµà´±à´¿à´‚ഗൠസിസàµà´±àµà´±à´‚ വിശàµà´µà´¸à´¿à´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´². തെറàµà´±à´¾à´¯ കോൺഫിഗറേഷൻ കാരണമോ ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿ നിങàµà´™à´³àµà´Ÿàµ† കണകàµà´·à´¨àµ† തടസàµà´¸à´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´²àµ‹ ആയിരികàµà´•à´¾à´‚ ഇതൠസംഭവിചàµà´šà´¤àµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">ഫോൾഡർ à´Žà´¡à´¿à´±àµà´±àµà´šàµ†à´¯àµà´¯àµà´•</translation>
-<translation id="6660210980321319655">à´¸àµà´µà´¯à´®àµ‡à´µ റിപàµà´ªàµ‹àµ¼à´Ÿàµà´Ÿàµà´šàµ†à´¯àµâ€Œà´¤à´¤àµ <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Chromium-à´¤àµà´¤à´¿àµ½ നിനàµà´¨àµ ഫോം നിർദàµà´¦àµ‡à´¶à´‚ നീകàµà´•à´‚ചെയàµà´¯à´£àµ‹?</translation>
<translation id="6685834062052613830">സൈൻ ഔടàµà´Ÿàµ ചെയàµâ€Œà´¤àµ, സജàµà´œà´®à´¾à´•àµà´•àµ½ പൂർതàµà´¤à´¿à´¯à´¾à´•àµà´•àµà´•</translation>
<translation id="6710213216561001401">à´•à´´à´¿à´žàµà´ž</translation>
@@ -521,6 +554,7 @@
<translation id="6753269504797312559">നയ മൂലàµà´¯à´‚</translation>
<translation id="6757797048963528358">നിങàµà´™à´³àµà´Ÿàµ† ഉപകരണം à´¸àµà´·àµà´ªàµâ€Œà´¤à´¿à´¯à´¿à´²à´¾à´¯à´¿.</translation>
<translation id="6778737459546443941">നിങàµà´™à´³àµà´Ÿàµ† à´°à´•àµà´·à´¿à´¤à´¾à´µàµ ഇതàµà´µà´°àµ† അംഗീകാരം നൽകിയിടàµà´Ÿà´¿à´²àµà´²</translation>
+<translation id="6810899417690483278">ഇഷàµâ€Œà´Ÿà´¾à´¨àµà´¸àµƒà´¤à´®à´¾à´•àµà´•àµ½ à´à´¡à´¿</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ† വെബàµà´ªàµ‡à´œàµ നിലവിലàµâ€ ലഭàµà´¯à´®à´²àµà´². അതൠഓവരàµâ€à´²àµ‹à´¡àµ ആയിരികàµà´•àµà´•à´¯àµ‹ à´ªàµà´¨à´°àµâ€à´¨à´¿à´°àµâ€à´®àµà´®à´¾à´£à´¤àµà´¤à´¿à´²à´¾à´¯à´¿à´°à´¿à´•àµà´•àµà´•à´¯àµ‹ ആവാം.</translation>
<translation id="6831043979455480757">വിവർതàµà´¤à´¨à´‚ ചെയàµà´¯àµà´•</translation>
@@ -541,6 +575,8 @@
<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>
+<translation id="7064851114919012435">കോണàµâ€à´Ÿà´¾à´•àµà´±àµà´±àµ വിവരം</translation>
+<translation id="7079718277001814089">à´ˆ സൈറàµà´±à´¿àµ½ മാൽവെയർ à´…à´Ÿà´™àµà´™à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ</translation>
<translation id="7087282848513945231">രാജàµà´¯à´‚</translation>
<translation id="7088615885725309056">വളരെ പഴയ</translation>
<translation id="7090678807593890770">Google-ൽ <ph name="LINK" /> തിരയàµà´•</translation>
@@ -555,6 +591,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" />, à´¸àµà´°à´•àµà´·à´¾ മാനദണàµà´¡à´™àµà´™àµ¾ പാലികàµà´•àµà´¨àµà´¨à´¿à´²àµà´².</translation>
<translation id="721197778055552897">à´ˆ à´ªàµà´°à´¶àµâ€Œà´¨à´¤àµà´¤àµ†à´•àµà´•àµà´±à´¿à´šàµà´šàµ <ph name="BEGIN_LINK" />കൂടàµà´¤àµ½â€ മനസിലാകàµà´•àµà´•<ph name="END_LINK" />.</translation>
<translation id="7219179957768738017">കണകàµà´·à´¨àµâ€ <ph name="SSL_VERSION" /> ഉപയോഗികàµà´•àµà´¨àµà´¨àµ.</translation>
+<translation id="724691107663265825">സൈറàµà´±à´¿àµ½ മാൽവെയർ à´…à´Ÿà´™àµà´™à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ</translation>
<translation id="724975217298816891">നിങàµà´™à´³àµà´Ÿàµ† കാർഡൠവിശദാംശങàµà´™àµ¾ à´…à´ªàµâ€Œà´¡àµ‡à´±àµà´±àµà´šàµ†à´¯àµà´¯à´¾àµ» <ph name="CREDIT_CARD" />-à´¨àµà´±àµ† കാലാവധി തീരàµà´¨àµà´¨ തീയതിയàµà´‚ CVC-à´¯àµà´‚ നൽകàµà´•. à´¸àµà´¥à´¿à´°àµ€à´•à´°à´¿à´šàµà´šàµ à´•à´´à´¿à´žàµà´žà´¾àµ½, à´ˆ സൈറàµà´±àµà´®à´¾à´¯à´¿ കാർഡൠവിശദാംശങàµà´™àµ¾ പങàµà´•à´¿à´Ÿàµà´‚.</translation>
<translation id="725866823122871198">നിങàµà´™à´³àµà´Ÿàµ† à´•à´®àµà´ªàµà´¯àµ‚à´Ÿàµà´Ÿà´±à´¿à´¨àµà´±àµ† തീയതിയàµà´‚ സമയവàµà´‚ (<ph name="DATE_AND_TIME" />) തെറàµà´±à´¾à´¯à´¤à´¿à´¨à´¾àµ½ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ‡à´•àµà´•àµà´³àµà´³ à´¸àµà´µà´•à´¾à´°àµà´¯ കണകàµà´·àµ» à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´¾à´¨à´¾à´µà´¿à´²àµà´².</translation>
<translation id="7269802741830436641">à´ˆ വെബàµà´ªàµ‡à´œà´¿à´¨àµ ഒരൠറീഡയറകàµà´Ÿàµ ലൂപàµà´ªàµ ഉണàµà´Ÿàµ</translation>
@@ -610,6 +647,7 @@
<translation id="7658239707568436148">റദàµà´¦à´¾à´•àµà´•àµ‚</translation>
<translation id="7667346355482952095">നൽകിയ നയ ടോകàµà´•àµº ശൂനàµà´¯à´®à´¾à´£àµ à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ നിലവിലെ ടോകàµà´•à´£àµà´®à´¾à´¯à´¿ യോജികàµà´•àµà´¨àµà´¨à´¿à´²àµà´²</translation>
<translation id="7668654391829183341">à´…à´œàµà´žà´¾à´¤à´®à´¾à´¯ ഉപകരണം</translation>
+<translation id="7669271284792375604">à´ˆ സൈറàµà´±à´¿à´²àµ† ആകàµà´°à´®à´£à´•à´¾à´°à´¿à´•àµ¾, à´¬àµà´°àµ—സർ à´…à´¨àµà´­à´µà´¤àµà´¤àµ† ദോഷകരമായി ബാധികàµà´•àµà´¨àµà´¨ à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•àµ¾ ഇൻസàµâ€Œà´±àµà´±à´¾àµ¾ ചെയàµà´¯à´¿à´•àµà´•àµà´¨àµà´¨ വിധതàµà´¤à´¿àµ½ നിങàµà´™à´³àµ† കബളിപàµà´ªà´¿à´•àµà´•à´¾àµ» à´¶àµà´°à´®à´¿à´šàµà´šàµ‡à´•àµà´•àµà´‚ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, നിങàµà´™à´³àµà´Ÿàµ† ഹോംപേജൠമാറàµà´±àµà´¨àµà´¨à´¤à´¿à´²àµ‚ടെയോ à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•àµà´¨àµà´¨ സൈറàµà´±àµà´•à´³à´¿àµ½ കൂടàµà´¤àµ½ പരസàµà´¯à´™àµà´™àµ¾ കാണികàµà´•àµà´¨àµà´¨à´¤à´¿à´²àµ‚ടെയോ).</translation>
<translation id="7674629440242451245">à´ªàµà´¤à´¿à´¯ Chrome-à´¨àµà´±àµ† രസകരമായ സവിശേഷതകളിൽ താൽപàµà´ªà´°àµà´¯à´®àµà´£àµà´Ÿàµ‹? chrome.com/dev-ൽ à´žà´™àµà´™à´³àµà´Ÿàµ† ഡെവലപàµà´ªàµ¼ ചാനൽ പരീകàµà´·à´¿à´šàµà´šàµà´¨àµ‹à´•àµà´•àµà´•.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /> <ph name="SITE" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ‡à´•àµà´•àµ പോകàµà´• (à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´²àµà´²)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">à´ˆ സൈറàµà´±àµ കാഷെയിൽ നിനàµà´¨àµ ലോഡàµà´šàµ†à´¯àµà´¯à´¾à´¨à´¾à´•à´¿à´²àµà´²</translation>
@@ -625,14 +663,17 @@
<translation id="7800304661137206267">സനàµà´¦àµ‡à´¶ à´ªàµà´°à´¾à´®à´¾à´£àµ€à´•à´°à´£à´¤àµà´¤à´¿à´¨àµà´‚ <ph name="KX" /> à´¨àµà´‚ കീ à´Žà´•àµà´¸àµ‡à´žàµà´šàµ മെകàµà´•à´¾à´¨à´¿à´¸à´®à´¾à´¯à´¿ <ph name="CIPHER" /> ഉപയോഗിചàµà´šàµ à´ˆ കണകàµà´·à´¨àµ† <ph name="MAC" /> à´Žà´¨àµà´¨à´¤àµà´®à´¾à´¯à´¿ à´Žà´¨àµâ€â€Œà´•àµà´°à´¿à´ªàµà´±àµà´±àµ ചെയàµà´¤à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ.</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="7887683347370398519">നിങàµà´™à´³àµà´Ÿàµ† CVC പരിശോധിചàµà´šàµ വീണàµà´Ÿàµà´‚ à´¶àµà´°à´®à´¿à´•àµà´•àµà´•</translation>
<translation id="7912024687060120840">ഫോൾഡറിൽ:</translation>
<translation id="7935318582918952113">DOM à´¡à´¿à´¸àµâ€Œà´±àµà´±à´¿à´²àµ¼</translation>
<translation id="7938958445268990899">സെരàµâ€à´µà´±à´¿à´¨àµâ€à´±àµ† സരàµâ€à´Ÿàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ ഇതàµà´µà´°àµ†à´¯àµà´‚ സാധàµà´µà´²àµà´².</translation>
<translation id="7942349550061667556">à´šàµà´µà´ªàµà´ªàµ</translation>
+<translation id="7947285636476623132">കാലാവധി തീരàµà´¨àµà´¨ വർഷം പരിശോധിചàµà´šàµ വീണàµà´Ÿàµà´‚ à´¶àµà´°à´®à´¿à´šàµà´šàµà´¨àµ‹à´•àµà´•àµ‚</translation>
<translation id="7951415247503192394">(32-ബിറàµà´±àµ)</translation>
<translation id="7956713633345437162">മൊബൈൽ à´¬àµà´•àµà´•àµâ€Œà´®à´¾àµ¼à´•àµà´•àµà´•àµ¾</translation>
<translation id="7961015016161918242">à´’à´°à´¿à´•àµà´•à´²àµà´‚</translation>
@@ -640,7 +681,10 @@
<translation id="7983301409776629893">à´Žà´²àµà´²à´¾à´¯àµâ€Œà´ªàµà´ªàµ‹à´´àµà´‚ <ph name="ORIGINAL_LANGUAGE" /> നെ <ph name="TARGET_LANGUAGE" /> ലേകàµà´•àµ വിവർതàµà´¤à´¨à´‚ ചെയàµà´¯àµà´•</translation>
<translation id="7995512525968007366">à´µàµà´¯à´•àµà´¤à´®à´¾à´•àµà´•à´¿à´¯à´¿à´Ÿàµà´Ÿà´¿à´²àµà´²</translation>
<translation id="8012647001091218357">ഇപàµà´ªàµ‹àµ¾ à´žà´™àµà´™àµ¾à´•àµà´•àµ നിങàµà´™à´³àµà´Ÿàµ† à´°à´•àµà´·à´•àµ¼à´¤àµà´¤à´¾à´•àµà´•à´³àµ† ബനàµà´§à´ªàµà´ªàµ†à´Ÿà´¾à´¨à´¾à´¯à´¿à´²àµà´². വീണàµà´Ÿàµà´‚ à´¶àµà´°à´®à´¿à´•àµà´•àµà´•.</translation>
+<translation id="8025119109950072390">à´ˆ സൈറàµà´±à´¿à´²àµ† ആകàµà´°à´®à´£à´•à´¾à´°à´¿à´•àµ¾ സോഫàµâ€Œà´±àµà´±àµâ€Œà´µàµ†à´¯àµ¼ ഇൻസàµà´±àµà´±à´¾àµ¾ ചെയàµà´¯àµà´¨àµà´¨à´¤àµ‹ à´µàµà´¯à´•àµà´¤à´¿à´ªà´°à´®à´¾à´¯ വിവരങàµà´™àµ¾ വെളിപàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´¨àµà´¨à´¤àµ‹ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾, ഫോൺ നമàµà´ªà´±àµà´•àµ¾, à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡàµà´•àµ¾ à´Žà´¨àµà´¨àµ€ വിവരങàµà´™àµ¾) പോലàµà´³àµà´³ അപകടകരമായ കാരàµà´¯à´™àµà´™àµ¾ ചെയàµà´¯à´¿à´ªàµà´ªà´¿à´•àµà´•àµà´¨àµà´¨ തരതàµà´¤à´¿àµ½ നിങàµà´™à´³àµ† കബളിപàµà´ªà´¿à´šàµà´šàµ‡à´•àµà´•à´¾à´‚.</translation>
+<translation id="803030522067524905"><ph name="SITE" /> à´Žà´¨àµà´¨ സൈറàµà´±à´¿àµ½ Google à´¸àµà´°à´•àµà´·à´¿à´¤ à´¬àµà´°àµ—സിംഗൠഈയിടെ ഫിഷിംഗൠകണàµà´Ÿàµ†à´¤àµà´¤à´¿. നിങàµà´™à´³àµ† കബളിപàµà´ªà´¿à´•àµà´•à´¾àµ» ഫിഷിംഗൠസൈറàµà´±àµà´•àµ¾ മറàµà´±àµ വെബàµà´¸àµˆà´±àµà´±àµà´•à´³à´¾à´¯à´¿ നടികàµà´•àµà´¨àµà´¨à´¤à´¾à´£àµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">à´ˆ പേജൠ<ph name="SOURCE_LANGUAGE" />-ലാണàµ. <ph name="TARGET_LANGUAGE" />-ലേകàµà´•àµ വിവർതàµà´¤à´¨à´‚ ചെയàµà´¯à´£àµ‹?</translation>
+<translation id="8041089156583427627">ഫീഡàµà´¬à´¾à´•àµà´•àµ അയയàµà´•àµà´•àµà´•</translation>
<translation id="8088680233425245692">ലേഖനം കാണàµà´¨àµà´¨à´¤àµ പരാജയപàµà´ªàµ†à´Ÿàµà´Ÿàµ.</translation>
<translation id="8089520772729574115">ഒരൠMB-യിൽ à´•àµà´±à´µà´¾à´£àµ</translation>
<translation id="8091372947890762290">സെർവറിൽ സജീവമാകàµà´•àµ½ തീർപàµà´ªà´¾à´•àµà´•à´¿à´¯à´¿à´Ÿàµà´Ÿà´¿à´²àµà´²</translation>
@@ -651,9 +695,11 @@
<translation id="8150722005171944719"><ph name="URL" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ† ഫയൽ റീഡàµà´šàµ†à´¯àµà´¯à´¾à´¨à´¾à´µàµà´¨àµà´¨à´¿à´²àµà´². അതൠനീകàµà´•à´‚ചെയàµâ€Œà´¤à´¿à´°à´¿à´•àµà´•àµà´•à´¯àµ‹, നീകàµà´•à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´•à´¯àµ‹ ഫയൽ à´…à´¨àµà´®à´¤à´¿à´•àµ¾ ആകàµâ€Œà´¸à´¸àµà´¸àµ തടയàµà´•à´¯àµ‹ ചെയàµà´¯àµà´¨àµà´¨àµà´£àµà´Ÿà´¾à´•à´¾à´‚.</translation>
<translation id="8194797478851900357">&amp;നീകàµà´•àµà´¨àµà´¨à´¤àµ പഴയപടിയാകàµà´•àµà´•</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" à´Žà´¨àµà´¨ à´à´¡à´¿à´¯àµà´³àµà´³ വിപàµà´²àµ€à´•à´°à´£à´¤àµà´¤à´¿à´¨àµà´±àµ† à´…à´ªàµâ€Œà´¡àµ‡à´±àµà´±àµ URL അസാധàµà´µà´¾à´£àµ.</translation>
+<translation id="8202097416529803614">ഓർഡർ സംഗàµà´°à´¹à´‚</translation>
<translation id="8218327578424803826">നൽകിയിരികàµà´•àµà´¨àµà´¨ ലൊകàµà´•àµ‡à´·àµ»:</translation>
<translation id="8225771182978767009">à´ˆ à´•à´®àµà´ªàµà´¯àµ‚à´Ÿàµà´Ÿàµ¼ സജàµà´œà´®à´¾à´•àµà´•à´¿à´¯ à´µàµà´¯à´•àµà´¤à´¿, à´ˆ സൈറàµà´±àµ à´¬àµà´²àµ‹à´•àµà´•àµà´šàµ†à´¯àµà´¯à´¾àµ» തീരàµà´®à´¾à´¨à´¿à´šàµà´šà´¿à´°àµà´¨àµà´¨àµ.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ† നിലവിലàµà´³àµà´³ ആകàµà´°à´®à´£à´•à´¾à´°à´¿à´•àµ¾ നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ ഇലàµà´²à´¾à´¤à´¾à´•àµà´•à´¾à´¨àµ‹ മോഷàµâ€Œà´Ÿà´¿à´•àµà´•à´¾à´¨àµ‹ ഇടയàµà´³àµà´³ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, ഫോടàµà´Ÿàµ‹à´•àµ¾, പാസàµâ€Œà´µàµ‡à´¡àµà´•àµ¾, സനàµà´¦àµ‡à´¶à´™àµà´™àµ¾, à´•àµà´°àµ†à´¡à´¿à´±àµà´±àµ കാർഡàµà´•àµ¾ പോലàµà´³àµà´³à´µ) അപകടകരമായ à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•àµ¾ നിങàµà´™à´³àµà´Ÿàµ† à´•à´®àµà´ªàµà´¯àµ‚à´Ÿàµà´Ÿà´±à´¿àµ½ ഇൻസàµâ€Œà´±àµà´±à´¾à´³àµà´šàµ†à´¯àµà´¯à´¾àµ» à´¶àµà´°à´®à´¿à´šàµà´šàµ‡à´•àµà´•à´¾à´‚.</translation>
<translation id="8241707690549784388">നിങàµà´™à´³àµâ€ à´…à´¨àµà´µàµ‡à´·à´¿à´•àµà´•àµà´¨àµà´¨ പേജൠനിങàµà´™à´³àµâ€ രേഖപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿à´¯ വിവരങàµà´™à´³àµâ€ ഉപയോഗികàµà´•àµà´¨àµà´¨àµ. à´† പേജിലേകàµà´•àµ മടങàµà´™àµà´¨àµà´¨à´¤àµ നിങàµà´™à´³àµâ€ ആവരàµâ€à´¤àµà´¤à´¿à´•àµà´•à´¾à´µàµà´¨àµà´¨ à´à´¤àµ†à´™àµà´•à´¿à´²àµà´‚ à´ªàµà´°à´µàµƒà´¤àµà´¤à´¿à´•àµà´•àµ കാരണമായേകàµà´•àµà´‚. à´¤àµà´Ÿà´°à´¾à´¨àµâ€ നിങàµà´™à´³àµâ€ ആഗàµà´°à´¹à´¿à´•àµà´•àµà´¨àµà´¨àµà´£àµà´Ÿàµ‹?</translation>
<translation id="8249320324621329438">അവസാനം ലഭàµà´¯à´®à´¾à´¯à´¤àµ:</translation>
<translation id="8261506727792406068">ഇലàµà´²à´¾à´¤à´¾à´•àµà´•àµ‚</translation>
@@ -662,11 +708,13 @@
<translation id="8294431847097064396">ഉറവിടം</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="8349305172487531364">à´¬àµà´•àµà´®à´¾à´°àµâ€à´•àµà´•àµ ബാരàµâ€</translation>
<translation id="8363502534493474904">à´«àµà´²àµˆà´±àµà´±àµ മോഡൠഓഫാകàµà´•àµà´¨àµà´¨àµ</translation>
<translation id="8364627913115013041">സജàµà´œà´®à´¾à´•àµà´•à´¿à´¯à´¿à´Ÿàµà´Ÿà´¿à´²àµà´².</translation>
<translation id="8380941800586852976">അപകടകരമായതàµ</translation>
<translation id="8382348898565613901">നിങàµà´™àµ¾ à´…à´Ÿàµà´¤àµà´¤à´¿à´Ÿàµ† സനàµà´¦àµ¼à´¶à´¿à´šàµà´š à´¬àµà´•àµà´•àµâ€Œà´®à´¾àµ¼à´•àµà´•àµà´•àµ¾ ഇവിടെ ദൃശàµà´¯à´®à´¾à´•àµà´‚</translation>
+<translation id="8398259832188219207">à´•àµà´°à´¾à´·àµ റിപàµà´ªàµ‹àµ¼à´Ÿàµà´Ÿàµ <ph name="UPLOAD_TIME" />-നൠഅപàµâ€Œà´²àµ‹à´¡àµà´šàµ†à´¯àµâ€Œà´¤àµ</translation>
<translation id="8412145213513410671">à´•àµà´°à´¾à´·àµà´•à´³àµâ€ <ph name="CRASH_COUNT" /></translation>
<translation id="8412392972487953978">നിങàµà´™à´³àµâ€ സമാന പാസàµà´«àµà´°àµ†à´¯àµà´¸àµ à´°à´£àµà´Ÿàµà´¤à´µà´£ നലàµâ€à´•à´£à´‚.</translation>
<translation id="8428213095426709021">à´•àµà´°à´®àµ€à´•à´°à´£à´™àµà´™à´³àµâ€</translation>
@@ -678,9 +726,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> à´ªàµà´°à´¤à´¿à´•à´°à´¿à´•àµà´•à´¾àµ» കൂടàµà´¤àµ½ സമയമെടàµà´¤àµà´¤àµ.</translation>
<translation id="852346902619691059">à´ˆ സെർവറിനൠഇതൠ<ph name="DOMAIN" /> ഡൊമെയàµâ€Œà´¨à´¾à´£àµ†à´¨àµà´¨àµ തെളിയികàµà´•à´¾à´¨à´¾à´¯à´¿à´²àµà´²; അതിനàµà´±àµ† à´¸àµà´°à´•àµà´·à´¾ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±à´¿à´¨àµ† നിങàµà´™à´³àµà´Ÿàµ† ഉപകരണതàµà´¤à´¿à´¨àµà´±àµ† à´“à´ªàµà´ªà´±àµ‡à´±àµà´±à´¿à´‚ഗൠസിസàµà´±àµà´±à´‚ വിശàµà´µà´¸à´¿à´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´². തെറàµà´±à´¾à´¯ കോൺഫിഗറേഷൻ കാരണമോ ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿ നിങàµà´™à´³àµà´Ÿàµ† കണകàµà´·à´¨àµ† തടസàµà´¸à´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´¨àµà´¨à´¤à´¿à´¨à´¾à´²àµ‹ ആയിരികàµà´•à´¾à´‚ ഇതൠസംഭവിചàµà´šà´¤àµ. <ph name="BEGIN_LEARN_MORE_LINK" />കൂടàµà´¤à´²à´±à´¿à´¯àµà´•<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Google പേയàµâ€Œà´®àµ†à´¨àµà´±àµ ഇതàµà´¤à´°à´‚ കാർഡിനെ പിനàµà´¤àµà´£à´¯àµà´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´². മറàµà´±àµŠà´¨àµà´¨àµ തിരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•.</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="8553075262323480129">പേജിനàµâ€à´±àµ† ഭാഷ നിരàµâ€â€Œà´£àµà´£à´¯à´¿à´•àµà´•à´¾à´¨àµâ€â€Œ കഴിയാതàµà´¤à´¤à´¿à´¨à´¾à´²àµâ€â€Œ വിവരàµâ€â€Œà´¤àµà´¤à´¨à´‚ പരാജയപàµà´ªàµ†à´Ÿàµà´Ÿàµ.</translation>
<translation id="8559762987265718583">നിങàµà´™à´³àµà´Ÿàµ† ഉപകരണതàµà´¤à´¿à´¨àµà´±àµ† തീയതിയàµà´‚ സമയവàµà´‚ (<ph name="DATE_AND_TIME" />) തെറàµà´±à´¾à´¯à´¤à´¿à´¨à´¾àµ½ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ‡à´•àµà´•àµà´³àµà´³ à´¸àµà´µà´•à´¾à´°àµà´¯ കണകàµà´·àµ» à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´¾à´¨à´¾à´µà´¿à´²àµà´².</translation>
-<translation id="856992080682148">à´ˆ സൈറàµà´±à´¿à´¨à´¾à´¯àµà´³àµà´³ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ 2017-ലോ അതിനàµà´¶àµ‡à´·à´®àµ‹ കാലഹരണപàµà´ªàµ†à´Ÿàµà´‚ à´’à´ªàµà´ªà´‚ സർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ ചെയിനിൽ SHA-1 ഉപയോഗിചàµà´šàµ സൈൻ ചെയàµâ€Œà´¤ ഒരൠസർടàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ à´…à´Ÿà´™àµà´™à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´•à´¯àµà´‚ ചെയàµà´¯àµà´¨àµà´¨àµ.</translation>
<translation id="8571890674111243710"><ph name="LANGUAGE" /> ലേകàµà´•àµ പേജൠവിവരàµâ€â€Œà´¤àµà´¤à´¨à´‚ ചെയàµà´¯àµà´¨àµà´¨àµ...</translation>
<translation id="859285277496340001">സാകàµâ€à´·àµà´¯à´ªà´¤àµà´°à´‚ അസാധàµà´µà´¾à´•àµà´•à´¿à´¯àµ‹ à´Žà´¨àµà´¨àµ പരിശോധികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ അതൠഒരൠമെകàµà´•à´¾à´¨à´¿à´¸à´¤àµà´¤àµ†à´¯àµà´‚ സൂചിപàµà´ªà´¿à´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´².</translation>
<translation id="8620436878122366504">നിങàµà´™à´³àµà´Ÿàµ† à´°à´•àµà´·à´¿à´¤à´¾à´•àµà´•àµ¾ ഇതàµà´µà´°àµ† അംഗീകാരം നൽകിയിടàµà´Ÿà´¿à´²àµà´²</translation>
@@ -693,10 +741,12 @@
<translation id="8740359287975076522"><ph name="HOST_NAME" /> ഹോസàµâ€Œà´±àµà´±à´¿à´¨àµà´±àµ† &lt;abbr id="dnsDefinition"&gt;DNS വിലാസം&lt;/abbr&gt; à´•à´£àµà´Ÿàµ†à´¤àµà´¤à´¾à´¨à´¾à´¯à´¿à´²àµà´². à´ªàµà´°à´¶àµâ€Œà´¨à´‚ നിർണàµà´£à´¯à´¿à´•àµà´•àµà´¨àµà´¨àµ.</translation>
<translation id="8790007591277257123">&amp;ഇലàµà´²à´¾à´¤à´¾à´•àµà´•àµà´¨àµà´¨à´¤àµ വീണàµà´Ÿàµà´‚ ചെയàµà´¯àµà´•</translation>
<translation id="8798099450830957504">à´¸àµà´¥à´¿à´°à´¸àµà´¥à´¿à´¤à´¿</translation>
+<translation id="8800988563907321413">നിങàµà´™à´³àµà´Ÿàµ† സമീപതàµà´¤àµà´³àµà´³ നിർദàµà´¦àµ‡à´¶à´™àµà´™àµ¾ ഇവിടെ ദൃശàµà´¯à´®à´¾à´•àµà´‚</translation>
<translation id="8804164990146287819">à´¸àµà´µà´•à´¾à´°àµà´¯à´¤ നയം</translation>
<translation id="8820817407110198400">à´¬àµà´•àµà´•àµâ€Œà´®à´¾à´°àµâ€à´•àµà´•àµà´•à´³àµâ€</translation>
<translation id="8834246243508017242">കോൺടാകàµâ€Œà´±àµà´±àµà´•àµ¾ ഉപയോഗിചàµà´šàµ à´“à´Ÿàµà´Ÿàµ‹à´«à´¿àµ½ à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´•àµà´·à´®à´®à´¾à´•àµà´•àµà´•â€¦</translation>
<translation id="883848425547221593">മറàµà´±àµà´³àµà´³ à´¬àµà´•àµà´•àµâ€Œà´®à´¾à´°àµâ€â€Œà´•àµà´•àµà´•à´³àµâ€â€Œ</translation>
+<translation id="884264119367021077">à´·à´¿à´ªàµà´ªà´¿à´‚ഗൠവിലാസം</translation>
<translation id="884923133447025588">അസാധàµà´µà´¾à´•àµà´•à´²àµâ€ à´ªàµà´°à´µà´°àµâ€à´¤àµà´¤à´¨à´‚ à´•à´£àµà´Ÿàµ†à´¤àµà´¤à´¿à´¯à´¿à´Ÿàµà´Ÿà´¿à´²àµà´².</translation>
<translation id="885730110891505394">Google-മായി പങàµà´•à´¿à´Ÿàµà´¨àµà´¨àµ</translation>
<translation id="8866481888320382733">നയ à´•àµà´°à´®àµ€à´•à´°à´£à´™àµà´™àµ¾ പാഴàµâ€Œà´¸àµà´šàµ†à´¯àµà´¯àµà´¨àµà´¨à´¤à´¿àµ½ പിശകàµ</translation>
@@ -714,18 +764,21 @@
<translation id="8971063699422889582">സെരàµâ€à´µà´±à´¿à´¨àµâ€à´±àµ† സരàµâ€à´Ÿàµà´Ÿà´¿à´«à´¿à´•àµà´•à´±àµà´±àµ കാലഹരണപàµà´ªàµ†à´Ÿàµà´Ÿàµ.</translation>
<translation id="8987927404178983737">മാസം</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">നിങàµà´™àµ¾ പോകാനിരികàµà´•àµà´¨àµà´¨ സൈറàµà´±à´¿àµ½ ദോഷകരമായ à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•à´³àµà´£àµà´Ÿàµ</translation>
<translation id="9001074447101275817">à´ªàµà´°àµ‹à´•àµâ€Œà´¸à´¿ <ph name="DOMAIN" /> ഡൊമെയàµâ€Œà´¨à´¿à´¨àµ ഉപയോകàµà´¤àµƒà´¨à´¾à´®à´µàµà´‚ പാസàµâ€Œà´µàµ‡à´¡àµà´‚ ആവശàµà´¯à´®à´¾à´£àµ.</translation>
<translation id="901974403500617787">ഉടമയàµâ€Œà´•àµà´•àµ മാതàµà´°à´®àµ‡ സിസàµà´±àµà´±à´¤àµà´¤à´¿à´²à´¾à´•à´®à´¾à´¨à´‚ ബാധകമാകàµà´•à´¾à´µàµà´¨àµà´¨ à´«àµà´²à´¾à´—ൠസജàµà´œà´®à´¾à´•àµà´•à´¾à´¨à´¾à´•àµ‚: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">à´ˆ പേജൠ<ph name="TARGET_LANGUAGE" />-ലേകàµà´•àµ വിവർതàµà´¤à´¨à´‚ ചെയàµâ€Œà´¤àµ</translation>
+<translation id="9035022520814077154">à´¸àµà´°à´•àµà´· പിശകàµ</translation>
<translation id="9038649477754266430">പേജàµà´•àµ¾ കൂടàµà´¤àµ½ വേഗതàµà´¤à´¿àµ½ ലോഡàµà´šàµ†à´¯àµà´¯à´¾àµ» ഒരൠപàµà´°à´µà´šà´¨ സേവനം ഉപയോഗികàµà´•àµà´•</translation>
<translation id="9039213469156557790">à´ˆ പേജിൽ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´²àµà´²à´¾à´¤àµà´¤ മറàµà´±àµ ഉറവിടങàµà´™àµ¾ ഉൾപàµà´ªàµ†à´Ÿàµà´¨àµà´¨àµ. à´ˆ ഉറവിടങàµà´™àµ¾ കൈമാറàµà´¨àµà´¨à´¤à´¿à´¨à´¿à´Ÿàµ† മറàµà´±àµà´³àµà´³à´µàµ¼à´•àµà´•àµ കാണാനàµà´‚ പേജിനàµà´±àµ† à´ªàµà´°à´µàµ¼à´¤àµà´¤à´¨à´°àµ€à´¤à´¿ മാറàµà´±àµà´¨àµà´¨ തരതàµà´¤à´¿àµ½ ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿à´¯àµâ€Œà´•àµà´•àµ പരിഷàµâ€Œà´•àµà´•à´°à´¿à´•àµà´•à´¾à´¨àµà´®à´¾à´¯àµ‡à´•àµà´•àµà´‚.</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> à´Žà´¨àµà´¨à´¤à´¿à´²àµ† ആകàµà´°à´®à´£à´•à´¾à´°à´¿à´•àµ¾, à´¬àµà´°àµ—സർ à´…à´¨àµà´­à´µà´¤àµà´¤àµ† ദോഷകരമായി ബാധികàµà´•àµà´¨àµà´¨ à´ªàµà´°àµ‹à´—àµà´°à´¾à´®àµà´•àµ¾ ഇൻസàµà´±àµà´±à´¾à´³àµà´šàµ†à´¯àµà´¯à´¿à´•àµà´•àµà´¨àµà´¨ വിധതàµà´¤à´¿àµ½ നിങàµà´™à´³àµ† കബളിപàµà´ªà´¿à´•àµà´•à´¾àµ» à´¶àµà´°à´®à´¿à´šàµà´šàµ‡à´•àµà´•àµà´‚ (ഉദാഹരണതàµà´¤à´¿à´¨àµ, നിങàµà´™à´³àµà´Ÿàµ† ഹോംപേജൠമാറàµà´±àµà´¨àµà´¨à´¤à´¿à´²àµ‚ടെ à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•àµà´¨àµà´¨ സൈറàµà´±àµà´•à´³à´¿àµ½ അധിക പരസàµà´¯à´™àµà´™àµ¾ കാണികàµà´•àµà´¨àµà´¨à´¤à´¿à´²àµ‚ടെ).</translation>
<translation id="9050666287014529139">പാസàµà´«àµà´°àµ†à´¯àµà´¸àµ</translation>
<translation id="9065203028668620118">à´Žà´¡à´¿à´±àµà´±àµà´šàµ†à´¯àµà´¯àµà´•</translation>
<translation id="9068849894565669697">വർണàµà´£à´‚ തിരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•</translation>
<translation id="9076283476770535406">ഇതിൽ à´®àµà´¤à´¿àµ¼à´¨àµà´¨à´µàµ¼à´•àµà´•àµà´³àµà´³ ഉളàµà´³à´Ÿà´•àµà´•à´‚ ഉണàµà´Ÿà´¾à´¯à´¿à´°à´¿à´•àµà´•à´¾à´‚</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> പേജൠപàµà´°à´µàµ¼à´¤àµà´¤à´¿à´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´²</translation>
<translation id="9103872766612412690">നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ പരിരകàµà´·à´¿à´•àµà´•à´¾àµ» സാധാരണയായി <ph name="SITE" />, എൻകàµà´°à´¿à´ªàµâ€Œà´·àµ» ഉപയോഗികàµà´•àµà´¨àµà´¨àµ. ഇപàµà´ªàµ‹àµ¾ <ph name="SITE" /> സൈറàµà´±à´¿à´²àµ‡à´•àµà´•àµ കണകàµâ€Œà´±àµà´±àµà´šàµ†à´¯àµà´¯à´¾àµ» Chromium à´¶àµà´°à´®à´¿à´šàµà´šà´ªàµà´ªàµ‹àµ¾, അസാധാരണമായതàµà´‚ തെറàµà´±à´¾à´¯à´¤àµà´®à´¾à´¯ à´•àµà´°àµ†à´¡àµ»à´·àµà´¯à´²àµà´•àµ¾ വെബàµâ€Œà´¸àµˆà´±àµà´±àµ തിരികെ അയചàµà´šàµ. ഒരൠആകàµà´°à´®à´£à´•à´¾à´°à´¿ <ph name="SITE" /> à´Žà´¨àµà´¨à´¤à´¾à´¯à´¿ ഭാവികàµà´•à´¾àµ» à´¶àµà´°à´®à´¿à´•àµà´•àµà´®àµà´ªàµ‹à´´àµ‹ Wi-Fi സൈൻ ഇൻ à´¸àµâ€Œà´•àµà´°àµ€àµ», കണകàµà´·à´¨àµ† തടസàµà´¸à´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´®àµà´ªàµ‹à´´àµ‹ ആണൠഇങàµà´™à´¨àµ† സംഭവികàµà´•à´¾à´¨à´¿à´Ÿà´¯àµà´³àµà´³à´¤àµ. à´à´¤àµ†à´™àµà´•à´¿à´²àµà´‚ ഡാറàµà´± കൈമാറàµà´¨àµà´¨à´¤à´¿à´¨àµà´®àµà´®àµà´ªàµ Chromium കണകàµà´·àµ» അവസാനിപàµà´ªà´¿à´šàµà´šà´¤à´¿à´¨à´¾àµ½, നിങàµà´™à´³àµà´Ÿàµ† വിവരങàµà´™àµ¾ à´¤àµà´Ÿàµ¼à´¨àµà´¨àµà´‚ à´¸àµà´°à´•àµà´·à´¿à´¤à´®à´¾à´¯à´¿à´°à´¿à´•àµà´•àµà´‚.</translation>
<translation id="9137013805542155359">യഥാരàµâ€à´¤àµà´¥à´®à´¾à´¯à´¤àµ കാണികàµà´•àµà´•</translation>
+<translation id="9137248913990643158">à´ˆ ആപàµà´ªàµ ഉപയോഗികàµà´•àµà´¨àµà´¨à´¤à´¿à´¨àµ à´®àµà´®àµà´ªàµ ആരംഭിചàµà´šàµ Chrome-ൽ സൈൻ ഇൻ ചെയàµà´¯àµà´•.</translation>
<translation id="9148507642005240123">&amp;à´Žà´¡à´¿à´±àµà´±àµà´šàµ†à´¯àµà´¯àµà´¨àµà´¨à´¤àµ പഴയപടിയാകàµà´•àµà´•</translation>
<translation id="9157595877708044936">സജàµà´œàµ€à´•à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ...</translation>
<translation id="9170848237812810038">â€&amp;പൂരàµâ€à´µà´¾à´µà´¸àµà´¥à´¯à´¿à´²à´¾à´•àµà´•àµà´•</translation>
@@ -738,7 +791,6 @@
<translation id="935608979562296692">ഫോം മായàµâ€Œà´•àµà´•àµà´•</translation>
<translation id="939736085109172342">à´ªàµà´¤à´¿à´¯ ഫോളàµâ€à´¡à´°àµâ€</translation>
<translation id="941721044073577244">à´ˆ സൈറàµà´±àµ സനàµà´¦àµ¼à´¶à´¿à´•àµà´•à´¾àµ» നിങàµà´™àµ¾à´•àµà´•àµ à´…à´¨àµà´®à´¤à´¿à´¯à´¿à´²àµà´²àµ†à´¨àµà´¨àµ തോനàµà´¨àµà´¨àµà´¨àµ</translation>
-<translation id="962701380617707048">നിങàµà´™à´³àµà´Ÿàµ† കാർഡൠവിശദാംശങàµà´™àµ¾ à´…à´ªàµâ€Œà´¡àµ‡à´±àµà´±àµà´šàµ†à´¯àµà´¯à´¾àµ», <ph name="CREDIT_CARD" />-à´¨àµà´±àµ† കാലാവധി തീരàµà´¨àµà´¨ തീയതിയàµà´‚ CVC-à´¯àµà´‚ നൽകàµà´•</translation>
<translation id="969892804517981540">ഔദàµà´¯àµ‹à´—à´¿à´• ബിലàµâ€à´¡àµ</translation>
<translation id="988159990683914416">വികാസക പതിപàµà´ªàµ</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 9672e97c861..511f2228ee2 100644
--- a/chromium/components/strings/components_strings_mr.xtb
+++ b/chromium/components/strings/components_strings_mr.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Wi-Fi शी पà¥à¤¨à¥à¤¹à¤¾ कनेकà¥à¤Ÿ करीत आहे</translation>
<translation id="1175364870820465910">&amp;मà¥à¤¦à¥à¤°à¤£...</translation>
<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="1206967143813997005">खराब पà¥à¤°à¤¾à¤°à¤‚भिक सà¥à¤µà¤¾à¤•à¥à¤·à¤°à¥€</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">निळसर</translation>
<translation id="1629803312968146339">Chrome ने हे कारà¥à¤¡ जतन करावे असे आपण इचà¥à¤›à¤¿à¤¤à¤¾?</translation>
<translation id="1640180200866533862">वापरकरà¥à¤¤à¤¾ धोरणे</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />साइटचà¥à¤¯à¤¾ मà¥à¤–à¥à¤¯à¤ªà¥ƒà¤·à¥à¤ à¤¾à¤µà¤° भेट देऊन<ph name="END_LINK" /> पहा.</translation>
<translation id="1644184664548287040">नेटवरà¥à¤• कॉनà¥à¤«à¤¿à¤—रेशन अवैध आहे आणि आयात केले जाऊ शकले नाही.</translation>
<translation id="1644574205037202324">इतिहास</translation>
<translation id="1645368109819982629">असमरà¥à¤¥à¤¿à¤¤ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²</translation>
<translation id="1676269943528358898"><ph name="SITE" /> आपली माहिती संरकà¥à¤·à¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ सामानà¥à¤¯à¤¤à¤ƒ कूटबदà¥à¤§à¥€à¤•à¤°à¤£ वापरते. Google Chrome ने यावेळी <ph name="SITE" /> शी कनेकà¥â€à¤Ÿ करणà¥â€à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ केला तेवà¥â€à¤¹à¤¾, वेबसाइटने असामानà¥à¤¯ आणि अयोगà¥à¤¯ कà¥à¤°à¥‡à¤¡à¥‡à¤¨à¥à¤¶à¤¿à¤¯à¤² परत पाठविले. à¤à¤•à¤¤à¤° आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¤¾ <ph name="SITE" /> असलà¥à¤¯à¤¾à¤šà¥€ बतावणी करणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करतो तेवà¥â€à¤¹à¤¾ किंवा Wi-Fi साइन इन सà¥à¤•à¥à¤°à¥€à¤¨à¤¨à¥‡ कनेकà¥à¤¶à¤¨à¤®à¤§à¥à¤¯à¥‡ वà¥à¤¯à¤¤à¥à¤¯à¤¯ आणले तेवà¥â€à¤¹à¤¾ हे घडू शकते. कोणतà¥à¤¯à¤¾à¤¹à¥€ डेटाची अदलाबदल करणà¥à¤¯à¤¾à¤ªà¥‚रà¥à¤µà¥€ Google Chrome ने कनेकà¥à¤¶à¤¨ थांबविलà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ आपली माहिती अदà¥à¤¯à¤¾à¤ª सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ आहे.</translation>
+<translation id="168328519870909584">सधà¥â€à¤¯à¤¾ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> वर असलेले आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ आपली माहिती (उदाहरणारà¥à¤¥, फोटो, संकेतशबà¥à¤¦, संदेश आणि कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) चोरणारे किंवा हटविणारे धोकादायक अâ€à¥…पà¥â€à¤¸ कदाचित आपलà¥à¤¯à¤¾ डिवà¥â€à¤¹à¤¾à¤‡à¤¸à¤µà¤° सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करू शकतात.</translation>
<translation id="168841957122794586">सरà¥à¤µà¥à¤¹à¤° पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤°à¤¾à¤¤ à¤à¤• कमकà¥à¤µà¤¤ कà¥à¤°à¤¿à¤ªà¥à¤Ÿà¥‹à¤—à¥à¤°à¤¾à¤«à¤¿à¤• की आहे.</translation>
-<translation id="1701955595840307032">सà¥à¤šà¤µà¤¿à¤²à¥‡à¤²à¥€ सामगà¥à¤°à¥€ मिळवा</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">या साइटला भेट देणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ आपलà¥à¤¯à¤¾à¤²à¤¾ <ph name="NAME" /> कडील परवानगी आवशà¥à¤¯à¤• आहे</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">पृषà¥à¤  कà¥à¤°à¤®à¤¾à¤‚क</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows नेटवरà¥à¤• निदान चालवून पहा<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">कृपया आपले संंकालित सांकेतिक वाकà¥à¤¯à¤¾à¤‚श अदà¥à¤¯à¤¤à¤¨à¤¿à¤¤ करा.</translation>
+<translation id="1787142507584202372">आपले खà¥à¤²à¥‡ टॅब येथे दिसतात</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚गला अलीकडे <ph name="SITE" /> वर <ph name="BEGIN_LINK" />मालवेअर आढळले आहे<ph name="END_LINK" />. सामानà¥à¤¯à¤¤à¤ƒ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ असलेलà¥à¤¯à¤¾ वेबसाइट काहीवेळा मालवेअरमà¥à¤³à¥‡ संकà¥à¤°à¤®à¤¿à¤¤ à¤à¤¾à¤²à¥‡à¤²à¥à¤¯à¤¾ असतात. à¤à¤• जà¥à¤žà¤¾à¤¤ मालवेअर वितरक असलेलà¥à¤¯à¤¾, <ph name="SUBRESOURCE_HOST" /> कडून दà¥à¤°à¥à¤­à¤¾à¤µà¤¨à¤¾à¤ªà¥‚रà¥à¤£ सामगà¥à¤°à¥€ येते. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">अवैध विनंती किंवा विनंती मापदंड</translation>
+<translation id="1834321415901700177">या साइटमधà¥à¤¯à¥‡ धोकादायक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® आहेत</translation>
<translation id="1838667051080421715">आपण वेब पृषà¥à¤ à¤¾à¤šà¤¾ सà¥à¤¤à¥à¤°à¥‹à¤¤ पहात आहात.</translation>
<translation id="1871208020102129563">पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ निशà¥à¤šà¤¿à¤¤ पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¥à¤¹à¤° वापरणà¥â€à¤¯à¤¾à¤¸ सेट करणà¥â€à¤¯à¤¾à¤¤ आले आहे, .pac सà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ URL नवà¥à¤¹à¥‡.</translation>
<translation id="1883255238294161206">सूची संकà¥à¤šà¤¿à¤¤ करा</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">मूळ कà¥à¤²à¤¾à¤¯à¤‚ट</translation>
<translation id="213826338245044447">Mobile बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="2148716181193084225">आज</translation>
-<translation id="2149973817440762519">बà¥à¤•à¤®à¤¾à¤°à¥à¤• संपादित करा</translation>
<translation id="2154054054215849342">आपलà¥à¤¯à¤¾ डोमेनसाठी संकालन उपलबà¥à¤§ नाही</translation>
<translation id="2166049586286450108">पूरà¥à¤£ पà¥à¤°à¤¶à¤¾à¤¸à¤¨ पà¥à¤°à¤µà¥‡à¤¶</translation>
<translation id="2166378884831602661">ही साइट सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ कनेकà¥à¤¶à¤¨ पà¥à¤°à¤¦à¤¾à¤¨ करू शकत नाही</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">आपण <ph name="SITE" /> ला आतà¥à¤¤à¤¾ भेट देऊ शकत नाही कारण वेबसाइट HSTS वापरते. नेटवरà¥à¤• तà¥à¤°à¥à¤Ÿà¥€ आणि आकà¥à¤°à¤®à¤£à¥‡ सामानà¥à¤¯à¤¤à¤ƒ तातà¥à¤ªà¥à¤°à¤¤à¥€ असतात, यामà¥à¤³à¥‡ हे पृषà¥à¤  कदाचित नंतर कारà¥à¤¯ करेल. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155"><ph name="ENTRY_INDEX" /> अनà¥à¤•à¥à¤°à¤®à¤£à¤¿à¤•à¥‡à¤®à¤§à¥€à¤² अवैध बà¥à¤•à¤®à¤¾à¤°à¥à¤•à¤•à¤¡à¥‡ दà¥à¤°à¥à¤²à¤•à¥à¤· केले</translation>
<translation id="2354001756790975382">इतर बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
+<translation id="2355395290879513365">आपण या साइटवर पाहत असलेलà¥à¤¯à¤¾ पà¥à¤°à¤¤à¤¿à¤®à¤¾ पाहणà¥à¤¯à¤¾à¤¸ आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ सकà¥à¤·à¤® असू शकतात आणि तà¥à¤¯à¤¾à¤¤ सà¥à¤§à¤¾à¤°à¤£à¤¾ करून आपली फसवणूक करू शकतात.</translation>
<translation id="2359808026110333948">सà¥à¤°à¥‚ ठेवा</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> वाजता कॅपà¥à¤šà¤° केलेला कà¥à¤°à¥…श अहवाल अपलोड केला नाही</translation>
<translation id="2367567093518048410">दरà¥à¤œà¤¾</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">कोणतेही मूलà¥à¤¯ सेट केलà¥à¤¯à¤¾à¤¶à¤¿à¤µà¤¾à¤¯ धोरणे दरà¥à¤¶à¤µà¤¾</translation>
<translation id="2396249848217231973">&amp;हटवा पूरà¥à¤µà¤µà¤¤ करा</translation>
<translation id="2455981314101692989">या वेबपृषà¥à¤ à¤¾à¤¨à¥‡ या फॉरà¥à¤®à¤•à¤°à¤¿à¤¤à¤¾ सà¥à¤µà¤¯à¤‚चलितरितà¥à¤¯à¤¾ भरणे अकà¥à¤·à¤® केले आहे.</translation>
+<translation id="2460160116472764928">Google सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚गला अलीकडे <ph name="SITE" /> वर <ph name="BEGIN_LINK" />मालवेअर आढळले आहे<ph name="END_LINK" />. सामानà¥à¤¯à¤¤à¤ƒ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ असलेलà¥à¤¯à¤¾ वेबसाइट काहीवेळा मालवेअरमà¥à¤³à¥‡ संकà¥à¤°à¤®à¤¿à¤¤ à¤à¤¾à¤²à¥‡à¤²à¥à¤¯à¤¾ असतात. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">भरून टाका</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />नेटवरà¥à¤• निदान चालविणे<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">अवैध शोध URL.</translation>
@@ -151,7 +157,7 @@
<translation id="255002559098805027"><ph name="HOST_NAME" /> नी à¤à¤• अवैध पà¥à¤°à¤¤à¤¿à¤¸à¤¾à¤¦ पाठविला.</translation>
<translation id="2552545117464357659">थोडे नवीन</translation>
<translation id="2556876185419854533">&amp; संपादित करा पूरà¥à¤µà¤µà¤¤ करा</translation>
-<translation id="2587841377698384444">शबà¥à¤¦à¤•à¥‹à¤¶ API ID:</translation>
+<translation id="2587841377698384444">शबà¥à¤¦à¤•à¥‹à¤¶ API आयडी:</translation>
<translation id="2597378329261239068">हा दसà¥à¤¤à¤à¤µà¤œ संकेतशबà¥à¤¦ संरकà¥à¤·à¤¿à¤¤ आहे. कृपया संकेतशबà¥à¤¦ पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ करा.</translation>
<translation id="2609632851001447353">तफावत</translation>
<translation id="2625385379895617796">आपले घडà¥à¤¯à¤¾à¤³ पà¥à¤¢à¥‡ आहे</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">आपली खातà¥à¤°à¥€ आहे की आपण ही पृषà¥à¤ à¥‡ आपलà¥â€à¤¯à¤¾ इतिहासातून हटवू इचà¥à¤›à¤¿à¤¤à¤¾?</translation>
<translation id="2677748264148917807">सोडा</translation>
<translation id="269990154133806163">सरà¥à¤µà¥à¤¹à¤°à¤¨à¥‡ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° पारदरà¥à¤¶à¤•à¤¤à¤¾ धोरणाचा वापर करून सारà¥à¤µà¤œà¤¨à¤¿à¤•à¤°à¤¿à¤¤à¥à¤¯à¤¾ उघड न केलेले à¤à¤• पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° सादर केले. काही पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤°à¥‡ विशà¥à¤µà¤¸à¤¨à¥€à¤¯ आहेत आणि आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤‚विरूदà¥à¤§ संरकà¥à¤·à¤£ करतात हे सà¥à¤¨à¤¿à¤¶à¥à¤šà¤¿à¤¤ करणà¥â€à¤¯à¤¾à¤¸à¤¾à¤ à¥€ तà¥à¤¯à¤¾à¤‚चà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ ही à¤à¤• आवशà¥à¤¯à¤•à¤¤à¤¾ आहे. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">वाचन सूची</translation>
<translation id="2704283930420550640">मूलà¥à¤¯ सà¥à¤µà¤°à¥à¤ªà¤¨à¤¾à¤¶à¥€ जà¥à¤³à¤¤ नाही.</translation>
<translation id="2704951214193499422">Chromium यावेळी आपलà¥à¤¯à¤¾ कारà¥à¤¡à¤šà¥€ पà¥à¤·à¥à¤Ÿà¥€ करणà¥à¤¯à¤¾à¤¤ अकà¥à¤·à¤® होते. कृपया नंतर पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा.</translation>
<translation id="2705137772291741111">या साइटची जतन (कॅश केलेली) केलेली पà¥à¤°à¤¤ वाचणà¥à¤¯à¤¾à¤œà¥‹à¤—ी नवà¥à¤¹à¤¤à¥€.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">आपण <ph name="DOMAIN" /> वर पोहोचणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ केला, परंतॠसरà¥à¤µà¥à¤¹à¤°à¤¨à¥‡ सादर केलेले पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° तà¥à¤¯à¤¾à¤šà¥à¤¯à¤¾ जारीकरà¥à¤¤à¥à¤¯à¤¾à¤¦à¥à¤µà¤¾à¤°à¥‡ मागे घेतले गेले आहे. याचा अरà¥à¤¥ सरà¥à¤µà¥à¤¹à¤°à¤¨à¥‡ सादर केलेलà¥à¤¯à¤¾ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤¤à¤¾ कà¥à¤°à¥‡à¤¡à¥‡à¤¨à¥à¤¶à¤¿à¤¯à¤²à¤µà¤° अजिबात विशà¥à¤µà¤¾à¤¸ ठेऊ नये. आपण कदाचित आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¶à¥€ संपà¥à¤°à¥‡à¤·à¤£ करत आहात. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">परवानगी मागा</translation>
<translation id="2713444072780614174">पांढरा</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">आपण सेटिंगà¥à¤œ पृषà¥à¤ à¤¾à¤µà¤°à¥‚न à¤à¤•à¤¾ कनेकà¥à¤¶à¤¨à¤¸à¤¾à¤ à¥€ कॉनà¥à¤«à¤¿à¤—र केलेले कोणतेही पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ अकà¥à¤·à¤® करू शकता.</translation>
<translation id="2955913368246107853">शोध बार बंद करा</translation>
<translation id="2958431318199492670">नेटवरà¥à¤• कॉनà¥à¤«à¤¿à¤—रेशन ONC मानकाचे पालन करत नाही. कॉनà¥à¤«à¤¿à¤—रेशनचे भाग आयात केले जाऊ शकत नाहीत.</translation>
+<translation id="29611076221683977"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> वरील आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ सधà¥à¤¯à¤¾ कदाचित आपली माहिती (उदाहरणारà¥à¤¥, फोटो, संकेतशबà¥à¤¦, संदेश आणि कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) चोरणारे आणि हटविणारे धोकादायक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® आपलà¥à¤¯à¤¾ Mac वर सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करू शकतात.</translation>
<translation id="2969319727213777354">à¤à¤• सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ कनेकà¥â€à¤¶à¤¨ सà¥â€à¤¥à¤¾à¤ªà¤¿à¤¤ करणà¥â€à¤¯à¤¾à¤ªà¥‚रà¥à¤µà¥€, आपले घडà¥â€à¤¯à¤¾à¤³ योगà¥à¤¯à¤°à¤¿à¤¤à¥à¤¯à¤¾ सेट केले असणे आवशà¥à¤¯à¤• आहे. वेबसाइट तà¥à¤¯à¤¾à¤‚ना सà¥â€à¤µà¤¤:ला ओळखणà¥â€à¤¯à¤¾à¤¸à¤¾à¤ à¥€ वापरतात ती पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤°à¥‡ केवळ निरà¥à¤¦à¤¿à¤·à¥â€à¤Ÿ केलेलà¥â€à¤¯à¤¾ कालावधीसाठी वैध असलà¥à¤¯à¤¾à¤¨à¥‡ हे असू शकते. आपलà¥â€à¤¯à¤¾ डिवà¥â€à¤¹à¤¾à¤‡à¤¸à¤šà¥‡ घडà¥â€à¤¯à¤¾à¤³ चà¥à¤•à¥€à¤šà¥‡ असलà¥â€à¤¯à¤¾à¤®à¥à¤³à¥‡, Google Chrome ही पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤°à¥‡ सतà¥à¤¯à¤¾à¤ªà¤¿à¤¤ करू शकत नाही.</translation>
<translation id="2972581237482394796">&amp;पà¥à¤¨à¥à¤¹à¤¾ करा</translation>
<translation id="2985306909656435243">सकà¥à¤·à¤® केलà¥â€à¤¯à¤¾à¤¸, Chromium जलदपणे फॉरà¥à¤® भरणà¥â€à¤¯à¤¾à¤¸à¤¾à¤ à¥€ आपलà¥â€à¤¯à¤¾ कारà¥à¤¡à¤šà¥€ à¤à¤• पà¥à¤°à¤¤ या डिवà¥â€à¤¹à¤¾à¤‡à¤¸à¤µà¤° संगà¥à¤°à¤¹à¤¿à¤¤ करेल.</translation>
@@ -200,7 +209,7 @@
<translation id="3041612393474885105">पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° माहिती...</translation>
<translation id="3063697135517575841">Chrome यावेळी आपलà¥à¤¯à¤¾ कारà¥à¤¡à¤šà¥€ पà¥à¤·à¥à¤Ÿà¥€ करणà¥à¤¯à¤¾à¤¤ अकà¥à¤·à¤® होते. कृपया नंतर पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा.</translation>
<translation id="3093245981617870298">आपण ऑफलाइन आहात.</translation>
-<translation id="3105172416063519923">मालमतà¥à¤¤à¤¾ ID:</translation>
+<translation id="3105172416063519923">मालमतà¥à¤¤à¤¾ आयडी:</translation>
<translation id="3109728660330352905">हे पृषà¥à¤  पाहणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ आपण पà¥à¤°à¤¾à¤§à¤¿à¤•à¥ƒà¤¤ नाही.</translation>
<translation id="31207688938192855"><ph name="BEGIN_LINK" />कनेकà¥à¤Ÿà¤¿à¤µà¥à¤¹à¤¿à¤Ÿà¥€ निदान चालवून पहा<ph name="END_LINK" />.</translation>
<translation id="3145945101586104090">पà¥à¤°à¤¤à¤¿à¤¸à¤¾à¤¦ डीकोड करणà¥à¤¯à¤¾à¤¤ अयशसà¥à¤µà¥€</translation>
@@ -233,7 +242,7 @@
<translation id="337363190475750230">तरतूद रदà¥à¤¦ केली</translation>
<translation id="3377188786107721145">धोरण विशà¥à¤²à¥‡à¤·à¤£ तà¥à¤°à¥à¤Ÿà¥€</translation>
<translation id="3380365263193509176">अजà¥à¤žà¤¾à¤¤ तà¥à¤°à¥à¤Ÿà¥€</translation>
-<translation id="3380864720620200369">कà¥à¤²à¤¾à¤¯à¤‚ट ID:</translation>
+<translation id="3380864720620200369">कà¥à¤²à¤¾à¤¯à¤‚ट आयडी:</translation>
<translation id="340013220407300675">आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ कदाचित <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> वरून आपली माहिती चोरणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करत असू शकतात (उदाहरणारà¥à¤¥, संकेतशबà¥à¤¦, संदेश किंवा कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡).</translation>
<translation id="3422472998109090673"><ph name="HOST_NAME" /> सधà¥à¤¯à¤¾ आवाकà¥à¤¯à¤¾à¤¬à¤¾à¤¹à¥‡à¤° आहे.</translation>
<translation id="3427342743765426898">&amp;संपादित करा पà¥à¤¨à¥à¤¹à¤¾ करा</translation>
@@ -244,7 +253,7 @@
<translation id="3462200631372590220">पà¥à¤°à¤—त लपवा</translation>
<translation id="3479539252931486093">हे अनपेकà¥à¤·à¤¿à¤¤ होते? <ph name="BEGIN_LINK" />आमà¥à¤¹à¤¾à¤²à¤¾ कळवा<ph name="END_LINK" /></translation>
<translation id="3479552764303398839">सधà¥à¤¯à¤¾ नाही</translation>
-<translation id="348000606199325318">कà¥à¤°à¥…श ID <ph name="CRASH_LOCAL_ID" /> (सरà¥à¤µà¥à¤¹à¤° ID: <ph name="CRASH_ID" />)</translation>
+<translation id="348000606199325318">कà¥à¤°à¥…श आयडी <ph name="CRASH_LOCAL_ID" /> (सरà¥à¤µà¥à¤¹à¤° आयडी: <ph name="CRASH_ID" />)</translation>
<translation id="3498215018399854026">याकà¥à¤·à¤£à¥€ आमà¥à¤¹à¥€ आपलà¥à¤¯à¤¾ पालकांपरà¥à¤¯à¤‚त पोहोचू शकलो नाही. कृपया पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा.</translation>
<translation id="3527085408025491307">फोलà¥à¤¡à¤°</translation>
<translation id="3528171143076753409">सरà¥à¤µà¥à¤¹à¤°à¤šà¥‡ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° विशà¥à¤µà¤¾à¤¸à¤¨à¥€à¤¯ नाही.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">तपशील लपवा</translation>
<translation id="3587482841069643663">सरà¥à¤µ</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">सरà¥à¤µà¥à¤¹à¤° TLS रीनिगोसिà¤à¤¶à¤¨ विसà¥à¤¤à¤¾à¤°à¤¾à¤¸ समरà¥à¤¥à¤¨ देत नाही.</translation>
<translation id="36224234498066874">बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग डेटा साफ करा...</translation>
<translation id="362276910939193118">पूरà¥à¤£ इतिहास दरà¥à¤¶à¤µà¤¾</translation>
<translation id="3623476034248543066">मूलà¥à¤¯ दरà¥à¤¶à¤µà¤¾</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">आपण हे वारंवार पहात असलà¥à¤¯à¤¾à¤¸, हे वापरून पहा <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">पà¥à¤¨à¤°à¤¾à¤µà¥ƒà¤¤à¥à¤¤à¥€</translation>
<translation id="3678029195006412963">विनंती सà¥à¤µà¤¾à¤•à¥à¤·à¤°à¥€à¤•à¥ƒà¤¤ करणे शकà¥à¤¯ à¤à¤¾à¤²à¥‡ नाही</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">परवाने संपà¥à¤·à¥à¤Ÿà¤¾à¤¤</translation>
<translation id="3714780639079136834">मोबाइल डेटा किंवा Wi-Fi चालू करणे</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />पà¥à¤°à¥‰à¤•à¥à¤¸à¥€, फायरवॉल आणि DNS कॉनà¥à¤«à¤¿à¤—रेशन तपासणे<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">आपलà¥à¤¯à¤¾à¤²à¤¾ आपलà¥à¤¯à¤¾ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤¤à¥‡à¤šà¥à¤¯à¤¾ जोखमी समजत असलà¥à¤¯à¤¾à¤¸, धोकादायक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® काढणà¥à¤¯à¤¾à¤ªà¥‚रà¥à¤µà¥€ आपण <ph name="BEGIN_LINK" />या असà¥à¤°à¤•à¥à¤·à¤¿à¤¤ साइटला भेट देऊ शकता<ph name="END_LINK" />.</translation>
<translation id="3739623965217189342">आपण कॉपी केलेलà¥à¤¯à¤¾à¤šà¤¾ दà¥à¤µà¤¾ जोडा</translation>
<translation id="375403751935624634">सरà¥à¤µà¥à¤¹à¤° तà¥à¤°à¥à¤Ÿà¥€à¤®à¥à¤³à¥‡ भाषांतर अयशसà¥à¤µà¥€ à¤à¤¾à¤²à¤¾.</translation>
<translation id="3759461132968374835">आपण अलीकडे कोणतेही कà¥à¤°à¥…श नोंदवले नाहीत. कà¥à¤°à¥…श नोंदवणे अकà¥à¤·à¤® असताना à¤à¤¾à¤²à¥‡à¤²à¥‡ कà¥à¤°à¥…श येथे दिसून येणार नाहीत.</translation>
-<translation id="3788090790273268753">या साइटसाठी असलेले पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° 2016 मधà¥â€à¤¯à¥‡ कालबाहà¥à¤¯ होते आणि पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° शà¥à¤°à¥ƒà¤‚खलेमधà¥â€à¤¯à¥‡ SHA-1 वापरून सà¥à¤µà¤¾à¤•à¥à¤·à¤°à¥€ केलेले पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° असते.</translation>
<translation id="382518646247711829">आपण पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¥à¤¹à¤° वापरत असलà¥à¤¯à¤¾à¤¸...</translation>
<translation id="3828924085048779000">रिकà¥à¤¤ सांकेतिक वाकà¥à¤¯à¤¾à¤‚शाची परवानगी नाही.</translation>
<translation id="3845539888601087042">आपलà¥à¤¯à¤¾ साइन-इन केलेलà¥à¤¯à¤¾ डिवà¥à¤¹à¤¾à¤‡à¤¸à¥‡à¤¸ वरील इतिहास दरà¥à¤¶à¤µà¤¿à¤¤ आहे. <ph name="BEGIN_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">की "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">कà¥à¤²à¤¾à¤¯à¤‚ट आणि सरà¥à¤µà¥à¤¹à¤° à¤à¤• सामानà¥à¤¯ SSL पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤² आवृतà¥à¤¤à¥€ किंवा सायफर संचाचे समरà¥à¤¥à¤¨ करीत नाही.</translation>
<translation id="4079302484614802869">पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ कॉनà¥à¤«à¤¿à¤—रेशन .pac सà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ URL वापरणà¥â€à¤¯à¤¾à¤¸ सेट करणà¥â€à¤¯à¤¾à¤¤ आले आहे, निशà¥à¤šà¤¿à¤¤ पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ सरà¥à¤µà¥à¤¹à¤° नवà¥à¤¹à¥‡.</translation>
+<translation id="4098354747657067197">भà¥à¤°à¤¾à¤®à¤• साइट पà¥à¤¢à¥‡ आहे</translation>
<translation id="4103249731201008433">डिवà¥à¤¹à¤¾à¤‡à¤¸ सिरीयल कà¥à¤°à¤®à¤¾à¤‚क अवैध आहे</translation>
<translation id="4103763322291513355">आपलà¥à¤¯à¤¾ सिसà¥à¤Ÿà¥€à¤® पà¥à¤°à¤¶à¤¾à¤¸à¤•à¤¾à¤¦à¥à¤µà¤¾à¤°à¥‡ पà¥à¤°à¤µà¤°à¥à¤¤à¤¿à¤¤ काळà¥à¤¯à¤¾à¤¸à¥‚चीतील URLs आणि अनà¥à¤¯ धोरणांची सूची पाहणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ &lt;strong&gt;chrome://policy&lt;/strong&gt; ला भेट दà¥à¤¯à¤¾.</translation>
<translation id="4110615724604346410">हा सरà¥à¤µà¥à¤¹à¤° हे <ph name="DOMAIN" /> असलà¥à¤¯à¤¾à¤šà¥‡ सिदà¥à¤§ करू शकला नाही; याचà¥à¤¯à¤¾ सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤°à¤¾à¤®à¤§à¥à¤¯à¥‡ तà¥à¤°à¥à¤Ÿà¥€ आहेत. हे कदाचित à¤à¤•à¤¾ चà¥à¤•à¥€à¤šà¥à¤¯à¤¾ कॉनà¥à¤«à¤¿à¤—रेशनमà¥à¤³à¥‡ किंवा आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¨à¥‡ आपले कनेकà¥à¤¶à¤¨ आंतरखंडित केलà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ à¤à¤¾à¤²à¥‡ असू शकते. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{काहीही नाही}=1{1 अâ€à¥…प ($1)}=2{2 अâ€à¥…पà¥à¤¸ ($1, $2)}one{# अâ€à¥…पà¥à¤¸ ($1, $2, $3)}other{# अâ€à¥…पà¥à¤¸ ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">कà¥à¤°à¥…श होते</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />नेटवरà¥à¤• निदान चालवून पहा<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">या साइटवरील आपले कनेकà¥à¤¶à¤¨ पूरà¥à¤£à¤ªà¤£à¥‡ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नाही</translation>
<translation id="4250680216510889253">नाही</translation>
<translation id="425582637250725228">आपण केलेले बदल कदाचित जतन केले जाणार नाहीत.</translation>
<translation id="4258748452823770588">खराब सà¥à¤µà¤¾à¤•à¥à¤·à¤°à¥€</translation>
<translation id="4269787794583293679">(वापरकरà¥à¤¤à¤¾à¤¨à¤¾à¤µ नाही)</translation>
+<translation id="4280429058323657511">, कालबाहà¥à¤¯à¤¤à¤¾ <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚गला <ph name="SITE" /> वर <ph name="BEGIN_LINK" />हानिकारक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® आढळले<ph name="END_LINK" /> <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">पालक सूचना</translation>
<translation id="4304224509867189079">लॉग इन</translation>
<translation id="432290197980158659">सरà¥à¤µà¥à¤¹à¤°à¤¨à¥‡ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° सादर केले जे अंगभूत अपेकà¥à¤·à¤¾à¤‚शी जà¥à¤³à¤¤ नाही. या अपेकà¥à¤·à¤¾ आपलà¥à¤¯à¤¾à¤²à¤¾ संरकà¥à¤·à¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ विशिषà¥à¤Ÿ, उचà¥à¤š-सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤¤à¥‡à¤šà¥à¤¯à¤¾ वेबसाइटसाठी समाविषà¥à¤Ÿ केलà¥à¤¯à¤¾ आहेत. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">लेख शोधणà¥à¤¯à¤¾à¤¤ अयशसà¥à¤µà¥€</translation>
+<translation id="4326324639298822553">आपली कालबाहà¥à¤¯à¤¤à¤¾ तारीख तपासा आणि पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा</translation>
<translation id="4331708818696583467">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नाही</translation>
+<translation id="4356973930735388585">या साइट वरील आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ आपली माहिती (उदाहरणारà¥à¤¥, फोटो, संकेतशबà¥à¤¦, संदेश आणि कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) चोरणारे किंवा हटविणारे धोकादायक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® आपलà¥à¤¯à¤¾ संगणकावर सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणà¥â€à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करू शकतात.</translation>
<translation id="4372948949327679948">अपेकà¥à¤·à¤¿à¤¤ <ph name="VALUE_TYPE" /> मूलà¥à¤¯.</translation>
<translation id="4381091992796011497">वापरकरà¥à¤¤à¤¾ नाव:</translation>
<translation id="4394049700291259645">अकà¥à¤·à¤® करा</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">आपण <ph name="DOMAIN" /> येथे पोहोचणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ केला, परंतॠसरà¥à¤µà¥à¤¹à¤°à¤¨à¥‡ अवैध पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° सादर केले. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">आपले <ph name="DOMAIN" /> वरील कनेकà¥à¤¶à¤¨ आधà¥à¤¨à¤¿à¤• सायफर सूट वापरून कूटबदà¥à¤§ केलेले आहे.</translation>
<translation id="4594403342090139922">&amp;हटवा पूरà¥à¤µà¤µà¤¤ करा</translation>
+<translation id="4619615317237390068">अनà¥à¤¯ डिवà¥à¤¹à¤¾à¤‡à¤¸à¥‡à¤¸à¤®à¤§à¥€à¤² टॅब</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">आपण à¤à¤• विसà¥à¤¤à¤¾à¤° पृषà¥à¤  पाहत आहात.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">धोरणे रीलोड करा</translation>
<translation id="4728558894243024398">पà¥à¤²à¥…टफॉरà¥à¤®</translation>
<translation id="4744603770635761495">कारà¥à¤¯à¤µà¤¾à¤¹à¥€à¤¯à¥‹à¤—à¥à¤¯ पथ</translation>
+<translation id="4750917950439032686">आपली माहिती (उदाहरणारà¥à¤¥, संकेतशबà¥à¤¦ किंवा कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡ कà¥à¤°à¤®à¤¾à¤‚क) या साइटवर पाठविली जाते तेवà¥à¤¹à¤¾ ती खाजगी राहते.</translation>
<translation id="4756388243121344051">&amp;इतिहास</translation>
+<translation id="4759118997339041434">पेमेंट सà¥à¤µà¤¯à¤‚भरण अकà¥à¤·à¤® केले</translation>
<translation id="4764776831041365478"><ph name="URL" /> येथील वेबपृषà¥à¤  कदाचित तातà¥à¤ªà¥à¤°à¤¤à¥‡ बंद आहे किंवा ते कदाचित कायमचे नवीन वेब पतà¥à¤¤à¥à¤¯à¤¾à¤µà¤° हलवले आहे.</translation>
<translation id="4771973620359291008">à¤à¤• अजà¥à¤žà¤¾à¤¤ तà¥à¤°à¥à¤Ÿà¥€ आली आहे.</translation>
<translation id="4800132727771399293">आपली कालबाहà¥à¤¯à¤¤à¤¾ तारीख आणि CVC तपासा आणि पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">नेटवरà¥à¤• तà¥à¤°à¥à¤Ÿà¥€</translation>
<translation id="4816492930507672669">पृषà¥â€à¤ à¤¾à¤¨à¥à¤°à¥à¤ª करा</translation>
<translation id="4850886885716139402">पहा</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{आणि 1 आणखी वेब पृषà¥à¤ }one{आणि # आणखी वेब पृषà¥â€à¤ }other{आणि # आणखी वेब पृषà¥à¤ à¥‡}}</translation>
<translation id="4923417429809017348">हे पृषà¥à¤  अजà¥à¤žà¤¾à¤¤ भाषेतून <ph name="LANGUAGE_LANGUAGE" /> मधà¥à¤¯à¥‡ अनà¥à¤µà¤¾à¤¦à¤¿à¤¤ करणà¥à¤¯à¤¾à¤¤ आले</translation>
+<translation id="4923459931733593730">देयक</translation>
<translation id="4926049483395192435">निरà¥à¤¦à¤¿à¤·à¥â€à¤Ÿ केले जाणे आवशà¥â€à¤¯à¤• आहे.</translation>
<translation id="495170559598752135">कà¥à¤°à¤¿à¤¯à¤¾</translation>
<translation id="4958444002117714549">सूची विसà¥à¤¤à¥ƒà¤¤ करा</translation>
@@ -395,16 +415,17 @@
<translation id="5181140330217080051">डाउनलोड करीत आहे</translation>
<translation id="5190835502935405962">बà¥à¤•à¤®à¤¾à¤°à¥à¤• बार</translation>
<translation id="5199729219167945352">पà¥à¤°à¤¯à¥‹à¤—</translation>
-<translation id="5199841536747119669">आपलà¥à¤¯à¤¾ सूचना येथे दिसतील</translation>
-<translation id="5251803541071282808">मेघ</translation>
+<translation id="5251803541071282808">कà¥à¤²à¤¾à¤‰à¤¡</translation>
<translation id="5277279256032773186">कारà¥à¤¯à¤¸à¥à¤¥à¤¾à¤¨à¥€ Chrome वापरत आहात? वà¥à¤¯à¤µà¤¸à¤¾à¤¯ तà¥à¤¯à¤¾à¤‚चà¥à¤¯à¤¾ करà¥à¤®à¤šà¤¾à¤°à¥â€à¤¯à¤¾à¤‚ंसाठी Chrome सेटिंगà¥à¤œ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करू शकतात. अधिक जाणनू घà¥à¤¯à¤¾</translation>
<translation id="5299298092464848405">धोरण विशà¥à¤²à¥‡à¤·à¤¿à¤¤ करताना तà¥à¤°à¥à¤Ÿà¥€</translation>
<translation id="5300589172476337783">दरà¥à¤¶à¤µà¤¾</translation>
<translation id="5308689395849655368">कà¥à¤°à¥…श अहवाल अकà¥à¤·à¤® केला गेला आहे.</translation>
<translation id="5317780077021120954">जतन करा</translation>
<translation id="5327248766486351172">नाव</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> वरील आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ सॉफà¥â€à¤Ÿà¤µà¥‡à¤…र सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणे किंवा आपली वैवकà¥à¤¤à¤¿à¤• माहिती (उदाहरणारà¥à¤¥, संकेतशबà¥à¤¦, फोन नंबर किंवा कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) उघड करणे यासारखे काहीतरी धोकादायक करणà¥â€à¤¯à¤¾à¤®à¤§à¥â€à¤¯à¥‡ आपलà¥â€à¤¯à¤¾à¤²à¤¾ यà¥à¤•à¥à¤¤à¥€à¤¨à¥‡ गà¥à¤‚तवू शकतात.</translation>
<translation id="5359637492792381994">हा सरà¥à¤µà¥à¤¹à¤° <ph name="DOMAIN" /> हे असलà¥à¤¯à¤¾à¤šà¥‡ सिदà¥à¤§ करू शकला नाही; याचे सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° यावेळी वैध नाही हे कदाचित à¤à¤•à¤¾ चà¥à¤•à¥€à¤šà¥à¤¯à¤¾ कॉनà¥à¤«à¤¿à¤—रेशनमà¥à¤³à¥‡ किंवा आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¨à¥‡ आपले कनेकà¥à¤¶à¤¨ आंतरखंडित केलà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ à¤à¤¾à¤²à¥‡ असू शकते. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">धोरण सेटिंगà¥à¤œ संचयित करणà¥à¤¯à¤¾à¤¤ अयशसà¥à¤µà¥€</translation>
+<translation id="5386426401304769735">या साइटसाठी असलेलà¥à¤¯à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° शà¥à¤°à¥ƒà¤‚खलेत SHA-1 वापरून सà¥à¤µà¤¾à¤•à¥à¤·à¤°à¥€ केलेले पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° असते.</translation>
<translation id="5421136146218899937">बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग डेटा साफ करा...</translation>
<translation id="5430298929874300616">बà¥à¤•à¤®à¤¾à¤°à¥à¤• काढा</translation>
<translation id="5431657950005405462">आपली फाईल आढळली नाही</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> वरील à¤à¤®à¥à¤¬à¥‡à¤¡ केलेले पृषà¥à¤  मà¥à¤¹à¤£à¤¤à¥‡:</translation>
<translation id="5556459405103347317">रीलोड करा</translation>
<translation id="5565735124758917034">सकà¥à¤°à¤¿à¤¯</translation>
+<translation id="5572851009514199876">कृपया पà¥à¤°à¤¾à¤°à¤‚भ करा आणि Chrome मधà¥â€à¤¯à¥‡ साइन इन करा जेणेकरून आपलà¥à¤¯à¤¾à¤²à¤¾ या साइटमधà¥à¤¯à¥‡ पà¥à¤°à¤µà¥‡à¤¶ करणà¥â€à¤¯à¤¾à¤šà¥€ अनà¥à¤®à¤¤à¥€ आहे किंवा नाही ते Chrome तपासू शकेल.</translation>
+<translation id="5580958916614886209">आपला कालबाहà¥à¤¯à¤¤à¤¾ महिना तपासा आणि पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा</translation>
<translation id="560412284261940334">वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ समरà¥à¤¥à¤¿à¤¤ नाही</translation>
<translation id="5610142619324316209">कनेकà¥à¤¶à¤¨ तपासणे</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> नी आपलà¥â€à¤¯à¤¾à¤²à¤¾ अनेक वेळा पà¥à¤¨à¤°à¥à¤¨à¤¿à¤°à¥à¤¦à¥‡à¤¶à¤¿à¤¤ केले.</translation>
<translation id="5622887735448669177">आपण ही साइट सोडू इचà¥à¤›à¤¿à¤¤à¤¾?</translation>
<translation id="5629630648637658800">धोरण सेटिंगà¥à¤œ लोड करणà¥à¤¯à¤¾à¤¤ अयशसà¥à¤µà¥€</translation>
<translation id="5631439013527180824">अवैध डिवà¥à¤¹à¤¾à¤‡à¤¸ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ टोकन</translation>
+<translation id="5669703222995421982">वैयकà¥à¤¤à¥€à¤•à¥ƒà¤¤ सामगà¥à¤°à¥€ मिळवा</translation>
+<translation id="5675650730144413517">हे पृषà¥à¤  कारà¥à¤¯ करीत नाही</translation>
<translation id="5677928146339483299">अवरोधित</translation>
<translation id="5694783966845939798">आपण <ph name="DOMAIN" /> वर पोहोचणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ केला परंतॠसरà¥à¤µà¥à¤¹à¤°à¤¨à¥‡ à¤à¤–ादà¥à¤¯à¤¾ कमकà¥à¤µà¤¤ सà¥à¤µà¤¾à¤•à¥à¤·à¤°à¥€ अलà¥à¤—ोरिदम (जसे की SHA-1) चा वापर करून सà¥à¤µà¤¾à¤•à¥à¤·à¤°à¥€à¤•à¥ƒà¤¤ केलेले पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° सादर केले. याचा अरà¥à¤¥ असा आहे की सरà¥à¤µà¥à¤¹à¤°à¤¨à¥‡ सादर केलेले सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤¤à¤¾ कà¥à¤°à¥‡à¤¡à¥‡à¤¨à¥à¤¶à¤¿à¤¯à¤² बनावटी असू शकतात आणि हा सरà¥à¤µà¥à¤¹à¤° आपलà¥à¤¯à¤¾à¤²à¤¾ अपेकà¥à¤·à¤¿à¤¤ असलेला सरà¥à¤µà¥à¤¹à¤° नसू शकतो (आपण कदाचित à¤à¤–ादà¥à¤¯à¤¾ आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¸à¤¹ संपà¥à¤°à¥‡à¤·à¤£ करीत आहात). <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">या वेबसाइटची ओळख सतà¥à¤¯à¤¾à¤ªà¤¿à¤¤ केली गेली नाही.</translation>
<translation id="5720705177508910913">वरà¥à¤¤à¤®à¤¾à¤¨ वापरकरà¥à¤¤à¤¾</translation>
-<translation id="572328651809341494">अलीकडील टॅब</translation>
<translation id="5732392974455271431">आपले पालक आपलà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ ती अनावरोधित करू शकतात</translation>
<translation id="5784606427469807560">आपलà¥à¤¯à¤¾ कारà¥à¤¡à¤šà¥€ पà¥à¤·à¥à¤Ÿà¥€ करताना समसà¥à¤¯à¤¾ आली. आपले इंटरनेट कनेकà¥à¤¶à¤¨ तपासा आणि पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा.</translation>
<translation id="5785756445106461925">पà¥à¤¢à¥‡, या पृषà¥à¤ à¤¾à¤¤ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नसलेली इतर संसाधने समाविषà¥à¤Ÿ आहेत. ही संसाधने संकà¥à¤°à¤®à¤£à¤¾à¤¤ असताना इतरांदà¥à¤µà¤¾à¤°à¥‡ पाहिली जाऊ शकतात आणि पृषà¥à¤ à¤¾à¤šà¥‡ सà¥à¤µà¤°à¥‚प बदलणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¦à¥à¤µà¤¾à¤°à¥‡ सà¥à¤§à¤¾à¤°à¤¿à¤¤ केली जाऊ शकतात.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">आपण <ph name="DOMAIN" /> वर पोहोचणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ केला, परंतॠसरà¥à¤µà¥à¤¹à¤°à¤¨à¥‡ à¤à¤• कमकà¥à¤µà¤¤ की असलेले पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° सादर केले. आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¨à¥‡ खाजगी की तोडलेली असू शकते आणि सरà¥à¤µà¥à¤¹à¤° हे आपलà¥à¤¯à¤¾à¤²à¤¾ अपेकà¥à¤·à¤¿à¤¤ असणारे सरà¥à¤µà¥à¤¹à¤° नसू शकते (आपण कदाचित आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¶à¥€ संपà¥à¤°à¥‡à¤·à¤£ करत असाल) <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">नवीन फोलà¥à¤¡à¤°</translation>
<translation id="5869405914158311789">या साइटवर पोहचणे शकà¥à¤¯ नाही</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">कारà¥à¤¡à¤šà¤¾ हा पà¥à¤°à¤•à¤¾à¤° या वà¥à¤¯à¤¾à¤ªà¤¾à¤°à¥â€à¤¯à¤¾à¤¸à¤¾à¤ à¥€ Google Payments दà¥à¤µà¤¾à¤°à¥‡ समरà¥à¤¥à¤¿à¤¤ नाही. कृपया à¤à¤• भिनà¥à¤¨ कारà¥à¤¡ निवडा.</translation>
<translation id="59174027418879706">सकà¥à¤·à¤® केलेले</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="5966707198760109579">आठवडा</translation>
<translation id="5967867314010545767">इतिहासातून काढा</translation>
<translation id="5975083100439434680">à¤à¥‚म कमी करा</translation>
@@ -472,8 +498,11 @@
<translation id="614940544461990577">हे करून पहा:</translation>
<translation id="6151417162996330722">सरà¥à¤µà¥à¤¹à¤° पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤°à¤¾à¤¸ वैधता कालावधी आहे जो खूप मोठा आहे.</translation>
<translation id="6165508094623778733">अधिक जाणून घà¥à¤¯à¤¾</translation>
+<translation id="6177128806592000436">या साइटवरील आपले कनेकà¥à¤¶à¤¨ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नाही</translation>
<translation id="6203231073485539293">आपले इंटरनेट कनेकà¥à¤¶à¤¨ तपासा</translation>
<translation id="6218753634732582820">Chromium वरून पतà¥à¤¤à¤¾ काढायचा?</translation>
+<translation id="6251924700383757765">गोपनीयता धोरण</translation>
+<translation id="625755898061068298">आपण या साइटसाठी सà¥à¤°à¤•à¥à¤·à¤¾ चेतावणी अकà¥à¤·à¤® करणे निवडले आहे.</translation>
<translation id="6259156558325130047">&amp;पà¥à¤¨à¤°à¥à¤•à¥à¤°à¤®à¤¿à¤¤ करा पà¥à¤¨à¥à¤¹à¤¾ करा</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="6264485186158353794">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤¤à¥‡à¤•à¤¡à¥‡ परत</translation>
@@ -482,6 +511,7 @@
<translation id="6305205051461490394"><ph name="URL" /> आवाकà¥à¤¯à¤¾à¤¬à¤¾à¤¹à¥‡à¤° आहे.</translation>
<translation id="6321917430147971392">आपलà¥à¤¯à¤¾ DNS सेटिंगà¥à¤œ तपासा</translation>
<translation id="6328639280570009161">नेटवरà¥à¤• पूरà¥à¤µà¤¾à¤¨à¥à¤®à¤¾à¤¨ अकà¥à¤·à¤® करून पहा</translation>
+<translation id="6328786501058569169">ही साइट फसवी आहे</translation>
<translation id="6337534724793800597">धोरणे नावानà¥à¤¸à¤¾à¤° फिलà¥à¤Ÿà¤° करा</translation>
<translation id="6342069812937806050">आतà¥à¤¤à¤¾à¤š</translation>
<translation id="6345221851280129312">अजà¥à¤žà¤¾à¤¤ आकार</translation>
@@ -490,6 +520,8 @@
<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="6404511346730675251">बà¥à¤•à¤®à¤¾à¤°à¥à¤• संपादित करा</translation>
+<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> साठी कालबाहà¥à¤¯à¤¤à¤¾ तारीख आणि CVC पà¥à¤°à¤µà¤¿à¤·à¥â€à¤Ÿ करा</translation>
<translation id="6414888972213066896">या साइटला भेट देणे ठीक आहे का ते आपण आपलà¥â€à¤¯à¤¾ पालकास विचारले</translation>
<translation id="6416403317709441254">Chromium पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ करू शकत नसलेले न समजणारे कà¥à¤°à¥‡à¤¡à¥‡à¤¨à¥à¤¶à¤¿à¤¯à¤² वेबसाइटने पाठविलà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ आपण आतà¥à¤¤à¤¾ <ph name="SITE" /> ला भेट देऊ शकत नाही. नेटवरà¥à¤• तà¥à¤°à¥à¤Ÿà¥€ आणि आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ सामानà¥à¤¯à¤¤à¤ƒ तातà¥à¤ªà¥à¤°à¤¤à¥‡ असतात, यामà¥à¤³à¥‡ हे पृषà¥à¤  कदाचित नंतर कारà¥à¤¯ करेल. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° मागे घेतले की नाही हे तपासणà¥à¤¯à¤¾à¤¤ अकà¥à¤·à¤®.</translation>
@@ -499,10 +531,12 @@
<translation id="6458467102616083041">धोरणानà¥à¤¸à¤¾à¤° डीफॉलà¥à¤Ÿ शोध अकà¥à¤·à¤® केलà¥à¤¯à¤¾à¤¨à¥‡ दà¥à¤°à¥à¤²à¤•à¥à¤· करणà¥â€à¤¯à¤¾à¤¤ आले.</translation>
<translation id="6462969404041126431">हा सरà¥à¤µà¥à¤¹à¤° <ph name="DOMAIN" /> हे असलà¥à¤¯à¤¾à¤šà¥‡ सिदà¥à¤§ करू शकला नाही; याचे सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° निरसà¥à¤¤ केले असावे. हे कदाचित à¤à¤•à¤¾ चà¥à¤•à¥€à¤šà¥à¤¯à¤¾ कॉनà¥à¤«à¤¿à¤—रेशनमà¥à¤³à¥‡ किंवा आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¨à¥‡ आपले कनेकà¥à¤¶à¤¨ आंतरखंडित केलà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ à¤à¤¾à¤²à¥‡ असू शकते. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">डिवà¥à¤¹à¤¾à¤‡à¤¸ धोरणे</translation>
+<translation id="6477321094435799029">Chrome ला या पृषà¥â€à¤ à¤¾à¤µà¤° असमानà¥à¤¯ कोड सापडला आहे आणि आपली वैयकà¥à¤¤à¤¿à¤• माहिती (उदा, संकेतशबà¥à¤¦, फोन नंबर आणि कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) संरकà¥à¤·à¤¿à¤¤ करणà¥â€à¤¯à¤¾à¤¸à¤¾à¤ à¥€ अवरोधित केला आहे.</translation>
<translation id="6489534406876378309">कà¥à¤°à¥…श अपलोड करणे पà¥à¤°à¤¾à¤°à¤‚भ करा</translation>
<translation id="6529602333819889595">&amp;पà¥à¤¨à¥à¤¹à¤¾ करा हटवा</translation>
<translation id="6534179046333460208">वासà¥à¤¤à¤µà¤¿à¤• वेब सूचना</translation>
<translation id="6550675742724504774">परà¥à¤¯à¤¾à¤¯</translation>
+<translation id="6556239504065605927">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ कनेकà¥à¤¶à¤¨</translation>
<translation id="6563469144985748109">आपलà¥à¤¯à¤¾ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤•à¤¾à¤¨à¥‡ अदà¥à¤¯à¤¾à¤ª ती मंजूर केली नाही</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" /> पेकà¥à¤·à¤¾ कमी</translation>
<translation id="6596325263575161958">कूटबदà¥à¤§à¤¤à¤¾ परà¥à¤¯à¤¾à¤¯</translation>
@@ -511,7 +545,6 @@
<translation id="6644283850729428850">हे धोरण नापसंत आहे.</translation>
<translation id="6652240803263749613">हा सरà¥à¤µà¥à¤¹à¤° <ph name="DOMAIN" /> हे असलà¥à¤¯à¤¾à¤šà¥‡ सिदà¥à¤§ करू शकला नाही; याचे सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° आपलà¥à¤¯à¤¾ संगणकाचà¥à¤¯à¤¾ ऑपरेटिंग पà¥à¤°à¤£à¤¾à¤²à¥€à¤¦à¥à¤µà¤¾à¤°à¥‡ विशà¥à¤µà¤¸à¤¨à¥€à¤¯ नाही; हे कदाचित à¤à¤•à¤¾ चà¥à¤•à¥€à¤šà¥à¤¯à¤¾ कॉनà¥à¤«à¤¿à¤—रेशनमà¥à¤³à¥‡ किंवा आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¨à¥‡ आपले कनेकà¥à¤¶à¤¨ आंतरखंडित केलà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ à¤à¤¾à¤²à¥‡ असू शकते. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">फोलà¥à¤¡à¤° संपादित करा</translation>
-<translation id="6660210980321319655">सà¥à¤µà¤¯à¤‚चलितपणे <ph name="CRASH_TIME" /> वाजता अहवाल दिला</translation>
<translation id="6671697161687535275">Chromium वरून फॉरà¥à¤® सूचना काढायचà¥à¤¯à¤¾?</translation>
<translation id="6685834062052613830">साइन आउट करा आणि सेटअप पूरà¥à¤£ करा</translation>
<translation id="6710213216561001401">मागील</translation>
@@ -523,6 +556,7 @@
<translation id="6753269504797312559">धोरण मूलà¥à¤¯</translation>
<translation id="6757797048963528358">आपले डिवà¥à¤¹à¤¾à¤‡à¤¸ निषà¥à¤•à¥à¤°à¥€à¤¯ à¤à¤¾à¤²à¥‡.</translation>
<translation id="6778737459546443941">आपलà¥à¤¯à¤¾ पालकाने अदà¥à¤¯à¤¾à¤ª ती मंजूर केली नाही</translation>
+<translation id="6810899417690483278">सानà¥à¤•à¥‚लीकरण आयडी</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" /> येथील वेबपृषà¥à¤  सधà¥à¤¯à¤¾ अनà¥à¤ªà¤²à¤¬à¥à¤§ आहे. ते ओवà¥à¤¹à¤°à¤²à¥‹à¤¡ असावे किंवा देखरेखीसाठी डाउन असावे.</translation>
<translation id="6831043979455480757">भाषांतर करा</translation>
@@ -543,6 +577,8 @@
<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>
+<translation id="7064851114919012435">संपरà¥à¤• माहिती</translation>
+<translation id="7079718277001814089">या साइटमधà¥à¤¯à¥‡ मालवेयर आहे</translation>
<translation id="7087282848513945231">परगणा</translation>
<translation id="7088615885725309056">थोडा जà¥à¤¨à¤¾</translation>
<translation id="7090678807593890770">Google वर <ph name="LINK" /> शोधा</translation>
@@ -557,6 +593,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> नी सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤¤à¤¾ मानकांचे पालन केले नाही.</translation>
<translation id="721197778055552897">या समसà¥à¤¯à¥‡à¤¬à¤¦à¥à¤¦à¤² <ph name="BEGIN_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LINK" />.</translation>
<translation id="7219179957768738017">कनेकà¥à¤¶à¤¨ <ph name="SSL_VERSION" /> वापरते.</translation>
+<translation id="724691107663265825">साइटमधà¥à¤¯à¥‡ पà¥à¤¢à¥‡ मालवेअर आहे</translation>
<translation id="724975217298816891">आपले कारà¥à¤¡ तपशील अदà¥à¤¯à¤¤à¤¨à¤¿à¤¤ करणà¥â€à¤¯à¤¾à¤¸à¤¾à¤ à¥€ <ph name="CREDIT_CARD" /> करिता कालबाहà¥à¤¯à¤¤à¤¾ तारीख आणि CVC पà¥à¤°à¤µà¤¿à¤·à¥â€à¤Ÿ करा. आपण पà¥à¤·à¥à¤Ÿà¥€ केलà¥à¤¯à¤¾à¤µà¤°, आपले कारà¥à¤¡ तपशील या साइटसह सामायिक केले जातील.</translation>
<translation id="725866823122871198">आपलà¥à¤¯à¤¾ संगणकाची तारीख आणि वेळ (<ph name="DATE_AND_TIME" />) चà¥à¤•à¥€à¤šà¥€ असलà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> सह खाजगी कनेकà¥à¤¶à¤¨ सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ केले जाऊ शकत नाही.</translation>
<translation id="7269802741830436641">या वेबपृषà¥à¤ à¤¾à¤²à¤¾ पà¥à¤¨à¤°à¥à¤¨à¤¿à¤°à¥à¤¦à¥‡à¤¶à¤¿à¤¤ पळवाट आहे</translation>
@@ -586,7 +623,7 @@
<translation id="7469372306589899959">कारà¥à¤¡à¤šà¥€ पà¥à¤·à¥à¤Ÿà¥€ करीत आहे</translation>
<translation id="7481312909269577407">पà¥à¤¢à¥€à¤²</translation>
<translation id="7485870689360869515">डेटा आढळला नाही.</translation>
-<translation id="7508255263130623398">परत केलेला धोरण डिवà¥à¤¹à¤¾à¤‡à¤¸ ID रिकà¥à¤¤ आहे किंवा वरà¥à¤¤à¤®à¤¾à¤¨ डिवà¥à¤¹à¤¾à¤‡à¤¸ ID शी जà¥à¤³à¤¤ नाही</translation>
+<translation id="7508255263130623398">परत केलेला धोरण डिवà¥à¤¹à¤¾à¤‡à¤¸ आयडी रिकà¥à¤¤ आहे किंवा वरà¥à¤¤à¤®à¤¾à¤¨ डिवà¥à¤¹à¤¾à¤‡à¤¸ आयडी शी जà¥à¤³à¤¤ नाही</translation>
<translation id="7514365320538308">डाउनलोड करा</translation>
<translation id="7518003948725431193">या वेबपतà¥à¤¤à¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ वेबपृषà¥à¤  आढळले नाही: <ph name="URL" /></translation>
<translation id="7535087603100972091">मूलà¥à¤¯</translation>
@@ -612,6 +649,7 @@
<translation id="7658239707568436148">रदà¥à¤¦ करा</translation>
<translation id="7667346355482952095">परत केलेले धोरण टोकन रिकà¥à¤¤ आहे किंवा वरà¥à¤¤à¤®à¤¾à¤¨ टोकनशी जà¥à¤³à¤¤ नाही</translation>
<translation id="7668654391829183341">अजà¥à¤žà¤¾à¤¤ डिवà¥à¤¹à¤¾à¤‡à¤¸</translation>
+<translation id="7669271284792375604">या साइट वरील आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ कदाचित आपलà¥à¤¯à¤¾ बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग अनà¥à¤­à¤µà¤¾à¤¸ हानी पोहोचविणारे पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® (उदाहरणारà¥à¤¥, आपले मà¥à¤–à¥à¤¯à¤ªà¥ƒà¤·à¥à¤  बदलून किंवा आपण भेट देता तà¥à¤¯à¤¾ साइटवर अतिरिकà¥à¤¤ जाहिराती दरà¥à¤¶à¤µà¥‚न) सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ करून आपली फसवणूक करणà¥â€à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करू शकतात.</translation>
<translation id="7674629440242451245">छान नवीन Chrome वैशिषà¥à¤Ÿà¥à¤¯à¤¾à¤‚मधà¥à¤¯à¥‡ सà¥à¤µà¤¾à¤°à¤¸à¥à¤¯ आहे? chrome.com/dev येथे आमचे dev चॅनेल वापरून पहा.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /> <ph name="SITE" /> (असà¥à¤°à¤•à¥à¤·à¤¿à¤¤) वर सà¥à¤°à¥ ठेवा<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">ही साइट कॅश मधून लोड करणे शकà¥à¤¯ नाही</translation>
@@ -627,22 +665,28 @@
<translation id="7800304661137206267">संदेश पà¥à¤°à¤®à¤¾à¤£à¥€à¤•à¤°à¤£à¤¾à¤¸à¤¾à¤ à¥€ <ph name="MAC" /> आणि की विनिमय तंतà¥à¤° महणून <ph name="KX" /> सह <ph name="CIPHER" /> वापरून कनेकà¥à¤¶à¤¨ कूटबदà¥à¤§ केले आहे.</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="7887683347370398519">आपले CVC तपासा आणि पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा</translation>
<translation id="7912024687060120840">फोलà¥à¤¡à¤°à¤®à¤§à¥â€à¤¯à¥‡:</translation>
<translation id="7935318582918952113">DOM डिसà¥à¤Ÿà¤¿à¤²à¤°</translation>
<translation id="7938958445268990899">सरà¥à¤µà¥à¤¹à¤°à¤šà¥‡ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° अदà¥à¤¯à¤¾à¤ª वैध नाही.</translation>
<translation id="7942349550061667556">लाल</translation>
+<translation id="7947285636476623132">आपले कालबाहà¥à¤¯à¤¤à¤¾ वरà¥à¤· तपासा आणि पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा</translation>
<translation id="7951415247503192394">(32-बिट)</translation>
<translation id="7956713633345437162">Mobile बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
<translation id="7961015016161918242">कधीही नाही</translation>
-<translation id="7962083544045318153">कà¥à¤°à¥…श ID <ph name="CRASH_LOCAL_ID" /></translation>
+<translation id="7962083544045318153">कà¥à¤°à¥…श आयडी <ph name="CRASH_LOCAL_ID" /></translation>
<translation id="7983301409776629893"> नेहमी <ph name="ORIGINAL_LANGUAGE" /> मधून <ph name="TARGET_LANGUAGE" /> मधà¥à¤¯à¥‡ भाषांतर करा</translation>
<translation id="7995512525968007366">निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ केलेले नाही</translation>
<translation id="8012647001091218357">आमà¥à¤¹à¥€ याकà¥à¤·à¤£à¥€ आपलà¥à¤¯à¤¾ पालकांपरà¥à¤¯à¤‚त पोहोचू शकलो नाही. कृपया पà¥à¤¨à¥à¤¹à¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करा.</translation>
+<translation id="8025119109950072390">या साइट वरील आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ सॉफà¥â€à¤Ÿà¤µà¥‡à¤…र सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणे किंवा आपली वैयकà¥à¤¤à¤¿à¤• माहिती (उदाहरणारà¥à¤¥, संकेतशबà¥à¤¦, फोन नंबर किंवा कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) उघड करणे यासारखे काहीतरी धोकादायक करणà¥â€à¤¯à¤¾à¤®à¤§à¥â€à¤¯à¥‡ आपलà¥â€à¤¯à¤¾à¤²à¤¾ यà¥à¤•à¥à¤¤à¥€à¤¨à¥‡ गà¥à¤‚तवू शकतात.</translation>
+<translation id="803030522067524905">Google सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग ला अलीकडे <ph name="SITE" /> वर फिशिंग आढळले. आपली फसवणूक करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ अनà¥à¤¯ वेबसाइट असलà¥à¤¯à¤¾à¤šà¥€ बतावणी फिशिंग साइट करतात. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">हे पृषà¥â€à¤  <ph name="SOURCE_LANGUAGE" /> मधà¥à¤¯à¥‡ आहे. तà¥à¤¯à¤¾à¤¸ <ph name="TARGET_LANGUAGE" /> मधà¥à¤¯à¥‡ भाषांतरीत करायचे?</translation>
+<translation id="8041089156583427627">अभिपà¥à¤°à¤¾à¤¯ पाठवा</translation>
<translation id="8088680233425245692">लेख पाहणà¥à¤¯à¤¾à¤¤ अयशसà¥à¤µà¥€.</translation>
<translation id="8089520772729574115">1 MB पेकà¥à¤·à¤¾ कमी</translation>
<translation id="8091372947890762290">सकà¥à¤°à¤¿à¤¯à¤•à¤°à¤£ सरà¥à¤µà¥à¤¹à¤°à¤µà¤° पà¥à¤°à¤²à¤‚बित आहे</translation>
@@ -652,10 +696,12 @@
<translation id="8149426793427495338">आपला संगणक निषà¥à¤•à¥à¤°à¥€à¤¯ à¤à¤¾à¤²à¤¾.</translation>
<translation id="8150722005171944719"><ph name="URL" /> येथील फाइल वाचनीय नाही. ती काढून टाकलेली, हलविलेली असू शकते किंवा फाइल परवानगà¥à¤¯à¤¾ पà¥à¤°à¤µà¥‡à¤¶ पà¥à¤°à¤¤à¤¿à¤¬à¤‚धित करत असू शकतात.</translation>
<translation id="8194797478851900357">&amp;हलवा पूरà¥à¤µà¤µà¤¤ करा</translation>
-<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" ID असलेलà¥à¤¯à¤¾ विसà¥à¤¤à¤¾à¤°à¤¾à¤¸à¤¾à¤ à¥€ अवैध अदà¥à¤¯à¤¤à¤¨ URL.</translation>
+<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" आयडी असलेलà¥à¤¯à¤¾ विसà¥à¤¤à¤¾à¤°à¤¾à¤¸à¤¾à¤ à¥€ अवैध अदà¥à¤¯à¤¤à¤¨ URL.</translation>
+<translation id="8202097416529803614">ऑरà¥à¤¡à¤° सारांश</translation>
<translation id="8218327578424803826">नियà¥à¤•à¥à¤¤ केलेले सà¥à¤¥à¤¾à¤¨:</translation>
<translation id="8225771182978767009">जà¥à¤¯à¤¾ वà¥à¤¯à¤•à¥à¤¤à¥€à¤¨à¥‡ हा संगणक सेट केला तà¥à¤¯à¤¾ वà¥à¤¯à¤•à¥à¤¤à¥€à¤¨à¥‡ ही साइट अवरोधित करणà¥à¤¯à¤¾à¤šà¥‡ निवडले आहे.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> वरील आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ सधà¥à¤¯à¤¾ कदाचित आपलà¥à¤¯à¤¾ संगणकावर आपली माहिती (उदाहरणारà¥à¤¥, फोटो, संकेतशबà¥à¤¦, संदेश आणि कà¥à¤°à¥‡à¤¡à¤¿à¤Ÿ कारà¥à¤¡) चोरणारे किंवा हटविणारे धोकादायक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करत असू शकतात.</translation>
<translation id="8241707690549784388">आपण जे पृषà¥à¤  शोधत आहत ते आपण पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ केलेली माहिती वापरत आहे. तà¥à¤¯à¤¾ पृषà¥à¤ à¤¾à¤•à¤¡à¥‡ परत गेलà¥à¤¯à¤¾à¤¸ कदाचित आपण केलेलà¥à¤¯à¤¾ कोणतà¥à¤¯à¤¾à¤¹à¥€ कà¥à¤°à¤¿à¤¯à¥‡à¤šà¥€ पà¥à¤¨à¤°à¤¾à¤µà¥ƒà¤¤à¥à¤¤à¥€ होईल. आपण सà¥à¤°à¥‚ ठेवू इचà¥à¤›à¤¿à¤¤à¤¾?</translation>
<translation id="8249320324621329438">अंतिम पà¥à¤°à¤¾à¤ªà¥à¤¤ केलेले:</translation>
<translation id="8261506727792406068">हटवा</translation>
@@ -664,11 +710,13 @@
<translation id="8294431847097064396">सà¥à¤°à¥‹à¤¤</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="8349305172487531364">बà¥à¤•à¤®à¤¾à¤°à¥à¤• बार</translation>
<translation id="8363502534493474904">विमान मोड बंद करा</translation>
<translation id="8364627913115013041">सेट केलेले नाही.</translation>
<translation id="8380941800586852976">धोकादायक</translation>
<translation id="8382348898565613901">आपण अलिकडेच भेट दिलेले बà¥à¤•à¤®à¤¾à¤°à¥à¤• येथे दिसतील</translation>
+<translation id="8398259832188219207">कà¥à¤°à¥…श अहवाल <ph name="UPLOAD_TIME" /> वाजता अपलोड केला</translation>
<translation id="8412145213513410671">कà¥à¤°à¥…श (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">आपण समान सांकेतिक वाकà¥à¤¯à¤¾à¤‚श दोनदा पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ करणे आवशà¥à¤¯à¤• आहे.</translation>
<translation id="8428213095426709021">सेटिंगà¥à¤œ</translation>
@@ -680,9 +728,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> नी पà¥à¤°à¤¤à¤¿à¤¸à¤¾à¤¦ देणà¥à¤¯à¤¾à¤¤ बराच वेळ घेतला.</translation>
<translation id="852346902619691059">हा सरà¥à¤µà¥à¤¹à¤° <ph name="DOMAIN" /> हे असलà¥à¤¯à¤¾à¤šà¥‡ सिदà¥à¤§ करू शकला नाही; याचे सà¥à¤°à¤•à¥à¤·à¤¾ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° आपलà¥à¤¯à¤¾ डिवà¥à¤¹à¤¾à¤‡à¤¸à¤šà¥à¤¯à¤¾ ऑपरेटिंग पà¥à¤°à¤£à¤¾à¤²à¥€à¤¦à¥à¤µà¤¾à¤°à¥‡ विशà¥à¤µà¤¸à¤¨à¥€à¤¯ नाही; हे कदाचित à¤à¤•à¤¾ चà¥à¤•à¥€à¤šà¥à¤¯à¤¾ कॉनà¥à¤«à¤¿à¤—रेशनमà¥à¤³à¥‡ किंवा आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¨à¥‡ आपले कनेकà¥à¤¶à¤¨ आंतरखंडित केलà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ à¤à¤¾à¤²à¥‡ असू शकते. <ph name="BEGIN_LEARN_MORE_LINK" />अधिक जाणून घà¥à¤¯à¤¾<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">या पà¥à¤°à¤•à¤¾à¤°à¤šà¥‡ कारà¥à¤¡ Google Payments दà¥à¤µà¤¾à¤°à¥‡ समरà¥à¤¥à¤¿à¤¤ नाही. कृपया à¤à¤• भिनà¥à¤¨ कारà¥à¤¡ निवडा.</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="8553075262323480129">भाषांतर करणà¥à¤¯à¤¾à¤¤ अयशसà¥à¤µà¥€ कारण पृषà¥à¤ à¤¾à¤šà¥€ भाषा निरà¥à¤§à¤¾à¤°à¤¿à¤¤ करणे शकà¥à¤¯ नाही.</translation>
<translation id="8559762987265718583">आपलà¥à¤¯à¤¾ डिवà¥à¤¹à¤¾à¤‡à¤¸à¤šà¥€ तारीख आणि वेळ (<ph name="DATE_AND_TIME" />) चà¥à¤•à¥€à¤šà¥€ असलà¥à¤¯à¤¾à¤¨à¥‡ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> सह खाजगी कनेकà¥à¤¶à¤¨ सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ केले जाऊ शकले नाही.</translation>
-<translation id="856992080682148">या साइटसाठी असलेले पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° 2017 किंवा तà¥à¤¯à¤¾à¤¨à¤‚तर कालबाहà¥à¤¯ होते आणि पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° शà¥à¤°à¥ƒà¤‚खलेमधà¥â€à¤¯à¥‡ SHA-1 वापरून सà¥à¤µà¤¾à¤•à¥à¤·à¤°à¥€ केलेले पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° असते.</translation>
<translation id="8571890674111243710"><ph name="LANGUAGE" /> मधà¥à¤¯à¥‡ पृषà¥à¤  अनà¥à¤µà¤¾à¤¦à¤¿à¤¤ करत आहे...</translation>
<translation id="859285277496340001">पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° निरसà¥à¤¤ à¤à¤¾à¤²à¥‡ आहे किंवा नाही हे तपासणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ पà¥à¤°à¤£à¤¾à¤²à¥€ निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ करत नाही.</translation>
<translation id="8620436878122366504">आपलà¥à¤¯à¤¾ पालकांनी अदà¥à¤¯à¤¾à¤ª ती मंजूर केली नाही</translation>
@@ -695,10 +743,12 @@
<translation id="8740359287975076522"><ph name="HOST_NAME" />चा &lt;abbr id="dnsDefinition"&gt;DNS पतà¥à¤¤à¤¾&lt;/abbr&gt; शोधणे शकà¥à¤¯ à¤à¤¾à¤²à¥‡ नाही. समसà¥à¤¯à¥‡à¤šà¥‡ निराकरण करीत आहे.</translation>
<translation id="8790007591277257123">&amp;पà¥à¤¨à¥à¤¹à¤¾ करा हटवा</translation>
<translation id="8798099450830957504">डीफॉलà¥à¤Ÿ</translation>
+<translation id="8800988563907321413">आपलà¥à¤¯à¤¾ जवळपासचà¥à¤¯à¤¾ सूचना येथे दिसतात</translation>
<translation id="8804164990146287819">गोपनीयता धोरण</translation>
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="8834246243508017242">संपरà¥à¤• वापरून सà¥à¤µà¤¯à¤‚-भरण सकà¥à¤·à¤® करा...</translation>
<translation id="883848425547221593">अनà¥à¤¯ बà¥à¤•à¤®à¤¾à¤°à¥à¤•</translation>
+<translation id="884264119367021077">वहनावळ पतà¥à¤¤à¤¾</translation>
<translation id="884923133447025588">कोणतीही निरसà¥à¤¤ करणà¥à¤¯à¤¾à¤šà¥€ पà¥à¤°à¤£à¤¾à¤²à¥€ आढळली नाही.</translation>
<translation id="885730110891505394">Google सह सामायिकरण</translation>
<translation id="8866481888320382733">धोरण सेटिंगà¥à¤œ विशà¥à¤²à¥‡à¤·à¤¿à¤¤ करताना तà¥à¤°à¥à¤Ÿà¥€</translation>
@@ -716,18 +766,21 @@
<translation id="8971063699422889582">सरà¥à¤µà¥à¤¹à¤°à¤šà¥‡ पà¥à¤°à¤®à¤¾à¤£à¤ªà¤¤à¥à¤° कालबाहà¥à¤¯ à¤à¤¾à¤²à¥‡ आहे.</translation>
<translation id="8987927404178983737">महिना</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">पà¥à¤¢à¥‡ असणारà¥â€à¤¯à¤¾ साइटमधà¥à¤¯à¥‡ हानिकारक पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® आहेत</translation>
<translation id="9001074447101275817">पà¥à¤°à¥‰à¤•à¥à¤¸à¥€ <ph name="DOMAIN" /> ला वापरकरà¥à¤¤à¤¾à¤¨à¤¾à¤µ आणि संकेतशबà¥à¤¦ आवशà¥à¤¯à¤• आहेत.</translation>
<translation id="901974403500617787">सिसà¥à¤Ÿà¥€à¤®-वà¥à¤¯à¤¾à¤ªà¥à¤¤ लागू होणारी धà¥à¤µà¤œà¤¾à¤‚कने केवळ मालकादà¥à¤µà¤¾à¤°à¥‡ सेट केली जाऊ शकतात: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">हे पृषà¥à¤  <ph name="TARGET_LANGUAGE" /> मधà¥à¤¯à¥‡ भाषांतरित केले गेले आहे.</translation>
+<translation id="9035022520814077154">सà¥à¤°à¤•à¥à¤·à¤¿à¤¤à¤¤à¤¾ तà¥à¤°à¥à¤Ÿà¥€</translation>
<translation id="9038649477754266430">पृषà¥à¤ à¥‡ अधिक दà¥à¤°à¥à¤¤à¤ªà¤£à¥‡ लोड करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ पूरà¥à¤µà¤¾à¤¨à¥à¤®à¤¾à¤¨ सेवेचा वापर करा</translation>
<translation id="9039213469156557790">पà¥à¤¢à¥‡, या पृषà¥à¤ à¤¾à¤¤ सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ नसलेली इतर संसाधने समाविषà¥à¤Ÿ आहेत. ही संसाधने संकà¥à¤°à¤®à¤£à¤¾à¤¤ असताना इतरांदà¥à¤µà¤¾à¤°à¥‡ पाहिली जाऊ शकतात आणि पृषà¥à¤ à¤¾à¤šà¥‡ वरà¥à¤¤à¤¨ बदलणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥à¤¯à¤¾à¤¦à¥à¤µà¤¾à¤°à¥‡ सà¥à¤§à¤¾à¤°à¤¿à¤¤ केली जाऊ शकतात.</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> वरील आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¥‡ कदाचित आपलà¥à¤¯à¤¾ बà¥à¤°à¤¾à¤‰à¤à¤¿à¤‚ग अनà¥à¤­à¤µà¤¾à¤¸ हानी पोहोचविणारे पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® (उदाहरणारà¥à¤¥, आपले मà¥à¤–à¥à¤¯à¤ªà¥ƒà¤·à¥à¤  बदलून किंवा आपण भेट देता तà¥à¤¯à¤¾ साइटवर अतिरिकà¥à¤¤ जाहिराती दरà¥à¤¶à¤µà¥‚न) सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ आपली फसवणूक पà¥à¤°à¤¯à¤¤à¥à¤¨ करू शकतात.</translation>
<translation id="9050666287014529139">सांकेतिक वाकà¥à¤¯à¤¾à¤‚श</translation>
<translation id="9065203028668620118">संपादन</translation>
<translation id="9068849894565669697">रंग निवडा</translation>
<translation id="9076283476770535406">कदाचित तिचà¥à¤¯à¤¾à¤®à¤§à¥à¤¯à¥‡ पà¥à¤°à¥Œà¤¢ सामगà¥à¤°à¥€ असू शकते</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> पृषà¥à¤  कारà¥à¤¯ करीत नाही</translation>
<translation id="9103872766612412690"><ph name="SITE" /> आपली माहिती संरकà¥à¤·à¤¿à¤¤ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ सामानà¥à¤¯à¤¤à¤ƒ कूटबदà¥à¤§à¥€à¤•à¤°à¤£ वापरते. Chromium ने यावेळी <ph name="SITE" /> शी कनेकà¥â€à¤Ÿ करणà¥â€à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ केला तेवà¥â€à¤¹à¤¾, वेबसाइटने असामानà¥à¤¯ आणि अयोगà¥à¤¯ कà¥à¤°à¥‡à¤¡à¥‡à¤¨à¥à¤¶à¤¿à¤¯à¤² परत पाठविले. à¤à¤•à¤¤à¤° आकà¥à¤°à¤®à¤£à¤•à¤°à¥à¤¤à¤¾ <ph name="SITE" /> असलà¥à¤¯à¤¾à¤šà¥€ बतावणी करणà¥à¤¯à¤¾à¤šà¤¾ पà¥à¤°à¤¯à¤¤à¥à¤¨ करतो तेवà¥â€à¤¹à¤¾ किंवा Wi-Fi साइन इन सà¥à¤•à¥à¤°à¥€à¤¨à¤¨à¥‡ कनेकà¥à¤¶à¤¨à¤®à¤§à¥à¤¯à¥‡ वà¥à¤¯à¤¤à¥à¤¯à¤¯ आणले तेवà¥â€à¤¹à¤¾ हे घडू शकते. कोणतà¥à¤¯à¤¾à¤¹à¥€ डेटाची अदलाबदल करणà¥à¤¯à¤¾à¤ªà¥‚रà¥à¤µà¥€ Chromium ने कनेकà¥à¤¶à¤¨ थांबविलà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ आपली माहिती अदà¥à¤¯à¤¾à¤ª सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ आहे.</translation>
<translation id="9137013805542155359">मूळ दरà¥à¤¶à¤µà¤¾</translation>
+<translation id="9137248913990643158">कृपया हा अॅप वापरणà¥â€à¤¯à¤¾à¤ªà¥‚रà¥à¤µà¥€ पà¥à¤°à¤¾à¤°à¤‚भ करा आणि Chrome मधà¥â€à¤¯à¥‡ साइन इन करा.</translation>
<translation id="9148507642005240123">&amp;संपादित करा पूरà¥à¤µà¤µà¤¤ करा</translation>
<translation id="9157595877708044936">सेट अप करीत आहे...</translation>
<translation id="9170848237812810038">&amp;पूरà¥à¤µà¤µà¤¤ करा</translation>
@@ -740,7 +793,6 @@
<translation id="935608979562296692">फॉरà¥à¤® साफ करा</translation>
<translation id="939736085109172342">नवीन फोलà¥â€à¤¡à¤°</translation>
<translation id="941721044073577244">आपलà¥à¤¯à¤¾à¤²à¤¾ या साइटला भेट देणà¥à¤¯à¤¾à¤šà¥€ परवानगी नाही असे दिसते</translation>
-<translation id="962701380617707048">आपले कारà¥à¤¡ तपशील अदà¥à¤¯à¤¤à¤¨à¤¿à¤¤ करणà¥â€à¤¯à¤¾à¤¸à¤¾à¤ à¥€ <ph name="CREDIT_CARD" /> करिता कालबाहà¥à¤¯à¤¤à¤¾ तारीख आणि CVC पà¥à¤°à¤µà¤¿à¤·à¥â€à¤Ÿ करा</translation>
<translation id="969892804517981540">अधिकृत बिलà¥à¤¡</translation>
<translation id="988159990683914416">विकसक बिलà¥à¤¡</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 118490f08ad..b332d0d82ea 100644
--- a/chromium/components/strings/components_strings_ms.xtb
+++ b/chromium/components/strings/components_strings_ms.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Menyambung semula kepada Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Cetak...</translation>
<translation id="1181037720776840403">Buang</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>
<translation id="1206967143813997005">Tandatangan awal tidak sah</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Sian</translation>
<translation id="1629803312968146339">Adakah anda mahu Chrome menyimpan kad ini?</translation>
<translation id="1640180200866533862">Dasar pengguna</translation>
+<translation id="1640244768702815859">Cuba <ph name="BEGIN_LINK" />lawat laman utama tapak<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Konfigurasi rangkaian tidak sah dan tidak boleh diimport.</translation>
<translation id="1644574205037202324">Sejarah</translation>
<translation id="1645368109819982629">Protokol tidak disokong</translation>
<translation id="1676269943528358898"><ph name="SITE" /> biasanya menggunakan penyulitan untuk melindungi maklumat anda. Apabila Google Chrome cuba menyambung ke <ph name="SITE" /> pada kali ini, tapak web tersebut mengembalikan bukti kelayakan yang luar biasa dan salah. Hal ini boleh berlaku apabila penyerang sedang cuba menyamar sebagai <ph name="SITE" /> atau skrin log masuk Wi-Fi telah memutuskan sambungan. Maklumat anda masih selamat kerana Google Chrome menghentikan sambungan sebelum sebarang pertukaran data berlaku.</translation>
+<translation id="168328519870909584">Penyerang yang kini berada di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin cuba memasang apl berbahaya pada peranti anda yang mencuri atau memadamkan maklumat anda (sebagai contoh, foto, kata laluan, mesej dan kad kredit).</translation>
<translation id="168841957122794586">Sijil pelayan mengandungi kunci kriptografi yang lemah.</translation>
-<translation id="1701955595840307032">Dapatkan kandungan yang dicadangkan</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Anda memerlukan kebenaran daripada <ph name="NAME" /> untuk melawat tapak ini</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Nombor halaman</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Cuba jalankan Diagnostik Rangkaian Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Sila kemas kini frasa laluan segerak anda.</translation>
+<translation id="1787142507584202372">Tab yang dibuka dipaparkan di sini</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Penyemakan Imbas Selamat Google <ph name="BEGIN_LINK" />mengesan perisian hasad<ph name="END_LINK" /> di <ph name="SITE" /> baru-baru ini. Tapak web yang lazimnya selamat, kadangkala dijangkiti perisian hasad. Kandungan hasad berasal daripada <ph name="SUBRESOURCE_HOST" />, iaitu pengedar perisian hasad yang diketahui. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Permintaan atau parameter permintaan tidak sah</translation>
+<translation id="1834321415901700177">Tapak ini mengandungi atur cara berbahaya</translation>
<translation id="1838667051080421715">Anda sedang melihat sumber halaman web.</translation>
<translation id="1871208020102129563">Proksi ditetapkan untuk menggunakan pelayan proksi tetap, bukannya URL skrip .pac.</translation>
<translation id="1883255238294161206">Runtuhkan senarai</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Klien Asli</translation>
<translation id="213826338245044447">Penanda Halaman Mudah Alih</translation>
<translation id="2148716181193084225">Hari ini</translation>
-<translation id="2149973817440762519">Edit Penanda Halaman</translation>
<translation id="2154054054215849342">Penyegerakan tidak tersedia untuk domain anda</translation>
<translation id="2166049586286450108">Akses Penuh Pentadbir</translation>
<translation id="2166378884831602661">Tapak ini tidak dapat menyediakan sambungan yang selamat</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Anda tidak boleh melawati <ph name="SITE" /> sekarang kerana tapak web ini menggunakan HSTS. Ralat rangkaian dan serangan biasanya bersifat sementara, oleh itu halaman ini mungkin akan berfungsi sebentar lagi. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Mengabaikan penanda halaman tidak sah pada indeks <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Penanda halaman lain</translation>
+<translation id="2355395290879513365">Penyerang mungkin dapat melihat imej yang anda lihat di tapak ini dan menipu anda dengan mengubah suainya.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Paparkan dasar tanpa nilai yang ditetapkan</translation>
<translation id="2396249848217231973">&amp;Buat asal pemadaman</translation>
<translation id="2455981314101692989">Halaman web ini melumpuhkan pengisian automatik untuk borang ini.</translation>
+<translation id="2460160116472764928">Penyemakan Imbas Selamat <ph name="BEGIN_LINK" />mengesan perisian hasad <ph name="END_LINK" /> di <ph name="SITE" /> baru-baru ini. Tapak web yang biasanya selamat, kadangkala dijangkiti perisian hasad. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Isi</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Jalankan Diagnostik Rangkaian<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL carian tidak sah.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Adakah anda pasti anda mahu memadamkan halaman ini daripada sejarah anda?</translation>
<translation id="2677748264148917807">Tinggalkan</translation>
<translation id="269990154133806163">Pelayan memberikan sijil yang tidak didedahkan kepada umum menggunakan dasar Ketelusan Sijil. Ini merupakan keperluan bagi sesetengah sijil, untuk memastikan bahawa sijil itu boleh dipercayai dan menyediakan perlindungan daripada penyerang.<ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Senarai Bacaan</translation>
<translation id="2704283930420550640">Nilai tidak sepadan dengan format.</translation>
<translation id="2704951214193499422">Chromium tidak dapat mengesahkan kad anda pada masa ini. Sila cuba lagi nanti.</translation>
<translation id="2705137772291741111">Salinan tapak ini yang disimpan (cache) tidak boleh dibaca.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Anda cuba mencapai <ph name="DOMAIN" />, tetapi sijil yang diberi pelayan telah dibatalkan oleh pengeluarnya. Ini bermakna bukti kelayakan keselamatan yang diberi pelayan sememangnya tidak boleh dipercayai. Anda mungkin berkomunikasi dengan penyerang. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Minta kebenaran</translation>
<translation id="2713444072780614174">Putih</translation>
+<translation id="2720342946869265578">Berdekatan</translation>
<translation id="2721148159707890343">Permintaan berjaya</translation>
<translation id="2728127805433021124">Sijil pelayan ditandatangani menggunakan algoritma tandatangan yang lemah.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Jalankan Diagnostik Sambungan<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Anda boleh melumpuhkan sebarang proksi yang dikonfigurasi untuk sambungan dari halaman tetapan.</translation>
<translation id="2955913368246107853">Tutup bar cari</translation>
<translation id="2958431318199492670">Konfigurasi rangkaian tidak mematuhi piawaian ONC. Sebahagian konfigurasi tidak boleh diimport.</translation>
+<translation id="29611076221683977">Penyerang yang ada di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pada masa ini mungkin cuba memasang program berbahaya pada Mac anda yang boleh mencuri atau memadamkan maklumat anda (contohnya foto, kata laluan, mesej dan kad kredit).</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="2985306909656435243">Jika didayakan, Chromium akan menyimpan salinan kad anda pada peranti ini untuk pengisian borang yang lebih cepat.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Sembunyikan butiran</translation>
<translation id="3587482841069643663">Semua</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Pelayan tidak menyokong sambungan rundingan semula TLS.</translation>
<translation id="36224234498066874">Kosongkan Data Penyemakan Imbas...</translation>
<translation id="362276910939193118">Paparkan Sejarah Penuh</translation>
<translation id="3623476034248543066">Tunjukkan nilai</translation>
@@ -272,6 +280,7 @@
<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="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>
@@ -281,10 +290,10 @@
<translation id="3712624925041724820">Kehabisan lesen</translation>
<translation id="3714780639079136834">Menghidupkan data mudah alih atau Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Menyemak proksi, tembok api dan konfigurasi DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Jika anda memahami risiko terhadap keselamatan anda, anda boleh <ph name="BEGIN_LINK" />melawati tapak web yang tidak selamat ini<ph name="END_LINK" /> sebelum program berbahaya dialih keluar.</translation>
<translation id="3739623965217189342">Pautan yang anda salin</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="3788090790273268753">Sijil untuk tapak web ini tamat tempoh pada tahun 2016 dan rantaian sijil ini mengandungi sijil yang ditandatangani menggunakan SHA-1.</translation>
<translation id="382518646247711829">Jika anda menggunakan pelayan proksi...</translation>
<translation id="3828924085048779000">Kosongkan frasa laluan adalah tidak dibenarkan.</translation>
<translation id="3845539888601087042">Menunjukkan sejarah daripada peranti anda yang dilog masuk. <ph name="BEGIN_LINK" />Ketahui lebih lanjut<ph name="END_LINK" />.</translation>
@@ -306,6 +315,7 @@
<translation id="4058922952496707368">Kekunci "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Pelanggan dan pelayan tidak menyokong versi protokol SSL atau set sifer biasa.</translation>
<translation id="4079302484614802869">Konfigurasi proksi ditetapkan kepada penggunaaan URL skrip .pac, bukannya pelayan proksi tetap.</translation>
+<translation id="4098354747657067197">Tapak menipu di hadapan</translation>
<translation id="4103249731201008433">Nombor siri peranti tidak sah</translation>
<translation id="4103763322291513355">Lawati &lt;strong&gt;chrome://policy&lt;/strong&gt; untuk melihat senarai URL yang disenarai hitam dan dasar lain yang dikuatkuasakan oleh pentadbir sistem anda.</translation>
<translation id="4110615724604346410">Pelayan ini tidak dapat membuktikan bahawa itu <ph name="DOMAIN" />; sijil keselamatannya mengandungi ralat. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintas sambungan anda. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -323,15 +333,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{tiada}=1{1 apl ($1)}=2{2 apl ($1, $2)}other{# apl ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Nahas</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Cuba jalankan Diagnostik Rangkaian<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Sambungan anda ke tapak ini tidak selamat sepenuhnya</translation>
<translation id="4250680216510889253">Tidak</translation>
<translation id="425582637250725228">Perubahan yang anda buat mungkin tidak disimpan.</translation>
<translation id="4258748452823770588">Tandatangan tidak elok</translation>
<translation id="4269787794583293679">(Tiada nama pengguna)</translation>
+<translation id="4280429058323657511">, tamat tempoh <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Penyemakan Imbas Selamat Google <ph name="BEGIN_LINK" />menemui atur cara berbahaya<ph name="END_LINK" /> di <ph name="SITE" /> baru-baru ini. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Cadangan ibu bapa</translation>
<translation id="4304224509867189079">Log Masuk</translation>
<translation id="432290197980158659">Pelayan memberikan sijil yang tidak sepadan dengan jangkaan terbina dalam. Jangkaan ini disertakan untuk tapak web keselamatan tinggi yang tertentu untuk melindungi anda. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Gagal menemui artikel</translation>
+<translation id="4326324639298822553">Semak tarikh tamat tempoh anda dan cuba lagi</translation>
<translation id="4331708818696583467">Tidak Selamat</translation>
+<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="4381091992796011497">Nama Pengguna:</translation>
<translation id="4394049700291259645">Lumpuhkan</translation>
@@ -349,6 +364,7 @@
<translation id="4589078953350245614">Anda cuba mencapai <ph name="DOMAIN" />, tetapi pelayan memberikan sijil yang tidak sah. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Tab daripada peranti lain</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Anda sedang melihat halaman sambungan.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -357,10 +373,13 @@
<translation id="4726672564094551039">Muat semula dasar</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4744603770635761495">Laluan Boleh Laku</translation>
+<translation id="4750917950439032686">Maklumat anda (contohnya, kata laluan atau nombor kad kredit) adalah berciri peribadi apabila dihantar ke tapak ini.</translation>
<translation id="4756388243121344051">&amp;Sejarah</translation>
+<translation id="4759118997339041434">Ciri autolengkap pembayaran dilumpuhkan</translation>
<translation id="4764776831041365478">Laman web di <ph name="URL" /> mungkin tergendala buat sementara waktu atau ia mungkin dpindahkan secara kekal ke alamat web baharu.</translation>
<translation id="4771973620359291008">Ralat tidak diketahui telah berlaku.</translation>
<translation id="4800132727771399293">Semak tarikh tamat tempoh serta CVC anda dan cuba lagi</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Ralat rangkaian</translation>
<translation id="4816492930507672669">Muat halaman</translation>
<translation id="4850886885716139402">Lihat</translation>
@@ -369,6 +388,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{dan 1 lagi halaman web}other{dan # lagi halaman web}}</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="495170559598752135">Tindakan</translation>
<translation id="4958444002117714549">Kembangkan senarai</translation>
@@ -396,7 +416,6 @@
<translation id="5181140330217080051">Memuat turun</translation>
<translation id="5190835502935405962">Bar Penanda Halaman</translation>
<translation id="5199729219167945352">Eksperimen</translation>
-<translation id="5199841536747119669">Cadangan anda dipaparkan di sini</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="5299298092464848405">Ralat semasa menghuraikan dasar</translation>
@@ -404,8 +423,10 @@
<translation id="5308689395849655368">Pelaporan nahas dilumpuhkan.</translation>
<translation id="5317780077021120954">Simpan</translation>
<translation id="5327248766486351172">Nama</translation>
+<translation id="5337705430875057403">Penyerang di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin menipu anda supaya melakukan sesuatu yang berbahaya seperti memasang perisian atau mendedahkan maklumat peribadi anda (contohnya, kata laluan, nombor telefon atau kad kredit).</translation>
<translation id="5359637492792381994">Pelayan ini tidak dapat membuktikan bahawa itu <ph name="DOMAIN" />; sijil keselamatannya tidak sah pada masa ini. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintas sambungan anda. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Gagal menyimpan tetapan dasar</translation>
+<translation id="5386426401304769735">Rantaian sijil untuk tapak ini mengandungi sijil yang ditandatangani menggunakan SHA-1.</translation>
<translation id="5421136146218899937">Kosongkan data semakan imbas...</translation>
<translation id="5430298929874300616">Alih keluar penanda halaman</translation>
<translation id="5431657950005405462">Fail anda tidak ditemui</translation>
@@ -426,17 +447,20 @@
<translation id="5544037170328430102">Halaman terbenam di <ph name="SITE" /> menyatakan:</translation>
<translation id="5556459405103347317">Muat Semula</translation>
<translation id="5565735124758917034">Aktif</translation>
+<translation id="5572851009514199876">Sila mulakan dan log masuk ke Chrome supaya Chrome boleh menyemak sama ada anda dibenarkan mengakses tapak ini.</translation>
+<translation id="5580958916614886209">Semak bulan tamat tempoh anda dan cuba lagi</translation>
<translation id="560412284261940334">Pengurusan tidak disokong</translation>
<translation id="5610142619324316209">Menyemak sambungan</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> telah terlalu kerap mengubah hala anda.</translation>
<translation id="5622887735448669177">Adakah anda ingin meninggalkan tapak ini?</translation>
<translation id="5629630648637658800">Gagal memuatkan tetapan dasar</translation>
<translation id="5631439013527180824">Token pengurusan peranti tidak sah</translation>
+<translation id="5669703222995421982">Dapatkan kandungan yang diperibadikan</translation>
+<translation id="5675650730144413517">Halaman ini tidak berfungsi</translation>
<translation id="5677928146339483299">Disekat</translation>
<translation id="5694783966845939798">Anda cuba mencapai <ph name="DOMAIN" />, tetapi pelayan memberikan sijil yang ditandatangani menggunakan algoritma tandatangan yang lemah (seperti SHA-1). Ini bermakna bukti kelayakan keselamatan yang diberikan oleh pelayan mungkin dipalsukan dan pelayan tersebut mungkin bukan pelayan yang anda jangkakan (anda mungkin berkomunikasi dengan penyerang). <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Identiti tapak web ini belum disahkan.</translation>
<translation id="5720705177508910913">Pengguna semasa</translation>
-<translation id="572328651809341494">Tab terbaharu</translation>
<translation id="5732392974455271431">Ibu bapa anda boleh menyahsekatnya untuk anda</translation>
<translation id="5784606427469807560">Terdapat masalah mengesahkan kad anda. Semak sambungan Internet anda dan cuba lagi.</translation>
<translation id="5785756445106461925">Selain itu, halaman ini mengandungi sumber lain yang tidak selamat. Sumber ini boleh dilihat oleh orang lain semasa dalam transit dan boleh diubah oleh penyerang untuk menukar penampilan halaman.</translation>
@@ -445,6 +469,7 @@
<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="5843436854350372569">Anda cuba mencapai <ph name="DOMAIN" />, tetapi pelayan memberi sijil yang mengandungi kunci yang lemah, Penyerang mungkin telah merosakkan kunci peribadi dan pelayan tersebut mungkin bukan pelayan yang anda jangkakan (anda mungkin berkomunikasi dengan penyerang). <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Folder Baharu</translation>
<translation id="5869405914158311789">Tapak ini tidak dapat dicapai</translation>
@@ -454,6 +479,7 @@
<translation id="59107663811261420">Kad jenis ini tidak disokong oleh Google Payments untuk pedagang ini. Sila pilih kad yang lain.</translation>
<translation id="59174027418879706">Didayakan</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="5966707198760109579">Minggu</translation>
<translation id="5967867314010545767">Buang daripada sejarah</translation>
<translation id="5975083100439434680">Zum keluar</translation>
@@ -472,8 +498,11 @@
<translation id="614940544461990577">Cuba:</translation>
<translation id="6151417162996330722">Sijil pelayan mempunyai tempoh sah yang terlalu panjang.</translation>
<translation id="6165508094623778733">Ketahui lebih lanjut</translation>
+<translation id="6177128806592000436">Sambungan anda ke tapak ini tidak selamat</translation>
<translation id="6203231073485539293">Semak sambungan Internet anda</translation>
<translation id="6218753634732582820">Alih keluar alamat daripada Chromium?</translation>
+<translation id="6251924700383757765">Dasar privasi</translation>
+<translation id="625755898061068298">Anda telah memilih untuk melumpuhkan amaran keselamatan bagi tapak ini.</translation>
<translation id="6259156558325130047">&amp;Buat Asal Susun Semula</translation>
<translation id="6263376278284652872">Penanda halaman <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Kembali ke keselamatan</translation>
@@ -482,6 +511,7 @@
<translation id="6305205051461490394"><ph name="URL" /> tidak dapat dicapai.</translation>
<translation id="6321917430147971392">Semak tetapan DNS anda</translation>
<translation id="6328639280570009161">Cuba lumpuhkan ramalan rangkaian</translation>
+<translation id="6328786501058569169">Tapak ini mengelirukan</translation>
<translation id="6337534724793800597">Tapis dasar mengikut nama</translation>
<translation id="6342069812937806050">Sebentar tadi</translation>
<translation id="6345221851280129312">saiz tidak diketahui</translation>
@@ -490,6 +520,8 @@
<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="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>
<translation id="6416403317709441254">Anda tidak boleh melawati <ph name="SITE" /> sekarang kerana tapak web ini menghantar bukti kelayakan yang dicampuradukkan sehingga tidak dapat diproses oleh Chromium. Ralat rangkaian dan serangan biasanya bersifat sementara, oleh itu halaman ini mungkin akan berfungsi sebentar lagi. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Tidak dapat memeriksa sama ada sijil telah dibatalkan.</translation>
@@ -499,10 +531,12 @@
<translation id="6458467102616083041">Diabaikan kerana carian lalai dilumpuhkan oleh dasar.</translation>
<translation id="6462969404041126431">Pelayan ini tidak dapat membuktikan bahawa itu <ph name="DOMAIN" />; sijil keselamatannya telah dibatalkan. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintas sambungan anda. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Buat Semula Pemadaman</translation>
<translation id="6534179046333460208">Cadangan Web Fizikal</translation>
<translation id="6550675742724504774">Pilihan</translation>
+<translation id="6556239504065605927">Sambungan selamat</translation>
<translation id="6563469144985748109">Pengurus anda belum meluluskannya</translation>
<translation id="6593753688552673085">kurang daripada <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Pilihan penyulitan</translation>
@@ -511,7 +545,6 @@
<translation id="6644283850729428850">Dasar ini telah dikecam.</translation>
<translation id="6652240803263749613">Pelayan ini tidak dapat membuktikan bahawa itu <ph name="DOMAIN" />; sijil keselamatannya tidak dipercayai oleh sistem pengendalian komputer anda. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintas sambungan anda. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Edit Folder</translation>
-<translation id="6660210980321319655">Dilaporkan secara automatik pada <ph name="CRASH_TIME" /></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>
@@ -523,6 +556,7 @@
<translation id="6753269504797312559">Nilai dasar</translation>
<translation id="6757797048963528358">Peranti anda tidak aktif.</translation>
<translation id="6778737459546443941">Ibu bapa anda belum meluluskannya</translation>
+<translation id="6810899417690483278">ID Penyesuaian</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Halaman web di <ph name="URL" /> kini tidak tersedia. Ia mungkin terlebih muatan atau tergendala akibat penyelenggaraan.</translation>
<translation id="6831043979455480757">Terjemah</translation>
@@ -543,6 +577,8 @@
<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>
+<translation id="7064851114919012435">Maklumat hubungan</translation>
+<translation id="7079718277001814089">Tapak ini mengandungi perisian hasad</translation>
<translation id="7087282848513945231">Daerah</translation>
<translation id="7088615885725309056">Lebih lama</translation>
<translation id="7090678807593890770">Cari <ph name="LINK" /> di Google</translation>
@@ -557,6 +593,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> tidak mematuhi piawaian keselamatan.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Ketahui lebih lanjut<ph name="END_LINK" /> mengenai masalah ini.</translation>
<translation id="7219179957768738017">Sambungan menggunakan <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Tapak di hadapan mengandungi perisian hasad</translation>
<translation id="724975217298816891">Masukkan tarikh tamat tempoh dan CVC untuk <ph name="CREDIT_CARD" /> bagi mengemas kini butiran kad anda. Setelah anda mengesahkan, butiran kad anda akan dikongsi dengan tapak ini.</translation>
<translation id="725866823122871198">Sambungan peribadi ke <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> tidak boleh diwujudkan kerana tarikh dan masa komputer anda (<ph name="DATE_AND_TIME" />) tidak betul.</translation>
<translation id="7269802741830436641">Halaman web ini mempunyai gelung ubah hala</translation>
@@ -612,6 +649,7 @@ Psst! <ph name="SHORTCUT_KEY" /> mod Inkognito mungkin akan berguna pada masa ak
<translation id="7658239707568436148">Batal</translation>
<translation id="7667346355482952095">Token dasar yang dikembalikan kosong atau tidak sepadan dengan token semasa</translation>
<translation id="7668654391829183341">Peranti tidak diketahui</translation>
+<translation id="7669271284792375604">Penyerang pada tapak ini mungkin cuba menipu dengan meminta anda memasang atur cara yang membahayakan pengalaman penyemakan imbas anda (contohnya, dengan menukar halaman utama anda atau menunjukkan iklan tambahan pada laman yang anda lawati).</translation>
<translation id="7674629440242451245">Berminat dengan ciri Chrome baharu yang hebat? Cuba saluran pembangun kami di chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Teruskan ke <ph name="SITE" /> (tidak selamat)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Tapak ini tidak dapat dimuatkan daripada cache</translation>
@@ -627,14 +665,17 @@ Psst! <ph name="SHORTCUT_KEY" /> mod Inkognito mungkin akan berguna pada masa ak
<translation id="7800304661137206267">Sambungan disulitkan menggunakan <ph name="CIPHER" />, dengan <ph name="MAC" /> untuk pengesahan mesej dan <ph name="KX" /> sebagai mekanisme pertukaran kunci.</translation>
<translation id="780301667611848630">Tidak, terima kasih</translation>
<translation id="7805768142964895445">Status</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Alih keluar cadangan borang daripada Chrome?</translation>
<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="7887683347370398519">Semak CVC anda dan cuba lagi</translation>
<translation id="7912024687060120840">Dalam Folder:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Sijil pelayan masih belum sah.</translation>
<translation id="7942349550061667556">Merah</translation>
+<translation id="7947285636476623132">Semak tahun tamat tempoh anda dan cuba lagi</translation>
<translation id="7951415247503192394">(32-bit)</translation>
<translation id="7956713633345437162">Penanda halaman mudah alih</translation>
<translation id="7961015016161918242">Tidak sama sekali</translation>
@@ -642,7 +683,10 @@ Psst! <ph name="SHORTCUT_KEY" /> mod Inkognito mungkin akan berguna pada masa ak
<translation id="7983301409776629893">Sentiasa terjemahkan <ph name="ORIGINAL_LANGUAGE" /> ke <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Tidak Ditentukan</translation>
<translation id="8012647001091218357">Kami tidak dapat menghubungi ibu bapa anda pada masa ini. Sila cuba lagi.</translation>
+<translation id="8025119109950072390">Penyerang pada tapak ini mungkin menipu anda supaya melakukan sesuatu yang berbahaya seperti memasang perisian atau mendedahkan maklumat peribadi anda (contohnya, kata laluan, nombor telefon atau maklumat kad kredit).</translation>
+<translation id="803030522067524905">Penyemakan Imbas Selamat Google mengesan pancingan data di <ph name="SITE" /> baru-baru ini. Tapak pancingan data menyamar menjadi tapak web lain untuk menipu anda. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Halaman ini dalam <ph name="SOURCE_LANGUAGE" />. Terjemahkannya kepada <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Hantar Maklum Balas</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>
@@ -653,9 +697,11 @@ Psst! <ph name="SHORTCUT_KEY" /> mod Inkognito mungkin akan berguna pada masa ak
<translation id="8150722005171944719">Fail di <ph name="URL" /> tidak boleh dibaca. Fail mungkin telah dialih keluar, dipindahkan atau kebenaran fail mungkin menghalang akses.</translation>
<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="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="8230421197304563332">Penyerang yang ada di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pada masa ini mungkin cuba memasang program berbahaya pada komputer anda yang boleh mencuri atau memadamkan maklumat anda (contohnya foto, kata laluan, mesej dan kad kredit).</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="8249320324621329438">Diambil kali terakhir:</translation>
<translation id="8261506727792406068">Padam</translation>
@@ -664,11 +710,13 @@ Psst! <ph name="SHORTCUT_KEY" /> mod Inkognito mungkin akan berguna pada masa ak
<translation id="8294431847097064396">Sumber</translation>
<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="8349305172487531364">Bar penanda halaman</translation>
<translation id="8363502534493474904">Matikan mod pesawat</translation>
<translation id="8364627913115013041">Tidak ditetapkan.</translation>
<translation id="8380941800586852976">Berbahaya</translation>
<translation id="8382348898565613901">Penanda halaman anda yang terbaharu dilawati dipaparkan di sini</translation>
+<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="8428213095426709021">Tetapan</translation>
@@ -680,9 +728,9 @@ Psst! <ph name="SHORTCUT_KEY" /> mod Inkognito mungkin akan berguna pada masa ak
<translation id="8498891568109133222"><ph name="HOST_NAME" /> mengambil masa terlalu lama untuk bertindak balas.</translation>
<translation id="852346902619691059">Pelayan ini tidak dapat membuktikan bahawa itu <ph name="DOMAIN" />; sijil keselamatannya tidak dipercayai oleh sistem pengendalian peranti anda. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintas sambungan anda. <ph name="BEGIN_LEARN_MORE_LINK" />Ketahui lebih lanjut<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Kad jenis ini tidak disokong oleh Google Payments. Sila pilih kad yang lain.</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="8553075262323480129">Gagal terjemahan kerana bahasa halaman tidak dapat ditentukan.</translation>
<translation id="8559762987265718583">Sambungan peribadi ke <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> tidak boleh diwujudkan kerana tarikh dan masa peranti anda (<ph name="DATE_AND_TIME" />) tidak betul.</translation>
-<translation id="856992080682148">Sijil untuk tapak web ini tamat tempoh pada tahun 2017 ke atas dan rantaian sijil ini mengandungi sijil yang ditandatangani menggunakan SHA-1.</translation>
<translation id="8571890674111243710">Menterjemah halaman ke <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Sijil tidak menyatakan mekanisme untuk memeriksa sama ada ia telah dibatalkan.</translation>
<translation id="8620436878122366504">Ibu bapa anda belum meluluskannya</translation>
@@ -695,10 +743,12 @@ Psst! <ph name="SHORTCUT_KEY" /> mod Inkognito mungkin akan berguna pada masa ak
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;Alamat DNS&lt;/abbr&gt; <ph name="HOST_NAME" /> tidak ditemui. Masalah sedang didiagnosis.</translation>
<translation id="8790007591277257123">&amp;Buat semula pemadaman</translation>
<translation id="8798099450830957504">Lalai</translation>
+<translation id="8800988563907321413">Cadangan berdekatan anda dipaparkan di sini</translation>
<translation id="8804164990146287819">Dasar Privasi</translation>
<translation id="8820817407110198400">Penanda buku</translation>
<translation id="8834246243508017242">Dayakan Auto-Isian menggunakan Kenalan ...</translation>
<translation id="883848425547221593">Penanda Halaman Lain</translation>
+<translation id="884264119367021077">Alamat penghantaran</translation>
<translation id="884923133447025588">Tiada mekanisme pembatalan dijumpai.</translation>
<translation id="885730110891505394">Berkongsi dengan Google</translation>
<translation id="8866481888320382733">Ralat semasa menghuraikan tetapan dasar</translation>
@@ -716,18 +766,21 @@ Psst! <ph name="SHORTCUT_KEY" /> mod Inkognito mungkin akan berguna pada masa ak
<translation id="8971063699422889582">Sijil pelayan telah tamat tempoh.</translation>
<translation id="8987927404178983737">Bulan</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Laman web yang akan dilayari mengandungi atur cara berbahaya</translation>
<translation id="9001074447101275817"><ph name="DOMAIN" /> proksi memerlukan nama pengguna dan kata laluan.</translation>
<translation id="901974403500617787">Bendera yang diguna pakai di seluruh sistem hanya boleh ditetapkan oleh pemilik: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Halaman ini telah diterjemahkan kepada <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Ralat keselamatan</translation>
<translation id="9038649477754266430">Gunakan perkhidmatan ramalan untuk memuatkan halaman lebih cepat</translation>
<translation id="9039213469156557790">Selain itu, halaman ini mengandungi sumber lain yang tidak selamat. Sumber ini boleh dilihat oleh orang lain semasa dalam transit dan boleh diubah oleh penyerang untuk menukar kelakuan halaman.</translation>
+<translation id="9040185888511745258">Penyerang pada <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin mencuba untuk menipu anda untuk memasang atur cara yang membahayakan pengalaman penyemak imbas anda (contohnya, dengan menukar halaman utama anda atau menunjukkan iklan tambahan pada laman yang anda lawati).</translation>
<translation id="9050666287014529139">Frasa laluan</translation>
<translation id="9065203028668620118">Edit</translation>
<translation id="9068849894565669697">Pilih warna</translation>
<translation id="9076283476770535406">Tapak mungkin mengandungi kandungan dewasa</translation>
-<translation id="9092364396508701805">Halaman <ph name="HOST_NAME" /> tidak berfungsi</translation>
<translation id="9103872766612412690"><ph name="SITE" /> biasanya menggunakan penyulitan untuk melindungi maklumat anda. Apabila Chromium cuba menyambung ke <ph name="SITE" /> pada kali ini, tapak web tersebut mengembalikan bukti kelayakan yang luar biasa dan salah. Hal ini boleh berlaku apabila penyerang sedang cuba menyamar sebagai <ph name="SITE" /> atau skrin log masuk Wi-Fi telah memutuskan sambungan. Maklumat anda masih selamat kerana Chromium menghentikan sambungan sebelum sebarang pertukaran data berlaku.</translation>
<translation id="9137013805542155359">Paparkan asal</translation>
+<translation id="9137248913990643158">Sila mulakan dan log masuk ke Chrome sebelum menggunakan apl ini.</translation>
<translation id="9148507642005240123">&amp;Buat asal edit</translation>
<translation id="9157595877708044936">Menyediakan...</translation>
<translation id="9170848237812810038">&amp;Buat asal</translation>
@@ -740,7 +793,6 @@ Psst! <ph name="SHORTCUT_KEY" /> mod Inkognito mungkin akan berguna pada masa ak
<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="962701380617707048">Masukkan tarikh tamat tempoh dan CVC untuk <ph name="CREDIT_CARD" /> bagi mengemas kini butiran kad anda</translation>
<translation id="969892804517981540">Binaan Rasmi</translation>
<translation id="988159990683914416">Binaan Pemaju</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 01b43ece317..684332acd2a 100644
--- a/chromium/components/strings/components_strings_nl.xtb
+++ b/chromium/components/strings/components_strings_nl.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Maak opnieuw verbinding met wifi</translation>
<translation id="1175364870820465910">&amp;Afdrukken...</translation>
<translation id="1181037720776840403">Verwijderen</translation>
+<translation id="1184214524891303587">Informatie over mogelijke beveiligingsincidenten <ph name="BEGIN_WHITEPAPER_LINK" />automatisch melden<ph name="END_WHITEPAPER_LINK" /> aan Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Volgende</translation>
<translation id="1201895884277373915">Meer van deze site</translation>
<translation id="1206967143813997005">Onjuiste eerste handtekening</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cyaan</translation>
<translation id="1629803312968146339">Wil je dat Chrome deze kaart opslaat?</translation>
<translation id="1640180200866533862">Gebruikersbeleid</translation>
+<translation id="1640244768702815859">Probeer <ph name="BEGIN_LINK" />de homepage van de site te bezoeken<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">De netwerkconfiguratie is ongeldig en kan niet worden geïmporteerd.</translation>
<translation id="1644574205037202324">Geschiedenis</translation>
<translation id="1645368109819982629">Niet-ondersteund protocol</translation>
<translation id="1676269943528358898"><ph name="SITE" /> gebruikt gewoonlijk versleuteling om je gegevens te beschermen. Toen Google Chrome deze keer probeerde verbinding te maken met <ph name="SITE" />, retourneerde de website ongewone en onjuiste inloggegevens. Dit kan gebeuren als een aanvaller probeert zich als <ph name="SITE" /> voor te doen of als een wifi-inlogscherm de verbinding heeft verbroken. Je gegevens zijn nog steeds veilig omdat Google Chrome de verbinding heeft beëindigd voordat er gegevens konden worden uitgewisseld.</translation>
+<translation id="168328519870909584">Cybercriminelen op <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> proberen mogelijk gevaarlijke apps op je apparaat te installeren waarmee je gegevens kunnen worden gestolen of verwijderd (bijvoorbeeld foto's, wachtwoorden, berichten en creditcardgegevens).</translation>
<translation id="168841957122794586">Het servercertificaat bevat een zwakke cryptografische sleutel.</translation>
-<translation id="1701955595840307032">Voorgestelde content ophalen</translation>
<translation id="1710259589646384581">Besturingssysteem</translation>
<translation id="1721312023322545264">Je hebt toestemming van <ph name="NAME" /> nodig om deze site te bezoeken</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Paginanummer</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Voer Windows Netwerkcontrole uit<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Update je wachtwoordzin voor synchronisatie.</translation>
+<translation id="1787142507584202372">Je geopende tabbladen worden hier weergegeven</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">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 besmet met malware. De schadelijke content is afkomstig van <ph name="SUBRESOURCE_HOST" />, een bekende distributeur van malware. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Ongeldige aanvraag of aanvraagparameters</translation>
+<translation id="1834321415901700177">Deze site bevat schadelijke programma's</translation>
<translation id="1838667051080421715">Je bekijkt de bron van een webpagina.</translation>
<translation id="1871208020102129563">Proxy is ingesteld op het gebruik van vaste proxyservers, niet op een script-URL van het PAC-type.</translation>
<translation id="1883255238294161206">Lijst samenvouwen</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Mobiele bladwijzers</translation>
<translation id="2148716181193084225">Vandaag</translation>
-<translation id="2149973817440762519">Bladwijzer bewerken</translation>
<translation id="2154054054215849342">De synchronisatieservice is niet beschikbaar voor je domein</translation>
<translation id="2166049586286450108">Volledige beheerderstoegang</translation>
<translation id="2166378884831602661">Deze site kan geen beveiligde verbinding leveren</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Je kunt <ph name="SITE" /> op dit moment niet bezoeken, omdat de website gebruikmaakt van HSTS. Aangezien netwerkfouten en aanvallen meestal van tijdelijke aard zijn, zal deze pagina later waarschijnlijk weer correct werken. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Ongeldige bladwijzer genegeerd bij index <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Andere bladwijzers</translation>
+<translation id="2355395290879513365">Aanvallers kunnen mogelijk de afbeeldingen zien die je op deze site bekijkt en je misleiden door de afbeeldingen aan te passen.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Beleid weergeven zonder waarde ingesteld</translation>
<translation id="2396249848217231973">&amp;Verwijderen ongedaan maken</translation>
<translation id="2455981314101692989">Deze webpagina heeft Automatisch aanvullen uitgeschakeld voor dit formulier.</translation>
+<translation id="2460160116472764928">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 besmet met malware. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Invullen</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Netwerkcontrole uitvoeren<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Ongeldige zoek-URL.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Weet je zeker dat u deze pagina's uit je geschiedenis wilt verwijderen?</translation>
<translation id="2677748264148917807">Verlaten</translation>
<translation id="269990154133806163">De server heeft een certificaat gepresenteerd dat niet openbaar bekend is gemaakt via het beleid voor Certificaattransparantie. Dit is voor bepaalde certificaten een vereiste om er zeker van te zijn dat ze betrouwbaar zijn en bescherming bieden tegen aanvallers. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Leeslijst</translation>
<translation id="2704283930420550640">Waarde komt niet overeen met notatie.</translation>
<translation id="2704951214193499422">Chromium kan je creditcard momenteel niet bevestigen. Probeer het later opnieuw.</translation>
<translation id="2705137772291741111">De in het cachegeheugen opgeslagen versie van deze site is niet bereikbaar.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Je probeert <ph name="DOMAIN" /> te bereiken, maar het certificaat dat door de server is geretourneerd, is ingetrokken door de uitgever. Dit betekent dat de beveiligingsreferenties die de server heeft geretourneerd absoluut niet kunnen worden vertrouwd. Het is mogelijk dat je met een hacker communiceert. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Toestemming vragen</translation>
<translation id="2713444072780614174">Wit</translation>
+<translation id="2720342946869265578">In de buurt</translation>
<translation id="2721148159707890343">Verzoek geslaagd</translation>
<translation id="2728127805433021124">Het certificaat van de server is ondertekend met een zwak ondertekeningsalgoritme.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Verbindingsdiagnose uitvoeren<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Via de instellingenpagina kun je proxyservers uitschakelen die voor een verbinding zijn geconfigureerd.</translation>
<translation id="2955913368246107853">Zoekbalk sluiten</translation>
<translation id="2958431318199492670">De netwerkconfiguratie voldoet niet aan de ONC-standaard. Delen van de configuratie worden mogelijk niet geïmporteerd.</translation>
+<translation id="29611076221683977">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 creditcarddetails).</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="2985306909656435243">Als deze instelling is ingeschakeld, slaat Chromium een kopie van je kaart op dit apparaat op zodat formulieren sneller kunnen worden ingevuld.</translation>
@@ -256,7 +265,6 @@
<translation id="3586931643579894722">Details verbergen</translation>
<translation id="3587482841069643663">Alles</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">De server ondersteunt de extensie voor TLS-onderhandeling niet.</translation>
<translation id="36224234498066874">Wis browsegegevens...</translation>
<translation id="362276910939193118">Volledige geschiedenis weergeven</translation>
<translation id="3623476034248543066">Waarde weergeven</translation>
@@ -267,19 +275,20 @@
<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="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="3696411085566228381">geen</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">Laden...</translation>
+<translation id="370665806235115550">Bezig met laden...</translation>
<translation id="3712624925041724820">Licenties zijn verbruikt</translation>
<translation id="3714780639079136834">Schakel mobiele data of wifi in</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Controleer de proxy, firewall en DNS-configuratie<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Als je de beveiligingsrisico's begrijpt, kun je <ph name="BEGIN_LINK" />deze onveilige site bezoeken<ph name="END_LINK" /> voordat de gevaarlijke programma's zijn verwijderd.</translation>
<translation id="3739623965217189342">Link die je hebt gekopieerd</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="3788090790273268753">Het certificaat voor deze site verloopt in 2016 en de certificaatketen bevat een certificaat dat is ondertekend met SHA-1.</translation>
<translation id="382518646247711829">Als je een proxyserver gebruikt...</translation>
<translation id="3828924085048779000">Een lege wachtwoordzin is niet toegestaan.</translation>
<translation id="3845539888601087042">Geschiedenis van je ingelogde apparaten wordt weergegeven. <ph name="BEGIN_LINK" />Meer informatie<ph name="END_LINK" />.</translation>
@@ -301,6 +310,7 @@
<translation id="4058922952496707368">Sleutel '<ph name="SUBKEY" />': <ph name="ERROR" /></translation>
<translation id="4075732493274867456">De client en server ondersteunen geen algemene SSL-protocolversie of coderingssuite.</translation>
<translation id="4079302484614802869">Proxyconfiguratie is ingesteld op het gebruik van een pac-script-URL, niet op het gebruik van vaste proxyservers.</translation>
+<translation id="4098354747657067197">Misleidende site gedetecteerd</translation>
<translation id="4103249731201008433">Serienummer van apparaat is ongeldig</translation>
<translation id="4103763322291513355">Ga naar &lt;strong&gt;chrome://policy&lt;/strong&gt; om de lijst met URL's op de zwarte lijst en andere beleidsregels te bekijken die worden afgedwongen door je systeembeheerder.</translation>
<translation id="4110615724604346410">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. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -318,15 +328,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{geen}=1{1 app ($1)}=2{2 apps ($1, $2)}other{# apps ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Crashes</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Voer Netwerkcontrole uit<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Je verbinding met deze site is niet volledig veilig</translation>
<translation id="4250680216510889253">Nee</translation>
<translation id="425582637250725228">Wijzigingen die je hebt aangebracht, worden mogelijk niet opgeslagen.</translation>
<translation id="4258748452823770588">Onjuiste handtekening</translation>
<translation id="4269787794583293679">(Geen gebruikersnaam)</translation>
+<translation id="4280429058323657511">, vervalt <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google Safe Browsing heeft onlangs <ph name="BEGIN_LINK" />schadelijke programma's gevonden<ph name="END_LINK" /> op <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Bovenliggende suggesties</translation>
<translation id="4304224509867189079">Inloggen</translation>
<translation id="432290197980158659">De server heeft een certificaat gepresenteerd dat niet overeenkomt met de ingebouwde verwachtingen. Deze verwachtingen zijn opgenomen voor bepaalde zwaar beveiligde websites om je te beschermen. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Kan artikel niet vinden</translation>
+<translation id="4326324639298822553">Controleer de vervaldatum en probeer het opnieuw</translation>
<translation id="4331708818696583467">Niet veilig</translation>
+<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="4381091992796011497">Gebruikersnaam:</translation>
<translation id="4394049700291259645">Uitschakelen</translation>
@@ -344,6 +359,7 @@
<translation id="4589078953350245614">Je probeert <ph name="DOMAIN" /> te bereiken, maar de server heeft een ongeldig certificaat geretourneerd. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Tabbladen van andere apparaten</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Je bekijkt nu een extensiepagina.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -352,10 +368,13 @@
<translation id="4726672564094551039">Beleid opnieuw laden</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4744603770635761495">Uitvoerbaar pad</translation>
+<translation id="4750917950439032686">Je gegevens (zoals wachtwoorden of creditcardnummers) zijn privé wanneer ze worden verzonden naar deze site.</translation>
<translation id="4756388243121344051">Gesc&amp;hiedenis</translation>
+<translation id="4759118997339041434">Automatisch aanvullen voor betalingen uitgeschakeld</translation>
<translation id="4764776831041365478">De webpagina op <ph name="URL" /> is mogelijk tijdelijk uitgeschakeld of permanent verplaatst naar een nieuw webadres.</translation>
<translation id="4771973620359291008">Er is een onbekende fout opgetreden.</translation>
<translation id="4800132727771399293">Controleer je vervaldatum en CVC-code en probeer het opnieuw</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Netwerkfout</translation>
<translation id="4816492930507672669">Aanpassen aan pagina</translation>
<translation id="4850886885716139402">Weergave</translation>
@@ -364,6 +383,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{en nog 1 webpagina}other{en nog # webpagina's}}</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="495170559598752135">Acties</translation>
<translation id="4958444002117714549">Lijst uitvouwen</translation>
@@ -391,7 +411,6 @@
<translation id="5181140330217080051">Downloaden</translation>
<translation id="5190835502935405962">Bladwijzerbalk</translation>
<translation id="5199729219167945352">Experimenten</translation>
-<translation id="5199841536747119669">Je suggesties worden hier weergegeven</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="5299298092464848405">Fout bij het parseren van het beleid</translation>
@@ -399,8 +418,10 @@
<translation id="5308689395849655368">Crashrapportage is uitgeschakeld.</translation>
<translation id="5317780077021120954">Opslaan</translation>
<translation id="5327248766486351172">Naam</translation>
+<translation id="5337705430875057403">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 creditcards).</translation>
<translation id="5359637492792381994">De server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat is momenteel niet geldig. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Opslaan van beleidsinstellingen is mislukt</translation>
+<translation id="5386426401304769735">De certificaatketen voor deze site bevat een certificaat dat is ondertekend met SHA-1.</translation>
<translation id="5421136146218899937">Browsegegevens wissen...</translation>
<translation id="5430298929874300616">Bladwijzer verwijderen</translation>
<translation id="5431657950005405462">Je bestand is niet gevonden</translation>
@@ -421,17 +442,20 @@
<translation id="5544037170328430102">Een ingesloten pagina op <ph name="SITE" /> meldt het volgende:</translation>
<translation id="5556459405103347317">Opnieuw laden</translation>
<translation id="5565735124758917034">Actief</translation>
+<translation id="5572851009514199876">Start Chrome en log in zodat Chrome kan controleren of je deze site mag openen.</translation>
+<translation id="5580958916614886209">Controleer de vervalmaand en probeer het opnieuw</translation>
<translation id="560412284261940334">Beheer wordt niet ondersteund</translation>
<translation id="5610142619324316209">Controleer de verbinding</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> heeft je te vaak omgeleid.</translation>
<translation id="5622887735448669177">Wil je deze site verlaten?</translation>
<translation id="5629630648637658800">Laden van beleidsinstellingen is mislukt</translation>
<translation id="5631439013527180824">Ongeldige token voor apparaatbeheer</translation>
+<translation id="5669703222995421982">Gepersonaliseerde content ontvangen</translation>
+<translation id="5675650730144413517">Deze pagina werkt niet</translation>
<translation id="5677928146339483299">Geblokkeerd</translation>
<translation id="5694783966845939798">Je hebt geprobeerd <ph name="DOMAIN" /> te bereiken. De server heeft echter een certificaat geretourneerd dat een zwak ondertekeningsalgoritme gebruikt. Dit houdt in dat de betrouwbaarheidsverklaring van de server kan zijn vervalst. Het is mogelijk dat de server zelf een imitatie is (wellicht een server die je schade probeert te berokkenen). <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">De identiteit van deze website is niet geverifieerd.</translation>
<translation id="5720705177508910913">Huidige gebruiker</translation>
-<translation id="572328651809341494">Recent gebruikte tabbladen</translation>
<translation id="5732392974455271431">Je ouders kunnen de blokkering van deze site opheffen</translation>
<translation id="5784606427469807560">Er is een probleem opgetreden bij het bevestigen van je creditcard. Controleer je internetverbinding en probeer het opnieuw.</translation>
<translation id="5785756445106461925">Bovendien bevat deze pagina bronnen die niet beveiligd zijn. Deze bronnen kunnen tijdens verzending door anderen worden bekeken en kunnen door een aanvaller worden gewijzigd om het uiterlijk van de pagina aan te passen.</translation>
@@ -440,6 +464,7 @@
<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="5843436854350372569">Je probeert <ph name="DOMAIN" /> te bereiken, maar de server heeft een certificaat geretourneerd met een zwakke sleutel. Een hacker kan de persoonlijke sleutel hebben aangepast en het is mogelijk dat de server zelf een imitatie is (wellicht een server die je schade probeert te berokkenen). <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Nieuwe map</translation>
<translation id="5869405914158311789">Deze site is niet bereikbaar</translation>
@@ -449,6 +474,7 @@
<translation id="59107663811261420">Dit type kaart wordt voor deze verkoper niet ondersteund door Google Payments. Selecteer een andere kaart.</translation>
<translation id="59174027418879706">Ingeschakeld</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="5966707198760109579">Week</translation>
<translation id="5967867314010545767">Verwijderen uit geschiedenis</translation>
<translation id="5975083100439434680">Uitzoomen</translation>
@@ -466,8 +492,11 @@
<translation id="614940544461990577">Probeer het volgende:</translation>
<translation id="6151417162996330722">Het servercertificaat heeft een te lange geldigheidsperiode.</translation>
<translation id="6165508094623778733">Meer informatie</translation>
+<translation id="6177128806592000436">Je verbinding met deze site is niet veilig</translation>
<translation id="6203231073485539293">Controleer je internetverbinding</translation>
<translation id="6218753634732582820">Adres verwijderen uit Chromium?</translation>
+<translation id="6251924700383757765">Privacybeleid</translation>
+<translation id="625755898061068298">Je hebt ervoor gekozen beveiligingswaarschuwingen voor deze site uit te schakelen.</translation>
<translation id="6259156558325130047">&amp;Opnieuw volgorde wijzigen</translation>
<translation id="6263376278284652872">Bladwijzers voor <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Terug naar veilige website</translation>
@@ -476,6 +505,7 @@
<translation id="6305205051461490394"><ph name="URL" /> is niet bereikbaar.</translation>
<translation id="6321917430147971392">Controleer je DNS-instellingen</translation>
<translation id="6328639280570009161">Probeer netwerkvoorspelling uit te schakelen</translation>
+<translation id="6328786501058569169">Deze site is misleidend</translation>
<translation id="6337534724793800597">Beleid filteren op naam</translation>
<translation id="6342069812937806050">Zojuist</translation>
<translation id="6345221851280129312">onbekende grootte</translation>
@@ -484,6 +514,8 @@
<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="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>
<translation id="6416403317709441254">Je kunt <ph name="SITE" /> op dit moment niet bezoeken, omdat de website gecodeerde inloggegevens heeft verstuurd die niet door Chromium kunnen worden verwerkt. Aangezien netwerkfouten en aanvallen meestal van tijdelijke aard zijn, zal deze pagina later waarschijnlijk weer correct werken. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Kan niet controleren of het certificaat is ingetrokken.</translation>
@@ -493,10 +525,12 @@
<translation id="6458467102616083041">Genegeerd omdat de standaardzoekoptie door het beleid is uitgeschakeld.</translation>
<translation id="6462969404041126431">De server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat van de server is mogelijk ingetrokken. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Opnieuw verwijderen</translation>
<translation id="6534179046333460208">Fysieke web-suggesties</translation>
<translation id="6550675742724504774">Opties</translation>
+<translation id="6556239504065605927">Beveiligde verbinding</translation>
<translation id="6563469144985748109">Je beheerder heeft dit nog niet goedgekeurd</translation>
<translation id="6593753688552673085">minder dan <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opties voor encryptie</translation>
@@ -505,7 +539,6 @@
<translation id="6644283850729428850">Dit beleid is verouderd.</translation>
<translation id="6652240803263749613">De server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat van de server wordt niet vertrouwd door het besturingssysteem van je computer. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Map bewerken</translation>
-<translation id="6660210980321319655">Automatisch gemeld: <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Formuliersuggestie verwijderen uit Chromium?</translation>
<translation id="6685834062052613830">Uitloggen en configuratie voltooien</translation>
<translation id="6710213216561001401">Vorige</translation>
@@ -517,6 +550,7 @@
<translation id="6753269504797312559">Beleidswaarde</translation>
<translation id="6757797048963528358">De slaapstand van je apparaat is geactiveerd.</translation>
<translation id="6778737459546443941">Je ouder of voogd heeft dit nog niet goedgekeurd</translation>
+<translation id="6810899417690483278">Aanpassings-ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">De webpagina op <ph name="URL" /> is momenteel niet beschikbaar. De pagina bevat wellicht teveel verkeer of is offline wegens onderhoud.</translation>
<translation id="6831043979455480757">Vertalen</translation>
@@ -537,6 +571,8 @@
<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>
+<translation id="7064851114919012435">Contactgegevens</translation>
+<translation id="7079718277001814089">Deze site bevat malware</translation>
<translation id="7087282848513945231">County</translation>
<translation id="7088615885725309056">Ouder</translation>
<translation id="7090678807593890770">Zoek op Google naar <ph name="LINK" /></translation>
@@ -551,6 +587,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> voldoet niet aan de beveiligingsnormen.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Meer informatie<ph name="END_LINK" /> over dit probleem.</translation>
<translation id="7219179957768738017">De verbinding maakt gebruik van <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">De volgende website bevat malware</translation>
<translation id="724975217298816891">Geef de vervaldatum en CVC-code voor <ph name="CREDIT_CARD" /> op om je creditcardgegevens te updaten. Zodra je bevestigt, worden je creditcardgegevens gedeeld met deze site.</translation>
<translation id="725866823122871198">Er kan geen privéverbinding met <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> tot stand worden gebracht, omdat de datum en tijd van de computer (<ph name="DATE_AND_TIME" />) onjuist zijn.</translation>
<translation id="7269802741830436641">Deze webpagina bevat een omleidingslus</translation>
@@ -606,6 +643,7 @@ De volgende keer kan het handig zijn de incognitomodus <ph name="SHORTCUT_KEY" /
<translation id="7658239707568436148">Annuleren</translation>
<translation id="7667346355482952095">Geretourneerde beleidstoken is leeg of komt niet overeen met huidige token</translation>
<translation id="7668654391829183341">Onbekend apparaat</translation>
+<translation id="7669271284792375604">Cybercriminelen op deze site proberen je mogelijk over te halen om programma's te installeren die schadelijk zijn voor de browsefunctionaliteit (door bijvoorbeeld je homepage te wijzigen of extra advertenties weer te geven op sites die je bezoekt).</translation>
<translation id="7674629440242451245">Ben je geïnteresseerd in nieuwe, coole Chrome-functies? Probeer ons ontwikkelaarskanaal op chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Doorgaan naar <ph name="SITE" /> (onveilig)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Deze site kan niet worden geladen vanuit het cachegeheugen</translation>
@@ -621,14 +659,17 @@ De volgende keer kan het handig zijn de incognitomodus <ph name="SHORTCUT_KEY" /
<translation id="7800304661137206267">De verbinding is geëncrypt met <ph name="CIPHER" />, met <ph name="MAC" /> voor berichtverificatie en <ph name="KX" /> als mechanisme voor sleuteluitwisseling.</translation>
<translation id="780301667611848630">Nee, bedankt</translation>
<translation id="7805768142964895445">Status</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Formuliersuggestie verwijderen uit Chrome?</translation>
<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="7887683347370398519">Controleer je CVC-code en probeer het opnieuw</translation>
<translation id="7912024687060120840">In map:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Het servercertificaat is nog niet geldig.</translation>
<translation id="7942349550061667556">Rood</translation>
+<translation id="7947285636476623132">Controleer het vervaljaar en probeer het opnieuw</translation>
<translation id="7951415247503192394">(32-bits)</translation>
<translation id="7956713633345437162">Mobiele bladwijzers</translation>
<translation id="7961015016161918242">Nooit</translation>
@@ -636,7 +677,10 @@ De volgende keer kan het handig zijn de incognitomodus <ph name="SHORTCUT_KEY" /
<translation id="7983301409776629893"><ph name="ORIGINAL_LANGUAGE" /> altijd vertalen in het <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Niet opgegeven</translation>
<translation id="8012647001091218357">We kunnen je ouders momenteel niet bereiken. Probeer het opnieuw.</translation>
+<translation id="8025119109950072390">Cybercriminelen op deze site proberen je mogelijk te misleiden om iets gevaarlijks te doen, zoals software installeren of je persoonlijke gegevens bekendmaken (bijvoorbeeld wachtwoorden, telefoonnummers of creditcards).</translation>
+<translation id="803030522067524905">Google Safe Browsing heeft onlangs phishing gedetecteerd op <ph name="SITE" />. Phishingsites doen zich voor als een andere website om je te misleiden. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Deze pagina is in het <ph name="SOURCE_LANGUAGE" />. Vertalen naar het <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Feedback verzenden</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>
@@ -647,9 +691,11 @@ De volgende keer kan het handig zijn de incognitomodus <ph name="SHORTCUT_KEY" /
<translation id="8150722005171944719">Het bestand op <ph name="URL" /> is onleesbaar. Het bestand is mogelijk verwijderd of verplaatst of de bestandsrechten zorgen ervoor dat het bestand niet kan worden geopend.</translation>
<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="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="8230421197304563332">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 creditcarddetails).</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="8249320324621329438">Laatst opgehaald:</translation>
<translation id="8261506727792406068">Verwijderen</translation>
@@ -658,11 +704,13 @@ De volgende keer kan het handig zijn de incognitomodus <ph name="SHORTCUT_KEY" /
<translation id="8294431847097064396">Bron</translation>
<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="8349305172487531364">Bladwijzerbalk</translation>
<translation id="8363502534493474904">Schakel de vliegtuigmodus uit</translation>
<translation id="8364627913115013041">Niet ingesteld.</translation>
<translation id="8380941800586852976">Gevaarlijk</translation>
<translation id="8382348898565613901">Je onlangs bezochte bladwijzers worden hier weergegeven</translation>
+<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="8428213095426709021">Instellingen</translation>
@@ -674,9 +722,9 @@ De volgende keer kan het handig zijn de incognitomodus <ph name="SHORTCUT_KEY" /
<translation id="8498891568109133222">Het duurt te lang voordat <ph name="HOST_NAME" /> reageert.</translation>
<translation id="852346902619691059">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. <ph name="BEGIN_LEARN_MORE_LINK" />Meer informatie<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Dit type kaart wordt niet ondersteund door Google Payments. Selecteer een andere kaart.</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="8553075262323480129">De vertaling is mislukt omdat de taal van de pagina niet kan worden bepaald.</translation>
<translation id="8559762987265718583">Er kan geen privéverbinding met <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> tot stand worden gebracht, omdat de datum en tijd van je apparaat (<ph name="DATE_AND_TIME" />) onjuist zijn.</translation>
-<translation id="856992080682148">Het certificaat voor deze site verloopt in 2017 of later en de certificaatketen bevat een certificaat dat is ondertekend met SHA-1.</translation>
<translation id="8571890674111243710">Pagina wordt vertaald in het <ph name="LANGUAGE" />...</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>
@@ -689,10 +737,12 @@ De volgende keer kan het handig zijn de incognitomodus <ph name="SHORTCUT_KEY" /
<translation id="8740359287975076522">Het &lt;abbr id="dnsDefinition"&gt;DNS-adres&lt;/abbr&gt; van <ph name="HOST_NAME" /> kan niet worden gevonden. Er wordt een diagnose van het probleem uitgevoerd.</translation>
<translation id="8790007591277257123">&amp;Opnieuw verwijderen</translation>
<translation id="8798099450830957504">Standaard</translation>
+<translation id="8800988563907321413">Je suggesties voor in de buurt worden hier weergegeven</translation>
<translation id="8804164990146287819">Privacybeleid</translation>
<translation id="8820817407110198400">Bladwijzers</translation>
<translation id="8834246243508017242">Automatisch aanvullen inschakelen met contacten…</translation>
<translation id="883848425547221593">Andere bladwijzers</translation>
+<translation id="884264119367021077">Verzendadres</translation>
<translation id="884923133447025588">Geen intrekkingsmechanisme gevonden.</translation>
<translation id="885730110891505394">Delen met Google</translation>
<translation id="8866481888320382733">Fout bij het parseren van beleidsinstellingen</translation>
@@ -710,18 +760,21 @@ De volgende keer kan het handig zijn de incognitomodus <ph name="SHORTCUT_KEY" /
<translation id="8971063699422889582">Het servercertificaat is verlopen.</translation>
<translation id="8987927404178983737">Maand</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">De volgende site bevat schadelijke programma's</translation>
<translation id="9001074447101275817">Voor de proxy <ph name="DOMAIN" /> zijn een gebruikersnaam en wachtwoord vereist.</translation>
<translation id="901974403500617787">Markeringen die op het hele systeem van toepassing zijn, kunnen alleen worden ingesteld door de eigenaar: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Deze pagina is vertaald naar het <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Beveiligingsfout</translation>
<translation id="9038649477754266430">Een voorspellingsservice gebruiken om pagina's sneller te laden</translation>
<translation id="9039213469156557790">Bovendien bevat deze pagina bronnen die niet beveiligd zijn. Deze bronnen kunnen tijdens verzending door anderen worden bekeken en kunnen door een aanvaller worden gewijzigd om het gedrag van de pagina aan te passen.</translation>
+<translation id="9040185888511745258">Cybercriminelen op <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> proberen je mogelijk over te halen om programma's te installeren die schadelijk zijn voor je surfervaring (door bijvoorbeeld je homepage te wijzigen of extra advertenties weer te geven op sites die je bezoekt).</translation>
<translation id="9050666287014529139">Wachtwoordzin</translation>
<translation id="9065203028668620118">Bewerken</translation>
<translation id="9068849894565669697">Kleur selecteren</translation>
<translation id="9076283476770535406">De site kan content voor volwassenen bevatten</translation>
-<translation id="9092364396508701805">De pagina op <ph name="HOST_NAME" /> werkt niet</translation>
<translation id="9103872766612412690"><ph name="SITE" /> gebruikt gewoonlijk versleuteling om je gegevens te beschermen. Toen Chromium deze keer probeerde verbinding te maken met <ph name="SITE" />, retourneerde de website ongewone en onjuiste inloggegevens. Dit gebeurt wanneer een aanvaller probeert zich als <ph name="SITE" /> voor te doen of wanneer een wifi-inlogscherm de verbinding heeft verbroken. Je gegevens zijn nog steeds veilig omdat Chromium de verbinding heeft beëindigd voordat er gegevens konden worden uitgewisseld.</translation>
<translation id="9137013805542155359">Origineel weergeven</translation>
+<translation id="9137248913990643158">Start Chrome en log in voordat je deze app gebruikt.</translation>
<translation id="9148507642005240123">&amp;Bewerken ongedaan maken</translation>
<translation id="9157595877708044936">Bezig met instellen...</translation>
<translation id="9170848237812810038">&amp;Ongedaan maken</translation>
@@ -734,7 +787,6 @@ De volgende keer kan het handig zijn de incognitomodus <ph name="SHORTCUT_KEY" /
<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="962701380617707048">Geef de vervaldatum en CVC-code voor <ph name="CREDIT_CARD" /> op om je creditcardgegevens te updaten</translation>
<translation id="969892804517981540">Officiële build</translation>
<translation id="988159990683914416">Ontwikkelaarsbuild</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 9a4fe1e6a90..4e8506f8aa2 100644
--- a/chromium/components/strings/components_strings_no.xtb
+++ b/chromium/components/strings/components_strings_no.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Koble til Wi-Fi på nytt</translation>
<translation id="1175364870820465910">&amp;Skriv ut...</translation>
<translation id="1181037720776840403">Fjern</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Rapportér<ph name="END_WHITEPAPER_LINK" /> automatisk detaljer om mulige sikkerhetsbrudd til Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Neste</translation>
<translation id="1201895884277373915">Mer fra dette nettstedet</translation>
<translation id="1206967143813997005">Ugyldig start på signaturen</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cyan</translation>
<translation id="1629803312968146339">Vil du at Chrome skal lagre dette kortet?</translation>
<translation id="1640180200866533862">Brukerretningslinjer</translation>
+<translation id="1640244768702815859">Prøv <ph name="BEGIN_LINK" />å gå til startsiden for nettstedet<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Nettverkskonfigurasjonen er ugyldig og kan ikke importeres.</translation>
<translation id="1644574205037202324">Logg</translation>
<translation id="1645368109819982629">Protokollen støttes ikke</translation>
<translation id="1676269943528358898"><ph name="SITE" /> bruker vanligvis kryptering for å beskytte informasjonen din. Da Chrome prøvde å koble til <ph name="SITE" /> denne gangen, sendte nettstedet tilbake uvanlig og feil legitimasjon. Dette kan skje hvis en angriper prøver å utgi seg for å være <ph name="SITE" />, eller hvis en Wi-Fi-påloggingsskjerm har avbrutt tilkoblingen. Informasjonen din er likevel sikker fordi Chrome stoppet tilkoblingen før det ble utvekslet noen data.</translation>
+<translation id="168328519870909584">Hackere som for øyeblikket er på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, prøver kanskje å installere farlige apper på enheten din. Disse appene kan stjele eller slette informasjonen din (for eksempel bilder, passord, meldinger og kredittkortinformasjon).</translation>
<translation id="168841957122794586">Tjenersertifikatet inneholder en svak kryptografisk nøkkel.</translation>
-<translation id="1701955595840307032">Få foreslått innhold</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Du trenger tillatelse fra <ph name="NAME" /> for å besøke dette nettstedet</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Sidenummer</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Prøv å kjøre Windows Nettverksdiagnose<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Oppdater passordfrasen for synkronisering.</translation>
+<translation id="1787142507584202372">De åpne fanene dine vises her</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google Safe Browsing oppdaget nylig <ph name="BEGIN_LINK" />skadelig programvare<ph name="END_LINK" /> på <ph name="SITE" />. Nettsteder som vanligvis er pålitelige, kan av og til bli infisert med skadelig programvare. Det skadelige innholdet kommer fra <ph name="SUBRESOURCE_HOST" />, som er en kjent distributør av skadelig programvare. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Ugyldig forespørsel eller forespørselsparametere</translation>
+<translation id="1834321415901700177">Dette nettstedet inneholder skadelige programmer</translation>
<translation id="1838667051080421715">Du ser på kildekoden for en nettside.</translation>
<translation id="1871208020102129563">Mellomtjeneren er angitt til å bruke statiske proxytjenere, ikke en nettadresse med .pac-skript.</translation>
<translation id="1883255238294161206">Skjul liste</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Bokmerker for mobil</translation>
<translation id="2148716181193084225">I dag</translation>
-<translation id="2149973817440762519">Rediger bokmerke</translation>
<translation id="2154054054215849342">Synkronisering er ikke tilgjengelig for domenet ditt</translation>
<translation id="2166049586286450108">Full administratortilgang</translation>
<translation id="2166378884831602661">Dette nettstedet tilbyr ikke sikre tilkoblinger</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Du kan ikke gå til <ph name="SITE" /> akkurat nå, fordi nettstedet bruker HSTS. Nettverksfeil og -angrep er som regel kortvarige, så denne siden fungerer nok igjen senere. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Ignorerte ugyldig bokmerke ved indeks <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Andre bokmerker</translation>
+<translation id="2355395290879513365">Angripere kan kanskje se bildene du ser på dette nettstedet, og lure deg ved å endre dem.</translation>
<translation id="2359808026110333948">Fortsett</translation>
<translation id="2365563543831475020">Programstopprapporten fra <ph name="CRASH_TIME" /> ble ikke lastet opp</translation>
<translation id="2367567093518048410">Nivå</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Vis innstillinger uten verdi</translation>
<translation id="2396249848217231973">&amp;Angre slettingen</translation>
<translation id="2455981314101692989">Denne nettsiden har deaktivert automatisk utfylling for dette skjemaet.</translation>
+<translation id="2460160116472764928">Google Safe Browsing oppdaget nylig <ph name="BEGIN_LINK" />skadelig programvare<ph name="END_LINK" /> på <ph name="SITE" />. Nettsteder som vanligvis er pålitelige, kan av og til bli infisert med skadelig programvare. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Fyll ut</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Kjør Nettverksdiagnose<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Ugyldig nettadresse for søk.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Er du sikker på at du vil slette disse sidene fra loggen?</translation>
<translation id="2677748264148917807">GÃ¥ ut</translation>
<translation id="269990154133806163">Tjeneren presenterte et sertifikat som ikke er offentlig vist i henhold til retningslinjene for sertifikatåpenhet. Dette er et krav for enkelte sertifikater, for å sikre at de er pålitelige, og bidrar til å beskytte mot angrep. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Leseliste</translation>
<translation id="2704283930420550640">Verdien samsvarer ikke med formatet.</translation>
<translation id="2704951214193499422">Chromium kunne ikke bekrefte kortet ditt akkurat nå. Prøv igjen senere.</translation>
<translation id="2705137772291741111">Den lagrede (bufrede) kopien av dette nettstedet kunne ikke leses.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Du prøvde å gå til <ph name="DOMAIN" />, men tjeneren presenterte et sertifikat som er trukket tilbake av utstederen. Dette innebærer at sikkerhetsinformasjonen tjeneren presenterte, ikke er klarert. Det kan hende at du kommuniserer med en angriper. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Be om tillatelse</translation>
<translation id="2713444072780614174">Hvit</translation>
+<translation id="2720342946869265578">Like ved</translation>
<translation id="2721148159707890343">Forespørselen var vellykket</translation>
<translation id="2728127805433021124">Tjenerens sertifikat er signert med en usikker signaturalgoritme.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Kjør Tilkoblingsdiagnostikk<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">PÃ¥ innstillingssiden kan du deaktivere eventuelle mellomtjenere for tilkoblinger.</translation>
<translation id="2955913368246107853">Lukk søkefelt</translation>
<translation id="2958431318199492670">Nettverkskonfigurasjonen overholder ikke ONC-standarden. Deler av konfigurasjonen kan muligens ikke importeres.</translation>
+<translation id="29611076221683977">Hackere som for øyeblikket er på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> prøver kanskje å installere farlige programmer på Mac-en din. Disse programmene kan stjele eller slette informasjonen din (for eksempel bilder, passord, e-post og kredittkortinformasjon).</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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Skjul detaljer</translation>
<translation id="3587482841069643663">Alle</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Tjeneren håndterer ikke TLS-utvidelsen for reforhandling.</translation>
<translation id="36224234498066874">Slett nettlesingsdata...</translation>
<translation id="362276910939193118">Vis fullstendig logg</translation>
<translation id="3623476034248543066">Vis verdien</translation>
@@ -271,19 +279,20 @@
<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="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="3696411085566228381">ingen</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="370665806235115550">Laster inn ...</translation>
<translation id="3712624925041724820">Lisensene er oppbrukt</translation>
<translation id="3714780639079136834">Slå på mobildata eller Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Sjekk proxy-tjener-, brannmur- og DNS-konfigurasjonen<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">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="3739623965217189342">En link du kopierte</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="3788090790273268753">Sertifikatet for dette nettstedet utløper i 2016, og sertifikatkjeden inneholder et sertifikat som er signert med SHA-1.</translation>
<translation id="382518646247711829">Hvis du bruker en mellomtjener...</translation>
<translation id="3828924085048779000">Tom passordfrase er ikke tillatt.</translation>
<translation id="3845539888601087042">Viser loggen fra enhetene du er logget på. <ph name="BEGIN_LINK" />Finn ut mer<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Nøkkel – «<ph name="SUBKEY" />»: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Klienten og tjeneren støtter ingen felles SSL-protokollversjon eller -chifferserie.</translation>
<translation id="4079302484614802869">Konfigurasjonen av proxytjeneren er angitt til å bruke en nettadresse med .pac-skript, ikke statiske proxytjenere.</translation>
+<translation id="4098354747657067197">Villedende nettsted i sikte</translation>
<translation id="4103249731201008433">Enhetens serienummer er ugyldig</translation>
<translation id="4103763322291513355">Gå til &lt;strong&gt;chrome://policy&lt;/strong&gt; for å se listen over sperrede nettadresser og andre innstillinger aktivert av systemadministratoren din.</translation>
<translation id="4110615724604346410">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />, ettersom sikkerhetssertifikatet inneholder feil. Dette kan skyldes en feilkonfigurasjon eller at en angriper avskjærer tilkoblingen din. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ingen}=1{1 app ($1)}=2{2 apper ($1, $2)}other{# apper ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Kræsj</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Prøv å kjøre Nettverksdiagnose<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Tilkoblingen til dette nettstedet er ikke helt sikker</translation>
<translation id="4250680216510889253">Nei</translation>
<translation id="425582637250725228">Det kan hende endringene dine ikke er lagret.</translation>
<translation id="4258748452823770588">DÃ¥rlig signatur</translation>
<translation id="4269787794583293679">(Uten brukernavn)</translation>
+<translation id="4280429058323657511">, utløper <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google Safe Browsing fant nylig <ph name="BEGIN_LINK" />skadelige programmer<ph name="END_LINK" /> på <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Overordnede forslag</translation>
<translation id="4304224509867189079">Logg på</translation>
<translation id="432290197980158659">Tjeneren presenterte et sertifikat som ikke samsvarer med innebygde forventninger. Disse forventningene benyttes for visse nettsteder med høy sikkerhet og brukes for å beskytte deg. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Artikkelen ble ikke funnet</translation>
+<translation id="4326324639298822553">Kontrollér utløpsdatoen, og prøv igjen</translation>
<translation id="4331708818696583467">Ikke sikker</translation>
+<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="4381091992796011497">Brukernavn:</translation>
<translation id="4394049700291259645">Slå av</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Du prøvde å gå til <ph name="DOMAIN" />, men tjeneren presenterte et ugyldig sertifikat. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Faner fra andre enheter</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Dette er en utvidelsesside.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Last inn retningslinjer på nytt</translation>
<translation id="4728558894243024398">Plattform</translation>
<translation id="4744603770635761495">Kjørbar sti</translation>
+<translation id="4750917950439032686">Informasjonen din (for eksempel passord eller kredittkortnumre) er privat når den sendes til dette nettstedet.</translation>
<translation id="4756388243121344051">&amp;Logg</translation>
+<translation id="4759118997339041434">Autofyll av betalingsinformasjon er slått av</translation>
<translation id="4764776831041365478">Det kan hende at nettsiden på <ph name="URL" /> er midlertidig nede eller flyttet permanent til en ny nettadresse.</translation>
<translation id="4771973620359291008">Det har oppstått en ukjent feil.</translation>
<translation id="4800132727771399293">Kontrollér utløpsdatoen og CVC-koden, og prøv igjen.</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Nettverksfeil</translation>
<translation id="4816492930507672669">Tilpass til siden</translation>
<translation id="4850886885716139402">Visning</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{og 1 nettside til}other{og # nettsider til}}</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="495170559598752135">Handlinger</translation>
<translation id="4958444002117714549">Utvid liste</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Laster ned</translation>
<translation id="5190835502935405962">Bokmerkerad</translation>
<translation id="5199729219167945352">Eksperimenter</translation>
-<translation id="5199841536747119669">Forslagene dine vises her</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="5299298092464848405">Feil under analysen av enhetsinnstillingene</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Rapportering av programstopp er deaktivert.</translation>
<translation id="5317780077021120954">Lagre</translation>
<translation id="5327248766486351172">Navn</translation>
+<translation id="5337705430875057403">Angripere på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> prøver kanskje å lure deg til å gjøre farlige ting som å installere programvare eller avsløre personopplysningene dine (for eksempel passord, telefonnumre eller kredittkortinformasjon).</translation>
<translation id="5359637492792381994">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />, ettersom sikkerhetssertifikatet ikke er gyldig for øyeblikket. Dette kan skyldes en feilkonfigurasjon eller at en angriper avskjærer tilkoblingen din. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Kunne ikke lagre angivelsen for enhetsinnstillinger</translation>
+<translation id="5386426401304769735">Sertifikatkjeden for dette nettstedet inneholder et sertifikat som er signert med SHA-1.</translation>
<translation id="5421136146218899937">Slett nettlesingsdata...</translation>
<translation id="5430298929874300616">Fjern bokmerke</translation>
<translation id="5431657950005405462">Filen ble ikke funnet</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">En innebygd side på <ph name="SITE" /> sier:</translation>
<translation id="5556459405103347317">Last inn på nytt</translation>
<translation id="5565735124758917034">Aktiv</translation>
+<translation id="5572851009514199876">Start og logg på Chrome, så Chrome kan sjekke om du har tillatelse til å gå til dette nettstedet.</translation>
+<translation id="5580958916614886209">Kontrollér utløpsmåneden, og prøv igjen</translation>
<translation id="560412284261940334">Administrering støttes ikke</translation>
<translation id="5610142619324316209">Sjekk tilkoblingen</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> viderekoblet deg for mange ganger.</translation>
<translation id="5622887735448669177">Vil du forlate dette nettstedet?</translation>
<translation id="5629630648637658800">Kunne ikke laste in angivelsen for enhetsinnstillinger</translation>
<translation id="5631439013527180824">Ugyldig token for enhetsadministrering</translation>
+<translation id="5669703222995421982">FÃ¥ innhold med et personlig preg</translation>
+<translation id="5675650730144413517">Denne siden fungerer ikke</translation>
<translation id="5677928146339483299">Blokkert</translation>
<translation id="5694783966845939798">Du prøvde å gå til <ph name="DOMAIN" />, men tjeneren presenterte et sertifikat som er signert med en usikker signaturalgoritme (som SHA-1). Dette betyr at sikkerhetslegitimasjonen tjeneren har presentert, kan være forfalsket. Tjeneren kan med andre ord være en annen tjener enn du tror (og du kommuniserer kanskje med en angriper). <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Identiteten til dette nettstedet er ikke verifisert.</translation>
<translation id="5720705177508910913">Gjeldende bruker</translation>
-<translation id="572328651809341494">Nylige faner</translation>
<translation id="5732392974455271431">Foreldrene dine kan oppheve blokkeringen for deg</translation>
<translation id="5784606427469807560">Det oppsto et problem under forsøket på å bekrefte kortet ditt. Kontrollér Internett-tilkoblingen din, og prøv igjen.</translation>
<translation id="5785756445106461925">Denne siden inneholder i tillegg andre ressurser som ikke er sikre. Disse ressursene er synlige for andre mens de sendes frem og tilbake, og eventuelle angripere kan modifisere dem for å endre på utseendet til siden.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Du prøvde å gå til <ph name="DOMAIN" />, men tjeneren presenterte et sertifikat som inneholder en usikker nøkkel. En angriper kan ha brutt sikkerheten i den private nøkkelen, og tjeneren er kanskje ikke den tjeneren du forventet (det kan hende at du kommuniserer med en angriper). <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Ny mappe</translation>
<translation id="5869405914158311789">Dette nettstedet er ikke tilgjengelig</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Denne korttypen støttes ikke av Google Payments for denne selgeren. Velg et annet kort.</translation>
<translation id="59174027418879706">Aktivert</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="5966707198760109579">Uke</translation>
<translation id="5967867314010545767">Fjern fra loggen</translation>
<translation id="5975083100439434680">Zoom ut</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Prøv dette:</translation>
<translation id="6151417162996330722">Tjenersertifikatet har en gyldighetsperiode som er for lang.</translation>
<translation id="6165508094623778733">Les mer</translation>
+<translation id="6177128806592000436">Tilkoblingen til dette nettstedet er ikke sikker</translation>
<translation id="6203231073485539293">Kontrollér Internett-tilkoblingen</translation>
<translation id="6218753634732582820">Vil du fjerne adressen fra Chromium?</translation>
+<translation id="6251924700383757765">Personvern</translation>
+<translation id="625755898061068298">Du har valgt å slå av sikkerhetsadvarsler for dette nettstedet.</translation>
<translation id="6259156558325130047">&amp;Omorganiser likevel</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" />-bokmerker</translation>
<translation id="6264485186158353794">Tilbake til trygg grunn</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> er ikke tilgjengelig.</translation>
<translation id="6321917430147971392">Kontrollér DNS-innstillingene dine</translation>
<translation id="6328639280570009161">Prøv å slå av nettverksforutsigelse</translation>
+<translation id="6328786501058569169">Dette nettstedet er villedende</translation>
<translation id="6337534724793800597">Filtrér retningslinjer etter navn</translation>
<translation id="6342069812937806050">Akkurat nå</translation>
<translation id="6345221851280129312">ukjent størrelse</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Du kan ikke gå til <ph name="SITE" /> akkurat nå fordi nettstedet sendte kryptert legitimasjon som Chromium ikke kan behandle. Nettverksfeil og -angrep er som regel kortvarige, så denne siden fungerer nok igjen senere. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Kan ikke kontrollere hvorvidt sertifikatet har blitt tilbakekalt.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Ignorert fordi standardsøk er deaktivert i henhold til retningslinje.</translation>
<translation id="6462969404041126431">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />, ettersom sikkerhetssertifikatet kan ha blitt trukket tilbake. Dette kan skyldes en feilkonfigurasjon eller at en angriper avskjærer tilkoblingen din. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Slett likevel</translation>
<translation id="6534179046333460208">Fysisk nett-forslag</translation>
<translation id="6550675742724504774">Alternativer</translation>
+<translation id="6556239504065605927">Sikker tilkobling</translation>
<translation id="6563469144985748109">Administratoren din har ikke godkjent det ennå</translation>
<translation id="6593753688552673085">mindre enn <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Krypteringsalternativer</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Denne retningslinjen er foreldet.</translation>
<translation id="6652240803263749613">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />, ettersom sikkerhetssertifikatet ikke er regnet som pålitelig av operativsystemet på datamaskinen din. Dette kan skyldes en feilkonfigurasjon eller at en angriper avskjærer tilkoblingen din. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Mapperedigering</translation>
-<translation id="6660210980321319655">Automatisk rapportert <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Retningslinjeverdi</translation>
<translation id="6757797048963528358">Enheten din gikk inn i hvilemodus.</translation>
<translation id="6778737459546443941">Forelderen din har ikke godkjent det ennå</translation>
+<translation id="6810899417690483278">Tilpasnings-ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Nettsiden på <ph name="URL" /> er for tiden utilgjengelig. Den kan være overbelastet eller under vedlikehold.</translation>
<translation id="6831043979455480757">Oversett</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Kontaktinformasjon</translation>
+<translation id="7079718277001814089">Dette nettstedet inneholder skadelig programvare</translation>
<translation id="7087282848513945231">Fylke</translation>
<translation id="7088615885725309056">Eldre</translation>
<translation id="7090678807593890770">Søk på Google etter <ph name="LINK" /></translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> retter seg ikke etter sikkerhetsstandardene.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Mer informasjon<ph name="END_LINK" /> om dette problemet.</translation>
<translation id="7219179957768738017">Tilkoblingen bruker <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Nettstedet du er på vei til, inneholder skadelig programvare</translation>
<translation id="724975217298816891">Skriv inn utløpsdatoen og verifiseringskoden for <ph name="CREDIT_CARD" /> for å oppdatere kortinformasjonen din. Når du bekrefter, deles denne informasjonen med dette nettstedet.</translation>
<translation id="725866823122871198">En privat forbindelse til <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> kan ikke opprettes fordi datoen og klokkeslettet (<ph name="DATE_AND_TIME" />) på datamaskinen er feil.</translation>
<translation id="7269802741830436641">Denne nettsiden har en omdirigeringssløyfe</translation>
@@ -611,6 +648,7 @@ Forresten: Inkognitomodus (<ph name="SHORTCUT_KEY" />) kan være kjekt neste gan
<translation id="7658239707568436148">Avbryt</translation>
<translation id="7667346355482952095">Det returnerte regeltokenet er tomt eller samsvarer ikke med det nåværende tokenet</translation>
<translation id="7668654391829183341">Ukjent enhet</translation>
+<translation id="7669271284792375604">Angripere på dette nettstedet kan forsøke å lure deg til å installere programmer som ødelegger surfeopplevelsen din (for eksempel ved å endre startsiden din eller vise ekstra annonser på nettstedene du besøker).</translation>
<translation id="7674629440242451245">Interessert i nye kule Chrome-funksjoner? Prøv utviklerkanalen vår på chrome.com/dev</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Fortsett til <ph name="SITE" /> (usikker side)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Dette nettstedet kan ikke lastes inn fra bufferen</translation>
@@ -626,14 +664,17 @@ Forresten: Inkognitomodus (<ph name="SHORTCUT_KEY" />) kan være kjekt neste gan
<translation id="7800304661137206267">Tilkoblingen er kryptert ved hjelp av <ph name="CIPHER" />, med <ph name="MAC" /> for autentisering av meldinger og <ph name="KX" /> som nøkkelutvekslingsmekanisme.</translation>
<translation id="780301667611848630">Nei takk</translation>
<translation id="7805768142964895445">Status</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Vil du fjerne skjemaforslaget fra Chrome?</translation>
<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="7887683347370398519">Kontrollér CVC-koden din, og prøv igjen.</translation>
<translation id="7912024687060120840">I mappen:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Tjenerens sertifikat er ikke gyldig ennå.</translation>
<translation id="7942349550061667556">Rød</translation>
+<translation id="7947285636476623132">Kontrollér utløpsdatoen, og prøv igjen</translation>
<translation id="7951415247503192394">(32-bit)</translation>
<translation id="7956713633345437162">Bokmerker for mobil</translation>
<translation id="7961015016161918242">Aldri</translation>
@@ -641,7 +682,10 @@ Forresten: Inkognitomodus (<ph name="SHORTCUT_KEY" />) kan være kjekt neste gan
<translation id="7983301409776629893">Oversett alltid <ph name="ORIGINAL_LANGUAGE" /> til <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Ikke spesifisert</translation>
<translation id="8012647001091218357">Vi kunne ikke nå foreldrene dine akkurat nå. Prøv igjen.</translation>
+<translation id="8025119109950072390">Angripere på dette nettstedet prøver kanskje å lure deg til å gjøre farlige ting som å installere programvare eller avsløre personopplysningene dine (for eksempel passord, telefonnumre eller kredittkortinformasjon).</translation>
+<translation id="803030522067524905">Google Safe Browsing oppdaget nylig nettfisking på <ph name="SITE" />. Nettsteder som driver med nettfisking, later som om de er andre nettsteder for å lure deg. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Denne siden er på <ph name="SOURCE_LANGUAGE" />. Vil du oversette den til <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Send tilbakemelding</translation>
<translation id="8088680233425245692">Kunne ikke åpne artikkelen.</translation>
<translation id="8089520772729574115">under 1 MB</translation>
<translation id="8091372947890762290">Aktivering venter på tjeneren</translation>
@@ -652,9 +696,11 @@ Forresten: Inkognitomodus (<ph name="SHORTCUT_KEY" />) kan være kjekt neste gan
<translation id="8150722005171944719">Kunne ikke lese filen på <ph name="URL" /> Den kan ha blitt fjernet eller flyttet. Det kan også være filtillatelser som forhindrer tilgang.</translation>
<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="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="8230421197304563332">Hackere som for øyeblikket er på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, prøver kanskje å installere farlige programmer på datamaskinen din. Disse kan stjele eller slette informasjonen din (for eksempel bilder, passord, e-post og kredittkortinformasjon).</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="8249320324621329438">Sist hentet:</translation>
<translation id="8261506727792406068">Slett</translation>
@@ -663,11 +709,13 @@ Forresten: Inkognitomodus (<ph name="SHORTCUT_KEY" />) kan være kjekt neste gan
<translation id="8294431847097064396">Kilde</translation>
<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="8349305172487531364">Bokmerkerad</translation>
<translation id="8363502534493474904">Slå av flymodus</translation>
<translation id="8364627913115013041">Ikke angitt.</translation>
<translation id="8380941800586852976">Farlig</translation>
<translation id="8382348898565613901">Bokmerkene du nylig har besøkt, vises her</translation>
+<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="8428213095426709021">Innstillinger</translation>
@@ -679,9 +727,9 @@ Forresten: Inkognitomodus (<ph name="SHORTCUT_KEY" />) kan være kjekt neste gan
<translation id="8498891568109133222"><ph name="HOST_NAME" /> brukte for lang tid på å svare.</translation>
<translation id="852346902619691059">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />, ettersom sikkerhetssertifikatet ikke er regnet som pålitelig av operativsystemet på enheten din. Dette kan skyldes en feilkonfigurasjon eller at en angriper avskjærer tilkoblingen din. <ph name="BEGIN_LEARN_MORE_LINK" />Finn ut mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Denne korttypen støttes ikke av Google Payments. Velg et annet kort.</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="8553075262323480129">Oversettelsen mislyktes fordi sidens språk ikke kunne fastslås.</translation>
<translation id="8559762987265718583">En privat tilkobling til <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> kunne ikke etableres fordi datoen og klokkeslettet (<ph name="DATE_AND_TIME" />) er feil på enheten.</translation>
-<translation id="856992080682148">Sertifikatet for dette nettstedet utløper i 2017 eller senere, og sertifikatkjeden inneholder et sertifikat som er signert med SHA-1.</translation>
<translation id="8571890674111243710">Oversett siden til <ph name="LANGUAGE" /></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>
@@ -694,10 +742,12 @@ Forresten: Inkognitomodus (<ph name="SHORTCUT_KEY" />) kan være kjekt neste gan
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;DNS-adressen&lt;/abbr&gt; til <ph name="HOST_NAME" /> ble ikke funnet. Problemet diagnostiseres.</translation>
<translation id="8790007591277257123">&amp;Slett likevel</translation>
<translation id="8798099450830957504">Standard</translation>
+<translation id="8800988563907321413">Forslagene dine om ting like ved vises her</translation>
<translation id="8804164990146287819">Personvernregler</translation>
<translation id="8820817407110198400">Bokmerker</translation>
<translation id="8834246243508017242">Aktiver autofyll for Kontakter</translation>
<translation id="883848425547221593">Andre bokmerker</translation>
+<translation id="884264119367021077">Leveringsadresse</translation>
<translation id="884923133447025588">Finner ingen tilbakekallingsmekanisme.</translation>
<translation id="885730110891505394">Deling med Google</translation>
<translation id="8866481888320382733">Analysefeil i angivelsen av enhetrsinnstillinger</translation>
@@ -715,18 +765,21 @@ Forresten: Inkognitomodus (<ph name="SHORTCUT_KEY" />) kan være kjekt neste gan
<translation id="8971063699422889582">Tjenerens sertifikat er utløpt.</translation>
<translation id="8987927404178983737">MÃ¥ned</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Nettstedet du er i ferd med å åpne, inneholder skadelige programmer</translation>
<translation id="9001074447101275817">Proxy-tjeneren <ph name="DOMAIN" /> krever brukernavn og passord.</translation>
<translation id="901974403500617787">Rapporteringer som gjelder for hele systemet kan bare angis av eieren: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Denne siden har blitt oversatt til <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Sikkerhetsfeil</translation>
<translation id="9038649477754266430">Bruk en prediksjonstjeneste for å laste inn sider raskere</translation>
<translation id="9039213469156557790">Denne siden inneholder i tillegg andre ressurser som ikke er sikre. Disse ressursene er synlige for andre mens de sendes frem og tilbake, og eventuelle angripere kan modifisere dem for å endre på atferden til siden.</translation>
+<translation id="9040185888511745258">Angripere på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kan forsøke å lure deg til å installere programmer som ødelegger surfeopplevelsen din (for eksempel ved å endre startsiden din eller vise ekstra annonser på nettstedene du besøker).</translation>
<translation id="9050666287014529139">Passordfrase</translation>
<translation id="9065203028668620118">Endre</translation>
<translation id="9068849894565669697">Velg farge</translation>
<translation id="9076283476770535406">Det kan ha voksent innhold</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" />-siden fungerer ikke</translation>
<translation id="9103872766612412690"><ph name="SITE" /> bruker vanligvis kryptering for å beskytte informasjonen din. Da Chromium prøvde å koble til <ph name="SITE" /> denne gangen, sendte nettstedet tilbake uvanlig og feil legitimasjon. Dette kan skje hvis en angriper prøver å utgi seg for å være <ph name="SITE" />, eller hvis en Wi-Fi-påloggingsskjerm har avbrutt tilkoblingen. Informasjonen din er likevel sikker fordi Chromium stoppet tilkoblingen før det ble utvekslet noen data.</translation>
<translation id="9137013805542155359">Vis original</translation>
+<translation id="9137248913990643158">Du må starte og logge på Chrome før du bruker denne appen.</translation>
<translation id="9148507642005240123">&amp;Angre endringen</translation>
<translation id="9157595877708044936">Konfigurerer ...</translation>
<translation id="9170848237812810038">&amp;Angre</translation>
@@ -739,7 +792,6 @@ Forresten: Inkognitomodus (<ph name="SHORTCUT_KEY" />) kan være kjekt neste gan
<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="962701380617707048">Skriv inn utløpsdatoen og verifiseringskoden for <ph name="CREDIT_CARD" /> for å oppdatere kortinformasjonen din</translation>
<translation id="969892804517981540">Offisiell delversjon</translation>
<translation id="988159990683914416">Utviklerversjon</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 029d20bff16..212302657ae 100644
--- a/chromium/components/strings/components_strings_pl.xtb
+++ b/chromium/components/strings/components_strings_pl.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Ponownie połącz się z Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Drukuj...</translation>
<translation id="1181037720776840403">Usuń</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Automatycznie przesyłaj<ph name="END_WHITEPAPER_LINK" /> do Google szczegółowe informacje o możliwych zagrożeniach. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Dalej</translation>
<translation id="1201895884277373915">Więcej z tej witryny</translation>
<translation id="1206967143813997005">Nieprawidłowy podpis wstępny</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cyjan</translation>
<translation id="1629803312968146339">Czy Chrome ma zapisać tę kartę?</translation>
<translation id="1640180200866533862">Zasady dotyczące użytkowników</translation>
+<translation id="1640244768702815859">Otwórz <ph name="BEGIN_LINK" />stronę główną witryny<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Konfiguracja sieci jest nieprawidłowa i nie można jej zaimportować.</translation>
<translation id="1644574205037202324">Historia</translation>
<translation id="1645368109819982629">Nieobsługiwany protokół</translation>
<translation id="1676269943528358898"><ph name="SITE" /> zazwyczaj używa szyfrowania do ochrony Twoich informacji. Gdy tym razem Google Chrome próbował połączyć się ze stroną <ph name="SITE" />, odesłała ona nietypowe i nieprawidłowe dane logowania. Może się tak zdarzyć, gdy pod stronę <ph name="SITE" /> podszywa się atakująca osoba albo gdy ekran logowania do sieci Wi-Fi przerwie połączenie. Twoje informacje są nadal bezpieczne, bo połączenie w Google Chrome zakończyło się przed wymianą jakichkolwiek danych.</translation>
+<translation id="168328519870909584">Hakerzy mogliby wykorzystać stronę <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, by zainstalować na Twoim urządzeniu niebezpieczne aplikacje, które mogłyby wykraść lub skasować Twoje dane (takie jak zdjęcia, hasła, wiadomości czy numery kart kredytowych).</translation>
<translation id="168841957122794586">Certyfikat serwera ma słaby klucz kryptograficzny.</translation>
-<translation id="1701955595840307032">Uzyskaj dostęp do proponowanej treści</translation>
<translation id="1710259589646384581">System operacyjny</translation>
<translation id="1721312023322545264">Aby wejść na tę stronę, musisz uzyskać pozwolenie od użytkownika <ph name="NAME" /></translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Numer strony</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Uruchom DiagnostykÄ™ sieci systemu Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Zaktualizuj swoje hasło synchronizacji.</translation>
+<translation id="1787142507584202372">Tutaj pojawiajÄ… siÄ™ otwarte karty</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Bezpieczne przeglądanie Google ostatnio <ph name="BEGIN_LINK" />wykryło złośliwe oprogramowanie<ph name="END_LINK" /> na <ph name="SITE" />. Strony, które normalnie są bezpieczne, czasem zostają zainfekowane złośliwym oprogramowaniem. Złośliwa zawartość pochodzi z <ph name="SUBRESOURCE_HOST" /> – znanego dystrybutora złośliwego oprogramowania. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Nieprawidłowe żądanie lub jego parametry</translation>
+<translation id="1834321415901700177">Ta strona zawiera szkodliwe programy</translation>
<translation id="1838667051080421715">Przeglądasz źródło strony internetowej.</translation>
<translation id="1871208020102129563">Proxy skonfigurowano do używania stałych serwerów proxy, a nie URL-a skryptu PAC.</translation>
<translation id="1883255238294161206">Zwiń listę</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Klient natywny</translation>
<translation id="213826338245044447">Zakładki na komórce</translation>
<translation id="2148716181193084225">Dzisiaj</translation>
-<translation id="2149973817440762519">Edytuj zakładkę</translation>
<translation id="2154054054215849342">Synchronizacja nie jest dostępna w Twojej domenie.</translation>
<translation id="2166049586286450108">Pełny dostęp administratora</translation>
<translation id="2166378884831602661">Ta witryna nie umożliwia bezpiecznego połączenia</translation>
@@ -121,13 +125,14 @@
<translation id="2262243747453050782">BÅ‚Ä…d HTTP</translation>
<translation id="2282872951544483773">Niedostępne eksperymenty</translation>
<translation id="2292556288342944218">Masz zablokowany dostęp do internetu</translation>
-<translation id="229702904922032456">Wygasł certyfikat główny lub pośredniczący.</translation>
+<translation id="229702904922032456">Wygasł certyfikat główny lub pośredni.</translation>
<translation id="230155334948463882">Nowa karta?</translation>
<translation id="2305919008529760154">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat mógł zostać wydany w celu dokonania oszustwa. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> wymaga nazwy użytkownika i hasła.</translation>
<translation id="2318774815570432836">Nie możesz teraz wejść na stronę <ph name="SITE" />, bo używa ona HSTS. Błędy sieci i ataki są zazwyczaj przejściowe, więc prawdopodobnie później strona będzie działać. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Zignorowano nieprawidłową zakładkę w indeksie <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Inne zakładki</translation>
+<translation id="2355395290879513365">Osoby dokonujące ataków mogą widzieć te same obrazy w witrynie co Ty i zmodyfikować je, by Cię oszukać.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Pokaż zasady bez ustawionej wartości</translation>
<translation id="2396249848217231973">&amp;Cofnij usunięcie</translation>
<translation id="2455981314101692989">Automatyczne wypełnianie tego formularza zostało wyłączone przez stronę internetową.</translation>
+<translation id="2460160116472764928">Bezpieczne przeglądanie Google ostatnio <ph name="BEGIN_LINK" />wykryło złośliwe oprogramowanie<ph name="END_LINK" /> na <ph name="SITE" />. Strony, które normalnie są bezpieczne, czasem zostają zainfekowane złośliwym oprogramowaniem. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Wpisz</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Uruchom diagnostykÄ™ sieci<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Nieprawidłowy URL wyszukiwania</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Czy na pewno chcesz usunąć te strony z historii?</translation>
<translation id="2677748264148917807">Wyjdź</translation>
<translation id="269990154133806163">Serwer przedstawił certyfikat, który nie został opublikowany za pomocą zasad Certificate Transparency. W przypadku niektórych certyfikatów to wymaganie musi być spełnione, ponieważ gwarantuje, że można im ufać i że chronią one przed atakami. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Do przeczytania</translation>
<translation id="2704283930420550640">Wartość nie pasuje do formatu.</translation>
<translation id="2704951214193499422">Chromium nie może obecnie potwierdzić karty. Spróbuj ponownie później.</translation>
<translation id="2705137772291741111">Zapisana w pamięci podręcznej kopia tej strony jest uszkodzona.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Próbujesz połączyć się z <ph name="DOMAIN" />, ale serwer przedstawił certyfikat unieważniony przez wystawcę. Oznacza to, że dane logowania podane przez serwer są zupełnie niewiarygodne. Możliwe, że komunikujesz się z intruzem. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">PoproÅ› o pozwolenie</translation>
<translation id="2713444072780614174">Biały</translation>
+<translation id="2720342946869265578">W pobliżu</translation>
<translation id="2721148159707890343">Żądanie wykonane pomyślnie</translation>
<translation id="2728127805433021124">Certyfikat serwera został podpisany przy użyciu słabego algorytmu podpisu.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Uruchomienie diagnostyki połączeń<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Możesz wyłączyć dowolne serwery proxy skonfigurowane dla połączenia na stronie ustawień.</translation>
<translation id="2955913368246107853">Zamknij pasek wyszukiwania</translation>
<translation id="2958431318199492670">Konfiguracja sieci jest niezgodna ze standardem ONC. Jej fragmenty mogły nie zostać zaimportowane.</translation>
+<translation id="29611076221683977">Osoby atakujące stronę <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogą spróbować zainstalować na Twoim Macu niebezpieczne programy, których celem jest wykradzenie lub usunięcie Twoich danych (na przykład zdjęć, haseł, wiadomości czy numerów kart kredytowych).</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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Ukryj szczegóły</translation>
<translation id="3587482841069643663">Wszystkie</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Serwer nie obsługuje rozszerzenia ponownej negocjacji protokołu TLS.</translation>
<translation id="36224234498066874">Wyczyść dane przeglądania...</translation>
<translation id="362276910939193118">Wyświetl całą historię</translation>
<translation id="3623476034248543066">Pokaż wartość</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Brak wolnych licencji</translation>
<translation id="3714780639079136834">Włącz komórkową transmisję danych lub Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Sprawdź serwer proxy, zaporę sieciową i konfigurację DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Jeśli rozumiesz zagrożenie, możesz <ph name="BEGIN_LINK" />odwiedzić tę niebezpieczną stronę<ph name="END_LINK" />, zanim niebezpieczne programy zostaną usunięte.</translation>
<translation id="3739623965217189342">Skopiowany 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="3788090790273268753">Certyfikat tej witryny wygasa w 2016 roku, a łańcuch certyfikatów zawiera certyfikat podpisany algorytmem SHA-1.</translation>
<translation id="382518646247711829">Jeśli używasz serwera proxy...</translation>
<translation id="3828924085048779000">Puste hasło jest niedozwolone.</translation>
<translation id="3845539888601087042">Wyświetlam historię z urządzeń, na których jesteś zalogowany. <ph name="BEGIN_LINK" />Więcej informacji<ph name="END_LINK" /></translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Klucz „<ph name="SUBKEY" />â€: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Klient i serwer nie obsługują wspólnej wersji protokołu SSL lub mechanizmu szyfrowania.</translation>
<translation id="4079302484614802869">Proxy skonfigurowano do używania URL-a skryptu PAC, a nie ustalonych serwerów proxy.</translation>
+<translation id="4098354747657067197">Wchodzisz na stronę wprowadzającą w błąd</translation>
<translation id="4103249731201008433">Numer seryjny urządzenia jest nieprawidłowy</translation>
<translation id="4103763322291513355">Wejdź na stronę &lt;strong&gt;chrome://policy&lt;/strong&gt;, aby wyświetlić czarną listę URL-i oraz inne zasady egzekwowane przez administratora systemu.</translation>
<translation id="4110615724604346410">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat zawiera błędy. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{brak}=1{1 aplikacja ($1)}=2{2 aplikacje ($1, $2)}few{# aplikacje ($1, $2, $3)}many{# aplikacji ($1, $2, $3)}other{# aplikacji ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Awarie</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Uruchom diagnostykÄ™ sieci<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Twoje połączenie z tą witryną nie jest w pełni bezpieczne</translation>
<translation id="4250680216510889253">Nie</translation>
<translation id="425582637250725228">Wprowadzone zmiany mogą nie zostać zapisane.</translation>
<translation id="4258748452823770588">Nieprawidłowy podpis</translation>
<translation id="4269787794583293679">(Brak nazwy użytkownika)</translation>
+<translation id="4280429058323657511">, ważna do: <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Bezpieczne przeglądanie Google ostatnio <ph name="BEGIN_LINK" />wykryło szkodliwe programy<ph name="END_LINK" /> na <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Propozycje rodziców</translation>
<translation id="4304224509867189079">Zaloguj siÄ™</translation>
<translation id="432290197980158659">Serwer przedstawił certyfikat, który nie spełnia wbudowanych warunków. Przeglądarka sprawdza je w przypadku określonych witryn wymagających wysokiego poziomu bezpieczeństwa. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Nie udało się znaleźć artykułu</translation>
+<translation id="4326324639298822553">Sprawdź datę ważności i spróbuj ponownie</translation>
<translation id="4331708818696583467">Niezabezpieczona</translation>
+<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="4381091992796011497">Nazwa użytkownika:</translation>
<translation id="4394049700291259645">Wyłącz</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Próbujesz wejść na <ph name="DOMAIN" />, ale serwer przedstawił nieprawidłowy certyfikat. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Karty z innych urządzeń</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">PrzeglÄ…dasz stronÄ™ rozszerzenia.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Odśwież zasady</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4744603770635761495">Ścieżka pliku wykonywalnego</translation>
+<translation id="4750917950439032686">Informacje, które wysyłasz tej witrynie (na przykład hasła lub numery kart kredytowych), pozostają prywatne.</translation>
<translation id="4756388243121344051">&amp;Historia</translation>
+<translation id="4759118997339041434">Autouzupełnianie danych o płatności jest wyłączone</translation>
<translation id="4764776831041365478">Strona internetowa pod adresem <ph name="URL" /> może być tymczasowo niedostępna lub została na stałe przeniesiona pod nowy adres internetowy.</translation>
<translation id="4771973620359291008">Wystąpił nieznany błąd.</translation>
<translation id="4800132727771399293">Sprawdź datę ważności i kod CVC, a potem spróbuj ponownie</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">BÅ‚Ä…d sieci</translation>
<translation id="4816492930507672669">Dopasuj do strony</translation>
<translation id="4850886885716139402">Widok</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Czynności</translation>
<translation id="4958444002117714549">Rozwiń listę</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Pobieranie</translation>
<translation id="5190835502935405962">Pasek zakładek</translation>
<translation id="5199729219167945352">Eksperymenty</translation>
-<translation id="5199841536747119669">Tutaj wyświetlają się sugestie</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="5299298092464848405">Podczas przetwarzania zasady wystąpił błąd</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Funkcja zgłaszania awarii jest wyłączona.</translation>
<translation id="5317780077021120954">Zapisz</translation>
<translation id="5327248766486351172">Nazwa</translation>
+<translation id="5337705430875057403">Osoby atakujące stronę <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogą podstępem nakłonić Cię do zrobienia czegoś niebezpiecznego, np. zainstalowania oprogramowania lub ujawnienia danych osobowych (takich jak hasła, numery telefonów i dane kart kredytowych).</translation>
<translation id="5359637492792381994">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat bezpieczeństwa nie jest obecnie ważny. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Zapisanie ustawień zasady nie powiodło się</translation>
+<translation id="5386426401304769735">ÅaÅ„cuch certyfikatów tej witryny zawiera certyfikat podpisany za pomocÄ… SHA-1.</translation>
<translation id="5421136146218899937">Wyczyść dane przeglądania...</translation>
<translation id="5430298929874300616">Usuń zakładkę</translation>
<translation id="5431657950005405462">Nie znaleziono pliku</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Komunikat z elementu umieszczonego na stronie <ph name="SITE" />:</translation>
<translation id="5556459405103347317">Odśwież</translation>
<translation id="5565735124758917034">Aktywny</translation>
+<translation id="5572851009514199876">Uruchom Chrome i zaloguj się w nim, by mógł sprawdzić, czy masz uprawnienia dostępu do tej strony.</translation>
+<translation id="5580958916614886209">Sprawdź miesiąc ważności i spróbuj ponownie</translation>
<translation id="560412284261940334">Zarządzanie jest nieobsługiwane</translation>
<translation id="5610142619324316209">Sprawdź połączenie</translation>
<translation id="5617949217645503996">Strona <ph name="HOST_NAME" /> spowodowała zbyt wiele przekierowań.</translation>
<translation id="5622887735448669177">Chcesz opuścić tę stronę?</translation>
<translation id="5629630648637658800">Åadowanie ustawieÅ„ zasady nie powiodÅ‚o siÄ™</translation>
<translation id="5631439013527180824">Nieprawidłowy token zarządzania urządzeniem</translation>
+<translation id="5669703222995421982">Otrzymywanie spersonalizowanych treści</translation>
+<translation id="5675650730144413517">Ta strona nie działa</translation>
<translation id="5677928146339483299">Zablokowane</translation>
<translation id="5694783966845939798">Próbujesz połączyć się z domeną <ph name="DOMAIN" />, ale serwer przedstawił certyfikat podpisany słabym algorytmem (np. SHA-1). Oznacza to, że dane logowania podane przez serwer mogły zostać sfałszowane, a serwer może nie być tym, którego oczekujesz (możliwe, że komunikujesz się z intruzem). <ph name="BEGIN_LEARN_MORE_LINK" />Więcej informacji<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">Tożsamość witryny nie została zweryfikowana.</translation>
<translation id="5720705177508910913">Bieżący użytkownik</translation>
-<translation id="572328651809341494">Ostatnie karty</translation>
<translation id="5732392974455271431">Mogą ją dla Ciebie odblokować Twoi rodzice</translation>
<translation id="5784606427469807560">Podczas potwierdzania karty wystąpił problem. Sprawdź połączenie internetowe i spróbuj ponownie.</translation>
<translation id="5785756445106461925">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ć wygląd strony.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Próbujesz połączyć się z <ph name="DOMAIN" />, ale serwer przedstawił certyfikat ze słabym kluczem. Intruz mógł złamać klucz prywatny, a serwer może nie być tym, którego oczekujesz (możliwe, że komunikujesz się z intruzem). <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Nowy folder</translation>
<translation id="5869405914158311789">Ta witryna jest nieosiÄ…galna</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Karty tego typu nie są obsługiwane w Google Payments w przypadku tego sprzedawcy. Wybierz inną.</translation>
<translation id="59174027418879706">WÅ‚Ä…czone</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="5966707198760109579">Tydzień</translation>
<translation id="5967867314010545767">Usuń z historii</translation>
<translation id="5975083100439434680">Pomniejsz</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Wypróbuj te rozwiązania:</translation>
<translation id="6151417162996330722">Certyfikat serwera ma za długi okres ważności.</translation>
<translation id="6165508094623778733">Więcej informacji</translation>
+<translation id="6177128806592000436">Twoje połączenie z tą witryną nie jest bezpieczne</translation>
<translation id="6203231073485539293">Sprawdź połączenie z internetem</translation>
<translation id="6218753634732582820">Usunąć ten adres z Chromium?</translation>
+<translation id="6251924700383757765">Polityka prywatności</translation>
+<translation id="625755898061068298">Wyłączyłeś ostrzeżenia dotyczące bezpieczeństwa tej witryny.</translation>
<translation id="6259156558325130047">&amp;Ponów zmianę kolejności</translation>
<translation id="6263376278284652872">Zakładki <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Powrót do bezpieczeństwa</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">Strona <ph name="URL" /> jest nieosiÄ…galna.</translation>
<translation id="6321917430147971392">Sprawdź ustawienia DNS</translation>
<translation id="6328639280570009161">Wyłącz przewidywanie działań sieciowych</translation>
+<translation id="6328786501058569169">Ta strona wprowadza w błąd</translation>
<translation id="6337534724793800597">Filtruj zasady według nazwy</translation>
<translation id="6342069812937806050">Przed momentem</translation>
<translation id="6345221851280129312">nieznany rozmiar</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Nie możesz teraz wejść na <ph name="SITE" />, bo ta witryna wysłała zaszyfrowane dane logowania, których Chromium nie może przetworzyć. Błędy sieci i ataki są zazwyczaj przejściowe, więc prawdopodobnie później strona będzie działać. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Nie można sprawdzić, czy certyfikat został unieważniony.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Ignorowana, ponieważ wyszukiwarka domyślna jest wyłączona przez zasadę.</translation>
<translation id="6462969404041126431">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat mógł zostać unieważniony. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Ponów usunięcie</translation>
<translation id="6534179046333460208">Sugestie dotyczÄ…ce internetu rzeczy</translation>
<translation id="6550675742724504774">Opcje</translation>
+<translation id="6556239504065605927">Bezpieczne połączenie</translation>
<translation id="6563469144985748109">Twój menedżer jeszcze na to nie zezwolił</translation>
<translation id="6593753688552673085">mniej niż <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opcje szyfrowania</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Zasada jest przestarzała.</translation>
<translation id="6652240803263749613">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat nie jest zaufany w systemie operacyjnym Twojego komputera. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Folder edytora</translation>
-<translation id="6660210980321319655">Automatyczne zgłoszenie: <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Wartość zasady</translation>
<translation id="6757797048963528358">Twoje urządzenie przeszło w tryb uśpienia.</translation>
<translation id="6778737459546443941">Twój rodzic jeszcze na to nie zezwolił</translation>
+<translation id="6810899417690483278">Identyfikator dostosowania</translation>
<translation id="6820686453637990663">Kod CVC</translation>
<translation id="6830600606572693159">Strona internetowa pod adresem <ph name="URL" /> jest obecnie niedostępna. Może być przeciążona lub wyłączona na czas konserwacji.</translation>
<translation id="6831043979455480757">TÅ‚umacz</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Dane kontaktowe</translation>
+<translation id="7079718277001814089">Ta strona zawiera złośliwe oprogramowanie</translation>
<translation id="7087282848513945231">Hrabstwo</translation>
<translation id="7088615885725309056">Starsze</translation>
<translation id="7090678807593890770">Wyszukaj w Google: <ph name="LINK" /></translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423">Serwer <ph name="HOST_NAME" /> nie spełnia norm bezpieczeństwa.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Więcej informacji<ph name="END_LINK" /> na temat tego problemu.</translation>
<translation id="7219179957768738017">Połączenie z szyfrowaniem <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Strona, którą chcesz otworzyć, zawiera złośliwe oprogramowanie</translation>
<translation id="724975217298816891">Wpisz datę ważności i kod CVC karty <ph name="CREDIT_CARD" />, by zaktualizować jej szczegółowe dane. Po potwierdzeniu zostaną one udostępnione tej stronie.</translation>
<translation id="725866823122871198">Nie można nawiązać prywatnego połączenia z <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, ponieważ data i godzina (<ph name="DATE_AND_TIME" />) ustawione na komputerze są nieprawidłowe.</translation>
<translation id="7269802741830436641">Ta strona internetowa zawiera pętlę przekierowań</translation>
@@ -609,8 +646,9 @@ Psst! Następnym razem możesz użyć trybu incognito <ph name="SHORTCUT_KEY" />
<translation id="7637571805876720304">Usunąć tę kartę kredytową z Chromium?</translation>
<translation id="765676359832457558">Ukryj ustawienia zaawansowane...</translation>
<translation id="7658239707568436148">Anuluj</translation>
-<translation id="7667346355482952095">Zwrócony token zasad jest pusty lub nie pasuje do bieżącego tokenu</translation>
+<translation id="7667346355482952095">Zwrócony token zasad jest pusty lub nie pasuje do bieżącego tokena</translation>
<translation id="7668654391829183341">Nieznane urzÄ…dzenie</translation>
+<translation id="7669271284792375604">Osoby atakujące tę stronę mogą podstępem próbować nakłonić Cię do zainstalowania programów utrudniających przeglądanie internetu (np. zmieniających stronę główną lub wyświetlających dodatkowe reklamy na stronach, na które wchodzisz).</translation>
<translation id="7674629440242451245">Interesują Cię nowe, przydatne funkcje Chrome? Wypróbuj wersję deweloperską ze strony chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Otwórz stronę <ph name="SITE" /> (niebezpieczną)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Nie można załadować tej strony z pamięci podręcznej</translation>
@@ -626,14 +664,17 @@ Psst! Następnym razem możesz użyć trybu incognito <ph name="SHORTCUT_KEY" />
<translation id="7800304661137206267">Przesyłane dane są szyfrowane za pomocą algorytmu <ph name="CIPHER" />. Metoda uwierzytelniania komunikatów to <ph name="MAC" />, a mechanizm wymiany kluczy to <ph name="KX" />.</translation>
<translation id="780301667611848630">Nie, dziękuję</translation>
<translation id="7805768142964895445">Stan</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Usunąć tę podpowiedź do formularza z Chrome?</translation>
<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="7887683347370398519">Sprawdź kod CVC i spróbuj ponownie</translation>
<translation id="7912024687060120840">W folderze:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Certyfikat serwera nie jest jeszcze ważny.</translation>
<translation id="7942349550061667556">Czerwony</translation>
+<translation id="7947285636476623132">Sprawdź rok ważności i spróbuj ponownie</translation>
<translation id="7951415247503192394">(32-bitowa)</translation>
<translation id="7956713633345437162">Zakładki na komórce</translation>
<translation id="7961015016161918242">Nigdy</translation>
@@ -641,7 +682,10 @@ Psst! Następnym razem możesz użyć trybu incognito <ph name="SHORTCUT_KEY" />
<translation id="7983301409776629893">Zawsze tłumacz z języka: <ph name="ORIGINAL_LANGUAGE" /> na <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Nie określono</translation>
<translation id="8012647001091218357">Obecnie nie możemy się skontaktować z Twoimi rodzicami. Spróbuj ponownie.</translation>
+<translation id="8025119109950072390">Osoby atakujące tę stronę mogą podstępem nakłonić Cię do zrobienia czegoś niebezpiecznego, np. zainstalowania oprogramowania lub ujawnienia danych osobowych (takich jak hasła, numery telefonów czy dane kart kredytowych).</translation>
+<translation id="803030522067524905">Bezpieczne przeglądanie Google ostatnio wykryło wyłudzanie informacji na <ph name="SITE" />. Strony wyłudzające informacje udają inne strony, by zmylić użytkowników. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Język tej strony to <ph name="SOURCE_LANGUAGE" />. Przetłumaczyć ją na <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Prześlij opinię</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>
@@ -652,9 +696,11 @@ Psst! Następnym razem możesz użyć trybu incognito <ph name="SHORTCUT_KEY" />
<translation id="8150722005171944719">Nie można odczytać pliku <ph name="URL" />. Być może został usunięty lub uprawnienia dostępu uniemożliwiają jego odczyt.</translation>
<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="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="8230421197304563332">Osoby atakujące stronę <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogą spróbować zainstalować na Twoim komputerze niebezpieczne programy, których celem jest wykradzenie lub usunięcie Twoich danych (na przykład zdjęć, haseł, wiadomości czy numerów kart kredytowych).</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="8249320324621329438">Ostatnie pobieranie:</translation>
<translation id="8261506727792406068">Usuń</translation>
@@ -663,11 +709,13 @@ Psst! Następnym razem możesz użyć trybu incognito <ph name="SHORTCUT_KEY" />
<translation id="8294431847097064396">Źródło</translation>
<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="8349305172487531364">Pasek zakładek</translation>
<translation id="8363502534493474904">Wyłącz tryb samolotowy</translation>
<translation id="8364627913115013041">Nie ustawiono.</translation>
<translation id="8380941800586852976">Niebezpieczna</translation>
<translation id="8382348898565613901">Tutaj wyświetlają się ostatnio otwierane zakładki</translation>
+<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="8428213095426709021">Ustawienia</translation>
@@ -679,9 +727,9 @@ Psst! Następnym razem możesz użyć trybu incognito <ph name="SHORTCUT_KEY" />
<translation id="8498891568109133222">Serwer <ph name="HOST_NAME" /> potrzebował zbyt wiele czasu na odpowiedź.</translation>
<translation id="852346902619691059">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat nie jest zaufany w systemie operacyjnym Twojego urządzenia. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego. <ph name="BEGIN_LEARN_MORE_LINK" />Dowiedz się więcej<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Karty tego typu nie są obsługiwane w Google Payments. Wybierz inną.</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="8553075262323480129">Tłumaczenie nie powiodło się, ponieważ nie można określić języka strony.</translation>
<translation id="8559762987265718583">Nie można nawiązać prywatnego połączenia z <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, ponieważ data i godzina (<ph name="DATE_AND_TIME" />) ustawione na urządzeniu są nieprawidłowe.</translation>
-<translation id="856992080682148">Certyfikat tej witryny wygasa w 2017 roku lub później, a łańcuch certyfikatów zawiera certyfikat podpisany algorytmem SHA-1.</translation>
<translation id="8571890674111243710">Trwa tłumaczenie strony na język: <ph name="LANGUAGE" />...</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>
@@ -694,10 +742,12 @@ Psst! Następnym razem możesz użyć trybu incognito <ph name="SHORTCUT_KEY" />
<translation id="8740359287975076522">Nie znaleziono &lt;abbr id="dnsDefinition"&gt;adresu DNS&lt;/abbr&gt; serwera <ph name="HOST_NAME" />. Diagnozujemy problem.</translation>
<translation id="8790007591277257123">&amp;Ponów usunięcie</translation>
<translation id="8798099450830957504">Domyślny</translation>
+<translation id="8800988563907321413">Tutaj wyświetlają się sugestie witryn w pobliżu</translation>
<translation id="8804164990146287819">Polityka prywatności</translation>
<translation id="8820817407110198400">Zakładki</translation>
<translation id="8834246243508017242">Włącz Autouzupełnianie z kontaktami…</translation>
<translation id="883848425547221593">Inne zakładki</translation>
+<translation id="884264119367021077">Adres wysyłki</translation>
<translation id="884923133447025588">Nie znaleziono mechanizmu unieważniania.</translation>
<translation id="885730110891505394">Udostępnianie Google</translation>
<translation id="8866481888320382733">Podczas przetwarzania ustawień zasady wystąpił błąd</translation>
@@ -715,18 +765,21 @@ Psst! Następnym razem możesz użyć trybu incognito <ph name="SHORTCUT_KEY" />
<translation id="8971063699422889582">Ważność certyfikatu serwera wygasła.</translation>
<translation id="8987927404178983737">MiesiÄ…c</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Na następnej stronie znajdują się szkodliwe programy</translation>
<translation id="9001074447101275817">Serwer proxy <ph name="DOMAIN" /> wymaga nazwy użytkownika i hasła.</translation>
<translation id="901974403500617787">Flagi stosowane w całym systemie może ustawić tylko właściciel: <ph name="OWNER_EMAIL" />.</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="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="9040185888511745258">Osoby dokonujące ataków na stronie <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mogą podstępem próbować nakłonić Cię do zainstalowania programów obniżających jakość przeglądania internetu (np. zmieniających stronę główną lub powodujących pokazywanie dodatkowych reklam na stronach, na które wchodzisz).</translation>
<translation id="9050666287014529139">Hasło</translation>
<translation id="9065203028668620118">Edycja</translation>
<translation id="9068849894565669697">Wybierz kolor</translation>
<translation id="9076283476770535406">Może zawierać treści dla dorosłych</translation>
-<translation id="9092364396508701805">Strona <ph name="HOST_NAME" /> nie działa</translation>
<translation id="9103872766612412690"><ph name="SITE" /> zazwyczaj używa szyfrowania do ochrony Twoich informacji. Gdy tym razem przeglądarka Chromium próbowała połączyć się ze stroną <ph name="SITE" />, odesłała ona nietypowe i nieprawidłowe dane logowania. Może się tak zdarzyć, gdy pod stronę <ph name="SITE" /> podszywa się osoba atakująca albo gdy ekran logowania do sieci Wi-Fi przerwie połączenie. Twoje informacje są nadal bezpieczne, bo połączenie w Chromium zakończyło się przed wymianą jakichkolwiek danych.</translation>
<translation id="9137013805542155359">Pokaż tekst oryginalny</translation>
+<translation id="9137248913990643158">Aby użyć tej aplikacji, najpierw uruchom Chrome i zaloguj się w nim.</translation>
<translation id="9148507642005240123">&amp;Cofnij edycjÄ™</translation>
<translation id="9157595877708044936">Konfigurowanie...</translation>
<translation id="9170848237812810038">&amp;Cofnij</translation>
@@ -739,7 +792,6 @@ Psst! Następnym razem możesz użyć trybu incognito <ph name="SHORTCUT_KEY" />
<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="962701380617707048">Wpisz datę ważności i kod CVC karty <ph name="CREDIT_CARD" />, by zaktualizować jej szczegółowe dane</translation>
<translation id="969892804517981540">Oficjalna wersja</translation>
<translation id="988159990683914416">Build</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 11d33add317..23d7de1f6b5 100644
--- a/chromium/components/strings/components_strings_pt-BR.xtb
+++ b/chromium/components/strings/components_strings_pt-BR.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Conectar-se à rede Wi-Fi novamente</translation>
<translation id="1175364870820465910">&amp;Imprimir...</translation>
<translation id="1181037720776840403">Remover</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Informar automaticamente<ph name="END_WHITEPAPER_LINK" /> ao Google detalhes de possíveis incidentes de segurança. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Próxima</translation>
<translation id="1201895884277373915">Mais deste site</translation>
<translation id="1206967143813997005">Assinatura inicial inválida</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Ciano</translation>
<translation id="1629803312968146339">Deseja que o Chrome salve este cartão?</translation>
<translation id="1640180200866533862">Políticas de usuário</translation>
+<translation id="1640244768702815859">Tente <ph name="BEGIN_LINK" />visitar a página inicial do site<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">A configuração de rede é inválida e não pôde ser importada.</translation>
<translation id="1644574205037202324">Histórico</translation>
<translation id="1645368109819982629">Protocolo não compatível</translation>
<translation id="1676269943528358898">O site <ph name="SITE" /> geralmente usa criptografia para proteger suas informações. Quando o Google Chrome tentou se conectar a <ph name="SITE" /> dessa vez, o website retornou credenciais incomuns e incorretas. Isso pode acontecer quando um invasor está fingindo ser <ph name="SITE" /> ou quando uma tela de login por Wi-Fi interrompeu a conexão. Suas informações ainda estão protegidas, porque o Google Chrome interrompeu a conexão antes que os dados fossem trocados.</translation>
+<translation id="168328519870909584">Os invasores que estão atualmente em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem tentar instalar apps perigosos no seu dispositivo para roubar ou excluir suas informações (por exemplo, fotos, senhas, mensagens e cartões de crédito).</translation>
<translation id="168841957122794586">O certificado do servidor contém uma chave de criptografia fraca.</translation>
-<translation id="1701955595840307032">Ver conteúdo sugerido</translation>
<translation id="1710259589646384581">SO</translation>
<translation id="1721312023322545264">É necessário pedir a permissão de <ph name="NAME" /> para visitar este site</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Numero da página</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Tente executar o Diagnóstico de Rede do Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Favor atualizar sua senha de sincronização.</translation>
+<translation id="1787142507584202372">Suas guias abertas são exibidas aqui</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Recentemente, a Navegação segura do Google <ph name="BEGIN_LINK" />detectou malware<ph name="END_LINK" /> em <ph name="SITE" />. Websites que costumam ser seguros às vezes são infectados por malware. O conteúdo malicioso vem de <ph name="SUBRESOURCE_HOST" />, um conhecido distribuidor de malware. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Solicitação ou parâmetros de solicitação inválidos</translation>
+<translation id="1834321415901700177">Este site contém programas perigosos</translation>
<translation id="1838667051080421715">Você está vendo o código-fonte de uma página da Web.</translation>
<translation id="1871208020102129563">O proxy foi configurado para utilizar servidores de proxy fixo e não um URL de script .pac.</translation>
<translation id="1883255238294161206">Recolher lista</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Cliente nativo</translation>
<translation id="213826338245044447">Favoritos de dispositivos móveis</translation>
<translation id="2148716181193084225">Hoje</translation>
-<translation id="2149973817440762519">Editar favoritos</translation>
<translation id="2154054054215849342">O serviço de sincronização não está disponível para seu domínio</translation>
<translation id="2166049586286450108">Acesso completo de administrador</translation>
<translation id="2166378884831602661">Não foi possível estabelecer uma conexão segura com este site</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Não é possível visitar <ph name="SITE" /> no momento, porque o website usa HSTS. Ataques e erros de rede costumam ser temporários. Portanto, essa página deve voltar a funcionar mais tarde. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Favorito inválido ignorado no índice <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Outros favoritos</translation>
+<translation id="2355395290879513365">É possível que invasores consigam ver as imagens que você está olhando nesse site e as modifiquem para enganar você.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Mostrar políticas sem valor definido</translation>
<translation id="2396249848217231973">&amp;Desfazer exclusão</translation>
<translation id="2455981314101692989">Esta página da web desativou o preenchimento automático para este formulário.</translation>
+<translation id="2460160116472764928">Recentemente, a Navegação segura do Google <ph name="BEGIN_LINK" />detectou malware<ph name="END_LINK" /> em <ph name="SITE" />. Websites que costumam ser seguros às vezes são infectados por malware. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Preencher</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Executar o Diagnóstico de Rede<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL de pesquisa inválido.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Tem certeza de que deseja excluir essas páginas do seu histórico?</translation>
<translation id="2677748264148917807">Sair</translation>
<translation id="269990154133806163">O servidor apresentou um certificado que não foi divulgado publicamente por meio da política de Transparência dos certificados. Essa é uma exigência para alguns certificados, a fim de garantir que eles sejam confiáveis e protejam você contra invasores. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Lista de leitura</translation>
<translation id="2704283930420550640">O valor não corresponde ao formato.</translation>
<translation id="2704951214193499422">Não foi possível confirmar seu cartão com o Chromium no momento. Tente novamente mais tarde.</translation>
<translation id="2705137772291741111">Não foi possível ler a cópia armazenada em cache deste site.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Você tentou acessar <ph name="DOMAIN" />, mas o certificado que o servidor apresentou foi revogado pelo emissor. Isso significa que as credenciais de segurança que o servidor apresentou não são nem um pouco seguras. Você pode estar se comunicando com um invasor. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Pedir permissão</translation>
<translation id="2713444072780614174">Branco</translation>
+<translation id="2720342946869265578">Por perto</translation>
<translation id="2721148159707890343">Solicitação bem-sucedida</translation>
<translation id="2728127805433021124">O certificado do servidor é assinado com um algoritmo de assinatura fraco.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Executar o Diagnóstico de Conectividade<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Na página "Configurações", você pode desativar quaisquer proxies configurados para uma conexão.</translation>
<translation id="2955913368246107853">Fechar barra de localização</translation>
<translation id="2958431318199492670">A configuração de rede não está de acordo com o padrão ONC. Partes da configuração podem não ser importadas.</translation>
+<translation id="29611076221683977">Os invasores que estão atualmente em <ph name="BEGIN_BOLD" /> <ph name="SITE" /> <ph name="END_BOLD" /> podem tentar instalar programas perigosos no seu Mac para roubar ou excluir suas informações (por exemplo, fotos, senhas, mensagens e cartões de crédito).</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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Ocultar detalhes</translation>
<translation id="3587482841069643663">Tudo</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">O servidor não é compatível com a extensão de renegociação TLS.</translation>
<translation id="36224234498066874">Limpar dados de navegação...</translation>
<translation id="362276910939193118">Mostrar histórico completo</translation>
<translation id="3623476034248543066">Mostrar valor</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Licenças esgotadas</translation>
<translation id="3714780639079136834">Ativar os dados da rede celular ou o Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Verificar a configuração de DNS, proxy e firewall<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Se você entende os riscos à sua segurança, pode <ph name="BEGIN_LINK" />acessar este site inseguro<ph name="END_LINK" /> antes de os programas perigosos serem removidos.</translation>
<translation id="3739623965217189342">Link que você copiou</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="3788090790273268753">O certificado deste site expira em 2016. A cadeia de certificados contém um certificado assinado com SHA-1.</translation>
<translation id="382518646247711829">Se você usa um servidor proxy...</translation>
<translation id="3828924085048779000">Uma senha vazia não é permitida.</translation>
<translation id="3845539888601087042">Exibindo histórico dos seus dispositivos conectados. <ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Chave "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">O cliente e o servidor não são compatíveis com uma versão do protocolo SSL comum ou com o pacote de criptografia.</translation>
<translation id="4079302484614802869">A configuração do proxy definida utiliza um URL de script .pac, e não servidores proxy fixos.</translation>
+<translation id="4098354747657067197">Site enganoso à frente</translation>
<translation id="4103249731201008433">O número de série do dispositivo é inválido</translation>
<translation id="4103763322291513355">Visite &lt;strong&gt;chrome://policy&lt;/strong&gt; para ver a lista de URLs adicionados à lista negra e outras políticas aplicadas pelo administrador do seu sistema.</translation>
<translation id="4110615724604346410">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança dele contém erros. Isso pode ser causado por uma configuração incorreta ou por um invasor que interceptou sua conexão. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{nenhum}=1{1 app ($1)}=2{2 apps ($1, $2)}other{# apps ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Falhas</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Tente executar o Diagnóstico de Rede<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Sua conexão com esse site não é completamente segura</translation>
<translation id="4250680216510889253">Não</translation>
<translation id="425582637250725228">É possível que as alterações feitas não sejam salvas.</translation>
<translation id="4258748452823770588">Assinatura inválida</translation>
<translation id="4269787794583293679">Sem nome de usuário</translation>
+<translation id="4280429058323657511">, validade <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Recentemente, a Navegação segura do Google <ph name="BEGIN_LINK" />encontrou programas prejudiciais<ph name="END_LINK" /> em <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Sugestões para pais</translation>
<translation id="4304224509867189079">Fazer login</translation>
<translation id="432290197980158659">O servidor apresentou um certificado que não corresponde às expectativas incorporadas. Essas expectativas são incluídas para determinados websites de alta segurança para proteger você. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Falha ao encontrar artigo</translation>
+<translation id="4326324639298822553">Verifique a data de validade e tente novamente</translation>
<translation id="4331708818696583467">Não seguro</translation>
+<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="4381091992796011497">Nome de usuário:</translation>
<translation id="4394049700291259645">Desativar</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Você tentou acessar <ph name="DOMAIN" />, mas o servidor apresentou um certificado inválido. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Guias de outros dispositivos</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Você está vendo uma página de extensões.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Atualizar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
<translation id="4744603770635761495">Caminho do executável</translation>
+<translation id="4750917950439032686">Suas informações (por exemplo, senhas ou números de cartão de crédito) permanecem particulares quando são enviadas para esse site.</translation>
<translation id="4756388243121344051">&amp;Histórico</translation>
+<translation id="4759118997339041434">Preenchimento automático de pagamento desativado</translation>
<translation id="4764776831041365478">A página da web em <ph name="URL" /> pode estar temporariamente indisponível ou pode ter sido movida permanentemente para um novo endereço da web.</translation>
<translation id="4771973620359291008">Ocorreu um erro desconhecido.</translation>
<translation id="4800132727771399293">Verifique sua data de validade e seu CVC e tente novamente</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Erro na rede</translation>
<translation id="4816492930507672669">Ajustar à página</translation>
<translation id="4850886885716139402">Visualizar</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Ações</translation>
<translation id="4958444002117714549">Expandir lista</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Fazendo download</translation>
<translation id="5190835502935405962">Barra de favoritos</translation>
<translation id="5199729219167945352">Experimentos</translation>
-<translation id="5199841536747119669">Suas sugestões aparecerão aqui</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="5299298092464848405">Política de análise de erros</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">O relatório de erros está desativado.</translation>
<translation id="5317780077021120954">Salvar</translation>
<translation id="5327248766486351172">Nome</translation>
+<translation id="5337705430875057403">Os invasores em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem induzir você a fazer algo perigoso, como instalar um software ou revelar suas informações pessoais (por exemplo, senhas, números de telefone ou cartões de crédito).</translation>
<translation id="5359637492792381994">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança dele não é válido no momento. Isso pode ser causado por uma configuração incorreta ou por um invasor que interceptou sua conexão. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Falha ao armazenar as configurações da política</translation>
+<translation id="5386426401304769735">A cadeia de certificados desse site contém um certificado assinado usando SHA-1.</translation>
<translation id="5421136146218899937">Limpar dados de navegação...</translation>
<translation id="5430298929874300616">Remover favorito</translation>
<translation id="5431657950005405462">O arquivo não foi encontrado</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Uma página incorporada em <ph name="SITE" /> diz:</translation>
<translation id="5556459405103347317">Recarregar</translation>
<translation id="5565735124758917034">Ativo</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>
+<translation id="5580958916614886209">Verifique o mês de validade e tente novamente</translation>
<translation id="560412284261940334">Gerenciamento não suportado</translation>
<translation id="5610142619324316209">Verificar a conexão</translation>
<translation id="5617949217645503996">Redirecionamento em excesso por <ph name="HOST_NAME" /></translation>
<translation id="5622887735448669177">Deseja sair deste site?</translation>
<translation id="5629630648637658800">Falha ao carregar as configurações da política</translation>
<translation id="5631439013527180824">Token de gerenciamento de dispositivo inválido</translation>
+<translation id="5669703222995421982">Receber conteúdo personalizado</translation>
+<translation id="5675650730144413517">Esta página não está funcionando</translation>
<translation id="5677928146339483299">Bloqueados</translation>
<translation id="5694783966845939798">Você tentou acessar <ph name="DOMAIN" />, mas o servidor apresentou um certificado assinado por meio de um algoritmo de assinatura fraco (por exemplo, SHA-1). Isso significa que as credenciais de segurança apresentadas pelo servidor podem ter sido forjadas, e talvez o servidor não seja o esperado (você pode estar se comunicando com um invasor). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">A identidade deste site não foi confirmada.</translation>
<translation id="5720705177508910913">Usuário atual</translation>
-<translation id="572328651809341494">Guias recentes</translation>
<translation id="5732392974455271431">Seus responsáveis podem desbloqueá-lo para você</translation>
<translation id="5784606427469807560">Ocorreu um problema ao confirmar seu cartão. Verifique a conexão com a Internet e tente novamente.</translation>
<translation id="5785756445106461925">Além disso, esta página inclui outros recursos que não são seguros. Esses recursos podem ser visualizados por outros usuários enquanto eles navegam e podem ser modificados por um invasor para alterar o comportamento da página.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Você tentou acessar <ph name="DOMAIN" />, mas o servidor apresentou um certificado que contém uma chave fraca. É possível que um invasor tenha violado a chave particular, e talvez o servidor não seja o esperado (você pode estar se comunicando com um invasor). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Nova pasta</translation>
<translation id="5869405914158311789">Não é possível acessar esse site</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Este tipo de cartão não é aceito pelo Google Payments para este comerciante. Selecione outro cartão.</translation>
<translation id="59174027418879706">Ativada</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="5966707198760109579">Semana</translation>
<translation id="5967867314010545767">Remover do histórico</translation>
<translation id="5975083100439434680">Diminuir zoom</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Tente:</translation>
<translation id="6151417162996330722">O certificado do servidor tem um período de validade excessivamente longo.</translation>
<translation id="6165508094623778733">Saiba mais</translation>
+<translation id="6177128806592000436">Sua conexão com esse site não é segura</translation>
<translation id="6203231073485539293">Verifique sua conexão com a Internet</translation>
<translation id="6218753634732582820">Remover endereço do Chromium?</translation>
+<translation id="6251924700383757765">Política de Privacidade</translation>
+<translation id="625755898061068298">Você optou por desativar os avisos de segurança para esse site.</translation>
<translation id="6259156558325130047">&amp;Refazer reordenar</translation>
<translation id="6263376278284652872">Favoritos de <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Voltar à segurança</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">Não é possível acessar <ph name="URL" />.</translation>
<translation id="6321917430147971392">Verifique suas configurações do DNS</translation>
<translation id="6328639280570009161">Tente desativar a previsão de rede</translation>
+<translation id="6328786501058569169">Este site é enganoso</translation>
<translation id="6337534724793800597">Filtrar políticas por nome</translation>
<translation id="6342069812937806050">Neste instante</translation>
<translation id="6345221851280129312">tamanho desconhecido</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Não é possível visitar <ph name="SITE" /> no momento porque o website enviou credenciais codificadas que o Chromium não consegue processar. Ataques e erros de rede costumam ser temporários. Portanto, esta página deve voltar a funcionar mais tarde. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Não foi possível verificar se o certificado foi revogado</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Ignorado porque a pesquisa padrão foi desativada por uma política.</translation>
<translation id="6462969404041126431">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança dele pode ter sido revogado. Isso pode ser causado por uma configuração incorreta ou por um invasor que interceptou sua conexão. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Refazer excluir</translation>
<translation id="6534179046333460208">Sugestões da Web física</translation>
<translation id="6550675742724504774">Opções</translation>
+<translation id="6556239504065605927">Conexão segura</translation>
<translation id="6563469144985748109">Seu administrador ainda não o aprovou</translation>
<translation id="6593753688552673085">menos que <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opções de criptografia</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Esta política foi encerrada.</translation>
<translation id="6652240803263749613">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança dele não é confiável para o sistema operacional do seu computador. Isso pode ser causado por uma configuração incorreta ou por um invasor que interceptou sua conexão. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Editar pasta</translation>
-<translation id="6660210980321319655">Denunciada automaticamente às <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Valor da política</translation>
<translation id="6757797048963528358">O dispositivo entrou em modo de suspensão.</translation>
<translation id="6778737459546443941">Seu responsável ainda não o aprovou</translation>
+<translation id="6810899417690483278">Código de personalização</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">A página da web <ph name="URL" /> não está disponível no momento. Ela pode estar sobrecarregada ou fora de operação para manutenção.</translation>
<translation id="6831043979455480757">Traduzir</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Dados de contato</translation>
+<translation id="7079718277001814089">Este site contém malware</translation>
<translation id="7087282848513945231">Condado</translation>
<translation id="7088615885725309056">Mais antigo</translation>
<translation id="7090678807593890770">Pesquise <ph name="LINK" /> no Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> não adere aos padrões de segurança.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" /> sobre este problema.</translation>
<translation id="7219179957768738017">A conexão usa a <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">O site a seguir contém malware</translation>
<translation id="724975217298816891">Digite a data de validade e o CVC do <ph name="CREDIT_CARD" /> para atualizar os detalhes do cartão. Depois da confirmação, os detalhes do cartão serão compartilhados com esse site.</translation>
<translation id="725866823122871198">Não é possível estabelecer uma conexão privada com <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, porque a data e a hora do seu computador (<ph name="DATE_AND_TIME" />) estão incorretas.</translation>
<translation id="7269802741830436641">Esta página da web tem um loop de redirecionamento</translation>
@@ -611,6 +648,7 @@ Dica: o modo de navegação anônimo <ph name="SHORTCUT_KEY" /> pode ser útil d
<translation id="7658239707568436148">Cancelar</translation>
<translation id="7667346355482952095">O token da política retornado está vazio ou não corresponde ao token atual</translation>
<translation id="7668654391829183341">Dispositivo desconhecido</translation>
+<translation id="7669271284792375604">Invasores nesse site podem tentar enganar você para que instale programas que prejudicam sua experiência de navegação (por exemplo, alterando sua página inicial ou mostrando mais anúncios nos sites que você visita).</translation>
<translation id="7674629440242451245">Interessado em novos recursos interessantes do Google Chrome? Veja nosso Canal Dev em chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Ir para <ph name="SITE" /> (não seguro)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Não é possível carregar este site a partir do cache</translation>
@@ -626,14 +664,17 @@ Dica: o modo de navegação anônimo <ph name="SHORTCUT_KEY" /> pode ser útil d
<translation id="7800304661137206267">A conexão foi criptografada utilizando <ph name="CIPHER" />, com <ph name="MAC" /> para mensagem de autenticação e <ph name="KX" /> como o mecanismo de troca de chaves.</translation>
<translation id="780301667611848630">Não</translation>
<translation id="7805768142964895445">Status</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Remover sugestão de formulário do Chrome?</translation>
<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="7887683347370398519">Verifique seu CVC e tente novamente</translation>
<translation id="7912024687060120840">Na pasta:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">O certificado do servidor ainda não é válido.</translation>
<translation id="7942349550061667556">Vermelho</translation>
+<translation id="7947285636476623132">Verifique o ano de validade e tente novamente</translation>
<translation id="7951415247503192394">32 bits</translation>
<translation id="7956713633345437162">Favoritos de dispositivos móveis</translation>
<translation id="7961015016161918242">Nunca</translation>
@@ -641,7 +682,10 @@ Dica: o modo de navegação anônimo <ph name="SHORTCUT_KEY" /> pode ser útil d
<translation id="7983301409776629893">Sempre traduzir <ph name="ORIGINAL_LANGUAGE" /> para <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Não especificado</translation>
<translation id="8012647001091218357">Não foi possível contatar seus pais. Tente novamente.</translation>
+<translation id="8025119109950072390">Invasores nesse site podem induzir você a fazer algo perigoso, como instalar um software ou revelar suas informações pessoais (por exemplo, senhas, números de telefone ou cartões de crédito).</translation>
+<translation id="803030522067524905">Recentemente, a Navegação segura do Google detectou phishing em <ph name="SITE" />. Os sites de phishing fingem ser outros websites para enganar você. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Esta página está escrita em <ph name="SOURCE_LANGUAGE" />. Traduzi-la para <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Enviar comentários</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>
@@ -652,9 +696,11 @@ Dica: o modo de navegação anônimo <ph name="SHORTCUT_KEY" /> pode ser útil d
<translation id="8150722005171944719">O arquivo em <ph name="URL" /> não está legível. Ele pode ter sido removido ou movido, ou as permissões do arquivo podem estar impedindo o acesso.</translation>
<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="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="8230421197304563332">Os invasores que estão atualmente em <ph name="BEGIN_BOLD" /> <ph name="SITE" /> <ph name="END_BOLD" /> podem tentar instalar programas perigosos no seu computador para roubar ou excluir suas informações (por exemplo, fotos, senhas, mensagens e cartões de crédito).</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="8249320324621329438">Última busca:</translation>
<translation id="8261506727792406068">Excluir</translation>
@@ -663,11 +709,13 @@ Dica: o modo de navegação anônimo <ph name="SHORTCUT_KEY" /> pode ser útil d
<translation id="8294431847097064396">Origem</translation>
<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="8349305172487531364">Barra de favoritos</translation>
<translation id="8363502534493474904">Desativar modo avião</translation>
<translation id="8364627913115013041">Não definida.</translation>
<translation id="8380941800586852976">Perigoso</translation>
<translation id="8382348898565613901">Os favoritos visitados recentemente aparecerão aqui</translation>
+<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="8428213095426709021">Configurações</translation>
@@ -679,9 +727,9 @@ Dica: o modo de navegação anônimo <ph name="SHORTCUT_KEY" /> pode ser útil d
<translation id="8498891568109133222"><ph name="HOST_NAME" /> demorou muito para responder.</translation>
<translation id="852346902619691059">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança dele não é confiável para o sistema operacional do seu dispositivo. Isso pode ser causado por uma configuração incorreta ou por um invasor que interceptou sua conexão. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Este tipo de cartão não é aceito pelo Google Payments. Selecione outro cartão.</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="8553075262323480129">A tradução falhou porque não foi possível determinar o idioma da página.</translation>
<translation id="8559762987265718583">Não é possível estabelecer uma conexão privada com <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, porque a data e a hora do seu dispositivo (<ph name="DATE_AND_TIME" />) estão incorretas.</translation>
-<translation id="856992080682148">O certificado deste site expira em 2017 ou posteriormente. A cadeia de certificados contém um certificado assinado com SHA-1.</translation>
<translation id="8571890674111243710">Traduzindo página para <ph name="LANGUAGE" />...</translation>
<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>
@@ -694,10 +742,12 @@ Dica: o modo de navegação anônimo <ph name="SHORTCUT_KEY" /> pode ser útil d
<translation id="8740359287975076522">Não foi possível encontrar o &lt;abbr id="dnsDefinition"&gt;endereço DNS&lt;/abbr&gt; de <ph name="HOST_NAME" />. Diagnosticando o problema.</translation>
<translation id="8790007591277257123">&amp;Refazer excluir</translation>
<translation id="8798099450830957504">Padrão</translation>
+<translation id="8800988563907321413">As sugestões de itens nas proximidades são exibidas aqui</translation>
<translation id="8804164990146287819">Política de Privacidade</translation>
<translation id="8820817407110198400">Favoritos</translation>
<translation id="8834246243508017242">Ativar preenchimento automático usando os Contatos.</translation>
<translation id="883848425547221593">Outros favoritos</translation>
+<translation id="884264119367021077">Endereço de entrega</translation>
<translation id="884923133447025588">Nenhum mecanismo de revogação encontrado.</translation>
<translation id="885730110891505394">Compartilhar com o Google</translation>
<translation id="8866481888320382733">Configurações da política de análise de erros</translation>
@@ -715,19 +765,22 @@ Dica: o modo de navegação anônimo <ph name="SHORTCUT_KEY" /> pode ser útil d
<translation id="8971063699422889582">O certificado do servidor expirou.</translation>
<translation id="8987927404178983737">Mês</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">O site a seguir contém programas prejudiciais</translation>
<translation id="9001074447101275817">O proxy <ph name="DOMAIN" /> exige um nome de usuário e uma senha.</translation>
<translation id="901974403500617787">Sinalizações aplicáveis a todo o sistema podem ser definidas apenas pelo proprietário: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Esta página foi traduzida para <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Erro de segurança</translation>
<translation id="9038649477754266430">Usar um serviço de previsão para carregar páginas mais rapidamente</translation>
<translation id="9039213469156557790">Além disso, esta página inclui outros recursos que não são seguros. Esses recursos podem ser visualizados por outros usuários enquanto eles navegam e podem ser modificados por um invasor para alterar o comportamento da página.</translation>
+<translation id="9040185888511745258">Invasores em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem tentar enganá-lo para que você instale programas que prejudicam sua experiência de navegação (por exemplo, mudando sua página inicial ou mostrando anúncios extras nos sites que você visita).</translation>
<translation id="9050666287014529139">Senha</translation>
<translation id="9065203028668620118">Editar</translation>
<translation id="9068849894565669697">Selecionar cor</translation>
<translation id="9076283476770535406">Pode apresentar conteúdo adulto</translation>
-<translation id="9092364396508701805">A página de <ph name="HOST_NAME" /> não está funcionando</translation>
<translation id="9103872766612412690">O site <ph name="SITE" /> geralmente usa criptografia para proteger suas informações. Quando o Chromium tentou se conectar a <ph name="SITE" /> dessa vez, o website retornou credenciais
incomuns e incorretas. Isso pode acontecer quando um invasor está fingindo ser <ph name="SITE" /> ou quando uma tela de login por Wi-Fi interrompeu a conexão. Suas informações ainda estão protegidas, porque o Chromium interrompeu a conexão antes que os dados fossem trocados.</translation>
<translation id="9137013805542155359">Mostrar original</translation>
+<translation id="9137248913990643158">Inicie e faça login no Chrome antes de usar este app.</translation>
<translation id="9148507642005240123">&amp;Desfazer editar</translation>
<translation id="9157595877708044936">Configurando...</translation>
<translation id="9170848237812810038">&amp;Desfazer</translation>
@@ -740,7 +793,6 @@ incomuns e incorretas. Isso pode acontecer quando um invasor está fingindo ser
<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="962701380617707048">Digite a data de validade e o CVC do <ph name="CREDIT_CARD" /> para atualizar os detalhes do cartão</translation>
<translation id="969892804517981540">Versão oficial</translation>
<translation id="988159990683914416">Versão do desenvolvedor</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 28614dc446e..661a17e344f 100644
--- a/chromium/components/strings/components_strings_pt-PT.xtb
+++ b/chromium/components/strings/components_strings_pt-PT.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Ligar novamente à rede Wi-Fi</translation>
<translation id="1175364870820465910">Im&amp;primir...</translation>
<translation id="1181037720776840403">Remover</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Comunicar automaticamente<ph name="END_WHITEPAPER_LINK" /> os detalhes de possíveis incidentes de segurança à Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Seguinte</translation>
<translation id="1201895884277373915">Mais a partir deste Web site</translation>
<translation id="1206967143813997005">Assinatura com inicial incorreta</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Turquesa</translation>
<translation id="1629803312968146339">Pretende que o Chrome guarde este cartão?</translation>
<translation id="1640180200866533862">Políticas do utilizador</translation>
+<translation id="1640244768702815859">Experimente <ph name="BEGIN_LINK" />aceder à página inicial do site<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">A configuração de rede é inválida e não pode ser importada.</translation>
<translation id="1644574205037202324">Histórico</translation>
<translation id="1645368109819982629">Protocolo não suportado</translation>
<translation id="1676269943528358898">Normalmente, o site <ph name="SITE" /> utiliza a encriptação para proteger as suas informações. Quando o Google Chrome tentou estabelecer ligação a <ph name="SITE" /> desta vez, o Website devolveu credenciais invulgares e incorretas. Isto pode acontecer quando um utilizador mal intencionado tenta simular ser <ph name="SITE" /> ou quando um ecrã de início de sessão Wi-Fi interrompe a ligação. As suas informações continuam seguras porque o Google Chrome interrompeu a ligação antes de qualquer troca de dados.</translation>
+<translation id="168328519870909584">Os utilizadores mal intencionados que se encontram em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem tentar instalar aplicações perigosas no seu dispositivo que roubem ou eliminem as suas informações (por exemplo, fotos, palavras-passe, mensagens e cartões de crédito).</translation>
<translation id="168841957122794586">O certificado do servidor contém uma chave criptográfica fraca.</translation>
-<translation id="1701955595840307032">Obter conteúdo sugerido</translation>
<translation id="1710259589646384581">SO</translation>
<translation id="1721312023322545264">Precisa da autorização de <ph name="NAME" /> para visitar este site</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Número de página</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Experimente executar o Diagnóstico de rede do Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Atualize a frase de acesso de sincronização.</translation>
+<translation id="1787142507584202372">Os separadores abertos aparecem aqui</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Recentemente, a Navegação segura do Google <ph name="BEGIN_LINK" />detetou programas maliciosos<ph name="END_LINK" /> em <ph name="SITE" />. Os Websites que normalmente são seguros estão, por vezes, infetados com programas maliciosos. O conteúdo malicioso é proveniente de <ph name="SUBRESOURCE_HOST" />, um distribuidor de programas maliciosos conhecido. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Pedido ou parâmetros do pedido inválidos</translation>
+<translation id="1834321415901700177">Este site contém programas prejudiciais</translation>
<translation id="1838667051080421715">Está a ver a fonte de uma página Web.</translation>
<translation id="1871208020102129563">O proxy está definido para utilizar servidores proxy fixos e não um URL de script .pac.</translation>
<translation id="1883255238294161206">Fechar lista</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Marcadores de Telemóvel</translation>
<translation id="2148716181193084225">Hoje</translation>
-<translation id="2149973817440762519">Editar marcador</translation>
<translation id="2154054054215849342">A sincronização não está disponível para o domínio</translation>
<translation id="2166049586286450108">Acesso de administrador total</translation>
<translation id="2166378884831602661">Este site não consegue fornecer uma ligação segura</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">De momento, não é possível aceder a <ph name="SITE" /> porque o Website utiliza HSTS. Geralmente, os erros de rede e os ataques são temporários, pelo que é provável que esta página funcione mais tarde. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Marcador inválido ignorado no índice <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Outros marcadores</translation>
+<translation id="2355395290879513365">Os atacantes podem ver as imagens que está a visualizar neste site e enganá-lo ao modificá-las.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Apresentar políticas sem valor definido</translation>
<translation id="2396249848217231973">&amp;Anular eliminação</translation>
<translation id="2455981314101692989">Esta página Web desactivou o preenchimento automático para este formulário.</translation>
+<translation id="2460160116472764928">Recentemente, a Navegação segura do Google <ph name="BEGIN_LINK" />detetou programas maliciosos<ph name="END_LINK" /> em <ph name="SITE" />. Os Websites que normalmente são seguros estão, por vezes, infetados com programas maliciosos. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Preencher</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Executar o Diagnóstico de rede<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL de pesquisa inválido.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Tem a certeza de que pretende eliminar estas páginas do seu histórico?</translation>
<translation id="2677748264148917807">Sair</translation>
<translation id="269990154133806163">O servidor apresentou um certificado que não foi publicamente divulgado através da política de Transparência de certificados. Isto é um requisito para alguns certificados para garantir que são fidedignos e para a proteção contra utilizadores mal-intencionados. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Lista de leitura</translation>
<translation id="2704283930420550640">O valor não corresponde ao formato.</translation>
<translation id="2704951214193499422">O Chromium não conseguiu confirmar o seu cartão neste momento. Tente novamente mais tarde.</translation>
<translation id="2705137772291741111">A cópia guardada (em cache) deste site era ilegível.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Tentou aceder a <ph name="DOMAIN" />, mas o certificado que o servidor apresentou foi revogado pelo respetivo emissor. Isto significa que as credenciais de segurança que o servidor apresentou não devem, em circunstância alguma, ser consideradas fidedignas. Pode estar a comunicar com um utilizador mal-intencionado. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Pedir autorização</translation>
<translation id="2713444072780614174">Branco</translation>
+<translation id="2720342946869265578">Próximo</translation>
<translation id="2721148159707890343">Pedido com êxito</translation>
<translation id="2728127805433021124">O certificado do servidor foi assinado utilizando um algoritmo de assinatura fraco.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Executar o Diagnóstico de conetividade<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Pode desativar qualquer proxy configurado para uma ligação a partir da página de definições.</translation>
<translation id="2955913368246107853">Fechar barra de localização</translation>
<translation id="2958431318199492670">A configuração de rede não cumpre a norma ONC. Partes da configuração podem não ser importadas.</translation>
+<translation id="29611076221683977">Os hackers que atualmente se encontram 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, fotografias, palavras-passe, mensagens e cartões de crédito).</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 Websites 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="2985306909656435243">Se ativada, o Chromium armazena uma cópia do seu cartão neste dispositivo para preencher formulários mais rapidamente.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Ocultar detalhes</translation>
<translation id="3587482841069643663">Tudo</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">O servidor não suporta a extensão de renegociação TLS.</translation>
<translation id="36224234498066874">Limpar dados de navegação...</translation>
<translation id="362276910939193118">Mostrar histórico completo</translation>
<translation id="3623476034248543066">Apresentar o valor</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Licenças esgotadas</translation>
<translation id="3714780639079136834">Ativar os dados móveis ou o Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Verificar a configuração do proxy, da firewall e de DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Se compreende os riscos para a sua segurança, pode <ph name="BEGIN_LINK" />visitar este site não seguro<ph name="END_LINK" /> antes de os programas perigosos terem sido removidos.</translation>
<translation id="3739623965217189342">Link copiado por si</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="3788090790273268753">O certificado deste site expira em 2016 e a cadeia de certificados inclui um certificado assinado através de SHA-1.</translation>
<translation id="382518646247711829">Se utilizar um servidor de proxy...</translation>
<translation id="3828924085048779000">Não é permitida uma frase de acesso vazia.</translation>
<translation id="3845539888601087042">A mostrar o histórico do seus dispositivos com sessão iniciada. <ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Chave "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">O cliente e o servidor não suportam uma versão do protocolo SSL ou um conjunto de cifras comum.</translation>
<translation id="4079302484614802869">A configuração do proxy está definida para utilizar um URL de script .pac e não servidores proxy fixos.</translation>
+<translation id="4098354747657067197">O site seguinte é fraudulento</translation>
<translation id="4103249731201008433">Número de série do dispositivo é inválido</translation>
<translation id="4103763322291513355">Visite &lt;strong&gt;chrome://policy&lt;/strong&gt; para ver os URLs que foram colocados na lista negra e outras políticas aplicadas pelo administrador do sistema.</translation>
<translation id="4110615724604346410">Este servidor não conseguiu provar que é <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 utilizador mal-intencionado que intercetou a sua ligação. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{nenhuma}=1{1 aplicação ($1)}=2{2 aplicações ($1, $2)}other{# aplicações ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Erros</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Experimente executar o Diagnóstico de rede<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">A ligação a este site não é totalmente segura</translation>
<translation id="4250680216510889253">Não</translation>
<translation id="425582637250725228">É possível que as alterações não tenham sido efetuadas.</translation>
<translation id="4258748452823770588">Assinatura incorreta</translation>
<translation id="4269787794583293679">(Sem nome de utilizador)</translation>
+<translation id="4280429058323657511">, exp. <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Recentemente, a Navegação segura do Google <ph name="BEGIN_LINK" />encontrou programas prejudiciais<ph name="END_LINK" /> em <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Sugestões superiores</translation>
<translation id="4304224509867189079">Iniciar sessão</translation>
<translation id="432290197980158659">O servidor apresentou um certificado que não corresponde às expetativas existentes. Estas expetativas estão incluídas em determinados Websites de alta segurança para o proteger. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Falha ao encontrar o artigo</translation>
+<translation id="4326324639298822553">Verifique a data de validade e tente novamente</translation>
<translation id="4331708818696583467">Inseguro</translation>
+<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="4381091992796011497">Nome do utilizador:</translation>
<translation id="4394049700291259645">Desactivar</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Tentou aceder a <ph name="DOMAIN" />, mas o servidor apresentou um certificado inválido. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Separadores de outros dispositivos</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Está a ver a página de uma extensão.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Recarregar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
<translation id="4744603770635761495">Caminho do Executável</translation>
+<translation id="4750917950439032686">As suas informações (por exemplo, palavras-passe ou números de cartões de crédito) são privadas quando são enviadas para este site.</translation>
<translation id="4756388243121344051">&amp;Histórico</translation>
+<translation id="4759118997339041434">Preenchimento automático do pagamento desativado</translation>
<translation id="4764776831041365478">A página Web em <ph name="URL" /> poderá estar temporariamente inactiva ou poderá ter sido movida permanentemente para um novo endereço Web.</translation>
<translation id="4771973620359291008">Ocorreu um erro desconhecido.</translation>
<translation id="4800132727771399293">Verifique a data de validade e o Código de Segurança/CVC e tente novamente</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Erro de rede</translation>
<translation id="4816492930507672669">Ajustar à página</translation>
<translation id="4850886885716139402">Ver</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{e mais 1 página Web}other{e mais # páginas Web}}</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="495170559598752135">Ações</translation>
<translation id="4958444002117714549">Expandir lista</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">A transferir</translation>
<translation id="5190835502935405962">Barra de marcadores</translation>
<translation id="5199729219167945352">Experiências</translation>
-<translation id="5199841536747119669">As suas sugestões são apresentadas aqui</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="5299298092464848405">Erro ao analisar a política</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">O relatório de falha está desativado.</translation>
<translation id="5317780077021120954">Guardar</translation>
<translation id="5327248766486351172">Nome</translation>
+<translation id="5337705430875057403">Utilizadores mal intencionados em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem enganá-lo no sentido de fazer algo perigoso como instalar software ou revelar as suas informações pessoais (por exemplo, palavras-passe, números de telefone ou cartões de crédito).</translation>
<translation id="5359637492792381994">Este servidor não conseguiu provar que é <ph name="DOMAIN" />; o respetivo certificado de segurança não é válido neste momento. Isto pode ser o resultado de uma configuração incorreta ou de um utilizador mal-intencionado que intercetou a sua ligação. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Falha ao armazenar as definições da política</translation>
+<translation id="5386426401304769735">A cadeia de certificados inclui um certificado assinado através de SHA-1.</translation>
<translation id="5421136146218899937">Limpar dados de navegação...</translation>
<translation id="5430298929874300616">Remover marcador</translation>
<translation id="5431657950005405462">O ficheiro não foi encontrado</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Uma página incorporada em <ph name="SITE" /> diz:</translation>
<translation id="5556459405103347317">Recarregar</translation>
<translation id="5565735124758917034">Ativo</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>
+<translation id="5580958916614886209">Verifique o mês de validade e tente novamente</translation>
<translation id="560412284261940334">Gestão não suportada</translation>
<translation id="5610142619324316209">Verificar a ligação</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> redirecionou-o demasiadas vezes.</translation>
<translation id="5622887735448669177">Pretende sair deste site?</translation>
<translation id="5629630648637658800">Falha ao carregar as definições da política</translation>
<translation id="5631439013527180824">Token de gestão do dispositivo inválido</translation>
+<translation id="5669703222995421982">Obter conteúdo personalizado</translation>
+<translation id="5675650730144413517">Esta página não está a funcionar</translation>
<translation id="5677928146339483299">Bloqueado</translation>
<translation id="5694783966845939798">Tentou aceder a <ph name="DOMAIN" />, mas o servidor apresentou um certificado assinado com um algoritmo de assinatura fraco (como SHA-1). Isto significa que as credenciais de segurança que o servidor apresentou podem ter sido falsificadas e que o servidor pode não ser o servidor esperado (pode estar a comunicar com um utilizador mal-intencionado). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">A identidade deste Web site não foi verificada.</translation>
<translation id="5720705177508910913">Utilizador atual</translation>
-<translation id="572328651809341494">Separadores recentes</translation>
<translation id="5732392974455271431">Os teus pais podem desbloquear-te</translation>
<translation id="5784606427469807560">Ocorreu um erro ao confirmar o cartão. Verifique a sua ligação à Internet e tente novamente.</translation>
<translation id="5785756445106461925">Além disso, esta página inclui outros recursos que não são seguros. Estes recursos podem ser vistos por outros utilizadores em trânsito e modificados por um utilizador mal intencionado com o intuito de alterar o aspeto da página.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Tentou aceder a <ph name="DOMAIN" />, mas o servidor apresentou um certificado que contém uma chave fraca. Um utilizador mal-intencionado pode ter quebrado a chave privada e o servidor pode não ser o servidor esperado (pode estar a comunicar com um utilizador mal-intencionado). <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Nova pasta</translation>
<translation id="5869405914158311789">Não é possível aceder a este site</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">O Google Payments não é compatível com este tipo de cartão para este comerciante. Selecione outro cartão.</translation>
<translation id="59174027418879706">Ativada</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="5966707198760109579">Semana</translation>
<translation id="5967867314010545767">Remover do histórico</translation>
<translation id="5975083100439434680">Reduzir</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Experimente:</translation>
<translation id="6151417162996330722">O certificado do servidor tem um período de validade demasiado longo.</translation>
<translation id="6165508094623778733">Saiba mais</translation>
+<translation id="6177128806592000436">A sua ligação a este site não é segura</translation>
<translation id="6203231073485539293">Verificar a ligação à Internet</translation>
<translation id="6218753634732582820">Pretende remover o endereço do Chromium?</translation>
+<translation id="6251924700383757765">Política de privacidade</translation>
+<translation id="625755898061068298">Optou por desativar os avisos de segurança para este site.</translation>
<translation id="6259156558325130047">&amp;Refazer reordenação</translation>
<translation id="6263376278284652872">Marcadores do <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Retroceder para segurança</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> está inacessível.</translation>
<translation id="6321917430147971392">Verificar as definições do DNS</translation>
<translation id="6328639280570009161">Tente desativar a previsão de rede</translation>
+<translation id="6328786501058569169">Este site é fraudulento</translation>
<translation id="6337534724793800597">Filtrar políticas pelo nome</translation>
<translation id="6342069812937806050">Mesmo agora</translation>
<translation id="6345221851280129312">tamanho desconhecido</translation>
@@ -489,6 +519,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{1 outra sugestão}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="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>
<translation id="6416403317709441254">De momento, não é possível aceder a <ph name="SITE" /> porque o Website enviou credenciais codificadas que o Chromium não consegue processar. Geralmente, os erros de rede e os ataques são temporários, pelo que é provável que esta página funcione mais tarde. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Não é possível verificar se o certificado foi revogado.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Ignorado porque a pesquisa predefinida foi desativada pela política.</translation>
<translation id="6462969404041126431">Este servidor não conseguiu provar que é <ph name="DOMAIN" />; o respetivo certificado de segurança pode ser revogado. Isto pode ser o resultado de uma configuração incorreta ou de um utilizador mal-intencionado que intercetou a sua ligação. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Refazer eliminação</translation>
<translation id="6534179046333460208">Sugestões da Web física</translation>
<translation id="6550675742724504774">Opções</translation>
+<translation id="6556239504065605927">Ligação segura</translation>
<translation id="6563469144985748109">O seu administrador ainda não o aprovou</translation>
<translation id="6593753688552673085">menos de <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opções de encriptação</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Esta política está obsoleta.</translation>
<translation id="6652240803263749613">Este servidor não conseguiu provar que é <ph name="DOMAIN" />; o sistema operativo do seu computador não confia no respetivo certificado de segurança. Isto pode ser o resultado de uma configuração incorreta ou de um utilizador mal-intencionado que intercetou a sua ligação. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Editar Pasta</translation>
-<translation id="6660210980321319655">Relatório enviado automaticamente em: <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Valor da política</translation>
<translation id="6757797048963528358">O dispositivo entrou em suspensão.</translation>
<translation id="6778737459546443941">O teu pai/a tua mãe ainda não o aprovou</translation>
+<translation id="6810899417690483278">ID de personalização</translation>
<translation id="6820686453637990663">Código de segurança</translation>
<translation id="6830600606572693159">De momento, a página Web em <ph name="URL" /> está indisponível. Poderá estar sobrecarregada ou em manutenção.</translation>
<translation id="6831043979455480757">Traduzir</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Informações de contacto</translation>
+<translation id="7079718277001814089">Este site contém um programa malicioso</translation>
<translation id="7087282848513945231">Condado</translation>
<translation id="7088615885725309056">Mais antigo</translation>
<translation id="7090678807593890770">Pesquisar <ph name="LINK" /> no Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> não respeita os padrões de segurança.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Saiba mais<ph name="END_LINK" /> sobre este problema.</translation>
<translation id="7219179957768738017">A ligação utiliza <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">O site que pretende visitar contém software malicioso</translation>
<translation id="724975217298816891">Introduza a data de validade e o Código de Segurança/CVC de <ph name="CREDIT_CARD" /> para atualizar os detalhes do cartão. Ao confirmar, os detalhes do cartão são partilhados com este site.</translation>
<translation id="725866823122871198">Não é possível estabelecer uma ligação privada a <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, porque a data e a hora do computador (<ph name="DATE_AND_TIME" />) estão incorretas.</translation>
<translation id="7269802741830436641">Esta página Web tem um ciclo de redireccionamento</translation>
@@ -611,6 +648,7 @@ O modo de navegação anónima <ph name="SHORTCUT_KEY" /> pode ser útil da pró
<translation id="7658239707568436148">Cancelar</translation>
<translation id="7667346355482952095">O símbolo da política devolvido está vazio ou não corresponde ao símbolo atual</translation>
<translation id="7668654391829183341">Dispositivo desconhecido</translation>
+<translation id="7669271284792375604">Os utilizadores mal intencionados neste site podem tentar enganá-lo para instalar programas que são prejudiciais para a sua experiência de navegação (por exemplo, ao alterar a sua página inicial ou ao mostrar anúncios adicionais em sites que visita).</translation>
<translation id="7674629440242451245">Está interessado nas novas e fantásticas funcionalidades do Chrome? Experimente o nosso canal para programadores em chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Prosseguir para <ph name="SITE" /> (não seguro)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Não é possível carregar este site a partir da cache</translation>
@@ -626,14 +664,17 @@ O modo de navegação anónima <ph name="SHORTCUT_KEY" /> pode ser útil da pró
<translation id="7800304661137206267">A ligação é encriptada utilizando <ph name="CIPHER" />, com <ph name="MAC" /> para autenticação de mensagens e <ph name="KX" /> como mecanismo de troca de chaves.</translation>
<translation id="780301667611848630">Não, obrigado</translation>
<translation id="7805768142964895445">Estado</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Pretende remover a sugestão do formulário do Chrome?</translation>
<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 Websites que visitar.</translation>
+<translation id="7855695075675558090"><ph name="TOTAL_LABEL" /> <ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="7887683347370398519">Verifique o Código de Segurança/CVC e tente novamente</translation>
<translation id="7912024687060120840">Na Pasta:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">O certificado do servidor ainda não é válido.</translation>
<translation id="7942349550061667556">Vermelho</translation>
+<translation id="7947285636476623132">Verifique o ano de validade e tente novamente</translation>
<translation id="7951415247503192394">32 bits</translation>
<translation id="7956713633345437162">Marcadores do telemóvel</translation>
<translation id="7961015016161918242">Nunca</translation>
@@ -641,7 +682,10 @@ O modo de navegação anónima <ph name="SHORTCUT_KEY" /> pode ser útil da pró
<translation id="7983301409776629893">Traduzir sempre do <ph name="ORIGINAL_LANGUAGE" /> para o <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Não especificado</translation>
<translation id="8012647001091218357">Não conseguimos falar com os seus pais de momento. Tente novamente.</translation>
+<translation id="8025119109950072390">Os utilizadores mal intencionados neste site podem enganá-lo no sentido de fazer algo perigoso como instalar software ou revelar as suas informações pessoais (por exemplo, palavras-passe, números de telefone ou cartões de crédito).</translation>
+<translation id="803030522067524905">Recentemente, a Navegação segura do Google detetou atividade de phishing em <ph name="SITE" />. Os sites de phishing fazem-se passar por outros Websites para o enganar. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Esta página está em <ph name="SOURCE_LANGUAGE" />. Pretende traduzi-la para <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Enviar comentários</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>
@@ -652,9 +696,11 @@ O modo de navegação anónima <ph name="SHORTCUT_KEY" /> pode ser útil da pró
<translation id="8150722005171944719">O ficheiro em <ph name="URL" /> não é legível. Pode ter sido removido, movido ou as permissões do ficheiro podem estar a impedir o acesso.</translation>
<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="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="8230421197304563332">Os hackers que se encontram em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem tentar instalar programas perigosos no seu computador que roubam ou eliminam as suas informações (por exemplo, fotografias, palavras-passe, mensagens e cartões de crédito).</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="8249320324621329438">Última obtenção:</translation>
<translation id="8261506727792406068">Eliminar</translation>
@@ -663,11 +709,13 @@ O modo de navegação anónima <ph name="SHORTCUT_KEY" /> pode ser útil da pró
<translation id="8294431847097064396">Origem</translation>
<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="8349305172487531364">Barra de marcadores</translation>
<translation id="8363502534493474904">Desativar o modo de avião</translation>
<translation id="8364627913115013041">Não definida.</translation>
<translation id="8380941800586852976">Perigosa</translation>
<translation id="8382348898565613901">Os seus marcadores visitados recentemente são apresentados aqui.</translation>
+<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="8428213095426709021">Definições</translation>
@@ -679,9 +727,9 @@ O modo de navegação anónima <ph name="SHORTCUT_KEY" /> pode ser útil da pró
<translation id="8498891568109133222"><ph name="HOST_NAME" /> demorou demasiado tempo a responder.</translation>
<translation id="852346902619691059">Este servidor não conseguiu provar que é <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 utilizador mal-intencionado que intercetou a sua ligação. <ph name="BEGIN_LEARN_MORE_LINK" />Saiba mais<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">O Google Payments não é compatível com este tipo de cartão. Selecione outro cartão.</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="8553075262323480129">A tradução falhou porque não foi possível determinar o idioma da página.</translation>
<translation id="8559762987265718583">Não é possível estabelecer uma ligação privada a <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, porque a data e a hora do seu dispositivo (<ph name="DATE_AND_TIME" />) estão incorretas.</translation>
-<translation id="856992080682148">O certificado deste site expira em 2017 ou depois e a cadeia de certificados inclui um certificado assinado através de SHA-1.</translation>
<translation id="8571890674111243710">A traduzir a página para <ph name="LANGUAGE" />...</translation>
<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>
@@ -694,10 +742,12 @@ O modo de navegação anónima <ph name="SHORTCUT_KEY" /> pode ser útil da pró
<translation id="8740359287975076522">Não foi possível encontrar o &lt;abbr id="dnsDefinition"&gt;endereço DNS&lt;/abbr&gt; de <ph name="HOST_NAME" />. Estamos a diagnosticar o problema.</translation>
<translation id="8790007591277257123">&amp;Refazer eliminação</translation>
<translation id="8798099450830957504">Predefinição</translation>
+<translation id="8800988563907321413">As sugestões próximas aparecem aqui</translation>
<translation id="8804164990146287819">Política de Privacidade</translation>
<translation id="8820817407110198400">Marcadores</translation>
<translation id="8834246243508017242">Ativar preenchimento automático utilizando os contactos...</translation>
<translation id="883848425547221593">Outros marcadores</translation>
+<translation id="884264119367021077">Endereço para envio</translation>
<translation id="884923133447025588">Não foi encontrado qualquer mecanismo de revogação.</translation>
<translation id="885730110891505394">Partilha com a Google</translation>
<translation id="8866481888320382733">Erro ao analisar as definições da política</translation>
@@ -715,18 +765,21 @@ O modo de navegação anónima <ph name="SHORTCUT_KEY" /> pode ser útil da pró
<translation id="8971063699422889582">O certificado do servidor expirou.</translation>
<translation id="8987927404178983737">Mês</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">O site que se segue contém programas prejudiciais</translation>
<translation id="9001074447101275817">O proxy <ph name="DOMAIN" /> necessita de um nome de utilizador e de uma palavra-passe.</translation>
<translation id="901974403500617787">Os sinalizadores que se aplicam a todo o sistema apenas podem ser definidos pelo proprietário: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Esta página foi traduzida para <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Erro de segurança</translation>
<translation id="9038649477754266430">Utilizar um serviço de previsão para carregar páginas mais rapidamente</translation>
<translation id="9039213469156557790">Além disso, esta página inclui outros recursos que não são seguros. Estes recursos podem ser vistos por outros utilizadores em trânsito e modificados por um utilizador mal intencionado com o intuito de alterar o comportamento da página.</translation>
+<translation id="9040185888511745258">Os atacantes em <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> podem tentar enganá-lo(a) para instalar programas que são prejudiciais para a sua experiência de navegação (por exemplo, ao alterar a sua página inicial ou ao mostrar anúncios adicionais em sites que visita).</translation>
<translation id="9050666287014529139">Frase de acesso</translation>
<translation id="9065203028668620118">Editar</translation>
<translation id="9068849894565669697">Selecionar cor</translation>
<translation id="9076283476770535406">Pode ter conteúdo para adultos</translation>
-<translation id="9092364396508701805">A página de <ph name="HOST_NAME" /> não está a funcionar</translation>
<translation id="9103872766612412690">Normalmente, o site <ph name="SITE" /> utiliza a encriptação para proteger as suas informações. Quando o Chromium tentou estabelecer ligação a <ph name="SITE" /> desta vez, o Website devolveu credenciais invulgares e incorretas. Isto pode acontecer quando um utilizador mal intencionado tenta simular ser <ph name="SITE" /> ou quando um ecrã de início de sessão Wi-Fi interrompe a ligação. As suas informações continuam seguras porque o Chromium interrompeu a ligação antes de qualquer troca de dados.</translation>
<translation id="9137013805542155359">Mostrar original</translation>
+<translation id="9137248913990643158">Comece e inicie sessão no Chrome antes de utilizar esta aplicação.</translation>
<translation id="9148507642005240123">&amp;Anular edição</translation>
<translation id="9157595877708044936">A configurar...</translation>
<translation id="9170848237812810038">An&amp;ular</translation>
@@ -739,7 +792,6 @@ O modo de navegação anónima <ph name="SHORTCUT_KEY" /> pode ser útil da pró
<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="962701380617707048">Introduza a data de validade e o Código de Segurança/CVC de <ph name="CREDIT_CARD" /> para atualizar os detalhes do cartão</translation>
<translation id="969892804517981540">Compilação oficial</translation>
<translation id="988159990683914416">Compilação de programador</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 3e10ba4dc5c..ee0a9288f3c 100644
--- a/chromium/components/strings/components_strings_ro.xtb
+++ b/chromium/components/strings/components_strings_ro.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">să te reconectezi la Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Printează...</translation>
<translation id="1181037720776840403">Elimină</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Raportează automat<ph name="END_WHITEPAPER_LINK" /> la Google detaliile eventualelor incidente privind securitatea. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">ÃŽnainte</translation>
<translation id="1201895884277373915">Mai multe de la acest site</translation>
<translation id="1206967143813997005">Semnătură inițială nevalidă</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cyan</translation>
<translation id="1629803312968146339">Dorești ca acest card să fie salvat în Chrome?</translation>
<translation id="1640180200866533862">Politici privind utilizatorii</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />Accesează pagina de pornire a site-ului<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Configurația rețelei este nevalidă și nu a putut fi importată.</translation>
<translation id="1644574205037202324">Istoric</translation>
<translation id="1645368109819982629">Protocol neacceptat</translation>
<translation id="1676269943528358898">Site-ul <ph name="SITE" /> folosește în mod obișnuit criptarea pentru a-ți proteja informațiile. Când Google Chrome a încercat să se conecteze la <ph name="SITE" /> de această dată, site-ul a returnat date de conectare neobișnuite și incorecte. Acest lucru s-a întâmplat fie pentru că un atacator încearcă să falsifice site-ul <ph name="SITE" />, fie pentru că un ecran de conectare Wi-Fi a întrerupt conexiunea. Securitatea informațiilor tale nu a fost afectată, deoarece Google Chrome a oprit conexiunea înainte ca datele să fie transferate.</translation>
+<translation id="168328519870909584">Atacatorii de pe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pot încerca să instaleze aplicații periculoase pe dispozitiv, care să îți fure sau să îți șteargă informațiile (de exemplu, fotografii, parole, mesaje și carduri de credit).</translation>
<translation id="168841957122794586">Certificatul de server conține o cheie criptografică slabă.</translation>
-<translation id="1701955595840307032">Obține sugestii de conținut</translation>
<translation id="1710259589646384581">Sistem de operare</translation>
<translation id="1721312023322545264">Ai nevoie de permisiunea utilizatorului <ph name="NAME" /> ca să accesezi acest site</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Numărul paginii</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Rulează Diagnostice rețea Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Actualizează expresia de acces pentru sincronizare.</translation>
+<translation id="1787142507584202372">Filele deschise sunt afișate aici</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Recent, Navigarea sigură Google <ph name="BEGIN_LINK" />a detectat 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. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Solicitarea sau parametrii săi sunt greșiți</translation>
+<translation id="1834321415901700177">Acest site conține programe dăunătoare</translation>
<translation id="1838667051080421715">Se afișează sursa unei pagini web.</translation>
<translation id="1871208020102129563">Configurația proxy este setată să utilizeze servere proxy fixe, și nu o adresă URL pentru scripturi .pac.</translation>
<translation id="1883255238294161206">Restrângeți lista</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Client nativ</translation>
<translation id="213826338245044447">Marcaje mobile</translation>
<translation id="2148716181193084225">Astăzi</translation>
-<translation id="2149973817440762519">Editați marcajul</translation>
<translation id="2154054054215849342">Sincronizarea nu este disponibilă pentru domeniul tău</translation>
<translation id="2166049586286450108">Acces complet de administrare</translation>
<translation id="2166378884831602661">Acest site nu poate oferi o conexiune sigură</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Momentan, nu poți accesa site-ul <ph name="SITE" />, deoarece acesta folosește HSTS. Erorile de rețea și atacurile sunt de obicei temporare și probabil că această pagină va funcționa mai târziu. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Marcaj nevalid ignorat la indexul <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Alte marcaje</translation>
+<translation id="2355395290879513365">Este posibil ca atacatorii să poată vedea imaginile la care te uiți pe acest site și să te păcălească prin modificarea lor.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Afișați politicile care nu au valori setate</translation>
<translation id="2396249848217231973">&amp;Anulați ștergerea</translation>
<translation id="2455981314101692989">Această pagină web a dezactivat completarea automată a formularului.</translation>
+<translation id="2460160116472764928">Recent, Navigarea sigură Google <ph name="BEGIN_LINK" />a detectat programe malware<ph name="END_LINK" /> pe <ph name="SITE" />. Site-urile care sunt de obicei sigure sunt uneori infectate cu programe malware. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Completează</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />să rulezi Diagnostice rețea<ph name="END_LINK" />;</translation>
<translation id="2479410451996844060">Adresă URL de căutare nevalidă.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Sigur doriți să ștergeți aceste pagini din istoric?</translation>
<translation id="2677748264148917807">Ieși</translation>
<translation id="269990154133806163">Serverul a prezentat un certificat care nu a fost dezvăluit public folosind politica privind Transparența certificatului. Aceasta este o cerință pentru anumite certificate, pentru a se asigura că sunt de încredere și pentru protecție împotriva atacatorilor. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Listă de lectură</translation>
<translation id="2704283930420550640">Valoarea nu se potrivește cu formatul.</translation>
<translation id="2704951214193499422">Momentan, Chromium nu a putut confirma cardul. Încearcă din nou mai târziu.</translation>
<translation id="2705137772291741111">Copia salvată (în memoria cache) a acestui site nu a putut fi citită.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Ai încercat să accesezi <ph name="DOMAIN" />, dar certificatul prezentat de server a fost revocat de emitentul său. Acest lucru înseamnă că datele de conectare de securitate prezentate de server nu sunt deloc de încredere. Este posibil să comunici cu un atacator. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Solicită permisiunea</translation>
<translation id="2713444072780614174">Alb</translation>
+<translation id="2720342946869265578">ÃŽn apropiere</translation>
<translation id="2721148159707890343">Solicitarea a reușit</translation>
<translation id="2728127805433021124">Certificatul serverului este semnat utilizând un algoritm de semnătură slab.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />să rulezi Diagnostice conectivitate<ph name="END_LINK" />;</translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Puteți să dezactivați serverele proxy configurate pentru o conexiune din pagina de setări.</translation>
<translation id="2955913368246107853">Închide Bara de căutare</translation>
<translation id="2958431318199492670">Configurația rețelei nu respectă standardul ONC. Este posibil ca anumite părți ale configurației să nu fie importate.</translation>
+<translation id="29611076221683977">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, fotografiile, parolele, mesajele sau informațiile despre cardurile de credit).</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="2985306909656435243">Dacă opțiunea este activată, Chromium va stoca o copie a cardului pe dispozitiv pentru a completa formularul mai rapid.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Ascunde detaliile</translation>
<translation id="3587482841069643663">Toate</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Serverul nu acceptă extensia de renegociere TLS.</translation>
<translation id="36224234498066874">Ștergeți datele de navigare...</translation>
<translation id="362276910939193118">Afișează întregul istoric</translation>
<translation id="3623476034248543066">Afișați valoarea</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Licențe epuizate</translation>
<translation id="3714780639079136834">să activezi datele mobile sau conexiunea Wi-Fi;</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />să verifici configurarea pentru proxy, firewall și DNS;<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Dacă înțelegeți riscurile de securitate, puteți să <ph name="BEGIN_LINK" />accesați acest site nesigur<ph name="END_LINK" /> înainte ca programele periculoase să fie eliminate.</translation>
<translation id="3739623965217189342">Linkul copiat de tine</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="3788090790273268753">Certificatul pentru acest site expiră în 2016, iar lanțul de certificate conține un certificat semnat cu SHA-1.</translation>
<translation id="382518646247711829">Dacă utilizați un server proxy...</translation>
<translation id="3828924085048779000">Trebuie să fie introdusă expresia de acces.</translation>
<translation id="3845539888601087042">Se afișează istoricul de pe dispozitivele pe care te-ai conectat. <ph name="BEGIN_LINK" />Află mai multe<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Cheie „<ph name="SUBKEY" />â€: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Clientul și serverul nu acceptă o versiune a protocolului SSL sau o suită de codificare obișnuită.</translation>
<translation id="4079302484614802869">Configurația pentru proxy este setată să utilizeze o adresă URL pentru scripturi .pac, și nu servere proxy fixe.</translation>
+<translation id="4098354747657067197">Urmează un site înșelător</translation>
<translation id="4103249731201008433">Numărul de serie al gadgetului este nevalid</translation>
<translation id="4103763322291513355">Accesați &lt;strong&gt;chrome://policy&lt;/strong&gt; pentru a vedea adresele URL introduse pe lista neagră și alte politici impuse de către administratorul de sistem.</translation>
<translation id="4110615724604346410">Acest server nu a putut dovedi că este <ph name="DOMAIN" />. Certificatul său de securitate conține erori. Cauza poate fi o eroare de configurare sau interceptarea conexiunii de către un atacator. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{niciuna}=1{1 aplicație ($1)}=2{2 aplicații ($1, $2)}few{# aplicații ($1, $2, $3)}other{# de aplicații ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Blocări</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Rulează Diagnostice rețea<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Conexiunea la acest site nu este complet sigură</translation>
<translation id="4250680216510889253">Nu</translation>
<translation id="425582637250725228">Este posibil ca modificările să nu se salveze.</translation>
<translation id="4258748452823770588">Semnătură greșită</translation>
<translation id="4269787794583293679">(Niciun nume de utilizator)</translation>
+<translation id="4280429058323657511">data expirării: <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Recent, Navigarea sigură Google <ph name="BEGIN_LINK" />a găsit programe rău-intenționate<ph name="END_LINK" /> pe <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Sugestii parentale</translation>
<translation id="4304224509867189079">Conectează-te</translation>
<translation id="432290197980158659">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, cu un grad sporit de securitate, pentru a te proteja. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Articolul nu a fost găsit</translation>
+<translation id="4326324639298822553">Verifică data de expirare și încearcă din nou</translation>
<translation id="4331708818696583467">Nesecurizat</translation>
+<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="4381091992796011497">Nume de utilizator:</translation>
<translation id="4394049700291259645">Dezactivează</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Ai încercat să accesezi <ph name="DOMAIN" />, dar serverul a prezentat un certificat nevalid. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">File de pe alte dispozitive</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Se afișează pagina extensiei.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Reîncărcați politicile</translation>
<translation id="4728558894243024398">Platformă</translation>
<translation id="4744603770635761495">Cale executabilă</translation>
+<translation id="4750917950439032686">Informațiile tale (de exemplu, parolele și numerele cardurilor de credit) sunt private când sunt trimise la acest site.</translation>
<translation id="4756388243121344051">&amp;Istoric</translation>
+<translation id="4759118997339041434">Completarea automată pentru plată este dezactivată</translation>
<translation id="4764776831041365478">Pagina web de la <ph name="URL" /> poate fi temporar nefuncțională sau a fost mutată definitiv la o nouă adresă web.</translation>
<translation id="4771973620359291008">A apărut o eroare necunoscută.</translation>
<translation id="4800132727771399293">Verifică data de expirare și codul CVC și încearcă din nou</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Eroare de rețea</translation>
<translation id="4816492930507672669">Încadrați în pagină</translation>
<translation id="4850886885716139402">Afișează</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Acțiuni</translation>
<translation id="4958444002117714549">Extindeți lista</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Se descarcă</translation>
<translation id="5190835502935405962">Bară de marcaje</translation>
<translation id="5199729219167945352">Experimente</translation>
-<translation id="5199841536747119669">Sugestiile apar aici</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="5299298092464848405">Eroare la analizarea politicii</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Raportarea blocărilor este dezactivată.</translation>
<translation id="5317780077021120954">Salvează</translation>
<translation id="5327248766486351172">Nume</translation>
+<translation id="5337705430875057403">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 personale (de exemplu, parole, numere de telefon sau carduri de credit).</translation>
<translation id="5359637492792381994">Acest server nu a putut dovedi că este <ph name="DOMAIN" />. Momentan, certificatul său de securitate nu este valid. Cauza poate fi o eroare de configurare sau interceptarea conexiunii de către un atacator. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Setările pentru politică nu au putut fi stocate</translation>
+<translation id="5386426401304769735">Lanțul de certificate pentru acest site conține un certificat semnat folosind SHA-1.</translation>
<translation id="5421136146218899937">Șterge datele de navigare...</translation>
<translation id="5430298929874300616">Elimină marcajul</translation>
<translation id="5431657950005405462">Fișierul nu a fost găsit</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">O pagină încorporată de pe <ph name="SITE" /> afișează mesajul:</translation>
<translation id="5556459405103347317">Reîncarcă</translation>
<translation id="5565735124758917034">Activ</translation>
+<translation id="5572851009514199876">Pornește și conectează-te la Chrome, ca acesta să verifice dacă ai permisiunea să accesezi site-ul.</translation>
+<translation id="5580958916614886209">Verifică luna în care expiră și încearcă din nou</translation>
<translation id="560412284261940334">Gestionarea nu este acceptată</translation>
<translation id="5610142619324316209">să verifici conexiunea;</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> te-a redirecționat de prea multe ori.</translation>
<translation id="5622887735448669177">Dorești să părăsești acest site?</translation>
<translation id="5629630648637658800">Setările pentru politică nu au putut fi încărcate</translation>
<translation id="5631439013527180824">Indicativ nevalid pentru gestionarea gadgetului</translation>
+<translation id="5669703222995421982">Obține conținut personalizat</translation>
+<translation id="5675650730144413517">Pagina nu funcționează</translation>
<translation id="5677928146339483299">Blocat</translation>
<translation id="5694783966845939798">Ai încercat să accesezi <ph name="DOMAIN" />, dar serverul a prezentat un certificat semnat folosind un algoritm de semnare slab (cum ar fi SHA-1). Acest lucru înseamnă că este posibil ca datele de conectare de securitate prezentate de server să fie falsificate sau ca serverul să nu fie cel așteptat (este posibil să comunici cu un atacator). <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Identitatea acestui site nu a fost confirmată.</translation>
<translation id="5720705177508910913">Utilizator curent</translation>
-<translation id="572328651809341494">File recente</translation>
<translation id="5732392974455271431">Părinții tăi îl pot debloca pentru tine</translation>
<translation id="5784606427469807560">A apărut o eroare la confirmarea cardului. Verifică conexiunea la internet și încearcă din nou.</translation>
<translation id="5785756445106461925">În plus, această pagină include alte resurse care nu sunt securizate. Aceste resurse sunt vizibile pentru alți utilizatori în cursul transferului și pot fi modificate de un atacator pentru a schimba aspectul paginii.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Ai încercat să accesezi <ph name="DOMAIN" />, dar serverul a prezentat un certificat care conține o cheie slabă. Un atacator ar fi putut sparge cheia privată și este posibil ca serverul să nu fie cel care crezi (este posibil să comunici cu un atacator). <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Dosar nou</translation>
<translation id="5869405914158311789">Acest site nu poate fi accesat</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Acest tip de card nu este acceptat de Google Payments pentru acest comerciant. Selectează alt card.</translation>
<translation id="59174027418879706">Activat</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="5966707198760109579">Săptămână</translation>
<translation id="5967867314010545767">Eliminați din istoric</translation>
<translation id="5975083100439434680">Micșorează</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Încearcă:</translation>
<translation id="6151417162996330722">Certificatul de server are o perioadă de validitate prea lungă.</translation>
<translation id="6165508094623778733">Află mai multe</translation>
+<translation id="6177128806592000436">Conexiunea la acest site nu este sigură</translation>
<translation id="6203231073485539293">Verificați conexiunea la internet</translation>
<translation id="6218753634732582820">Elimini adresa din Chromium?</translation>
+<translation id="6251924700383757765">Politica de confidențialitate</translation>
+<translation id="625755898061068298">Ai ales să dezactivezi avertismentele de securitate pentru acest site.</translation>
<translation id="6259156558325130047">&amp;Repetați reordonarea</translation>
<translation id="6263376278284652872">Marcaje <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Înapoi la zona sigură</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">Adresa URL <ph name="URL" /> nu poate fi accesată.</translation>
<translation id="6321917430147971392">Verificați setările DNS</translation>
<translation id="6328639280570009161">Încercați să dezactivați anticiparea rețelei</translation>
+<translation id="6328786501058569169">Acest site este înșelător</translation>
<translation id="6337534724793800597">Filtrați politicile după nume</translation>
<translation id="6342069812937806050">Adineauri</translation>
<translation id="6345221851280129312">dimensiune necunoscută</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Momentan, nu poți accesa site-ul <ph name="SITE" />, deoarece acesta a trimis date de conectare într-un format necunoscut pe care Chromium nu le poate procesa. Erorile de rețea și atacurile sunt de obicei temporare și probabil că această pagină va funcționa mai târziu. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Nu se poate verifica dacă certificatul a fost revocat.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Valoare ignorată, deoarece politica a dezactivat căutarea prestabilită.</translation>
<translation id="6462969404041126431">Acest server nu a putut dovedi că este <ph name="DOMAIN" />. Este posibil ca certificatul său de securitate să fi fost revocat. Cauza poate fi o eroare de configurare sau interceptarea conexiunii de către un atacator. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Repetați ștergerea</translation>
<translation id="6534179046333460208">Sugestii pentru Webul material</translation>
<translation id="6550675742724504774">Opțiuni</translation>
+<translation id="6556239504065605927">Conexiune securizată</translation>
<translation id="6563469144985748109">Administratorul nu l-a aprobat încă</translation>
<translation id="6593753688552673085">mai puțin de <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Opțiuni de criptare</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Această politică este învechită.</translation>
<translation id="6652240803263749613">Acest server nu a putut dovedi că este <ph name="DOMAIN" />. Sistemul de operare al computerului nu consideră că certificatul său de securitate este de încredere. Cauza poate fi o eroare de configurare sau interceptarea conexiunii de către un atacator. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Modificați dosarul</translation>
-<translation id="6660210980321319655">Raportată automat <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Valoarea politicii</translation>
<translation id="6757797048963528358">Dispozitivul este inactiv.</translation>
<translation id="6778737459546443941">Părintele tău nu l-a aprobat încă</translation>
+<translation id="6810899417690483278">ID-ul de personalizare</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Pagina web de la <ph name="URL" /> nu este disponibilă momentan. Poate fi supraîncărcată sau dezactivată pentru lucrări de întreținere.</translation>
<translation id="6831043979455480757">Tradu</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Informații de contact</translation>
+<translation id="7079718277001814089">Acest site conține programe malware</translation>
<translation id="7087282848513945231">Comitat</translation>
<translation id="7088615885725309056">Mai vechi</translation>
<translation id="7090678807593890770">Caută <ph name="LINK" /> pe Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> nu respectă standardele de securitate.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Află mai multe<ph name="END_LINK" /> despre această problemă.</translation>
<translation id="7219179957768738017">Conexiunea utilizează <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Site-ul pe care urmează să îl accesezi conține programe malware</translation>
<translation id="724975217298816891">Introdu data de expirare și codul CVC pentru <ph name="CREDIT_CARD" />, pentru a actualiza detaliile cardului. După ce confirmi, acest site va avea acces la detaliile cardului tău.</translation>
<translation id="725866823122871198">O conexiune privată la <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> nu poate fi stabilită, deoarece data și ora computerului (<ph name="DATE_AND_TIME" />) sunt incorecte.</translation>
<translation id="7269802741830436641">Această pagină web are o buclă de redirecționare</translation>
@@ -611,6 +648,7 @@ Data viitoare ați putea utiliza combinația <ph name="SHORTCUT_KEY" /> pentru a
<translation id="7658239707568436148">Anulează</translation>
<translation id="7667346355482952095">Indicativul returnat pentru politică este gol sau nu corespunde cu indicativul actual</translation>
<translation id="7668654391829183341">Dispozitiv necunoscut</translation>
+<translation id="7669271284792375604">Atacatorii de pe acest site pot încerca să te înșele pentru a instala programe care dăunează experienței de navigare (de exemplu, schimbând pagina de pornire sau afișând anunțuri suplimentare pe site-urile pe care le accesezi).</translation>
<translation id="7674629440242451245">Doriți să utilizați funcții Chrome noi și interesante? Încercați canalul nostru pentru dezvoltatori, de la chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Accesați <ph name="SITE" /> (nesigur)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Acest site nu poate fi încărcat din memoria cache</translation>
@@ -626,14 +664,17 @@ Data viitoare ați putea utiliza combinația <ph name="SHORTCUT_KEY" /> pentru a
<translation id="7800304661137206267">Conexiunea este criptată utilizând <ph name="CIPHER" />, cu <ph name="MAC" /> pentru autentificarea mesajelor și <ph name="KX" /> ca mecanism de schimb al cheii.</translation>
<translation id="780301667611848630">Nu, mulțumesc</translation>
<translation id="7805768142964895445">Stare</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Elimini sugestia pentru formular din Chrome?</translation>
<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="7887683347370398519">Verifică codul CVC și încearcă din nou</translation>
<translation id="7912024687060120840">ÃŽn dosarul:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Certificatul serverului nu este încă valid.</translation>
<translation id="7942349550061667556">Roșu</translation>
+<translation id="7947285636476623132">Verifică anul în care expiră și încearcă din nou</translation>
<translation id="7951415247503192394">(32 de biți)</translation>
<translation id="7956713633345437162">Marcaje mobile</translation>
<translation id="7961015016161918242">Niciodată</translation>
@@ -641,7 +682,10 @@ Data viitoare ați putea utiliza combinația <ph name="SHORTCUT_KEY" /> pentru a
<translation id="7983301409776629893">Tradu întotdeauna din <ph name="ORIGINAL_LANGUAGE" /> în <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Nespecificată</translation>
<translation id="8012647001091218357">Momentan, nu ți-am putut contacta părinții. Încearcă din nou.</translation>
+<translation id="8025119109950072390">Atacatorii de pe acest site 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 carduri de credit).</translation>
+<translation id="803030522067524905">Recent, Navigarea sigură Google a detectat phishing pe <ph name="SITE" />. Site-urile de phishing falsifică alte site-uri, pentru a te înșela. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Această pagină este în <ph name="SOURCE_LANGUAGE" />. Doriți traducere în <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Trimiteți feedback</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>
@@ -652,9 +696,11 @@ Data viitoare ați putea utiliza combinația <ph name="SHORTCUT_KEY" /> pentru a
<translation id="8150722005171944719">Fișierul de la <ph name="URL" /> nu poate fi citit. Este posibil să fi fost eliminat ori mutat sau ca permisiunile pentru fișiere să împiedice accesarea acestuia.</translation>
<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="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="8230421197304563332">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, fotografiile, parolele, mesajele sau informațiile despre cardurile de credit).</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="8249320324621329438">Ultima preluare:</translation>
<translation id="8261506727792406068">Șterge</translation>
@@ -663,11 +709,13 @@ Data viitoare ați putea utiliza combinația <ph name="SHORTCUT_KEY" /> pentru a
<translation id="8294431847097064396">Sursă</translation>
<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="8349305172487531364">Bara de marcaje</translation>
<translation id="8363502534493474904">să dezactivezi modul Avion.</translation>
<translation id="8364627913115013041">Nesetată.</translation>
<translation id="8380941800586852976">Periculos</translation>
<translation id="8382348898565613901">Marcajele accesate recent apar aici</translation>
+<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="8428213095426709021">Setări</translation>
@@ -679,9 +727,9 @@ Data viitoare ați putea utiliza combinația <ph name="SHORTCUT_KEY" /> pentru a
<translation id="8498891568109133222"><ph name="HOST_NAME" /> a răspuns prea târziu.</translation>
<translation id="852346902619691059">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 eroare de configurare sau interceptarea conexiunii de către un atacator. <ph name="BEGIN_LEARN_MORE_LINK" />Află mai multe<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Acest tip de card nu este acceptat de Google Payments. Selectează alt card.</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="8553075262323480129">Traducerea nu a reușit, deoarece nu a putut fi stabilită limba paginii.</translation>
<translation id="8559762987265718583">O conexiune privată la <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> nu poate fi stabilită, deoarece data și ora dispozitivului (<ph name="DATE_AND_TIME" />) sunt incorecte.</translation>
-<translation id="856992080682148">Certificatul pentru acest site expiră în 2017 sau mai târziu, iar lanțul de certificate conține un certificat semnat cu SHA-1.</translation>
<translation id="8571890674111243710">Se traduce pagina în <ph name="LANGUAGE" />...</translation>
<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>
@@ -694,10 +742,12 @@ Data viitoare ați putea utiliza combinația <ph name="SHORTCUT_KEY" /> pentru a
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;Adresa DNS&lt;/abbr&gt; pentru <ph name="HOST_NAME" /> nu a putut fi găsită. Se diagnostichează problema.</translation>
<translation id="8790007591277257123">&amp;Repetați ștergerea</translation>
<translation id="8798099450830957504">Prestabilit</translation>
+<translation id="8800988563907321413">Sugestiile pentru În apropiere sunt afișate aici</translation>
<translation id="8804164990146287819">Politica de confidențialitate</translation>
<translation id="8820817407110198400">Marcaje</translation>
<translation id="8834246243508017242">Activați completarea automată folosind Agenda…</translation>
<translation id="883848425547221593">Alte marcaje</translation>
+<translation id="884264119367021077">Adresa de expediere</translation>
<translation id="884923133447025588">Nu a fost găsit niciun mecanism de revocare.</translation>
<translation id="885730110891505394">Permiterea accesului pentru Google</translation>
<translation id="8866481888320382733">Eroare la analizarea setărilor pentru politică</translation>
@@ -715,18 +765,21 @@ Data viitoare ați putea utiliza combinația <ph name="SHORTCUT_KEY" /> pentru a
<translation id="8971063699422889582">Certificatul serverului a expirat.</translation>
<translation id="8987927404178983737">Lună</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Site-ul pe care urmează să îl accesezi conține programe dăunătoare</translation>
<translation id="9001074447101275817">Pentru a accesa proxy-ul <ph name="DOMAIN" />, trebuie să introduci un nume de utilizator și o parolă.</translation>
<translation id="901974403500617787">Marcajele care se aplică la nivel de sistem pot fi create numai de proprietar: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Această pagină a fost tradusă în <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Eroare de securitate</translation>
<translation id="9038649477754266430">Folosește un serviciu de predicții pentru a încărca paginile mai rapid</translation>
<translation id="9039213469156557790">În plus, această pagină include alte resurse care nu sunt securizate. Aceste resurse sunt vizibile pentru alți utilizatori în cursul transferului și pot fi modificate de un atacator pentru a schimba comportamentul paginii.</translation>
+<translation id="9040185888511745258">Atacatorii de pe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> pot încerca să te înșele pentru a instala programe care dăunează experienței de navigare (de exemplu, schimbând pagina de pornire sau afișând anunțuri suplimentare pe site-urile pe care le accesezi).</translation>
<translation id="9050666287014529139">Expresie de acces</translation>
<translation id="9065203028668620118">Editează</translation>
<translation id="9068849894565669697">Selectați culoarea</translation>
<translation id="9076283476770535406">Poate include conținut destinat adulților</translation>
-<translation id="9092364396508701805">Pagina de pe <ph name="HOST_NAME" /> nu funcționează</translation>
<translation id="9103872766612412690">Site-ul <ph name="SITE" /> folosește în mod obișnuit criptarea pentru a-ți proteja informațiile. Când Chromium a încercat să se conecteze la <ph name="SITE" /> de această dată, site-ul a returnat date de conectare neobișnuite și incorecte. Acest lucru s-a întâmplat fie pentru că un atacator încearcă să falsifice site-ul <ph name="SITE" />, fie pentru că un ecran de conectare Wi-Fi a întrerupt conexiunea. Securitatea informațiilor tale nu a fost afectată, deoarece Chromium a oprit conexiunea înainte ca datele să fie transferate.</translation>
<translation id="9137013805542155359">Afișează originalul</translation>
+<translation id="9137248913990643158">Pornește și conectează-te la Chrome înainte de a folosi această aplicație.</translation>
<translation id="9148507642005240123">&amp;Anulați editarea</translation>
<translation id="9157595877708044936">Se configurează...</translation>
<translation id="9170848237812810038">&amp;Anulează</translation>
@@ -739,7 +792,6 @@ Data viitoare ați putea utiliza combinația <ph name="SHORTCUT_KEY" /> pentru a
<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="962701380617707048">Introdu data de expirare și codul CVC pentru <ph name="CREDIT_CARD" />, pentru a actualiza detaliile cardului</translation>
<translation id="969892804517981540">Versiune oficială</translation>
<translation id="988159990683914416">Versiune de programare</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 bc0b3e6d00e..2fa230302c2 100644
--- a/chromium/components/strings/components_strings_ru.xtb
+++ b/chromium/components/strings/components_strings_ru.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">ПодключитеÑÑŒ к Ñети Wi-Fi ещё раз.</translation>
<translation id="1175364870820465910">&amp;Печать...</translation>
<translation id="1181037720776840403">Удалить</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />ÐвтоматичеÑки отправлÑÑ‚ÑŒ в Google<ph name="END_WHITEPAPER_LINK" /> информацию о возможных проблемах безопаÑноÑти. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Далее</translation>
<translation id="1201895884277373915">Другие запиÑи по Ñтому Ñайту</translation>
<translation id="1206967143813997005">ÐÐ°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ð¿Ð¾Ð´Ð¿Ð¸ÑÑŒ недейÑтвительна</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Голубой</translation>
<translation id="1629803312968146339">Сохранить Ñту карту в Chrome?</translation>
<translation id="1640180200866533862">ПользовательÑкие правила</translation>
+<translation id="1640244768702815859">Попробуйте <ph name="BEGIN_LINK" />открыть главную Ñтраницу Ñайта<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Импорт невозможен: недопуÑÑ‚Ð¸Ð¼Ð°Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ñети.</translation>
<translation id="1644574205037202324">ИÑториÑ</translation>
<translation id="1645368109819982629">Ðеподдерживаемый протокол</translation>
<translation id="1676269943528358898">Ðа Ñайте <ph name="SITE" /> Ð´Ð»Ñ Ð·Ð°Ñ‰Ð¸Ñ‚Ñ‹ ваших данных обычно иÑпользуетÑÑ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ðµ. Однако учетные данные, которые мы получили от Ñайта <ph name="SITE" /> ÑейчаÑ, отличаютÑÑ Ð¾Ñ‚ тех, которые он отправлÑет обычно. ВероÑтно, вредоноÑный Ñайт пытаетÑÑ Ð²Ñ‹Ð´Ð°Ñ‚ÑŒ ÑÐµÐ±Ñ Ð·Ð° <ph name="SITE" />, либо Ñтраница Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº Ñети Wi-Fi прервала Ñоединение. Ваша Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾-прежнему в безопаÑноÑти, так как браузер Google Chrome разорвал Ñоединение до того, как произошел обмен данными.</translation>
+<translation id="168328519870909584">Через Ñайт <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> на ваш компьютер могут уÑтановить вредоноÑное ПО Ð´Ð»Ñ ÐºÑ€Ð°Ð¶Ð¸ или ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð»Ð¸Ñ‡Ð½Ð¾Ð¹ информации (например, фотографий, паролей, Ñообщений и реквизитов банковÑких карт).</translation>
<translation id="168841957122794586">Сертификат Ñервера Ñодержит ненадежный криптографичеÑкий ключ.</translation>
-<translation id="1701955595840307032">Получайте рекомендации</translation>
<translation id="1710259589646384581">ОС</translation>
<translation id="1721312023322545264">Ð”Ð»Ñ Ð´Ð¾Ñтупа к Ñтой Ñтранице требуетÑÑ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ðµ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ <ph name="NAME" /></translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Ðомер Ñтраницы</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Выполните диагноÑтику Ñети в Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Обновите кодовую фразу Ð´Ð»Ñ Ñинхронизации.</translation>
+<translation id="1787142507584202372">ЗдеÑÑŒ поÑвÑÑ‚ÑÑ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ðµ вкладки.</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">СиÑтема Google по проверке безопаÑноÑти недавно обнаружила на Ñайте <ph name="SITE" /> <ph name="BEGIN_LINK" />вредоноÑное ПО<ph name="END_LINK" />. Его иÑточник, <ph name="SUBRESOURCE_HOST" />, не раз замечен в раÑпроÑтранении вируÑов. Будьте внимательны: иногда даже на надежных Ñайтах поÑвлÑÑŽÑ‚ÑÑ Ð²Ð¸Ñ€ÑƒÑÑ‹. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">ÐедопуÑтимый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸Ð»Ð¸ неверные параметры запроÑа</translation>
+<translation id="1834321415901700177">Сайт Ñодержит вредоноÑное ПО</translation>
<translation id="1838667051080421715">Ð’Ñ‹ проÑматриваете код Ñтраницы.</translation>
<translation id="1871208020102129563">Выбрано иÑпользование фикÑированных прокÑи-Ñерверов, а не URL PAC-Ñкрипта.</translation>
<translation id="1883255238294161206">Свернуть ÑпиÑок</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Закладки на мобильном</translation>
<translation id="2148716181193084225">СегоднÑ</translation>
-<translation id="2149973817440762519">Закладка</translation>
<translation id="2154054054215849342">Ð’ вашем домене ÑÐ¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½ÐµÐ´Ð¾Ñтупна</translation>
<translation id="2166049586286450108">ДоÑтуп админиÑтратора ко вÑем данным</translation>
<translation id="2166378884831602661">Этот Ñайт не может обеÑпечить безопаÑное Ñоединение</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Соединение Ñ Ð²ÐµÐ±-Ñайтом <ph name="SITE" />, иÑпользующим механизм HSTS, могло быть Ñкомпрометировано, поÑтому его Ð½ÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚ÑŒ в наÑтоÑщий момент. Проблема также может быть ÑвÑзана Ñ Ð½Ð°Ñтройками Ñервера или дейÑтвиÑми злоумышленников. Скорее вÑего, Ñайт заработает через некоторое времÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">Пропущена недопуÑÑ‚Ð¸Ð¼Ð°Ñ Ð·Ð°ÐºÐ»Ð°Ð´ÐºÐ°, Ð¸Ð½Ð´ÐµÐºÑ <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Другие закладки</translation>
+<translation id="2355395290879513365">Злоумышленники могут видеть изображениÑ, которые видны вам, и изменÑÑ‚ÑŒ их в целÑÑ… мошенничеÑтва.</translation>
<translation id="2359808026110333948">Далее</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" />: получен отчет о Ñбое (не загружен)</translation>
<translation id="2367567093518048410">Уровень</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Показывать правила, Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… не заданы</translation>
<translation id="2396249848217231973">&amp;Отменить удаление</translation>
<translation id="2455981314101692989">Ðа Ñтой веб-Ñтранице отключено автоматичеÑкое заполнение формы.</translation>
+<translation id="2460160116472764928">СиÑтема Google по проверке безопаÑноÑти недавно обнаружила на Ñайте <ph name="SITE" /> <ph name="BEGIN_LINK" />вредоноÑное ПО<ph name="END_LINK" />. Будьте внимательны: иногда даже на надежных Ñайтах поÑвлÑÑŽÑ‚ÑÑ Ð²Ð¸Ñ€ÑƒÑÑ‹. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">Заполнить</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Выполните диагноÑтику Ñети<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">ÐедейÑтвительный URL поиÑковой ÑиÑтемы.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Удалить Ñти Ñтраницы из иÑтории поÑещений?</translation>
<translation id="2677748264148917807">Закрыть</translation>
<translation id="269990154133806163">ПредоÑтавленный Ñервером Ñертификат не проходил проверку безопаÑноÑти. Ð’ некоторых ÑлучаÑÑ… она необходима, чтобы обеÑпечить надежноÑÑ‚ÑŒ Ñертификата и защиту от злоумышленников. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">СпиÑок Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ</translation>
<translation id="2704283930420550640">Значение не ÑоответÑтвует формату.</translation>
<translation id="2704951214193499422">Ðе удалоÑÑŒ подтвердить данные карты. Повторите попытку позже.</translation>
<translation id="2705137772291741111">Ðевозможно прочитать копию Ñайта, Ñохраненную в кеше.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Ð’Ñ‹ пытаетеÑÑŒ обратитьÑÑ Ðº Ñерверу в домене <ph name="DOMAIN" />, но его Ñертификат был отозван издателем. Это означает, что доверÑÑ‚ÑŒ информации Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ реÑурÑа нельзÑ. Возможно, вы имеете дело Ñо злоумышленниками. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">ПопроÑить разрешениÑ</translation>
<translation id="2713444072780614174">Белый</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">ПрокÑи-Ñерверы, иÑпользуемые Ð´Ð»Ñ ÑоединениÑ, можно отключить на Ñтранице наÑтроек.</translation>
<translation id="2955913368246107853">Закрыть панель поиÑка</translation>
<translation id="2958431318199492670">Ðекоторые Ñлементы Ñетевой конфигурации невозможно импортировать, поÑкольку она не ÑоответÑтвует Ñтандарту ONC.</translation>
+<translation id="29611076221683977">Сайт <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> может уÑтановить на ваш компьютер Mac вредоноÑное ПО, которое крадет или удалÑет личную информацию (например, фотографии, пароли, ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ реквизиты банковÑких карт).</translation>
<translation id="2969319727213777354">Ð”Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°Ñного Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾, чтобы Ð¿Ð¾ÐºÐ°Ð·Ð°Ð½Ð¸Ñ ÑиÑтемных чаÑов были верны. Причина в том, что Ñертификаты Ð´Ð»Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸ Ñайтов имеют ограниченный Ñрок дейÑтвиÑ. ЕÑли чаÑÑ‹ на уÑтройÑтве неточны, Chrome не может проверить актуальноÑÑ‚ÑŒ Ñтих Ñертификатов.</translation>
<translation id="2972581237482394796">&amp;Повторить</translation>
<translation id="2985306909656435243">ЕÑли Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°, Chromium будет хранить на Ñтом уÑтройÑтве данные карты Ð´Ð»Ñ Ð±Ñ‹Ñтрого Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ„Ð¾Ñ€Ð¼.</translation>
@@ -258,7 +267,6 @@
<translation id="3586931643579894722">Скрыть подробноÑти</translation>
<translation id="3587482841069643663">Ð’Ñе</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Сервер не поддерживает возобновление Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ TLS.</translation>
<translation id="36224234498066874">ОчиÑтить иÑторию...</translation>
<translation id="362276910939193118">Показать вÑÑŽ иÑторию</translation>
<translation id="3623476034248543066">Показать значение</translation>
@@ -270,6 +278,7 @@
<translation id="3655670868607891010">ЕÑли Ñта проблема возникает чаÑто, изучите <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">ВерÑиÑ</translation>
<translation id="3678029195006412963">Ðе удалоÑÑŒ подпиÑать запроÑ</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>
@@ -279,10 +288,10 @@
<translation id="3712624925041724820">ÐедоÑтаточно лицензий</translation>
<translation id="3714780639079136834">Включите Wi-Fi или передачу данных по мобильной Ñети.</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Проверьте наÑтройки прокÑи-Ñервера, брандмауÑра и DNS<ph name="END_LINK" />.</translation>
+<translation id="3736520371357197498">ЕÑли вы готовы подвергнуть риÑку ваши личные данные, вы можете <ph name="BEGIN_LINK" />перейти на зараженный Ñайт<ph name="END_LINK" />, не дожидаÑÑÑŒ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð²Ñ€ÐµÐ´Ð¾Ð½Ð¾Ñного ПО.</translation>
<translation id="3739623965217189342">Ð¡ÐºÐ¾Ð¿Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ ÑÑылка</translation>
<translation id="375403751935624634">Сбой при переводе вÑледÑтвие ошибки Ñервера.</translation>
<translation id="3759461132968374835">Ðет запиÑей о недавних ÑбоÑÑ…. Сбои, которые произошли при отключенной функции запиÑи Ñбоев, здеÑÑŒ не отображаютÑÑ.</translation>
-<translation id="3788090790273268753">Срок дейÑÑ‚Ð²Ð¸Ñ Ñертификата Ð´Ð»Ñ Ñтого Ñайта иÑтекает в 2016 году. Кроме того, в цепочке приÑутÑтвует Ñертификат Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñью SHA-1.</translation>
<translation id="382518646247711829">ЕÑли вы иÑпользуете прокÑи-Ñервер...</translation>
<translation id="3828924085048779000">ПуÑтые кодовые фразы запрещены.</translation>
<translation id="3845539888601087042">Показана иÑÑ‚Ð¾Ñ€Ð¸Ñ Ñо вÑех уÑтройÑтв, на которых иÑпользуетÑÑ Ñтот аккаунт. <ph name="BEGIN_LINK" />Подробнее…<ph name="END_LINK" /></translation>
@@ -304,6 +313,7 @@
<translation id="4058922952496707368">Ключ "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Клиент и Ñервер поддерживают разные верÑии протокола SSL или набора шифров.</translation>
<translation id="4079302484614802869">ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¾ÐºÑи-Ñервера предуÑматривает иÑпользование URL PAC-Ñкриптов вмеÑто фикÑированных прокÑи-Ñерверов.</translation>
+<translation id="4098354747657067197">ОÑторожно, поддельный Ñайт!</translation>
<translation id="4103249731201008433">Серийный номер уÑтройÑтва недейÑтвителен</translation>
<translation id="4103763322291513355">Чтобы проÑмотреть URL, внеÑенные в черный ÑпиÑок, и другие правила, заданные ÑиÑтемным админиÑтратором, перейдите по адреÑу: &lt;strong&gt;chrome://policy&lt;/strong&gt;.</translation>
<translation id="4110615724604346410">Сервер не может подтвердить ÑвÑзь Ñ Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð¼Â <ph name="DOMAIN" />. Его Ñертификат безопаÑноÑти Ñодержит ошибки. Возможно, проблема ÑвÑзана Ñ Ð½Ð°Ñтройками Ñервера или дейÑтвиÑми злоумышленников, которые пытаютÑÑ Ð¿ÐµÑ€ÐµÑ…Ð²Ð°Ñ‚Ð¸Ñ‚ÑŒ Ñоединение. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -321,15 +331,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{нет}=1{1 приложение ($1)}=2{2Â Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ ($1 и $2)}one{# приложение ($1, $2, $3)}few{#Â Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ ($1, $2, $3)}many{# приложений ($1, $2, $3)}other{#Â Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Завершение работы программы</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Выполните диагноÑтику Ñети<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Подключение к Ñайту защищено не полноÑтью</translation>
<translation id="4250680216510889253">Ðет</translation>
<translation id="425582637250725228">Возможно, внеÑенные Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ðµ ÑохранÑÑ‚ÑÑ.</translation>
<translation id="4258748452823770588">ПодпиÑÑŒ недейÑтвительна</translation>
<translation id="4269787794583293679">(Ðе указано)</translation>
+<translation id="4280429058323657511">, дейÑтвует до <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">СиÑтема Google по проверке безопаÑноÑти недавно обнаружила на Ñайте <ph name="SITE" /> <ph name="BEGIN_LINK" />вредоноÑное ПО<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">ПодÑказки Ð´Ð»Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑких Ñлементов</translation>
<translation id="4304224509867189079">Вход</translation>
<translation id="432290197980158659">Сервер предоÑтавил Ñертификат, который не ÑоответÑтвует требованиÑм браузера. Эти Ñ‚Ñ€ÐµÐ±Ð¾Ð²Ð°Ð½Ð¸Ñ Ð²Ñ‹Ð´Ð²Ð¸Ð³Ð°ÑŽÑ‚ÑÑ Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, чтобы обезопаÑить Ð²Ð°Ñ Ð¾Ñ‚ иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½ÐµÐ½Ð°Ð´ÐµÐ¶Ð½Ñ‹Ñ… Ñайтов. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">Ðе удалоÑÑŒ найти Ñтатью</translation>
+<translation id="4326324639298822553">Проверьте Ñрок дейÑÑ‚Ð²Ð¸Ñ Ð¸ повторите попытку</translation>
<translation id="4331708818696583467">Ðенадежный</translation>
+<translation id="4356973930735388585">Злоумышленники могут иÑпользовать Ñтот Ñайт, чтобы уÑтановить на ваш компьютер вредоноÑное ПО, которое крадет или удалÑет личную информацию (например, фотографии, пароли, ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ реквизиты банковÑких карт).</translation>
<translation id="4372948949327679948">Ожидаемое значение: <ph name="VALUE_TYPE" />.</translation>
<translation id="4381091992796011497">Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ:</translation>
<translation id="4394049700291259645">Отключить</translation>
@@ -347,6 +362,7 @@
<translation id="4589078953350245614">Ð’Ñ‹ пытаетеÑÑŒ обратитьÑÑ Ñерверу в домене <ph name="DOMAIN" />, но он предоÑтавил недейÑтвительный Ñертификат. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4592951414987517459">Соединение Ñ <ph name="DOMAIN" /> зашифровано Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñовременных наборов шифров.</translation>
<translation id="4594403342090139922">&amp;Отменить удаление</translation>
+<translation id="4619615317237390068">Вкладки Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… уÑтройÑтв</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Ð’Ñ‹ проÑматриваете Ñтраницу раÑширениÑ</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -355,10 +371,13 @@
<translation id="4726672564094551039">Повторно загрузить политики</translation>
<translation id="4728558894243024398">Платформа</translation>
<translation id="4744603770635761495">Путь к иÑполнÑемому файлу</translation>
+<translation id="4750917950439032686">ИнформациÑ, которую вы Ñообщаете Ñтому Ñайту (например, пароли и номера банковÑких карт), защищена.</translation>
<translation id="4756388243121344051">&amp;ИÑториÑ</translation>
+<translation id="4759118997339041434">Ðвтозаполнение данных Ð´Ð»Ñ Ð¾Ð¿Ð»Ð°Ñ‚Ñ‹ отключено</translation>
<translation id="4764776831041365478">Веб-Ñтраница по адреÑу <ph name="URL" />, возможно, временно недоÑтупна или поÑтоÑнно перемещена по новому адреÑу.</translation>
<translation id="4771973620359291008">Произошла неизвеÑÑ‚Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°.</translation>
<translation id="4800132727771399293">Проверьте Ñрок дейÑÑ‚Ð²Ð¸Ñ Ð¸ CVC-код, а затем повторите попытку</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Ошибка Ñети</translation>
<translation id="4816492930507672669">По размеру Ñтраницы</translation>
<translation id="4850886885716139402">ПоÑмотреть</translation>
@@ -367,6 +386,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> и <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{и ещё 1 веб-Ñтраница}one{и ещё # веб-Ñтраница}few{и ещё # веб-Ñтраницы}many{и ещё # веб-Ñтраниц}other{и ещё # веб-Ñтраницы}}</translation>
<translation id="4923417429809017348">Эта Ñтраница была автоматичеÑки переведена Ñ Ð½ÐµÐ¸Ð·Ð²ÐµÑтного Ñзыка на <ph name="LANGUAGE_LANGUAGE" /></translation>
+<translation id="4923459931733593730">Платеж</translation>
<translation id="4926049483395192435">Укажите значение.</translation>
<translation id="495170559598752135">ДейÑтвиÑ</translation>
<translation id="4958444002117714549">Развернуть ÑпиÑок</translation>
@@ -394,7 +414,6 @@
<translation id="5181140330217080051">СкачиваетÑÑ</translation>
<translation id="5190835502935405962">Панель закладок</translation>
<translation id="5199729219167945352">ЭкÑпериментальные функции</translation>
-<translation id="5199841536747119669">ЗдеÑÑŒ поÑвÑÑ‚ÑÑ Ñ€ÐµÐºÐ¾Ð¼ÐµÐ½Ð´Ð°Ñ†Ð¸Ð¸.</translation>
<translation id="5251803541071282808">Облако</translation>
<translation id="5277279256032773186">ИÑпользуете Chrome на работе? Узнайте, как компании могут управлÑÑ‚ÑŒ наÑтройками Chrome на корпоративных уÑтройÑтвах.</translation>
<translation id="5299298092464848405">Ðе удалоÑÑŒ выполнить анализ политики</translation>
@@ -402,8 +421,10 @@
<translation id="5308689395849655368">Отчеты о ÑбоÑÑ… отключены.</translation>
<translation id="5317780077021120954">Сохранить</translation>
<translation id="5327248766486351172">Ðазвание</translation>
+<translation id="5337705430875057403">ПоÑещение Ñайта <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> может привеÑти к уÑтановке вредоноÑного ПО или хищению вашей личной информации (например, паролей, телефонных номеров и данных кредитных карт).</translation>
<translation id="5359637492792381994">Сервер не может подтвердить ÑвÑзь Ñ Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð¼Â <ph name="DOMAIN" />, его Ñертификат в наÑтоÑщий момент недейÑтвителен. Возможно, проблема ÑвÑзана Ñ Ð½Ð°Ñтройками Ñервера или дейÑтвиÑми злоумышленников, которые пытаютÑÑ Ð¿ÐµÑ€ÐµÑ…Ð²Ð°Ñ‚Ð¸Ñ‚ÑŒ Ñоединение. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">Ðе удалоÑÑŒ Ñохранить наÑтройки политики</translation>
+<translation id="5386426401304769735">Ð’ цепочке Ñертификатов Ñтого Ñайта еÑÑ‚ÑŒ Ñертификат, подпиÑанный Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ алгоритма SHA-1.</translation>
<translation id="5421136146218899937">ОчиÑтить иÑторию...</translation>
<translation id="5430298929874300616">Удалить закладку</translation>
<translation id="5431657950005405462">Файл не найден</translation>
@@ -424,17 +445,20 @@
<translation id="5544037170328430102">Подтвердите дейÑтвие на <ph name="SITE" />:</translation>
<translation id="5556459405103347317">Перезагрузить</translation>
<translation id="5565735124758917034">Ðктивен</translation>
+<translation id="5572851009514199876">Выполните вход, чтобы Chrome определил, разрешен ли вам доÑтуп к Ñтому Ñайту.</translation>
+<translation id="5580958916614886209">Проверьте меÑÑц в Ñроке дейÑÑ‚Ð²Ð¸Ñ ÐºÐ°Ñ€Ñ‚Ñ‹ и повторите попытку</translation>
<translation id="560412284261940334">Управление уÑтройÑтвами не поддерживаетÑÑ</translation>
<translation id="5610142619324316209">Проверьте подключение к Интернету.</translation>
<translation id="5617949217645503996">Сайт <ph name="HOST_NAME" /> выполнил переадреÑацию Ñлишком много раз.</translation>
<translation id="5622887735448669177">Покинуть Ñту Ñтраницу?</translation>
<translation id="5629630648637658800">Ðе удалоÑÑŒ применить наÑтройки политики</translation>
<translation id="5631439013527180824">Токен уÑтройÑтва недейÑтвителен</translation>
+<translation id="5669703222995421982">Получение перÑонализированного контента</translation>
+<translation id="5675650730144413517">Страница недоÑтупна</translation>
<translation id="5677928146339483299">Заблокировано</translation>
<translation id="5694783966845939798">Ð’Ñ‹ пытаетеÑÑŒ обратитьÑÑ Ðº Ñерверу в домене <ph name="DOMAIN" />, но его Ñертификат подпиÑан Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ ненадежного алгоритма (например, SHA-1). Это означает, что учетные данные безопаÑноÑти и Ñам Ñервер могут оказатьÑÑ Ð¿Ð¾Ð´Ð´ÐµÐ»ÑŒÐ½Ñ‹Ð¼Ð¸. Возможно, вы имеете дело Ñо злоумышленниками. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">Идентификационные данные Ñтого Ñайта не проверены.</translation>
<translation id="5720705177508910913">Текущий пользователь</translation>
-<translation id="572328651809341494">Ðедавние вкладки</translation>
<translation id="5732392974455271431">Ð”Ð»Ñ Ñ€Ð°Ð·Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ¸ обратитеÑÑŒ к родителÑм.</translation>
<translation id="5784606427469807560">Ðе удалоÑÑŒ подтвердить данные карты. Проверьте подключение к Интернету и повторите попытку.</translation>
<translation id="5785756445106461925">Обратите внимание, что на Ñтранице обнаружен небезопаÑный контент. Возможно, при передаче реÑурÑÑ‹ проÑматриваютÑÑ Ñ‚Ñ€ÐµÑ‚ÑŒÐ¸Ð¼Ð¸ лицами, а злоумышленники могут получить доÑтуп к Ñтранице и изменить ее поведение или внешний вид.</translation>
@@ -443,6 +467,7 @@
<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="5843436854350372569">Ð’Ñ‹ пытаетеÑÑŒ обратитьÑÑ Ðº Ñерверу в домене <ph name="DOMAIN" />, но он предоÑтавил Ñертификат Ñ Ð½ÐµÐ½Ð°Ð´ÐµÐ¶Ð½Ñ‹Ð¼ ключом. Возможно, вы имеете дело Ñо злоумышленником, который взломал закрытый ключ. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">ÐÐ¾Ð²Ð°Ñ Ð¿Ð°Ð¿ÐºÐ°</translation>
<translation id="5869405914158311789">Ðе удаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ доÑтуп к Ñайту</translation>
@@ -452,6 +477,7 @@
<translation id="59107663811261420">Ð¡ÐµÑ€Ð²Ð¸Ñ Google Payments не поддерживает карты Ñтого типа Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ продавца. Выберите другую карту.</translation>
<translation id="59174027418879706">Включено</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="5966707198760109579">ÐеделÑ</translation>
<translation id="5967867314010545767">Удалить из иÑтории</translation>
<translation id="5975083100439434680">Уменьшить</translation>
@@ -470,8 +496,11 @@
<translation id="614940544461990577">Попробуйте Ñделать Ñледующее:</translation>
<translation id="6151417162996330722">Слишком долгий Ñрок дейÑÑ‚Ð²Ð¸Ñ Ñертификата, предоÑтавленного Ñервером.</translation>
<translation id="6165508094623778733">Подробнее...</translation>
+<translation id="6177128806592000436">Подключение к Ñайту не защищено</translation>
<translation id="6203231073485539293">Проверьте подключение к Интернету</translation>
<translation id="6218753634732582820">Удалить Ð°Ð´Ñ€ÐµÑ Ð¸Ð· Chromium?</translation>
+<translation id="6251924700383757765">Политика конфиденциальноÑти</translation>
+<translation id="625755898061068298">Ð’Ñ‹ отключили Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñ ÑиÑтемы безопаÑноÑти Ð´Ð»Ñ Ñтого Ñайта.</translation>
<translation id="6259156558325130047">&amp;Повторить изменение порÑдка</translation>
<translation id="6263376278284652872">Закладки <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Ðазад к безопаÑноÑти</translation>
@@ -480,6 +509,7 @@
<translation id="6305205051461490394">Сайт <ph name="URL" /> недоÑтупен.</translation>
<translation id="6321917430147971392">Проверьте наÑтройки DNS</translation>
<translation id="6328639280570009161">Отключите предÑказание Ñетевых дейÑтвий</translation>
+<translation id="6328786501058569169">Это поддельный Ñайт</translation>
<translation id="6337534724793800597">Фильтровать политики по названию</translation>
<translation id="6342069812937806050">только что</translation>
<translation id="6345221851280129312">размер неизвеÑтен</translation>
@@ -488,6 +518,8 @@
<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="6404511346730675251">Изменить закладку</translation>
+<translation id="6410264514553301377">Введите Ñрок дейÑÑ‚Ð²Ð¸Ñ Ð¸ CVC-код карты <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° проÑмотр Ñайта отправлен вашему родителю</translation>
<translation id="6416403317709441254">Веб-Ñайт <ph name="SITE" /> отправлÑет Chromium некорректные идентификационные данные, поÑтому открыть его в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½ÐµÐ»ÑŒÐ·Ñ. Сбой мог быть вызван Ñетевой ошибкой или дейÑтвиÑми злоумышленников. Возможно, Ñайт заработает через некоторое времÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">Ðе удаетÑÑ Ð¿Ñ€Ð¾Ð²ÐµÑ€Ð¸Ñ‚ÑŒ, был ли отозван Ñертификат.</translation>
@@ -497,10 +529,12 @@
<translation id="6458467102616083041">ИгнорируетÑÑ, так как поиÑк по умолчанию запрещен правилами.</translation>
<translation id="6462969404041126431">Сервер не может подтвердить ÑвÑзь Ñ Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð¼Â <ph name="DOMAIN" />. ВероÑтно, его Ñертификат был отозван. Проблема также может быть ÑвÑзана Ñ Ð½Ð°Ñтройками Ñервера или дейÑтвиÑми злоумышленников, которые пытаютÑÑ Ð¿ÐµÑ€ÐµÑ…Ð²Ð°Ñ‚Ð¸Ñ‚ÑŒ Ñоединение. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="647261751007945333">Правила уÑтройÑтва</translation>
+<translation id="6477321094435799029">Браузер Chrome обнаружил на Ñтой Ñтранице необычный код и заблокировал его, чтобы защитить ваши данные (например, пароли, а также номера телефонов и банковÑких карт).</translation>
<translation id="6489534406876378309">Ðачать загрузку Ñведений об ошибках</translation>
<translation id="6529602333819889595">&amp;Повторить удаление</translation>
<translation id="6534179046333460208">Интернет вокруг наÑ: рекомендации</translation>
<translation id="6550675742724504774">Параметры</translation>
+<translation id="6556239504065605927">Защищенное Ñоединение</translation>
<translation id="6563469144985748109">Ещё не одобрено админиÑтратором</translation>
<translation id="6593753688552673085">менее <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Параметры шифрованиÑ</translation>
@@ -509,7 +543,6 @@
<translation id="6644283850729428850">Правило уÑтарело.</translation>
<translation id="6652240803263749613">Сервер не может подтвердить ÑвÑзь Ñ Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð¼Â <ph name="DOMAIN" />. Его Ñертификат безопаÑноÑти не принимаетÑÑ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ ÑиÑтемой вашего компьютера. Возможно, проблема ÑвÑзана Ñ Ð½Ð°Ñтройками Ñервера или дейÑтвиÑми злоумышленников, которые пытаютÑÑ Ð¿ÐµÑ€ÐµÑ…Ð²Ð°Ñ‚Ð¸Ñ‚ÑŒ Ñоединение. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">Редактировать папку</translation>
-<translation id="6660210980321319655">Отчет о Ñбое отправлен автоматичеÑки. Ð’Ñ€ÐµÐ¼Ñ Ð²Ð¾Ð·Ð½Ð¸ÐºÐ½Ð¾Ð²ÐµÐ½Ð¸Ñ ÑбоÑ: <ph name="CRASH_TIME" />.</translation>
<translation id="6671697161687535275">Удалить подÑказку из Chromium?</translation>
<translation id="6685834062052613830">Выйдите из аккаунта и завершите наÑтройку</translation>
<translation id="6710213216561001401">Ðазад</translation>
@@ -521,6 +554,7 @@
<translation id="6753269504797312559">Значение правила</translation>
<translation id="6757797048963528358">УÑтройÑтво находитÑÑ Ð² ÑпÑщем режиме.</translation>
<translation id="6778737459546443941">Ещё не одобрено родителем</translation>
+<translation id="6810899417690483278">Идентификатор перÑонализации</translation>
<translation id="6820686453637990663">Код CVC</translation>
<translation id="6830600606572693159">Веб-Ñтраница по адреÑу <ph name="URL" /> в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð½ÐµÐ´Ð¾Ñтупна. Возможно, она перегружена запроÑами или отключена Ð´Ð»Ñ Ð¾Ð±ÑлуживаниÑ.</translation>
<translation id="6831043979455480757">ПеревеÑти</translation>
@@ -531,7 +565,7 @@
<translation id="6897140037006041989">User Agent</translation>
<translation id="6915804003454593391">Пользователь:</translation>
<translation id="6957887021205513506">Возможно, Ñертификат Ñервера фальÑифицирован.</translation>
-<translation id="6965382102122355670">ОК</translation>
+<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">УÑтройÑтво</translation>
<translation id="6970216967273061347">Район</translation>
<translation id="6973656660372572881">Указаны как фикÑированные прокÑи-Ñерверы, так и URL PAC-Ñкриптов.</translation>
@@ -541,6 +575,8 @@
<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>
+<translation id="7064851114919012435">Контактные данные</translation>
+<translation id="7079718277001814089">Этот Ñайт Ñодержит вредоноÑное ПО</translation>
<translation id="7087282848513945231">ГрафÑтво</translation>
<translation id="7088615885725309056">Раньше</translation>
<translation id="7090678807593890770">Выполните поиÑк по запроÑу <ph name="LINK" /> в Google</translation>
@@ -555,6 +591,7 @@
<translation id="7210863904660874423">Сайт <ph name="HOST_NAME" /> не ÑоответÑтвует Ñтандартам безопаÑноÑти.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Подробнее <ph name="END_LINK" /> об Ñтой неполадке.</translation>
<translation id="7219179957768738017">Ð’ Ñтом подключении иÑпользуетÑÑ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð» <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">ОÑторожно, вредоноÑное ПО!</translation>
<translation id="724975217298816891">Введите Ñрок дейÑÑ‚Ð²Ð¸Ñ Ð¸ CVC-код карты <ph name="CREDIT_CARD" />. ПоÑле Ñтого ее данные будут переданы Ñайту.</translation>
<translation id="725866823122871198">Ðе удалоÑÑŒ уÑтановить защищенное Ñоединение Ñ Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð¼ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> из-за неверных наÑтроек ÑиÑтемных чаÑов и ÐºÐ°Ð»ÐµÐ½Ð´Ð°Ñ€Ñ (<ph name="DATE_AND_TIME" />).</translation>
<translation id="7269802741830436641">Ðа Ñтой Ñтранице обнаружена цикличеÑÐºÐ°Ñ Ð¿ÐµÑ€ÐµÐ°Ð´Ñ€ÐµÑациÑ</translation>
@@ -610,6 +647,7 @@
<translation id="7658239707568436148">Отмена</translation>
<translation id="7667346355482952095">Возвращенный токен пуÑÑ‚ или не ÑоответÑтвует имеющемуÑÑ</translation>
<translation id="7668654391829183341">ÐеизвеÑтное уÑтройÑтво</translation>
+<translation id="7669271284792375604">ПоÑещение Ñтого Ñайта может привеÑти к уÑтановке вредоноÑного ПО, которое будет мешать работе браузера (например, менÑÑ Ñтартовую Ñтраницу или Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½ÑƒÑŽ рекламу на Ñайтах).</translation>
<translation id="7674629440242451245">Хотите быть в курÑе новинок Chrome? Выберите канал Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ€Ð°Ð·Ñ€Ð°Ð±Ð¾Ñ‚Ñ‡Ð¸ÐºÐ¾Ð² на Ñтранице "chrome.com/dev".</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Перейти на Ñайт <ph name="SITE" /> (небезопаÑно)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Ðе удалоÑÑŒ загрузить Ñайт из кеша</translation>
@@ -625,14 +663,17 @@
<translation id="7800304661137206267">Соединение зашифровано Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ <ph name="CIPHER" />, Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸ Ñообщений иÑпользуетÑÑ <ph name="MAC" />, Ð´Ð»Ñ Ð¾Ð±Ð¼ÐµÐ½Ð° ключами иÑпользуетÑÑ <ph name="KX" />.</translation>
<translation id="780301667611848630">СпаÑибо, не надо</translation>
<translation id="7805768142964895445">СоÑтоÑние</translation>
+<translation id="7812922009395017822">Мир</translation>
<translation id="7813600968533626083">Удалить подÑказку из Chrome?</translation>
<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="7887683347370398519">Проверьте CVC-код и повторите попытку</translation>
<translation id="7912024687060120840">В папке:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Сертификат Ñервера еще не дейÑтвителен.</translation>
<translation id="7942349550061667556">КраÑный</translation>
+<translation id="7947285636476623132">Проверьте год в Ñроке дейÑÑ‚Ð²Ð¸Ñ ÐºÐ°Ñ€Ñ‚Ñ‹ и повторите попытку</translation>
<translation id="7951415247503192394">(32 бит)</translation>
<translation id="7956713633345437162">Закладки на мобильном</translation>
<translation id="7961015016161918242">Ðет</translation>
@@ -640,7 +681,10 @@
<translation id="7983301409776629893">Ð’Ñегда переводить <ph name="ORIGINAL_LANGUAGE" /> на <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Ðе указано</translation>
<translation id="8012647001091218357">Ðе удалоÑÑŒ ÑвÑзатьÑÑ Ñ Ð²Ð°ÑˆÐ¸Ð¼Ð¸ родителÑми. Повторите попытку.</translation>
+<translation id="8025119109950072390">ПоÑещение Ñтого Ñайта может привеÑти к уÑтановке вредоноÑного ПО или хищению личной информации (например, паролей, телефонных номеров и данных банковÑких карт).</translation>
+<translation id="803030522067524905">СиÑтема Google по проверке безопаÑноÑти недавно обнаружила попытку фишинга на Ñайте <ph name="SITE" />. Будьте внимательны: мошенники чаÑто Ñоздают веб-Ñтраницы, похожие на знакомые вам Ñайты. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">Язык Ñтой Ñтраницы: <ph name="SOURCE_LANGUAGE" />. ПеревеÑти ее на <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Отправить отзыв</translation>
<translation id="8088680233425245692">Ðе удалоÑÑŒ показать Ñтатью</translation>
<translation id="8089520772729574115">менее 1 МБ</translation>
<translation id="8091372947890762290">ÐÐºÑ‚Ð¸Ð²Ð°Ñ†Ð¸Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑƒÑтройÑтвами не завершена</translation>
@@ -651,9 +695,11 @@
<translation id="8150722005171944719">Файл по адреÑу <ph name="URL" /> недоÑтупен. Возможно, он был удален или перемещен либо права доÑтупа к нему ограничены.</translation>
<translation id="8194797478851900357">&amp;Отменить перемещение</translation>
<translation id="8201077131113104583">ÐедейÑтвительный URL Ð´Ð»Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ€Ð°ÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ‚Ð¾Ñ€Ð¾Ð¼ <ph name="EXTENSION_ID" />.</translation>
+<translation id="8202097416529803614">Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ заказе</translation>
<translation id="8218327578424803826">Ðазначенное меÑтоположение:</translation>
<translation id="8225771182978767009">Тот, кто наÑтраивал компьютер, заблокировал Ñтот Ñайт.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" /> и <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">Сайт <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> может уÑтановить на ваш компьютер вредоноÑное ПО, которое крадет или удалÑет личную информацию (например, фотографии, пароли, ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ реквизиты банковÑких карт).</translation>
<translation id="8241707690549784388">Ðа Ñтранице, которую вы ищете, иÑпользовалаÑÑŒ Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ð°Ñ Ð²Ð°Ð¼Ð¸ информациÑ. При возврате на Ñту Ñтраницу может потребоватьÑÑ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¸Ñ‚ÑŒ выполненные ранее дейÑтвиÑ. Продолжить?</translation>
<translation id="8249320324621329438">Ð’Ñ€ÐµÐ¼Ñ Ð¿Ð¾Ñледней загрузки:</translation>
<translation id="8261506727792406068">Удалить</translation>
@@ -662,11 +708,13 @@
<translation id="8294431847097064396">ИÑточник</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="8349305172487531364">Панель закладок</translation>
<translation id="8363502534493474904">Отключите режим полета.</translation>
<translation id="8364627913115013041">Ðе задано</translation>
<translation id="8380941800586852976">ОпаÑно</translation>
<translation id="8382348898565613901">ЗдеÑÑŒ поÑвÑÑ‚ÑÑ Ð·Ð°ÐºÐ»Ð°Ð´ÐºÐ¸, которые вы недавно открывали.</translation>
+<translation id="8398259832188219207"><ph name="UPLOAD_TIME" />: отчет о ÑбоÑÑ… загружен.</translation>
<translation id="8412145213513410671">Сбои (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Ðеобходимо дважды ввеÑти одну и ту же кодовую фразу.</translation>
<translation id="8428213095426709021">ÐаÑтройки</translation>
@@ -678,9 +726,9 @@
<translation id="8498891568109133222">Превышено Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð¾Ñ‚Ð²ÐµÑ‚Ð° от Ñайта <ph name="HOST_NAME" />.</translation>
<translation id="852346902619691059">Сервер не может подтвердить ÑвÑзь Ñ Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð¼Â <ph name="DOMAIN" />. Его Ñертификат безопаÑноÑти не принимаетÑÑ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ ÑиÑтемой вашего уÑтройÑтва. Возможно, проблема ÑвÑзана Ñ Ð½Ð°Ñтройками Ñервера или дейÑтвиÑми злоумышленников, которые пытаютÑÑ Ð¿ÐµÑ€ÐµÑ…Ð²Ð°Ñ‚Ð¸Ñ‚ÑŒ Ñоединение. <ph name="BEGIN_LEARN_MORE_LINK" />Подробнее…<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Этот тип карт не поддерживаетÑÑ Ð² Google Payments. Выберите другую карту.</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="8553075262323480129">Перевод не удалÑÑ, так как не удаетÑÑ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ð¸Ñ‚ÑŒ Ñзык Ñтраницы.</translation>
<translation id="8559762987265718583">Ðе удалоÑÑŒ уÑтановить защищенное Ñоединение Ñ Ð´Ð¾Ð¼ÐµÐ½Ð¾Ð¼ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> из-за неверных наÑтроек ÑиÑтемных чаÑов и ÐºÐ°Ð»ÐµÐ½Ð´Ð°Ñ€Ñ (<ph name="DATE_AND_TIME" />).</translation>
-<translation id="856992080682148">Срок дейÑÑ‚Ð²Ð¸Ñ Ñертификата Ð´Ð»Ñ Ñтого Ñайта иÑтекает не ранее 2017 года. Кроме того, в цепочке приÑутÑтвует Ñертификат Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñью SHA-1.</translation>
<translation id="8571890674111243710">Перевод Ñтраницы на <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Этот Ñертификат не определÑет механизм проверки отзыва.</translation>
<translation id="8620436878122366504">Ещё не одобрено родителÑми</translation>
@@ -693,10 +741,12 @@
<translation id="8740359287975076522">Ðе удаетÑÑ Ð½Ð°Ð¹Ñ‚Ð¸ &lt;abbr id="dnsDefinition"&gt;DNS-адреÑ&lt;/abbr&gt; Ñайта <ph name="HOST_NAME" />. ВыполнÑетÑÑ Ð´Ð¸Ð°Ð³Ð½Ð¾Ñтика.</translation>
<translation id="8790007591277257123">&amp;Повторить удаление</translation>
<translation id="8798099450830957504">По умолчанию</translation>
+<translation id="8800988563907321413">ЗдеÑÑŒ поÑвÑÑ‚ÑÑ Ñ€ÐµÐºÐ¾Ð¼ÐµÐ½Ð´Ð°Ñ†Ð¸Ð¸.</translation>
<translation id="8804164990146287819">Политика конфиденциальноÑти</translation>
<translation id="8820817407110198400">Закладки</translation>
<translation id="8834246243508017242">Включить автозаполнение контактов…</translation>
<translation id="883848425547221593">Другие закладки</translation>
+<translation id="884264119367021077">ÐÐ´Ñ€ÐµÑ Ð´Ð¾Ñтавки</translation>
<translation id="884923133447025588">Ðе обнаружен механизм отзыва.</translation>
<translation id="885730110891505394">ДоÑтуп Google</translation>
<translation id="8866481888320382733">Ðе удалоÑÑŒ выполнить анализ наÑтроек политики</translation>
@@ -714,18 +764,21 @@
<translation id="8971063699422889582">Сертификат Ñервера уÑтарел.</translation>
<translation id="8987927404178983737">МеÑÑц</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Сайт Ñодержит нежелательное ПО</translation>
<translation id="9001074447101275817">Ð”Ð»Ñ Ð´Ð¾Ñтупа на прокÑи-Ñервер <ph name="DOMAIN" /> требуетÑÑ ÑƒÐºÐ°Ð·Ð°Ñ‚ÑŒ Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸ пароль.</translation>
<translation id="901974403500617787">СиÑтемные флаги может уÑтанавливать только владелец (<ph name="OWNER_EMAIL" />).</translation>
<translation id="9020542370529661692">Эта Ñтраница переведена на <ph name="TARGET_LANGUAGE" />.</translation>
+<translation id="9035022520814077154">Ошибка безопаÑноÑти</translation>
<translation id="9038649477754266430">ИÑпользовать подÑказки Ð´Ð»Ñ ÑƒÑÐºÐ¾Ñ€ÐµÐ½Ð¸Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Ñтраниц</translation>
<translation id="9039213469156557790">Обратите внимание, что на Ñтранице обнаружен небезопаÑный контент. Возможно, при передаче реÑурÑÑ‹ проÑматриваютÑÑ Ñ‚Ñ€ÐµÑ‚ÑŒÐ¸Ð¼Ð¸ лицами, а злоумышленники могут получить доÑтуп к Ñтранице и изменить ее поведение.</translation>
+<translation id="9040185888511745258">Сайт <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> может уÑтановить на ваше уÑтройÑтво вредоноÑное ПО, которое будет мешать работе браузера (например, менÑÑ Ñтартовую Ñтраницу или Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½ÑƒÑŽ рекламу на Ñайтах).</translation>
<translation id="9050666287014529139">ÐšÐ¾Ð´Ð¾Ð²Ð°Ñ Ñ„Ñ€Ð°Ð·Ð°</translation>
<translation id="9065203028668620118">Изменить</translation>
<translation id="9068849894565669697">Выберите цвет</translation>
<translation id="9076283476770535406">Может Ñодержать контент Ð´Ð»Ñ Ð²Ð·Ñ€Ð¾Ñлых</translation>
-<translation id="9092364396508701805">Страница <ph name="HOST_NAME" /> не работает</translation>
<translation id="9103872766612412690">Ðа Ñайте <ph name="SITE" /> Ð´Ð»Ñ Ð·Ð°Ñ‰Ð¸Ñ‚Ñ‹ ваших данных обычно иÑпользуетÑÑ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ðµ. Однако учетные данные, которые мы получили от Ñайта <ph name="SITE" /> ÑейчаÑ, отличаютÑÑ Ð¾Ñ‚ тех, которые он отправлÑет обычно. ВероÑтно, вредоноÑный Ñайт пытаетÑÑ Ð²Ñ‹Ð´Ð°Ñ‚ÑŒ ÑÐµÐ±Ñ Ð·Ð° <ph name="SITE" />, либо Ñтраница Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº Ñети Wi-Fi прервала Ñоединение. Ваша Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾-прежнему в безопаÑноÑти, так как браузер Chromium разорвал Ñоединение до того, как произошел обмен данными.</translation>
<translation id="9137013805542155359">Показать оригинал</translation>
+<translation id="9137248913990643158">Войдите в Chrome, прежде чем иÑпользовать Ñто приложение.</translation>
<translation id="9148507642005240123">&amp;Отменить изменениÑ</translation>
<translation id="9157595877708044936">ÐаÑтройка...</translation>
<translation id="9170848237812810038">&amp;Отменить</translation>
@@ -738,7 +791,6 @@
<translation id="935608979562296692">ОЧИСТИТЬ ФОРМУ</translation>
<translation id="939736085109172342">ÐÐ¾Ð²Ð°Ñ Ð¿Ð°Ð¿ÐºÐ°</translation>
<translation id="941721044073577244">Ð”Ð»Ñ Ð´Ð¾Ñтупа к Ñтому Ñайту требуетÑÑ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ðµ</translation>
-<translation id="962701380617707048">Введите Ñрок дейÑÑ‚Ð²Ð¸Ñ Ð¸ CVC-код карты <ph name="CREDIT_CARD" /></translation>
<translation id="969892804517981540">ÐžÑ„Ð¸Ñ†Ð¸Ð°Ð»ÑŒÐ½Ð°Ñ Ñборка</translation>
<translation id="988159990683914416">Сборка Ð´Ð»Ñ Ñ€Ð°Ð·Ñ€Ð°Ð±Ð¾Ñ‚Ñ‡Ð¸ÐºÐ¾Ð²</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 874d9990fa6..d30a00362ce 100644
--- a/chromium/components/strings/components_strings_sk.xtb
+++ b/chromium/components/strings/components_strings_sk.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Znovu sa pripojiť k sieti Wi-Fi</translation>
<translation id="1175364870820465910">&amp;TlaÄiÅ¥...</translation>
<translation id="1181037720776840403">Odstrániť</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Automaticky nahlasovaÅ¥<ph name="END_WHITEPAPER_LINK" /> Googlu podrobnosti o možných problémoch so zabezpeÄením. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">ÄŽalej</translation>
<translation id="1201895884277373915">Viac z týchto stránok</translation>
<translation id="1206967143813997005">Chybný úvodný podpis</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Azúrová</translation>
<translation id="1629803312968146339">Chcete, aby Chrome uložil túto kartu?</translation>
<translation id="1640180200866533862">Pravidlá pre používateľa</translation>
+<translation id="1640244768702815859">Skúste <ph name="BEGIN_LINK" />navštíviť domovskú stránku webu<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Konfigurácia siete je neplatná a nepodarilo sa ju importovať.</translation>
<translation id="1644574205037202324">História</translation>
<translation id="1645368109819982629">Nepodporovaný protokol</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="168328519870909584">ÚtoÄníci, ktorí sú práve na webe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, sa možno pokúsia nainÅ¡talovaÅ¥ na vaÅ¡e zariadenie nebezpeÄné programy, ktoré ukradnú alebo odstránia vaÅ¡e informácie, napríklad fotky, heslá, správy alebo kreditné karty.</translation>
<translation id="168841957122794586">Certifikát servera obsahuje slabý kryptografický kľúÄ.</translation>
-<translation id="1701955595840307032">Získanie navrhovaného obsahu</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Ak chcete navštíviť tento web, potrebujete povolenie od správcu <ph name="NAME" /></translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Číslo stránky</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Skúste spustiť nástroj Diagnostika siete systému Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Aktualizujte prístupovú frázu na synchronizáciu.</translation>
+<translation id="1787142507584202372">Tu sa zobrazia otvorené karty</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Funkcia BezpeÄné prehliadanie Google nedávno <ph name="BEGIN_LINK" />zistila malvér<ph name="END_LINK" /> na webe <ph name="SITE" />. Weby, 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. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">Neplatná žiadosť alebo parametre žiadosti</translation>
+<translation id="1834321415901700177">Tento web obsahuje škodlivé programy</translation>
<translation id="1838667051080421715">Zobrazuje sa vám zdroj webovej stránky.</translation>
<translation id="1871208020102129563">Proxy je nastavené na použitie pevne daných serverov proxy, nie skriptov PAC webovej adresy.</translation>
<translation id="1883255238294161206">Zbaliť zoznam</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Záložky v mobile</translation>
<translation id="2148716181193084225">Dnes</translation>
-<translation id="2149973817440762519">Upraviť záložku</translation>
<translation id="2154054054215849342">Synchronizácia nie je pre vašu doménu k dispozícii</translation>
<translation id="2166049586286450108">Úplný prístup správcu</translation>
<translation id="2166378884831602661">Tento web nedokáže poskytnúť zabezpeÄené pripojenie</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Web <ph name="SITE" /> momentálne nemôžete navÅ¡tíviÅ¥, pretože používa pravidlo HSTS. Chyby siete a útoky sú zvyÄajne doÄasné, takže by táto stránka mala neskôr pravdepodobne fungovaÅ¥. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">Neplatná záložka v indexe <ph name="ENTRY_INDEX" /> bola ignorovaná</translation>
<translation id="2354001756790975382">Iné záložky</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="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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Zobraziť pravidlá bez nastavenej hodnoty</translation>
<translation id="2396249848217231973">&amp;Vrátiť späť odstránenie</translation>
<translation id="2455981314101692989">Táto webová stránka zakázala automatické dopĺňanie tohto formulára.</translation>
+<translation id="2460160116472764928">Funkcia BezpeÄné prehliadanie Google nedávno <ph name="BEGIN_LINK" />zistila malvér<ph name="END_LINK" /> na webe <ph name="SITE" />. Weby, ktoré sú zvyÄajne bezpeÄné, môžu byÅ¥ niekedy nakazené malvérom. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">Vyplniť</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Spustiť nástroj Diagnostika siete<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Neplatná webová adresa vyhľadávania.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Naozaj chcete odstrániť tieto stránky zo svojej histórie?</translation>
<translation id="2677748264148917807">Opustiť</translation>
<translation id="269990154133806163">Server predložil certifikát, ktorý nebol zverejnený pomocou pravidla transparentnosti certifikátov. V prípade niektorých certifikátov sa táto podmienka vyžaduje s cieľom zaistiÅ¥ ich dôveryhodnosÅ¥ a ochranu proti útoÄníkom. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">Čitateľský zoznam</translation>
<translation id="2704283930420550640">Hodnota nezodpovedá formátu.</translation>
<translation id="2704951214193499422">PrehliadaÄu Chromium sa nepodarilo overiÅ¥ vaÅ¡u kartu. Skúste to znova neskôr.</translation>
<translation id="2705137772291741111">Uložená kópia tohto webu (vo vyrovnávacej pamäti) bola neÄitateľná.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Pokúsili ste sa prejsÅ¥ do domény <ph name="DOMAIN" />, ale certifikát predložený serverom bol zruÅ¡ený vydavateľom. Znamená to, že povereniam zabezpeÄenia predloženým serverom sa nedá celkom dôverovaÅ¥. Je možné, že komunikujete s útoÄníkom. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">Požiadať o povolenie</translation>
<translation id="2713444072780614174">Biela</translation>
+<translation id="2720342946869265578">Nablízku</translation>
<translation id="2721148159707890343">Žiadosť bola úspešná</translation>
<translation id="2728127805433021124">Certifikát servera je podpísaný pomocou slabého podpisového algoritmu.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Spustiť nástroj Diagnostika konektivity<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Môžete zakázať ktorékoľvek servery proxy nakonfigurované na pripojenie na stránke nastavení.</translation>
<translation id="2955913368246107853">Zatvoriť panel pre vyhľadávanie</translation>
<translation id="2958431318199492670">Konfigurácia siete nie je v súlade so Å¡tandardom ONC. Niektoré Äasti konfigurácie sa nemusia importovaÅ¥.</translation>
+<translation id="29611076221683977">ÚtoÄníci, ktorí sú práve na webe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, 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="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="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>
@@ -256,7 +265,6 @@
<translation id="3586931643579894722">Skryť podrobnosti</translation>
<translation id="3587482841069643663">VÅ¡etko</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Server nepodporuje rozšírenie opätovného vyjednávania TLS.</translation>
<translation id="36224234498066874">Vymazať údaje prehliadania...</translation>
<translation id="362276910939193118">Zobraziť celú históriu</translation>
<translation id="3623476034248543066">Zobraziť hodnotu</translation>
@@ -267,6 +275,7 @@
<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="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>
@@ -276,10 +285,10 @@
<translation id="3712624925041724820">VyÄerpané licencie</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>
<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="3788090790273268753">Platnosť certifikátu pre tieto stránky vyprší v roku 2016 a reťazec certifikátu obsahuje certifikát podpísaný pomocou funkcie SHA-1.</translation>
<translation id="382518646247711829">Ak používate server proxy...</translation>
<translation id="3828924085048779000">Prístupová fráza nemôže byť prázdna.</translation>
<translation id="3845539888601087042">Zobrazuje sa história zo zariadení, na ktorých ste prihlásený/-á. <ph name="BEGIN_LINK" />Ďalšie informácie<ph name="END_LINK" /></translation>
@@ -301,6 +310,7 @@
<translation id="4058922952496707368">KÄ¾ÃºÄ <ph name="SUBKEY" />: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Klient a server nepodporujú spoloÄnú verziu protokolu SSL ani Å¡ifrovaciu súpravu.</translation>
<translation id="4079302484614802869">Konfigurácia proxy je nastavená na použitie skriptu PAC webovej adresy, nie pevne daných serverov proxy.</translation>
+<translation id="4098354747657067197">Nasledujúce stránky sú klamlivé</translation>
<translation id="4103249731201008433">Sériové Äíslo zariadenia je neplatné</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="4110615724604346410">Tomuto serveru sa nepodarilo dokázaÅ¥, že ide o doménu <ph name="DOMAIN" />; jej bezpeÄnostný certifikát obsahuje chyby. Môže to byÅ¥ následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útoÄníkom. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -318,15 +328,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{žiadne}=1{1 aplikácia ($1)}=2{2 aplikácie ($1, $2)}few{# aplikácie ($1, $2, $3)}many{# aplikácie ($1, $2, $3)}other{# aplikácií ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Zlyhania</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Skúste spustiť nástroj Diagnostika siete<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">VaÅ¡e pripojenie k tomuto webu nie je úplne zabezpeÄené</translation>
<translation id="4250680216510889253">Nie</translation>
<translation id="425582637250725228">Zmeny, ktoré ste vykonali, sa nemusia uložiť.</translation>
<translation id="4258748452823770588">Chybný podpis</translation>
<translation id="4269787794583293679">(Žiadne používateľské meno)</translation>
+<translation id="4280429058323657511">, dátum vypršania platnosti: <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Funkcia BezpeÄné prehliadanie Google nedávno <ph name="BEGIN_LINK" />naÅ¡la Å¡kodlivé programy<ph name="END_LINK" /> na webe <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">Návrhy rodiÄa</translation>
<translation id="4304224509867189079">Prihlásiť</translation>
<translation id="432290197980158659">Server predložil certifikát, ktorý nezodpovedá vstavaným požiadavkám. Tieto požiadavky sú zahrnuté v prípade konkrétnych webov s vysokým zabezpeÄením, aby sa zaistila vaÅ¡a ochrana. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">Článok sa nepodarilo nájsť</translation>
+<translation id="4326324639298822553">Skontrolujte dátum vypršania platnosti a skúste to znova</translation>
<translation id="4331708818696583467">NezabezpeÄené</translation>
+<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="4381091992796011497">Meno používateľa:</translation>
<translation id="4394049700291259645">Zakázať</translation>
@@ -344,6 +359,7 @@
<translation id="4589078953350245614">Pokúsili ste sa prejsť do domény <ph name="DOMAIN" />, ale server predložil neplatný certifikát. <ph name="BEGIN_LEARN_MORE_LINK" />Ďalšie informácie<ph name="END_LEARN_MORE_LINK" /></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="4619615317237390068">Karty z iných zariadení</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Prezeráte si stránku s rozšíreniami.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -352,10 +368,13 @@
<translation id="4726672564094551039">Znova naÄítaÅ¥ pravidlá</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4744603770635761495">Spustiteľná cesta</translation>
+<translation id="4750917950439032686">VaÅ¡e informácie (napríklad heslá a Äísla kreditných kariet) zostanú po odoslaní na tento web súkromné.</translation>
<translation id="4756388243121344051">&amp;História</translation>
+<translation id="4759118997339041434">Automatické dopĺňanie platobných údajov je zakázané</translation>
<translation id="4764776831041365478">Webové stránky na adrese <ph name="URL" /> môžu byÅ¥ doÄasne nedostupné alebo sa mohli natrvalo premiestniÅ¥ na novú webovú adresu.</translation>
<translation id="4771973620359291008">Vyskytla sa neznáma chyba.</translation>
<translation id="4800132727771399293">Skontrolujte dátum vypršania platnosti aj kód CVC a skúste to znova</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Chyba siete</translation>
<translation id="4816492930507672669">Prispôsobiť stránke</translation>
<translation id="4850886885716139402">Zobraziť</translation>
@@ -364,6 +383,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Akcie</translation>
<translation id="4958444002117714549">Rozbaliť zoznam</translation>
@@ -391,7 +411,6 @@
<translation id="5181140330217080051">SÅ¥ahovanie</translation>
<translation id="5190835502935405962">Panel so záložkami</translation>
<translation id="5199729219167945352">Experimenty</translation>
-<translation id="5199841536747119669">Tu sa zobrazia vaše návrhy</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="5299298092464848405">Pri analýze pravidla sa vyskytla chyba</translation>
@@ -399,8 +418,10 @@
<translation id="5308689395849655368">Hlásenie zlyhaní je zakázané.</translation>
<translation id="5317780077021120954">Uložiť</translation>
<translation id="5327248766486351172">Názov</translation>
+<translation id="5337705430875057403">ÚtoÄníci na webe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sa vás môžu pokúsiÅ¥ naviesÅ¥ vykonaÅ¥ nieÄo nebezpeÄné, ako je inÅ¡talovanie softvéru alebo odhalenie osobných informácií (napr. heslá, telefónne Äísla alebo kreditné karty).</translation>
<translation id="5359637492792381994">Tomuto serveru sa nepodarilo dokázaÅ¥, že ide o doménu <ph name="DOMAIN" />; jej bezpeÄnostný certifikát je momentálne neplatný. Môže to byÅ¥ následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útoÄníkom. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">Nastavenia pravidla sa nepodarilo uložiť</translation>
+<translation id="5386426401304769735">Reťazec certifikátu pre tento web obsahuje certifikát podpísaný pomocou funkcie SHA-1.</translation>
<translation id="5421136146218899937">Odstrániť dáta prehliadania…</translation>
<translation id="5430298929874300616">Odstrániť záložku</translation>
<translation id="5431657950005405462">Súbor sa nenašiel</translation>
@@ -421,17 +442,20 @@
<translation id="5544037170328430102">Vložená stránka na webe <ph name="SITE" /> hovorí:</translation>
<translation id="5556459405103347317">Obnoviť</translation>
<translation id="5565735124758917034">Aktívne</translation>
+<translation id="5572851009514199876">ZaÄnite a prihláste sa do Chromu, aby skontroloval, Äi môžete navÅ¡tíviÅ¥ tento web.</translation>
+<translation id="5580958916614886209">Skontrolujte mesiac vypršania platnosti a skúste to znova</translation>
<translation id="560412284261940334">Správa nie je podporovaná</translation>
<translation id="5610142619324316209">Skontrolovať pripojenie</translation>
<translation id="5617949217645503996">Web <ph name="HOST_NAME" /> vás presmeroval príliš veľakrát.</translation>
<translation id="5622887735448669177">Chcete tento web opustiť?</translation>
<translation id="5629630648637658800">Nastavenia pravidla sa nepodarilo naÄítaÅ¥</translation>
<translation id="5631439013527180824">Neplatný token správy zariadenia</translation>
+<translation id="5669703222995421982">Ako získať prispôsobený obsah</translation>
+<translation id="5675650730144413517">Táto stránka nefunguje</translation>
<translation id="5677928146339483299">Zablokované</translation>
<translation id="5694783966845939798">Pokúsili ste sa prejsÅ¥ do domény <ph name="DOMAIN" />, ale server predložil certifikát podpísaný slabým algoritmom podpisu (napr. SHA-1). Znamená to, že predložené bezpeÄnostné poverenia mohli byÅ¥ sfalÅ¡ované a môže ísÅ¥ o úplne iný server, ako ste oÄakávali (je možné, že komunikujete s útoÄníkom). <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">Identita tejto webovej stránky nebola overená.</translation>
<translation id="5720705177508910913">Aktuálny používateľ</translation>
-<translation id="572328651809341494">Nedávne karty</translation>
<translation id="5732392974455271431">VaÅ¡i rodiÄia ho môžu pre vás odblokovaÅ¥</translation>
<translation id="5784606427469807560">Pri overovaní karty sa vyskytol problém. Skontrolujte pripojenie k internetu a skúste to znova.</translation>
<translation id="5785756445106461925">Táto stránka obsahuje aj iné zdroje, ktoré nie sú zabezpeÄené. Tieto zdroje môžu pri prenose vidieÅ¥ ostatní používatelia a útoÄník ich môže upraviÅ¥ tak, aby zmenil vzhľad stránky.</translation>
@@ -440,6 +464,7 @@
<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="5843436854350372569">Pokúsili ste sa prejsÅ¥ do domény <ph name="DOMAIN" />, ale server predložil certifikát obsahujúci slabý kľúÄ. ÚtoÄník mohol tento súkromný kÄ¾ÃºÄ prelomiÅ¥ a môže ísÅ¥ o iný server, než ste oÄakávali (je možné, že komunikujete s útoÄníkom). <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">Nový prieÄinok</translation>
<translation id="5869405914158311789">K tomuto webu sa nedá pripojiť</translation>
@@ -449,6 +474,7 @@
<translation id="59107663811261420">Služba Google Payments nepodporuje v prípade tohto obchodníka tento typ karty. Vyberte inú kartu.</translation>
<translation id="59174027418879706">Povolené</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="5966707198760109579">Týždeň</translation>
<translation id="5967867314010545767">Odstrániť z histórie</translation>
<translation id="5975083100439434680">Oddialiť</translation>
@@ -457,7 +483,7 @@
<translation id="6008256403891681546">JCB</translation>
<translation id="6016158022840135739">{COUNT,plural, =1{Strana 1}few{Strana #}many{Strana #}other{Strana #}}</translation>
<translation id="6017514345406065928">Zelená</translation>
-<translation id="6040143037577758943">Zatvoriť</translation>
+<translation id="6040143037577758943">Zavrieť</translation>
<translation id="604124094241169006">Automatické</translation>
<translation id="6042308850641462728">Viac</translation>
<translation id="6060685159320643512">Opatrne, tieto experimenty môžu spôsobiť problémy</translation>
@@ -466,8 +492,11 @@
<translation id="614940544461990577">Vyskúšajte:</translation>
<translation id="6151417162996330722">Obdobie platnosti certifikátu servera je príliš dlhé</translation>
<translation id="6165508094623778733">Viac informácií</translation>
+<translation id="6177128806592000436">VaÅ¡e pripojenie k tomuto webu nie je zabezpeÄené</translation>
<translation id="6203231073485539293">Skontrolujte internetové pripojenie</translation>
<translation id="6218753634732582820">Chcete adresu odstrániÅ¥ z prehliadaÄa Chromium?</translation>
+<translation id="6251924700383757765">Pravidlá ochrany súkromia</translation>
+<translation id="625755898061068298">Rozhodli ste sa deaktivovaÅ¥ upozornenia týkajúce sa zabezpeÄenia pre tento web.</translation>
<translation id="6259156558325130047">&amp;Znova zmeniť poradie</translation>
<translation id="6263376278284652872">Záložky domény <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Naspäť do bezpeÄného režimu</translation>
@@ -476,6 +505,7 @@
<translation id="6305205051461490394">Web <ph name="URL" /> je nedostupný.</translation>
<translation id="6321917430147971392">Skontrolujte nastavenia DNS</translation>
<translation id="6328639280570009161">Skúste zakázať predpovede siete</translation>
+<translation id="6328786501058569169">Tento web je klamlivý</translation>
<translation id="6337534724793800597">Filtrovať pravidlá podľa mena</translation>
<translation id="6342069812937806050">Práve teraz</translation>
<translation id="6345221851280129312">neznáma veľkosť</translation>
@@ -484,6 +514,8 @@
<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="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>
<translation id="6416403317709441254">Web <ph name="SITE" /> momentálne nemôžete navÅ¡tíviÅ¥, pretože odoslal zakódované poverenia, ktoré Chromium neodkáže spracovaÅ¥. Chyby siete a útoky sú zvyÄajne doÄasné, takže táto stránka by mala pravdepodobne neskôr fungovaÅ¥. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">Nie je možné skontrolovaÅ¥, Äi bol certifikát odmietnutý.</translation>
@@ -493,10 +525,12 @@
<translation id="6458467102616083041">Ignorované, pretože predvolený vyhľadávaÄ je podľa pravidla zakázaný.</translation>
<translation id="6462969404041126431">Tomuto serveru sa nepodarilo dokázaÅ¥, že ide o doménu <ph name="DOMAIN" />; jej bezpeÄnostný certifikát bol zrejme odvolaný. Môže to byÅ¥ následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útoÄníkom. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<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="6529602333819889595">&amp;Znova odstrániť</translation>
<translation id="6534179046333460208">Návrhy Fyzického webu</translation>
<translation id="6550675742724504774">Možnosti</translation>
+<translation id="6556239504065605927">ZabezpeÄené pripojenie</translation>
<translation id="6563469144985748109">Váš správca to zatiaľ neschválil</translation>
<translation id="6593753688552673085">menej ako <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Možnosti šifrovania</translation>
@@ -505,7 +539,6 @@
<translation id="6644283850729428850">Toto pravidlo bolo oznaÄené ako zastarané.</translation>
<translation id="6652240803263749613">Tomuto serveru sa nepodarilo dokázaÅ¥, že ide o doménu <ph name="DOMAIN" />; operaÄný systém vášho poÄítaÄa nedôveruje jej bezpeÄnostnému certifikátu. Môže to byÅ¥ následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útoÄníkom. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">UpraviÅ¥ prieÄinok</translation>
-<translation id="6660210980321319655">Automaticky nahlásené <ph name="CRASH_TIME" /></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>
@@ -517,6 +550,7 @@
<translation id="6753269504797312559">Hodnota pravidla</translation>
<translation id="6757797048963528358">Vaše zariadenie prešlo do režimu spánku.</translation>
<translation id="6778737459546443941">Váš rodiÄ to zatiaľ neschválil</translation>
+<translation id="6810899417690483278">Identifikátor prispôsobenia</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Webová stránka na adrese <ph name="URL" /> momentálne nie je k dispozícii. Môže byť preťažená alebo na nej prebieha údržba.</translation>
<translation id="6831043979455480757">Preložiť</translation>
@@ -537,6 +571,8 @@
<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>
+<translation id="7064851114919012435">Kontaktné informácie</translation>
+<translation id="7079718277001814089">Tento web obsahuje malvér</translation>
<translation id="7087282848513945231">Grófstvo</translation>
<translation id="7088615885725309056">Staršie</translation>
<translation id="7090678807593890770">Vyhľadajte na Googli výraz <ph name="LINK" /></translation>
@@ -551,6 +587,7 @@
<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="7219179957768738017">Spojenie používa protokol <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Webové stránky, ktoré chcete otvoriť, obsahujú malvér</translation>
<translation id="724975217298816891">Ak chcete aktualizovať podrobnosti o karte <ph name="CREDIT_CARD" />, zadajte dátum vypršania platnosti a kód CVC. Po potvrdení budú podrobnosti o karte zdieľané s týmto webom.</translation>
<translation id="725866823122871198">Súkromné pripojenie k doméne <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> sa nedá nadviazaÅ¥, pretože dátum a Äas (<ph name="DATE_AND_TIME" />) vášho poÄítaÄa sú nesprávne.</translation>
<translation id="7269802741830436641">Táto webová stránka obsahuje cyklické presmerovanie</translation>
@@ -606,6 +643,7 @@ Nabudúce by sa vám mohol hodiť režim inkognito (<ph name="SHORTCUT_KEY" />).
<translation id="7658239707568436148">Zrušiť</translation>
<translation id="7667346355482952095">Vrátený token pravidla je prázdny alebo sa nezhoduje s aktuálnym tokenom</translation>
<translation id="7668654391829183341">Neznáme zariadenie</translation>
+<translation id="7669271284792375604">ÚtoÄníci na tomto webe sa vás môžu pokúsiÅ¥ podvodom presvedÄiÅ¥, aby ste si nainÅ¡talovali programy poÅ¡kodzujúce vaÅ¡e prostredie prehliadania (napríklad zmenou domovskej stránky alebo zobrazovaním Äalších reklám na weboch, ktoré navÅ¡tevujete).</translation>
<translation id="7674629440242451245">Máte záujem o skvelé nové funkcie prehliadaÄa Chrome? Vyskúšajte verziu pre vývojárov na stránke chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />PrejsÅ¥ na stránky <ph name="SITE" /> (nebezpeÄné)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Tento web nie je možné naÄítaÅ¥ z vyrovnávacej pamäte</translation>
@@ -621,14 +659,17 @@ Nabudúce by sa vám mohol hodiť režim inkognito (<ph name="SHORTCUT_KEY" />).
<translation id="7800304661137206267">Pripojenie je Å¡ifrované pomocou Å¡tandardu <ph name="CIPHER" /> s algoritmom <ph name="MAC" /> pre overovanie správ a mechanizmom výmeny kľúÄov <ph name="KX" />.</translation>
<translation id="780301667611848630">Nie, Äakujem</translation>
<translation id="7805768142964895445">Stav</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Chcete návrh odstrániÅ¥ z prehliadaÄa Chrome?</translation>
<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="7887683347370398519">Skontrolujte svoj kód CVC a skúste to znova</translation>
<translation id="7912024687060120840">V prieÄinku:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Certifikát servera ešte nie je platný.</translation>
<translation id="7942349550061667556">Červená</translation>
+<translation id="7947285636476623132">Skontrolujte rok vypršania platnosti a skúste to znova</translation>
<translation id="7951415247503192394">(32-bitová verzia)</translation>
<translation id="7956713633345437162">Záložky v mobile</translation>
<translation id="7961015016161918242">Nikdy</translation>
@@ -636,7 +677,10 @@ Nabudúce by sa vám mohol hodiť režim inkognito (<ph name="SHORTCUT_KEY" />).
<translation id="7983301409776629893">Vždy preložiť jazyk <ph name="ORIGINAL_LANGUAGE" /> do jazyka <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Nie je upresnené</translation>
<translation id="8012647001091218357">V tejto chvíli sa nám nepodarilo spojiÅ¥ s vaÅ¡imi rodiÄmi. Skúste to znova neskôr.</translation>
+<translation id="8025119109950072390">ÚtoÄníci na tomto webe sa vás môžu pokúsiÅ¥ naviesÅ¥ vykonaÅ¥ nieÄo nebezpeÄné, ako je inÅ¡talovanie softvéru alebo odhalenie osobných informácií (napr. hesiel, telefónnych Äísel alebo kreditných kariet).</translation>
+<translation id="803030522067524905">Funkcia BezpeÄné prehliadanie Google nedávno zistila phishing na webe <ph name="SITE" />. Phishingové weby sú také, ktoré sa vydávajú za iné weby, aby vás oklamali. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">Táto stránka je v jazyku <ph name="SOURCE_LANGUAGE" />. Chcete ju preložiť do jazyka <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Odoslať spätnú väzbu</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>
@@ -647,9 +691,11 @@ Nabudúce by sa vám mohol hodiť režim inkognito (<ph name="SHORTCUT_KEY" />).
<translation id="8150722005171944719">Súbor na adrese <ph name="URL" /> nie je možné preÄítaÅ¥. Je možné, že bol odstránený, presunutý alebo môžu v prístupe brániÅ¥ povolenia súboru.</translation>
<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="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="8230421197304563332">ÚtoÄníci, ktorí sú práve na webe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, 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="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="8249320324621329438">Naposledy naÄítané:</translation>
<translation id="8261506727792406068">Odstrániť</translation>
@@ -658,11 +704,13 @@ Nabudúce by sa vám mohol hodiť režim inkognito (<ph name="SHORTCUT_KEY" />).
<translation id="8294431847097064396">Zdroj</translation>
<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="8349305172487531364">Panel so záložkami</translation>
<translation id="8363502534493474904">Vypnúť režim v lietadle</translation>
<translation id="8364627913115013041">Nenastavené.</translation>
<translation id="8380941800586852976">NebezpeÄná</translation>
<translation id="8382348898565613901">Tu sa zobrazia vaše nedávno navštívené záložky</translation>
+<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="8428213095426709021">Nastavenia</translation>
@@ -674,9 +722,9 @@ Nabudúce by sa vám mohol hodiť režim inkognito (<ph name="SHORTCUT_KEY" />).
<translation id="8498891568109133222">Web <ph name="HOST_NAME" /> príliš dlho neodpovedal.</translation>
<translation id="852346902619691059">Tomuto serveru sa nepodarilo dokázaÅ¥, že 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Å¥ následok nesprávnej konfigurácie alebo napadnutia vášho pripojenia útoÄníkom. <ph name="BEGIN_LEARN_MORE_LINK" />ÄŽalÅ¡ie informácie<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Služba Google Payments nepodporuje tento typ karty. Vyberte inú kartu.</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="8553075262323480129">Prekladanie zlyhalo, pretože sa nepodarilo urÄiÅ¥ jazyk stránky.</translation>
<translation id="8559762987265718583">Súkromné pripojenie k doméne <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> sa nedá nadviazaÅ¥, pretože dátum a Äas (<ph name="DATE_AND_TIME" />) vášho zariadenia sú nesprávne.</translation>
-<translation id="856992080682148">Platnosť certifikátu pre tieto stránky vyprší v roku 2017 alebo neskôr a reťazec certifikátu obsahuje certifikát podpísaný pomocou funkcie SHA-1.</translation>
<translation id="8571890674111243710">Prebieha preklad stránky do jazyka: <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">V certifikáte nie je uvedené, akým spôsobom sa má skontrolovaÅ¥, Äi certifikát nebol odmietnutý.</translation>
<translation id="8620436878122366504">VaÅ¡i rodiÄia to zatiaľ neschválili</translation>
@@ -689,10 +737,12 @@ Nabudúce by sa vám mohol hodiť režim inkognito (<ph name="SHORTCUT_KEY" />).
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;Adresa DNS&lt;/abbr&gt; webu <ph name="HOST_NAME" /> sa nenašla. Problém sa diagnostikuje.</translation>
<translation id="8790007591277257123">&amp;Znova vymazať</translation>
<translation id="8798099450830957504">Predvolené</translation>
+<translation id="8800988563907321413">Tu sa zobrazia návrhy funkcie Nablízku</translation>
<translation id="8804164990146287819">Pravidlá ochrany súkromia</translation>
<translation id="8820817407110198400">Záložky</translation>
<translation id="8834246243508017242">Povoliť automatické dopĺňanie pomocou Kontaktov…</translation>
<translation id="883848425547221593">Iné záložky</translation>
+<translation id="884264119367021077">Dodacia adresa</translation>
<translation id="884923133447025588">Nenašiel sa žiadny mechanizmus rušenia certifikátov.</translation>
<translation id="885730110891505394">Zdieľanie s Googlom</translation>
<translation id="8866481888320382733">Pri analýze nastavení pravidla sa vyskytla chyba</translation>
@@ -710,18 +760,21 @@ Nabudúce by sa vám mohol hodiť režim inkognito (<ph name="SHORTCUT_KEY" />).
<translation id="8971063699422889582">Platnosť certifikátu servera vypršala.</translation>
<translation id="8987927404178983737">Mesiac</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Webové stránky, ktoré sa chystáte navštíviť, obsahujú škodlivé programy</translation>
<translation id="9001074447101275817">Proxy server <ph name="DOMAIN" /> vyžaduje používateľské meno a heslo.</translation>
<translation id="901974403500617787">Príznaky, ktoré platia v celom systéme, môže nastaviť iba vlastník: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Táto stránka bola preložená do jazyka <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Chyba zabezpeÄenia</translation>
<translation id="9038649477754266430">PoužívaÅ¥ službu predpovedí na rýchlejÅ¡ie naÄítanie stránok</translation>
<translation id="9039213469156557790">Táto stránka obsahuje aj iné zdroje, ktoré nie sú zabezpeÄené. Tieto zdroje môžu pri prenose vidieÅ¥ ostatní používatelia a útoÄník ich môže upraviÅ¥ tak, aby zmenil správanie stránky.</translation>
+<translation id="9040185888511745258">ÚtoÄníci na webe <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sa vás môžu pokúsiÅ¥ podvodom presvedÄiÅ¥, aby ste si nainÅ¡talovali programy poÅ¡kodzujúce vaÅ¡e prostredie prehliadania (napríklad zmenou vaÅ¡ej domovskej stránky alebo zobrazovaním Äalších reklám na stránkach, ktoré navÅ¡tevujete).</translation>
<translation id="9050666287014529139">Prístupová fráza</translation>
<translation id="9065203028668620118">Upraviť</translation>
<translation id="9068849894565669697">Výber farby</translation>
<translation id="9076283476770535406">Môže zahŕňať obsah pre dospelých</translation>
-<translation id="9092364396508701805">Táto stránka webu <ph name="HOST_NAME" /> nefunguje</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="9157595877708044936">Prebieha nastavenie...</translation>
<translation id="9170848237812810038">&amp;Naspäť</translation>
@@ -734,7 +787,6 @@ Nabudúce by sa vám mohol hodiť režim inkognito (<ph name="SHORTCUT_KEY" />).
<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="962701380617707048">Ak chcete aktualizovať podrobnosti o karte <ph name="CREDIT_CARD" />, zadajte dátum vypršania platnosti a kód CVC</translation>
<translation id="969892804517981540">Oficiálne zostavenie</translation>
<translation id="988159990683914416">Zostavenie pre vývojárov</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 cde7eb9d98e..0ce16d86e2d 100644
--- a/chromium/components/strings/components_strings_sl.xtb
+++ b/chromium/components/strings/components_strings_sl.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">znova vzpostaviti povezavo z omrežjem Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Natisni ...</translation>
<translation id="1181037720776840403">Odstrani</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Samodejno poroÄanje<ph name="END_WHITEPAPER_LINK" /> podrobnosti morebitnih varnostnih dogodkov Googlu. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Naprej</translation>
<translation id="1201895884277373915">VeÄ s tega mesta</translation>
<translation id="1206967143813997005">Neveljaven prvotni podpis</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cianova</translation>
<translation id="1629803312968146339">Ali želite, da Chrome shrani to kartico?</translation>
<translation id="1640180200866533862">Uporabniški pravilniki</translation>
+<translation id="1640244768702815859">Poskusite <ph name="BEGIN_LINK" />obiskati domaÄo stran spletnega mesta<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Omrežna konfiguracija ni veljavna in je ni mogoÄe uvoziti.</translation>
<translation id="1644574205037202324">Zgodovina</translation>
<translation id="1645368109819982629">Nepodprt protokol</translation>
<translation id="1676269943528358898">Spletno mesto <ph name="SITE" /> za zaÅ¡Äito vaÅ¡ih podatkov obiÄajno uporablja Å¡ifriranje. Ko se je Google Chrome tokrat poskusil povezati s spletnim mestom <ph name="SITE" />, je to vrnilo nenavadne in nepravilne poverilnice. Do tega lahko pride, Äe se napadalec lažno predstavlja za spletno mesto <ph name="SITE" /> ali Äe je povezavo prekinil zaslon za prijavo v omrežje Wi-Fi. VaÅ¡i podatki so Å¡e vedno varni, saj je Google Chrome pred izmenjavo podatkov prekinil povezavo.</translation>
+<translation id="168328519870909584">Napadalci, ki so trenutno na spletnem mestu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, lahko poskusijo v vaÅ¡i napravi namestiti nevarne programe, ki kradejo ali briÅ¡ejo podatke (na primer fotografije, gesla, sporoÄila in podatke kreditnih kartic).</translation>
<translation id="168841957122794586">Potrdilo strežnika vsebuje Å¡ibek Å¡ifrirni kljuÄ.</translation>
-<translation id="1701955595840307032">Prejemanje predlagane vsebine</translation>
<translation id="1710259589646384581">Operacijski sistem</translation>
<translation id="1721312023322545264"><ph name="NAME" /> vam mora odobriti obisk tega spletnega mesta</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Å tevilka strani</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Poskušajte zagnati orodje Omrežna diagnostika Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Posodobite geslo za sinhronizacijo.</translation>
+<translation id="1787142507584202372">Tu so prikazani odprti zavihki</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">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. Zlonamerno vsebino razÅ¡irja znani distributer zlonamerne programske opreme, <ph name="SUBRESOURCE_HOST" />. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Neveljavna zahteva ali parametri zahteve</translation>
+<translation id="1834321415901700177">Na tem spletnem mestu so Å¡kodljivi programi</translation>
<translation id="1838667051080421715">Ogledujete si vir spletne strani.</translation>
<translation id="1871208020102129563">Proxy je nastavljen na uporabo stalnih strežnikov proxy, ne na URL skripta .pac.</translation>
<translation id="1883255238294161206">Strni seznam</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Tehnologija Native Client</translation>
<translation id="213826338245044447">Zaznamki mobilne naprave</translation>
<translation id="2148716181193084225">Danes</translation>
-<translation id="2149973817440762519">Uredi zaznamek</translation>
<translation id="2154054054215849342">Sinhronizacija ni na voljo za vašo domeno</translation>
<translation id="2166049586286450108">Polni skrbniški dostop</translation>
<translation id="2166378884831602661">To spletno mesto ne more zagotoviti varne povezave</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Spletnega mesta <ph name="SITE" /> trenutno ni mogoÄe obiskati, saj uporablja protokol HSTS. Napake omrežja in napadi na omrežje so obiÄajno zaÄasni, zato bo ta stran verjetno delovala pozneje. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">Neveljaveni zaznamek na indeksu <ph name="ENTRY_INDEX" /> je bil prezrt</translation>
<translation id="2354001756790975382">Drugi zaznamki</translation>
+<translation id="2355395290879513365">Napadalci morda vidijo slike, ki si jih ogledujete na tem spletnem mestu, in vas ukanijo, tako da jih spremenijo.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Pokaži pravilnike brez nastavljene vrednosti</translation>
<translation id="2396249848217231973">&amp;Razveljavi izbris</translation>
<translation id="2455981314101692989">Spletna stran je onemogoÄila samodejno izpolnjevanje za ta obrazec.</translation>
+<translation id="2460160116472764928">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. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Izpolni</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Izvajanje orodja za omrežno diagnostiko<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Neveljaven URL iskanja.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Ali ste prepriÄani, da želite te strani izbrisati iz svoje zgodovine?</translation>
<translation id="2677748264148917807">Zapusti</translation>
<translation id="269990154133806163">Strežnik je posredoval potrdilo, ki ni bilo javno razkrito na podlagi pravilnika o preglednosti potrdila. To je obvezno za nekatera potrdila zaradi zagotavljanja, da so zaupanja vredna in Å¡Äitijo pred napadalci. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Reading List</translation>
<translation id="2704283930420550640">Vrednost se ne ujema z obliko.</translation>
<translation id="2704951214193499422">Chromium trenutno ni mogel potrditi vaše kartice. Poskusite znova pozneje.</translation>
<translation id="2705137772291741111">Shranjena (predpomnjena) kopija tega spletnega mesta je bila neberljiva.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Poskusili ste dostopati do domene <ph name="DOMAIN" />, vendar je izdajatelj preklical potrdilo, ki ga je posredoval strežnik. To pomeni, da varnostnim poverilnicam, ki jih je posredoval strežnik, nikakor ne smete zaupati. Morda komunicirate z napadalcem. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">Zahtevaj dovoljenje</translation>
<translation id="2713444072780614174">Bela</translation>
+<translation id="2720342946869265578">V bližini</translation>
<translation id="2721148159707890343">Zahteva je uspela</translation>
<translation id="2728127805433021124">Strežnikovo potrdilo je podpisano s šibkim podpisnim algoritmom.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Izvajanje orodja Diagnostika povezljivosti<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">NamestniÅ¡ke strežnike, konfigurirane za povezavo, lahko onemogoÄite na strani z nastavitvami.</translation>
<translation id="2955913368246107853">Zapri vrstico za iskanje</translation>
<translation id="2958431318199492670">Omrežna konfiguracija ne ustreza standardu ONC. Deli konfiguracije morda niso bili uvoženi.</translation>
+<translation id="29611076221683977">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).</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="2985306909656435243">ÄŒe je to omogoÄeno, Chromium shrani kopijo kartice v tej napravi zaradi hitrejÅ¡ega izpolnjevanja obrazcev.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Skrij podrobnosti</translation>
<translation id="3587482841069643663">Vse</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Strežnik ne podpora razÅ¡iritve za vnoviÄno pogajanje TLS.</translation>
<translation id="36224234498066874">Izbriši podatke brskanja ...</translation>
<translation id="362276910939193118">Prikaži celotno zgodovino</translation>
<translation id="3623476034248543066">Pokaži vrednost</translation>
@@ -271,19 +279,20 @@
<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="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="3696411085566228381">brez</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">Nalagam ...</translation>
+<translation id="370665806235115550">Nalaganje ...</translation>
<translation id="3712624925041724820">Ni dovolj licenc</translation>
<translation id="3714780639079136834">vklopiti prenos podatkov v mobilnih omrežjih ali Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />preveriti strežnik proxy, požarni zid in konfiguracijo DNS-ja<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">ÄŒe se zavedate varnostnega tveganja, lahko <ph name="BEGIN_LINK" />obiÅ¡Äete to spletno mesto, ki ni varno<ph name="END_LINK" />, preden bodo nevarni programi odstranjeni.</translation>
<translation id="3739623965217189342">Povezava, ki ste jo kopirali</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="3788090790273268753">Potrdilo za to spletno mesto poteÄe leta 2016 in veriga potrdil vsebuje potrdilo, podpisano z algoritmom SHA-1.</translation>
<translation id="382518646247711829">Če uporabite namestniški strežnik ...</translation>
<translation id="3828924085048779000">Prazno geslo ni dovoljeno.</translation>
<translation id="3845539888601087042">Prikazana je zgodovina iz naprav, v katere ste prijavljeni. <ph name="BEGIN_LINK" />VeÄ o tem<ph name="END_LINK" /></translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">KljuÄ Â»<ph name="SUBKEY" />«: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Odjemalec in strežnik ne podpirata skupne razliÄice protokola SSL ali Å¡ifrirne zbirke.</translation>
<translation id="4079302484614802869">Konfiguracija strežnika proxy je nastavljena na uporabo URL-ja skripta .pac, ne na stalne strežnike proxy.</translation>
+<translation id="4098354747657067197">ZavajajoÄe spletno mesto</translation>
<translation id="4103249731201008433">Neveljavna serijska Å¡tevilka naprave</translation>
<translation id="4103763322291513355">Na &lt;strong&gt;chrome://policy&lt;/strong&gt; si lahko ogledate seznam blokiranih URL-jev in drugih pravilnikov, ki jih uveljavlja skrbnik sistema.</translation>
<translation id="4110615724604346410">Temu strežniku ni uspelo dokazati, da je domena <ph name="DOMAIN" />; njegovo varnostno potrdilo vsebuje napake. Razlog za to je lahko napaÄna konfiguracija ali napadalÄevo prestrezanje povezave. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{niÄ}=1{1 aplikacija ($1)}=2{2 aplikaciji ($1, $2)}one{# aplikacija ($1, $2, $3)}two{# aplikaciji ($1, $2, $3)}few{# aplikacije ($1, $2, $3)}other{# aplikacij ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Zrušitve</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Poskušajte zagnati orodje za omrežno diagnostiko<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Povezava s tem mestom ni povsem varna.</translation>
<translation id="4250680216510889253">Ne</translation>
<translation id="425582637250725228">Spremembe, ki ste jih naredili, morda niso shranjene.</translation>
<translation id="4258748452823770588">NapaÄen podpis</translation>
<translation id="4269787794583293679">(Ni uporabniškega imena)</translation>
+<translation id="4280429058323657511">, datum poteka veljavnosti: <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Googlova funkcija varnega brskanja je na spletnem mestu <ph name="SITE" /> nedavno <ph name="BEGIN_LINK" />odkrila Å¡kodljive programe<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Predlogi staršev</translation>
<translation id="4304224509867189079">Prijava</translation>
<translation id="432290197980158659">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. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">ÄŒlanka ni bilo mogoÄe najti</translation>
+<translation id="4326324639298822553">Preverite datum poteka veljavnosti in poskusite znova.</translation>
<translation id="4331708818696583467">Ni varno</translation>
+<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="4381091992796011497">Uporabniško ime:</translation>
<translation id="4394049700291259645">OnemogoÄi</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Poskusili ste dostopati do domene <ph name="DOMAIN" />, vendar je strežnik posredoval neveljavno potrdilo. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Zavihki iz drugih naprav</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Ogledujete si stran z razširitvami.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Znova naloži pravilnike</translation>
<translation id="4728558894243024398">Okolje</translation>
<translation id="4744603770635761495">Pot do izvedljive datoteke</translation>
+<translation id="4750917950439032686">Vaši podatki (npr. gesla ali številke kreditnih kartic) so zasebni, kadar so poslani temu spletnemu mestu.</translation>
<translation id="4756388243121344051">&amp;Zgodovina</translation>
+<translation id="4759118997339041434">Samodejno izpolnjevanje podatkov za plaÄevanje je onemogoÄeno</translation>
<translation id="4764776831041365478">Spletna stran na naslovu <ph name="URL" /> morda zaÄasno ne deluje ali pa je trajno premaknjena na novi spletni naslov.</translation>
<translation id="4771973620359291008">Prišlo je do neznane napake.</translation>
<translation id="4800132727771399293">Preverite datum poteka in Å¡tevilko CVC ter poskusite znova</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Napaka v omrežju</translation>
<translation id="4816492930507672669">Prilagodi strani</translation>
<translation id="4850886885716139402">Pogled</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></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="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="495170559598752135">Dejanja</translation>
<translation id="4958444002117714549">Razširi seznam</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Prenos</translation>
<translation id="5190835502935405962">Vrstica z zaznamki</translation>
<translation id="5199729219167945352">Poskusi</translation>
-<translation id="5199841536747119669">Tu so prikazani predlogi</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="5299298092464848405">Napaka pri razÄlenjevanju pravilnika</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">PoroÄanje o zruÅ¡itvah je onemogoÄeno.</translation>
<translation id="5317780077021120954">Shrani</translation>
<translation id="5327248766486351172">Ime</translation>
+<translation id="5337705430875057403">Napadalci na spletnem mestu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> vas lahko z zavajanjem 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).</translation>
<translation id="5359637492792381994">Temu strežniku ni uspelo dokazati, da je domena <ph name="DOMAIN" />; njegovo varnostno potrdilo trenutno ni veljavno. Razlog za to je lahko napaÄna konfiguracija ali napadalÄevo prestrezanje povezave. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Nastavitev pravilnika ni bilo mogoÄe shraniti</translation>
+<translation id="5386426401304769735">Veriga potrdil za to spletno mesto vsebuje potrdilo, podpisano z algoritmom SHA-1.</translation>
<translation id="5421136146218899937">Izbriši podatke brskanja ...</translation>
<translation id="5430298929874300616">Odstrani zaznamek</translation>
<translation id="5431657950005405462">Datoteke ni bilo mogoÄe najti</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Vdelana stran na spletnem mestu <ph name="SITE" /> sporoÄa:</translation>
<translation id="5556459405103347317">Ponovno naloži</translation>
<translation id="5565735124758917034">Aktivno</translation>
+<translation id="5572851009514199876">ZaÄnite s prijavo v Chrome, da lahko Chrome preveri, ali vam je dovoljeno dostopati do tega spletnega mesta.</translation>
+<translation id="5580958916614886209">Preverite mesec poteka veljavnosti in poskusite znova</translation>
<translation id="560412284261940334">Upravljanje ni podprto</translation>
<translation id="5610142619324316209">preveriti povezavo</translation>
<translation id="5617949217645503996">Spletno mesto <ph name="HOST_NAME" /> vas je preveÄkrat preusmerilo.</translation>
<translation id="5622887735448669177">Ali želite zapustiti to spletno mesto?</translation>
<translation id="5629630648637658800">Nastavitev pravilnika ni bilo mogoÄe naložiti</translation>
<translation id="5631439013527180824">Neveljaven žeton za upravljanje naprave</translation>
+<translation id="5669703222995421982">Prilagojena vsebina</translation>
+<translation id="5675650730144413517">Ta stran ne deluje</translation>
<translation id="5677928146339483299">Blokirano</translation>
<translation id="5694783966845939798">Poskusili ste dostopati do domene <ph name="DOMAIN" />, vendar je strežnik posredoval potrdilo, podpisano s Å¡ibkim podpisnim algoritmom (na primer SHA-1). To pomeni, da so varnostne poverilnice, ki jih je posredoval strežnik, morda ponarejene in strežnik morda ni tisti, ki ga priÄakujete (morda komunicirate z napadalcem). <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Identiteta tega spletnega mesta ni bila potrjena.</translation>
<translation id="5720705177508910913">Trenutni uporabnik</translation>
-<translation id="572328651809341494">Nedavni zavihki</translation>
<translation id="5732392974455271431">Starši ga lahko odblokirajo</translation>
<translation id="5784606427469807560">Težava pri potrditvi kartice. Preverite internetno povezavo in poskusite znova.</translation>
<translation id="5785756445106461925">Poleg tega so na tej strani druga sredstva, ki niso varna. Ta sredstva lahko med prenosom pregledujejo drugi, morebitni napadalec pa jih lahko spremeni, tako da se spremeni videz strani.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Poskusili ste dostopati do domene <ph name="DOMAIN" />, vendar ima strežnik potrdilo s Å¡ibkim kljuÄem. Napadalec je morda deÅ¡ifriral zasebni kljuÄ in strežnik morda ni tisti, ki ste ga priÄakovali (morda komunicirate z napadalcem). <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Nova mapa</translation>
<translation id="5869405914158311789">Tega spletnega mesta ni mogoÄe doseÄi</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Ta vrsta kartice ni podprta v storitvi Google Payments pri tem trgovcu. Izberite drugo kartico.</translation>
<translation id="59174027418879706">OmogoÄena</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="5966707198760109579">Teden</translation>
<translation id="5967867314010545767">Odstrani iz zgodovine</translation>
<translation id="5975083100439434680">Pomanjšaj</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Poskusite:</translation>
<translation id="6151417162996330722">Potrdilo strežnika ima predolgo obdobje veljavnosti.</translation>
<translation id="6165508094623778733">VeÄ o tem</translation>
+<translation id="6177128806592000436">Povezava s tem spletnim mestom ni zasebna</translation>
<translation id="6203231073485539293">Preverite internetno povezavo</translation>
<translation id="6218753634732582820">Želite naslov odstraniti iz Chromiuma?</translation>
+<translation id="6251924700383757765">Pravilnik o zasebnosti</translation>
+<translation id="625755898061068298">Izbrali ste onemogoÄanje varnostnih opozoril za to spletno mesto.</translation>
<translation id="6259156558325130047">&amp;Uveljavi razvrstitev</translation>
<translation id="6263376278284652872">Zaznamki <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Nazaj na varnost</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">Naslov <ph name="URL" /> je nedosegljiv.</translation>
<translation id="6321917430147971392">Preverite nastavitve za DNS</translation>
<translation id="6328639280570009161">Poskusite onemogoÄiti omrežno predvidevanje</translation>
+<translation id="6328786501058569169">To spletno mesto je zavajajoÄe</translation>
<translation id="6337534724793800597">Filtriraj pravilnike po imenu</translation>
<translation id="6342069812937806050">Pravkar</translation>
<translation id="6345221851280129312">neznana velikost</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Spletnega mesta <ph name="SITE" /> trenutno ne morete obiskati, saj je poslalo Å¡ifrirane poverilnice, ki jih Chromium ne more obdelati. Napake omrežja in napadi na omrežje so obiÄajno zaÄasni, zato bo ta stran verjetno delovala pozneje. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Ni mogoÄe preveriti, ali je bilo potrdilo preklicano.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Prezrto, ker je pravilnik onemogoÄil privzeto iskanje.</translation>
<translation id="6462969404041126431">Temu strežniku ni uspelo dokazati, da je domena <ph name="DOMAIN" />; njegovo varnostno potrdilo je bilo morda preklicano. Razlog za to je lahko napaÄna konfiguracija ali napadalÄevo prestrezanje povezave. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Uveljavi izbris</translation>
<translation id="6534179046333460208">Predlogi za FiziÄni splet</translation>
<translation id="6550675742724504774">Možnosti</translation>
+<translation id="6556239504065605927">Varna povezava</translation>
<translation id="6563469144985748109">Skrbnik Å¡e ni odobril</translation>
<translation id="6593753688552673085">manj kot <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Možnosti šifriranja</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Ta pravilnik je zastarel.</translation>
<translation id="6652240803263749613">Temu strežniku ni uspelo dokazati, da je domena <ph name="DOMAIN" />; operacijski sistem vaÅ¡ega raÄunalnika ne zaupa njegovemu varnostnemu potrdilu. Razlog za to je lahko napaÄna konfiguracija ali napadalÄevo prestrezanje povezave. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Urejanje mape</translation>
-<translation id="6660210980321319655">Samodejno sporoÄeno: <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Vrednost pravilnika</translation>
<translation id="6757797048963528358">Naprava je preklopila v stanje pripravljenosti.</translation>
<translation id="6778737459546443941">Starši še niso odobrili</translation>
+<translation id="6810899417690483278">ID za prilagajanje</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Spletna stran na naslovu <ph name="URL" /> trenutno ni na voljo. Morda je preobremenjena ali nedosegljiva zaradi vzdrževanja.</translation>
<translation id="6831043979455480757">Prevedi</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Podatki o stiku</translation>
+<translation id="7079718277001814089">To spletno mesto vsebuje zlonamerno programsko opremo</translation>
<translation id="7087282848513945231">Okraj</translation>
<translation id="7088615885725309056">Starejše</translation>
<translation id="7090678807593890770">IÅ¡Äite v Googlu s poizvedbo <ph name="LINK" /></translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423">Spletno mesto <ph name="HOST_NAME" /> ne upošteva varnostnih standardov.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />VeÄ o<ph name="END_LINK" /> tej težavi.</translation>
<translation id="7219179957768738017">Povezava uporablja <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Spletno mesto z zlonamerno programsko opremo</translation>
<translation id="724975217298816891">Vnesite datum poteka in CVC za <ph name="CREDIT_CARD" />, da posodobite podatke o kartici. Ko potrdite, bodo temu spletnemu mestu razkriti podatki o kartici.</translation>
<translation id="725866823122871198">Zasebne povezave z domeno <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ni mogoÄe vzpostaviti, ker sta datum in ura (<ph name="DATE_AND_TIME" />) v raÄunalniku nepravilna.</translation>
<translation id="7269802741830436641">Ta spletna stran ima preusmeritveno zanko</translation>
@@ -611,6 +648,7 @@ Pst! NaslednjiÄ vam lahko pride prav bližnjica <ph name="SHORTCUT_KEY" /> za n
<translation id="7658239707568436148">PrekliÄi</translation>
<translation id="7667346355482952095">Vrnjen žeton pravilnika je prazen ali se ne ujema s trenutnim žetonom</translation>
<translation id="7668654391829183341">Neznana naprava</translation>
+<translation id="7669271284792375604">Napadalci na tem spletnem mestu vas bodo morda poskusili zavesti, da bi namestili programe, ki Å¡kodljivo vplivajo na brskanje (na primer tako, da spremenijo vaÅ¡o domaÄo stran ali na spletnih mestih, ki jih obiÅ¡Äete, prikazujejo dodatne oglase).</translation>
<translation id="7674629440242451245">Vas zanimajo super nove funkcije Chroma? Preskusite naš kanal za razvijalce na chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Nadaljuj na spletno mesto <ph name="SITE" /> (ni varno)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Tega spletnega mesta ni mogoÄe naložiti iz predpomnilnika</translation>
@@ -626,14 +664,17 @@ Pst! NaslednjiÄ vam lahko pride prav bližnjica <ph name="SHORTCUT_KEY" /> za n
<translation id="7800304661137206267">Povezava je Å¡ifrirana s <ph name="CIPHER" /> in uporablja <ph name="MAC" /> za preverjanje pristnosti sporoÄil ter <ph name="KX" /> kot mehanizem za izmenjavo kljuÄev.</translation>
<translation id="780301667611848630">Ne, hvala</translation>
<translation id="7805768142964895445">Stanje</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Želite odstraniti predlog obrazca iz Chroma?</translation>
<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="7887683347370398519">Preverite CVC in poskusite znova</translation>
<translation id="7912024687060120840">V mapi:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Potrdilo strežnika še ni veljavno.</translation>
<translation id="7942349550061667556">RdeÄa</translation>
+<translation id="7947285636476623132">Preverite leto poteka veljavnosti in poskusite znova</translation>
<translation id="7951415247503192394">(32-bitno)</translation>
<translation id="7956713633345437162">Zaznamki mobilne naprave</translation>
<translation id="7961015016161918242">Nikoli</translation>
@@ -641,7 +682,10 @@ Pst! NaslednjiÄ vam lahko pride prav bližnjica <ph name="SHORTCUT_KEY" /> za n
<translation id="7983301409776629893">Vedno prevedi iz jezika <ph name="ORIGINAL_LANGUAGE" /> v jezik <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Ni navedeno</translation>
<translation id="8012647001091218357">Trenutno ni mogoÄe vzpostaviti stika s starÅ¡i. Poskusi znova pozneje.</translation>
+<translation id="8025119109950072390">Napadalci na tem spletnem mestu 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).</translation>
+<translation id="803030522067524905">Googlova funkcija varnega brskanja je nedavno zaznala lažno predstavljanje na spletnem mestu <ph name="SITE" />. Spletna mesta z lažnim predstavljanjem zavajajo ljudi, tako da se izdajajo za druga spletna mesta. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Ta stran je v jeziku <ph name="SOURCE_LANGUAGE" />. Jo želite prevesti v jezik <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Pošlji povratne informacije</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>
@@ -652,9 +696,11 @@ Pst! NaslednjiÄ vam lahko pride prav bližnjica <ph name="SHORTCUT_KEY" /> za n
<translation id="8150722005171944719">Datoteke na <ph name="URL" /> ni mogoÄe prebrati. Morda je odstranjena, premaknjena ali pa dostop prepreÄujejo dovoljenja za datoteke.</translation>
<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="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="8230421197304563332">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 namestiti nevarne programe, ki kradejo ali briÅ¡ejo podatke (na primer fotografije, gesla, sporoÄila in podatke kreditnih kartic).</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="8249320324621329438">Nazadnje preneseno:</translation>
<translation id="8261506727792406068">Izbriši</translation>
@@ -663,11 +709,13 @@ Pst! NaslednjiÄ vam lahko pride prav bližnjica <ph name="SHORTCUT_KEY" /> za n
<translation id="8294431847097064396">Vir</translation>
<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="8349305172487531364">Vrstica z zaznamki</translation>
<translation id="8363502534493474904">izklopiti naÄin za letalo</translation>
<translation id="8364627913115013041">Ni nastavljen.</translation>
<translation id="8380941800586852976">Nevarno</translation>
<translation id="8382348898565613901">Tu so prikazani nedavno obiskani zaznamki</translation>
+<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="8428213095426709021">Nastavitve</translation>
@@ -679,9 +727,9 @@ Pst! NaslednjiÄ vam lahko pride prav bližnjica <ph name="SHORTCUT_KEY" /> za n
<translation id="8498891568109133222">Spletno mesto <ph name="HOST_NAME" /> se ni odzvalo v ustreznem Äasu.</translation>
<translation id="852346902619691059">Temu strežniku ni uspelo dokazati, da je domena <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. <ph name="BEGIN_LEARN_MORE_LINK" />VeÄ o tem<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Ta vrsta kartice ni podprta v storitvi Google Payments. Izberite drugo kartico.</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="8553075262323480129">Prevod ni uspel, ker ni mogoÄe doloÄiti jezika strani.</translation>
<translation id="8559762987265718583">Zasebne povezave z domeno <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ni mogoÄe vzpostaviti, ker sta datum in ura (<ph name="DATE_AND_TIME" />) v napravi nepravilna.</translation>
-<translation id="856992080682148">Potrdilo za to spletno mesto poteÄe leta 2017 ali pozneje in veriga potrdil vsebuje potrdilo, podpisano z algoritmom SHA-1.</translation>
<translation id="8571890674111243710">Prevajanje strani v jezik <ph name="LANGUAGE" /> ...</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>
@@ -694,10 +742,12 @@ Pst! NaslednjiÄ vam lahko pride prav bližnjica <ph name="SHORTCUT_KEY" /> za n
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;Naslova DNS&lt;/abbr&gt; spletnega mesta <ph name="HOST_NAME" /> ni bilo mogoÄe najti. Poteka diagnosticiranje težave.</translation>
<translation id="8790007591277257123">&amp;Uveljavi izbris</translation>
<translation id="8798099450830957504">Privzeto</translation>
+<translation id="8800988563907321413">Tu so prikazani predlogi v bližini</translation>
<translation id="8804164990146287819">Pravilnik o zasebnosti</translation>
<translation id="8820817407110198400">Zaznamki</translation>
<translation id="8834246243508017242">OmogoÄi samodejno izpolnjevanje s stiki …</translation>
<translation id="883848425547221593">Drugi zaznamki</translation>
+<translation id="884264119367021077">Naslov za pošiljanje</translation>
<translation id="884923133447025588">Najden ni bil noben mehanizem za preklic.</translation>
<translation id="885730110891505394">Deljenje z Googlom</translation>
<translation id="8866481888320382733">Napaka pri razÄlenjevanju nastavitev pravilnika</translation>
@@ -715,18 +765,21 @@ Pst! NaslednjiÄ vam lahko pride prav bližnjica <ph name="SHORTCUT_KEY" /> za n
<translation id="8971063699422889582">Potrdilo strežnika je poteklo.</translation>
<translation id="8987927404178983737">Mesec</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Pozor: Spletno mesto vsebuje Å¡kodljive programe.</translation>
<translation id="9001074447101275817">Strežnik proxy <ph name="DOMAIN" /> zahteva uporabniško ime in geslo.</translation>
<translation id="901974403500617787">Zastavice, ki se uporabijo v celotnem sistemu, lahko nastavi samo lastnik: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Ta stran je prevedena v jezik <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Varnostna napaka</translation>
<translation id="9038649477754266430">Uporaba storitve predvidevanja za hitrejše nalaganje strani</translation>
<translation id="9039213469156557790">Poleg tega so na tej strani druga sredstva, ki niso varna. Ta sredstva lahko med prenosom pregledujejo drugi, morebitni napadalec pa jih lahko spremeni, tako da se spremeni naÄin delovanja strani.</translation>
+<translation id="9040185888511745258">Napadalci na spletnem mestu <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> vas bodo morda poskusili zavesti, da bi namestili programe, ki Å¡kodljivo vplivajo na brskanje (na primer tako, da spremenijo vaÅ¡o domaÄo stran ali na spletnih mestih, ki jih obiÅ¡Äete, prikazujejo dodatne oglase).</translation>
<translation id="9050666287014529139">Geslo</translation>
<translation id="9065203028668620118">Uredi</translation>
<translation id="9068849894565669697">Izbira barve</translation>
<translation id="9076283476770535406">Morda vsebuje vsebino za odrasle</translation>
-<translation id="9092364396508701805">Stran spletnega mesta <ph name="HOST_NAME" /> ne deluje</translation>
<translation id="9103872766612412690">Spletno mesto <ph name="SITE" /> za zaÅ¡Äito vaÅ¡ih podatkov obiÄajno uporablja Å¡ifriranje. Ko se je Chromium tokrat poskusil povezati s spletnim mestom <ph name="SITE" />, je to vrnilo nenavadne in nepravilne poverilnice. Do tega lahko pride, Äe se napadalec lažno predstavlja za spletno mesto <ph name="SITE" /> ali Äe je povezavo prekinil zaslon za prijavo v omrežje Wi-Fi. VaÅ¡i podatki so Å¡e vedno varni, saj je Chromium pred izmenjavo podatkov prekinil povezavo.</translation>
<translation id="9137013805542155359">Pokaži izvirno besedilo</translation>
+<translation id="9137248913990643158">ZaÄnite s prijavo v Chrome, preden zaÄnete uporabljati to aplikacijo.</translation>
<translation id="9148507642005240123">&amp;Razveljavi urejanje</translation>
<translation id="9157595877708044936">Nastavljanje ...</translation>
<translation id="9170848237812810038">&amp;Razveljavi</translation>
@@ -739,7 +792,6 @@ Pst! NaslednjiÄ vam lahko pride prav bližnjica <ph name="SHORTCUT_KEY" /> za n
<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="962701380617707048">Vnesite datum poteka in CVC za <ph name="CREDIT_CARD" />, da posodobite podatke o kartici.</translation>
<translation id="969892804517981540">Uradna razliÄica</translation>
<translation id="988159990683914416">RazliÄica za razvijalce</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 972d68f6f1f..6ffeffdaf3c 100644
--- a/chromium/components/strings/components_strings_sr.xtb
+++ b/chromium/components/strings/components_strings_sr.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">поново да Ñе повежете Ñа Wi-Fi мрежом</translation>
<translation id="1175364870820465910">&amp;Одштампај...</translation>
<translation id="1181037720776840403">Уклони</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />ÐутоматÑки пријави<ph name="END_WHITEPAPER_LINK" /> Google-у детаље о могућим безбедноÑним инцидентима. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Даље</translation>
<translation id="1201895884277373915">Више Ñа овог Ñајта</translation>
<translation id="1206967143813997005">ÐеиÑправан Ð¿Ð¾Ñ‚Ð¿Ð¸Ñ Ð¸Ð½Ð¸Ñ†Ð¸Ñ˜Ð°Ð»Ð¸Ð¼Ð°</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Плавозелена</translation>
<translation id="1629803312968146339">Желите ли да Chrome Ñачува ову картицу?</translation>
<translation id="1640180200866533862">Смернице за кориÑнике</translation>
+<translation id="1640244768702815859">Покушајте да <ph name="BEGIN_LINK" />поÑетите почетну Ñтраницу Ñајта<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Конфигурација мреже је неважећа и не може да Ñе увезе.</translation>
<translation id="1644574205037202324">ИÑторија</translation>
<translation id="1645368109819982629">Ðеподржани протокол</translation>
<translation id="1676269943528358898"><ph name="SITE" /> обично кориÑти шифровање да би заштитио информације. Када је Google Chrome овог пута покушао да Ñе повеже Ñа <ph name="SITE" />, веб-Ñајт је вратио необичне и нетачне акредитиве. Или нападач покушава да Ñе предÑтави као <ph name="SITE" /> или је екран за Wi-Fi пријављивање прекинуо везу. Информације Ñу и даље безбедне зато што је Google Chrome прекинуо везу пре него што Ñу размењени било какви подаци.</translation>
+<translation id="168328519870909584">Ðападачи који Ñу тренутно на <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ће можда покушати да инÑталирају опаÑне апликације на уређај које краду или бришу податке (на пример, Ñлике, лозинке, поруке и бројеве кредитних картица).</translation>
<translation id="168841957122794586">Сертификат Ñервера Ñадржи Ñлаб криптографÑки кључ.</translation>
-<translation id="1701955595840307032">Добијајте предложени Ñадржај</translation>
<translation id="1710259589646384581">ОС</translation>
<translation id="1721312023322545264">Потребна вам је дозвола кориÑника <ph name="NAME" /> да биÑте поÑетили овај Ñајт</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Број Ñтранице</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Покушајте да покренете Windows дијагноÑтику мреже<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Ðжурирај приÑтупну фразу за Ñинхронизацију</translation>
+<translation id="1787142507584202372">Отворене картице Ñе појављују овде</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google безбедно прегледање је недавно <ph name="BEGIN_LINK" />открило малвер<ph name="END_LINK" /> на <ph name="SITE" />. Веб-Ñајтови који Ñу обично безбедни Ñе понекад заразе малвером. Злонамеран Ñадржај потиче Ñа <ph name="SUBRESOURCE_HOST" />, који је познати диÑтрибутер малвера. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Ðеважећи захтев или параметри захтева</translation>
+<translation id="1834321415901700177">Овај Ñајт Ñадржи штетне програме</translation>
<translation id="1838667051080421715">Прегледате извор веб-Ñтранице.</translation>
<translation id="1871208020102129563">ПрокÑи је подешен да кориÑти фикÑне прокÑи Ñервере, а не URL адреÑу .pac Ñкрипте.</translation>
<translation id="1883255238294161206">Скупи лиÑту</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Обележивачи на мобилном уређају</translation>
<translation id="2148716181193084225">ДанаÑ</translation>
-<translation id="2149973817440762519">Измените обележивач</translation>
<translation id="2154054054215849342">Синхронизација није доÑтупна за домен</translation>
<translation id="2166049586286450108">Потпуни админиÑтраторÑки приÑтуп</translation>
<translation id="2166378884831602661">Овај Ñајт не може да пружи безбедну везу</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Тренутно не можете да поÑетите <ph name="SITE" /> зато што веб-Ñајт кориÑти HSTS. Грешке и напади на мрежи Ñу обично привремени, па ће ова Ñтраница вероватно функциониÑати каÑније. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Ðеважећи обележивач је игнориÑан у индекÑу <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">ОÑтали обележивачи</translation>
+<translation id="2355395290879513365">Ðападачи ће моћи да виде Ñлике које гледате на овом Ñајту и да их измене како би Ð²Ð°Ñ Ð¿Ñ€ÐµÐ²Ð°Ñ€Ð¸Ð»Ð¸.</translation>
<translation id="2359808026110333948">ÐаÑтави</translation>
<translation id="2365563543831475020">Извештај о отказивању Ñнимљен у <ph name="CRASH_TIME" /> није отпремљен</translation>
<translation id="2367567093518048410">Ðиво</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Прикажи Ñмернице без подешених вредноÑти</translation>
<translation id="2396249848217231973">&amp;Опозови бриÑање</translation>
<translation id="2455981314101692989">Ова веб-Ñтраница је онемогућила аутоматÑко попуњавање за овај образац.</translation>
+<translation id="2460160116472764928">Google безбедно прегледање је недавно <ph name="BEGIN_LINK" />открило малвер<ph name="END_LINK" /> на <ph name="SITE" />. Веб-Ñајтови који Ñу обично безбедни Ñе понекад заразе малвером. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Попуни</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />да покренете дијагноÑтику мреже<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Ðеважећа URL адреÑа претраге.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Желите ли Ñтварно да избришете ове Ñтранице из иÑторије?</translation>
<translation id="2677748264148917807">Затвори</translation>
<translation id="269990154133806163">Сервер је поÑлао Ñертификат који није јавно откривен помоћу Ñмерница за транÑпарентноÑÑ‚ Ñертификата. То је обавезно за неке Ñертификате да биÑмо Ñе уверили да Ñу поуздани и да штите од нападача. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">ЛиÑта за читање</translation>
<translation id="2704283930420550640">ВредноÑÑ‚ Ñе не подудара Ñа форматом.</translation>
<translation id="2704951214193499422">Chromium није уÑпео да потврди картицу. Пробајте поново каÑније.</translation>
<translation id="2705137772291741111">Сачувана (кеширана) копија овог Ñајта није могла да Ñе чита.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Покушали Ñте да поÑетите <ph name="DOMAIN" />, али је издавач опозвао Ñертификат који је Ñервер поÑлао. То значи да никако не треба да имате поверења у безбедноÑне акредитиве које је Ñервер поÑлао. Могуће је да комуницирате Ñа нападачем. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Затражи дозволу</translation>
<translation id="2713444072780614174">Бела</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Ðа Ñтраници Подешавања можете да онемогућите Ñве прокÑије конфигуриÑане за везу.</translation>
<translation id="2955913368246107853">Затворите траку за проналажење</translation>
<translation id="2958431318199492670">Конфигурација мреже није у Ñкладу Ñа ONC Ñтандардом. Делови конфигурације не могу да Ñе увезу.</translation>
+<translation id="29611076221683977">Ðападачи који Ñу тренутно на <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ће можда покушати да инÑталирају опаÑне програме на Mac-у који краду или бришу податке (на пример, Ñлике, лозинке, поруке и бројеве кредитних картица).</translation>
<translation id="2969319727213777354">Да биÑте уÑпоÑтавили безбедну везу, Ñат на уређају мора да буде тачан. То је зато што Ñертификати које веб-Ñајтови кориÑте за идентификацију важе Ñамо за одређене временÑке периоде. Пошто Ñат на вашем уређају није тачан, Google Chrome не може да верификује те Ñертификате.</translation>
<translation id="2972581237482394796">&amp;Понови радњу</translation>
<translation id="2985306909656435243">Ðко омогућите ову опцију, Chromium ће Ñкладиштити копију картице на овом уређају ради бржег попуњавања образаца.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Сакриј детаље</translation>
<translation id="3587482841069643663">Све</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Сервер не подржава додатак за поновно прихватање TLS-а.</translation>
<translation id="36224234498066874">Обриши податке прегледања...</translation>
<translation id="362276910939193118">Прикажи комплетну иÑторију</translation>
<translation id="3623476034248543066">Прикажи вредноÑÑ‚</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">Ðко вам Ñе ово чеÑто приказује, можда вам помогну Ñледећи <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">Ревизија</translation>
<translation id="3678029195006412963">ПотпиÑивање захтева није уÑпело</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Ðема више лиценци</translation>
<translation id="3714780639079136834">да укључите податке за мобилне уређаје или Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />да проверите конфигурацију прокÑија, заштитног зида и DNS-а<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Ðко разумете безбедноÑне ризике, можете да <ph name="BEGIN_LINK" />поÑетите овај небезбедни Ñајт<ph name="END_LINK" /> пре него што уклонимо опаÑне програме.</translation>
<translation id="3739623965217189342">Линк који Ñте копирали</translation>
<translation id="375403751935624634">Превођење није уÑпело због грешке Ñервера.</translation>
<translation id="3759461132968374835">Ðемате ниједно недавно пријављено отказивање. Отказивања која Ñу Ñе деÑила док је пријављивање отказивања било онемогућено неће Ñе овде приказати.</translation>
-<translation id="3788090790273268753">Сертификат за овај Ñајт иÑтиче 2016, а ланац Ñертификата Ñадржи Ñертификат потпиÑан помоћу алгоритма SHA-1.</translation>
<translation id="382518646247711829">Ðко кориÑтите прокÑи Ñервер...</translation>
<translation id="3828924085048779000">Ðије дозвољено да поље за приÑтупну фразу буде празно.</translation>
<translation id="3845539888601087042">Приказујемо иÑторију Ñа уређаја на које Ñте пријављени. <ph name="BEGIN_LINK" />Сазнајте више<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Кључ „<ph name="SUBKEY" />“: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Клијент и Ñервер не подржавају иÑту верзију SSL протокола или пакет за шифровање.</translation>
<translation id="4079302484614802869">Конфигурација прокÑија је подешена да кориÑти URL адреÑу .pac Ñкрипте, а не фикÑне прокÑи Ñервере.</translation>
+<translation id="4098354747657067197">Пред вама је обмањујући Ñајт</translation>
<translation id="4103249731201008433">СеријÑки број уређаја је неважећи</translation>
<translation id="4103763322291513355">ПоÑетите &lt;strong&gt;chrome://policy&lt;/strong&gt; да биÑте видели лиÑту URL-ова Ñтављених на црну лиÑту и друге Ñмернице које је наметнуо админиÑтратор ÑиÑтема.</translation>
<translation id="4110615724604346410">Овај Ñервер не може да докаже да је <ph name="DOMAIN" />; његов безбедноÑни Ñертификат Ñадржи грешке. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ниједна}=1{1 апликација ($1)}=2{2 апликације ($1, $2)}one{# апликација ($1, $2, $3)}few{# апликације ($1, $2, $3)}other{# апликација ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Отказивања</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Покушајте да покренете дијагноÑтику мреже<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Веза Ñа овим Ñајтом није потпуно безбедна</translation>
<translation id="4250680216510889253">Ðе</translation>
<translation id="425582637250725228">Промене које Ñте унели можда неће бити Ñачуване.</translation>
<translation id="4258748452823770588">ÐеиÑправан потпиÑ</translation>
<translation id="4269787794583293679">(Без кориÑничког имена)</translation>
+<translation id="4280429058323657511">, иÑтиче <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google безбедно прегледање је недавно <ph name="BEGIN_LINK" />открило штетне програме<ph name="END_LINK" /> на <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Предлози родитеља</translation>
<translation id="4304224509867189079">Пријављивање</translation>
<translation id="432290197980158659">Сервер је поÑлао Ñертификат који Ñе не подудара Ñа уграђеним очекивањима. Та очекивања Ñу обухваћена због одређених веб-Ñајтова Ñа јаким безбедноÑним мерама како би Ð²Ð°Ñ Ð·Ð°ÑˆÑ‚Ð¸Ñ‚Ð¸Ð»Ð°. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">ÐиÑмо уÑпели да пронађемо чланак</translation>
+<translation id="4326324639298822553">Проверите датум иÑтека и пробајте поново</translation>
<translation id="4331708818696583467">Ðије безбедан</translation>
+<translation id="4356973930735388585">Ðападачи на овом Ñајту ће можда покушати да инÑталирају опаÑне програме на рачунару који краду или бришу информације (на пример, Ñлике, лозинке, поруке и бројеве кредитних картица).</translation>
<translation id="4372948949327679948">Очекивана вредноÑÑ‚ je <ph name="VALUE_TYPE" />.</translation>
<translation id="4381091992796011497">Име кориÑника:</translation>
<translation id="4394049700291259645">Онемогући</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Покушали Ñте да поÑетите <ph name="DOMAIN" />, али је Ñервер поÑлао неважећи Ñертификат. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">Веза Ñа доменом <ph name="DOMAIN" /> је шифрована помоћу модерног пакета за шифровање.</translation>
<translation id="4594403342090139922">&amp;Опозови бриÑање</translation>
+<translation id="4619615317237390068">Картице Ñа других уређаја</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Прегледате Ñтраницу Ñа додацима.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Поново учитај Ñмернице</translation>
<translation id="4728558894243024398">Платформа</translation>
<translation id="4744603770635761495">Путања извршне датотеке</translation>
+<translation id="4750917950439032686">Информације (на пример, лозинке или бројеви кредитних картица) Ñу приватне када Ñе шаљу овом Ñајту.</translation>
<translation id="4756388243121344051">&amp;ИÑторија</translation>
+<translation id="4759118997339041434">ÐутоматÑко попуњавање плаћања је онемогућено</translation>
<translation id="4764776831041365478">Могуће је да веб-Ñтраница на адреÑи <ph name="URL" /> привремено не функционише или да је трајно премештена на нову веб адреÑу.</translation>
<translation id="4771973620359291008">Дошло је до непознате грешке.</translation>
<translation id="4800132727771399293">Проверите датум иÑтека и CVC и покушајте поново</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Грешка на мрежи</translation>
<translation id="4816492930507672669">Уклопи у Ñтраницу</translation>
<translation id="4850886885716139402">Приказ</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{и јој 1 веб-Ñтраница}one{и још # веб-Ñтраница}few{и још # веб-Ñтранице}other{и још # веб-Ñтраница}}</translation>
<translation id="4923417429809017348">Ова Ñтраница је преведена Ñа непознатог језика на <ph name="LANGUAGE_LANGUAGE" /></translation>
+<translation id="4923459931733593730">Плаћање</translation>
<translation id="4926049483395192435">Мора да буде наведено.</translation>
<translation id="495170559598752135">Радње</translation>
<translation id="4958444002117714549">Прошири лиÑту</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Преузимање</translation>
<translation id="5190835502935405962">Трака Ñа обележивачима</translation>
<translation id="5199729219167945352">ЕкÑперименти</translation>
-<translation id="5199841536747119669">Предлози ће Ñе приказивати овде</translation>
<translation id="5251803541071282808">Клауд</translation>
<translation id="5277279256032773186">Да ли кориÑтите Chrome на поÑлу? Предузеће може да управља подешавањима Chrome-а за запоÑлене. Сазнајте више</translation>
<translation id="5299298092464848405">Грешка при рашчлањивању Ñмерница</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Извештавање о отказивању је онемогућено.</translation>
<translation id="5317780077021120954">Сачувај</translation>
<translation id="5327248766486351172">Ðазив</translation>
+<translation id="5337705430875057403">Ðападачи на <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> могу да Ð²Ð°Ñ Ð¿Ñ€ÐµÐ²Ð°Ñ€Ðµ како биÑте урадили нешто опаÑно, на пример, да инÑталирате Ñофтвер или откријете личне податке (попут лозинки, бројева телефона или бројева кредитних картица).</translation>
<translation id="5359637492792381994">Овај Ñервер не може да докаже да је <ph name="DOMAIN" />; његов безбедноÑни Ñертификат тренутно није важећи. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Складиштење подешавања Ñмерница није уÑпело</translation>
+<translation id="5386426401304769735">Ланац Ñертификата за овај Ñајт Ñадржи Ñертификат потпиÑан помоћу алгоритма SHA-1.</translation>
<translation id="5421136146218899937">Обриши податке прегледања...</translation>
<translation id="5430298929874300616">Уклоните обележивач</translation>
<translation id="5431657950005405462">Датотека није пронађена</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Уграђена Ñтраница на <ph name="SITE" /> каже:</translation>
<translation id="5556459405103347317">Учитај поново</translation>
<translation id="5565735124758917034">Ðктивно</translation>
+<translation id="5572851009514199876">Отворите и пријавите Ñе у Chrome да би Chrome могао да провери да ли имате дозволу за приÑтуп овом Ñајту.</translation>
+<translation id="5580958916614886209">Проверите меÑец иÑтека и пробајте поново</translation>
<translation id="560412284261940334">Управљање није подржано</translation>
<translation id="5610142619324316209">да проверите везу</translation>
<translation id="5617949217645503996">ХоÑÑ‚ <ph name="HOST_NAME" /> Ð²Ð°Ñ Ñ˜Ðµ преуÑмерио превелики број пута.</translation>
<translation id="5622887735448669177">Желите ли да напуÑтите овај Ñајт?</translation>
<translation id="5629630648637658800">Учитавање подешавања Ñмерница није уÑпело</translation>
<translation id="5631439013527180824">Ðеважећи токен за управљање уређајима</translation>
+<translation id="5669703222995421982">Добијте перÑонализовани Ñадржај</translation>
+<translation id="5675650730144413517">Ова Ñтраница не функционише</translation>
<translation id="5677928146339483299">Блокирано</translation>
<translation id="5694783966845939798">Покушали Ñте да поÑетите <ph name="DOMAIN" />, али је Ñервер поÑлао Ñертификат потпиÑан Ñлабим алгоритмом (као што је SHA-1). То значи да Ñу безбедноÑни акредитиви које је Ñервер поÑлао можда кривотворени и Ñервер можда није онај који миÑлите да јеÑте (можда комуницирате Ñа нападачем). <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Идентитет овог веб Ñајта није верификован.</translation>
<translation id="5720705177508910913">Тренутни кориÑник</translation>
-<translation id="572328651809341494">Ðедавно коришћене картице</translation>
<translation id="5732392974455271431">Родитељи могу да га деблокирају за тебе</translation>
<translation id="5784606427469807560">Дошло је до проблема при потврди картице. Проверите интернет везу и покушајте поново.</translation>
<translation id="5785756445106461925">Поред тога, ова Ñтраница Ñадржи и друге реÑурÑе који ниÑу безбедни. Ове реÑурÑе могу да виде и други док Ñу у пролазу и нападач може да их измени како би променио изглед Ñтранице.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Покушали Ñте да поÑетите <ph name="DOMAIN" />, али је Ñервер поÑлао Ñертификат који Ñадржи Ñлаб кључ. Могуће је да је нападач открио приватни кључ и да Ñервер можда није онај који миÑлите да јеÑте (можда комуницирате Ñа нападачем). <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Ðови директоријум</translation>
<translation id="5869405914158311789">Овај Ñајт није доÑтупан</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Google Payments не подржава овај тип картице за овог продавца. Изаберите неку другу картицу.</translation>
<translation id="59174027418879706">Омогућено</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="5966707198760109579">Ðедеља</translation>
<translation id="5967867314010545767">Уклони из иÑторије</translation>
<translation id="5975083100439434680">Умањивање</translation>
@@ -470,8 +496,11 @@
<translation id="614940544461990577">Покушајте:</translation>
<translation id="6151417162996330722">Сертификат Ñервера има предугачак период важења.</translation>
<translation id="6165508094623778733">Сазнајте више</translation>
+<translation id="6177128806592000436">Веза Ñа овим Ñајтом није безбедна</translation>
<translation id="6203231073485539293">Проверите интернет везу</translation>
<translation id="6218753634732582820">Желите ли да уклоните адреÑу из Chromium-а?</translation>
+<translation id="6251924700383757765">Политика приватноÑти</translation>
+<translation id="625755898061068298">Онемогућили Ñте безбедноÑна упозорења за овај Ñајт.</translation>
<translation id="6259156558325130047">&amp;Понови промену редоÑледа</translation>
<translation id="6263376278284652872">Обележивачи домена <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Ðазад на безбедно</translation>
@@ -480,6 +509,7 @@
<translation id="6305205051461490394">URL <ph name="URL" /> није доÑтупан.</translation>
<translation id="6321917430147971392">Проверите DNS подешавања</translation>
<translation id="6328639280570009161">Покушајте да онемогућите предвиђање мреже</translation>
+<translation id="6328786501058569169">Овај Ñајт је обмањујућ</translation>
<translation id="6337534724793800597">Филтрирај Ñмернице према називу</translation>
<translation id="6342069812937806050">Малопре</translation>
<translation id="6345221851280129312">непозната величина</translation>
@@ -488,6 +518,8 @@
<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="6404511346730675251">Измена обележивача</translation>
+<translation id="6410264514553301377">УнеÑите датум иÑтека и CVC за картицу <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Питао/ла Ñи родитеља да ли Ñмеш да поÑетиш овај Ñајт</translation>
<translation id="6416403317709441254">Тренутно не можете да поÑетите <ph name="SITE" /> јер је веб-Ñајт поÑлао кодиране акредитиве које Chromium не може да обради. Грешке на мрежи и напади Ñу углавном привремени, па ће Ñтраница вероватно функциониÑати каÑније. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Ðије могуће проверити да ли је Ñертификат опозван.</translation>
@@ -497,10 +529,12 @@
<translation id="6458467102616083041">Занемарује Ñе зато што ове Ñмернице онемогућавају подразумевану претрагу.</translation>
<translation id="6462969404041126431">Овај Ñервер не може да докаже да је <ph name="DOMAIN" />; његов безбедноÑни Ñертификат је можда опозван. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">Смернице за уређај</translation>
+<translation id="6477321094435799029">Chrome је открио неуобичајени кôд на овој Ñтраници и блокирао је ради заштите ваших личних података (попут лозинки, бројева телефона или бројева кредитних картица).</translation>
<translation id="6489534406876378309">Покрени отпремање отказивања</translation>
<translation id="6529602333819889595">&amp;Понови бриÑање</translation>
<translation id="6534179046333460208">Предлози Интернета око наÑ</translation>
<translation id="6550675742724504774">Опције</translation>
+<translation id="6556239504065605927">Безбедна веза</translation>
<translation id="6563469144985748109">Менаџер га још увек није одобрио</translation>
<translation id="6593753688552673085">мање од <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Опције шифровања</translation>
@@ -509,7 +543,6 @@
<translation id="6644283850729428850">Ове Ñмернице Ñу заÑтареле.</translation>
<translation id="6652240803263749613">Овај Ñервер не може да докаже да је <ph name="DOMAIN" />; оперативни ÑиÑтем вашег рачунара нема поверења у његов безбедноÑни Ñертификат. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Измена директоријума</translation>
-<translation id="6660210980321319655">ÐутоматÑки пријављено <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Желите ли да уклоните предлог из Chromium-а?</translation>
<translation id="6685834062052613830">Одјавите Ñе и довршите подешавање</translation>
<translation id="6710213216561001401">Претходно</translation>
@@ -521,6 +554,7 @@
<translation id="6753269504797312559">ВредноÑÑ‚ Ñмерница</translation>
<translation id="6757797048963528358">Уређај је прешао у режим Ñпавања.</translation>
<translation id="6778737459546443941">Родитељ га још увек није одобрио</translation>
+<translation id="6810899417690483278">ИД за прилагођавање</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Веб-Ñтраница на адреÑи <ph name="URL" /> тренутно је недоÑтупна. Можда је преоптерећена или неактивна због одржавања.</translation>
<translation id="6831043979455480757">Преведи</translation>
@@ -541,6 +575,8 @@
<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>
+<translation id="7064851114919012435">Контакт информације</translation>
+<translation id="7079718277001814089">Овај Ñајт Ñадржи малвер</translation>
<translation id="7087282848513945231">Округ</translation>
<translation id="7088615885725309056">Старије</translation>
<translation id="7090678807593890770">Потражите <ph name="LINK" /> на Google-у</translation>
@@ -555,6 +591,7 @@
<translation id="7210863904660874423">ХоÑÑ‚ <ph name="HOST_NAME" /> не поштује безбедноÑне Ñтандарде.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Сазнајте више<ph name="END_LINK" /> о овом проблему.</translation>
<translation id="7219179957768738017">Веза кориÑти <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Сајт који ћете поÑетити Ñадржи малвер</translation>
<translation id="724975217298816891">УнеÑите рок трајања и CVC за картицу <ph name="CREDIT_CARD" /> да биÑте ажурирали податке о картици. Када будете потврдили, подаци о картици ће бити поÑлати овом Ñајту.</translation>
<translation id="725866823122871198">Ðије могуће уÑпоÑтавити приватну везу Ñа доменом <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> јер време и датум на рачунару (<ph name="DATE_AND_TIME" />) ниÑу тачни.</translation>
<translation id="7269802741830436641">Ова веб-Ñтраница има петљу за преуÑмеравање</translation>
@@ -610,6 +647,7 @@
<translation id="7658239707568436148">Откажи</translation>
<translation id="7667346355482952095">Враћени токен Ñмерница је празан или Ñе не подудара Ñа актуелним токеном</translation>
<translation id="7668654391829183341">Ðепознат уређај</translation>
+<translation id="7669271284792375604">Ðападачи на овом Ñајту могу да покушају да Ð²Ð°Ñ Ð¿Ñ€ÐµÐ²Ð°Ñ€Ðµ да биÑте инÑталирали програме који штете доживљају прегледања (на пример, тако што мењају почетну Ñтраницу или приказују додатне оглаÑе на Ñајтовима које поÑећујете).</translation>
<translation id="7674629440242451245">ИнтереÑују Ð²Ð°Ñ Ð½Ð¾Ð²Ðµ занимљиве Chrome функције? ИÑпробајте програмерÑки канал на chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />ÐаÑтави на <ph name="SITE" /> (није безбедно)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Ðије могуће учитати овај Ñајт из кеша</translation>
@@ -625,14 +663,17 @@
<translation id="7800304661137206267">Веза је шифрована помоћу <ph name="CIPHER" />, Ñа <ph name="MAC" /> за потврду идентитета поруке и <ph name="KX" /> као механизмом за размену кључева.</translation>
<translation id="780301667611848630">Ðе, хвала</translation>
<translation id="7805768142964895445">СтатуÑ</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Желите ли да уклоните предлог из Chrome-а?</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="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="7887683347370398519">Проверите CVC и покушајте поново</translation>
<translation id="7912024687060120840">У директоријуму:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Сертификат Ñервера још увек није важећи.</translation>
<translation id="7942349550061667556">Црвена</translation>
+<translation id="7947285636476623132">Проверите годину иÑтека и пробајте поново</translation>
<translation id="7951415247503192394">(32-битни)</translation>
<translation id="7956713633345437162">Обележивачи на мобилном уређају</translation>
<translation id="7961015016161918242">Ðикад</translation>
@@ -640,7 +681,10 @@
<translation id="7983301409776629893">Увек преводи Ñа језика: <ph name="ORIGINAL_LANGUAGE" /> на <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Ðије наведено</translation>
<translation id="8012647001091218357">Тренутно не можемо да контактирамо родитеље. Пробај поново.</translation>
+<translation id="8025119109950072390">Ðападачи на овом Ñајту могу да Ð²Ð°Ñ Ð¿Ñ€ÐµÐ²Ð°Ñ€Ðµ да биÑте урадили нешто опаÑно, на пример, да инÑталирате Ñофтвер или откријете личне податке (попут лозинки, бројева телефона или бројева кредитних картица).</translation>
+<translation id="803030522067524905">Google безбедно прегледање је недавно открило „пецање“ на <ph name="SITE" />. Сајтови Ñа „пецањем“ Ñе предÑтављају као неки други веб-Ñајтови да би Ð²Ð°Ñ Ð¿Ñ€ÐµÐ²Ð°Ñ€Ð¸Ð»Ð¸. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Језик ове Ñтранице је <ph name="SOURCE_LANGUAGE" />. Желите ли да је преведете на <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Пошаљи повратне информације</translation>
<translation id="8088680233425245692">Прегледање чланка није уÑпело.</translation>
<translation id="8089520772729574115">мање од 1 MB</translation>
<translation id="8091372947890762290">Ðктивација је на чекању на Ñерверу</translation>
@@ -651,9 +695,11 @@
<translation id="8150722005171944719">Датотека на адреÑи <ph name="URL" /> не може да Ñе чита. Можда је уклоњена или премештена или дозволе за датотеке Ñпречавају приÑтуп.</translation>
<translation id="8194797478851900357">&amp;Опозови премештање</translation>
<translation id="8201077131113104583">Ðеважећи URL за ажурирање за додатак Ñа ИД-ом „<ph name="EXTENSION_ID" />“.</translation>
+<translation id="8202097416529803614">Резиме поруџбине</translation>
<translation id="8218327578424803826">Додељена локација:</translation>
<translation id="8225771182978767009">ОÑоба која је подеÑила овај рачунар је одлучила да блокира овај Ñајт.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">Ðападачи који Ñу тренутно на <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> ће можда покушати да на ваш рачунар инÑталирају опаÑне програме који краду или бришу податке (на пример, Ñлике, лозинке, поруке и бројеве кредитних картица).</translation>
<translation id="8241707690549784388">Страница коју тражите кориÑтила је информације које Ñте унели. Повратак на ту Ñтраницу може да проузрокује потребну понављања радњи које Ñте извршили. Желите ли да наÑтавите?</translation>
<translation id="8249320324621329438">ПоÑледње учитано:</translation>
<translation id="8261506727792406068">Избриши</translation>
@@ -662,11 +708,13 @@
<translation id="8294431847097064396">Извор</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="8349305172487531364">Трака Ñа обележивачима</translation>
<translation id="8363502534493474904">да иÑкључите режим рада у авиону</translation>
<translation id="8364627913115013041">Ðије подешено.</translation>
<translation id="8380941800586852976">ОпаÑно</translation>
<translation id="8382348898565613901">Ðедавно поÑећени обележивачи Ñе приказују овде</translation>
+<translation id="8398259832188219207">Извештај о отказивању је отпремљен у: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Отказивања (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Потребно је да двапут унеÑете иÑту приÑтупну фразу.</translation>
<translation id="8428213095426709021">Подешавања</translation>
@@ -678,9 +726,9 @@
<translation id="8498891568109133222">Одговор хоÑта <ph name="HOST_NAME" /> је трајао предуго.</translation>
<translation id="852346902619691059">Овај Ñервер не може да докаже да је <ph name="DOMAIN" />; оперативни ÑиÑтем вашег уређаја нема поверења у његов безбедноÑни Ñертификат. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу. <ph name="BEGIN_LEARN_MORE_LINK" />Сазнајте више<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Google Payments не подржава овај тип картице. Изаберите неку другу картицу.</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="8553075262323480129">Превод није уÑпео јер језик Ñтранице није могао да буде утврђен.</translation>
<translation id="8559762987265718583">Ðије могуће уÑпоÑтавити приватну везу Ñа доменом <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> јер датум и време на уређају (<ph name="DATE_AND_TIME" />) ниÑу тачни.</translation>
-<translation id="856992080682148">Сертификат за овај Ñајт иÑтиче 2017, а ланац Ñертификата Ñадржи Ñертификат потпиÑан помоћу алгоритма SHA-1.</translation>
<translation id="8571890674111243710">Превођење Ñтранице на <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Сертификат не наводи механизам којим Ñе проверава да ли је опозван.</translation>
<translation id="8620436878122366504">Родитељи га још увек ниÑу одобрили</translation>
@@ -693,10 +741,12 @@
<translation id="8740359287975076522">ÐиÑмо уÑпели да пронађемо &lt;abbr id="dnsDefinition"&gt;DNS адреÑу&lt;/abbr&gt; хоÑта <ph name="HOST_NAME" />. Покушавамо да утврдимо у чему је проблем.</translation>
<translation id="8790007591277257123">&amp;Понови бриÑање</translation>
<translation id="8798099450830957504">Подразумевано</translation>
+<translation id="8800988563907321413">Предлози у близини ће Ñе приказивати овде</translation>
<translation id="8804164990146287819">Политика приватноÑти</translation>
<translation id="8820817407110198400">Обележивачи</translation>
<translation id="8834246243508017242">Омогућавање аутоматÑког попуњавања контактима…</translation>
<translation id="883848425547221593">ОÑтали обележивачи</translation>
+<translation id="884264119367021077">ÐдреÑа за Ñлање</translation>
<translation id="884923133447025588">Ðије пронађен ниједан механизам опозива.</translation>
<translation id="885730110891505394">Дељење Ñа Google-ом</translation>
<translation id="8866481888320382733">Грешка при рашчлањивању подешавања Ñмерница</translation>
@@ -714,18 +764,21 @@
<translation id="8971063699422889582">Сертификат Ñервера је иÑтекао.</translation>
<translation id="8987927404178983737">МеÑец</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Сајт који ћете поÑетити Ñадржи штетне програме</translation>
<translation id="9001074447101275817">ПрокÑи <ph name="DOMAIN" /> захтева кориÑничко име и лозинку.</translation>
<translation id="901974403500617787">Ознаке које Ñе примењују у целом ÑиÑтему може да подеÑи Ñамо влаÑник: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Ова Ñтраница је преведена на <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">БезбедноÑна грешка</translation>
<translation id="9038649477754266430">КориÑтите уÑлугу предвиђања да биÑте брже учитавали Ñтранице</translation>
<translation id="9039213469156557790">Поред тога, ова Ñтраница Ñадржи и друге реÑурÑе који ниÑу безбедни. Ове реÑурÑе могу да виде и други док Ñу у пролазу и нападач може да их измени како би променио понашање Ñтранице.</translation>
+<translation id="9040185888511745258">Ðападачи на <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> могу да покушају да Ð²Ð°Ñ Ð¿Ñ€ÐµÐ²Ð°Ñ€Ðµ да биÑте инÑталирали програме који штете доживљају прегледања (на пример, тако што мењају почетну Ñтраницу или приказују додатне оглаÑе на Ñајтовима које поÑећујете).</translation>
<translation id="9050666287014529139">ПриÑтупна фраза</translation>
<translation id="9065203028668620118">Измени</translation>
<translation id="9068849894565669697">Изаберите боју</translation>
<translation id="9076283476770535406">Можда обухвата Ñадржај за одраÑле</translation>
-<translation id="9092364396508701805">Страница хоÑта <ph name="HOST_NAME" /> не функционише</translation>
<translation id="9103872766612412690"><ph name="SITE" /> обично кориÑти шифровање да би заштитио информације. Када је Chromium овог пута покушао да Ñе повеже Ñа <ph name="SITE" />, веб-Ñајт је вратио необичне и нетачне акредитиве. Или нападач покушава да Ñе предÑтави као <ph name="SITE" /> или је екран за Wi-Fi пријављивање прекинуо везу. Информације Ñу и даље безбедне зато што је Chromium прекинуо везу пре него што Ñу размењени било какви подаци.</translation>
<translation id="9137013805542155359">Прикажи оригинал</translation>
+<translation id="9137248913990643158">Пријавите Ñе у Chrome пре коришћења ове апликације.</translation>
<translation id="9148507642005240123">&amp;Опозови измену</translation>
<translation id="9157595877708044936">Подешавање...</translation>
<translation id="9170848237812810038">&amp;Опозови</translation>
@@ -738,7 +791,6 @@
<translation id="935608979562296692">ОБРИШИ ОБРÐЗÐЦ</translation>
<translation id="939736085109172342">Ðови директоријум</translation>
<translation id="941721044073577244">Изгледа да немате дозволу да поÑетите овај Ñајт</translation>
-<translation id="962701380617707048">УнеÑите датум иÑтека и CVC за картицу <ph name="CREDIT_CARD" /> да биÑте ажурирали податке о картици</translation>
<translation id="969892804517981540">Званична верзија</translation>
<translation id="988159990683914416">Верзија за програмере</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 eaee4210b63..9d46ef01832 100644
--- a/chromium/components/strings/components_strings_sv.xtb
+++ b/chromium/components/strings/components_strings_sv.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Ã¥teransluta till Wi-Fi</translation>
<translation id="1175364870820465910">Skriv &amp;ut...</translation>
<translation id="1181037720776840403">Ta bort</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Rapportera uppgifter<ph name="END_WHITEPAPER_LINK" /> om möjliga säkerhetsincidenter till Google automatiskt. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Nästa</translation>
<translation id="1201895884277373915">Mer från den här webbplatsen</translation>
<translation id="1206967143813997005">Felaktig första signatur</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Cyanblå</translation>
<translation id="1629803312968146339">Vill du att Chrome sparar det här kortet?</translation>
<translation id="1640180200866533862">Användarpolicyer</translation>
+<translation id="1640244768702815859">Testa att <ph name="BEGIN_LINK" />besöka webbplatsens startsida<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Nätverkskonfigurationen är ogiltig och kan inte importeras.</translation>
<translation id="1644574205037202324">Historik</translation>
<translation id="1645368109819982629">Det finns inget stöd för protokollet</translation>
<translation id="1676269943528358898">På <ph name="SITE" /> används vanligtvis kryptering (SSL) för att skydda din information. När Chrome försökte ansluta till <ph name="SITE" /> den här gången skickade webbplatsen tillbaka ovanliga och felaktiga uppgifter. Sådant kan hända när en angripare utger sig för att vara <ph name="SITE" /> eller när anslutningen har avbrutits av en Wi-Fi-inloggningsskärm. Din information är fortfarande säker eftersom Chrome avbröt anslutningen innan någon data utbyttes.</translation>
+<translation id="168328519870909584">Det kan hända att angripare som för närvarande finns på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> försöker installera skadliga appar på enheten som stjäl eller raderar dina uppgifter (t.ex. foton, lösenord, meddelanden och kreditkort).</translation>
<translation id="168841957122794586">Servercertifikatet innehåller en svag kryptografisk nyckel.</translation>
-<translation id="1701955595840307032">Få förslag på innehåll</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Du behöver tillstånd från <ph name="NAME" /> om du vill besöka den här webbplatsen</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Sidnummer</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Testa att köra nätverksdiagnostik för Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Uppdatera lösenfrasen för synkroniseringen.</translation>
+<translation id="1787142507584202372">Öppna flikar visas här</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Googles tjänst Säker webbsökning <ph name="BEGIN_LINK" />hittade skadliga program<ph name="END_LINK" /> på <ph name="SITE" /> nyligen. Ibland förekommer det skadlig programvara på webbplatser som vanligtvis är säkra. Det skadliga innehållet kommer från <ph name="SUBRESOURCE_HOST" />, som är en känd distributör av skadlig programvara. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Begäran eller parametrar i begäran var ogiltiga</translation>
+<translation id="1834321415901700177">Den här webbplatsen innehåller skadliga program</translation>
<translation id="1838667051080421715">Du tittar på källkoden till en webbsida.</translation>
<translation id="1871208020102129563">Proxyn är inställd på att använda fasta proxyservrar, inte en webbadress med PAC-skript.</translation>
<translation id="1883255238294161206">Komprimera lista</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Bokmärken i mobilen</translation>
<translation id="2148716181193084225">Idag</translation>
-<translation id="2149973817440762519">Redigera bokmärke</translation>
<translation id="2154054054215849342">Synkronisering är inte tillgänglig för din domän</translation>
<translation id="2166049586286450108">Fullständig administrativ åtkomst</translation>
<translation id="2166378884831602661">Webbplatsen kan inte tillhandahålla en säker anslutning</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Det går inte att besöka <ph name="SITE" /> just nu på grund av att HSTS används på webbplatsen. Nätverksfel och attacker är vanligtvis tillfälliga, så den här sidan fungerar troligen senare. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Ignorerade ogiltigt bokmärke vid index <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Övriga bokmärken</translation>
+<translation id="2355395290879513365">Det är möjligt att hackare kan se vilka bilder du visar på den här webbplatsen och att de försöker lura dig genom att modifiera dem.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Visa policyer utan inställt värde</translation>
<translation id="2396249848217231973">&amp;Ã…ngra Ta bort</translation>
<translation id="2455981314101692989">Webbsidan har inaktiverat Autofyll för det här formuläret.</translation>
+<translation id="2460160116472764928">Googles tjänst Säker webbsökning <ph name="BEGIN_LINK" />hittade skadliga program<ph name="END_LINK" /> på <ph name="SITE" /> nyligen. Ibland förekommer det skadlig programvara på webbplatser som vanligtvis är säkra. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Fyll i</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />att köra nätverksdiagnostik<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Ogiltig sökadress.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Vill du ta bort de här sidorna från historiken?</translation>
<translation id="2677748264148917807">Lämna</translation>
<translation id="269990154133806163">Servern angav ett certifikat som inte har lämnats ut enligt policyn Certifikattransparens. Detta krävs för vissa certifikat för att säkerställa att de är tillförlitliga och skyddar mot hackare. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Läslista</translation>
<translation id="2704283930420550640">Värdet matchar inte formatet.</translation>
<translation id="2704951214193499422">Chromium kunde inte bekräfta kortet. Försök igen senare.</translation>
<translation id="2705137772291741111">Det gick inte att läsa den sparade (cachelagrade) kopian av webbplatsen.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Du försökte öppna <ph name="DOMAIN" />, men servern angav ett certifikat som har återkallats av utfärdaren. Det innebär att säkerhetsuppgifterna som servern anger inte är tillförlitliga. Du kanske kommunicerar med en angripare. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Begär behörighet</translation>
<translation id="2713444072780614174">Vit</translation>
+<translation id="2720342946869265578">I närheten</translation>
<translation id="2721148159707890343">Begäran genomfördes</translation>
<translation id="2728127805433021124">Serverns certifikat är signerat med en svag signaturalgoritm.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />att köra anslutningsdiagnostik<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Du kan inaktivera alla proxyservrar som har konfigurerats för en anslutning från sidan Inställningar.</translation>
<translation id="2955913368246107853">Stäng sökfältet</translation>
<translation id="2958431318199492670">Nätverkskonfigurationen uppfyller inte ONC-standarden. Det kan hända att delar av konfigurationen inte kan importeras.</translation>
+<translation id="29611076221683977">Det kan hända att angripare som för närvarande finns på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> försöker installera skadliga program på din Mac som stjäl eller raderar dina uppgifter (t.ex. foton, lösenord, meddelanden och kreditkort).</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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Dölj detaljerad information</translation>
<translation id="3587482841069643663">Alla</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Servern stöder inte tillägget för TLS-omförhandling.</translation>
<translation id="36224234498066874">Rensa webbinformation...</translation>
<translation id="362276910939193118">Visa fullständig historik</translation>
<translation id="3623476034248543066">Visa värde</translation>
@@ -271,19 +279,20 @@
<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="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="3696411085566228381">ingen</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">Laddar...</translation>
+<translation id="370665806235115550">Läser in...</translation>
<translation id="3712624925041724820">Licenserna har tagit slut</translation>
<translation id="3714780639079136834">aktivera mobildata eller Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />kontrollera proxyn, brandväggen och DNS-konfigureringen<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">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="3739623965217189342">Länk som du har kopierat</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="3788090790273268753">Certifikatet för den här webbplatsen går ut 2016. Certifikatkedjan innehåller ett certifikat som signerades med SHA-1.</translation>
<translation id="382518646247711829">Om du använder en proxyserver ...</translation>
<translation id="3828924085048779000">Lösenfrasen får inte vara tom.</translation>
<translation id="3845539888601087042">Historik från enheter som du är inloggad på visas. <ph name="BEGIN_LINK" />Läs mer<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Nyckel <ph name="SUBKEY" />: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Klienten och servern har inte stöd för en gemensam SSL-protokollversion eller chiffersvit.</translation>
<translation id="4079302484614802869">Proxykonfigurationen är inställd på att använda en webbadress med PAC-skript, inte fasta proxyservrar.</translation>
+<translation id="4098354747657067197">Webbplatsen som öppnas är bedräglig</translation>
<translation id="4103249731201008433">Enhetens serienummer är ogiltigt</translation>
<translation id="4103763322291513355">Besök &lt;strong&gt;chrome://policy&lt;/strong&gt; om du vill visa listan med webbadresser som inte är godkända och andra policyer som angetts av systemadministratören.</translation>
<translation id="4110615724604346410">Det gick inte att bevisa att serverns identitet är <ph name="DOMAIN" /> eftersom dess säkerhetscertifikat innehåller fel. Det kan bero på att servern är felkonfigurerad eller att anslutningen har blivit kapad. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{inga}=1{1 app ($1)}=2{2 appar ($1, $2)}other{# appar ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Kraschar</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Testa att köra nätverksdiagnostik<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Anslutningen till den här webbplatsen är inte helt säker</translation>
<translation id="4250680216510889253">Nej</translation>
<translation id="425582637250725228">Ändringar som du har gjort kanske inte sparas.</translation>
<translation id="4258748452823770588">Felaktig signatur</translation>
<translation id="4269787794583293679">(Inget användarnamn)</translation>
+<translation id="4280429058323657511">, utgångsdatum <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Googles tjänst Säker webbsökning <ph name="BEGIN_LINK" />hittade skadliga program<ph name="END_LINK" /> på <ph name="SITE" /> nyligen. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Föräldratips</translation>
<translation id="4304224509867189079">Logga in</translation>
<translation id="432290197980158659">Ett certifikat som inte överensstämmer med inbyggda förväntningar angavs av servern. Förväntningarna gäller för webbplatser med hög säkerhet för att skydda dig. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</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="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="4381091992796011497">Användarnamn:</translation>
<translation id="4394049700291259645">Inaktivera</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Du försökte öppna <ph name="DOMAIN" />, men servern angav ett ogiltigt certifikat. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Flikar från andra enheter</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">En tilläggssida visas.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Läs in policyer på nytt</translation>
<translation id="4728558894243024398">Plattform</translation>
<translation id="4744603770635761495">Sökväg till körbar fil</translation>
+<translation id="4750917950439032686">Dina uppgifter (till exempel lösenord eller kreditkortsnummer) är privata när de skickas till den här webbplatsen.</translation>
<translation id="4756388243121344051">&amp;Historik</translation>
+<translation id="4759118997339041434">Autofyll för betalning har inaktiverats</translation>
<translation id="4764776831041365478">Webbsidan på <ph name="URL" /> kan ligga nere för tillfället eller kan ha flyttats permanent till en ny webbadress.</translation>
<translation id="4771973620359291008">Ett okänt fel uppstod.</translation>
<translation id="4800132727771399293">Kontrollera utgångsdatum och CVC-kod och försök igen</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Nätverksfel</translation>
<translation id="4816492930507672669">Anpassa till sida</translation>
<translation id="4850886885716139402">Visa</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{och en till webbsida}other{och # till webbsidor}}</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="495170559598752135">Åtgärder</translation>
<translation id="4958444002117714549">Expandera lista</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Nedladdning</translation>
<translation id="5190835502935405962">Bokmärkesfältet</translation>
<translation id="5199729219167945352">Experiment</translation>
-<translation id="5199841536747119669">Förslag till dig visas här</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="5299298092464848405">Det uppstod ett fel när policyn analyserades</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Krashrapportering har inaktiverats.</translation>
<translation id="5317780077021120954">Spara</translation>
<translation id="5327248766486351172">Namn</translation>
+<translation id="5337705430875057403">Angripare på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kan lura dig att göra något farligt, till exempel installera program eller lämna ut din personliga information (som lösenord, telefonnummer eller kreditkortsuppgifter).</translation>
<translation id="5359637492792381994">Det gick inte att bevisa att serverns identitet är <ph name="DOMAIN" />. Dess säkerhetscertifikat är inte giltigt för närvarande. Det kan bero på att servern är felkonfigurerad eller att anslutningen har blivit kapad. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Det gick inte att spara policyinställningarna</translation>
+<translation id="5386426401304769735">Certifikatkedjan för den här webbplatsen innehåller ett certifikat som signerades med SHA-1.</translation>
<translation id="5421136146218899937">Ta bort webbinformation...</translation>
<translation id="5430298929874300616">Ta bort bokmärke</translation>
<translation id="5431657950005405462">Filen hittades inte</translation>
@@ -425,17 +446,20 @@
<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="5565735124758917034">Aktiv</translation>
+<translation id="5572851009514199876">Logga in på Chrome så att Chrome kan kontrollera om du har tillgång till den här webbplatsen.</translation>
+<translation id="5580958916614886209">Kontrollera utgångsmånad och försök igen</translation>
<translation id="560412284261940334">Hantering stöds inte</translation>
<translation id="5610142619324316209">kontrollera anslutningen</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> omdirigerade dig för många gånger.</translation>
<translation id="5622887735448669177">Vill du lämna den här webbplatsen?</translation>
<translation id="5629630648637658800">Det gick inte att läsa in policyinställningarna</translation>
<translation id="5631439013527180824">Ogiltig enhetshanteringstoken</translation>
+<translation id="5669703222995421982">Få anpassat innehåll</translation>
+<translation id="5675650730144413517">Sidan fungerar inte</translation>
<translation id="5677928146339483299">Blockerad</translation>
<translation id="5694783966845939798">Du försökte öppna <ph name="DOMAIN" />, men servern angav ett certifikat som har signerats med en svag signaturalgoritm (till exempel SHA-1). Det innebär att säkerhetsuppgifterna som servern anger kan vara en förfalskning och att servern kanske inte är den server du tror (du kanske kommunicerar med en angripare). <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Webbplatsens identitet har inte verifierats.</translation>
<translation id="5720705177508910913">Aktuell användare</translation>
-<translation id="572328651809341494">Senaste flikarna</translation>
<translation id="5732392974455271431">Dina föräldrar kan ta bort blockeringen</translation>
<translation id="5784606427469807560">Det gick inte att bekräfta kortet. Kontrollera internetanslutningen och försök igen.</translation>
<translation id="5785756445106461925">Den här sidan innehåller emellertid andra resurser som inte är säkra. Andra kan se resurserna när de överförs och hackare kan ändra resurserna så att sidan får ett annat utseende.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Du försökte öppna <ph name="DOMAIN" />, men servern angav ett certifikat som signerats med en svag nyckel. Det innebär att den privata nyckeln som servern anger kan vara en förfalskning och att servern kanske inte är den server du tror (du kanske kommunicerar med en angripare). <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Ny mapp</translation>
<translation id="5869405914158311789">Webbplatsen kan inte nås</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Den här korttypen stöds inte i Google Payments för den här säljaren. Välj ett annat kort.</translation>
<translation id="59174027418879706">Aktiverat</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="5966707198760109579">Vecka</translation>
<translation id="5967867314010545767">Ta bort från historiken</translation>
<translation id="5975083100439434680">Zooma ut</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Testa att</translation>
<translation id="6151417162996330722">Servercertifikatet har för lång giltighetstid.</translation>
<translation id="6165508094623778733">Läs mer</translation>
+<translation id="6177128806592000436">Anslutningen till webbplatsen är inte säker</translation>
<translation id="6203231073485539293">Kontrollera internetanslutningen</translation>
<translation id="6218753634732582820">Vill du ta bort adressen från Chromium?</translation>
+<translation id="6251924700383757765">Sekretesspolicy</translation>
+<translation id="625755898061068298">Du har valt att inaktivera säkerhetsvarningar för den här webbplatsen.</translation>
<translation id="6259156558325130047">&amp;Gör om Ändra ordning</translation>
<translation id="6263376278284652872">Bokmärken för <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Tillbaka till säker webbplats</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> kan inte nås.</translation>
<translation id="6321917430147971392">Kontrollera DNS-inställningarna</translation>
<translation id="6328639280570009161">Prova att inaktivera nätverksförslag</translation>
+<translation id="6328786501058569169">Den här webbplatsen är bedräglig</translation>
<translation id="6337534724793800597">Filtrera policy efter namn</translation>
<translation id="6342069812937806050">Alldeles nyss</translation>
<translation id="6345221851280129312">okänd storlek</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Det går inte att besöka <ph name="SITE" /> just nu eftersom webbplatsen skickade förvrängda användaruppgifter som Chromium inte kan behandla. Nätverksfel och attacker är vanligtvis tillfälliga, så sidan fungerar troligen senare. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Det gick inte att kontrollera om certifikatet har återkallats.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Ignoreras eftersom standardsökmotorn är inaktiverad av en policy.</translation>
<translation id="6462969404041126431">Det gick inte att bevisa att serverns identitet är <ph name="DOMAIN" /> eftersom dess säkerhetscertifikat kan ha återkallats. Detta kan orsakas av en felaktig konfigurering eller att någon spärrar anslutningen. <ph name="BEGIN_LEARN_MORE_LINK" />Lär mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Gör om Ta bort</translation>
<translation id="6534179046333460208">Förslag från Physical Web</translation>
<translation id="6550675742724504774">Alternativ</translation>
+<translation id="6556239504065605927">Säker anslutning</translation>
<translation id="6563469144985748109">Den ansvarige har inte godkänt den ännu</translation>
<translation id="6593753688552673085">mindre än <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Krypteringsalternativ</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Policyn är föråldrad.</translation>
<translation id="6652240803263749613">Det gick inte att bevisa att serverns identitet är <ph name="DOMAIN" /> eftersom datorns operativsystem inte litar på dess säkerhetscertifikat. Det kan bero på att servern är felkonfigurerad eller att anslutningen har blivit kapad. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Redigera mapp</translation>
-<translation id="6660210980321319655">Rapporterades automatiskt <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Policyvärde</translation>
<translation id="6757797048963528358">Enheten gick i viloläge.</translation>
<translation id="6778737459546443941">Din förälder har inte godkänt den ännu</translation>
+<translation id="6810899417690483278">Anpassnings-id</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Webbsidan <ph name="URL" /> är inte tillgänglig just nu. Den kan vara överbelastad eller ligga nere för underhåll.</translation>
<translation id="6831043979455480757">Översätt</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Kontaktuppgifter</translation>
+<translation id="7079718277001814089">Webbplatsen innehåller skadlig programvara.</translation>
<translation id="7087282848513945231">Grevskap</translation>
<translation id="7088615885725309056">Äldre</translation>
<translation id="7090678807593890770">Sök efter <ph name="LINK" /> på Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> följer inte säkerhetsstandarderna.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Läs mer<ph name="END_LINK" /> om problemet.</translation>
<translation id="7219179957768738017">För anslutningen används <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Det förekommer skadlig programvara på följande sida</translation>
<translation id="724975217298816891">Ange utgångsdatum och CVC-kod för <ph name="CREDIT_CARD" /> om du vill uppdatera kortinformationen. När du bekräftar delas kortinformationen med den här webbplatsen.</translation>
<translation id="725866823122871198">Det gick inte att upprätta en privat anslutning till <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> eftersom datorns datum och tid (<ph name="DATE_AND_TIME" />) inte stämmer.</translation>
<translation id="7269802741830436641">Den här webbsidan innehåller en omdirigeringsslinga</translation>
@@ -611,6 +648,7 @@ Psst! Nästa gÃ¥ng kanske inkognitoläget <ph name="SHORTCUT_KEY" /> kan vara nÃ
<translation id="7658239707568436148">Avbryt</translation>
<translation id="7667346355482952095">Policytoken som returnerades är tom eller matchade inte den aktuella token</translation>
<translation id="7668654391829183341">Okänd enhet</translation>
+<translation id="7669271284792375604">Angripare på den här webbplatsen kan försöka lura dig att installera program som skadar din webbupplevelse (till exempel genom att byta ut din startsida eller visa extra annonser på webbplatser som du besöker).</translation>
<translation id="7674629440242451245">Är du intresserad av häftiga nya funktioner i Chrome? Pröva vår utvecklarkanal på chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Fortsätt till <ph name="SITE" /> (osäkert)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Det går inte att läsa in webbplatsen från cachelagringen</translation>
@@ -626,14 +664,17 @@ Psst! Nästa gÃ¥ng kanske inkognitoläget <ph name="SHORTCUT_KEY" /> kan vara nÃ
<translation id="7800304661137206267">Anslutningen är krypterad med <ph name="CIPHER" />, med <ph name="MAC" /> för meddelandeautentisering och <ph name="KX" /> som mekanism för nyckelutbyte.</translation>
<translation id="780301667611848630">Nej tack</translation>
<translation id="7805768142964895445">Status</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Vill du ta bort formulärförslaget från Chrome?</translation>
<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="7887683347370398519">Kontrollera CVC-koden och försök igen</translation>
<translation id="7912024687060120840">I mappen:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Servercertifikatet är inte giltigt ännu.</translation>
<translation id="7942349550061667556">Röd</translation>
+<translation id="7947285636476623132">Kontrollera utgångsår och försök igen</translation>
<translation id="7951415247503192394">(32 bitar)</translation>
<translation id="7956713633345437162">Bokmärken i mobilen</translation>
<translation id="7961015016161918242">Aldrig</translation>
@@ -641,7 +682,10 @@ Psst! Nästa gÃ¥ng kanske inkognitoläget <ph name="SHORTCUT_KEY" /> kan vara nÃ
<translation id="7983301409776629893">Översätt alltid <ph name="ORIGINAL_LANGUAGE" /> till <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Inte specificerad</translation>
<translation id="8012647001091218357">Vi kunde inte nå dina föräldrar just nu. Försök igen.</translation>
+<translation id="8025119109950072390">Angripare på den här webbplatsen kan lura dig att göra något farligt, till exempel installera programvara eller lämna ut personliga uppgifter (som lösenord, telefonnummer eller kreditkortsuppgifter).</translation>
+<translation id="803030522067524905">Googles tjänst Säker webbsökning upptäckte nyligen nätfiske på <ph name="SITE" />. Webbplatser för nätfiske utger sig för att vara andra webbplatser i syfte att lura dig. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Den här sidan är skriven på <ph name="SOURCE_LANGUAGE" />. Vill du översätta den till <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Skicka feedback</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>
@@ -652,9 +696,11 @@ Psst! Nästa gÃ¥ng kanske inkognitoläget <ph name="SHORTCUT_KEY" /> kan vara nÃ
<translation id="8150722005171944719">Det går inte att läsa filen på <ph name="URL" />. Den kan ha tagits bort eller flyttats, eller så krävs behörighet för att få åtkomst till den.</translation>
<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="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="8230421197304563332">Det kan hända att angripare som för närvarande finns på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 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="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="8249320324621329438">Senast hämtad:</translation>
<translation id="8261506727792406068">Radera</translation>
@@ -663,11 +709,13 @@ Psst! Nästa gÃ¥ng kanske inkognitoläget <ph name="SHORTCUT_KEY" /> kan vara nÃ
<translation id="8294431847097064396">Källa</translation>
<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="8349305172487531364">Bokmärkesfältet</translation>
<translation id="8363502534493474904">inaktivera flygplansläget</translation>
<translation id="8364627913115013041">Inte angiven.</translation>
<translation id="8380941800586852976">Farlig</translation>
<translation id="8382348898565613901">Dina senast besökta bokmärken visas här</translation>
+<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="8428213095426709021">Inställningar</translation>
@@ -679,9 +727,9 @@ Psst! Nästa gÃ¥ng kanske inkognitoläget <ph name="SHORTCUT_KEY" /> kan vara nÃ
<translation id="8498891568109133222"><ph name="HOST_NAME" /> tog för lång tid på sig att svara.</translation>
<translation id="852346902619691059">Det gick inte att bevisa att serverns identitet är <ph name="DOMAIN" /> eftersom datorns operativsystem inte litar på dess säkerhetscertifikat. Det kan bero på att servern är felkonfigurerad eller att anslutningen har blivit kapad. <ph name="BEGIN_LEARN_MORE_LINK" />Läs mer<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Den här korttypen stöds inte i Google Payments. Välj ett annat kort.</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="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="8559762987265718583">Det gick inte att upprätta en privat anslutning till <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> eftersom enhetens datum och tid (<ph name="DATE_AND_TIME" />) inte stämmer.</translation>
-<translation id="856992080682148">Certifikatet för den här webbplatsen upphör 2017 eller senare. Certifikatkedjan innehåller ett certifikat som signerades med SHA-1.</translation>
<translation id="8571890674111243710">Översätter sidan till <ph name="LANGUAGE" />...</translation>
<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>
@@ -694,10 +742,12 @@ Psst! Nästa gÃ¥ng kanske inkognitoläget <ph name="SHORTCUT_KEY" /> kan vara nÃ
<translation id="8740359287975076522">Det gick inte att hitta <ph name="HOST_NAME" />s &lt;abbr id="dnsDefinition"&gt;DNS-adress&lt;/abbr&gt;. Diagnostiserar problemet.</translation>
<translation id="8790007591277257123">&amp;Gör om Ta bort</translation>
<translation id="8798099450830957504">Standard</translation>
+<translation id="8800988563907321413">Förslag på webbsidor i närheten visas här</translation>
<translation id="8804164990146287819">Sekretesspolicy</translation>
<translation id="8820817407110198400">Bokmärken</translation>
<translation id="8834246243508017242">Aktivera Autofyll från Kontakter …</translation>
<translation id="883848425547221593">Övriga bokmärken</translation>
+<translation id="884264119367021077">Leveransadress</translation>
<translation id="884923133447025588">Ingen återkallningsmekanism har hittats.</translation>
<translation id="885730110891505394">Delar med Google</translation>
<translation id="8866481888320382733">Det uppstod ett fel när policyinställningarna analyserades</translation>
@@ -715,18 +765,21 @@ Psst! Nästa gÃ¥ng kanske inkognitoläget <ph name="SHORTCUT_KEY" /> kan vara nÃ
<translation id="8971063699422889582">Servercertifikatet har gått ut.</translation>
<translation id="8987927404178983737">MÃ¥nad</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Webbplatsen som öppnas innehåller skadliga program</translation>
<translation id="9001074447101275817">Proxyservern <ph name="DOMAIN" /> kräver användarnamn och lösenord.</translation>
<translation id="901974403500617787">Flaggor som gäller hela systemet kan endast ställas in av ägaren: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Den här sidan har översatts till <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Säkerhetsfel</translation>
<translation id="9038649477754266430">Använd en förslagstjänst om du vill läsa in sidor snabbare</translation>
<translation id="9039213469156557790">Den här sidan innehåller emellertid andra resurser som inte är säkra. Andra kan se resurserna när de överförs och hackare kan ändra resurserna så att sidan får ett annat beteende.</translation>
+<translation id="9040185888511745258">Angripare på <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kan försöka lura dig att installera program som skadar din webbupplevelse (till exempel genom att byta ut din startsida eller visa extra annonser på webbplatser som du besöker).</translation>
<translation id="9050666287014529139">Lösenfras</translation>
<translation id="9065203028668620118">Redigera</translation>
<translation id="9068849894565669697">Välj färg</translation>
<translation id="9076283476770535406">Den kan innehålla barnförbjudet innehåll</translation>
-<translation id="9092364396508701805">Sidan på <ph name="HOST_NAME" /> fungerar inte</translation>
<translation id="9103872766612412690">På <ph name="SITE" /> används normalt kryptering (SSL) för att skydda din information. När Chromium försökte ansluta till <ph name="SITE" /> den här gången skickade webbplatsen tillbaka ovanliga och felaktiga uppgifter. Sådant kan hända när en angripare utger sig för att vara <ph name="SITE" /> eller när anslutningen har avbrutits av en Wi-Fi-inloggningsskärm. Din information är fortfarande säker eftersom Chromium avbröt anslutningen innan någon data utbyttes.</translation>
<translation id="9137013805542155359">Visa original</translation>
+<translation id="9137248913990643158">Logga in på Chrome innan du använder den här appen.</translation>
<translation id="9148507642005240123">&amp;Ã…ngra Redigera</translation>
<translation id="9157595877708044936">Konfigurerar...</translation>
<translation id="9170848237812810038">&amp;Ã…ngra</translation>
@@ -739,7 +792,6 @@ Psst! Nästa gÃ¥ng kanske inkognitoläget <ph name="SHORTCUT_KEY" /> kan vara nÃ
<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="962701380617707048">Ange utgångsdatum och CVC-kod för <ph name="CREDIT_CARD" /> om du vill uppdatera kortinformationen</translation>
<translation id="969892804517981540">Officiell version</translation>
<translation id="988159990683914416">Utvecklarversion</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 6eeb3734a74..2098b0ec551 100644
--- a/chromium/components/strings/components_strings_sw.xtb
+++ b/chromium/components/strings/components_strings_sw.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Kuunganisha tena kwenye Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Chapisha...</translation>
<translation id="1181037720776840403">Ondoa</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Ripoti kiotomatiki<ph name="END_WHITEPAPER_LINK" /> kwa Google kuhusu maelezo ya uwezekano wa matukio yasiyo salama. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Ifuatayo</translation>
<translation id="1201895884277373915">Zaidi kutoka kwenye tovuti hii</translation>
<translation id="1206967143813997005">Sahihi mbaya ya mwanzo</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Samawati-Kijani</translation>
<translation id="1629803312968146339">Je, unataka Chrome ihifadhi kadi hii?</translation>
<translation id="1640180200866533862">Sera za mtumiaji</translation>
+<translation id="1640244768702815859">Jaribu <ph name="BEGIN_LINK" />kutembelea ukurasa wa kwanza wa tovuti<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Usanidi wa mtandao ni batili na usingeweza kuingizwa.</translation>
<translation id="1644574205037202324">Historia</translation>
<translation id="1645368109819982629">Itifaki haitumiki</translation>
<translation id="1676269943528358898">Kwa kawaida <ph name="SITE" /> hutumia usimbaji fiche ili kulinda maelezo yako. Google Chrome ilipojaribu kuunganisha kwenye <ph name="SITE" /> wakati huu, tovuti ilituma kitambulisho kisicho cha kawaida na kisicho sahihi. Hili linaweza kutokea mvamizi anapojaribu kujifanya kuwa <ph name="SITE" />, au uchanganuzi wa kuingia katika Wi-Fi umeingilia muunganisho. Maelezo yako yangali salama kwa sababu Google Chrome ilisimamisha muunganisho kabla data yoyote itumwe.</translation>
+<translation id="168328519870909584">Huenda wavamizi walio kwenye <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kwa sasa wakajaribu kusakinisha programu hatari ambazo zinaiba au kufuta maelezo kwenye kifaa chako (kwa mfano, picha, manenosiri, ujumbe na kadi za mkopo).</translation>
<translation id="168841957122794586">Cheti cha seva kina kitufe dhaifu cha kifichua msimbo.</translation>
-<translation id="1701955595840307032">Pata maudhui yanayopendekezwa</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Unahitaji ruhusa kutoka kwa <ph name="NAME" /> ili utembelee tovuti hii</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Nambari ya ukurasa</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Jaribu kutumia zana ya Kuchunguza Mtandao wa Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Tafadhali sasisha kaulisiri yako iliyolandanishwa.</translation>
+<translation id="1787142507584202372">Vichupo vyako vilivyo wazi huonekana hapa</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Mfumo wa Google wa Kuvinjari Salama <ph name="BEGIN_LINK" />uligundua programu hasidi<ph name="END_LINK" /> kwenye <ph name="SITE" /> hivi majuzi. 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. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Ombi au vigezo vya ombi batili</translation>
+<translation id="1834321415901700177">Tovuti hii ina programu hatari</translation>
<translation id="1838667051080421715">Unaangalia chanzo cha ukurasa wa wavuti.</translation>
<translation id="1871208020102129563">Proksi imewekwa ili kutumia seva za proksi thabiti, siyo URL ya hati .pac.</translation>
<translation id="1883255238294161206">Kunja orodha</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Mteja Halisi</translation>
<translation id="213826338245044447">Alamisho kwenye Simu</translation>
<translation id="2148716181193084225">Leo</translation>
-<translation id="2149973817440762519">Badilisha Alamisho</translation>
<translation id="2154054054215849342">Huduma ya usawazishaji haipatikani kwa ajili ya kikoa chako</translation>
<translation id="2166049586286450108">Idhini Kamili ya Kufikia ya Msimamizi</translation>
<translation id="2166378884831602661">Tovuti hii haiwezi kutoa muunganisho salama</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Huwezi kutembelea <ph name="SITE" /> sasa hivi kwa sababu tovuti hii hutumia HSTS. Hitilafu na uvamizi wa mtandao kwa kawaida huwa wa muda tu, kwa hivyo ukurasa huu huenda utafanya kazi baadaye.<ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Imepuuzia alamisho batili katika farahasa <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Alamisho zingine</translation>
+<translation id="2355395290879513365">Wavamizi wanaweza kuona picha unazoangalia kwenye tovuti hii na wakuhadae kwa kuzibadilisha.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Onyesha sera zisizowekwa thamani</translation>
<translation id="2396249848217231973">Tendua kufuta</translation>
<translation id="2455981314101692989">Ukurasa wavuti huu umelemaza mjazo otomatiki kwa fomu hii.</translation>
+<translation id="2460160116472764928">Mfumo wa Google wa Kuvinjari Salama <ph name="BEGIN_LINK" />umegundua programu hasidi<ph name="END_LINK" /> kwenye <ph name="SITE" /> hivi majuzi. Tovuti ambazo kwa kawaida huwa salama wakati mwingine huathiriwa na programu hasidi. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Jaza</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Inaendesha Zana ya Kuchunguza Mtandao<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL batili ya utafutaji.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Je, una hakika kuwa ungependa kufuta kurasa hizi kutoka historia yako?</translation>
<translation id="2677748264148917807">Ondoka</translation>
<translation id="269990154133806163">Seva imewasilisha cheti ambacho hakikuonyeshwa hadharani kwa kutumia sera ya Uwazi wa Cheti. Baadhi ya vyeti huwa na masharti haya, ili kuhakikisha kwamba ni vya kuaminika na kulinda dhidi ya wavamizi.<ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Orodha ya Kusoma</translation>
<translation id="2704283930420550640">Thamani haioani na umbizo.</translation>
<translation id="2704951214193499422">Chromium haikuweza kuthibitisha kadi yako wakati huu. Tafadhali jaribu tena baadaye.</translation>
<translation id="2705137772291741111">Nakala iliyohifadhiwa (iliyowekwa katika akiba) ya tovuti hii haikusomeka.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Ulijaribu kufikia <ph name="DOMAIN" />, lakini cheti kilichowasilishwa na seva kimebatilishwa na mtoaji wacho. Inamaanisha kuwa stakabadhi za usalama zilizowasilishwa na seva hii hazifai kuaminiwa kabisa. Huenda unawasiliana na mvamizi.<ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Omba ruhusa</translation>
<translation id="2713444072780614174">Nyeupe</translation>
+<translation id="2720342946869265578">Uhamishaji wa Karibu</translation>
<translation id="2721148159707890343">Ombi limefanikiwa</translation>
<translation id="2728127805433021124">Cheti cha seva kimetiwa sahihi kwa kutumia algoriti dhaifu ya sahihi.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Inaendesha Zana ya Kuchunguza Muunganisho<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Unaweza kuzima proksi zozote zilizosanidiwa kwa muunganisho kutoka kwenye ukurasa wa mipangilio.</translation>
<translation id="2955913368246107853">Funga upau wa kupata</translation>
<translation id="2958431318199492670">Usanidi wa mtandao hautii kiwango cha ONC. Sehemu za usanidi haziwezi kuingizwa.</translation>
+<translation id="29611076221683977">Wavamizi walio kwenye <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kwa sasa huenda wakajaribu kusakinisha programu hatari ambazo zinaiba au kufuta maelezo kwenye Mac yako (kwa mfano picha, manenosiri, ujumbe, na kadi za mkopo).</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="2985306909656435243">Ikiwashwa, Chromium itahifadhi nakala ya kadi yako kwenye kifaa hiki kwa ajili ya kujaza fomu haraka zaidi.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Ficha maelezo</translation>
<translation id="3587482841069643663">Zote</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Seva haihimili kiendelezi cha TLS cha utambuaji.</translation>
<translation id="36224234498066874">Futa Data ya Kuvinjari...</translation>
<translation id="362276910939193118">Onyesha Historia Kamili</translation>
<translation id="3623476034248543066">Onyesha thamani</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Leseni zimekwisha</translation>
<translation id="3714780639079136834">Kuwasha data ya simu au Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Kuangalia seva mbadala, kinga-mtandao na mipangilio ya DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Ikiwa unaelewa kiwango cha hatari kinachoweza kutokea, unaweza <ph name="BEGIN_LINK" />kutembelea tovuti hii isiyo salama<ph name="END_LINK" /> kabla programu hatari hazijaondolewa.</translation>
<translation id="3739623965217189342">Kiungo ulichonakili</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="3788090790273268753">Cheti cha tovuti hii kitakwisha muda mwaka wa 2016, na msururu wa cheti una cheti kilichotiwa sahihi kwa kutumia SHA-1.</translation>
<translation id="382518646247711829">Ukitumia seva mbadala...</translation>
<translation id="3828924085048779000">Kaulisiri tupu hairuhusiwi.</translation>
<translation id="3845539888601087042">Inaonyesha historia kutoka vifaa vyako ulivyotumia kuingia katika akaunti. <ph name="BEGIN_LINK" />Pata maelezo zaidi<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Kitufe "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Mteja na seva hazitumii toleo la kawaida la itifaki ya SSL au mipangilio ya kriptografia.</translation>
<translation id="4079302484614802869">Usanidi wa proksi umewekwa kutumia URL hati ya .pac, siyo seva proksi za kudumu.</translation>
+<translation id="4098354747657067197">Kuna tovuti danganyifu mbele</translation>
<translation id="4103249731201008433">Namabari tambulishi ya kifaa ni batili</translation>
<translation id="4103763322291513355">Tembelea &lt;strong&gt;chrome://policy&lt;/strong&gt; ili kuona orodha ya URL zilizoondolewa idhini na sera zingine zinazosimamiwa na msimamizi wako wa mfumo.</translation>
<translation id="4110615724604346410">Seva hii haikuweza kuthibitisha kwamba ni <ph name="DOMAIN" />; cheti chake cha usalama kina hitilafu. Hii inatokana na uwekaji mipangilio usiofaa au mvamizi kuingilia muunganisho wako. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{hamna}=1{Programu 1 ($1)}=2{Programu 2 ($1, $2)}other{Programu # ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Mivurugo</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Jaribu kutumia zana ya Kuchunguza Mtandao<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Muunganisho wako kwenye tovuti hii si salama kabisa</translation>
<translation id="4250680216510889253">La</translation>
<translation id="425582637250725228">Huenda mabadiliko uliyofanya yasihifadhiwe.</translation>
<translation id="4258748452823770588">Sahihi mbaya</translation>
<translation id="4269787794583293679">(Hakuna jina la mtumiaji)</translation>
+<translation id="4280429058323657511">, muda wa kutumika utakwisha <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Mfumo wa Google wa Kuvinjari Salama <ph name="BEGIN_LINK" />uligundua programu zenye virusi<ph name="END_LINK" /> kwenye <ph name="SITE" /> hivi majuzi. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Mapendekezo ya wazazi</translation>
<translation id="4304224509867189079">Ingia</translation>
<translation id="432290197980158659">Seva hii iliwasilisha cheti ambacho hakilingani na vipengele vilivyojengewa ndani. Vipengele hivi vimejumuishwa kwenye baadhi ya tovuti za usalama wa juu ili kukulinda. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Haikupata makala</translation>
+<translation id="4326324639298822553">Angalia tarehe kuisha kwa muda wa matumizi halafu ujajibu tena</translation>
<translation id="4331708818696583467">Si Salama</translation>
+<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="4381091992796011497">Jina la mtumiaji:</translation>
<translation id="4394049700291259645">Zima</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Umejaribu kufikia <ph name="DOMAIN" />, lakini seva imewasilisha cheti kisicho sahihi. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Vichupo kutoka kwenye vifaa vingine</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Unaangalia ukurasa wa kiendelezi.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Pakia sera upya</translation>
<translation id="4728558894243024398">Mfumo wa uendeshaji</translation>
<translation id="4744603770635761495">Njia Tekelezi</translation>
+<translation id="4750917950439032686">Maelezo yako (kwa mfano, manenosiri, au nambari za kadi za mikopo) ni ya faragha yanapotumwa kwenye tovuti hii.</translation>
<translation id="4756388243121344051">&amp;Historia</translation>
+<translation id="4759118997339041434">Kujaza maelezo ya kadi ya mikopo kiotomatiki kumezimwa</translation>
<translation id="4764776831041365478">Ukurasa wa wavuti ulio <ph name="URL" /> unaweza kuwa haupatikani kwa muda au unaweza kuwa umehamishwa kabisa hadi anwani mpya ya wavuti.</translation>
<translation id="4771973620359291008">Hitilafu isiyojulikana imetokea.</translation>
<translation id="4800132727771399293">Angalia tarehe yako ya kuisha muda na CVC na ujaribu tena</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Hitilafu ya mtandao</translation>
<translation id="4816492930507672669">Sawazisha kwenye ukurasa</translation>
<translation id="4850886885716139402">Mwonekano</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{na ukurasa 1 zaidi wa wavuti}other{na kurasa # zaidi za wavuti}}</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="495170559598752135">Vitendo</translation>
<translation id="4958444002117714549">Panua orodha</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Inapakua</translation>
<translation id="5190835502935405962">Sehemu ya Alamisho</translation>
<translation id="5199729219167945352">Majaribio</translation>
-<translation id="5199841536747119669">Mapendekezo yako yataonekana hapa</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="5299298092464848405">Hitilafu wakati wa kuchanganua sera</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Kuripoti uharibifu kumelemazwa.</translation>
<translation id="5317780077021120954">Hifadhi</translation>
<translation id="5327248766486351172">Jina</translation>
+<translation id="5337705430875057403">Huenda wavamizi kwenye <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> wakakulaghai ili ufanye kitu hatari kama vile kusakinisha programu au kuonyesha maelezo yako ya kibinafsi (kwa mfano, manenosiri, nambari za simu, au kadi za mikopo).</translation>
<translation id="5359637492792381994">Seva hii haikuweza kuthibitisha kwamba ni <ph name="DOMAIN" />; cheti chake cha usalama si sahihi. Hii inatokana na uwekaji mipangilio usiofaa au mvamizi kuingilia muunganisho wako. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Imeshindwa kuhifadhi mipangilio ya sera</translation>
+<translation id="5386426401304769735">Msururu wa cheti wa tovuti hii una cheti kilichotiwa sahihi kwa kutumia SHA-1.</translation>
<translation id="5421136146218899937">Futa data ya kuvinjari...</translation>
<translation id="5430298929874300616">Ondoa alamisho</translation>
<translation id="5431657950005405462">Faili yako haikupatikana</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">Ukurasa uliopachikwa kwenye <ph name="SITE" /> unasema:</translation>
<translation id="5556459405103347317">Pakia upya</translation>
<translation id="5565735124758917034">Inatumika</translation>
+<translation id="5572851009514199876">Tafadhali anza na uingie katika Chrome ili Chrome iangalie ikiwa unaruhusiwa kufikia tovuti hii.</translation>
+<translation id="5580958916614886209">Angalia mwezi kuisha kwa muda wa matumizi halafu ujajibu tena</translation>
<translation id="560412284261940334">Usimamizi hautumiki</translation>
<translation id="5610142619324316209">Kuangalia muunganisho</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> imekuelekeza upya mara nyingi mno.</translation>
<translation id="5622887735448669177">Ungependa kuondoka kwenye tovuti hii?</translation>
<translation id="5629630648637658800">Imeshindwa kupakia mipangilio ya sera</translation>
<translation id="5631439013527180824">Ishara ya usimamizi wa kifaa batili</translation>
+<translation id="5669703222995421982">Pata maudhui yanayokufaa</translation>
+<translation id="5675650730144413517">Ukurasa huu haufanyi kazi</translation>
<translation id="5677928146339483299">Vilivyozuiwa</translation>
<translation id="5694783966845939798">Umejaribu kufikia <ph name="DOMAIN" />, lakini seva iliwasilisha cheti kilichowekwa sahihi kwa kutumia algoriti dhaifu. Hii inamaanisha kuwa huenda stakabadhi za usalama za seva zilizowasilishwa ni ghushi, na huenda seva siyo uliyoitarajia (huenda unawasiliana na mvamizi).<ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Utambulisho wa tovuti hii haujathibitishwa.</translation>
<translation id="5720705177508910913">Mtumiaji wa sasa</translation>
-<translation id="572328651809341494">Vichupo vya hivi majuzi</translation>
<translation id="5732392974455271431">Wazazi wako wanaweza kukuondolea kizuizi</translation>
<translation id="5784606427469807560">Kulikuwa na tatizo wakati wa kuthibitisha kadi yako. Angalia muunganisho wako wa intaneti kisha ujaribu tena.</translation>
<translation id="5785756445106461925">Mbali na hayo, ukurasa huu una rasilimali nyingine zisizo salama. Rasilimali hizi zinaweza kuangaliwa na watu wengine wanaosafiri, na zinaweza kurekebishwa na mvamizi kubadilisha mwonekano wa ukurasa.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Umejaribu kufikia <ph name="DOMAIN" />, lakini seva iliwasilisha cheti chenye ufunguo duni. Huenda mvamizi alivunja ufunguo wa faragha, na huenda seva si ile uliyoitarajia (huenda unawasiliana na mvamizi).<ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Folda Mpya</translation>
<translation id="5869405914158311789">Tovuti hii haiwezi kufikiwa</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Aina hii ya kadi haiwezi kutumiwa na Google Payments kwa muuzaji huyu. Tafadhali teua kadi tofauti.</translation>
<translation id="59174027418879706">Imewashwa</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="5966707198760109579">Juma</translation>
<translation id="5967867314010545767">Ondoa kwenye historia</translation>
<translation id="5975083100439434680">Fifiza</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Jaribu:</translation>
<translation id="6151417162996330722">Cheti cha seva kina muda sahihi ambao ni mrefu sana.</translation>
<translation id="6165508094623778733">Pata maelezo zaidi</translation>
+<translation id="6177128806592000436">Muunganisho wako kwenye tovuti hii si salama</translation>
<translation id="6203231073485539293">Angalia muunganisho wako wa Intaneti</translation>
<translation id="6218753634732582820">Je, ungependa kuondoa anwani kwenye Chromium?</translation>
+<translation id="6251924700383757765">Sera ya faragha</translation>
+<translation id="625755898061068298">Umechagua kuzima maonyo ya usalama ya tovuti hii.</translation>
<translation id="6259156558325130047">Rudia Kupanga Upya</translation>
<translation id="6263376278284652872">Alamisho za <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Rejea kwenye usalama</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> haiwezi kufikiwa.</translation>
<translation id="6321917430147971392">Angalia mipangilio yako ya DNS</translation>
<translation id="6328639280570009161">Jaribu kuzima utabiri wa mtandao</translation>
+<translation id="6328786501058569169">Tovuti hii ni ya udanganyifu</translation>
<translation id="6337534724793800597">Chuja sera kwa jina</translation>
<translation id="6342069812937806050">Sasa hivi tu</translation>
<translation id="6345221851280129312">ukubwa usiojulikana</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Huwezi kutembelea <ph name="SITE" /> hivi sasa kwa sababu tovuti ilituma kitambulisho kilichoharibika ambacho Chromium haiwezi kuchakata. Hitilafu na uvamizi wa mtandao kwa kawaida huwa wa muda, kwa hivyo ukurasa huu huenda utafanya kazi baadaye. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Haiwezi kukagua ikiwa cheti kimebatilishwa.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Imepuuzwa kwa sababu utafutaji chaguo-msingi unalemazwa kwa sera.</translation>
<translation id="6462969404041126431">Seva hii haikuweza kuthibitisha kwamba ni <ph name="DOMAIN" />; cheti chake cha usalama huenda kimebatilishwa. Huenda hali hii inatokana na uwekaji mipangilio usiofaa au mvamizi kuingilia muunganisho wako. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">Rudia Kufuta</translation>
<translation id="6534179046333460208">Mapendekezo ya Wavuti kila Mahali</translation>
<translation id="6550675742724504774">Chaguo</translation>
+<translation id="6556239504065605927">Muunganisho salama</translation>
<translation id="6563469144985748109">Msimamizi wako bado hajaiidhinisha</translation>
<translation id="6593753688552673085">chini ya <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Chaguo za usimbaji fiche</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Sera hii imepingwa.</translation>
<translation id="6652240803263749613">Seva hii haikuweza kuthibitisha kwamba ni <ph name="DOMAIN" />; cheti chake cha usalama hakiaminiwi na mfumo wa uendeshaji wa kompyuta yako. Hali hii inatokana na uwekaji mipangilio usiofaa au mvamizi kuingilia muunganisho wako. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Badilisha Folda</translation>
-<translation id="6660210980321319655">Imeripoti kiotomatiki <ph name="CRASH_TIME" /></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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Thamani ya sera</translation>
<translation id="6757797048963528358">Kifaa chako kiko katika hali tuli.</translation>
<translation id="6778737459546443941">Mzazi wako bado hajaiidhinisha</translation>
+<translation id="6810899417690483278">Kitambulisho cha kubadilisha ili kukufaa</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Ukurasa wa wavuti katika <ph name="URL" /> haupatikani kwa sasa. Huenda umezidishwa kiwango au unarekebishwa.</translation>
<translation id="6831043979455480757">Tafsiri</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Maelezo ya mawasiliano</translation>
+<translation id="7079718277001814089">Tovuti hii ina programu hasidi</translation>
<translation id="7087282848513945231">Nchi</translation>
<translation id="7088615885725309056">Za awali</translation>
<translation id="7090678807593890770">Tafuta <ph name="LINK" /> kwenye Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> haizingatii viwango vya usalama.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Pata maelezo zaidi<ph name="END_LINK" /> kuhusu tatizo hili.</translation>
<translation id="7219179957768738017">Muunganisho unatumia <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Tovuti unayoelekea kufungua ina programu hasidi</translation>
<translation id="724975217298816891">Weka tarehe ya kuisha kwa muda wa matumizi na CVC ya <ph name="CREDIT_CARD" /> ili usasishe maelezo ya kadi yako. Baada ya kuthibitisha, maelezo ya kadi yako yatashirikiwa na tovuti hii.</translation>
<translation id="725866823122871198">Muunganisho wa faragha kwenye <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> hauwezi kutambuliwa kwa sababu tarehe na wakati wa kompyuta yako (<ph name="DATE_AND_TIME" />) si sahihi.</translation>
<translation id="7269802741830436641">Ukurasa huu wa wavuti una kitanzi cha kuelekeza upya</translation>
@@ -611,6 +648,7 @@ Hebu! Huenda hali fiche <ph name="SHORTCUT_KEY" /> ikakufaa wakati ujao.</transl
<translation id="7658239707568436148">Ghairi</translation>
<translation id="7667346355482952095">Tokeni ya sera iliyoletwa haina chochote au hailingani na tokeni ya sasa</translation>
<translation id="7668654391829183341">Kifaa kisichojulikana</translation>
+<translation id="7669271284792375604">Wavamizi kwenye tovuti hii wanaweza kujaribu kukulaghai kusakinisha programu zinazoathiri hali yako ya kuvinjari (kwa mfano, kwa kubadilisha ukurasa wako wa kwanza au kwa kuonyesha matangazo ya ziada kwenye tovuti unazotembelea).</translation>
<translation id="7674629440242451245">Unavutiwa na vipengee vipya vizuri vya Chrome? Jaribu kituo chetu cha dev katika chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Nenda kwenye <ph name="SITE" /> (isiyo salama)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Faili hii haiwezi kupakiwa kutoka akiba</translation>
@@ -626,14 +664,17 @@ Hebu! Huenda hali fiche <ph name="SHORTCUT_KEY" /> ikakufaa wakati ujao.</transl
<translation id="7800304661137206267">Muunganisho umesimbwa fiche kwa kutumia <ph name="CIPHER" />, kwa <ph name="MAC" /> ya uthibitishaji wa ujumbe na <ph name="KX" /> kama utaratibu muhimu wa ubadilishanaji.</translation>
<translation id="780301667611848630">Sitaki</translation>
<translation id="7805768142964895445">Hali</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Ungependa kuondoa pendekezo la fomu kutoka kwenye Chrome?</translation>
<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="7887683347370398519">Angalia CVC yako na ujaribu tena</translation>
<translation id="7912024687060120840">Katika Folda:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Cheti cha seva bado sio halali.</translation>
<translation id="7942349550061667556">Nyekundu</translation>
+<translation id="7947285636476623132">Angalia mwaka kuisha kwa muda wa matumizi halafu ujajibu tena</translation>
<translation id="7951415247503192394">(biti 32)</translation>
<translation id="7956713633345437162">Alamisho kwenye simu</translation>
<translation id="7961015016161918242">Katu</translation>
@@ -641,7 +682,10 @@ Hebu! Huenda hali fiche <ph name="SHORTCUT_KEY" /> ikakufaa wakati ujao.</transl
<translation id="7983301409776629893">Tafsiri kutoka <ph name="ORIGINAL_LANGUAGE" /> hadi <ph name="TARGET_LANGUAGE" /> kila wakati</translation>
<translation id="7995512525968007366">Hakijabainishwa</translation>
<translation id="8012647001091218357">Hatukuweza kuwafikia wazazi wako wakati huu. Tafadhali jaribu tena.</translation>
+<translation id="8025119109950072390">Wavamizi kwenye tovuti hii wanaweza kukulaghai ili ufanye kitu hatari kama vile kusakinisha programu au kuonyesha maelezo yako binafsi (kwa mfano, manenosiri, nambari za simu au kadi za mikopo).</translation>
+<translation id="803030522067524905">Mfumo wa Kuvinjari Salama kwenye Google uligundua jaribio la kuhadaa ili kupata maelezo ya kibinafsi kwenye <ph name="SITE" /> hivi majuzi. Tovuti za kuhadaa ili kupata maelezo ya kibinafsi hujifanya kuwa tovuti nyingine unayoijua ili kukulaghai. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Ukurasa huu ni wa lugha ya <ph name="SOURCE_LANGUAGE" />. Je, ungependa kuutasfiri kuwa <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Tuma Maoni</translation>
<translation id="8088680233425245692">Haikufaulu kuangalia makala.</translation>
<translation id="8089520772729574115">chini ya MB 1</translation>
<translation id="8091372947890762290">Uwashaji unasubiri kwenye seva</translation>
@@ -652,9 +696,11 @@ Hebu! Huenda hali fiche <ph name="SHORTCUT_KEY" /> ikakufaa wakati ujao.</transl
<translation id="8150722005171944719">Faili katika <ph name="URL" /> haisomeki. Huenda imeondolewa, kusogezwa, au idhini za faili huenda zinazuia ufikiaji.</translation>
<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="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="8230421197304563332">Huenda wavamizi walio kwenye <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> kwa sasa wakajaribu kusakinisha programu hatari ambazo zinaiba au kufuta maelezo kwenye kifaa chako (kwa mfano, picha, manenosiri, ujumbe na kadi za mkopo).</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="8249320324621329438">Iliyoletwa mwisho:</translation>
<translation id="8261506727792406068">Futa</translation>
@@ -663,11 +709,13 @@ Hebu! Huenda hali fiche <ph name="SHORTCUT_KEY" /> ikakufaa wakati ujao.</transl
<translation id="8294431847097064396">Chanzo</translation>
<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="8349305172487531364">Sehemu ya Alamisho</translation>
<translation id="8363502534493474904">Kuzima hali ya ndegeni</translation>
<translation id="8364627913115013041">Haijawekwa.</translation>
<translation id="8380941800586852976">Hatari</translation>
<translation id="8382348898565613901">Alamisho ulizotembelea hivi majuzi zitaonekana hapa</translation>
+<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="8428213095426709021">Mipangilio</translation>
@@ -679,9 +727,9 @@ Hebu! Huenda hali fiche <ph name="SHORTCUT_KEY" /> ikakufaa wakati ujao.</transl
<translation id="8498891568109133222"><ph name="HOST_NAME" /> imechukua muda mrefu kupakia.</translation>
<translation id="852346902619691059">Seva hii haikuweza kuthibitisha kwamba ni <ph name="DOMAIN" />; cheti chake cha usalama hakiaminiwi na mfumo wa uendeshaji wa kifaa chako. Hii inatokana na uwekaji mipangilio usiofaa au mvamizi kuingilia muunganisho wako. <ph name="BEGIN_LEARN_MORE_LINK" />Pata maelezo zaidi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Aina hii ya kadi haiwezi kutumiwa na Google Payments. Tafadhali teua kadi tofauti.</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="8553075262323480129">Tafsiri imeshindwa kwa sababu lugha ya ukurasa isingeweza kuthibitishwa.</translation>
<translation id="8559762987265718583">Muunganisho wa faragha kwenye <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> hauwezi kupatikana kwa sababu tarehe na wakati wa kifaa chako (<ph name="DATE_AND_TIME" />) si sahihi.</translation>
-<translation id="856992080682148">Cheti cha tovuuti hii kitakwisha muda mwaka wa 2017 au baadaye, na msururu wa cheti una cheti kilichotiwa sahihi kwa kutumia SHA-1.</translation>
<translation id="8571890674111243710">Inatafsiri ukurasa katika <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Cheti hakibainishi utaratibu wa kuangalia iwapo kimekataliwa.</translation>
<translation id="8620436878122366504">Wazazi wako bado hawajaiidhinisha</translation>
@@ -694,10 +742,12 @@ Hebu! Huenda hali fiche <ph name="SHORTCUT_KEY" /> ikakufaa wakati ujao.</transl
<translation id="8740359287975076522"><ph name="HOST_NAME" /> &lt;abbr id="dnsDefinition"&gt;anwani ya DNS&lt;/abbr&gt; haikupatikana. Tatizo linachunguzwa.</translation>
<translation id="8790007591277257123">Rudia kufuta</translation>
<translation id="8798099450830957504">Chaguo Msingi</translation>
+<translation id="8800988563907321413">Mapendekezo ya maudhui ya uhamishaji wa karibu yataonekana hapa</translation>
<translation id="8804164990146287819">Sera ya Faragha</translation>
<translation id="8820817407110198400">Alamisho</translation>
<translation id="8834246243508017242">Washa Kujaza Otomatiki ukitumia Anwani…</translation>
<translation id="883848425547221593">Alamisho Zingine</translation>
+<translation id="884264119367021077">Anwani ya kusafirisha</translation>
<translation id="884923133447025588">Mbinu ya ubatilishaji haikupatikana.</translation>
<translation id="885730110891505394">Kushiriki kwenye Google</translation>
<translation id="8866481888320382733">Hitilafu wakati wa kuchanganua mipangilio ya sera</translation>
@@ -715,18 +765,21 @@ Hebu! Huenda hali fiche <ph name="SHORTCUT_KEY" /> ikakufaa wakati ujao.</transl
<translation id="8971063699422889582">Cheti cha seva kimechina.</translation>
<translation id="8987927404178983737">Mwezi</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Tovuti unayoelekea kufungua ina programu zinazodhuru</translation>
<translation id="9001074447101275817">Seva mbadala ya <ph name="DOMAIN" /> inahitaji jina la mtumiaji na nenosiri.</translation>
<translation id="901974403500617787">Alama zinazotumika katika mfumo mzima zinaweza kuwekwa na mmiliki pekee: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Ukurasa huu umetafsiriwa kwenda <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Hitilafu ya usalama</translation>
<translation id="9038649477754266430">Tumia huduma ya kutabiri ili upakie kurasa kwa haraka zaidi</translation>
<translation id="9039213469156557790">Mbali na hayo, ukurasa huu una rasilimali nyingine zisizo salama. Rasilimali hizi zinaweza kuangaliwa na watu wengine wanaosafiri, na zinaweza kurekebishwa na mvamizi kubadilisha tabia ya ukurasa.</translation>
+<translation id="9040185888511745258">Wavamizi kwenye <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> huenda wakajaribu kukulaghai ili kusakinisha programu zinazodhuru hali yako ya kuvinjari (kwa mfano, kwa kubadilisha ukurasa wako wa mwanzo au kuonyesha matangazo ya ziada kwenye tovuti unazotembelea).</translation>
<translation id="9050666287014529139">Kaulisiri</translation>
<translation id="9065203028668620118">Badilisha</translation>
<translation id="9068849894565669697">Chagua rangi</translation>
<translation id="9076283476770535406">Huenda ina maudhui ya ngono</translation>
-<translation id="9092364396508701805">Ukurasa wa <ph name="HOST_NAME" /> haufanyi kazi</translation>
<translation id="9103872766612412690">Kwa kawaida <ph name="SITE" /> hutumia usimbaji fiche ili kulinda maelezo yako. Chromium ilipojaribu kuunganisha kwenye <ph name="SITE" /> wakati huu, tovuti ilituma kitambulisho kisicho cha kawaida na kisicho sahihi. Hili linaweza kutokea mvamizi anapojaribu kujifanya kuwa <ph name="SITE" />, au uchanganuzi wa kuingia katika Wi-Fi umeingilia muunganisho. Maelezo yako yangali salama kwa sababu Chromium ilisimamisha muunganisho kabla data yoyote itumwe.</translation>
<translation id="9137013805542155359">Onyesha asili</translation>
+<translation id="9137248913990643158">Tafadhali anza na uingie katika Chrome kabla ya kutumia programu hii.</translation>
<translation id="9148507642005240123">Tendua kuhariri</translation>
<translation id="9157595877708044936">Inasanidi...</translation>
<translation id="9170848237812810038">&amp;Tendua</translation>
@@ -739,7 +792,6 @@ Hebu! Huenda hali fiche <ph name="SHORTCUT_KEY" /> ikakufaa wakati ujao.</transl
<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="962701380617707048">Weka tarehe ya kuisha kwa muda wa matumizi na CVC ya <ph name="CREDIT_CARD" /> ili usasishe maelezo ya kadi yako</translation>
<translation id="969892804517981540">Muundo Rasmi</translation>
<translation id="988159990683914416">Muundo wa Wasanidi Programu</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 748035a54a7..2702b48a906 100644
--- a/chromium/components/strings/components_strings_ta.xtb
+++ b/chromium/components/strings/components_strings_ta.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">வைஃபையà¯à®Ÿà®©à¯ மீணà¯à®Ÿà¯à®®à¯ இணைதà¯à®¤à®²à¯</translation>
<translation id="1175364870820465910">&amp;அசà¯à®šà®¿à®Ÿà¯...</translation>
<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="1206967143813997005">தொடகà¯à®•à®•à¯ கையொபà¯à®ªà®®à¯ சரியானதலà¯à®²</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">சியானà¯</translation>
<translation id="1629803312968146339">இநà¯à®¤à®•à¯ காரà¯à®Ÿà¯ˆ Chrome சேமிகà¯à®• வேணà¯à®Ÿà¯à®®à®¾?</translation>
<translation id="1640180200866533862">பயனர௠கொளà¯à®•à¯ˆà®•à®³à¯</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />தளதà¯à®¤à®¿à®©à¯ à®®à¯à®•à®ªà¯à®ªà¯à®ªà¯ பகà¯à®•à®¤à¯à®¤à®¿à®±à¯à®•à¯à®šà¯ செலà¯à®²à®µà¯à®®à¯<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">பிணைய உளà¯à®³à®®à¯ˆà®µà¯ தவறானத௠மேலà¯à®®à¯ அதை இறகà¯à®•à¯à®®à®¤à®¿ செயà¯à®¯ à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="1644574205037202324">வரலாறà¯</translation>
<translation id="1645368109819982629">ஆதரிகà¯à®•à®ªà¯à®ªà®Ÿà®¾à®¤ நெறிமà¯à®±à¯ˆ</translation>
<translation id="1676269943528358898">வழகà¯à®•à®®à®¾à®•, <ph name="SITE" /> உஙà¯à®•à®³à¯ தகவலைப௠பாதà¯à®•à®¾à®ªà¯à®ªà®¤à®±à¯à®•à®¾à®• à®®à¯à®±à¯ˆà®®à¯ˆà®¯à®¾à®•à¯à®•à®¤à¯à®¤à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®•à®¿à®±à®¤à¯. இநà¯à®¤ à®®à¯à®±à¯ˆ <ph name="SITE" /> உடன௠இணைவதறà¯à®•à¯ Google Chrome à®®à¯à®¯à®±à¯à®šà®¿à®¤à¯à®¤à®ªà¯‹à®¤à¯ வழகà¯à®•à®¤à¯à®¤à®¿à®±à¯à®•à¯ மாறான மறà¯à®±à¯à®®à¯ தவறான நறà¯à®šà®¾à®©à¯à®±à®¿à®¤à®´à¯à®•à®³à¯ˆ இணையதளம௠வழஙà¯à®•à®¿à®¯à®¤à¯. தாகà¯à®•à¯à®ªà®µà®°à¯ தனà¯à®©à¯ˆ <ph name="SITE" /> ஆகக௠காடà¯à®Ÿ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à¯à®®à¯ போத௠அலà¯à®²à®¤à¯ இணைபà¯à®ªà¯ˆ வைஃபை உளà¯à®¨à¯à®´à¯ˆà®µà¯à®¤à¯ திரை கà¯à®±à¯à®•à¯à®•à®¿à®Ÿà¯à®®à¯ போத௠இத௠à®à®±à¯à®ªà®Ÿà®²à®¾à®®à¯. இரà¯à®ªà¯à®ªà®¿à®©à¯à®®à¯, தரவ௠எதà¯à®µà¯à®®à¯ பரிமாறà¯à®±à®ªà¯à®ªà®Ÿà¯à®µà®¤à®±à¯à®•à¯ à®®à¯à®©à¯ Google Chrome இணைபà¯à®ªà¯ˆ நிறà¯à®¤à¯à®¤à®¿à®¯à®¤à®¾à®²à¯ உஙà¯à®•à®³à¯ தகவல௠பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®•à®µà¯‡ இரà¯à®•à¯à®•à®¿à®±à®¤à¯.</translation>
+<translation id="168328519870909584"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> தளதà¯à®¤à®¿à®²à®¿à®°à¯à®•à¯à®•à¯à®®à¯ தாகà¯à®•à¯à®ªà®µà®°à¯à®•à®³à¯, உஙà¯à®•à®³à¯ தகவலைத௠(எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®•, படஙà¯à®•à®³à¯, கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯, செயà¯à®¤à®¿à®•à®³à¯ மறà¯à®±à¯à®®à¯ கிரெடிட௠காரà¯à®Ÿà¯à®•à®³à¯) திரà¯à®Ÿà®•à¯à®•à¯‚டிய அலà¯à®²à®¤à¯ நீகà¯à®•à®•à¯à®•à¯‚டிய தீஙà¯à®•à®¿à®´à¯ˆà®•à¯à®•à¯à®®à¯ பயனà¯à®ªà®¾à®Ÿà¯à®•à®³à¯ˆ உஙà¯à®•à®³à¯ சாதனதà¯à®¤à®¿à®²à¯ நிறà¯à®µ à®®à¯à®¯à®±à¯à®šà®¿à®¤à¯à®¤à®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="168841957122794586">சேவையக சானà¯à®±à®¿à®¤à®´à®¿à®²à¯ வலà¯à®µà®±à¯à®± கà¯à®±à®¿à®¯à¯€à®Ÿà¯à®Ÿà®¾à®•à¯à®• விசை இரà¯à®•à¯à®•à®¿à®±à®¤à¯.</translation>
-<translation id="1701955595840307032">பரிநà¯à®¤à¯à®°à¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯ உளà¯à®³à®Ÿà®•à¯à®•à®¤à¯à®¤à¯ˆà®ªà¯ பெறà¯à®•</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®±à¯à®•à¯à®šà¯ செலà¯à®², <ph name="NAME" /> இன௠அனà¯à®®à®¤à®¿ வேணà¯à®Ÿà¯à®®à¯</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">பகà¯à®• எணà¯</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows நெடà¯à®µà¯Šà®°à¯à®•à¯ டயகà¯à®©à®¸à¯à®Ÿà®¿à®•à¯à®¸à¯ கரà¯à®µà®¿à®¯à¯ˆ இயகà¯à®•à®µà¯à®®à¯<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">உஙà¯à®•à®³à®¿à®©à¯ ஒதà¯à®¤à®¿à®šà¯ˆ சொறà¯à®±à¯Šà®Ÿà®°à¯ˆà®ªà¯ பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®•à®µà¯à®®à¯.</translation>
+<translation id="1787142507584202372">உஙà¯à®•à®³à¯ தாவலà¯à®•à®³à¯ இஙà¯à®•à¯‡ தோனà¯à®±à¯à®®à¯</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">சமீபதà¯à®¤à®¿à®²à¯ Google இன௠பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© உலாவலானத௠<ph name="SITE" /> இல௠<ph name="BEGIN_LINK" />தீமà¯à®ªà¯Šà®°à¯à®³à¯ˆà®•à¯ கணà¯à®Ÿà®±à®¿à®¨à¯à®¤à¯à®³à¯à®³à®¤à¯<ph name="END_LINK" />. பொதà¯à®µà®¾à®•à®ªà¯ பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®• இரà¯à®•à¯à®•à¯à®®à¯ இணையதளஙà¯à®•à®³à¯ சில நேரஙà¯à®•à®³à®¿à®²à¯ தீமà¯à®ªà¯Šà®°à¯à®³à®¾à®²à¯ பாதிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®µà®¤à¯à®£à¯à®Ÿà¯. தீஙà¯à®•à®¿à®´à¯ˆà®•à¯à®•à¯à®®à¯ உளà¯à®³à®Ÿà®•à¯à®•à®®à®¾à®©à®¤à¯ பிரபலமான <ph name="SUBRESOURCE_HOST" /> எனà¯à®®à¯ தீமà¯à®ªà¯Šà®°à¯à®³à¯ வழஙà¯à®•à¯à®¨à®°à®¿à®Ÿà®®à®¿à®°à¯à®¨à¯à®¤à¯ வரà¯à®•à®¿à®±à®¤à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">தவறான கோரிகà¯à®•à¯ˆ அலà¯à®²à®¤à¯ கோரிகà¯à®•à¯ˆ அளவà¯à®°à¯à®•à¯à®•à®³à¯</translation>
+<translation id="1834321415901700177">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®²à¯ தீஙà¯à®•à®¿à®´à¯ˆà®•à¯à®•à¯à®®à¯ நிரலà¯à®•à®³à¯ உளà¯à®³à®©</translation>
<translation id="1838667051080421715">இணையப௠பகà¯à®•à®¤à¯à®¤à®¿à®©à¯ மூலதà¯à®¤à¯ˆà®ªà¯ பாரà¯à®•à¯à®•à®¿à®±à¯€à®°à¯à®•à®³à¯.</translation>
<translation id="1871208020102129563">நிலையான பà¯à®°à®¾à®•à¯à®¸à®¿ சேவையகஙà¯à®•à®³à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤ பà¯à®°à®¾à®•à¯à®¸à®¿ அமைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯, .pac ஸà¯à®•à®¿à®°à®¿à®ªà¯à®Ÿà¯ URL அலà¯à®².</translation>
<translation id="1883255238294161206">படà¯à®Ÿà®¿à®¯à®²à¯ˆà®šà¯ சà¯à®°à¯à®•à¯à®•à¯</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">நேடà¯à®Ÿà®¿à®µà¯ கிளையனà¯à®Ÿà¯</translation>
<translation id="213826338245044447">மொபைல௠பà¯à®•à¯à®®à®¾à®°à¯à®•à¯à®•à¯à®•à®³à¯</translation>
<translation id="2148716181193084225">இனà¯à®±à¯</translation>
-<translation id="2149973817440762519">பà¯à®•à¯à®®à®¾à®°à¯à®•à¯à®•à¯ˆ மாறà¯à®±à¯à®•</translation>
<translation id="2154054054215849342">உஙà¯à®•à®³à¯ டொமைனà¯à®•à¯à®•à¯ ஒதà¯à®¤à®¿à®šà¯ˆà®¤à¯à®¤à®²à¯ சேவை à®®à¯à®Ÿà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯</translation>
<translation id="2166049586286450108">à®®à¯à®´à¯ நிரà¯à®µà®¾à®•à®¿ அணà¯à®•à®²à¯</translation>
<translation id="2166378884831602661">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¾à®²à¯ பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© இணைபà¯à®ªà¯ˆ வழஙà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836"><ph name="SITE" /> இணையதளம௠HSTSà®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®µà®¤à®¾à®²à¯, இபà¯à®ªà¯‹à®¤à¯ அதà¯à®¤à®³à®¤à¯à®¤à®¿à®±à¯à®•à¯à®šà¯ செலà¯à®² à®®à¯à®Ÿà®¿à®¯à®¾à®¤à¯. நெடà¯à®µà¯Šà®°à¯à®•à¯ பிழைகளà¯à®®à¯ பாதிபà¯à®ªà¯à®•à®³à¯à®®à¯ தறà¯à®•à®¾à®²à®¿à®•à®®à®¾à®©à®µà¯ˆà®¯à¯‡. இநà¯à®¤à®ªà¯ பகà¯à®•à®®à¯ சிறித௠நேரம௠கழிதà¯à®¤à¯à®šà¯ செயலà¯à®ªà®Ÿà¯à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155"><ph name="ENTRY_INDEX" /> அடà¯à®Ÿà®µà®£à¯ˆà®¯à®¿à®²à¯ தவறான பà¯à®•à¯à®®à®¾à®°à¯à®•à¯ பà¯à®±à®•à¯à®•à®£à®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯</translation>
<translation id="2354001756790975382">பிற பà¯à®•à¯à®®à®¾à®°à¯à®•à¯à®¸à¯</translation>
+<translation id="2355395290879513365">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®²à¯ நீஙà¯à®•à®³à¯ பாரà¯à®¤à¯à®¤à¯à®•à¯ கொணà¯à®Ÿà®¿à®°à¯à®•à¯à®•à¯à®®à¯ படஙà¯à®•à®³à¯ˆ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯à®•à®³à¯à®®à¯ பாரà¯à®¤à¯à®¤à¯, அவறà¯à®±à¯ˆ மாறà¯à®±à®¿à®¯à®®à¯ˆà®¤à¯à®¤à¯ உஙà¯à®•à®³à¯ˆ à®à®®à®¾à®±à¯à®±à®•à¯à®•à¯‚டà¯à®®à¯.</translation>
<translation id="2359808026110333948">தொடரà¯à®•</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" /> அனà¯à®±à¯ பெறà¯à®± சிதைவ௠அறிகà¯à®•à¯ˆ பதிவேறà¯à®±à®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="2367567093518048410">நிலை</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">மதிபà¯à®ªà¯à®®à¯ எதà¯à®µà¯à®®à¯ அமைகà¯à®•à®ªà¯à®ªà®Ÿà®¾à®¤ கொளà¯à®•à¯ˆà®•à®³à¯ˆà®•à¯ காடà¯à®Ÿà¯</translation>
<translation id="2396249848217231973">&amp;நீகà¯à®•à¯à®¤à®²à¯ˆà®šà¯ செயலà¯à®¤à®µà®¿à®°à¯</translation>
<translation id="2455981314101692989">இநà¯à®¤à®ªà¯ படிவதà¯à®¤à®¿à®±à¯à®•à®¾à®© தானியஙà¯à®•à¯ நிரபà¯à®ªà¯à®¤à®²à¯ˆ இநà¯à®¤ வலைபà¯à®ªà®•à¯à®•à®®à¯ à®®à¯à®Ÿà®•à¯à®•à®¿à®¯à¯à®³à¯à®³à®¤à¯.</translation>
+<translation id="2460160116472764928">சமீபதà¯à®¤à®¿à®²à¯ Google இன௠பாதà¯à®•à®¾à®ªà¯à®ªà¯ உலாவலானத௠<ph name="SITE" /> இல௠<ph name="BEGIN_LINK" />தீமà¯à®ªà¯Šà®°à¯à®³à¯ˆà®•à¯ கணà¯à®Ÿà®±à®¿à®¨à¯à®¤à¯à®³à¯à®³à®¤à¯<ph name="END_LINK" />. பொதà¯à®µà®¾à®•à®ªà¯ பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®• இரà¯à®•à¯à®•à¯à®®à¯ இணையதளஙà¯à®•à®³à¯ சில நேரஙà¯à®•à®³à®¿à®²à¯ தீமà¯à®ªà¯Šà®°à¯à®³à®¾à®²à¯ பாதிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®µà®¤à¯à®£à¯à®Ÿà¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">நிரபà¯à®ªà¯</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />நெடà¯à®µà¯Šà®°à¯à®•à¯ டயகà¯à®©à®¸à¯à®Ÿà®¿à®•à¯à®¸à¯ கரà¯à®µà®¿à®¯à¯ˆ இயகà¯à®•à®µà¯à®®à¯<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">தவறான தேடல௠URL.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">உஙà¯à®•à®³à¯ வரலாறà¯à®±à®¿à®²à®¿à®°à¯à®•à¯à®•à¯à®®à¯ பகà¯à®•à®™à¯à®•à®³à¯ˆ நிசà¯à®šà®¯à®®à®¾à®• நீகà¯à®• விரà¯à®®à¯à®ªà¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾?</translation>
<translation id="2677748264148917807">வெளியேறà¯</translation>
<translation id="269990154133806163">சானà¯à®±à®¿à®¤à®´à¯ வெளிபà¯à®ªà®Ÿà¯ˆà®¤à¯à®¤à®©à¯à®®à¯ˆ கொளà¯à®•à¯ˆà®¯à®¿à®©à¯à®ªà®Ÿà®¿ பொதà¯à®µà®¿à®²à¯ வெளியிடபà¯à®ªà®Ÿà®¾à®¤ சானà¯à®±à®¿à®¤à®´à¯ˆ சேவையகம௠வழஙà¯à®•à®¿à®¯à¯à®³à¯à®³à®¤à¯. சில சானà¯à®±à®¿à®¤à®´à¯à®•à®³à®¿à®©à¯ நமà¯à®ªà®•à®¤à¯à®¤à®©à¯à®®à¯ˆà®¯à¯ˆà®¯à¯à®®à¯ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯à®•à®³à¯à®•à¯à®•à¯ எதிரான பாதà¯à®•à®¾à®ªà¯à®ªà¯ˆà®¯à¯à®®à¯ உறà¯à®¤à®¿à®šà¯†à®¯à¯à®¯, இத௠அவசியமாகà¯à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">வாசிபà¯à®ªà¯à®ªà¯ படà¯à®Ÿà®¿à®¯à®²à¯</translation>
<translation id="2704283930420550640">மதிபà¯à®ªà®¾à®©à®¤à¯ வடிவமைபà¯à®ªà®¿à®±à¯à®•à¯à®ªà¯ பொரà¯à®¨à¯à®¤à®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="2704951214193499422">இபà¯à®ªà¯‹à®¤à¯ உஙà¯à®•à®³à¯ காரà¯à®Ÿà¯ˆ உறà¯à®¤à®¿à®šà¯†à®¯à¯à®¯ à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ. பிறக௠மà¯à®¯à®²à®µà¯à®®à¯.</translation>
<translation id="2705137772291741111">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®©à¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ (தறà¯à®•à®¾à®²à®¿à®•à®šà¯ சேமிபà¯à®ªà¯) நகலைப௠படிகà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082"><ph name="DOMAIN" />கà¯à®•à¯à®šà¯ செலà¯à®² à®®à¯à®¯à®©à¯à®±à¯€à®°à¯à®•à®³à¯, ஆனால௠சேவையகம௠வழஙà¯à®•à®¿à®¯ சானà¯à®±à®¿à®¤à®´à¯ அதன௠வழஙà¯à®•à¯à®¨à®°à®¾à®²à¯ ரதà¯à®¤à¯à®šà¯†à®¯à¯à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯. ஆகையாலà¯, சேவையகம௠வழஙà¯à®•à®¿à®¯ அனà¯à®®à®¤à®¿à®šà¯ சானà¯à®±à¯à®•à®³à¯ˆ நமà¯à®ª வேணà¯à®Ÿà®¾à®®à¯. நீஙà¯à®•à®³à¯ தொடரà¯à®ªà¯à®•à¯Šà®£à¯à®Ÿà®¿à®°à¯à®ªà¯à®ªà®¤à¯ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®¾à®³à®°à®¾à®• இரà¯à®•à¯à®•à®²à®¾à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">அனà¯à®®à®¤à®¿ கேளà¯</translation>
<translation id="2713444072780614174">வெளà¯à®³à¯ˆ</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">இணைபà¯à®ªà®¿à®±à¯à®•à®¾à®• உளà¯à®³à®®à¯ˆà®¤à¯à®¤ எநà¯à®¤ பிராகà¯à®šà®¿à®•à®³à¯ˆà®¯à¯à®®à¯ நீஙà¯à®•à®³à¯ அமைபà¯à®ªà¯à®•à®³à¯ பகà¯à®•à®¤à¯à®¤à®¿à®²à®¿à®°à¯à®¨à¯à®¤à¯ à®®à¯à®Ÿà®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="2955913368246107853">தேடல௠பெடà¯à®Ÿà®¿à®¯à¯ˆ மூடà¯à®•</translation>
<translation id="2958431318199492670">பிணைய உளà¯à®³à®®à¯ˆà®ªà¯à®ªà®¾à®©à®¤à¯ ONC தரதà¯à®¤à¯à®Ÿà®©à¯ இணஙà¯à®•à®µà®¿à®²à¯à®²à¯ˆ. உளà¯à®³à®®à¯ˆà®µà®¿à®©à¯ பகà¯à®¤à®¿à®•à®³à¯ இறகà¯à®•à¯à®®à®¤à®¿à®¯à®¾à®•à®¾à®®à®²à¯ போககà¯à®•à¯‚டà¯à®®à¯.</translation>
+<translation id="29611076221683977"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> தளதà¯à®¤à®¿à®²à®¿à®°à¯à®•à¯à®•à¯à®®à¯ தாகà¯à®•à¯à®ªà®µà®°à¯à®•à®³à¯, உஙà¯à®•à®³à¯ தகவலைத௠(எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®•, படஙà¯à®•à®³à¯, கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯, செயà¯à®¤à®¿à®•à®³à¯ மறà¯à®±à¯à®®à¯ கிரெடிட௠காரà¯à®Ÿà¯à®•à®³à¯) திரà¯à®Ÿà®•à¯à®•à¯‚டிய அலà¯à®²à®¤à¯ நீகà¯à®•à®•à¯à®•à¯‚டிய தீஙà¯à®•à®¿à®´à¯ˆà®•à¯à®•à¯à®®à¯ நிரலà¯à®•à®³à¯ˆ உஙà¯à®•à®³à¯ Mac இல௠நிறà¯à®µ à®®à¯à®¯à®±à¯à®šà®¿à®¤à¯à®¤à®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="2969319727213777354">பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© இணைபà¯à®ªà¯ˆ à®à®±à¯à®ªà®Ÿà¯à®¤à¯à®¤, கடிகாரம௠சரியாக அமைகà¯à®•à®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà¯à®®à¯. இணையதளஙà¯à®•à®³à¯ தஙà¯à®•à®³à¯ˆà®¤à¯ தாமே அடையாளபà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®®à¯ சானà¯à®±à®¿à®¤à®´à¯à®•à®³à¯ கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯à®Ÿ காலதà¯à®¤à®¿à®±à¯à®•à¯ மடà¯à®Ÿà¯à®®à¯ செலà¯à®²à¯à®ªà®Ÿà®¿à®¯à®¾à®µà®¤à®¾à®²à¯, இத௠செயà¯à®¯à®ªà¯à®ªà®Ÿ வேணà¯à®Ÿà¯à®®à¯. உஙà¯à®•à®³à¯ சாதனதà¯à®¤à®¿à®©à¯ கடிகாரம௠தவறாக இரà¯à®ªà¯à®ªà®¤à®¾à®²à¯, இநà¯à®¤à®šà¯ சானà¯à®±à®¿à®¤à®´à¯à®•à®³à¯ˆ Google Chrome ஆல௠சரிபாரà¯à®•à¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="2972581237482394796">&amp;மீணà¯à®Ÿà¯à®®à¯ செயà¯</translation>
<translation id="2985306909656435243">இத௠இயகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¾à®²à¯, விரைவாகப௠படிவதà¯à®¤à¯ˆ நிரபà¯à®ª, உஙà¯à®•à®³à¯ காரà¯à®Ÿà®¿à®©à¯ நகலை Chromium இநà¯à®¤à®šà¯ சாதனதà¯à®¤à®¿à®²à¯ சேமிகà¯à®•à¯à®®à¯.</translation>
@@ -256,7 +265,6 @@
<translation id="3586931643579894722">விவரஙà¯à®•à®³à¯ˆ மறை</translation>
<translation id="3587482841069643663">அனைதà¯à®¤à¯à®®à¯</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">TLS மறà¯à®ªà¯‡à®° நீடà¯à®Ÿà®¿à®ªà¯à®ªà¯ˆ சேவையகம௠ஆதரிகà¯à®•à®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="36224234498066874">உலாவல௠தரவை அழி...</translation>
<translation id="362276910939193118">à®®à¯à®´à¯ வரலாறà¯à®±à¯ˆà®¯à¯à®®à¯ காணà¯à®ªà®¿</translation>
<translation id="3623476034248543066">மதிபà¯à®ªà¯ˆà®•à¯ காடà¯à®Ÿà¯</translation>
@@ -268,19 +276,20 @@
<translation id="3655670868607891010">இதை அடிகà¯à®•à®Ÿà®¿ காணà¯à®•à®¿à®±à¯€à®°à¯à®•à®³à¯ எனிலà¯, <ph name="HELP_LINK" /> à® à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯.</translation>
<translation id="3658742229777143148">மீளà¯à®¤à®¿à®°à¯à®¤à¯à®¤à®™à¯à®•à®³à¯</translation>
<translation id="3678029195006412963">கோரிகà¯à®•à¯ˆà®¯à®¿à®²à¯ கையொபà¯à®ªà®®à®¿à®Ÿ à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ</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="3696411085566228381">எதà¯à®µà¯à®®à®¿à®²à¯à®²à¯ˆ</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">நினைவேறà¯à®•à®¿à®±à®¤à¯...</translation>
+<translation id="370665806235115550">à®à®±à¯à®±à¯à®•à®¿à®±à®¤à¯â€¦</translation>
<translation id="3712624925041724820">உரிமம௠மà¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯</translation>
<translation id="3714780639079136834">மொபைல௠தரவ௠அலà¯à®²à®¤à¯ வைஃபையை இயகà¯à®•à¯à®¤à®²à¯</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />பà¯à®°à®¾à®•à¯à®¸à®¿, ஃபயரà¯à®µà®¾à®²à¯ மறà¯à®±à¯à®®à¯ DNS உளà¯à®³à®®à¯ˆà®µà¯ˆà®šà¯ சரிபாரà¯à®¤à¯à®¤à®²à¯<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">உஙà¯à®•à®³à¯ பாதà¯à®•à®¾à®ªà¯à®ªà®¿à®±à¯à®•à®¾à®© ஆபதà¯à®¤à¯ˆà®ªà¯ பà¯à®°à®¿à®¨à¯à®¤à¯à®•à¯Šà®£à¯à®Ÿà®¾à®²à¯, தீஙà¯à®•à®¿à®´à¯ˆà®•à¯à®•à¯à®®à¯ நிரலà¯à®•à®³à¯ அகறà¯à®±à®ªà¯à®ªà®Ÿà¯à®µà®¤à®±à¯à®•à¯ à®®à¯à®©à¯ <ph name="BEGIN_LINK" />இநà¯à®¤à®ªà¯ பாதà¯à®•à®¾à®ªà¯à®ªà®±à¯à®± தளதà¯à®¤à¯ˆà®ªà¯ பாரà¯à®µà¯ˆà®¯à®¿à®Ÿà®²à®¾à®®à¯<ph name="END_LINK" />.</translation>
<translation id="3739623965217189342">நீஙà¯à®•à®³à¯ நகலெடà¯à®¤à¯à®¤ இணைபà¯à®ªà¯</translation>
<translation id="375403751935624634">ஒர௠சேவையகப௠பிழையின௠காரணமாக மொழிபெயரà¯à®ªà¯à®ªà¯à®¤à¯ தோலà¯à®µà®¿à®¯à®Ÿà¯ˆà®¨à¯à®¤à®¤à¯.</translation>
<translation id="3759461132968374835">உஙà¯à®•à®³à®¿à®Ÿà®®à¯ சமீபதà¯à®¤à®¿à®²à¯ செயலிழபà¯à®ªà¯à®•à®³à¯ எதà¯à®µà¯à®®à¯ பà¯à®•à®¾à®°à®³à®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ. செயலிழபà¯à®ªà¯ பà¯à®•à®¾à®°à®³à®¿à®¤à¯à®¤à®²à¯ à®®à¯à®Ÿà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®¨à¯à®¤à®ªà¯‹à®¤à¯ à®à®±à¯à®ªà®Ÿà¯à®Ÿ செயலிழபà¯à®ªà¯à®•à®³à¯ இஙà¯à®•à¯ காணà¯à®ªà®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà®¾à®¤à¯.</translation>
-<translation id="3788090790273268753">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®±à¯à®•à®¾à®© சானà¯à®±à®¿à®¤à®´à¯ 2016 ஆம௠ஆணà¯à®Ÿà¯ காலாவதியாகà¯à®®à¯, மேலà¯à®®à¯ சானà¯à®±à®¿à®¤à®´à¯ சஙà¯à®•à®¿à®²à®¿à®¯à®¿à®²à¯ SHA-1à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®•à¯ கையொபà¯à®ªà®®à®¿à®Ÿà¯à®Ÿ சானà¯à®±à®¿à®¤à®´à¯ உளà¯à®³à®¤à¯.</translation>
<translation id="382518646247711829">நீஙà¯à®•à®³à¯ பிராகà¯à®šà®¿ சரà¯à®µà®°à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®©à®¾à®²à¯....</translation>
<translation id="3828924085048779000">வெறà¯à®±à¯ கடவà¯à®šà¯à®šà¯Šà®±à¯à®±à¯Šà®Ÿà®°à¯à®•à¯à®•à¯ அனà¯à®®à®¤à®¿à®¯à®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="3845539888601087042">நீஙà¯à®•à®³à¯ உளà¯à®¨à¯à®´à¯ˆà®¨à¯à®¤à®¿à®°à¯à®•à¯à®•à¯à®®à¯ சாதனஙà¯à®•à®³à®¿à®²à®¿à®°à¯à®¨à¯à®¤à¯ வரலாறà¯à®±à¯ˆà®•à¯ காடà¯à®Ÿà¯à®•à®¿à®±à®¤à¯. <ph name="BEGIN_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LINK" />.</translation>
@@ -302,6 +311,7 @@
<translation id="4058922952496707368">விசை "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">கà¯à®³à¯ˆà®¯à®©à¯à®Ÿà¯à®Ÿà¯à®®à¯ சேவையகமà¯à®®à¯ பொதà¯à®µà®¾à®© SSL நெறிமà¯à®±à¯ˆà®ªà¯ பதிபà¯à®ªà¯ˆà®¯à¯‹ சைஃபர௠பொதியையோ ஆதரிகà¯à®•à®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="4079302484614802869">பà¯à®°à®¾à®•à¯à®¸à®¿ உளà¯à®³à®®à¯ˆà®µà®¾à®©à®¤à¯, .pac ஸà¯à®•à®¿à®°à®¿à®ªà¯à®Ÿà¯ URL à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯à®ªà®Ÿà®¿ அமைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®¿à®±à®¤à¯, நிலையான பà¯à®°à®¾à®•à¯à®¸à®¿ சேவையகஙà¯à®•à®³à¯à®•à¯à®•à¯ அலà¯à®².</translation>
+<translation id="4098354747657067197">மோசடிசெயà¯à®¯à¯à®®à¯ தளம௠உளà¯à®³à®¤à¯</translation>
<translation id="4103249731201008433">சாதன சீரியல௠எண௠தவறானதà¯</translation>
<translation id="4103763322291513355">à®à®±à¯à®•à®¤à¯à®¤à®•à®¾à®¤ URLகளின௠படà¯à®Ÿà®¿à®¯à®²à¯ˆà®¯à¯à®®à¯ உஙà¯à®•à®³à¯ கணினி நிரà¯à®µà®¾à®•à®¿à®¯à®¾à®²à¯ செயறà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®®à¯ பிற கொளà¯à®•à¯ˆà®•à®³à¯ˆà®¯à¯à®®à¯ காண &lt;strong&gt;chrome://policy&lt;/strong&gt; à®à®ªà¯ பாரà¯à®µà¯ˆà®¯à®¿à®Ÿà®µà¯à®®à¯.</translation>
<translation id="4110615724604346410"><ph name="DOMAIN" /> என இநà¯à®¤à®šà¯ சேவையகதà¯à®¤à®¾à®²à¯ நிரூபிகà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ. இதன௠பாதà¯à®•à®¾à®ªà¯à®ªà¯à®šà¯ சானà¯à®±à®¿à®¤à®´à®¿à®²à¯ பிழைகள௠உளà¯à®³à®©. இத௠தவறான உளà¯à®³à®®à¯ˆà®µà®¾à®²à¯ à®à®±à¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯ அலà¯à®²à®¤à¯ உஙà¯à®•à®³à¯ இணைபà¯à®ªà®¿à®²à¯ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯ கà¯à®±à¯à®•à¯à®•à®¿à®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -319,15 +329,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{பயனà¯à®ªà®¾à®Ÿà¯à®•à®³à¯ இலà¯à®²à¯ˆ}=1{1 பயனà¯à®ªà®¾à®Ÿà¯ ($1)}=2{2 பயனà¯à®ªà®¾à®Ÿà¯à®•à®³à¯ ($1, $2)}other{# பயனà¯à®ªà®¾à®Ÿà¯à®•à®³à¯ ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">செயலிழபà¯à®ªà¯à®•à®³à¯</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />நெடà¯à®µà¯Šà®°à¯à®•à¯ டயகà¯à®©à®¸à¯à®Ÿà®¿à®•à¯à®¸à¯ கரà¯à®µà®¿à®¯à¯ˆ இயகà¯à®•à®µà¯à®®à¯<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®±à¯à®•à®¾à®© உஙà¯à®•à®³à¯ இணைபà¯à®ªà¯, à®®à¯à®´à¯à®ªà¯ பாதà¯à®•à®¾à®ªà¯à®ªà¯à®Ÿà®©à¯ இலà¯à®²à¯ˆ</translation>
<translation id="4250680216510889253">இலà¯à®²à¯ˆ</translation>
<translation id="425582637250725228">உஙà¯à®•à®³à¯ மாறà¯à®±à®™à¯à®•à®³à¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà®¾à®®à®²à¯ இரà¯à®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="4258748452823770588">தவறான கையொபà¯à®ªà®®à¯</translation>
<translation id="4269787794583293679">(பயனரà¯à®ªà¯†à®¯à®°à¯ இலà¯à®²à¯ˆ)</translation>
+<translation id="4280429058323657511">, காலாவதித௠தேதி: <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">சமீபதà¯à®¤à®¿à®²à¯ Google இன௠பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© உலாவலானத௠<ph name="SITE" /> இல௠<ph name="BEGIN_LINK" />தீஙà¯à®•à¯à®µà®¿à®³à¯ˆà®µà®¿à®•à¯à®•à¯à®®à¯ நிரலà¯à®•à®³à¯ˆà®•à¯ கணà¯à®Ÿà®±à®¿à®¨à¯à®¤à¯à®³à¯à®³à®¤à¯<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">மூலப௠பரிநà¯à®¤à¯à®°à¯ˆà®•à®³à¯</translation>
<translation id="4304224509867189079">உளà¯à®¨à¯à®´à¯ˆ</translation>
<translation id="432290197980158659">உளà¯à®³à®®à¯ˆà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ எதிரà¯à®ªà®¾à®°à¯à®ªà¯à®ªà¯à®•à®³à¯à®Ÿà®©à¯ பொரà¯à®¨à¯à®¤à®¾à®¤à®šà¯ சானà¯à®±à®¿à®¤à®´à¯ˆ சேவையகம௠வழஙà¯à®•à®¿à®¯à¯à®³à¯à®³à®¤à¯. உஙà¯à®•à®³à¯ˆà®ªà¯ பாதà¯à®•à®¾à®•à¯à®•à¯à®®à¯ நோகà¯à®•à®¤à¯à®¤à®¿à®²à¯, கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯à®Ÿ அதிக பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© இணையதளஙà¯à®•à®³à®¿à®²à¯ இநà¯à®¤ எதிரà¯à®ªà®¾à®°à¯à®ªà¯à®ªà¯à®•à®³à¯ அமைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®•à®¿à®©à¯à®±à®©. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">கடà¯à®Ÿà¯à®°à¯ˆà®¯à¯ˆà®•à¯ கணà¯à®Ÿà®±à®¿à®¯ à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ</translation>
+<translation id="4326324639298822553">காலாவதித௠தேதியைச௠சரிபாரà¯à®¤à¯à®¤à¯, மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®²à®µà¯à®®à¯</translation>
<translation id="4331708818696583467">பாதà¯à®•à®¾à®ªà¯à®ªà®±à¯à®±à®¤à¯</translation>
+<translation id="4356973930735388585">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®²à¯ உளà¯à®³ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯à®•à®³à¯, உஙà¯à®•à®³à¯ தகவலைத௠(எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà¯: படஙà¯à®•à®³à¯, கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯, செயà¯à®¤à®¿à®•à®³à¯ மறà¯à®±à¯à®®à¯ கிரெடிட௠காரà¯à®Ÿà¯à®•à®³à¯) திரà¯à®Ÿà®•à¯à®•à¯‚டிய அலà¯à®²à®¤à¯ நீகà¯à®•à®•à¯à®•à¯‚டிய தீஙà¯à®•à®¿à®´à¯ˆà®•à¯à®•à¯à®®à¯ நிரலà¯à®•à®³à¯ˆ உஙà¯à®•à®³à¯ கணினியில௠நிறà¯à®µ à®®à¯à®¯à®²à®²à®¾à®®à¯.</translation>
<translation id="4372948949327679948">எதிரà¯à®ªà®¾à®°à¯à®¤à¯à®¤ <ph name="VALUE_TYPE" /> மதிபà¯à®ªà¯.</translation>
<translation id="4381091992796011497">பயனர௠பெயரà¯:</translation>
<translation id="4394049700291259645">à®®à¯à®Ÿà®•à¯à®•à¯</translation>
@@ -345,6 +360,7 @@
<translation id="4589078953350245614"><ph name="DOMAIN" />கà¯à®•à¯à®šà¯ செலà¯à®² à®®à¯à®¯à®©à¯à®±à¯€à®°à¯à®•à®³à¯, ஆனால௠சேவையகம௠தவறான சானà¯à®±à®¿à®¤à®´à¯ˆ வழஙà¯à®•à®¿à®¯à¯à®³à¯à®³à®¤à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">நவீன சைபர௠சூடà¯à®Ÿà¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿ <ph name="DOMAIN" /> உடனான உஙà¯à®•à®³à¯ இணைபà¯à®ªà¯ எனà¯à®•à¯à®°à®¿à®ªà¯à®Ÿà¯ செயà¯à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯.</translation>
<translation id="4594403342090139922">&amp;நீகà¯à®•à¯à®¤à®²à¯ˆà®šà¯ செயலà¯à®¤à®µà®¿à®°à¯</translation>
+<translation id="4619615317237390068">பிற சாதனஙà¯à®•à®³à®¿à®©à¯ தாவலà¯à®•à®³à¯</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">நீடà¯à®Ÿà®¿à®ªà¯à®ªà¯à®ªà¯ பகà¯à®•à®¤à¯à®¤à¯ˆà®ªà¯ பாரà¯à®•à¯à®•à®¿à®±à¯€à®°à¯à®•à®³à¯.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -353,10 +369,13 @@
<translation id="4726672564094551039">கொளà¯à®•à¯ˆà®•à®³à¯ˆ மீணà¯à®Ÿà¯à®®à¯ à®à®±à¯à®±à¯</translation>
<translation id="4728558894243024398">பà¯à®³à®¾à®Ÿà¯à®ƒà®ªà®¾à®°à¯à®®à¯</translation>
<translation id="4744603770635761495">இயகà¯à®•à®¨à®¿à®°à®²à¯ பாதை</translation>
+<translation id="4750917950439032686">உஙà¯à®•à®³à¯ தகவலை (எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà¯: கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯ அலà¯à®²à®¤à¯ கிரெடிட௠காரà¯à®Ÿà¯ எணà¯à®•à®³à¯) இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®±à¯à®•à¯ அனà¯à®ªà¯à®ªà¯à®®à¯ போதà¯, தனிபà¯à®ªà®Ÿà¯à®Ÿà®¤à®¾à®• இரà¯à®•à¯à®•à¯à®®à¯.</translation>
<translation id="4756388243121344051">&amp;வரலாறà¯</translation>
+<translation id="4759118997339041434">கடà¯à®Ÿà®£à®¤à¯à®¤à¯ˆà®¤à¯ தானாக நிரபà¯à®ªà¯à®µà®¤à¯ à®®à¯à®Ÿà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯</translation>
<translation id="4764776831041365478"><ph name="URL" /> இல௠உளà¯à®³ வலைபà¯à®ªà®•à¯à®•à®®à®¾à®©à®¤à¯ தறà¯à®•à®¾à®²à®¿à®•à®®à®¾à®• இயஙà¯à®•à®¾à®®à®²à¯ இரà¯à®•à¯à®•à®²à®¾à®®à¯ அலà¯à®²à®¤à¯ அத௠ஒர௠பà¯à®¤à®¿à®¯ வலை à®®à¯à®•à®µà®°à®¿à®•à¯à®•à¯ நிரநà¯à®¤à®°à®®à®¾à®• நகரà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="4771973620359291008">அறியபà¯à®ªà®Ÿà®¾à®¤ பிழை à®à®±à¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯.</translation>
<translation id="4800132727771399293">காலாவதியாகà¯à®®à¯ நேரதà¯à®¤à¯ˆà®¯à¯à®®à¯, CVCà®à®¯à¯à®®à¯ சரிபாரà¯à®¤à¯à®¤à¯, மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">பிணைய பிழை</translation>
<translation id="4816492930507672669">பகà¯à®•à®¤à¯à®¤à®¿à®²à¯ பொரà¯à®¤à¯à®¤à¯</translation>
<translation id="4850886885716139402">காடà¯à®šà®¿</translation>
@@ -365,6 +384,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{மேலà¯à®®à¯ ஒர௠இணையப௠பகà¯à®•à®®à¯}other{மேலà¯à®®à¯ # இணையப௠பகà¯à®•à®™à¯à®•à®³à¯}}</translation>
<translation id="4923417429809017348">ஒர௠அறியபà¯à®ªà®Ÿà®¾à®¤ மொழியிலிரà¯à®¨à¯à®¤à¯ <ph name="LANGUAGE_LANGUAGE" /> -கà¯à®•à¯ இநà¯à®¤à®ªà¯ பகà¯à®•à®®à¯ மொழிபெயரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯</translation>
+<translation id="4923459931733593730">கடà¯à®Ÿà®£ à®®à¯à®±à¯ˆ</translation>
<translation id="4926049483395192435">கடà¯à®Ÿà®¾à®¯à®®à¯ கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿ வேணà¯à®Ÿà¯à®®à¯.</translation>
<translation id="495170559598752135">செயலà¯à®•à®³à¯</translation>
<translation id="4958444002117714549">படà¯à®Ÿà®¿à®¯à®²à¯ˆ விரி</translation>
@@ -392,7 +412,6 @@
<translation id="5181140330217080051">பதிவிறகà¯à®•à¯à®•à®¿à®±à®¤à¯</translation>
<translation id="5190835502935405962">பà¯à®•à¯à®®à®¾à®°à¯à®•à¯à®•à¯à®•à®³à¯ படà¯à®Ÿà®¿</translation>
<translation id="5199729219167945352">சோதனைகளà¯</translation>
-<translation id="5199841536747119669">நீஙà¯à®•à®³à¯ பரிநà¯à®¤à¯à®°à¯ˆà®¤à¯à®¤à®µà¯ˆ இஙà¯à®•à¯‡ தோனà¯à®±à¯à®®à¯</translation>
<translation id="5251803541071282808">மேககà¯à®•à®£à®¿</translation>
<translation id="5277279256032773186">பணியில௠Chromeà®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾? வணிக நிறà¯à®µà®©à®™à¯à®•à®³à¯ தஙà¯à®•à®³à®¿à®©à¯ பணியாளரà¯à®•à®³à¯à®•à¯à®•à®¾à®© Chrome அமைபà¯à®ªà¯à®•à®³à¯ˆ நிரà¯à®µà®•à®¿à®•à¯à®•à®²à®¾à®®à¯. மேலà¯à®®à¯ அறிக</translation>
<translation id="5299298092464848405">கொளà¯à®•à¯ˆà®¯à¯ˆ அலசà¯à®µà®¤à®¿à®²à¯ பிழை</translation>
@@ -400,8 +419,10 @@
<translation id="5308689395849655368">செயலிழபà¯à®ªà¯ பà¯à®•à®¾à®°à®³à®¿à®¤à¯à®¤à®²à¯ à®®à¯à®Ÿà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯.</translation>
<translation id="5317780077021120954">சேமி</translation>
<translation id="5327248766486351172">பெயரà¯</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> இல௠உளà¯à®³ தாகà¯à®•à¯à®ªà®µà®°à¯à®•à®³à¯, மெனà¯à®ªà¯Šà®°à¯à®³à¯ˆ நிறà¯à®µà¯à®¤à®²à¯ அலà¯à®²à®¤à¯ உஙà¯à®•à®³à¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ தகவலை (எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®•, கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯, ஃபோன௠எணà¯à®•à®³à¯ அலà¯à®²à®¤à¯ கிரெடிட௠காரà¯à®Ÿà¯à®•à®³à¯) வெளியிடச௠செயà¯à®¤à®²à¯ போனà¯à®± ஆபதà¯à®¤à®¾à®© செயலà¯à®•à®³à¯ˆà®šà¯ செயà¯à®¯à¯à®®à¯à®ªà®Ÿà®¿ à®à®®à®¾à®±à¯à®± à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="5359637492792381994"><ph name="DOMAIN" /> என இநà¯à®¤à®šà¯ சேவையகதà¯à®¤à®¾à®²à¯ நிரூபிகà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ. இதன௠பாதà¯à®•à®¾à®ªà¯à®ªà¯à®šà¯ சானà¯à®±à®¿à®¤à®´à¯ இபà¯à®ªà¯‹à®¤à¯ செலà¯à®²à¯à®ªà®Ÿà®¿à®¯à®¾à®•à®µà®¿à®²à¯à®²à¯ˆ. இத௠தவறான உளà¯à®³à®®à¯ˆà®µà®¾à®²à¯ à®à®±à¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯ அலà¯à®²à®¤à¯ உஙà¯à®•à®³à¯ இணைபà¯à®ªà®¿à®²à¯ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯ கà¯à®±à¯à®•à¯à®•à®¿à®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">கொளà¯à®•à¯ˆ அமைபà¯à®ªà¯à®•à®³à¯ˆà®šà¯ சேமிபà¯à®ªà®¤à®¿à®²à¯ தோலà¯à®µà®¿</translation>
+<translation id="5386426401304769735">இநà¯à®¤à®šà¯ சானà¯à®±à®¿à®¤à®´à¯ சஙà¯à®•à®¿à®²à®¿à®¯à®¿à®²à¯, SHA-1à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿ கையொபà¯à®ªà®®à®¿à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿ சானà¯à®±à®¿à®¤à®´à¯ உளà¯à®³à®¤à¯.</translation>
<translation id="5421136146218899937">உலாவல௠தரவை அழி...</translation>
<translation id="5430298929874300616">பà¯à®¤à¯à®¤à®•à®•à¯à®•à¯à®±à®¿à®¯à¯ˆ அகறà¯à®±à¯</translation>
<translation id="5431657950005405462">உஙà¯à®•à®³à¯ கோபà¯à®ªà¯ இலà¯à®²à¯ˆ</translation>
@@ -422,17 +443,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> இல௠உளà¯à®³ உடà¯à®ªà¯Šà®¤à®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ பகà¯à®•à®®à¯ தெரிவிபà¯à®ªà®¤à¯:</translation>
<translation id="5556459405103347317">மீணà¯à®Ÿà¯à®®à¯ à®à®±à¯à®±à¯</translation>
<translation id="5565735124758917034">செயலில௠உளà¯à®³à®¤à¯</translation>
+<translation id="5572851009514199876">Chromeà®à®¤à¯ தொடஙà¯à®•à®¿ உளà¯à®¨à¯à®´à¯ˆà®¯à®µà¯à®®à¯. அபà¯à®ªà¯‹à®¤à¯à®¤à®¾à®©à¯ இநà¯à®¤à®¤à¯ தளதà¯à®¤à¯ˆ அணà¯à®•à¯à®µà®¤à®±à¯à®•à¯ உஙà¯à®•à®³à¯à®•à¯à®•à¯ அனà¯à®®à®¤à®¿ உளà¯à®³à®¤à®¾ எனà¯à®ªà®¤à¯ˆ Chrome ஆல௠சரிபாரà¯à®•à¯à®• à®®à¯à®Ÿà®¿à®¯à¯à®®à¯.</translation>
+<translation id="5580958916614886209">காலாவதி மாததà¯à®¤à¯ˆà®šà¯ சரிபாரà¯à®¤à¯à®¤à¯, மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®²à®µà¯à®®à¯</translation>
<translation id="560412284261940334">நிரà¯à®µà®¾à®•à®®à¯ ஆதரிகà¯à®•à®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="5610142619324316209">இணைபà¯à®ªà¯ˆà®šà¯ சரிபாரà¯à®¤à¯à®¤à®²à¯</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> உஙà¯à®•à®³à¯ˆà®ªà¯ பலமà¯à®±à¯ˆ திசைதிரà¯à®ªà¯à®ªà®®à¯ செயà¯à®¤à®¤à¯.</translation>
<translation id="5622887735448669177">தளதà¯à®¤à®¿à®²à®¿à®°à¯à®¨à¯à®¤à¯ வெளியேறவா?</translation>
<translation id="5629630648637658800">கொளà¯à®•à¯ˆ அமைபà¯à®ªà¯à®•à®³à¯ˆ à®à®±à¯à®±à¯à®µà®¤à®¿à®²à¯ தோலà¯à®µà®¿</translation>
<translation id="5631439013527180824">தவறான சாதன நிரà¯à®µà®¾à®• டோகà¯à®•à®©à¯</translation>
+<translation id="5669703222995421982">தனிபà¯à®ªà®¯à®©à®¾à®•à¯à®•à®¿à®¯ உளà¯à®³à®Ÿà®•à¯à®•à®¤à¯à®¤à¯ˆà®ªà¯ பெறà¯à®™à¯à®•à®³à¯</translation>
+<translation id="5675650730144413517">இநà¯à®¤à®ªà¯ பகà¯à®•à®®à¯ செயலà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="5677928146339483299">தடà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯</translation>
<translation id="5694783966845939798"><ph name="DOMAIN" />கà¯à®•à¯à®šà¯ செலà¯à®² à®®à¯à®¯à®±à¯à®šà®¿ செயà¯à®¤à¯€à®°à¯à®•à®³à¯, ஆனால௠சேவையகமானத௠வலிமையறà¯à®± கையொபà¯à®ª அலà¯à®•à®¾à®°à®¿à®¤à®®à¯ˆà®ªà¯ (SHA-1 போனà¯à®±à®µà¯ˆ) பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®•à¯ கையொபà¯à®ªà®®à®¿à®Ÿà¯à®Ÿ சானà¯à®±à®¿à®¤à®´à¯ˆ வழஙà¯à®•à®¿à®¯à¯à®³à¯à®³à®¤à¯. அதாவதà¯, சேவையகம௠வழஙà¯à®•à®¿à®¯à¯à®³à¯à®³ பாதà¯à®•à®¾à®ªà¯à®ªà¯ அனà¯à®®à®¤à®¿à®šà¯ சானà¯à®±à¯à®•à®³à¯ போலியாக உரà¯à®µà®¾à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯, அதà¯à®¤à¯à®Ÿà®©à¯ சேவையகம௠நீஙà¯à®•à®³à¯ எதிரà¯à®ªà®¾à®°à¯à®¤à¯à®¤ சேவையகமாக இலà¯à®²à®¾à®®à®²à¯ இரà¯à®•à¯à®•à®•à¯à®•à¯‚டà¯à®®à¯ (நீஙà¯à®•à®³à¯ தொடரà¯à®ªà¯à®•à¯Šà®£à¯à®Ÿà®¿à®°à¯à®ªà¯à®ªà®¤à¯ ஹேகà¯à®•à®°à®¾à®• இரà¯à®•à¯à®•à®²à®¾à®®à¯). <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">இநà¯à®¤ தளதà¯à®¤à®¿à®©à¯ அடையாளம௠சரிபாரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="5720705177508910913">நடபà¯à®ªà¯à®ªà¯ பயனரà¯</translation>
-<translation id="572328651809341494">சமீபதà¯à®¤à®¿à®¯ தாவலà¯à®•à®³à¯</translation>
<translation id="5732392974455271431">உஙà¯à®•à®³à¯à®•à¯à®•à®¾à®•, தளதà¯à®¤à®¿à®©à¯ தடà¯à®ªà¯à®ªà¯ˆ உஙà¯à®•à®³à¯ பெறà¯à®±à¯‹à®°à¯ நீகà¯à®• à®®à¯à®Ÿà®¿à®¯à¯à®®à¯</translation>
<translation id="5784606427469807560">காரà¯à®Ÿà¯ˆ உறà¯à®¤à®¿à®šà¯†à®¯à¯à®µà®¤à®¿à®²à¯ சிகà¯à®•à®²à¯ à®à®±à¯à®ªà®Ÿà¯à®Ÿà®¤à¯. இணைய இணைபà¯à®ªà¯ˆà®šà¯ சரிபாரà¯à®¤à¯à®¤à¯, மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®²à®µà¯à®®à¯.</translation>
<translation id="5785756445106461925">மேலà¯à®®à¯, பாதà¯à®•à®¾à®ªà¯à®ªà®±à¯à®± பிற ஆதாரஙà¯à®•à®³à¯ இநà¯à®¤à®ªà¯ பகà¯à®•à®¤à¯à®¤à®¿à®²à¯ உளà¯à®³à®©. இநà¯à®¤ ஆதாரஙà¯à®•à®³à¯ˆ டà¯à®°à®¾à®©à¯à®¸à®¿à®Ÿà¯à®Ÿà®¿à®²à¯ இரà¯à®•à¯à®•à¯à®®à¯à®ªà¯‹à®¤à¯à®®à¯ பிறர௠பாரà¯à®µà¯ˆà®¯à®¿à®Ÿà®²à®¾à®®à¯, மேலà¯à®®à¯ பகà¯à®•à®¤à¯à®¤à®¿à®©à¯ தோறà¯à®±à®¤à¯à®¤à¯ˆ மாறà¯à®±, தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯ அதை மாறà¯à®±à®¿à®¯à®®à¯ˆà®•à¯à®•à®²à®¾à®®à¯.</translation>
@@ -441,6 +465,7 @@
<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="5843436854350372569"><ph name="DOMAIN" />கà¯à®•à¯à®šà¯ செலà¯à®² à®®à¯à®¯à®©à¯à®±à¯€à®°à¯à®•à®³à¯, ஆனால௠சேவையகம௠வலà¯à®µà®±à¯à®± கà¯à®±à®¿à®¯à¯€à®Ÿà¯à®Ÿà¯ˆà®•à¯ கொணà¯à®Ÿ சானà¯à®±à®¿à®¤à®´à¯ˆ வழஙà¯à®•à®¿à®¯à¯à®³à¯à®³à®¤à¯. தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ கà¯à®±à®¿à®¯à¯€à®Ÿà¯à®Ÿà¯ˆà®¤à¯ திரà¯à®Ÿà®¿à®¯à®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯, மேலà¯à®®à¯ சேவையகம௠நீஙà¯à®•à®³à¯ எதிரà¯à®ªà®¾à®°à¯à®¤à¯à®¤ சேவையகமாக இலà¯à®²à®¾à®®à®²à¯ இரà¯à®•à¯à®•à®•à¯à®•à¯‚டà¯à®®à¯ (நீஙà¯à®•à®³à¯ தொடரà¯à®ªà¯à®•à¯Šà®£à¯à®Ÿà®¿à®°à¯à®ªà¯à®ªà®¤à¯ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®¾à®³à®°à®¾à®• இரà¯à®•à¯à®•à®²à®¾à®®à¯). <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">பà¯à®¤à®¿à®¯ கோபà¯à®ªà¯à®±à¯ˆ</translation>
<translation id="5869405914158311789">இநà¯à®¤à®¤à¯ தளதà¯à®¤à¯ˆ அணà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ</translation>
@@ -450,6 +475,7 @@
<translation id="59107663811261420">இநà¯à®¤ வியாபாரிகà¯à®•à¯ Google Payments இல௠இநà¯à®¤ வகையான காரà¯à®Ÿà¯ ஆதரிகà¯à®•à®ªà¯à®ªà®Ÿà®¾à®¤à¯. வேறொர௠காரà¯à®Ÿà¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯.</translation>
<translation id="59174027418879706">செயலாகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯</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="5966707198760109579">வாரமà¯</translation>
<translation id="5967867314010545767">வரலாறà¯à®±à®¿à®²à®¿à®°à¯à®¨à¯à®¤à¯ அகறà¯à®±à¯</translation>
<translation id="5975083100439434680">சிறிதாகà¯à®•à¯</translation>
@@ -467,8 +493,11 @@
<translation id="614940544461990577">இவறà¯à®±à¯ˆà®šà¯ செயà¯à®¤à¯ பாரà¯à®•à¯à®•à®µà¯à®®à¯:</translation>
<translation id="6151417162996330722">சேவை சானà¯à®±à®¿à®¤à®´à¯ நீணà¯à®Ÿ செலà¯à®²à¯à®ªà®Ÿà®¿à®•à¯ காலதà¯à®¤à¯ˆà®•à¯ கொணà¯à®Ÿà¯à®³à¯à®³à®¤à¯.</translation>
<translation id="6165508094623778733">மேலà¯à®®à¯ அறிக</translation>
+<translation id="6177128806592000436">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®±à¯à®•à®¾à®© உஙà¯à®•à®³à¯ இணைபà¯à®ªà¯, பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®• இலà¯à®²à¯ˆ</translation>
<translation id="6203231073485539293">உஙà¯à®•à®³à¯ இணைய இணைபà¯à®ªà¯ˆà®šà¯ சரிபாரà¯à®•à¯à®•à®µà¯à®®à¯</translation>
<translation id="6218753634732582820">Chromium இலிரà¯à®¨à¯à®¤à¯ à®®à¯à®•à®µà®°à®¿à®¯à¯ˆ அகறà¯à®±à®µà®¾?</translation>
+<translation id="6251924700383757765">தனியà¯à®°à®¿à®®à¯ˆà®•à¯ கொளà¯à®•à¯ˆ</translation>
+<translation id="625755898061068298">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®±à¯à®•à®¾à®© பாதà¯à®•à®¾à®ªà¯à®ªà¯ எசà¯à®šà®°à®¿à®•à¯à®•à¯ˆà®•à®³à¯ˆ à®®à¯à®Ÿà®•à¯à®•à®¤à¯ தேரà¯à®µà¯à®šà¯†à®¯à¯à®¤à¯à®³à¯à®³à¯€à®°à¯à®•à®³à¯.</translation>
<translation id="6259156558325130047">&amp;மறà¯à®µà®°à®¿à®šà¯ˆà®ªà¯à®ªà®Ÿà¯à®¤à¯à®¤à®²à¯ˆ மீணà¯à®Ÿà¯à®®à¯ செயà¯</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> பà¯à®•à¯à®®à®¾à®°à¯à®•à¯à®•à¯à®•à®³à¯</translation>
<translation id="6264485186158353794">பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© நிலைகà¯à®•à¯à®¤à¯ திரà¯à®®à¯à®ªà¯</translation>
@@ -477,6 +506,7 @@
<translation id="6305205051461490394"><ph name="URL" />஠அடையமà¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="6321917430147971392">உஙà¯à®•à®³à¯ DNS அமைபà¯à®ªà¯à®•à®³à¯ˆà®šà¯ சரிபாரà¯à®•à¯à®•à®µà¯à®®à¯</translation>
<translation id="6328639280570009161">பிணைய யூகதà¯à®¤à¯ˆ à®®à¯à®Ÿà®•à¯à®•à¯à®µà®¤à®±à¯à®•à¯ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯</translation>
+<translation id="6328786501058569169">இத௠à®à®®à®¾à®±à¯à®±à®•à¯à®•à¯‚டிய தளமà¯</translation>
<translation id="6337534724793800597">பெயரினà¯à®ªà®Ÿà®¿ கொளà¯à®•à¯ˆà®•à®³à¯ˆ வடி</translation>
<translation id="6342069812937806050">இபà¯à®ªà¯‹à®¤à¯</translation>
<translation id="6345221851280129312">அறியபà¯à®ªà®Ÿà®¾à®¤ அளவà¯</translation>
@@ -485,6 +515,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{மேலà¯à®®à¯ 1 பரிநà¯à®¤à¯à®°à¯ˆ}other{மேலà¯à®®à¯ # பரிநà¯à®¤à¯à®°à¯ˆà®•à®³à¯}}</translation>
<translation id="6387478394221739770">பà¯à®¤à®¿à®¯ Chrome à®…à®®à¯à®šà®™à¯à®•à®³à®¿à®²à¯ ஆரà¯à®µà®®à¯ உளà¯à®³à®¤à®¾? chrome.com/beta இல௠எஙà¯à®•à®³à¯à®Ÿà¯ˆà®¯ பீடà¯à®Ÿà®¾ அலைவரிசையை à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯.</translation>
<translation id="6389758589412724634">நினைவகப௠பறà¯à®±à®¾à®•à¯à®•à¯à®±à¯ˆà®¯à®¿à®©à®¾à®²à¯, Chromium இநà¯à®¤ இணையபà¯à®ªà®•à¯à®•à®¤à¯à®¤à¯ˆà®•à¯ காடà¯à®Ÿà®µà®¿à®²à¯à®²à¯ˆ.</translation>
+<translation id="6404511346730675251">பà¯à®•à¯à®®à®¾à®°à¯à®•à¯à®•à¯à®•à®³à¯ˆà®¤à¯ திரà¯à®¤à¯à®¤à¯</translation>
+<translation id="6410264514553301377"><ph name="CREDIT_CARD" />கà¯à®•à®¾à®© காலாவதித௠தேதியையà¯à®®à¯ CVC எணà¯à®£à¯ˆà®¯à¯à®®à¯ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯</translation>
<translation id="6414888972213066896">இநà¯à®¤à®¤à¯ தளதà¯à®¤à¯ˆà®ªà¯ பாரà¯à®µà¯ˆà®¯à®¿à®Ÿà®²à®¾à®®à®¾ என, நீஙà¯à®•à®³à¯ பெறà¯à®±à¯‹à®°à®¿à®Ÿà®®à¯ கேடà¯à®Ÿà¯à®³à¯à®³à¯€à®°à¯à®•à®³à¯</translation>
<translation id="6416403317709441254">Chromium ஆல௠செயலà¯à®ªà®Ÿà¯à®¤à¯à®¤ à®®à¯à®Ÿà®¿à®¯à®¾à®¤ சிதைநà¯à®¤ அனà¯à®®à®¤à®¿à®šà¯ சானà¯à®±à¯à®•à®³à¯ˆ இணையதளம௠அனà¯à®ªà¯à®ªà®¿à®¯à¯à®³à¯à®³à®¤à®¾à®²à¯, இபà¯à®ªà¯‹à®¤à¯ <ph name="SITE" />கà¯à®•à¯à®šà¯ செலà¯à®² à®®à¯à®Ÿà®¿à®¯à®¾à®¤à¯. பொதà¯à®µà®¾à®• நெடà¯à®µà¯Šà®°à¯à®•à¯ பிழைகளà¯à®®à¯ பாதிபà¯à®ªà¯à®•à®³à¯à®®à¯ தறà¯à®•à®¾à®²à®¿à®•à®®à®¾à®©à®µà¯ˆà®¯à¯‡. இநà¯à®¤à®ªà¯ பகà¯à®•à®®à¯ சிறித௠நேரம௠கழிதà¯à®¤à¯à®šà¯ செயலà¯à®ªà®Ÿà¯à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">சானà¯à®±à®¿à®¤à®´à¯ திரà¯à®®à¯à®ªà®ªà¯à®ªà¯†à®±à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à®¾ எனà¯à®±à¯ சோதிகà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ.</translation>
@@ -494,10 +526,12 @@
<translation id="6458467102616083041">கொளà¯à®•à¯ˆ மூலம௠இயலà¯à®ªà¯à®¨à®¿à®²à¯ˆ தேடல௠மà¯à®Ÿà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à®¾à®²à¯, பாலிசியின௠மதிபà¯à®ªà¯ பà¯à®±à®•à¯à®•à®£à®¿à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯.</translation>
<translation id="6462969404041126431"><ph name="DOMAIN" /> என இநà¯à®¤à®šà¯ சேவையகதà¯à®¤à®¾à®²à¯ நிரூபிகà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ. இதன௠பாதà¯à®•à®¾à®ªà¯à®ªà¯à®šà¯ சானà¯à®±à®¿à®¤à®´à¯ ரதà¯à®¤à¯à®šà¯†à®¯à¯à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯. இத௠தவறான உளà¯à®³à®®à¯ˆà®µà®¾à®²à¯ à®à®±à¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯ அலà¯à®²à®¤à¯ உஙà¯à®•à®³à¯ இணைபà¯à®ªà®¿à®²à¯ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯ கà¯à®±à¯à®•à¯à®•à®¿à®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">சாதனக௠கொளà¯à®•à¯ˆà®•à®³à¯</translation>
+<translation id="6477321094435799029">இநà¯à®¤à®ªà¯ பகà¯à®•à®¤à¯à®¤à®¿à®²à¯ வழகà¯à®•à®¤à¯à®¤à®¿à®±à¯à®•à¯ மாறான கà¯à®±à®¿à®¯à¯€à®Ÿà¯ இரà¯à®ªà¯à®ªà®¤à¯ˆ Chrome கணà¯à®Ÿà®±à®¿à®¨à¯à®¤à¯à®³à¯à®³à®¤à¯, மேலà¯à®®à¯ உஙà¯à®•à®³à¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ தகவலை (எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà¯: கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯, ஃபோன௠எணà¯à®•à®³à¯ மறà¯à®±à¯à®®à¯ கிரெடிட௠காரà¯à®Ÿà¯à®•à®³à¯) பாதà¯à®•à®¾à®•à¯à®• அதைத௠தடà¯à®¤à¯à®¤à¯à®³à¯à®³à®¤à¯.</translation>
<translation id="6489534406876378309">சிதைவà¯à®•à®³à¯ˆà®ªà¯ பதிவேறà¯à®±à¯à®µà®¤à¯ˆà®¤à¯ தொடஙà¯à®•à¯</translation>
<translation id="6529602333819889595">&amp;நீகà¯à®•à¯à®¤à®²à¯ˆ மீணà¯à®Ÿà¯à®®à¯ செயà¯</translation>
<translation id="6534179046333460208">இயலà¯à®¨à®¿à®²à¯ˆ இணையப௠பரிநà¯à®¤à¯à®°à¯ˆà®•à®³à¯</translation>
<translation id="6550675742724504774">விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯‡à®°à¯à®µà¯à®•à®³à¯</translation>
+<translation id="6556239504065605927">பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© இணைபà¯à®ªà¯</translation>
<translation id="6563469144985748109">இனà¯à®©à¯à®®à¯ உஙà¯à®•à®³à¯ நிரà¯à®µà®¾à®•à®¿ அனà¯à®®à®¤à®¿à®•à¯à®•à®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" />கà¯à®•à¯à®®à¯ கà¯à®±à¯ˆà®µà®¾à®• உளà¯à®³à®¤à¯</translation>
<translation id="6596325263575161958">கà¯à®±à®¿à®¯à®¾à®•à¯à®• விரà¯à®ªà¯à®ªà®™à¯à®•à®³à¯</translation>
@@ -506,7 +540,6 @@
<translation id="6644283850729428850">இநà¯à®¤à®•à¯ கொளà¯à®•à¯ˆ தவிரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯.</translation>
<translation id="6652240803263749613"><ph name="DOMAIN" /> என இநà¯à®¤à®šà¯ சேவையகதà¯à®¤à®¾à®²à¯ நிரூபிகà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ. இதன௠பாதà¯à®•à®¾à®ªà¯à®ªà¯à®šà¯ சானà¯à®±à®¿à®¤à®´à¯ˆ உஙà¯à®•à®³à¯ கணினியின௠இயகà¯à®• à®®à¯à®±à¯ˆà®®à¯ˆ நமà¯à®ªà®µà®¿à®²à¯à®²à¯ˆ. இத௠தவறான உளà¯à®³à®®à¯ˆà®µà®¾à®²à¯ à®à®±à¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯ அலà¯à®²à®¤à¯ உஙà¯à®•à®³à¯ இணைபà¯à®ªà®¿à®²à¯ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯ கà¯à®±à¯à®•à¯à®•à®¿à®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">கோபà¯à®ªà¯à®±à¯ˆà®¯à¯ˆà®¤à¯ திரà¯à®¤à¯à®¤à¯</translation>
-<translation id="6660210980321319655">தானாக அறிவிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯: <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Chromium இலிரà¯à®¨à¯à®¤à¯ படிவப௠பரிநà¯à®¤à¯à®°à¯ˆà®¯à¯ˆ அகறà¯à®±à®µà®¾?</translation>
<translation id="6685834062052613830">வெளியேறி, அமைபà¯à®ªà¯ˆ à®®à¯à®Ÿà®¿à®•à¯à®•à®µà¯à®®à¯</translation>
<translation id="6710213216561001401">à®®à¯à®¨à¯à®¤à¯ˆà®¯à®¤à¯</translation>
@@ -518,6 +551,7 @@
<translation id="6753269504797312559">கொளà¯à®•à¯ˆ மதிபà¯à®ªà¯</translation>
<translation id="6757797048963528358">உஙà¯à®•à®³à¯ சாதனம௠உறகà¯à®•à®¨à®¿à®²à¯ˆà®•à¯à®•à¯à®šà¯ செனà¯à®±à®¤à¯.</translation>
<translation id="6778737459546443941">இனà¯à®©à¯à®®à¯ உஙà¯à®•à®³à¯ பெறà¯à®±à¯‹à®°à¯ அனà¯à®®à®¤à®¿à®•à¯à®•à®µà®¿à®²à¯à®²à¯ˆ</translation>
+<translation id="6810899417690483278">தனிபà¯à®ªà®¯à®©à®¾à®•à¯à®•à®²à¯ à®à®Ÿà®¿</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" /> இல௠உளà¯à®³ வலைபà¯à®ªà®•à¯à®•à®®à¯ தறà¯à®ªà¯‹à®¤à¯ கிடைகà¯à®•à®µà®¿à®²à¯à®²à¯ˆ. அத௠அதிக சà¯à®®à¯ˆà®¯à®¿à®²à¯ இரà¯à®•à¯à®•à®²à®¾à®®à¯ அலà¯à®²à®¤à¯ பராமரிபà¯à®ªà®¿à®©à¯ காரணமாக செயலà¯à®ªà®Ÿà®¾à®®à®²à¯ இரà¯à®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="6831043979455480757">மொழிபெயரà¯</translation>
@@ -538,6 +572,8 @@
<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>
+<translation id="7064851114919012435">தொடரà¯à®ªà¯à®¤à¯ தகவலà¯</translation>
+<translation id="7079718277001814089">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®²à¯ தீபà¯à®ªà¯Šà®°à¯à®³à¯ உளà¯à®³à®¤à¯</translation>
<translation id="7087282848513945231">மாகாணமà¯</translation>
<translation id="7088615885725309056">பழையவை</translation>
<translation id="7090678807593890770">Google இல௠<ph name="LINK" />à®à®¤à¯ தேடவà¯à®®à¯</translation>
@@ -552,6 +588,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> பாதà¯à®•à®¾à®ªà¯à®ªà¯à®¤à¯ தரநிலைகளà¯à®•à¯à®•à¯ உடà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="721197778055552897">இநà¯à®¤ சிகà¯à®•à®²à¯ கà¯à®±à®¿à®¤à¯à®¤à¯ <ph name="BEGIN_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LINK" />.</translation>
<translation id="7219179957768738017">இணைபà¯à®ªà¯ <ph name="SSL_VERSION" /> à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®•à®¿à®±à®¤à¯.</translation>
+<translation id="724691107663265825">தளதà¯à®¤à®¿à®²à¯ தீபà¯à®ªà¯Šà®°à¯à®³à¯ உளà¯à®³à®¤à¯</translation>
<translation id="724975217298816891">காரà¯à®Ÿà¯ விவரஙà¯à®•à®³à¯ˆà®ªà¯ பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®•, <ph name="CREDIT_CARD" /> இன௠காலாவதி தேதியையà¯à®®à¯ CVC எணà¯à®£à¯ˆà®¯à¯à®®à¯ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯. உறà¯à®¤à®¿à®šà¯†à®¯à¯à®¤ பினà¯à®©à®°à¯, உஙà¯à®•à®³à¯ காரà¯à®Ÿà¯ விவரஙà¯à®•à®³à¯ இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®±à¯à®•à¯à®ªà¯ பகிரபà¯à®ªà®Ÿà¯à®®à¯.</translation>
<translation id="725866823122871198">உஙà¯à®•à®³à¯ கணினியின௠தேதி மறà¯à®±à¯à®®à¯ நேரம௠(<ph name="DATE_AND_TIME" />) தவறாக இரà¯à®ªà¯à®ªà®¤à®¾à®²à¯ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> கà¯à®•à®¾à®© தனிபà¯à®ªà®Ÿà¯à®Ÿ இணைபà¯à®ªà¯ˆ à®à®±à¯à®ªà®Ÿà¯à®¤à¯à®¤ à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="7269802741830436641">இநà¯à®¤ வலைபà¯à®ªà®•à¯à®•à®¤à¯à®¤à®¿à®²à¯ ஒர௠திரà¯à®ªà¯à®ªà®¿à®µà®¿à®Ÿà®²à¯ சà¯à®´à®±à¯à®šà®¿ உளà¯à®³à®¤à¯</translation>
@@ -607,6 +644,7 @@
<translation id="7658239707568436148">ரதà¯à®¤à¯ செயà¯</translation>
<translation id="7667346355482952095">கிடைதà¯à®¤ பாலிசி டோகà¯à®•à®©à¯ காலியாக உளà¯à®³à®¤à¯ அலà¯à®²à®¤à¯ தறà¯à®ªà¯‹à®¤à¯ˆà®¯ டோகà¯à®•à®©à¯à®Ÿà®©à¯ பொரà¯à®¨à¯à®¤à®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="7668654391829183341">அறியபà¯à®ªà®Ÿà®¾à®¤ சாதனமà¯</translation>
+<translation id="7669271284792375604">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®²à¯ உளà¯à®³ ஹேகà¯à®•à®°à¯à®•à®³à¯, உஙà¯à®•à®³à¯ˆ à®à®®à®¾à®±à¯à®±à®¿ (எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®•, உஙà¯à®•à®³à¯ à®®à¯à®•à®ªà¯à®ªà¯à®ªà¯ பகà¯à®•à®¤à¯à®¤à¯ˆ மாறà¯à®±à¯à®µà®¤à¯ அலà¯à®²à®¤à¯ நீஙà¯à®•à®³à¯ பாரà¯à®µà¯ˆà®¯à®¿à®Ÿà¯à®®à¯ தளஙà¯à®•à®³à®¿à®²à¯ கூடà¯à®¤à®²à¯ விளமà¯à®ªà®°à®™à¯à®•à®³à¯ˆà®•à¯ காடà¯à®Ÿà¯à®µà®¤à¯), உஙà¯à®•à®³à¯ உலாவல௠அனà¯à®ªà®µà®¤à¯à®¤à¯ˆà®ªà¯ பாதிகà¯à®•à®•à¯à®•à¯‚டிய நிரலà¯à®•à®³à¯ˆ நிறà¯à®µ வைகà¯à®•à®²à®¾à®®à¯.</translation>
<translation id="7674629440242451245">பà¯à®¤à®¿à®¯ Chrome à®…à®®à¯à®šà®™à¯à®•à®³à®¿à®²à¯ ஆரà¯à®µà®®à®¾à®• உளà¯à®³à¯€à®°à¯à®•à®³à®¾? chrome.com/dev இல௠எஙà¯à®•à®³à¯à®Ÿà¯ˆà®¯ dev சேனலை à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /><ph name="SITE" /> (பாதà¯à®•à®¾à®ªà¯à®ªà®±à¯à®± தளமà¯) கà¯à®•à¯à®šà¯ செலà¯à®²à®µà¯à®®à¯<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">தறà¯à®•à®¾à®²à®¿à®•à®šà¯ சேமிபà¯à®ªà®¿à®²à®¿à®°à¯à®¨à¯à®¤à¯ இநà¯à®¤à®¤à¯ தளதà¯à®¤à¯ˆ à®à®±à¯à®± à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ</translation>
@@ -622,14 +660,17 @@
<translation id="7800304661137206267">செயà¯à®¤à®¿ à®…à®™à¯à®•à¯€à®•à®°à®¿à®ªà¯à®ªà®¿à®±à¯à®•à®¾à®•, <ph name="KX" /> ஠விசைப௠பரிமாறà¯à®± செயலà¯à®®à¯à®±à¯ˆà®¯à®¾à®•à®•à¯ கொணà¯à®Ÿà¯, <ph name="MAC" /> உடன௠<ph name="CIPHER" /> à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿ இணைபà¯à®ªà®¾à®©à®¤à¯ கà¯à®±à®¿à®¯à®¾à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯.</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="7887683347370398519">CVCà®à®šà¯ சோதிதà¯à®¤à¯, மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯</translation>
<translation id="7912024687060120840">கோபà¯à®ªà¯à®±à¯ˆà®¯à®¿à®²à¯:</translation>
<translation id="7935318582918952113">DOM டிஸà¯à®Ÿà®¿à®²à¯à®²à®°à¯</translation>
<translation id="7938958445268990899">சேவையகச௠சானà¯à®±à®¿à®¤à®´à¯ இனà¯à®©à¯à®®à¯ செலà¯à®²à¯à®ªà®Ÿà®¿à®¯à®¾à®•à®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="7942349550061667556">சிவபà¯à®ªà¯</translation>
+<translation id="7947285636476623132">காலாவதி ஆணà¯à®Ÿà¯ˆà®šà¯ சரிபாரà¯à®¤à¯à®¤à¯, மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®²à®µà¯à®®à¯</translation>
<translation id="7951415247503192394">(32-பிடà¯)</translation>
<translation id="7956713633345437162">மொபைல௠பà¯à®•à¯à®®à®¾à®°à¯à®•à¯à®•à¯à®•à®³à¯</translation>
<translation id="7961015016161918242">எபà¯à®ªà¯‹à®¤à¯à®®à¯ இலà¯à®²à¯ˆ</translation>
@@ -637,7 +678,10 @@
<translation id="7983301409776629893"><ph name="ORIGINAL_LANGUAGE" /> à® <ph name="TARGET_LANGUAGE" /> கà¯à®•à¯ எபà¯à®ªà¯‹à®¤à¯à®®à¯ மொழிபெயரà¯à®ªà¯à®ªà¯ செயà¯à®•</translation>
<translation id="7995512525968007366">கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="8012647001091218357">தறà¯à®ªà¯‹à®¤à¯ எஙà¯à®•à®³à®¾à®²à¯ உஙà¯à®•à®³à¯ பெறà¯à®±à¯‹à®°à¯à®•à®³à¯ˆà®¤à¯ தொடரà¯à®ªà¯à®•à¯Šà®³à¯à®³ à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ. மீணà¯à®Ÿà¯à®®à¯ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯.</translation>
+<translation id="8025119109950072390">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®²à¯ உளà¯à®³ ஹேகà¯à®•à®°à¯à®•à®³à¯, உஙà¯à®•à®³à¯ˆ à®à®®à®¾à®±à¯à®±à®¿, மெனà¯à®ªà¯Šà®°à¯à®³à¯ˆ நிறà¯à®µà¯à®µà®¤à¯ அலà¯à®²à®¤à¯ தனிபà¯à®ªà®Ÿà¯à®Ÿ தகவலை (எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®•, கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯, ஃபோன௠எணà¯à®•à®³à¯ அலà¯à®²à®¤à¯ கிரெடிட௠காரà¯à®Ÿà¯à®•à®³à¯) வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®µà®¤à¯ போனà¯à®± உஙà¯à®•à®³à¯à®•à¯à®•à¯ ஆபதà¯à®¤à¯ˆ விளைவிகà¯à®•à¯à®®à¯ செயலà¯à®•à®³à¯ˆà®šà¯ செயà¯à®¯ வைகà¯à®•à®²à®¾à®®à¯.</translation>
+<translation id="803030522067524905">சமீபதà¯à®¤à®¿à®²à¯ Google இன௠பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®© உலாவலானத௠<ph name="SITE" /> இல௠ஃபிஷிஙà¯à®•à¯ˆà®•à¯ கணà¯à®Ÿà®±à®¿à®¨à¯à®¤à¯à®³à¯à®³à®¤à¯. ஃபிஷிங௠தளஙà¯à®•à®³à¯ பிற தளஙà¯à®•à®³à¯ˆà®ªà¯ போலவே தோறà¯à®±à®®à®³à®¿à®¤à¯à®¤à¯ உஙà¯à®•à®³à¯ˆ à®à®®à®¾à®±à¯à®±à¯à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">இநà¯à®¤à®ªà¯ பகà¯à®•à®®à¯ <ph name="SOURCE_LANGUAGE" /> மொழியில௠உளà¯à®³à®¤à¯. இதை <ph name="TARGET_LANGUAGE" /> கà¯à®•à¯ மொழிபெயரà¯à®•à¯à®•à®µà®¾?</translation>
+<translation id="8041089156583427627">கரà¯à®¤à¯à®¤à¯à®¤à¯ தெரிவிகà¯à®•à®µà¯à®®à¯</translation>
<translation id="8088680233425245692">கடà¯à®Ÿà¯à®°à¯ˆà®¯à¯ˆà®•à¯ காடà¯à®Ÿà¯à®µà®¤à®¿à®²à¯ தோலà¯à®µà®¿.</translation>
<translation id="8089520772729574115">1 மெ.பை. கà¯à®•à¯à®®à¯ கà¯à®±à¯ˆà®µà®¾à®• உளà¯à®³à®¤à¯</translation>
<translation id="8091372947890762290">சேவையகதà¯à®¤à®¿à®²à¯ செயலாகà¯à®•à®®à¯ நிலà¯à®µà¯ˆà®¯à®¿à®²à¯à®³à¯à®³à®¤à¯</translation>
@@ -648,9 +692,11 @@
<translation id="8150722005171944719"><ph name="URL" /> இல௠உளà¯à®³ கோபà¯à®ªà¯ படிகà¯à®•à®•à¯ கூடியதாக இலà¯à®²à¯ˆ. அத௠அகறà¯à®±à®ªà¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯, நகரà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯ அலà¯à®²à®¤à¯ கோபà¯à®ªà¯ அனà¯à®®à®¤à®¿à®•à®³à¯ அணà¯à®•à®²à¯ˆà®¤à¯ தடà¯à®¤à¯à®¤à®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="8194797478851900357">&amp;நகரà¯à®¤à¯à®¤à®²à¯ˆà®šà¯ செயலà¯à®¤à®µà®¿à®°à¯</translation>
<translation id="8201077131113104583">"<ph name="EXTENSION_ID" />" à®à®Ÿà®¿à®¯à¯à®Ÿà®©à¯ கூடிய நீடà¯à®Ÿà®¿à®ªà¯à®ªà®¿à®±à¯à®•à®¾à®© தவறான பà¯à®¤à¯à®ªà¯à®ªà®¿à®ªà¯à®ªà¯ URL.</translation>
+<translation id="8202097416529803614">ஆரà¯à®Ÿà®°à¯ பறà¯à®±à®¿à®¯ சà¯à®°à¯à®•à¯à®•à®µà®¿à®µà®°à®®à¯</translation>
<translation id="8218327578424803826">ஒதà¯à®•à¯à®•à®¿à®¯ இரà¯à®ªà¯à®ªà®¿à®Ÿà®®à¯:</translation>
<translation id="8225771182978767009">இநà¯à®¤à®•à¯ கணினியை அமைதà¯à®¤ நபர௠இநà¯à®¤à®¤à¯ தளதà¯à®¤à¯ˆà®¤à¯ தடà¯à®•à¯à®•à¯à®®à¯à®ªà®Ÿà®¿ தேரà¯à®µà¯à®šà¯†à®¯à¯à®¤à¯à®³à¯à®³à®¾à®°à¯.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> தளதà¯à®¤à®¿à®²à¯ தறà¯à®ªà¯‹à®¤à¯ இரà¯à®•à¯à®•à¯à®®à¯ தாகà¯à®•à¯à®ªà®µà®°à¯à®•à®³à¯, உஙà¯à®•à®³à¯ படஙà¯à®•à®³à¯, கடவà¯à®šà¯à®šà¯Šà®±à¯à®•à®³à¯, செயà¯à®¤à®¿à®•à®³à¯, கிரெடிக௠காரà¯à®Ÿà¯à®•à®³à¯ போனà¯à®± தகவலà¯à®•à®³à¯ˆà®¤à¯ திரà¯à®Ÿ அலà¯à®²à®¤à¯ அழிகà¯à®•à®•à¯à®•à¯‚டிய ஆபதà¯à®¤à®¾à®© நிரலà¯à®•à®³à¯ˆ உஙà¯à®•à®³à¯ கணினியில௠நிறà¯à®µ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®²à®¾à®®à¯.</translation>
<translation id="8241707690549784388">நீஙà¯à®•à®³à¯ தேடà¯à®®à¯ பகà¯à®•à®®à®¾à®©à®¤à¯ நீஙà¯à®•à®³à¯ உளà¯à®³à®¿à®Ÿà¯à®Ÿ தகவலைப௠பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®¯à®¤à¯. மீணà¯à®Ÿà¯à®®à¯ அநà¯à®¤ பகà¯à®•à®¤à¯à®¤à®¿à®±à¯à®•à¯ திரà¯à®®à¯à®ªà®¿à®©à®¾à®²à¯, நீஙà¯à®•à®³à¯ செயà¯à®¤ à®à®¤à¯‡à®©à¯à®®à¯ செயலை மீணà¯à®Ÿà¯à®®à¯ செயà¯à®¯ வேணà¯à®Ÿà®¿à®¯à®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯. தொடர விரà¯à®®à¯à®ªà¯à®•à®¿à®±à¯€à®°à¯à®•à®³à®¾?</translation>
<translation id="8249320324621329438">கடைசியாக எடà¯à®¤à¯à®¤à®¤à¯:</translation>
<translation id="8261506727792406068">நீகà¯à®•à¯</translation>
@@ -659,11 +705,13 @@
<translation id="8294431847097064396">மூலமà¯</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="8349305172487531364">பà¯à®•à¯à®®à®¾à®°à¯à®•à¯ படà¯à®Ÿà®¿</translation>
<translation id="8363502534493474904">விமானப௠பயனà¯à®®à¯à®±à¯ˆà®¯à¯ˆ à®®à¯à®Ÿà®•à¯à®•à¯à®¤à®²à¯</translation>
<translation id="8364627913115013041">அமைகà¯à®•à®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="8380941800586852976">ஆபதà¯à®¤à®¾à®©à®¤à¯</translation>
<translation id="8382348898565613901">நீஙà¯à®•à®³à¯ சமீபதà¯à®¤à®¿à®²à¯ பாரà¯à®µà¯ˆà®¯à®¿à®Ÿà¯à®Ÿ பà¯à®¤à¯à®¤à®•à®•à¯à®•à¯à®±à®¿à®•à®³à¯ இஙà¯à®•à¯‡ தோனà¯à®±à¯à®®à¯</translation>
+<translation id="8398259832188219207">சிதைவ௠அறிகà¯à®•à¯ˆ பதிவேறà¯à®±à®ªà¯à®ªà®Ÿà¯à®Ÿ தேதி: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">செயலிழபà¯à®ªà¯à®•à®³à¯ (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">நீஙà¯à®•à®³à¯ கணà¯à®Ÿà®¿à®ªà¯à®ªà®¾à®• ஒரே கடவà¯à®šà¯à®šà¯Šà®±à¯à®±à¯Šà®Ÿà®°à¯ˆ இர௠மà¯à®±à¯ˆ உளà¯à®³à®¿à®Ÿ வேணà¯à®Ÿà¯à®®à¯.</translation>
<translation id="8428213095426709021">அமைபà¯à®ªà¯à®•à®³à¯</translation>
@@ -675,9 +723,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> பதிலளிகà¯à®• நீணà¯à®Ÿ நேரம௠எடà¯à®¤à¯à®¤à¯à®•à¯à®•à¯Šà®£à¯à®Ÿà®¤à¯.</translation>
<translation id="852346902619691059"><ph name="DOMAIN" /> என இநà¯à®¤à®šà¯ சேவையகதà¯à®¤à®¾à®²à¯ நிரூபிகà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ. இதன௠பாதà¯à®•à®¾à®ªà¯à®ªà¯à®šà¯ சானà¯à®±à®¿à®¤à®´à¯ˆ உஙà¯à®•à®³à¯ சாதனதà¯à®¤à®¿à®©à¯ இயகà¯à®• à®®à¯à®±à¯ˆà®®à¯ˆ நமà¯à®ªà®µà®¿à®²à¯à®²à¯ˆ. இத௠தவறான உளà¯à®³à®®à¯ˆà®µà®¾à®²à¯ à®à®±à¯à®ªà®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯ அலà¯à®²à®¤à¯ உஙà¯à®•à®³à¯ இணைபà¯à®ªà®¿à®²à¯ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯ கà¯à®±à¯à®•à¯à®•à®¿à®Ÿà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯. <ph name="BEGIN_LEARN_MORE_LINK" />மேலà¯à®®à¯ அறிக<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Google Payments இல௠இநà¯à®¤ வகையான காரà¯à®Ÿà¯ ஆதரிகà¯à®•à®ªà¯à®ªà®Ÿà®¾à®¤à¯. வேறொர௠காரà¯à®Ÿà¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à®µà¯à®®à¯.</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="8553075262323480129">பகà¯à®•à®¤à¯à®¤à®¿à®©à¯ மொழியைத௠தீரà¯à®®à®¾à®©à®¿à®•à¯à®• à®®à¯à®Ÿà®¿à®¯à®¾à®¤à®¤à®¾à®²à¯ மொழிபெயரà¯à®ªà¯à®ªà¯ தோலà¯à®µà®¿à®¯à®Ÿà¯ˆà®¨à¯à®¤à®¤à¯.</translation>
<translation id="8559762987265718583">உஙà¯à®•à®³à¯ சாதனதà¯à®¤à®¿à®©à¯ தேதி மறà¯à®±à¯à®®à¯ நேரம௠(<ph name="DATE_AND_TIME" />) தவறாக உளà¯à®³à®¤à®¾à®²à¯ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> கà¯à®•à®¾à®© தனிபà¯à®ªà®Ÿà¯à®Ÿ இணைபà¯à®ªà¯ˆ à®à®±à¯à®ªà®Ÿà¯à®¤à¯à®¤ à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ.</translation>
-<translation id="856992080682148">இநà¯à®¤à®¤à¯ தளதà¯à®¤à®¿à®±à¯à®•à®¾à®© சானà¯à®±à®¿à®¤à®´à¯ 2017 ஆம௠ஆணà¯à®Ÿà¯ அலà¯à®²à®¤à¯ அதறà¯à®•à¯à®ªà¯ பினà¯à®©à®°à¯ காலாவதியாகà¯à®®à¯, சானà¯à®±à®¿à®¤à®´à¯ சஙà¯à®•à®¿à®²à®¿à®¯à®¿à®²à¯ SHA-1à®à®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿à®•à¯ கையொபà¯à®ªà®®à®¿à®Ÿà¯à®Ÿ சானà¯à®±à®¿à®¤à®´à¯ உளà¯à®³à®¤à¯.</translation>
<translation id="8571890674111243710"><ph name="LANGUAGE" /> கà¯à®•à¯ பகà¯à®•à®¤à¯à®¤à¯ˆ மொழிபெயரà¯à®•à¯à®•à®¿à®±à®¤à¯...</translation>
<translation id="859285277496340001">இநà¯à®¤ சானà¯à®±à®¿à®¤à®´à¯ திரà¯à®®à¯à®ªà®ªà¯à®ªà¯†à®±à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à®¾ எனà¯à®ªà®¤à¯ˆà®šà¯ சரிபாரà¯à®ªà¯à®ªà®¤à®±à¯à®•à®¾à®© செயலà¯à®®à¯à®±à¯ˆ இதில௠இலà¯à®²à¯ˆ.</translation>
<translation id="8620436878122366504">இனà¯à®©à¯à®®à¯ உஙà¯à®•à®³à¯ பெறà¯à®±à¯‹à®°à¯ அனà¯à®®à®¤à®¿à®•à¯à®•à®µà®¿à®²à¯à®²à¯ˆ</translation>
@@ -690,10 +738,12 @@
<translation id="8740359287975076522"><ph name="HOST_NAME" />’s &lt;abbr id="dnsDefinition"&gt;DNS à®®à¯à®•à®µà®°à®¿à®¯à¯ˆà®•à¯&lt;/abbr&gt; கணà¯à®Ÿà®±à®¿à®¯ à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ. சிகà¯à®•à®²à¯ˆ ஆயà¯à®µà¯ செயà¯à®•à®¿à®±à®¤à¯.</translation>
<translation id="8790007591277257123">&amp;நீகà¯à®•à¯à®¤à®²à¯ˆ மீணà¯à®Ÿà¯à®®à¯ செயà¯</translation>
<translation id="8798099450830957504">இயலà¯à®ªà¯à®¨à®¿à®²à¯ˆ</translation>
+<translation id="8800988563907321413">உஙà¯à®•à®³à¯ à®…à®°à¯à®•à®¿à®²à¯à®³à¯à®³à®µà®±à¯à®±à¯à®•à¯à®•à®¾à®© பரிநà¯à®¤à¯à®°à¯ˆà®•à®³à¯ இஙà¯à®•à¯‡ தோனà¯à®±à¯à®®à¯</translation>
<translation id="8804164990146287819">தனியà¯à®°à®¿à®®à¯ˆà®•à¯ கொளà¯à®•à¯ˆ</translation>
<translation id="8820817407110198400">பà¯à®¤à¯à®¤à®•à®•à¯à®•à¯à®±à®¿à®•à®³à¯</translation>
<translation id="8834246243508017242">தொடரà¯à®ªà¯à®•à®³à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿ தனà¯à®©à®¿à®°à®ªà¯à®ªà®¿à®¯à¯ˆ இயகà¯à®•à¯â€¦</translation>
<translation id="883848425547221593">மறà¯à®± பà¯à®•à¯à®®à®¾à®°à¯à®•à¯à®•à¯à®•à®³à¯</translation>
+<translation id="884264119367021077">ஷிபà¯à®ªà®¿à®™à¯ à®®à¯à®•à®µà®°à®¿</translation>
<translation id="884923133447025588">திரà¯à®®à¯à®ªà®ªà¯à®ªà¯†à®±à¯à®¤à®²à¯ செயலà¯à®®à¯à®±à¯ˆ காணபà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ.</translation>
<translation id="885730110891505394">Google உடன௠பகிரà¯à®ªà®µà¯ˆ</translation>
<translation id="8866481888320382733">கொளà¯à®•à¯ˆ அமைபà¯à®ªà¯à®•à®³à¯ˆ அலசà¯à®µà®¤à®¿à®²à¯ பிழை</translation>
@@ -711,19 +761,22 @@
<translation id="8971063699422889582">சேவையகச௠சானà¯à®±à®¿à®¤à®´à¯ காலாவதியானதà¯.</translation>
<translation id="8987927404178983737">மாதமà¯</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">தளதà¯à®¤à®¿à®²à¯ தீஙà¯à®•à®¿à®´à¯ˆà®•à¯à®•à¯à®®à¯ நிரலà¯à®•à®³à¯ அதிகளவில௠உளà¯à®³à®©</translation>
<translation id="9001074447101275817"><ph name="DOMAIN" /> எனà¯à®•à®¿à®± பà¯à®°à®¾à®•à¯à®¸à®¿à®•à¯à®•à¯ பயனரà¯à®ªà¯†à®¯à®°à¯à®®à¯ கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯à®®à¯ தேவை.</translation>
<translation id="901974403500617787">கணினி அளவில௠பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®®à¯ கொடிகள௠பினà¯à®µà®°à¯à®®à¯ உரிமையாளரால௠மடà¯à®Ÿà¯à®®à¯‡ அமைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">இநà¯à®¤à®ªà¯ பகà¯à®•à®®à¯ <ph name="TARGET_LANGUAGE" /> கà¯à®•à¯ மொழிபெயரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯</translation>
+<translation id="9035022520814077154">பாதà¯à®•à®¾à®ªà¯à®ªà¯à®ªà¯ பிழை</translation>
<translation id="9038649477754266430">பகà¯à®•à®™à¯à®•à®³à¯ˆ இனà¯à®©à¯à®®à¯ விரைவாக à®à®±à¯à®±, யூக சேவையைப௠பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®µà¯à®®à¯</translation>
<translation id="9039213469156557790">மேலà¯à®®à¯, பாதà¯à®•à®¾à®ªà¯à®ªà®±à¯à®± பிற ஆதாரஙà¯à®•à®³à¯ இநà¯à®¤à®ªà¯ பகà¯à®•à®¤à¯à®¤à®¿à®²à¯ உளà¯à®³à®©. இநà¯à®¤ ஆதாரஙà¯à®•à®³à¯ˆ டà¯à®°à®¾à®©à¯à®¸à®¿à®Ÿà¯à®Ÿà®¿à®²à¯ இரà¯à®•à¯à®•à¯à®®à¯à®ªà¯‹à®¤à¯à®®à¯ பிறர௠பாரà¯à®µà¯ˆà®¯à®¿à®Ÿà®²à®¾à®®à¯, மேலà¯à®®à¯ பகà¯à®•à®¤à¯à®¤à®¿à®©à¯ செயலà¯à®ªà®¾à®Ÿà¯à®Ÿà¯ˆ மாறà¯à®±, தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯ அதை மாறà¯à®±à®¿à®¯à®®à¯ˆà®•à¯à®•à®²à®¾à®®à¯.</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> இல௠உளà¯à®³ தீஙà¯à®•à®¿à®´à¯ˆà®ªà¯à®ªà®µà®°à¯à®•à®³à¯, உஙà¯à®•à®³à¯ உலாவல௠அனà¯à®ªà®µà®¤à¯à®¤à¯ˆà®ªà¯ பாதிகà¯à®•à¯à®®à¯ நிரலà¯à®•à®³à¯ˆ நிறà¯à®µà¯à®µà®¤à®±à¯à®•à¯ உஙà¯à®•à®³à¯ˆ à®à®®à®¾à®±à¯à®± à®®à¯à®¯à®±à¯à®šà®¿à®¤à¯à®¤à®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯ (எடà¯à®¤à¯à®¤à¯à®•à¯à®•à®¾à®Ÿà¯à®Ÿà®¾à®•, உஙà¯à®•à®³à¯ à®®à¯à®•à®ªà¯à®ªà¯à®ªà¯à®ªà®•à¯à®•à®¤à¯à®¤à¯ˆ மாறà¯à®±à¯à®µà®¤à¯ அலà¯à®²à®¤à¯ நீஙà¯à®•à®³à¯ பாரà¯à®µà¯ˆà®¯à®¿à®Ÿà¯à®®à¯ தளஙà¯à®•à®³à®¿à®²à¯ கூடà¯à®¤à®²à¯ விளமà¯à®ªà®°à®™à¯à®•à®³à¯ˆà®•à¯ காடà¯à®Ÿà¯à®µà®¤à¯).</translation>
<translation id="9050666287014529139">கடவà¯à®šà¯à®šà¯Šà®±à¯à®±à¯Šà®Ÿà®°à¯</translation>
<translation id="9065203028668620118">திரà¯à®¤à¯à®¤à¯</translation>
<translation id="9068849894565669697">வணà¯à®£à®¤à¯à®¤à¯ˆà®¤à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯</translation>
<translation id="9076283476770535406">இதில௠பெரியவரà¯à®•à®³à¯à®•à¯à®•à®¾à®© உளà¯à®³à®Ÿà®•à¯à®•à®®à¯ இரà¯à®•à¯à®•à®•à¯à®•à¯‚டà¯à®®à¯</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> பகà¯à®•à®®à¯ செயலà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ</translation>
<translation id="9103872766612412690">வழகà¯à®•à®®à®¾à®•, <ph name="SITE" /> உஙà¯à®•à®³à¯ தகவலைப௠பாதà¯à®•à®¾à®ªà¯à®ªà®¤à®±à¯à®•à®¾à®• à®®à¯à®±à¯ˆà®®à¯ˆà®¯à®¾à®•à¯à®•à®¤à¯à®¤à¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®•à®¿à®±à®¤à¯.
இநà¯à®¤ à®®à¯à®±à¯ˆ <ph name="SITE" /> உடன௠இணைவதறà¯à®•à¯ Chromium à®®à¯à®¯à®±à¯à®šà®¿à®¤à¯à®¤à®ªà¯‹à®¤à¯ வழகà¯à®•à®¤à¯à®¤à®¿à®±à¯à®•à¯ மாறான, தவறான நறà¯à®šà®¾à®©à¯à®±à®¿à®¤à®´à¯à®•à®³à¯ˆ இணையதளம௠வழஙà¯à®•à®¿à®¯à®¤à¯. தாகà¯à®•à¯à®ªà®µà®°à¯ தனà¯à®©à¯ˆ <ph name="SITE" /> ஆகக௠காடà¯à®Ÿ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à¯à®®à¯ போத௠அலà¯à®²à®¤à¯ இணைபà¯à®ªà¯ˆ வைஃபை உளà¯à®¨à¯à®´à¯ˆà®µà¯à®¤à¯ திரை கà¯à®±à¯à®•à¯à®•à®¿à®Ÿà¯à®®à¯ போத௠இத௠à®à®±à¯à®ªà®Ÿà®²à®¾à®®à¯. இரà¯à®ªà¯à®ªà®¿à®©à¯à®®à¯, தரவ௠எதà¯à®µà¯à®®à¯ பரிமாறà¯à®±à®ªà¯à®ªà®Ÿà¯à®µà®¤à®±à¯à®•à¯ à®®à¯à®©à¯ Chromium இணைபà¯à®ªà¯ˆ நிறà¯à®¤à¯à®¤à®¿à®¯à®¤à®¾à®²à¯ உஙà¯à®•à®³à¯ தகவல௠பாதà¯à®•à®¾à®ªà¯à®ªà®¾à®•à®µà¯‡ இரà¯à®•à¯à®•à®¿à®±à®¤à¯.</translation>
<translation id="9137013805542155359">அசலைக௠காணà¯à®ªà®¿</translation>
+<translation id="9137248913990643158">இநà¯à®¤à®ªà¯ பயனà¯à®ªà®¾à®Ÿà¯à®Ÿà¯ˆà®ªà¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯ à®®à¯à®©à¯, Chromeà®à®¤à¯ தொடஙà¯à®•à®¿ உளà¯à®¨à¯à®´à¯ˆà®¯à®µà¯à®®à¯.</translation>
<translation id="9148507642005240123">&amp;திரà¯à®¤à¯à®¤à®²à¯ˆà®šà¯ செயலà¯à®¤à®µà®¿à®°à¯</translation>
<translation id="9157595877708044936">அமைகà¯à®•à®¿à®±à®¤à¯...</translation>
<translation id="9170848237812810038">&amp;செயலà¯à®¤à®µà®¿à®°à¯</translation>
@@ -736,7 +789,6 @@
<translation id="935608979562296692">படிவதà¯à®¤à¯ˆ அழி</translation>
<translation id="939736085109172342">பà¯à®¤à®¿à®¯ கோபà¯à®ªà¯à®±à¯ˆ</translation>
<translation id="941721044073577244">உஙà¯à®•à®³à¯à®•à¯à®•à¯ இநà¯à®¤à®¤à¯ தளதà¯à®¤à¯ˆà®ªà¯ பாரà¯à®µà¯ˆà®¯à®¿à®Ÿ அனà¯à®®à®¤à®¿ இலà¯à®²à¯ˆ</translation>
-<translation id="962701380617707048">காரà¯à®Ÿà¯ விவரஙà¯à®•à®³à¯ˆà®ªà¯ பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®•, <ph name="CREDIT_CARD" /> இன௠காலாவதி தேதியையà¯à®®à¯ CVC எணà¯à®£à¯ˆà®¯à¯à®®à¯ உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯</translation>
<translation id="969892804517981540">அதிகாரபà¯à®ªà¯‚à®°à¯à®µ கடà¯à®Ÿà®®à¯ˆà®ªà¯à®ªà¯</translation>
<translation id="988159990683914416">டெவலபà¯à®ªà®°à¯ கடà¯à®Ÿà®®à¯ˆà®ªà¯à®ªà¯</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 a6c6817711f..3c9dba540b0 100644
--- a/chromium/components/strings/components_strings_te.xtb
+++ b/chromium/components/strings/components_strings_te.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Wi-Fià°•à°¿ మళà±à°²à±€ కనెకà±à°Ÿà± చేయడం</translation>
<translation id="1175364870820465910">&amp;à°®à±à°¦à±à°°à°¿à°‚à°šà±...</translation>
<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="1206967143813997005">తపà±à°ªà± à°ªà±à°°à°¾à°°à°‚à°­ సంతకం</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">నీలి ఆకà±à°ªà°šà±à°š</translation>
<translation id="1629803312968146339">Chrome à°ˆ కారà±à°¡à±â€Œà°¨à± సేవౠచేయాలని మీరౠకోరà±à°•à±à°‚à°Ÿà±à°¨à±à°¨à°¾à°°à°¾?</translation>
<translation id="1640180200866533862">వినియోగదారౠవిధానాలà±</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />సైటౠయొకà±à°• హోమà±â€Œà°ªà±‡à°œà±€à°¨à°¿ సందరà±à°¶à°¿à°‚à°šà°¡à°‚<ph name="END_LINK" /> à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿.</translation>
<translation id="1644184664548287040">నెటà±â€Œà°µà°°à±à°•à± కానà±à°«à°¿à°—రేషనౠచెలà±à°²à°¦à± మరియౠదిగà±à°®à°¤à°¿ చేయడం సాధà±à°¯à°‚ కాదà±.</translation>
<translation id="1644574205037202324">à°šà°°à°¿à°¤à±à°°</translation>
<translation id="1645368109819982629">à°ªà±à°°à±‹à°Ÿà±‹à°•à°¾à°²à±â€Œà°•à± మదà±à°¦à°¤à± లేదà±</translation>
<translation id="1676269943528358898"><ph name="SITE" /> సాధారణంగా మీ సమాచారానà±à°¨à°¿ à°°à°•à±à°·à°¿à°‚చడానికి à°—à±à°ªà±à°¤à±€à°•à°°à°£à°¨à± ఉపయోగిసà±à°¤à±à°‚ది. Google Chrome ఈసారి <ph name="SITE" />à°•à°¿ కనెకà±à°Ÿà± చేయడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చినపà±à°ªà±à°¡à±, వెబà±â€Œà°¸à±ˆà°Ÿà± అసాధారణ మరియౠతపà±à°ªà± ఆధారాలౠఅని à°ªà±à°°à°¤à°¿à°¸à±à°ªà°‚దించింది. దాడి చేసే à°µà±à°¯à°•à±à°¤à°¿ <ph name="SITE" />à°—à°¾ à°µà±à°¯à°µà°¹à°°à°¿à°‚à°šà°¿ మోసగించడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°¸à±à°¤à±à°¨à±à°¨à°ªà±à°ªà±à°¡à± లేదా Wi-Fi సైనà±-ఇనౠసà±à°•à±à°°à±€à°¨à± కనెకà±à°·à°¨à±â€Œà°•à± అంతరాయం కలిగించినపà±à°ªà±à°¡à± ఇలా జరగవచà±à°šà±. Google Chrome డేటా వినిమయం సంభవించక à°®à±à°‚దే కనెకà±à°·à°¨à±â€Œà°¨à± ఆపివేసినందà±à°¨ మీ సమాచారం ఇపà±à°ªà°Ÿà°¿à°•à±€ à°¸à±à°°à°•à±à°·à°¿à°¤à°‚గానే ఉంది.</translation>
+<translation id="168328519870909584">à°ªà±à°°à°¸à±à°¤à±à°¤à°‚ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />పై దాడి చేసేవారౠమీ పరికరంలో మీ సమాచారానà±à°¨à°¿ (ఉదాహరణకà±, ఫోటోలà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±, సందేశాలౠమరియౠకà±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à±â€Œà°²à±) దొంగిలించగలిగే లేదా తొలగించగలిగే à°ªà±à°°à°®à°¾à°¦à°•à°°à°®à±ˆà°¨ à°…à°¨à±à°µà°°à±à°¤à°¨à°¾à°²à°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చవచà±à°šà±.</translation>
<translation id="168841957122794586">సరà±à°µà°°à± à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ బలహీన à°•à±à°°à°¿à°ªà±à°Ÿà±‹à°—à±à°°à°¾à°«à°¿à°•à± కీని కలిగి ఉంది.</translation>
-<translation id="1701955595840307032">సూచిత కంటెంటà±â€Œà°¨à± పొందండి</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">à°ˆ సైటà±â€Œà°¨à°¿ సందరà±à°¶à°¿à°‚చడానికి మీకౠ<ph name="NAME" /> à°¨à±à°‚à°¡à°¿ à°…à°¨à±à°®à°¤à°¿ అవసరం</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">పేజీ సంఖà±à°¯</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows నెటà±â€Œà°µà°°à±à°•à± సమసà±à°¯ విశà±à°²à±‡à°·à°£à°²à°¨à± అమలౠచేయడం à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">దయచేసి మీ సమకాలీకరణ పాసà±â€Œà°«à±à°°à±‡à°œà±â€Œà°¨à± నవీకరించండి.</translation>
+<translation id="1787142507584202372">మీ తెరవబడిన à°Ÿà±à°¯à°¾à°¬à±â€Œà°²à± ఇకà±à°•à°¡ కనిపిసà±à°¤à°¾à°¯à°¿</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google à°¸à±à°°à°•à±à°·à°¿à°¤ à°¬à±à°°à±Œà°œà°¿à°‚గౠఇటీవల <ph name="SITE" />లో <ph name="BEGIN_LINK" />మాలà±à°µà±‡à°°à±â€Œà°¨à°¿ à°—à±à°°à±à°¤à°¿à°‚చింది<ph name="END_LINK" />. సాధారణంగా à°¸à±à°°à°•à±à°·à°¿à°¤à°®à±ˆà°¨ వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œâ€Œà°²à± కూడా కొనà±à°¨à°¿à°¸à°¾à°°à±à°²à± మాలà±à°µà±‡à°°à± బారినపడతాయి. మాలà±à°µà±‡à°°à±â€Œ పంపిణీదారà±à°—à°¾ పేరà±à°—ాంచిన <ph name="SUBRESOURCE_HOST" /> à°¨à±à°‚à°¡à°¿ ఇతరà±à°²à°•à± హాని తలపటà±à°Ÿà±‡ లాంటి కంటెంటౠసంకà±à°°à°®à°¿à°¸à±à°¤à±à°‚ది. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">చెలà±à°²à°¨à°¿ à°…à°­à±à°¯à°°à±à°¥à°¨ లేదా à°…à°­à±à°¯à°°à±à°¥à°¨ పరామితà±à°²à±</translation>
+<translation id="1834321415901700177">à°ˆ సైటౠహానికరమైన à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à°¨à± కలిగి ఉంది</translation>
<translation id="1838667051080421715">మీరౠవెబౠపేజీ యొకà±à°• సోరà±à°¸à±â€Œà°¨à± వీకà±à°·à°¿à°¸à±à°¤à±à°¨à±à°¨à°¾à°°à±.</translation>
<translation id="1871208020102129563">à°ªà±à°°à°¾à°•à±à°¸à±€ à°¸à±à°¥à°¿à°°à°®à±ˆà°¨ à°ªà±à°°à°¾à°•à±à°¸à±€ సరà±à°µà°°à±â€Œà°²à°¨à± ఉపయోగించడానికి సెటౠచేయబడింది, .pac à°¸à±à°•à±à°°à°¿à°ªà±à°Ÿà± URLనౠకాదà±.</translation>
<translation id="1883255238294161206">జాబితానౠకà±à°¦à°¿à°‚à°šà±</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">జిపౠకోడà±</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 సూచన}other{# సూచనలà±}}</translation>
<translation id="2065985942032347596">à°ªà±à°°à°¾à°®à°¾à°£à±€à°•à°°à°£ అవసరం</translation>
-<translation id="2079545284768500474">à°…à°¨à±à°¡à±</translation>
+<translation id="2079545284768500474">à°šà°°à±à°¯ à°°à°¦à±à°¦à±</translation>
<translation id="20817612488360358">సిసà±à°Ÿà°®à± à°ªà±à°°à°¾à°•à±à°¸à±€ సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à± ఉపయోగించడానికి సెటౠచేయబడà±à°¡à°¾à°¯à°¿ కానీ à°¸à±à°ªà°·à±à°Ÿà°®à±ˆà°¨ à°ªà±à°°à°¾à°•à±à°¸à±€ కానà±à°«à°¿à°—రేషనౠకూడా పేరà±à°•à±Šà°¨à°¬à°¡à°¿à°‚ది.</translation>
<translation id="2086652334978798447">Google సూచించే à°µà±à°¯à°•à±à°¤à°¿à°—తీకృత కంటెంటà±â€Œà°¨à± పొందడానికి, Chromeà°•à°¿ సైనౠఇనౠచేయండి.</translation>
<translation id="2089090684895656482">తకà±à°•à±à°µ</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">దేశీయ à°•à±à°²à°¯à°¿à°‚à°Ÿà±</translation>
<translation id="213826338245044447">మొబైలౠబà±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°²à±</translation>
<translation id="2148716181193084225">à°ˆ రోజà±</translation>
-<translation id="2149973817440762519">à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°¨à± సవరించà±</translation>
<translation id="2154054054215849342">సమకాలీకరణ మీ డొమైనà±â€Œà°•à± à°…à°‚à°¦à±à°¬à°¾à°Ÿà±à°²à±‹ లేదà±</translation>
<translation id="2166049586286450108">పూరà±à°¤à°¿ నిరà±à°µà°¾à°¹à°• à°ªà±à°°à°¾à°ªà±à°¯à°¤</translation>
<translation id="2166378884831602661">à°ˆ సైటౠసà±à°°à°•à±à°·à°¿à°¤à°®à±ˆà°¨ కనెకà±à°·à°¨à±â€Œà°¨à± అందించలేదà±</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">వెబà±â€Œà°¸à±ˆà°Ÿà± HSTSని ఉపయోగిసà±à°¤à±à°¨à±à°¨à°‚à°¦à±à°¨ మీరౠపà±à°°à°¸à±à°¤à±à°¤à°‚ <ph name="SITE" />ని సందరà±à°¶à°¿à°‚చలేరà±. నెటà±â€Œà°µà°°à±à°•à± లోపాలౠమరియౠదాడà±à°²à± సాధారణంగా తాతà±à°•à°¾à°²à°¿à°•à°‚గానే ఉంటాయి, కావà±à°¨ à°ˆ పేజీ కాసేపటి తరà±à°µà°¾à°¤ పని చేసే అవకాశం ఉంది. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155"><ph name="ENTRY_INDEX" />à°µ సూచికలో చెలà±à°²à°¨à°¿ à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à± విసà±à°®à°°à°¿à°‚చబడింది</translation>
<translation id="2354001756790975382">ఇతర à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°²à±</translation>
+<translation id="2355395290879513365">దాడికి పాలà±à°ªà°¡à±‡à°µà°¾à°°à± à°ˆ సైటà±â€Œà°²à±‹ మీరౠచూసà±à°¤à±à°¨à±à°¨ à°šà°¿à°¤à±à°°à°¾à°²à°¨à± చూడగలగవచà±à°šà± మరియౠవాటిని సవరించడం à°¦à±à°µà°¾à°°à°¾ మిమà±à°®à°²à±à°¨à°¿ మోసగించవచà±à°šà±.</translation>
<translation id="2359808026110333948">కొనసాగà±</translation>
<translation id="2365563543831475020"><ph name="CRASH_TIME" />à°•à°¿ సంగà±à°°à°¹à°¿à°‚à°šà°¿à°¨ à°•à±à°°à°¾à°·à± నివేదిక à°…à°ªà±â€Œà°²à±‹à°¡à± కాలేదà±</translation>
<translation id="2367567093518048410">à°¸à±à°¥à°¾à°¯à°¿</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">విలà±à°µ సెటౠచేయని విధానాలనౠచూపà±</translation>
<translation id="2396249848217231973">&amp;తొలగించడానà±à°¨à°¿ à°°à°¦à±à°¦à± చేయి</translation>
<translation id="2455981314101692989">à°ˆ వెబà±â€Œà°ªà±‡à°œà±€ à°ˆ ఫారమà±â€Œ కోసం à°¸à±à°µà°¯à°‚చాలకంగా పూరà±à°¤à°¿ చెయà±à°¯à°¡à°¾à°¨à±à°¨à°¿ ఆపివేసింది.</translation>
+<translation id="2460160116472764928">Google à°¸à±à°°à°•à±à°·à°¿à°¤ à°¬à±à°°à±Œà°œà°¿à°‚గౠఇటీవల <ph name="SITE" />లో <ph name="BEGIN_LINK" />మాలà±à°µà±‡à°°à±â€Œà°¨à°¿ à°—à±à°°à±à°¤à°¿à°‚చింది<ph name="END_LINK" />. సాధారణంగా à°¸à±à°°à°•à±à°·à°¿à°¤à°®à±ˆà°¨ వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œâ€Œà°²à± కూడా కొనà±à°¨à°¿à°¸à°¾à°°à±à°²à± మాలà±à°µà±‡à°°à±â€Œ బారినపడతాయి. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">పూరించà±</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />నెటà±â€Œà°µà°°à±à°•à± సమసà±à°¯ విశà±à°²à±‡à°·à°£à°²à°¨à± అమలౠచేయడం<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">చెలà±à°²à°¨à°¿ శోధన URL.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">మీ à°šà°°à°¿à°¤à±à°° à°¨à±à°‚à°¡à°¿ à°ˆ పేజీలనౠతొలగించదలిచారా?</translation>
<translation id="2677748264148917807">నిషà±à°•à±à°°à°®à°¿à°‚à°šà±</translation>
<translation id="269990154133806163">సరà±à°µà°°à± à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°° పారదరà±à°¶à°•à°¤ విధానానà±à°¨à°¿ ఉపయోగించి పబà±à°²à°¿à°•à±â€Œà°—à°¾ బహిరంగపరచబడని à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à±à°¨à°¿ అందించింది. కొనà±à°¨à°¿ à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°²à°•à±, అవి విశà±à°µà°¸à°¨à±€à°¯à°®à±ˆà°¨à°µà°¨à°¿ మరియౠదాడి చేసేవారి à°¨à±à°‚à°¡à°¿ à°°à°•à±à°·à°£ à°•à°²à±à°ªà°¿à°‚చగలవని నిరà±à°§à°¾à°°à°¿à°‚చడానికి ఇది ఆవశà±à°¯à°•à°‚. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">పఠనా జాబితా</translation>
<translation id="2704283930420550640">విలà±à°µ ఆకృతికి సరిపోలలేదà±.</translation>
<translation id="2704951214193499422">Chromium à°ªà±à°°à°¸à±à°¤à±à°¤à°‚ మీ కారà±à°¡à±â€Œà°¨à± నిరà±à°§à°¾à°°à°¿à°‚చలేకపోయింది. దయచేసి తరà±à°µà°¾à°¤ మళà±à°²à±€ à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿.</translation>
<translation id="2705137772291741111">à°ˆ సైటౠయొకà±à°• సేవౠచేయబడిన (కాషౠచేసిన) కాపీ చదవదగినటà±à°²à±à°—à°¾ లేదà±.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">మీరౠ<ph name="DOMAIN" />కౠకనెకà±à°Ÿà± కావడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చారà±, కానీ సరà±à°µà°°à± అందించిన à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à±à°¨à°¿ దానà±à°¨à°¿ జారీ చేసినవారౠరదà±à°¦à± చేసారà±. దీని à°ªà±à°°à°•à°¾à°°à°‚, సరà±à°µà°°à± అందించిన à°­à°¦à±à°°à°¤à°¾ ఆధారాలౠఖచà±à°šà°¿à°¤à°‚à°—à°¾ విశà±à°µà°¸à°¿à°‚చలేనివి. మీరౠదాడి చేసే వారితో à°•à°®à±à°¯à±‚నికేటౠచేసà±à°¤à±à°‚డవచà±à°šà±. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">à°…à°¨à±à°®à°¤à°¿ à°…à°¡à±à°—à±</translation>
<translation id="2713444072780614174">తెలà±à°ªà±</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">మీరౠసెటà±à°Ÿà°¿à°‚à°—à±â€Œà°² పేజీ à°¨à±à°‚à°¡à°¿ కనెకà±à°·à°¨à± కోసం కానà±à°«à°¿à°—రౠచేయబడిన à° à°ªà±à°°à°¾à°•à±à°¸à±€à°²à°¨à± అయినా నిలిపివేయవచà±à°šà±.</translation>
<translation id="2955913368246107853">à°•à°¨à±à°—ొనౠపటà±à°Ÿà±€à°¨à°¿ మూసివేయి</translation>
<translation id="2958431318199492670">నెటà±â€Œà°µà°°à±à°•à± కానà±à°«à°¿à°—రేషనౠONC à°ªà±à°°à°®à°¾à°£à°¾à°¨à°¿à°•à°¿ à°…à°¨à±à°•à±‚లంగా లేదà±. కానà±à°«à°¿à°—రేషనà±â€Œà°²à±‹à°¨à°¿ భాగాలౠదిగà±à°®à°¤à°¿ కాకపోయి ఉండకపోవచà±à°šà±.</translation>
+<translation id="29611076221683977">à°ªà±à°°à°¸à±à°¤à±à°¤à°‚ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />పై దాడి చేసినవారౠమీ సమాచారానà±à°¨à°¿ (ఉదాహరణకà±, ఫోటోలà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±, సందేశాలౠమరియౠకà±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à±â€Œà°²à±) దొంగిలించడం కోసం లేదా తొలగించడం కోసం మీ Macలో à°ªà±à°°à°®à°¾à°¦à°•à°° à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°¿ ఉండవచà±à°šà±.</translation>
<translation id="2969319727213777354">à°¸à±à°°à°•à±à°·à°¿à°¤ కనెకà±à°·à°¨à±â€Œà°¨à± à°à°°à±à°ªà°¾à°Ÿà± చేయడానికి, మీ గడియారానà±à°¨à°¿ సరైన సమయానికి సెటౠచేయాలి. à°Žà°‚à°¦à±à°•à°‚టే వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œà°²à± వాటిని à°—à±à°°à±à°¤à°¿à°‚చడానికి ఉపయోగించే à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°²à± నిరà±à°¦à°¿à°·à±à°Ÿ కాలవà±à°¯à°µà°§à±à°²à±à°²à±‹ మాతà±à°°à°®à±‡ చెలà±à°²à±à°¬à°¾à°Ÿà± à°…à°µà±à°¤à°¾à°¯à°¿. మీ పరికరం గడియారం సమయం తపà±à°ªà±à°—à°¾ ఉనà±à°¨à°‚à°¦à±à°¨, Google Chrome à°ˆ à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°²à°¨à± ధృవీకరించలేదà±.</translation>
<translation id="2972581237482394796">&amp;à°ªà±à°¨à°°à°¾à°µà±ƒà°¤à°‚</translation>
<translation id="2985306909656435243">à°ªà±à°°à°¾à°°à°‚à°­à°¿à°¸à±à°¤à±‡, Chromium వేగవంతమైన ఫారమౠపూరింపౠకోసం à°ˆ పరికరంలో మీ కారà±à°¡à± కాపీని నిలà±à°µ చేసà±à°¤à±à°‚ది.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">వివరాలనౠదాచిపెటà±à°Ÿà±</translation>
<translation id="3587482841069643663">మొతà±à°¤à°‚</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">సరà±à°µà°°à± TLS à°ªà±à°¨à°ƒà°¸à°‚à°ªà±à°°à°¦à°¿à°‚పౠపొడిగింపà±à°•à± మదà±à°¦à°¤à± ఇవà±à°µà°²à±‡à°¦à±.</translation>
<translation id="36224234498066874">à°¬à±à°°à±Œà°œà°¿à°‚గౠడేటానౠకà±à°²à°¿à°¯à°°à± చెయà±à°¯à°¿...</translation>
<translation id="362276910939193118">పూరà±à°¤à°¿ à°šà°°à°¿à°¤à±à°°à°¨à± చూపించà±</translation>
<translation id="3623476034248543066">విలà±à°µà°¨à± చూపండి</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">మీరౠదీనà±à°¨à°¿ తరచà±à°—à°¾ చూసà±à°¤à±à°‚టే, à°ˆ <ph name="HELP_LINK" />ని à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿.</translation>
<translation id="3658742229777143148">à°ªà±à°¨à°°à±à°µà°¿à°®à°°à±à°¶</translation>
<translation id="3678029195006412963">à°…à°­à±à°¯à°°à±à°¥à°¨à°•à± సంతకం అందించడం సాధà±à°¯à°ªà°¡à°²à±‡à°¦à±</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">లైసెనà±à°¸à±â€Œà°²à± అయిపోయాయి</translation>
<translation id="3714780639079136834">మొబైలౠడేటా లేదా Wi-Fiని ఆనౠచేయడం</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />à°ªà±à°°à°¾à°•à±à°¸à±€, ఫైరà±â€Œà°µà°¾à°²à± మరియౠDNS కానà±à°«à°¿à°—రేషనà±â€Œà°¨à± తనిఖీ చేయడం<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">మీ à°­à°¦à±à°°à°¤à°•à± వాటిలà±à°²à±‡ ఆపదల à°—à±à°°à°¿à°‚à°šà°¿ మీకౠఅరà±à°¥à°‚ à°…à°¯à±à°¯à°¿ ఉంటే, à°ªà±à°°à°®à°¾à°¦à°•à°°à°®à±ˆà°¨ à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à± తీసివేయబడటానికి à°®à±à°‚దే <ph name="BEGIN_LINK" />à°ˆ à°…à°¸à±à°°à°•à±à°·à°¿à°¤à°®à±ˆà°¨ సైటà±â€Œà°¨à± సందరà±à°¶à°¿à°‚చవచà±à°šà±<ph name="END_LINK" />.</translation>
<translation id="3739623965217189342">మీరౠకాపీ చేసిన లింకà±</translation>
<translation id="375403751935624634">సరà±à°µà°°à± లోపం వలà±à°² à°…à°¨à±à°µà°¾à°¦à°‚ విఫలమైంది.</translation>
<translation id="3759461132968374835">మీకౠఇటీవల నివేదించిన à°•à±à°°à°¾à°·à±â€Œà°²à± లేవà±. à°•à±à°°à°¾à°·à±â€Œ నివేదన నిలిపివేసినపà±à°¡à± à°à°°à±à°ªà°¡à±‡ à°•à±à°°à°¾à°·à±â€Œà°²à± ఇకà±à°•à°¡ కనిపించవà±.</translation>
-<translation id="3788090790273268753">à°ˆ సైటౠపà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à°¿à°•à°¿ 2016లో à°—à°¡à±à°µà± à°®à±à°—à±à°¸à±à°¤à±à°‚ది, à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ గొలà±à°¸à±à°²à±‹ SHA-1ని ఉపయోగించి సంతకం చేసిన à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ ఉంది.</translation>
<translation id="382518646247711829">మీరౠపà±à°°à°¾à°•à±à°¸à±€ సరà±à°µà°°à±â€Œà°¨à± ఉపయోగిసà±à°¤à±‡...</translation>
<translation id="3828924085048779000">ఖాళీ పాసà±â€Œà°«à±à°°à±‡à°œà± à°…à°¨à±à°®à°¤à°¿à°‚చబడదà±.</translation>
<translation id="3845539888601087042">మీరౠసైనà±-ఇనౠచేసిన పరికరాల à°¨à±à°‚à°¡à°¿ à°šà°°à°¿à°¤à±à°°à°¨à± చూపà±à°¤à±‹à°‚ది. <ph name="BEGIN_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">à°•à±€ "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">à°•à±à°²à°¯à°¿à°‚టౠమరియౠసరà±à°µà°°à± ఒకే SSL à°ªà±à°°à±‹à°Ÿà±‹à°•à°¾à°²à± సంసà±à°•à°°à°£ లేదా సైఫరౠసూటà±â€Œà°•à± మదà±à°¦à°¤à°¿à°µà±à°µà°µà±.</translation>
<translation id="4079302484614802869">à°ªà±à°°à°¾à°•à±à°¸à±€ కానà±à°«à°¿à°—రేషనౠసà±à°¥à°¿à°°à°®à±ˆà°¨ à°ªà±à°°à°¾à°•à±à°¸à±€ సరà±à°µà°°à±â€Œà°²à°¨à± కాకà±à°‚à°¡à°¾, à°’à°• .pac à°¸à±à°•à±à°°à°¿à°ªà±à°Ÿà± URLనౠఉపయోగించడానికి సెటౠచేయబడింది.</translation>
+<translation id="4098354747657067197">à°®à±à°‚దౠవంచనాతà±à°®à°• సైటౠఉంది</translation>
<translation id="4103249731201008433">పరికరం à°•à±à°°à°® సంఖà±à°¯ చెలà±à°²à°¦à±</translation>
<translation id="4103763322291513355">నిరోధిత జాబితాలో ఉనà±à°¨ URLà°² జాబితానౠమరియౠమీ సిసà±à°Ÿà°®à± నిరà±à°µà°¾à°¹à°•à±à°¨à°¿ à°¦à±à°µà°¾à°°à°¾ అమలౠచేయబడిన ఇతర విధానాలనౠచూడటానికి &lt;strong&gt;chrome://policy&lt;/strong&gt;ని సందరà±à°¶à°¿à°‚à°šà°‚à°¡à°¿.</translation>
<translation id="4110615724604346410">à°ˆ సరà±à°µà°°à± ఇది à°’à°• <ph name="DOMAIN" /> అని నిరూపించà±à°•à±‹à°²à±‡à°•à°ªà±‹à°¯à°¿à°‚ది; దీని à°­à°¦à±à°°à°¤à°¾ à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ లోపాలనౠకలిగి ఉంది. తపà±à°ªà±à°—à°¾ కానà±à°«à°¿à°—రౠచేయడం వలన లేదా దాడిచేసే à°µà±à°¯à°•à±à°¤à°¿ మీ కనెకà±à°·à°¨à±â€Œà°•à°¿ అంతరాయం కలిగించడం వలన ఇలా జరిగి ఉండవచà±à°šà±. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{à°à°¦à±€ లేదà±}=1{1 à°…à°¨à±à°µà°°à±à°¤à°¨à°‚ ($1)}=2{2 à°…à°¨à±à°µà°°à±à°¤à°¨à°¾à°²à± ($1, $2)}other{# à°…à°¨à±à°µà°°à±à°¤à°¨à°¾à°²à± ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">à°•à±à°°à°¾à°·à±â€Œà°²à±</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />నెటà±â€Œà°µà°°à±à°•à± సమసà±à°¯ విశà±à°²à±‡à°·à°£à°²à°¨à± అమలౠచేయడం à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">à°ˆ సైటà±â€Œà°•à°¿ మీ కనెకà±à°·à°¨à± పూరà±à°¤à°¿ à°¸à±à°¥à°¾à°¯à°¿à°²à±‹ à°¸à±à°°à°•à±à°·à°¿à°¤à°‚à°—à°¾ లేదà±</translation>
<translation id="4250680216510889253">కాదà±</translation>
<translation id="425582637250725228">మీరౠచేసిన మారà±à°ªà±à°²à± సేవౠఅయà±à°¯à°¿ ఉండకపోవచà±à°šà±.</translation>
<translation id="4258748452823770588">చెలà±à°²à°¨à°¿ సంతకం</translation>
<translation id="4269787794583293679">(వినియోగదారౠపేరౠలేదà±)</translation>
+<translation id="4280429058323657511">, à°—à°¡à±à°µà± à°®à±à°—ింపౠ<ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google à°¸à±à°°à°•à±à°·à°¿à°¤ à°¬à±à°°à±Œà°œà°¿à°‚గౠఇటీవల <ph name="SITE" />లో <ph name="BEGIN_LINK" />హానికరమైన à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à°¨à± à°—à±à°°à±à°¤à°¿à°‚చింది<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">తలà±à°²à°¿/తండà±à°°à°¿ సూచనలà±</translation>
<translation id="4304224509867189079">లాగినà±</translation>
<translation id="432290197980158659">సరà±à°µà°°à± అంతరà±à°¨à°¿à°°à±à°®à°¿à°¤ అంచనాలకౠసరిపోలని à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à±à°¨à°¿ అందించింది. మిమà±à°®à°²à±à°¨à°¿ సంరకà±à°·à°¿à°‚చే దిశగా నిరà±à°§à°¿à°·à±à°Ÿ, ఉనà±à°¨à°¤ à°¸à±à°¥à°¾à°¯à°¿ à°­à°¦à±à°°à°¤à°¾ వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œà°² కోసం à°ˆ అంచనాలౠచేరà±à°šà°¬à°¡à±à°¡à°¾à°¯à°¿. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">కథనానà±à°¨à°¿ à°•à°¨à±à°—ొనడం విఫలమైంది</translation>
+<translation id="4326324639298822553">మీ à°—à°¡à±à°µà± à°®à±à°—ింపౠతేదీని తనిఖీ చేసి, ఆపై మళà±à°²à±€ à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿</translation>
<translation id="4331708818696583467">à°¸à±à°°à°•à±à°·à°¿à°¤à°‚ కాదà±</translation>
+<translation id="4356973930735388585">à°ˆ సైటà±â€Œà°²à±‹à°¨à°¿ దాడి చేసేవారౠమీ సమాచారానà±à°¨à°¿ (ఉదాహరణకà±, ఫోటోలà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±, సందేశాలౠమరియౠకà±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à±â€Œà°²à±) దొంగిలించడం కోసం లేదా తొలగించడం కోసం మీ à°•à°‚à°ªà±à°¯à±‚à°Ÿà°°à±â€Œà°²à±‹ à°ªà±à°°à°®à°¾à°¦à°•à°°à°®à±ˆà°¨ à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చవచà±à°šà±.</translation>
<translation id="4372948949327679948">ఆశిసà±à°¤à±à°¨à±à°¨ <ph name="VALUE_TYPE" /> విలà±à°µ.</translation>
<translation id="4381091992796011497">యూజరౠపేరà±:</translation>
<translation id="4394049700291259645">ఆపివెయà±à°¯à°¿</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">మీరౠ<ph name="DOMAIN" />à°•à°¿ కనెకà±à°Ÿà± కావడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చారà±, కానీ సరà±à°µà°°à± à°’à°• చెలà±à°²à±à°¬à°¾à°Ÿà± కాని à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à±à°¨à°¿ అందించింది. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459"><ph name="DOMAIN" />à°•à°¿ à°—à°² మీ కనెకà±à°·à°¨à± ఆధà±à°¨à°¿à°• సైఫరౠసూటౠఉపయోగించి à°—à±à°ªà±à°¤à±€à°•à°°à°¿à°‚చబడింది.</translation>
<translation id="4594403342090139922">&amp;తొలగించడానà±à°¨à°¿ à°°à°¦à±à°¦à± చేయి</translation>
+<translation id="4619615317237390068">ఇతర పరికరాలà±à°²à±‹à°¨à°¿ à°Ÿà±à°¯à°¾à°¬à±â€Œà°²à±</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">మీరౠపొడిగింపౠపేజీని వీకà±à°·à°¿à°¸à±à°¤à±à°¨à±à°¨à°¾à°°à±.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">విధానాలనౠమళà±à°²à±€ లోడౠచేయి</translation>
<translation id="4728558894243024398">à°ªà±à°²à°¾à°Ÿà±â€Œà°«à°¾à°°à°®à±</translation>
<translation id="4744603770635761495">అమలౠచేయగల మారà±à°—à°‚</translation>
+<translation id="4750917950439032686">మీ సమాచారానà±à°¨à°¿ (ఉదాహరణకà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à± లేదా à°•à±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à± నంబరà±â€Œà°²à±) à°ˆ సైటà±â€Œà°•à°¿ పంపినపà±à°ªà±à°¡à± అది à°ªà±à°°à±ˆà°µà±‡à°Ÿà±â€Œà°—à°¾ ఉంచబడà±à°¤à±à°‚ది.</translation>
<translation id="4756388243121344051">&amp;à°šà°°à°¿à°¤à±à°°</translation>
+<translation id="4759118997339041434">చెలà±à°²à°¿à°‚పౠసà±à°µà±€à°¯à°ªà±‚à°°à°£ నిలిపివేయబడింది</translation>
<translation id="4764776831041365478"><ph name="URL" /> వదà±à°¦ వెబà±â€Œà°ªà±‡à°œà±€ తాతà±à°•à°¾à°²à°¿à°•à°‚à°—à°¾ తెరà±à°šà±à°•à±‹à°µà°Ÿà°‚ లేదౠలేదా అది à°•à±à°°à±Šà°¤à±à°¤ వెబౠచిరà±à°¨à°¾à°®à°¾à°•à± శాశà±à°µà°¤à°‚à°—à°¾ తరలించబడి ఉండవచà±à°šà±.</translation>
<translation id="4771973620359291008">తెలియని లోపం à°’à°•à°Ÿà°¿ à°à°°à±à°ªà°¡à°¿à°‚ది.</translation>
<translation id="4800132727771399293">మీ à°—à°¡à±à°µà± à°®à±à°—ింపౠతేదీ మరియౠCVCని తనిఖీ చేసి, మళà±à°²à±€ à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">నెటà±â€Œà°µà°°à±à°•à± లోపం</translation>
<translation id="4816492930507672669">పేజీకి తగినటà±à°²à± అమరà±à°šà±</translation>
<translation id="4850886885716139402">వీకà±à°·à°£</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{మరియౠమరో 1 వెబౠపేజీ}other{మరియౠమరో # వెబౠపేజీలà±}}</translation>
<translation id="4923417429809017348">à°ˆ పేజీ తెలియని భాష à°¨à±à°‚à°¡à°¿ <ph name="LANGUAGE_LANGUAGE" />à°•à± à°…à°¨à±à°µà°¦à°¿à°‚చబడింది</translation>
+<translation id="4923459931733593730">చెలà±à°²à°¿à°‚à°ªà±</translation>
<translation id="4926049483395192435">à°–à°šà±à°šà°¿à°¤à°‚à°—à°¾ పేరà±à°•à±Šà°¨à°¾à°²à°¿.</translation>
<translation id="495170559598752135">à°šà°°à±à°¯à°²à±</translation>
<translation id="4958444002117714549">జాబితానౠవిసà±à°¤à°°à°¿à°‚à°šà±</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">డౌనà±â€Œà°²à±‹à°¡à± చేసà±à°¤à±‹à°‚ది</translation>
<translation id="5190835502935405962">à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°² బారà±</translation>
<translation id="5199729219167945352">à°ªà±à°°à°¯à±‹à°—ాలà±</translation>
-<translation id="5199841536747119669">మీ సూచనలౠఇకà±à°•à°¡ కనిపిసà±à°¤à°¾à°¯à°¿</translation>
<translation id="5251803541071282808">à°•à±à°²à±Œà°¡à±</translation>
<translation id="5277279256032773186">కారà±à°¯à°¾à°²à°¯à°‚లో Chrome ఉపయోగిసà±à°¤à±à°¨à±à°¨à°¾à°°à°¾? à°µà±à°¯à°¾à°ªà°¾à°° సంసà±à°¥à°²à± తమ ఉదà±à°¯à±‹à°—à±à°² కోసం Chrome సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± నిరà±à°µà°¹à°¿à°‚చగలవà±. మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿</translation>
<translation id="5299298092464848405">విధానానà±à°¨à°¿ à°…à°¨à±à°µà°¯à°¿à°‚చడంలో లోపం</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">à°•à±à°°à°¾à°·à± నివేదిక నిలిపివెయà±à°¯à°¬à°¡à°¿à°‚ది.</translation>
<translation id="5317780077021120954">సేవౠచేయి</translation>
<translation id="5327248766486351172">పేరà±</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />పై దాడి చేసే à°µà±à°¯à°•à±à°¤à±à°²à± సాఫà±à°Ÿà±â€Œà°µà±‡à°°à±â€Œà°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేసà±à°•à±‹à°®à°¨à°¡à°‚ లేదా మీ à°µà±à°¯à°•à±à°¤à°¿à°—à°¤ సమాచారానà±à°¨à°¿ (ఉదాహరణకà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±, ఫోనౠనంబరà±â€Œà°²à± లేదా à°•à±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à±â€Œà°²à±) వెలà±à°²à°¡à°¿à°‚చమనడం వంటి à°ªà±à°°à°®à°¾à°¦à°•à°°à°®à±ˆà°¨ పనà±à°²à± చేయమని à°ªà±à°°à°¿à°•à±Šà°²à±à°ªà±‡à°²à°¾ మిమà±à°®à°²à±à°¨à°¿ మాయ చేయవచà±à°šà±.</translation>
<translation id="5359637492792381994">à°ˆ సరà±à°µà°°à± ఇది à°’à°• <ph name="DOMAIN" /> అని నిరూపించà±à°•à±‹à°²à±‡à°•à°ªà±‹à°¯à°¿à°‚ది; à°ˆ సమయంలో దీని à°­à°¦à±à°°à°¤à°¾ à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ చెలà±à°²à°¦à±. తపà±à°ªà±à°—à°¾ కానà±à°«à°¿à°—రౠచేయడం వలన లేదా దాడిచేసే à°µà±à°¯à°•à±à°¤à°¿ మీ కనెకà±à°·à°¨à±â€Œà°•à°¿ అంతరాయం కలిగించడం వలన ఇలా జరిగి ఉండవచà±à°šà±. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">విధాన సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± నిలà±à°µ చేయడంలో విఫలమైంది</translation>
+<translation id="5386426401304769735">à°ˆ సైటౠపà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ గొలà±à°¸à±à°²à±‹ SHA-1 ఉపయోగించి సంతకం చేసిన à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ ఉంది.</translation>
<translation id="5421136146218899937">à°¬à±à°°à±Œà°œà°¿à°‚గౠడేటానౠకà±à°²à°¿à°¯à°°à± చెయà±à°¯à°¿...</translation>
<translation id="5430298929874300616">à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°¨à°¿ తీసివేయి</translation>
<translation id="5431657950005405462">మీ ఫైలౠకనà±à°—ొనబడలేదà±</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102"><ph name="SITE" />లో పొందà±à°ªà°°à°¿à°šà°¿à°¨ పేజీ ఇలా చెబà±à°¤à±‹à°‚ది:</translation>
<translation id="5556459405103347317">రీలోడà±</translation>
<translation id="5565735124758917034">సకà±à°°à°¿à°¯à°‚</translation>
+<translation id="5572851009514199876">దయచేసి Chromeని à°ªà±à°°à°¾à°°à°‚à°­à°¿à°‚à°šà°¿, దానికి సైనౠఇనౠచేయండి, à°…à°ªà±à°ªà±à°¡à± మీకౠఈ సైటà±â€Œà°¨à± à°ªà±à°°à°¾à°ªà±à°¯à°¤ చేయడానికి à°…à°¨à±à°®à°¤à°¿ ఉందో లేదో Chrome తనిఖీ చేయగలదà±.</translation>
+<translation id="5580958916614886209">మీ à°—à°¡à±à°µà± à°®à±à°—ింపౠనెలనౠతనిఖీ చేసి, ఆపై మళà±à°²à±€ à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿</translation>
<translation id="560412284261940334">నిరà±à°µà°¹à°£à°•à± మదà±à°¦à°¤à± లేదà±</translation>
<translation id="5610142619324316209">కనెకà±à°·à°¨à±â€Œà°¨à± తనిఖీ చేయడం</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> మిమà±à°®à°²à±à°¨à°¿ అనేక సారà±à°²à± దారి మళà±à°²à°¿à°‚చింది.</translation>
<translation id="5622887735448669177">మీరౠఈ సైటà±â€Œ à°¨à±à°‚à°¡à°¿ నిషà±à°•à±à°°à°®à°¿à°‚చాలనà±à°•à±à°‚à°Ÿà±à°¨à±à°¨à°¾à°°à°¾?</translation>
<translation id="5629630648637658800">విధాన సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± లోడౠచేయడంలో విఫలమైంది</translation>
<translation id="5631439013527180824">చెలà±à°²à°¨à°¿ పరికర నిరà±à°µà°¹à°£ టోకెనà±</translation>
+<translation id="5669703222995421982">à°µà±à°¯à°•à±à°¤à°¿à°—తీకరించిన కంటెంటà±â€Œà°¨à± పొందండి</translation>
+<translation id="5675650730144413517">à°ˆ పేజీ పని చేయడం లేదà±</translation>
<translation id="5677928146339483299">à°¬à±à°²à°¾à°•à± చెయà±à°¯à°¬à°¡à°¿à°‚ది</translation>
<translation id="5694783966845939798">మీరౠ<ph name="DOMAIN" />à°•à°¿ కనెకà±à°Ÿà± చేయడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చారà±, కానీ సరà±à°µà°°à± బలహీనమైన సంతకం à°…à°²à±à°—ారిథమà±â€Œà°¨à± (SHA-1 వంటిది) ఉపయోగించి సంతకం చేసిన à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à±à°¨à°¿ అందించింది. దీని à°ªà±à°°à°•à°¾à°°à°‚, సరà±à°µà°°à± అందించిన à°­à°¦à±à°°à°¤à°¾ ఆధారాలౠనకిలీవి కావచà±à°šà± మరియౠసరà±à°µà°°à± మీరౠఊహించిన సరà±à°µà°°à± కాకపోవచà±à°šà± (మీరౠదాడి చేసే వారితో à°•à°®à±à°¯à±‚నికేటౠచేసà±à°¤à±à°‚డవచà±à°šà±). <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">à°ˆ వెబà±â€â€Œà°¸à±ˆà°Ÿà± యొకà±à°• à°—à±à°°à±à°¤à°¿à°‚పౠనిరà±à°¥à°¾à°°à°¿à°‚చబడలేదà±.</translation>
<translation id="5720705177508910913">à°ªà±à°°à°¸à±à°¤à±à°¤ వినియోగదారà±</translation>
-<translation id="572328651809341494">ఇటీవలి à°Ÿà±à°¯à°¾à°¬à±â€Œà°²à±</translation>
<translation id="5732392974455271431">మీ తలà±à°²à°¿à°¦à°‚à°¡à±à°°à±à°²à± దీనà±à°¨à°¿ మీ కోసం à°…à°¨à±â€Œà°¬à±à°²à°¾à°•à± చేయగలరà±</translation>
<translation id="5784606427469807560">మీ కారà±à°¡à±â€Œà°¨à± నిరà±à°§à°¾à°°à°¿à°‚చడంలో సమసà±à°¯ à°à°°à±à°ªà°¡à°¿à°‚ది. మీ ఇంటరà±à°¨à±†à°Ÿà± కనెకà±à°·à°¨à±â€Œà°¨à°¿ తనిఖీ చేసి, ఆపై మళà±à°²à±€ à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿.</translation>
<translation id="5785756445106461925">అలాగే, à°ˆ పేజీలో à°¸à±à°°à°•à±à°·à°¿à°¤à°‚ కాని ఇతర వనరà±à°²à± ఉనà±à°¨à°¾à°¯à°¿. à°ˆ వనరà±à°²à°¨à± బదిలీ చేసà±à°¤à±à°¨à±à°¨à°ªà±à°ªà±à°¡à± ఇతరà±à°²à± చూడగలరౠమరియౠదాడికి పాలà±à°ªà°¡à±‡à°µà°¾à°°à± పేజీ రూపానà±à°¨à°¿ మారà±à°šà±‡à°²à°¾ వీటిని సవరించగలరà±.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">మీరౠ<ph name="DOMAIN" />కౠకనెకà±à°Ÿà± కావడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చారà±, కానీ సరà±à°µà°°à± బలహీన కీని కలిగి ఉనà±à°¨ à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à±à°¨à°¿ అందించింది. దాడి చేసేవారౠపà±à°°à±ˆà°µà±‡à°Ÿà± కీని విచà±à°›à°¿à°¨à±à°¨à°‚ చేసారౠమరియౠసరà±à°µà°°à± మీరౠఊహించిన సరà±à°µà°°à± కాకపోవచà±à°šà± (మీరౠదాడి చేసే వారితో à°•à°®à±à°¯à±‚నికేటౠచేసà±à°¤à±à°‚డవచà±à°šà±). <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">à°•à±à°°à±Šà°¤à±à°¤ ఫోలà±à°¡à°°à±</translation>
<translation id="5869405914158311789">à°ˆ సైటà±â€Œà°¨à± చేరà±à°•à±‹à°²à±‡à°•à°ªà±‹à°¯à°¾à°®à±</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">à°ˆ à°µà±à°¯à°¾à°ªà°¾à°°à°¿ కోసం Google Payments à°ˆ రకమైన కారà±à°¡à±â€Œà°•à°¿ మదà±à°¦à°¤à°¿à°µà±à°µà°¦à±. దయచేసి వేరొక కారà±à°¡à±â€Œà°¨à± à°Žà°‚à°šà±à°•à±‹à°‚à°¡à°¿.</translation>
<translation id="59174027418879706">à°ªà±à°°à°¾à°°à°‚భించబడింది</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="5966707198760109579">వారం</translation>
<translation id="5967867314010545767">à°šà°°à°¿à°¤à±à°° à°¨à±à°‚à°¡à°¿ తీసివేయి</translation>
<translation id="5975083100439434680">దూరంగా జూమౠచెయà±à°¯à°¿</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">ఇలా చేసి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿:</translation>
<translation id="6151417162996330722">సరà±à°µà°°à± à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ చెలà±à°²à±à°¬à°¾à°Ÿà± à°µà±à°¯à°µà°§à°¿ చాలా à°Žà°•à±à°•à±à°µ కాలం ఉంది.</translation>
<translation id="6165508094623778733">మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿</translation>
+<translation id="6177128806592000436">à°ˆ సైటà±â€Œà°•à°¿ మీ కనెకà±à°·à°¨à± à°¸à±à°°à°•à±à°·à°¿à°¤à°‚à°—à°¾ లేదà±</translation>
<translation id="6203231073485539293">మీ ఇంటరà±à°¨à±†à°Ÿà± కనెకà±à°·à°¨à±â€Œà°¨à± తనిఖీ చేయండి</translation>
<translation id="6218753634732582820">Chromium à°¨à±à°‚à°¡à°¿ à°šà°¿à°°à±à°¨à°¾à°®à°¾à°¨à± తీసివేయాలా?</translation>
+<translation id="6251924700383757765">గోపà±à°¯à°¤à°¾ విధానం</translation>
+<translation id="625755898061068298">మీరౠఈ సైటà±â€Œà°•à± à°­à°¦à±à°°à°¤à°¾ హెచà±à°šà°°à°¿à°•à°²à°¨à± నిలిపివేయాలని à°Žà°‚à°šà±à°•à±à°¨à±à°¨à°¾à°°à±.</translation>
<translation id="6259156558325130047">&amp;మళà±à°²à±€ à°•à±à°°à°®à°‚ చేయడానà±à°¨à°¿ à°ªà±à°¨à°°à°¾à°µà±ƒà°¤à°‚ చేయి</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°²à±</translation>
<translation id="6264485186158353794">à°­à°¦à±à°°à°¤à°•à± తిరిగి వెళà±à°³à±</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" />ని చేరà±à°•à±‹à°²à±‡à°•à°ªà±‹à°¯à°¾à°®à±.</translation>
<translation id="6321917430147971392">మీ DNS సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± తనిఖీ చేయండి</translation>
<translation id="6328639280570009161">నెటà±â€Œà°µà°°à±à°•à± సూచననౠనిలిపివేసి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿</translation>
+<translation id="6328786501058569169">ఈ సైటౠమోసపూరితమైనది</translation>
<translation id="6337534724793800597">పేరౠదà±à°µà°¾à°°à°¾ విధానాలనౠఫిలà±à°Ÿà°°à± చేయి</translation>
<translation id="6342069812937806050">ఇపà±à°ªà±à°¡à±‡</translation>
<translation id="6345221851280129312">పరిమాణం తెలియదà±</translation>
@@ -489,6 +519,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{1 ఇతర సూచన}other{# ఇతర సూచనలà±}}</translation>
<translation id="6387478394221739770">à°…à°¦à±à°­à±à°¤à°®à±ˆà°¨ à°•à±à°°à±Šà°¤à±à°¤ Chrome లకà±à°·à°£à°¾à°² పటà±à°² ఆసకà±à°¤à°¿à°—à°¾ ఉనà±à°¨à°¾à°°à°¾? chrome.com/betaలో మా బీటా ఛానెలà±â€Œà°¨à± à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿.</translation>
<translation id="6389758589412724634">Chromium à°ˆ వెబà±â€Œà°ªà±‡à°œà±€à°¨à°¿ à°ªà±à°°à°¦à°°à±à°¶à°¿à°‚చడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°¸à±à°¤à±à°¨à±à°¨à°ªà±à°ªà±à°¡à± మెమరీ అయిపోయింది.</translation>
+<translation id="6404511346730675251">à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°¨à± సవరించà±</translation>
+<translation id="6410264514553301377"><ph name="CREDIT_CARD" /> à°—à°¡à±à°µà± à°®à±à°—ింపౠతేదీ మరియౠCVCని నమోదౠచేయండి</translation>
<translation id="6414888972213066896">మీరౠఈ సైటà±â€Œà°¨à°¿ సందరà±à°¶à°¿à°‚చడానికి à°…à°¨à±à°®à°¤à°¿à°‚చమని కోరà±à°¤à±‚ మీ తలà±à°²à°¿/తండà±à°°à°¿à°•à°¿ à°…à°­à±à°¯à°°à±à°¥à°¨ పంపారà±</translation>
<translation id="6416403317709441254"><ph name="SITE" /> Chromium à°ªà±à°°à°¾à°¸à±†à°¸à± చేయలేని రీతిలో గజిబిజిగా ఉండే ఆధారాలనౠపంపినందà±à°¨ మీరౠపà±à°°à°¸à±à°¤à±à°¤à°‚ à°† వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œà°¨à°¿ సందరà±à°¶à°¿à°‚చలేరà±. నెటà±â€Œà°µà°°à±à°•à± లోపాలౠమరియౠదాడà±à°²à± సాధారణంగా తాతà±à°•à°¾à°²à°¿à°•à°‚గానే ఉంటాయి, కావà±à°¨ à°ˆ పేజీ కాసేపటి తరà±à°µà°¾à°¤ పని చేసే అవకాశం ఉంది. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ à°°à°¦à±à°¦à± చెయà±à°¯à°¬à°¡à°¿à°‚దా అని తనిఖీ చెయà±à°¯à°¡à°‚ సాధà±à°¯à°‚ కాలేదà±.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">విధానంచే డిపాలà±à°Ÿà± శోధన ఆపివేయబడినందà±à°¨ విసà±à°®à°°à°¿à°‚చబడింది.</translation>
<translation id="6462969404041126431">à°ˆ సరà±à°µà°°à± ఇది à°’à°• <ph name="DOMAIN" /> అని నిరూపించà±à°•à±‹à°²à±‡à°•à°ªà±‹à°¯à°¿à°‚ది; దీని à°­à°¦à±à°°à°¤à°¾ à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ ఉపసంహరించబడింది. తపà±à°ªà±à°—à°¾ కానà±à°«à°¿à°—రౠచేయడం వలన లేదా దాడిచేసే à°µà±à°¯à°•à±à°¤à°¿ మీ కనెకà±à°·à°¨à±â€Œà°•à°¿ అంతరాయం కలిగించడం వలన ఇలా జరిగి ఉండవచà±à°šà±. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">పరికర విధానాలà±</translation>
+<translation id="6477321094435799029">Chrome à°ˆ పేజీలో అసాధారణ కోడà±â€Œà°¨à± à°—à±à°°à±à°¤à°¿à°‚చింది మరియౠమీ à°µà±à°¯à°•à±à°¤à°¿à°—à°¤ సమాచారం (ఉదాహరణకà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±, ఫోనౠనంబరà±â€Œà°²à± మరియౠకà±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à±â€Œà°²à±) à°°à°•à±à°·à°¿à°‚చడానికి దానà±à°¨à°¿ à°¬à±à°²à°¾à°•à± చేసింది.</translation>
<translation id="6489534406876378309">à°•à±à°°à°¾à°·à±â€Œà°²à°¨à± à°…à°ªà±â€Œà°²à±‹à°¡à± చేయడానà±à°¨à°¿ à°ªà±à°°à°¾à°°à°‚à°­à°¿à°‚à°šà°‚à°¡à°¿</translation>
<translation id="6529602333819889595">&amp;తొలగించడానà±à°¨à°¿ à°ªà±à°¨à°°à°¾à°µà±ƒà°¤à°‚ చేయి</translation>
<translation id="6534179046333460208">à°ªà±à°°à°¤à±à°¯à°•à±à°· వెబౠసూచనలà±</translation>
<translation id="6550675742724504774">ఎంపికలà±</translation>
+<translation id="6556239504065605927">à°¸à±à°°à°•à±à°·à°¿à°¤ కనెకà±à°·à°¨à±</translation>
<translation id="6563469144985748109">మీ నిరà±à°µà°¾à°¹à°•à±à°¡à± దీనà±à°¨à°¿ ఇంకా ఆమోదించలేదà±</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" /> కంటే తకà±à°•à±à°µ</translation>
<translation id="6596325263575161958">à°—à±à°ªà±à°¤à±€à°•à°°à°£ ఎంపికలà±</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">à°ˆ విధానం విలà±à°µ తగà±à°—ించబడింది.</translation>
<translation id="6652240803263749613">à°ˆ సరà±à°µà°°à± ఇది à°’à°• <ph name="DOMAIN" /> అని నిరూపించà±à°•à±‹à°²à±‡à°•à°ªà±‹à°¯à°¿à°‚ది; దీని à°­à°¦à±à°°à°¤à°¾ à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à±à°¨à°¿ మీ à°•à°‚à°ªà±à°¯à±‚టరౠఆపరేటింగౠసిసà±à°Ÿà°®à± విశà±à°µà°¸à°¿à°‚చలేదà±. తపà±à°ªà±à°—à°¾ కానà±à°«à°¿à°—రౠచేయడం వలన లేదా దాడిచేసే à°µà±à°¯à°•à±à°¤à°¿ మీ కనెకà±à°·à°¨à±â€Œà°•à°¿ అంతరాయం కలిగించడం వలన ఇలా జరిగి ఉండవచà±à°šà±. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">ఫోలà±à°¡à°°à±â€Œà°¨à± సవరించండి</translation>
-<translation id="6660210980321319655">à°¸à±à°µà°¯à°‚చాలకంగా నివేదించబడిన సమయం <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Chromium à°¨à±à°‚à°¡à°¿ ఫారమౠసూచననౠతీసివేయాలా?</translation>
<translation id="6685834062052613830">సైనౠఅవà±à°Ÿà± చేసి, సెటపà±â€Œà°¨à± పూరà±à°¤à°¿ చేయండి</translation>
<translation id="6710213216561001401">à°®à±à°¨à±à°ªà°Ÿà°¿</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">విధానం విలà±à°µ</translation>
<translation id="6757797048963528358">మీ పరికరం నిదà±à°°à°¾à°µà°¸à±à°¥à°•à°¿ వెళà±à°²à°¿à°‚ది.</translation>
<translation id="6778737459546443941">మీ తలà±à°²à°¿/తండà±à°°à°¿ దీనà±à°¨à°¿ ఇంకా ఆమోదించలేదà±</translation>
+<translation id="6810899417690483278">à°…à°¨à±à°•à±‚లీకరణ ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" /> వదà±à°¦ ఉనà±à°¨ వెబà±â€Œà°ªà±‡à°œà±€ à°ªà±à°°à°¸à±à°¤à±à°¤à°¾à°¨à°¿à°•à°¿ à°…à°‚à°¦à±à°¬à°¾à°Ÿà±à°²à±‹ లేదà±. నిరà±à°µà°¹à°£ కోసం ఇది à°Žà°•à±à°•à°µ లేదా తకà±à°•à°µ కావచà±à°šà±.</translation>
<translation id="6831043979455480757">à°…à°¨à±à°µà°¦à°¿à°‚à°šà±</translation>
@@ -542,6 +576,8 @@
<translation id="7012363358306927923">చైనా యూనియనౠపే</translation>
<translation id="7012372675181957985">మీ Google ఖాతా <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" />లో ఇతర à°¬à±à°°à±Œà°œà°¿à°‚à°—à± à°šà°°à°¿à°¤à±à°° రూపాలౠఉండవచà±à°šà±.</translation>
<translation id="7029809446516969842">పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±</translation>
+<translation id="7064851114919012435">సంపà±à°°à°¦à°¿à°‚పౠసమాచారం</translation>
+<translation id="7079718277001814089">à°ˆ సైటౠమాలà±à°µà±‡à°°à±â€Œà°¨à°¿ కలిగి ఉంది</translation>
<translation id="7087282848513945231">కౌంటి</translation>
<translation id="7088615885725309056">పాతవి</translation>
<translation id="7090678807593890770"><ph name="LINK" /> కోసం Googleలో శోధించండి</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> à°­à°¦à±à°°à°¤à°¾ à°ªà±à°°à°®à°¾à°£à°¾à°²à°•à± à°•à°Ÿà±à°Ÿà±à°¬à°¡à°¿ లేదà±.</translation>
<translation id="721197778055552897">à°ˆ సమసà±à°¯ à°—à±à°°à°¿à°‚à°šà°¿ <ph name="BEGIN_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿ <ph name="END_LINK" />.</translation>
<translation id="7219179957768738017">కనెకà±à°·à°¨à± <ph name="SSL_VERSION" />ని ఉపయోగిసà±à°¤à±à°‚ది.</translation>
+<translation id="724691107663265825">సైటౠమà±à°¨à±à°®à±à°‚దౠమాలà±à°µà±‡à°°à± కలిగి ఉంది</translation>
<translation id="724975217298816891">మీ కారà±à°¡à± వివరాలనౠనవీకరించడానికి <ph name="CREDIT_CARD" /> కారà±à°¡à± à°—à°¡à±à°µà± à°®à±à°—ింపౠతేదీ మరియౠCVCని నమోదౠచేయండి. మీరౠనిరà±à°§à°¾à°°à°¿à°‚à°šà°¿à°¨ తరà±à°µà°¾à°¤, మీ కారà±à°¡à± వివరాలౠఈ సైటà±â€Œà°¤à±‹ భాగసà±à°µà°¾à°®à±à°¯à°‚ చేయబడతాయి.</translation>
<translation id="725866823122871198">మీ à°•à°‚à°ªà±à°¯à±‚టరౠతేదీ మరియౠసమయం (<ph name="DATE_AND_TIME" />) తపà±à°ªà±à°—à°¾ ఉనà±à°¨à°‚à°¦à±à°¨ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />à°•à°¿ à°ªà±à°°à±ˆà°µà±‡à°Ÿà± కనెకà±à°·à°¨à± à°à°°à±à°ªà°¾à°Ÿà± చేయబడదà±.</translation>
<translation id="7269802741830436641">à°ˆ వెబà±â€Œà°ªà±‡à°œà±€ దారిమళà±à°³à°¿à°‚చబడà±à°¡ లూపà±â€Œà°¨à°¿ కలిగి ఉంది</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">à°°à°¦à±à°¦à± చెయà±à°¯à°¿</translation>
<translation id="7667346355482952095">అందించిన విధాన టోకెనౠఖాళీగా ఉంది లేదా à°ªà±à°°à°¸à±à°¤à±à°¤ టోకెనà±â€Œà°¤à±‹ సరిపోలలేదà±</translation>
<translation id="7668654391829183341">తెలియని పరికరం</translation>
+<translation id="7669271284792375604">à°ˆ సైటà±â€Œà°²à±‹à°¨à°¿ దాడి చేసేవారౠమీ à°¬à±à°°à±Œà°œà°¿à°‚à°—à± à°…à°¨à±à°­à°µà°¾à°¨à°¿à°•à°¿ (ఉదాహరణకà±, మీ హోమౠపేజీని మారà±à°šà°¡à°‚ లేదా మీరౠసందరà±à°¶à°¿à°‚చే సైటà±â€Œà°²à±à°²à±‹ అదనపౠపà±à°°à°•à°Ÿà°¨à°²à°¨à± చూపడం à°¦à±à°µà°¾à°°à°¾) హాని కలిగించే à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేసే విధంగా మిమà±à°®à°²à±à°¨à°¿ మోసగించడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చవచà±à°šà±.</translation>
<translation id="7674629440242451245">à°…à°¦à±à°­à±à°¤à°®à±ˆà°¨ à°•à±à°°à±Šà°¤à±à°¤ Chrome లకà±à°·à°£à°¾à°² పటà±à°² ఆసకà±à°¤à°¿à°—à°¾ ఉనà±à°¨à°¾à°°à°¾? chrome.com/devలో మా డెవలపరౠఛానెలà±â€Œà°¨à± à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /><ph name="SITE" />à°•à°¿ కొనసాగించండి (à°…à°¸à±à°°à°•à±à°·à°¿à°¤à°‚)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">కాషౠనà±à°‚à°¡à°¿ à°ˆ సైటà±â€Œà°¨à± లోడౠచేయలేకపోయామà±</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267"><ph name="KX" />ని à°•à±€ మారà±à°ªà°¿à°¡à°¿ విధానం వలె మరియౠసందేశ à°ªà±à°°à°¾à°®à°¾à°£à±€à°•à°°à°£ కోసం <ph name="CIPHER" />ని <ph name="MAC" />తో ఉపయోగించడం à°¦à±à°µà°¾à°°à°¾ కనెకà±à°·à°¨à± à°—à±à°ªà±à°¤à±€à°•à°°à°¿à°‚చబడింది.</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="7887683347370398519">మీ CVCని తనిఖీ చేసి, మళà±à°²à±€ à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿</translation>
<translation id="7912024687060120840">ఫోలà±à°¡à°°à±â€Œà°²à±‹:</translation>
<translation id="7935318582918952113">DOM à°¡à°¿à°¸à±à°Ÿà°¿à°²à±à°²à°°à±</translation>
<translation id="7938958445268990899">సరà±à°µà°°à± à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ ఇంకా చెలà±à°²à±à°¬à°¾à°Ÿà± కాదà±.</translation>
<translation id="7942349550061667556">à°Žà°°à±à°ªà±</translation>
+<translation id="7947285636476623132">మీ à°—à°¡à±à°µà± à°®à±à°—ింపౠసంవతà±à°¸à°°à°¾à°¨à±à°¨à°¿ తనిఖీ చేసి, ఆపై మళà±à°²à±€ à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿</translation>
<translation id="7951415247503192394">(32-బిటà±)</translation>
<translation id="7956713633345437162">మొబైలౠబà±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°²à±</translation>
<translation id="7961015016161918242">à°Žà°ªà±à°ªà±à°¡à±‚ లేదà±</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">à°Žà°²à±à°²à°ªà±à°ªà±à°¡à±‚ <ph name="ORIGINAL_LANGUAGE" />నౠ<ph name="TARGET_LANGUAGE" />à°•à± à°…à°¨à±à°µà°¦à°¿à°‚à°šà±</translation>
<translation id="7995512525968007366">పేరà±à°•à±Šà°¨à°¬à°¡à°²à±‡à°¦à±</translation>
<translation id="8012647001091218357">మేమౠపà±à°°à°¸à±à°¤à±à°¤à°‚ మీ తలà±à°²à°¿à°¦à°‚à°¡à±à°°à±à°²à°¨à± సంపà±à°°à°¦à°¿à°‚చలేకపోయామà±. దయచేసి మళà±à°²à±€ à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°‚à°¡à°¿.</translation>
+<translation id="8025119109950072390">à°ˆ సైటà±â€Œà°²à±‹à°¨à°¿ దాడి చేసేవారౠసాఫà±à°Ÿà±â€Œà°µà±‡à°°à±â€Œà°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయడం లేదా మీ à°µà±à°¯à°•à±à°¤à°¿à°—à°¤ సమాచారానà±à°¨à°¿ (ఉదాహరణకà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±, ఫోనౠనంబరà±â€Œà°²à± లేదా à°•à±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à±â€Œà°²à±) వెలà±à°²à°¡à°¿à°‚à°šà°¡à°‚ వంటి à°ªà±à°°à°®à°¾à°¦à°•à°°à°®à±ˆà°¨ పనà±à°²à± చేసేలా మిమà±à°®à°²à±à°¨à°¿ మాయ చేయవచà±à°šà±.</translation>
+<translation id="803030522067524905">Google à°¸à±à°°à°•à±à°·à°¿à°¤ à°¬à±à°°à±Œà°œà°¿à°‚గౠఇటీవల <ph name="SITE" />లో à°«à°¿à°·à°¿à°‚à°—à±â€Œà°¨à± à°—à±à°°à±à°¤à°¿à°‚చింది. ఫిషింగౠసైటà±â€Œà°²à± ఇతర వెబà±â€Œà°¸à±ˆà°Ÿà±â€Œà°² వలె నమà±à°®à°¿à°‚à°šà°¿ మిమà±à°®à°²à±à°¨à°¿ మాయ చేసà±à°¤à°¾à°¯à°¿. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">à°ˆ పేజీ <ph name="SOURCE_LANGUAGE" />లో ఉంది. దీనà±à°¨à°¿ <ph name="TARGET_LANGUAGE" />లోకి à°…à°¨à±à°µà°¦à°¿à°‚చాలా?</translation>
+<translation id="8041089156583427627">à°ªà±à°°à°¤à°¿à°¸à±à°ªà°‚దననౠపంపండి</translation>
<translation id="8088680233425245692">కథనానà±à°¨à°¿ వీకà±à°·à°¿à°‚చడంలో విఫలమైంది.</translation>
<translation id="8089520772729574115">1 MB కంటే తకà±à°•à±à°µ</translation>
<translation id="8091372947890762290">సకà±à°°à°¿à°¯à°‚ సరà±à°µà°°à±â€Œà°²à±‹ పెండింగà±â€Œà°²à±‹ ఉంది</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719"><ph name="URL" />లో ఫైలౠచదవగలిగేది కాదà±. దీనà±à°¨à°¿ తీసివేసి ఉండవచà±à°šà±, తరలించి ఉండవచà±à°šà± లేదా ఫైలౠఅనà±à°®à°¤à±à°²à± à°ªà±à°°à°¾à°ªà±à°¯à°¤à°¨à± నిరోధిసà±à°¤à±à°‚డవచà±à°šà±.</translation>
<translation id="8194797478851900357">&amp;తరలించడానà±à°¨à°¿ à°°à°¦à±à°¦à± చేయి</translation>
<translation id="8201077131113104583">ID "<ph name="EXTENSION_ID" />" ఉనà±à°¨ పొడిగింపౠకోసం నవీకరణ URL చెలà±à°²à°¦à±.</translation>
+<translation id="8202097416529803614">ఆరà±à°¡à°°à± సారాంశం</translation>
<translation id="8218327578424803826">కేటాయించిన à°¸à±à°¥à°¾à°¨à°‚:</translation>
<translation id="8225771182978767009">à°ˆ à°•à°‚à°ªà±à°¯à±‚à°Ÿà°°à±â€Œà°¨à± సెటపౠచేసిన à°µà±à°¯à°•à±à°¤à°¿ à°ˆ సైటà±â€Œà°¨à± à°¬à±à°²à°¾à°•à± చేయడానికి à°Žà°‚à°šà±à°•à±à°¨à±à°¨à°¾à°°à±.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">à°ªà±à°°à°¸à±à°¤à±à°¤à°‚ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />పై దాడి చేసినవారౠమీ సమాచారానà±à°¨à°¿ (ఉదాహరణకà±, ఫోటోలà±, పాసà±â€Œà°µà°°à±à°¡à±â€Œà°²à±, సందేశాలౠమరియౠకà±à°°à±†à°¡à°¿à°Ÿà± కారà±à°¡à±â€Œà°²à±) దొంగిలించడం కోసం లేదా తొలగించడం కోసం మీ à°•à°‚à°ªà±à°¯à±‚à°Ÿà°°à±â€Œà°²à±‹ à°ªà±à°°à°®à°¾à°¦à°•à°°à°®à±ˆà°¨ à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేయడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°¿ ఉండవచà±à°šà±.</translation>
<translation id="8241707690549784388">మీరౠవెతికే పేజీ మీరౠఎంటరౠచేసిన సమాచారానà±à°¨à°¿ ఉపయోగించà±à°•à±à°‚ది. à°† పేజీకి తిరిగి వెళà±à°³à°¡à°‚ à°¦à±à°µà°¾à°°à°¾ మీరౠచేసిన à° à°šà°°à±à°¯ అయినా à°ªà±à°¨à°°à°¾à°µà±ƒà°¤à°‚ చెయà±à°¯à°µà°²à°¸à°¿ వసà±à°¤à±à°‚ది. మీరౠకొనసాగాలనà±à°•à±à°‚à°Ÿà±à°¨à±à°¨à°¾à°°à°¾?</translation>
<translation id="8249320324621329438">చివరగా పొందబడినవి:</translation>
<translation id="8261506727792406068">తొలగించà±</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">మూలం</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="8349305172487531364">à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°² పటà±à°Ÿà±€</translation>
<translation id="8363502534493474904">ఎయిరà±â€Œà°ªà±à°²à±ˆà°¨à± మోడà±â€Œà°¨à± ఆఫౠచేయడం</translation>
<translation id="8364627913115013041">సెటౠచేయలేదà±.</translation>
<translation id="8380941800586852976">అపాయకరమైనది</translation>
<translation id="8382348898565613901">మీరౠఇటీవల సందరà±à°¶à°¿à°‚à°šà°¿à°¨ à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°²à± ఇకà±à°•à°¡ కనిపిసà±à°¤à°¾à°¯à°¿</translation>
+<translation id="8398259832188219207">à°•à±à°°à°¾à°·à± నివేదిక <ph name="UPLOAD_TIME" />à°•à°¿ à°…à°ªà±â€Œà°²à±‹à°¡à± చేయబడింది</translation>
<translation id="8412145213513410671">à°•à±à°°à°¾à°·à±â€Œà°²à± (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">మీరౠఅదే పాసà±â€Œà°«à±à°°à±‡à°œà±â€Œà°¨à°¿ రెండà±à°¸à°¾à°°à±à°²à± à°–à°šà±à°šà°¿à°¤à°‚à°—à°¾ ఎంటరౠచెయà±à°¯à°¾à°²à°¿.</translation>
<translation id="8428213095426709021">సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à±</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> à°ªà±à°°à°¤à°¿à°¸à±à°ªà°‚దించడానికి చాలా à°Žà°•à±à°•à±à°µ సమయం పటà±à°Ÿà°¿à°‚ది.</translation>
<translation id="852346902619691059">à°ˆ సరà±à°µà°°à± ఇది à°’à°• <ph name="DOMAIN" /> అని నిరూపించలేకపోయింది; దీని à°­à°¦à±à°°à°¤à°¾ à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à±à°¨à°¿ మీ పరికరం ఆపరేటింగౠసిసà±à°Ÿà°®à± విశà±à°µà°¸à°¿à°‚చలేదà±. తపà±à°ªà±à°—à°¾ కానà±à°«à°¿à°—రౠచేయడం వలన లేదా దాడిచేసే à°µà±à°¯à°•à±à°¤à°¿ మీ కనెకà±à°·à°¨à±â€Œà°•à°¿ అంతరాయం కలిగించడం వలన ఇలా జరిగి ఉండవచà±à°šà±. <ph name="BEGIN_LEARN_MORE_LINK" />మరింత తెలà±à°¸à±à°•à±‹à°‚à°¡à°¿<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Google Payments à°ˆ రకమైన కారà±à°¡à±â€Œà°•à°¿ మదà±à°¦à°¤à°¿à°µà±à°µà°¦à±. దయచేసి వేరొక కారà±à°¡à±â€Œà°¨à± à°Žà°‚à°šà±à°•à±‹à°‚à°¡à°¿.</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="8553075262323480129">పేజీ భాష నిరà±à°¥à°¾à°°à°¿à°‚చలేకపోయినందà±à°¨ à°…à°¨à±à°µà°¾à°¦à°‚ విఫలమైంది.</translation>
<translation id="8559762987265718583">మీ పరికరం తేదీ మరియౠసమయం (<ph name="DATE_AND_TIME" />) తపà±à°ªà±à°—à°¾ ఉనà±à°¨à°‚à°¦à±à°¨ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />à°•à°¿ à°ªà±à°°à±ˆà°µà±‡à°Ÿà± కనెకà±à°·à°¨à± à°à°°à±à°ªà°¾à°Ÿà± చేయబడదà±.</translation>
-<translation id="856992080682148">à°ˆ సైటౠపà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°¾à°¨à°¿à°•à°¿ 2017లో లేదా à°† తరà±à°µà°¾à°¤ à°—à°¡à±à°µà± à°®à±à°—à±à°¸à±à°¤à±à°‚ది, à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ గొలà±à°¸à±à°²à±‹ SHA-1ని ఉపయోగించి సంతకం చేసిన à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ ఉంది.</translation>
<translation id="8571890674111243710">పేజీని <ph name="LANGUAGE" />à°•à± à°…à°¨à±à°µà°¦à°¿à°¸à±à°¤à±‹à°‚ది...</translation>
<translation id="859285277496340001">ఇది à°°à°¦à±à°¦à± చెయà±à°¯à°¬à°¡à°¿à°‚దా అని తనిఖీ చెయà±à°¯à°¡à°¾à°¨à°¿à°•à°¿ à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ విధానానà±à°¨à°¿ పేరà±à°•à±Šà°¨à°²à±‡à°¦à±.</translation>
<translation id="8620436878122366504">మీ తలà±à°²à°¿à°¦à°‚à°¡à±à°°à±à°²à± దీనà±à°¨à°¿ ఇంకా ఆమోదించలేదà±</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522"><ph name="HOST_NAME" /> &lt;abbr id="dnsDefinition"&gt;DNS à°šà°¿à°°à±à°¨à°¾à°®à°¾&lt;/abbr&gt; à°•à°¨à±à°—ొనబడలేదà±. సమసà±à°¯à°¨à± నిరà±à°§à°¾à°°à°¿à°¸à±à°¤à±‹à°‚ది.</translation>
<translation id="8790007591277257123">&amp;తొలగించడానà±à°¨à°¿ à°ªà±à°¨à°°à°¾à°µà±ƒà°¤à°‚ చేయి</translation>
<translation id="8798099450830957504">డిఫాలà±à°Ÿà±</translation>
+<translation id="8800988563907321413">మీ సమీపంలోని సూచనలౠఇకà±à°•à°¡ కనిపిసà±à°¤à°¾à°¯à°¿</translation>
<translation id="8804164990146287819">గోపà±à°¯à°¤à°¾ విధానం</translation>
<translation id="8820817407110198400">à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°²à±</translation>
<translation id="8834246243508017242">పరిచయాలనౠఉపయోగించి à°¸à±à°µà±€à°¯à°ªà±‚à°°à±à°¤à°¿à°¨à°¿ à°ªà±à°°à°¾à°°à°‚భించండి…</translation>
<translation id="883848425547221593">ఇతర à°¬à±à°•à±â€Œà°®à°¾à°°à±à°•à±â€Œà°²à±:</translation>
+<translation id="884264119367021077">à°·à°¿à°ªà±à°ªà°¿à°‚à°—à± à°šà°¿à°°à±à°¨à°¾à°®à°¾</translation>
<translation id="884923133447025588">à° à°°à°¦à±à°¦à± విధానం à°•à°¨à±à°—ొనబడలేదà±.</translation>
<translation id="885730110891505394">Googleతో భాగసà±à°µà°¾à°®à±à°¯à°‚</translation>
<translation id="8866481888320382733">విధాన సెటà±à°Ÿà°¿à°‚à°—à±â€Œà°²à°¨à± à°…à°¨à±à°µà°¯à°¿à°‚చడంలో లోపం</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">సరà±à°µà°°à± యొకà±à°• à°ªà±à°°à°®à°¾à°£à°ªà°¤à±à°°à°‚ à°—à°¡à±à°µà± à°®à±à°—ిసింది.</translation>
<translation id="8987927404178983737">నెల</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">à°ˆ సైటౠహానికర à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à°¨à± కలిగి ఉంది</translation>
<translation id="9001074447101275817"><ph name="DOMAIN" /> à°ªà±à°°à°¾à°•à±à°¸à±€à°•à°¿ వినియోగదారౠపేరౠమరియౠపాసà±â€Œà°µà°°à±à°¡à± అవసరం.</translation>
<translation id="901974403500617787">సిసà±à°Ÿà°®à± à°µà±à°¯à°¾à°ªà±à°¤à°‚à°—à°¾ వరà±à°¤à°¿à°‚పజేయబడే à°«à±à°²à°¾à°—à±â€Œà°²à± యజమాని à°¦à±à°µà°¾à°°à°¾ మాతà±à°°à°®à±‡ సెటౠచేయబడతాయి: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">à°ˆ పేజీ <ph name="TARGET_LANGUAGE" />à°•à°¿ à°…à°¨à±à°µà°¦à°¿à°‚చబడింది</translation>
+<translation id="9035022520814077154">à°­à°¦à±à°°à°¤à°¾ లోపం</translation>
<translation id="9038649477754266430">పేజీలనౠమరింత శీఘà±à°°à°‚à°—à°¾ లోడౠచేయడానికి సూచన సేవనౠఉపయోగించండి</translation>
<translation id="9039213469156557790">అలాగే, à°ˆ పేజీలో à°¸à±à°°à°•à±à°·à°¿à°¤à°‚ కాని ఇతర వనరà±à°²à± ఉనà±à°¨à°¾à°¯à°¿. à°ˆ వనరà±à°²à°¨à± బదిలీ చేసà±à°¤à±à°¨à±à°¨à°ªà±à°ªà±à°¡à± ఇతరà±à°²à± చూడగలరౠమరియౠదాడికి పాలà±à°ªà°¡à±‡à°µà°¾à°°à± పేజీ à°ªà±à°°à°µà°°à±à°¤à°¨à°¨à± మారà±à°šà±‡à°²à°¾ వీటిని సవరించగలరà±.</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />పై దాడి చేసేవారౠమీ à°¬à±à°°à±Œà°œà°¿à°‚à°—à± à°…à°¨à±à°­à°µà°¾à°¨à°¿à°•à°¿ (ఉదాహరణకà±, మీ హోమౠపేజీని మారà±à°šà°¡à°‚ లేదా మీరౠసందరà±à°¶à°¿à°‚à°šà°¿à°¨ సైటà±â€Œà°²à±à°²à±‹ అదనపౠపà±à°°à°•à°Ÿà°¨à°²à°¨à± చూపడం à°¦à±à°µà°¾à°°à°¾) హాని కలిగించే à°ªà±à°°à±‹à°—à±à°°à°¾à°®à±â€Œà°²à°¨à± ఇనà±â€Œà°¸à±à°Ÿà°¾à°²à± చేసే విధంగా మిమà±à°®à°²à±à°¨à°¿ మోసగించడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚à°šà°¿ ఉండవచà±à°šà±.</translation>
<translation id="9050666287014529139">పాసà±â€Œà°«à±à°°à±‡à°œà±</translation>
<translation id="9065203028668620118">సవరించà±</translation>
<translation id="9068849894565669697">à°°à°‚à°—à±à°¨à°¿ à°Žà°‚à°šà±à°•à±‹à°‚à°¡à°¿</translation>
<translation id="9076283476770535406">ఇందà±à°²à±‹ పెదà±à°¦à°²à°•à± మాతà±à°°à°®à±‡ à°…à°¨à±à°®à°¤à°¿à°‚à°šà°¿à°¨ కంటెంటౠఉండవచà±à°šà±</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> పేజీ పని చేయడం లేదà±</translation>
<translation id="9103872766612412690"><ph name="SITE" /> సాధారణంగా మీ సమాచారానà±à°¨à°¿ à°°à°•à±à°·à°¿à°‚చడానికి à°—à±à°ªà±à°¤à±€à°•à°°à°£à°¨à± ఉపయోగిసà±à°¤à±à°‚ది. Chromium ఈసారి <ph name="SITE" />à°•à°¿ కనెకà±à°Ÿà± చేయడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°‚చినపà±à°ªà±à°¡à±, వెబà±â€Œà°¸à±ˆà°Ÿà± అసాధారణ మరియౠతపà±à°ªà± ఆధారాలౠఅని à°ªà±à°°à°¤à°¿à°¸à±à°ªà°‚దించింది. దాడి చేసే à°µà±à°¯à°•à±à°¤à°¿ <ph name="SITE" />à°—à°¾ à°µà±à°¯à°µà°¹à°°à°¿à°‚à°šà°¿ మోసగించడానికి à°ªà±à°°à°¯à°¤à±à°¨à°¿à°¸à±à°¤à±à°¨à±à°¨à°ªà±à°ªà±à°¡à± లేదా Wi-Fi సైనà±-ఇనౠసà±à°•à±à°°à±€à°¨à± కనెకà±à°·à°¨à±â€Œà°•à± అంతరాయం కలిగించినపà±à°ªà±à°¡à± ఇలా జరగవచà±à°šà±. Chromium ఎలాంటి డేటా వినిమయం సంభవించక à°®à±à°‚దే కనెకà±à°·à°¨à±â€Œà°¨à± ఆపివేసినందà±à°¨ మీ సమాచారం ఇపà±à°ªà°Ÿà°¿à°•à±€ à°¸à±à°°à°•à±à°·à°¿à°¤à°‚గానే ఉంది.</translation>
<translation id="9137013805542155359">అసలà±à°¨à± చూపించà±</translation>
+<translation id="9137248913990643158">దయచేసి à°ˆ à°…à°¨à±à°µà°°à±à°¤à°¨à°¾à°¨à±à°¨à°¿ ఉపయోగించే à°®à±à°‚దౠChromeని à°ªà±à°°à°¾à°°à°‚à°­à°¿à°‚à°šà°¿, దానికి సైనౠఇనౠచేయండి.</translation>
<translation id="9148507642005240123">&amp;సవరించడానà±à°¨à°¿ à°°à°¦à±à°¦à± చేయి</translation>
<translation id="9157595877708044936">అమరà±à°šà±à°¤à±‹à°‚ది...</translation>
<translation id="9170848237812810038">&amp;à°…à°¨à±à°¡à±</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">ఫారమà±â€Œà°¨à± తీసివేయండి</translation>
<translation id="939736085109172342">à°•à±à°°à±Šà°¤à±à°¤ ఫోలà±à°¡à°°à±</translation>
<translation id="941721044073577244">à°ˆ సైటà±â€Œà°¨à± సందరà±à°¶à°¿à°‚చడానికి మీకౠఅనà±à°®à°¤à°¿ లేనటà±à°²à±à°—à°¾ కనిపిసà±à°¤à±‹à°‚ది</translation>
-<translation id="962701380617707048">మీ కారà±à°¡à± వివరాలనౠనవీకరించడానికి <ph name="CREDIT_CARD" /> కారà±à°¡à± à°—à°¡à±à°µà± à°®à±à°—ింపౠతేదీ మరియౠCVCని నమోదౠచేయండి</translation>
<translation id="969892804517981540">అధికారిక బిలà±à°¡à±</translation>
<translation id="988159990683914416">డెవలపరౠబిలà±à°¡à±</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 ff5b4fb5f5e..a683080ff17 100644
--- a/chromium/components/strings/components_strings_th.xtb
+++ b/chromium/components/strings/components_strings_th.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">เชื่อมต่อ Wi-Fi ใหม่</translation>
<translation id="1175364870820465910">&amp;พิมพ์...</translation>
<translation id="1181037720776840403">ลบ</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />รายงาน<ph name="END_WHITEPAPER_LINK" />รายละเอียดของเหตุà¸à¸²à¸£à¸“์ความปลอดภัยที่เป็นไปได้ต่อ Google โดยอัตโนมัติ <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">ถัดไป</translation>
<translation id="1201895884277373915">เพิ่มเติมจาà¸à¹„ซต์นี้</translation>
<translation id="1206967143813997005">ลายเซ็นเริ่มต้นไม่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">สีฟ้า</translation>
<translation id="1629803312968146339">คุณต้องà¸à¸²à¸£à¹ƒà¸«à¹‰ Chrome บันทึà¸à¸šà¸±à¸•à¸£à¸™à¸µà¹‰à¹„หม</translation>
<translation id="1640180200866533862">นโยบายผู้ใช้</translation>
+<translation id="1640244768702815859">ลอง<ph name="BEGIN_LINK" />ไปที่หน้าà¹à¸£à¸à¸‚องเว็บไซต์<ph name="END_LINK" /></translation>
<translation id="1644184664548287040">à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าเครือข่ายไม่ถูà¸à¸•à¹‰à¸­à¸‡à¹à¸¥à¸°à¹„ม่สามารถนำเข้า</translation>
<translation id="1644574205037202324">ประวัติà¸à¸²à¸£à¹€à¸‚้าชม</translation>
<translation id="1645368109819982629">ไม่รองรับโปรโตคอล</translation>
<translation id="1676269943528358898">โดยทั่วไป <ph name="SITE" /> จะใช้à¸à¸²à¸£à¹€à¸‚้ารหัสเพื่อปà¸à¸›à¹‰à¸­à¸‡à¸‚้อมูลของคุณ เมื่อ Google Chrome พยายามเชื่อมต่อà¸à¸±à¸š <ph name="SITE" /> ในครั้งนี้ เว็บไซต์ดังà¸à¸¥à¹ˆà¸²à¸§à¸ªà¹ˆà¸‡à¸‚้อมูลรับรองที่ผิดปà¸à¸•à¸´à¹à¸¥à¸°à¹„ม่ถูà¸à¸•à¹‰à¸­à¸‡à¸à¸¥à¸±à¸šà¸¡à¸² เหตุà¸à¸²à¸£à¸“์นี้อาจเà¸à¸´à¸”ขึ้นเมื่อผู้บุà¸à¸£à¸¸à¸à¸žà¸¢à¸²à¸¢à¸²à¸¡à¸›à¸¥à¸­à¸¡à¹€à¸›à¹‡à¸™ <ph name="SITE" /> หรือหน้าจอà¸à¸²à¸£à¸¥à¸‡à¸Šà¸·à¹ˆà¸­à¹€à¸‚้าใช้ Wi-Fi รบà¸à¸§à¸™à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­ ข้อมูลของคุณยังปลอดภัยอยู่เนื่องจาภGoogle Chrome หยุดà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸à¹ˆà¸­à¸™à¸¡à¸µà¸à¸²à¸£à¹à¸¥à¸à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸‚้อมูล</translation>
+<translation id="168328519870909584">ผู้บุà¸à¸£à¸¸à¸à¸—ี่à¸à¸³à¸¥à¸±à¸‡à¸­à¸¢à¸¹à¹ˆà¹ƒà¸™ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> อาจพยายามติดตั้งโปรà¹à¸à¸£à¸¡à¸­à¸±à¸™à¸•à¸£à¸²à¸¢à¸šà¸™à¸­à¸¸à¸›à¸à¸£à¸“์ของคุณ ซึ่งจะขโมยหรือลบข้อมูล (ตัวอย่างเช่น รูปภาพ รหัสผ่าน ข้อความ à¹à¸¥à¸°à¸šà¸±à¸•à¸£à¹€à¸„รดิต)</translation>
<translation id="168841957122794586">ใบรับรองของเซิร์ฟเวอร์มีคีย์à¸à¸²à¸£à¹€à¸‚้ารหัสที่ไม่รัดà¸à¸¸à¸¡</translation>
-<translation id="1701955595840307032">รับเนื้อหาที่à¹à¸™à¸°à¸™à¸³</translation>
<translation id="1710259589646384581">ระบบปà¸à¸´à¸šà¸±à¸•à¸´à¸à¸²à¸£</translation>
<translation id="1721312023322545264">คุณต้องได้รับสิทธิ์จาภ<ph name="NAME" /> เพื่อเข้าชมเว็บไซต์นี้</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">เลขหน้า</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />ลองเรียà¸à¹ƒà¸Šà¹‰à¸à¸²à¸£à¸§à¸´à¸™à¸´à¸ˆà¸‰à¸±à¸¢à¹€à¸„รือข่ายของ Windows<ph name="END_LINK" /></translation>
<translation id="1783075131180517613">โปรดอัปเดตข้อความรหัสผ่านที่ซิงค์ของคุณ</translation>
+<translation id="1787142507584202372">à¹à¸—็บที่คุณเปิดไว้จะปราà¸à¸à¸—ี่นี่</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">เมื่อเร็วๆ นี้ Google Safe Browsing <ph name="BEGIN_LINK" />ตรวจพบมัลà¹à¸§à¸£à¹Œ<ph name="END_LINK" />บน <ph name="SITE" /> บางครั้งเว็บไซต์ที่โดยปà¸à¸•à¸´à¹à¸¥à¹‰à¸§à¸ˆà¸°à¸›à¸¥à¸­à¸”ภัยอาจติดมัลà¹à¸§à¸£à¹Œà¹„ด้ เนื้อหาที่เป็นอันตรายมาจาภ<ph name="SUBRESOURCE_HOST" /> ซึ่งเป็นผู้เผยà¹à¸žà¸£à¹ˆà¸¡à¸±à¸¥à¹à¸§à¸£à¹Œà¸—ี่เป็นที่รู้จัภ<ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">คำขอหรือพารามิเตอร์คำขอไม่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
+<translation id="1834321415901700177">เว็บไซต์นี้มีโปรà¹à¸à¸£à¸¡à¸­à¸±à¸™à¸•à¸£à¸²à¸¢</translation>
<translation id="1838667051080421715">คุณà¸à¸³à¸¥à¸±à¸‡à¸”ูà¹à¸«à¸¥à¹ˆà¸‡à¸—ี่มาของหน้าเว็บ</translation>
<translation id="1871208020102129563">พร็อà¸à¸‹à¸µà¸–ูà¸à¸•à¸±à¹‰à¸‡à¸„่าให้ใช้พร็อà¸à¸‹à¸µà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹à¸šà¸šà¸„งที่ ไม่ใช่ URL สคริปต์ .pac</translation>
<translation id="1883255238294161206">ยุบรายà¸à¸²à¸£</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">บุ๊à¸à¸¡à¸²à¸£à¹Œà¸à¸šà¸™à¸¡à¸·à¸­à¸–ือ</translation>
<translation id="2148716181193084225">วันนี้</translation>
-<translation id="2149973817440762519">à¹à¸à¹‰à¹„ขบุ๊à¸à¸¡à¸²à¸£à¹Œà¸</translation>
<translation id="2154054054215849342">ไม่มีà¸à¸²à¸£à¸‹à¸´à¸‡à¸„์สำหรับโดเมนของคุณ</translation>
<translation id="2166049586286450108">à¸à¸²à¸£à¹€à¸‚้าถึงระดับผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¹‚ดยสมบูรณ์</translation>
<translation id="2166378884831602661">เว็บไซต์นี้ไม่สามารถให้à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸—ี่ปลอดภัย</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">คุณไม่สามารถเข้าชม <ph name="SITE" /> ได้ในขณะนี้ เนื่องจาà¸à¹€à¸§à¹‡à¸šà¹„ซต์ดังà¸à¸¥à¹ˆà¸²à¸§à¹ƒà¸Šà¹‰ HSTS โดยปà¸à¸•à¸´à¸‚้อผิดพลาดของเครือข่ายà¹à¸¥à¸°à¸à¸²à¸£à¹‚จมตีจะเà¸à¸´à¸”ขึ้นเพียงชั่วคราว หน้านี้จึงอาจจะใช้งานได้ในภายหลัง <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">บุ๊à¸à¸¡à¸²à¸£à¹Œà¸à¸—ี่ไม่ถูà¸à¸•à¹‰à¸­à¸‡à¸–ูà¸à¹€à¸žà¸´à¸à¹€à¸‰à¸¢à¸—ี่ดัชนี <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">บุ๊à¸à¸¡à¸²à¸£à¹Œà¸à¸­à¸·à¹ˆà¸™à¹†</translation>
+<translation id="2355395290879513365">ผู้โจมตีอาจเห็นรูปภาพที่คุณà¸à¸³à¸¥à¸±à¸‡à¸”ูอยู่บนเว็บไซต์นี้à¹à¸¥à¸°à¸«à¸¥à¸­à¸à¸¥à¸§à¸‡à¸„ุณโดยà¸à¸²à¸£à¹à¸à¹‰à¹„ขรูปภาพ</translation>
<translation id="2359808026110333948">ดำเนินà¸à¸²à¸£à¸•à¹ˆà¸­</translation>
<translation id="2365563543831475020">รายงานข้อขัดข้องเมื่อ <ph name="CRASH_TIME" /> ยังไม่ได้อัปโหลด</translation>
<translation id="2367567093518048410">ระดับ</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">à¹à¸ªà¸”งนโยบายโดยที่ไม่ได้ตั้งค่า</translation>
<translation id="2396249848217231973">&amp;เลิà¸à¸—ำà¸à¸²à¸£à¸™à¸³à¸­à¸­à¸</translation>
<translation id="2455981314101692989">หน้าเว็บนี้ปิดใช้งานà¸à¸²à¸£à¸›à¹‰à¸­à¸™à¸­à¸±à¸•à¹‚นมัติสำหรับฟอร์มนี้</translation>
+<translation id="2460160116472764928">เมื่อเร็วๆ นี้ Google Safe Browsing <ph name="BEGIN_LINK" />ตรวจพบมัลà¹à¸§à¸£à¹Œ<ph name="END_LINK" />บน <ph name="SITE" /> บางครั้งเว็บไซต์ที่โดยปà¸à¸•à¸´à¹à¸¥à¹‰à¸§à¸ˆà¸°à¸›à¸¥à¸­à¸”ภัยอาจติดมัลà¹à¸§à¸£à¹Œà¹„ด้ <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">à¸à¸£à¸­à¸à¸‚้อมูล</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />เรียà¸à¹ƒà¸Šà¹‰à¸à¸²à¸£à¸§à¸´à¸™à¸´à¸ˆà¸‰à¸±à¸¢à¹€à¸„รือข่าย<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL ค้นหาไม่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸­à¹„ม่ว่าต้องà¸à¸²à¸£à¸™à¸³à¸­à¸­à¸à¸«à¸™à¹‰à¸²à¹€à¸«à¸¥à¹ˆà¸²à¸™à¸µà¹‰à¸­à¸­à¸à¸ˆà¸²à¸à¸›à¸£à¸°à¸§à¸±à¸•à¸´à¸à¸²à¸£à¹€à¸‚้าชมของคุณ</translation>
<translation id="2677748264148917807">ออà¸</translation>
<translation id="269990154133806163">เซิร์ฟเวอร์à¹à¸ªà¸”งใบรับรองที่ไม่เปิดเผยต่อสาธารณะโดยใช้นโยบายความโปร่งใสของใบรับรอง นี่เป็นข้อà¸à¸³à¸«à¸™à¸”สำหรับใบรับรองบางรายà¸à¸²à¸£ เพื่อให้à¹à¸™à¹ˆà¹ƒà¸ˆà¹„ด้ว่าใบรับรองมีความน่าเชื่อถือà¹à¸¥à¸°à¸›à¹‰à¸­à¸‡à¸à¸±à¸™à¸œà¸¹à¹‰à¹‚จมตีได้ <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">รายà¸à¸²à¸£à¸—ี่จะอ่าน</translation>
<translation id="2704283930420550640">ค่าไม่ตรงà¸à¸±à¸šà¸£à¸¹à¸›à¹à¸šà¸š</translation>
<translation id="2704951214193499422">Chromium ไม่สามารถยืนยันบัตรของคุณได้ในขณะนี้ โปรดลองอีà¸à¸„รั้งในภายหลัง</translation>
<translation id="2705137772291741111">อ่านสำเนาที่บันทึà¸à¹„ว้ (à¹à¸„ช) ของเว็บไซต์นี้ไม่ได้</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">คุณพยายามเข้าถึง <ph name="DOMAIN" /> à¹à¸•à¹ˆà¹ƒà¸šà¸£à¸±à¸šà¸£à¸­à¸‡à¸—ี่เซิร์ฟเวอร์à¹à¸ªà¸”งถูà¸à¹€à¸žà¸´à¸à¸–อนโดยผู้ออà¸à¹ƒà¸šà¸£à¸±à¸šà¸£à¸­à¸‡ ซึ่งหมายความว่าข้อมูลรับรองด้านความปลอดภัยที่เซิร์ฟเวอร์à¹à¸ªà¸”งมานั้นไม่สามารถเชื่อถือได้ คุณอาจà¸à¸³à¸¥à¸±à¸‡à¸ªà¸·à¹ˆà¸­à¸ªà¸²à¸£à¸­à¸¢à¸¹à¹ˆà¸à¸±à¸šà¸œà¸¹à¹‰à¹‚จมตี <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">ขออนุà¸à¸²à¸•</translation>
<translation id="2713444072780614174">สีขาว</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">คุณสามารถปิดใช้งานพร็อà¸à¸‹à¸µà¸—ี่à¸à¸³à¸«à¸™à¸”ค่าสำหรับà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸ˆà¸²à¸à¸«à¸™à¹‰à¸²à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าได้</translation>
<translation id="2955913368246107853">ปิดà¹à¸–บค้นหา</translation>
<translation id="2958431318199492670">à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าเครือข่ายไม่เป็นไปตามมาตรà¸à¸²à¸™ ONC à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าบางส่วนอาจไม่ได้รับà¸à¸²à¸£à¸™à¸³à¹€à¸‚้า</translation>
+<translation id="29611076221683977">ผู้บุà¸à¸£à¸¸à¸à¸—ี่à¸à¸³à¸¥à¸±à¸‡à¸­à¸¢à¸¹à¹ˆà¹ƒà¸™ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> อาจพยายามติดตั้งโปรà¹à¸à¸£à¸¡à¸­à¸±à¸™à¸•à¸£à¸²à¸¢à¸‹à¸¶à¹ˆà¸‡à¸‚โมยหรือลบข้อมูล (ตัวอย่างเช่น รูปภาพ รหัสผ่าน ข้อความ à¹à¸¥à¸°à¸šà¸±à¸•à¸£à¹€à¸„รดิต) ลงในเครื่อง Mac ของคุณ</translation>
<translation id="2969319727213777354">หาà¸à¸•à¹‰à¸­à¸‡à¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸—ี่ปลอดภัย คุณต้องตั้งค่านาฬิà¸à¸²à¹ƒà¸«à¹‰à¸–ูà¸à¸•à¹‰à¸­à¸‡à¹€à¸™à¸·à¹ˆà¸­à¸‡à¸ˆà¸²à¸à¹ƒà¸šà¸£à¸±à¸šà¸£à¸­à¸‡à¸—ี่เว็บไซต์ใช้เพื่อระบุตัวตนจะใช้ได้ในช่วงเวลาที่เจาะจงเท่านั้น à¹à¸•à¹ˆà¹€à¸™à¸·à¹ˆà¸­à¸‡à¸ˆà¸²à¸à¸™à¸²à¸¬à¸´à¸à¸²à¸‚องอุปà¸à¸£à¸“์ไม่ถูà¸à¸•à¹‰à¸­à¸‡ Google Chrome จึงไม่สามารถยืนยันใบรับรองเหล่านี้</translation>
<translation id="2972581237482394796">&amp;ทำซ้ำ</translation>
<translation id="2985306909656435243">หาà¸à¹€à¸›à¸´à¸”ใช้ไว้ Chromium จะจัดเà¸à¹‡à¸šà¸ªà¸³à¹€à¸™à¸²à¸šà¸±à¸•à¸£à¸‚องคุณในอุปà¸à¸£à¸“์นี้เพื่อà¸à¸²à¸£à¸à¸£à¸­à¸à¹à¸šà¸šà¸Ÿà¸­à¸£à¹Œà¸¡à¸—ี่รวดเร็วขึ้น</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">ซ่อนรายละเอียด</translation>
<translation id="3587482841069643663">ทั้งหมด</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">เซิร์ฟเวอร์ไม่สนับสนุนà¸à¸²à¸£à¸¢à¸­à¸¡à¸£à¸±à¸šà¸à¸²à¸£à¹à¸à¹‰à¹„ข TLS ซ้ำอีà¸à¸„รั้ง</translation>
<translation id="36224234498066874">ล้างข้อมูลà¸à¸²à¸£à¸—่องเว็บ</translation>
<translation id="362276910939193118">à¹à¸ªà¸”งประวัติà¸à¸²à¸£à¹€à¸‚้าชมทั้งหมด</translation>
<translation id="3623476034248543066">à¹à¸ªà¸”งค่า</translation>
@@ -271,19 +279,20 @@
<translation id="3655670868607891010">หาà¸à¸„ุณเห็นข้อความนี้บ่อยๆ ให้ลองไปที่ <ph name="HELP_LINK" /></translation>
<translation id="3658742229777143148">à¸à¸²à¸£à¹à¸à¹‰à¹„ข</translation>
<translation id="3678029195006412963">ไม่สามารถลงนามคำขอ</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="3696411085566228381">ไม่มี</translation>
<translation id="3704609568417268905"><ph name="TIME" /> <ph name="BOOKMARKED" /> <ph name="TITLE" /> <ph name="DOMAIN" /></translation>
-<translation id="370665806235115550">à¸à¸³à¸¥à¸±à¸‡à¹‚หลด...</translation>
+<translation id="370665806235115550">à¸à¸³à¸¥à¸±à¸‡à¹‚หลด ...</translation>
<translation id="3712624925041724820">ใบอนุà¸à¸²à¸•à¸«à¸¡à¸”</translation>
<translation id="3714780639079136834">เปิดข้อมูลเครือข่ายมือถือหรือ Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />ตรวจสอบพร็อà¸à¸‹à¸µ ไฟร์วอลล์ à¹à¸¥à¸°à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่า DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">หาà¸à¸„ุณเข้าใจความเสี่ยงต่อความปลอดภัย คุณสามารถ<ph name="BEGIN_LINK" />ไปยังไซต์ที่ไม่ปลอดภัยนี้<ph name="END_LINK" /> à¸à¹ˆà¸­à¸™à¸ˆà¸°à¸¡à¸µà¸à¸²à¸£à¸™à¸³à¹‚ปรà¹à¸à¸£à¸¡à¸­à¸±à¸™à¸•à¸£à¸²à¸¢à¸­à¸­à¸</translation>
<translation id="3739623965217189342">ลิงà¸à¹Œà¸—ี่คุณคัดลอà¸à¸¡à¸²</translation>
<translation id="375403751935624634">à¸à¸²à¸£à¹à¸›à¸¥à¸¥à¹‰à¸¡à¹€à¸«à¸¥à¸§à¹€à¸™à¸·à¹ˆà¸­à¸‡à¸ˆà¸²à¸à¸‚้อผิดพลาดของเซิร์ฟเวอร์</translation>
<translation id="3759461132968374835">คุณไม่ได้รายงานข้อขัดข้องเมื่อเร็วๆ นี้ ข้อขัดข้องที่เà¸à¸´à¸”ขึ้นเมื่อปิดใช้งานà¸à¸²à¸£à¸£à¸²à¸¢à¸‡à¸²à¸™à¸‚้อขัดข้อง จะไม่ปราà¸à¸à¸—ี่นี่</translation>
-<translation id="3788090790273268753">ใบรับรองของเว็บไซต์นี้จะหมดอายุในปี 2016 à¹à¸¥à¸°à¸à¸¥à¸¸à¹ˆà¸¡à¹ƒà¸šà¸£à¸±à¸šà¸£à¸­à¸‡à¸™à¸µà¹‰à¸›à¸£à¸°à¸à¸­à¸šà¸”้วยใบรับรองที่ลงนามโดยใช้ SHA-1</translation>
<translation id="382518646247711829">หาà¸à¸„ุณใช้พร็อà¸à¸‹à¸µà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œ...</translation>
<translation id="3828924085048779000">ข้อความรหัสผ่านต้องไม่เว้นว่างไว้</translation>
<translation id="3845539888601087042">à¸à¸³à¸¥à¸±à¸‡à¹à¸ªà¸”งประวัติà¸à¸²à¸£à¹€à¸‚้าชมจาà¸à¸­à¸¸à¸›à¸à¸£à¸“์ที่คุณลงชื่อเข้าใช้ <ph name="BEGIN_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LINK" /></translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">คีย์ "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">ไคลเอ็นต์à¹à¸¥à¸°à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹„ม่รองรับโปรโตคอล SSL เวอร์ชันทั่วไปหรือชุดà¸à¸²à¸£à¹€à¸‚้ารหัส</translation>
<translation id="4079302484614802869">à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าพร็อà¸à¸‹à¸µà¸¡à¸µà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าให้ใช้ URL สคริปต์ .pac ไม่ใช่พร็อà¸à¸‹à¸µà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹à¸šà¸šà¸„งที่</translation>
+<translation id="4098354747657067197">เว็บไซต์ข้างหน้ามีà¸à¸²à¸£à¸«à¸¥à¸­à¸à¸¥à¸§à¸‡</translation>
<translation id="4103249731201008433">หมายเลขซีเรียลของอุปà¸à¸£à¸“์ไม่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
<translation id="4103763322291513355">ไปที่ &lt;strong&gt;chrome://policy&lt;/strong&gt; เพื่อดูรายà¸à¸²à¸£à¸‚อง URL ที่ไม่ได้รับอนุà¸à¸²à¸• à¹à¸¥à¸°à¸™à¹‚ยบายอื่นๆ ที่ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¸‚องคุณบังคับใช้</translation>
<translation id="4110615724604346410">เซิร์ฟเวอร์นี้พิสูจน์ไม่ได้ว่าเป็น <ph name="DOMAIN" /> เพราะใบรับรองความปลอดภัยของเซิร์ฟเวอร์มีข้อผิดพลาด สาเหตุอาจเà¸à¸´à¸”จาà¸à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าผิดหรือผู้โจมตีขัดขวางà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸‚องคุณ <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ไม่มี}=1{1 à¹à¸­à¸› ($1)}=2{2 à¹à¸­à¸› ($1, $2)}other{# à¹à¸­à¸› ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">à¸à¸²à¸£à¸‚ัดข้อง</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />ลองเรียà¸à¹ƒà¸Šà¹‰à¸à¸²à¸£à¸§à¸´à¸™à¸´à¸ˆà¸‰à¸±à¸¢à¹€à¸„รือข่าย<ph name="END_LINK" /></translation>
+<translation id="4250431568374086873">à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸à¸±à¸šà¹€à¸§à¹‡à¸šà¹„ซต์นี้ไม่ปลอดภัยโดยสมบูรณ์</translation>
<translation id="4250680216510889253">ไม่มี</translation>
<translation id="425582637250725228">ระบบอาจไม่ได้บันทึà¸à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸‚องคุณ</translation>
<translation id="4258748452823770588">ลายเซ็นไม่เหมาะสม</translation>
<translation id="4269787794583293679">(ไม่มีชื่อผู้ใช้)</translation>
+<translation id="4280429058323657511">หมดอายุ <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">เมื่อเร็วๆ นี้ Google Safe Browsing <ph name="BEGIN_LINK" />พบโปรà¹à¸à¸£à¸¡à¸­à¸±à¸™à¸•à¸£à¸²à¸¢<ph name="END_LINK" />บน <ph name="SITE" /> <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">คำà¹à¸™à¸°à¸™à¸³à¸£à¸°à¸”ับบนสุด</translation>
<translation id="4304224509867189079">เข้าสู่ระบบ</translation>
<translation id="432290197980158659">เซิร์ฟเวอร์à¹à¸ªà¸”งใบรับรองที่ไม่ตรงà¸à¸±à¸šà¸à¸²à¸£à¸„าดà¸à¸²à¸£à¸“์ที่มีอยู่ à¸à¸²à¸£à¸„าดà¸à¸²à¸£à¸“์เหล่านี้มีอยู่ในบางเว็บไซต์ที่มีà¸à¸²à¸£à¸£à¸±à¸à¸©à¸²à¸„วามปลอดภัยสูงเพื่อปà¸à¸›à¹‰à¸­à¸‡à¸„ุณ <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">à¸à¸²à¸£à¸„้นหาบทความล้มเหลว</translation>
+<translation id="4326324639298822553">ตรวจสอบวันหมดอายุà¹à¸¥à¹‰à¸§à¸¥à¸­à¸‡à¸­à¸µà¸à¸„รั้ง</translation>
<translation id="4331708818696583467">ไม่ปลอดภัย</translation>
+<translation id="4356973930735388585">ผู้โจมตีในเว็บไซต์นี้อาจพยายามติดตั้งโปรà¹à¸à¸£à¸¡à¸­à¸±à¸™à¸•à¸£à¸²à¸¢à¸‹à¸¶à¹ˆà¸‡à¸ˆà¸°à¸‚โมยหรือลบข้อมูล (ตัวอย่างเช่น รูปภาพ รหัสผ่าน ข้อความ à¹à¸¥à¸°à¸šà¸±à¸•à¸£à¹€à¸„รดิต) ลงในคอมพิวเตอร์ของคุณ</translation>
<translation id="4372948949327679948">ค่า <ph name="VALUE_TYPE" /> ที่คาดไว้</translation>
<translation id="4381091992796011497">ชื่อผู้ใช้:</translation>
<translation id="4394049700291259645">ปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">คุณพยายามเข้าถึง <ph name="DOMAIN" /> à¹à¸•à¹ˆà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹à¸ªà¸”งใบรับรองที่ไม่ถูà¸à¸•à¹‰à¸­à¸‡ <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4592951414987517459">มีà¸à¸²à¸£à¹€à¸‚้ารหัสà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸‚องคุณà¸à¸±à¸š <ph name="DOMAIN" /> ด้วยชุดà¸à¸²à¸£à¹€à¸‚้ารหัสที่ทันสมัย</translation>
<translation id="4594403342090139922">&amp;เลิà¸à¸—ำà¸à¸²à¸£à¸™à¸³à¸­à¸­à¸</translation>
+<translation id="4619615317237390068">à¹à¸—็บจาà¸à¸­à¸¸à¸›à¸à¸£à¸“์อื่นๆ</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">คุณà¸à¸³à¸¥à¸±à¸‡à¸”ูหน้าส่วนขยาย</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">โหลดนโยบายซ้ำ</translation>
<translation id="4728558894243024398">à¹à¸žà¸¥à¸•à¸Ÿà¸­à¸£à¹Œà¸¡</translation>
<translation id="4744603770635761495">เส้นทางปà¸à¸´à¸šà¸±à¸•à¸´à¸à¸²à¸£</translation>
+<translation id="4750917950439032686">ข้อมูลของคุณ (ตัวอย่างเช่น รหัสผ่านหรือหมายเลขบัตรเครดิต) จะเป็นส่วนตัวเมื่อส่งมายังเว็บไซต์นี้</translation>
<translation id="4756388243121344051">&amp;ประวัติà¸à¸²à¸£à¹€à¸‚้าชม</translation>
+<translation id="4759118997339041434">ปิดใช้à¸à¸²à¸£à¸›à¹‰à¸­à¸™à¸à¸²à¸£à¸Šà¸³à¸£à¸°à¹€à¸‡à¸´à¸™à¸­à¸±à¸•à¹‚นมัติà¹à¸¥à¹‰à¸§</translation>
<translation id="4764776831041365478">หน้าเว็บใน <ph name="URL" /> อาจหยุดให้บริà¸à¸²à¸£à¸Šà¸±à¹ˆà¸§à¸„ราวหรืออาจถูà¸à¸¢à¹‰à¸²à¸¢à¹„ปยังที่อยู่เว็บใหม่อย่างถาวร</translation>
<translation id="4771973620359291008">มีข้อผิดพลาดที่ไม่ทราบเà¸à¸´à¸”ขึ้น</translation>
<translation id="4800132727771399293">ตรวจสอบวันหมดอายุà¹à¸¥à¸° CVC à¹à¸¥à¹‰à¸§à¸¥à¸­à¸‡à¸­à¸µà¸à¸„รั้ง</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">ข้อผิดพลาดของเครือข่าย</translation>
<translation id="4816492930507672669">พอดีà¸à¸±à¸šà¸«à¸™à¹‰à¸²</translation>
<translation id="4850886885716139402">มุมมอง</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> à¹à¸¥à¸° <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{à¹à¸¥à¸°à¸«à¸™à¹‰à¸²à¹€à¸§à¹‡à¸šà¸­à¸µà¸ 1 หน้า}other{à¹à¸¥à¸°à¸«à¸™à¹‰à¸²à¹€à¸§à¹‡à¸šà¸­à¸µà¸ # หน้า}}</translation>
<translation id="4923417429809017348">หน้านี้à¹à¸›à¸¥à¸ˆà¸²à¸à¸ à¸²à¸©à¸²à¸—ี่ไม่รู้จัà¸à¹€à¸›à¹‡à¸™à¸ à¸²à¸©à¸² <ph name="LANGUAGE_LANGUAGE" /></translation>
+<translation id="4923459931733593730">à¸à¸²à¸£à¸Šà¸³à¸£à¸°à¹€à¸‡à¸´à¸™</translation>
<translation id="4926049483395192435">ต้องระบุ</translation>
<translation id="495170559598752135">à¸à¸²à¸£à¸—ำงาน</translation>
<translation id="4958444002117714549">ขยายรายà¸à¸²à¸£</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">à¸à¸³à¸¥à¸±à¸‡à¸”าวน์โหลด</translation>
<translation id="5190835502935405962">à¹à¸–บบุ๊à¸à¸¡à¸²à¸£à¹Œà¸</translation>
<translation id="5199729219167945352">à¸à¸²à¸£à¸—ดลอง</translation>
-<translation id="5199841536747119669">คำà¹à¸™à¸°à¸™à¸³à¸ˆà¸°à¸›à¸£à¸²à¸à¸à¸—ี่นี่</translation>
<translation id="5251803541071282808">ระบบคลาวด์</translation>
<translation id="5277279256032773186">หาà¸à¹ƒà¸Šà¹‰ Chrome ที่ทำงาน ธุรà¸à¸´à¸ˆà¸ªà¸²à¸¡à¸²à¸£à¸–จัดà¸à¸²à¸£à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า Chrome ให้พนัà¸à¸‡à¸²à¸™à¸‚องตนได้ เรียนรู้เพิ่มเติม</translation>
<translation id="5299298092464848405">ข้อผิดพลาดในà¸à¸²à¸£à¹à¸¢à¸à¸§à¸´à¹€à¸„ราะห์นโยบาย</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">à¸à¸²à¸£à¸£à¸²à¸¢à¸‡à¸²à¸™à¸‚้อขัดข้องถูà¸à¸›à¸´à¸”ใช้งาน</translation>
<translation id="5317780077021120954">บันทึà¸</translation>
<translation id="5327248766486351172">ชื่อ</translation>
+<translation id="5337705430875057403">ผู้บุà¸à¸£à¸¸à¸à¸—ี่อยู่ใน <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> อาจหลอà¸à¸¥à¹ˆà¸­à¸„ุณเพื่อทำบางสิ่งที่อันตราย เช่น à¸à¸²à¸£à¸•à¸´à¸”ตั้งซอฟต์à¹à¸§à¸£à¹Œà¸«à¸£à¸·à¸­à¹€à¸›à¸´à¸”เผยข้อมูลส่วนบุคคล (ตัวอย่างเช่น รหัสผ่าน หมายเลขโทรศัพท์ หรือบัตรเครดิต)</translation>
<translation id="5359637492792381994">เซิร์ฟเวอร์นี้พิสูจน์ไม่ได้ว่าเป็น <ph name="DOMAIN" /> เพราะใบรับรองความปลอดภัยของเซิร์ฟเวอร์ใช้ไม่ได้ในขณะนี้ สาเหตุอาจเà¸à¸´à¸”จาà¸à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าผิดหรือผู้โจมตีขัดขวางà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸‚องคุณ <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">ไม่สามารถจัดเà¸à¹‡à¸šà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านโยบาย</translation>
+<translation id="5386426401304769735">à¸à¸¥à¸¸à¹ˆà¸¡à¹ƒà¸šà¸£à¸±à¸šà¸£à¸­à¸‡à¸ªà¸³à¸«à¸£à¸±à¸šà¹€à¸§à¹‡à¸šà¹„ซต์นี้มีใบรับรองที่ลงนามโดยใช้ SHA-1</translation>
<translation id="5421136146218899937">ล้างข้อมูลà¸à¸²à¸£à¸—่องเว็บ...</translation>
<translation id="5430298929874300616">นำบุ๊à¸à¸¡à¸²à¸£à¹Œà¸à¸­à¸­à¸</translation>
<translation id="5431657950005405462">ไม่พบไฟล์ของคุณ</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">หน้าที่à¸à¸±à¸‡à¹„ว้ใน <ph name="SITE" /> บอà¸à¸§à¹ˆà¸²:</translation>
<translation id="5556459405103347317">โหลดใหม่</translation>
<translation id="5565735124758917034">ใช้งานอยู่</translation>
+<translation id="5572851009514199876">โปรดเปิดà¹à¸¥à¸°à¸¥à¸‡à¸Šà¸·à¹ˆà¸­à¹€à¸‚้าใช้ Chrome เพื่อให้ Chrome ตรวจสอบได้ว่าคุณได้รับอนุà¸à¸²à¸•à¹ƒà¸«à¹‰à¹€à¸‚้าถึงไซต์นี้หรือไม่</translation>
+<translation id="5580958916614886209">ตรวจสอบเดือนหมดอายุà¹à¸¥à¹‰à¸§à¸¥à¸­à¸‡à¸­à¸µà¸à¸„รั้ง</translation>
<translation id="560412284261940334">ไม่สนับสนุนà¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£</translation>
<translation id="5610142619324316209">ตรวจสอบà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> เปลี่ยนเส้นทางของคุณบ่อยเà¸à¸´à¸™à¹„ป</translation>
<translation id="5622887735448669177">คุณต้องà¸à¸²à¸£à¸­à¸­à¸à¸ˆà¸²à¸à¹€à¸§à¹‡à¸šà¹„ซต์นี้ใช่ไหม</translation>
<translation id="5629630648637658800">ไม่สามารถโหลดà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านโยบาย</translation>
<translation id="5631439013527180824">โทเค็นà¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¸­à¸¸à¸›à¸à¸£à¸“์ไม่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
+<translation id="5669703222995421982">รับเนื้อหาที่ปรับเปลี่ยนในà¹à¸šà¸šà¸‚องคุณ</translation>
+<translation id="5675650730144413517">หน้านี้ใช้ไม่ได้</translation>
<translation id="5677928146339483299">ถูà¸à¸šà¸¥à¹‡à¸­à¸</translation>
<translation id="5694783966845939798">คุณพยายามเข้าถึง <ph name="DOMAIN" /> à¹à¸•à¹ˆà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹à¸ªà¸”งใบรับรองที่ลงนามด้วยอัลà¸à¸­à¸£à¸´à¸—ึมลายเซ็นที่ไม่รัดà¸à¸¸à¸¡ (เช่น SHA-1) ซึ่งหมายความว่าข้อมูลรับรองด้านความปลอดภัยที่เซิร์ฟเวอร์à¹à¸ªà¸”งอาจถูà¸à¸›à¸¥à¸­à¸¡à¹à¸›à¸¥à¸‡à¸‚ึ้น à¹à¸¥à¸°à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¸”ังà¸à¸¥à¹ˆà¸²à¸§à¸­à¸²à¸ˆà¹„ม่ใช่เซิร์ฟเวอร์ที่คุณคิด (คุณอาจà¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ต่อà¸à¸±à¸šà¸œà¸¹à¹‰à¹‚จมตี) <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5710435578057952990">ข้อมูลประจำตัวของเว็บไซต์นี้ยังไม่ได้รับà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™</translation>
<translation id="5720705177508910913">ผู้ใช้ปัจจุบัน</translation>
-<translation id="572328651809341494">à¹à¸—็บล่าสุด</translation>
<translation id="5732392974455271431">ผู้ปà¸à¸„รองสามารถเลิà¸à¸šà¸¥à¹‡à¸­à¸à¹€à¸§à¹‡à¸šà¹„ซต์ให้คุณ</translation>
<translation id="5784606427469807560">เà¸à¸´à¸”ปัà¸à¸«à¸²à¹ƒà¸™à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸šà¸±à¸•à¸£à¸‚องคุณ โปรดตรวจสอบà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸­à¸´à¸™à¹€à¸—อร์เน็ตà¹à¸¥à¸°à¸¥à¸­à¸‡à¸­à¸µà¸à¸„รั้ง</translation>
<translation id="5785756445106461925">นอà¸à¸ˆà¸²à¸à¸™à¸µà¹‰ หน้านี้ประà¸à¸­à¸šà¸”้วยทรัพยาà¸à¸£à¸­à¸·à¹ˆà¸™à¹† ซึ่งไม่ปลอดภัย ผู้อื่นสามารถดูทรัพยาà¸à¸£à¹€à¸«à¸¥à¹ˆà¸²à¸™à¸µà¹‰à¸‚ณะถ่ายโอน à¹à¸¥à¸°à¸œà¸¹à¹‰à¸šà¸¸à¸à¸£à¸¸à¸à¸ªà¸²à¸¡à¸²à¸£à¸–à¹à¸à¹‰à¹„ขเพื่อเปลี่ยนรูปลัà¸à¸©à¸“์ของหน้าได้</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">คุณพยายามเข้าถึง <ph name="DOMAIN" /> à¹à¸•à¹ˆà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹à¸ªà¸”งใบรับรองที่มีคีย์ที่ไม่รัดà¸à¸¸à¸¡ ผู้โจมตีอาจทำให้คีย์ส่วนตัวเสียหายไปà¹à¸¥à¹‰à¸§à¹à¸¥à¸°à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¸™à¸µà¹‰à¸­à¸²à¸ˆà¹„ม่ใช่เซิร์ฟเวอร์ที่คุณคิด (คุณอาจà¸à¸³à¸¥à¸±à¸‡à¸•à¸´à¸”ต่อà¸à¸±à¸šà¸œà¸¹à¹‰à¹‚จมตี) <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">โฟลเดอร์ใหม่</translation>
<translation id="5869405914158311789">ไม่สามารถเข้าถึงเว็บไซต์นี้</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Google Payments ไม่สนับสนุนบัตรประเภทนี้สำหรับผู้ขายรายนี้ โปรดเลือà¸à¸šà¸±à¸•à¸£à¸­à¸·à¹ˆà¸™</translation>
<translation id="59174027418879706">เปิดใช้งานà¹à¸¥à¹‰à¸§</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="5966707198760109579">สัปดาห์</translation>
<translation id="5967867314010545767">ลบจาà¸à¸›à¸£à¸°à¸§à¸±à¸•à¸´à¸à¸²à¸£à¹€à¸‚้าชม</translation>
<translation id="5975083100439434680">ย่อ</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">ลอง:</translation>
<translation id="6151417162996330722">ใบรับรองเซิร์ฟเวอร์มีระยะเวลาที่สามารถใช้ได้นานเà¸à¸´à¸™à¹„ป</translation>
<translation id="6165508094623778733">เรียนรู้เพิ่มเติม</translation>
+<translation id="6177128806592000436">à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸à¸±à¸šà¹€à¸§à¹‡à¸šà¹„ซต์นี้ไม่ปลอดภัย</translation>
<translation id="6203231073485539293">ตรวจสอบà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸­à¸´à¸™à¹€à¸—อร์เน็ต</translation>
<translation id="6218753634732582820">ต้องà¸à¸²à¸£à¸™à¸³à¸—ี่อยู่ออà¸à¸ˆà¸²à¸ Chromium ใช่ไหม</translation>
+<translation id="6251924700383757765">นโยบายความเป็นส่วนตัว</translation>
+<translation id="625755898061068298">คุณเลือà¸à¸›à¸´à¸”ใช้คำเตือนด้านความปลอดภัยของเว็บไซต์นี้</translation>
<translation id="6259156558325130047">&amp;ทำซ้ำà¸à¸²à¸£à¸ˆà¸±à¸”ลำดับใหม่</translation>
<translation id="6263376278284652872">บุ๊à¸à¸¡à¸²à¸£à¹Œà¸ <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">à¸à¸¥à¸±à¸šà¸ªà¸¹à¹ˆà¸„วามปลอดภัย</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">ไม่สามารถเข้าถึง <ph name="URL" /></translation>
<translation id="6321917430147971392">ตรวจสอบà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า DNS</translation>
<translation id="6328639280570009161">ลองปิดà¸à¸²à¸£à¸„าดคะเนเครือข่าย</translation>
+<translation id="6328786501058569169">เว็บไซต์นี้มีà¸à¸²à¸£à¸«à¸¥à¸­à¸à¸¥à¸§à¸‡</translation>
<translation id="6337534724793800597">à¸à¸£à¸­à¸‡à¸™à¹‚ยบายตามชื่อ</translation>
<translation id="6342069812937806050">เพิ่งเสร็จ</translation>
<translation id="6345221851280129312">ไม่ทราบขนาด</translation>
@@ -489,6 +519,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{อีภ1 คำà¹à¸™à¸°à¸™à¸³}other{อีภ# คำà¹à¸™à¸°à¸™à¸³}}</translation>
<translation id="6387478394221739770">หาà¸à¸ªà¸™à¹ƒà¸ˆà¹ƒà¸™à¸„ุณลัà¸à¸©à¸“ะสุดเจ๋งของ Chrome ใหม่ ลองใช้เวอร์ชันเบต้าของเราที่ chrome.com/beta</translation>
<translation id="6389758589412724634">Chromium หน่วยความจำเต็มเมื่อพยายามà¹à¸ªà¸”งหน้าเว็บนี้</translation>
+<translation id="6404511346730675251">à¹à¸à¹‰à¹„ขบุ๊à¸à¸¡à¸²à¸£à¹Œà¸</translation>
+<translation id="6410264514553301377">ป้อนวันหมดอายุà¹à¸¥à¸° CVC ของ <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">คุณถามผู้ปà¸à¸„รองà¹à¸¥à¹‰à¸§à¸§à¹ˆà¸²à¸ªà¸²à¸¡à¸²à¸£à¸–เข้าชมเว็บไซต์นี้ได้ไหม</translation>
<translation id="6416403317709441254">คุณไม่สามารถเข้าชม <ph name="SITE" /> ได้ในขณะนี้ เนื่องจาà¸à¹€à¸§à¹‡à¸šà¹„ซต์ส่งข้อมูลรับรองที่ถูà¸à¹à¸›à¸¥à¸‡à¸‹à¸¶à¹ˆà¸‡ Chromium ไม่สามารถดำเนินà¸à¸²à¸£à¹„ด้ โดยปà¸à¸•à¸´à¸‚้อผิดพลาดของเครือข่ายà¹à¸¥à¸°à¸à¸²à¸£à¹‚จมตีจะเà¸à¸´à¸”ขึ้นเพียงชั่วคราว หน้านี้จึงอาจจะใช้งานได้ในภายหลัง <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">ไม่สามารถตรวจสอบว่าใบรับรองถูà¸à¹€à¸žà¸´à¸à¸–อนหรือไม่</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">ไม่ใช้งานเนื่องจาà¸à¸à¸²à¸£à¸„้นหาเริ่มต้นถูà¸à¸›à¸´à¸”ใช้งานตามนโยบาย</translation>
<translation id="6462969404041126431">เซิร์ฟเวอร์นี้พิสูจน์ไม่ได้ว่าเป็น <ph name="DOMAIN" /> เพราะใบรับรองความปลอดภัยของเซิร์ฟเวอร์อาจถูà¸à¹€à¸žà¸´à¸à¸–อนไปà¹à¸¥à¹‰à¸§ สาเหตุอาจเà¸à¸´à¸”จาà¸à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าผิดหรือผู้โจมตีขัดขวางà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸‚องคุณ <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="647261751007945333">นโยบายอุปà¸à¸£à¸“์</translation>
+<translation id="6477321094435799029">Chrome ได้ตรวจพบรหัสที่ผิดปà¸à¸•à¸´à¸šà¸™à¸«à¸™à¹‰à¸²à¸™à¸µà¹‰à¹à¸¥à¸°à¹„ด้บล็อà¸à¸£à¸«à¸±à¸ªà¸”ังà¸à¸¥à¹ˆà¸²à¸§à¹€à¸žà¸·à¹ˆà¸­à¸›à¸à¸›à¹‰à¸­à¸‡à¸‚้อมูลส่วนบุคคลของคุณ (เช่น รหัสผ่าน หมายเลขโทรศัพท์ à¹à¸¥à¸°à¸šà¸±à¸•à¸£à¹€à¸„รดิต)</translation>
<translation id="6489534406876378309">เริ่มอัปโหลดข้อขัดข้อง</translation>
<translation id="6529602333819889595">&amp;ทำซ้ำà¸à¸²à¸£à¸™à¸³à¸­à¸­à¸</translation>
<translation id="6534179046333460208">คำà¹à¸™à¸°à¸™à¸³ Physical Web</translation>
<translation id="6550675742724504774">ตัวเลือà¸</translation>
+<translation id="6556239504065605927">à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸—ี่ปลอดภัย</translation>
<translation id="6563469144985748109">ผู้จัดà¸à¸²à¸£à¸¢à¸±à¸‡à¹„ม่ได้อนุมัติเว็บไซต์นี้</translation>
<translation id="6593753688552673085">น้อยà¸à¸§à¹ˆà¸² <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">ตัวเลือà¸à¸à¸²à¸£à¹€à¸‚้ารหัส</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">นโยบายนี้ถูà¸à¸¢à¸à¹€à¸¥à¸´à¸à¹à¸¥à¹‰à¸§</translation>
<translation id="6652240803263749613">เซิร์ฟเวอร์นี้พิสูจน์ไม่ได้ว่าเป็น <ph name="DOMAIN" /> เพราะระบบปà¸à¸´à¸šà¸±à¸•à¸´à¸à¸²à¸£à¸‚องคอมพิวเตอร์ไม่เชื่อถือใบรับรองความปลอดภัยของเซิร์ฟเวอร์นี้ สาเหตุอาจเà¸à¸´à¸”จาà¸à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าผิดหรือผู้โจมตีขัดขวางà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸‚องคุณ <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">à¹à¸à¹‰à¹„ขโฟลเดอร์</translation>
-<translation id="6660210980321319655">รายงานอัตโนมัติเมื่อ<ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">ต้องà¸à¸²à¸£à¸™à¸³à¸„ำà¹à¸™à¸°à¸™à¸³à¸ªà¸³à¸«à¸£à¸±à¸šà¹à¸šà¸šà¸Ÿà¸­à¸£à¹Œà¸¡à¸­à¸­à¸à¸ˆà¸²à¸ Chromium ใช่ไหม</translation>
<translation id="6685834062052613830">ออà¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸šà¹à¸¥à¸°à¸•à¸±à¹‰à¸‡à¸„่าให้เสร็จสมบูรณ์</translation>
<translation id="6710213216561001401">à¸à¹ˆà¸­à¸™à¸«à¸™à¹‰à¸²</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">ค่านโยบาย</translation>
<translation id="6757797048963528358">อุปà¸à¸£à¸“์ของคุณเข้าสู่โหมดสลีปà¹à¸¥à¹‰à¸§</translation>
<translation id="6778737459546443941">ผู้ปà¸à¸„รองยังไม่ได้อนุมัติเว็บไซต์นี้</translation>
+<translation id="6810899417690483278">รหัสà¸à¸²à¸£à¸›à¸£à¸±à¸šà¹à¸•à¹ˆà¸‡</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">หน้าเว็บที่ <ph name="URL" /> ไม่สามารถใช้งานได้ในขณะนี้ หน้าเว็บอาจà¸à¸³à¸¥à¸±à¸‡à¸–ูà¸à¹€à¸£à¸µà¸¢à¸à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸¡à¸²à¸à¹€à¸à¸´à¸™à¹„ปหรือถูà¸à¸›à¸´à¸”เพื่อซ่อมบำรุง</translation>
<translation id="6831043979455480757">à¹à¸›à¸¥à¸ à¸²à¸©à¸²</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">ข้อมูลติดต่อ</translation>
+<translation id="7079718277001814089">เว็บไซต์นี้มีมัลà¹à¸§à¸£à¹Œ</translation>
<translation id="7087282848513945231">อำเภอ</translation>
<translation id="7088615885725309056">เà¸à¹ˆà¸²à¸à¸§à¹ˆà¸²</translation>
<translation id="7090678807593890770">ค้นหา <ph name="LINK" /> จาภGoogle</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> ไม่เป็นไปตามมาตรà¸à¸²à¸™à¸„วามปลอดภัย</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LINK" /> เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸›à¸±à¸à¸«à¸²à¸™à¸µà¹‰</translation>
<translation id="7219179957768738017">à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¹ƒà¸Šà¹‰ <ph name="SSL_VERSION" /></translation>
+<translation id="724691107663265825">ไซต์ที่จะเปิดมีมัลà¹à¸§à¸£à¹Œ</translation>
<translation id="724975217298816891">ป้อนวันหมดอายุà¹à¸¥à¸° CVC สำหรับ <ph name="CREDIT_CARD" /> เพื่ออัปเดตรายละเอียดของบัตร เมื่อคุณยืนยันà¹à¸¥à¹‰à¸§ รายละเอียดบัตรของคุณจะà¹à¸Šà¸£à¹Œà¸à¸±à¸šà¹€à¸§à¹‡à¸šà¹„ซต์นี้</translation>
<translation id="725866823122871198">ไม่สามารถเริ่มà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸ªà¹ˆà¸§à¸™à¸•à¸±à¸§à¸à¸±à¸š <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ได้เนื่องจาà¸à¸§à¸±à¸™à¸—ี่à¹à¸¥à¸°à¹€à¸§à¸¥à¸²à¸‚องคอมพิวเตอร์ (<ph name="DATE_AND_TIME" />) ไม่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
<translation id="7269802741830436641">หน้าเว็บนี้มีà¸à¸²à¸£à¸§à¸™à¸£à¸­à¸šà¹€à¸¡à¸·à¹ˆà¸­à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹€à¸ªà¹‰à¸™à¸—าง</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">ยà¸à¹€à¸¥à¸´à¸</translation>
<translation id="7667346355482952095">โทเค็นนโยบายที่ส่งà¸à¸¥à¸±à¸šà¸§à¹ˆà¸²à¸‡à¹€à¸›à¸¥à¹ˆà¸²à¸«à¸£à¸·à¸­à¹„ม่ตรงà¸à¸±à¸šà¹‚ทเค็นปัจจุบัน</translation>
<translation id="7668654391829183341">อุปà¸à¸£à¸“์ที่ไม่รู้จัà¸</translation>
+<translation id="7669271284792375604">ผู้โจมตีเว็บไซต์นี้อาจพยายามหลอà¸à¸¥à¹ˆà¸­à¹ƒà¸«à¹‰à¸„ุณติดตั้งโปรà¹à¸à¸£à¸¡à¸—ี่เป็นอันตรายต่อประสบà¸à¸²à¸£à¸“์à¸à¸²à¸£à¸—่องเว็บของคุณ (ตัวอย่างเช่น โดยà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸«à¸™à¹‰à¸²à¹à¸£à¸à¸«à¸£à¸·à¸­à¹à¸ªà¸”งโฆษณาเพิ่มเติมในเว็บไซต์ที่คุณเข้าชม)</translation>
<translation id="7674629440242451245">หาà¸à¸ªà¸™à¹ƒà¸ˆà¹ƒà¸™à¸„ุณลัà¸à¸©à¸“ะใหม่ๆ สุดเจ๋งของ Chrome ลองใช้เวอร์ชันที่à¸à¸³à¸¥à¸±à¸‡à¸žà¸±à¸’นาของเราที่ chrome.com/dev</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />ไปยัง <ph name="SITE" /> (ไม่ปลอดภัย)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">ไม่สามารถโหลดเว็บไซต์นี้จาà¸à¹à¸„ช</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸–ูà¸à¹€à¸‚้ารหัสโดยใช้ <ph name="CIPHER" /> โดยใช้ <ph name="MAC" /> สำหรับตรวจสอบสิทธิ์ข้อความà¹à¸¥à¸° <ph name="KX" /> เป็นà¸à¸¥à¹„ลà¸à¸²à¸£à¹à¸¥à¸à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸à¸¸à¸à¹à¸ˆ</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_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="7887683347370398519">ตรวจสอบ CVC à¹à¸¥à¸°à¸¥à¸­à¸‡à¸­à¸µà¸à¸„รั้ง</translation>
<translation id="7912024687060120840">ในโฟลเดอร์:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">ใบรับรองของเซิร์ฟเวอร์ยังไม่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
<translation id="7942349550061667556">สีà¹à¸”ง</translation>
+<translation id="7947285636476623132">ตรวจสอบปีหมดอายุà¹à¸¥à¹‰à¸§à¸¥à¸­à¸‡à¸­à¸µà¸à¸„รั้ง</translation>
<translation id="7951415247503192394">(32 บิต)</translation>
<translation id="7956713633345437162">บุ๊à¸à¸¡à¸²à¸£à¹Œà¸à¸šà¸™à¸¡à¸·à¸­à¸–ือ</translation>
<translation id="7961015016161918242">ไม่ต้องเลย</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">à¹à¸›à¸¥ <ph name="ORIGINAL_LANGUAGE" /> เป็น <ph name="TARGET_LANGUAGE" /> เสมอ</translation>
<translation id="7995512525968007366">ไม่ได้ระบุ</translation>
<translation id="8012647001091218357">เราไม่สามารถติดต่อผู้ปà¸à¸„รองของคุณได้ในขณะนี้ โปรดลองอีà¸à¸„รั้ง</translation>
+<translation id="8025119109950072390">ผู้โจมตีในเว็บไซต์นี้อาจหลอà¸à¸¥à¹ˆà¸­à¹ƒà¸«à¹‰à¸„ุณทำบางสิ่งที่อันตราย เช่น à¸à¸²à¸£à¸•à¸´à¸”ตั้งซอฟต์à¹à¸§à¸£à¹Œà¸«à¸£à¸·à¸­à¹€à¸›à¸´à¸”เผยข้อมูลส่วนบุคคล (ตัวอย่างเช่น รหัสผ่าน หมายเลขโทรศัพท์ หรือบัตรเครดิต)</translation>
+<translation id="803030522067524905">เมื่อเร็วๆ นี้ Google Safe Browsing ตรวจพบฟิชชิงบน <ph name="SITE" /> เว็บไซต์ฟิชชิงปลอมเป็นเว็บไซต์อื่นๆ เพื่อหลอà¸à¸„ุณ <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">หน้าเว็บนี้อยู่ในภาษา<ph name="SOURCE_LANGUAGE" /> ต้องà¸à¸²à¸£à¹à¸›à¸¥à¹€à¸›à¹‡à¸™à¸ à¸²à¸©à¸²<ph name="TARGET_LANGUAGE" />ไหม</translation>
+<translation id="8041089156583427627">ส่งความคิดเห็น</translation>
<translation id="8088680233425245692">à¸à¸²à¸£à¸”ูบทความล้มเหลว</translation>
<translation id="8089520772729574115">ไม่ถึง 1 MB</translation>
<translation id="8091372947890762290">à¸à¸³à¸¥à¸±à¸‡à¸£à¸­à¸à¸²à¸£à¹€à¸›à¸´à¸”ใช้งานบนเซิร์ฟเวอร์</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719">ไฟล์ที่ <ph name="URL" /> ไม่สามารถอ่านได้ เนื่องจาà¸à¸­à¸²à¸ˆà¸–ูà¸à¸¥à¸š ย้ายไปà¹à¸¥à¹‰à¸§ หรือà¸à¸²à¸£à¸­à¸™à¸¸à¸à¸²à¸•à¸‚องไฟล์อาจป้องà¸à¸±à¸™à¸à¸²à¸£à¹€à¸‚้าถึง</translation>
<translation id="8194797478851900357">&amp;เลิà¸à¸—ำà¸à¸²à¸£à¸¢à¹‰à¸²à¸¢</translation>
<translation id="8201077131113104583">à¸à¸²à¸£à¸­à¸±à¸›à¹€à¸”ต URL ไม่ถูà¸à¸•à¹‰à¸­à¸‡à¸ªà¸³à¸«à¸£à¸±à¸šà¸ªà¹ˆà¸§à¸™à¸‚ยายรหัส "<ph name="EXTENSION_ID" />"</translation>
+<translation id="8202097416529803614">ข้อมูลสรุปคำสั่งซื้อ</translation>
<translation id="8218327578424803826">ตำà¹à¸«à¸™à¹ˆà¸‡à¸—ี่มอบหมาย:</translation>
<translation id="8225771182978767009">ผู้ที่ตั้งค่าคอมพิวเตอร์เครื่องนี้เลือà¸à¸šà¸¥à¹‡à¸­à¸à¹€à¸§à¹‡à¸šà¹„ซต์นี้</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">ผู้บุà¸à¸£à¸¸à¸à¸—ี่à¸à¸³à¸¥à¸±à¸‡à¸­à¸¢à¸¹à¹ˆà¹ƒà¸™ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> อาจพยายามติดตั้งโปรà¹à¸à¸£à¸¡à¸­à¸±à¸™à¸•à¸£à¸²à¸¢à¸‹à¸¶à¹ˆà¸‡à¸ˆà¸°à¸‚โมยหรือลบข้อมูล (ตัวอย่างเช่น รูปภาพ รหัสผ่าน ข้อความ à¹à¸¥à¸°à¸šà¸±à¸•à¸£à¹€à¸„รดิต) ลงในคอมพิวเตอร์ของคุณ</translation>
<translation id="8241707690549784388">หน้าที่คุณà¸à¸³à¸¥à¸±à¸‡à¸¡à¸­à¸‡à¸«à¸²à¹ƒà¸Šà¹‰à¸‚้อมูลที่คุณได้ป้อนไว้à¹à¸¥à¹‰à¸§ à¸à¸²à¸£à¸à¸¥à¸±à¸šà¹„ปสู่หน้านั้นอาจทำให้คุณต้องทำซ้ำà¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¹ƒà¸”ๆ ที่คุณทำà¹à¸¥à¹‰à¸§ คุณต้องà¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸•à¹ˆà¸­à¸«à¸£à¸·à¸­à¹„ม่</translation>
<translation id="8249320324621329438">เรียà¸à¸”ูครั้งสุดท้ายเมื่อ:</translation>
<translation id="8261506727792406068">ลบ</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">à¹à¸«à¸¥à¹ˆà¸‡à¸—ี่มา</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="8349305172487531364">à¹à¸–บบุ๊à¸à¸¡à¸²à¸£à¹Œà¸</translation>
<translation id="8363502534493474904">ปิดโหมดบนเครื่องบิน</translation>
<translation id="8364627913115013041">ไม่ได้ตั้งค่า</translation>
<translation id="8380941800586852976">อันตราย</translation>
<translation id="8382348898565613901">บุ๊à¸à¸¡à¸²à¸£à¹Œà¸à¸—ี่คุณเข้าชมล่าสุดจะปราà¸à¸à¸—ี่นี่</translation>
+<translation id="8398259832188219207">อัปโหลดรายงานข้อขัดข้องเมื่อ <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">ข้อขัดข้อง (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">คุณต้องป้อนข้อความรหัสผ่านที่เหมือนà¸à¸±à¸™à¸ªà¸­à¸‡à¸„รั้ง</translation>
<translation id="8428213095426709021">à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ใช้เวลาตอบà¸à¸¥à¸±à¸šà¸™à¸²à¸™à¹€à¸à¸´à¸™à¹„ป</translation>
<translation id="852346902619691059">เซิร์ฟเวอร์นี้พิสูจน์ไม่ได้ว่าเป็น <ph name="DOMAIN" /> เพราะระบบปà¸à¸´à¸šà¸±à¸•à¸´à¸à¸²à¸£à¸‚องอุปà¸à¸£à¸“์ไม่เชื่อถือใบรับรองความปลอดภัยของเซิร์ฟเวอร์นี้ สาเหตุอาจเà¸à¸´à¸”จาà¸à¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าผิดหรือผู้โจมตีขัดขวางà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸‚องคุณ <ph name="BEGIN_LEARN_MORE_LINK" />เรียนรู้เพิ่มเติม<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Google Payments ไม่สนับสนุนบัตรประเภทนี้ โปรดเลือà¸à¸šà¸±à¸•à¸£à¸­à¸·à¹ˆà¸™</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="8553075262323480129">à¸à¸²à¸£à¹à¸›à¸¥à¸¥à¹‰à¸¡à¹€à¸«à¸¥à¸§à¹€à¸™à¸·à¹ˆà¸­à¸‡à¸ˆà¸²à¸à¹„ม่สามารถระบุภาษาของหน้าเว็บนี้ได้</translation>
<translation id="8559762987265718583">ไม่สามารถเริ่มà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸ªà¹ˆà¸§à¸™à¸•à¸±à¸§à¸à¸±à¸š <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ได้เนื่องจาà¸à¸§à¸±à¸™à¸—ี่à¹à¸¥à¸°à¹€à¸§à¸¥à¸²à¸‚องอุปà¸à¸£à¸“์ (<ph name="DATE_AND_TIME" />) ไม่ถูà¸à¸•à¹‰à¸­à¸‡</translation>
-<translation id="856992080682148">ใบรับรองสำหรับเว็บไซต์นี้จะหมดอายุในปี 2017 หรือหลังจาà¸à¸™à¸±à¹‰à¸™ à¸à¸¥à¸¸à¹ˆà¸¡à¹ƒà¸šà¸£à¸±à¸šà¸£à¸­à¸‡à¸™à¸µà¹‰à¸›à¸£à¸°à¸à¸­à¸šà¸”้วยใบรับรอบที่ลงนามโดยใช้ SHA-1</translation>
<translation id="8571890674111243710">à¸à¸³à¸¥à¸±à¸‡à¹à¸›à¸¥à¸«à¸™à¹‰à¸²à¹€à¸§à¹‡à¸šà¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸ à¸²à¸©à¸²<ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">ใบรับรองไม่ระบุวิธีà¸à¸²à¸£à¸•à¸£à¸§à¸ˆà¸ªà¸­à¸šà¸§à¹ˆà¸²à¸¡à¸µà¸à¸²à¸£à¹€à¸žà¸´à¸à¸–อนไปà¹à¸¥à¹‰à¸§à¸«à¸£à¸·à¸­à¹„ม่</translation>
<translation id="8620436878122366504">ผู้ปà¸à¸„รองยังไม่ได้อนุมัติเว็บไซต์นี้</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522">ไม่พบ&lt;abbr id="dnsDefinition"&gt;ที่อยู่ DNS&lt;/abbr&gt; ของ <ph name="HOST_NAME" /> à¸à¸³à¸¥à¸±à¸‡à¸§à¸´à¸™à¸´à¸ˆà¸‰à¸±à¸¢à¸›à¸±à¸à¸«à¸²</translation>
<translation id="8790007591277257123">&amp;ทำซ้ำà¸à¸²à¸£à¸™à¸³à¸­à¸­à¸</translation>
<translation id="8798099450830957504">ค่าเริ่มต้น</translation>
+<translation id="8800988563907321413">คำà¹à¸™à¸°à¸™à¸³à¹ƒà¸™à¸šà¸£à¸´à¹€à¸§à¸“ใà¸à¸¥à¹‰à¹€à¸„ียงจะปราà¸à¸à¸—ี่นี่</translation>
<translation id="8804164990146287819">นโยบายความเป็นส่วนตัว</translation>
<translation id="8820817407110198400">บุ๊à¸à¸¡à¸²à¸£à¹Œà¸</translation>
<translation id="8834246243508017242">เปิดใช้à¸à¸²à¸£à¸›à¹‰à¸­à¸™à¸‚้อความอัตโนมัติโดยใช้รายชื่อติดต่อ…</translation>
<translation id="883848425547221593">บุ๊à¸à¸¡à¸²à¸£à¹Œà¸à¸­à¸·à¹ˆà¸™à¹†</translation>
+<translation id="884264119367021077">ที่อยู่จัดส่ง</translation>
<translation id="884923133447025588">ไม่พบà¸à¸£à¸°à¸šà¸§à¸™à¸à¸²à¸£à¹€à¸žà¸´à¸à¸–อน</translation>
<translation id="885730110891505394">à¸à¸²à¸£à¹à¸Šà¸£à¹Œà¸à¸±à¸š Google</translation>
<translation id="8866481888320382733">ข้อผิดพลาดในà¸à¸²à¸£à¹à¸¢à¸à¸§à¸´à¹€à¸„ราะห์à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่านโยบาย</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">ใบรับรองของเซิร์ฟเวอร์หมดอายุà¹à¸¥à¹‰à¸§</translation>
<translation id="8987927404178983737">เดือน</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">ไซต์ที่จะเปิดมีโปรà¹à¸à¸£à¸¡à¸—ี่เป็นอันตราย</translation>
<translation id="9001074447101275817"><ph name="DOMAIN" /> ของพร็อà¸à¸‹à¸µà¸•à¹‰à¸­à¸‡à¹ƒà¸Šà¹‰à¸Šà¸·à¹ˆà¸­à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹à¸¥à¸°à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™</translation>
<translation id="901974403500617787">à¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่าสถานะที่ใช้ทั้งระบบสามารถตั้งค่าได้โดยเจ้าของเท่านั้น: <ph name="OWNER_EMAIL" /></translation>
<translation id="9020542370529661692">หน้านี้ได้รับà¸à¸²à¸£à¹à¸›à¸¥à¹€à¸›à¹‡à¸™ <ph name="TARGET_LANGUAGE" /> à¹à¸¥à¹‰à¸§</translation>
+<translation id="9035022520814077154">ข้อผิดพลาดของà¸à¸²à¸£à¸£à¸±à¸à¸©à¸²à¸„วามปลอดภัย</translation>
<translation id="9038649477754266430">ใช้บริà¸à¸²à¸£à¸à¸²à¸£à¸„าดคะเนเพื่อโหลดหน้าได้เร็วขึ้น</translation>
<translation id="9039213469156557790">นอà¸à¸ˆà¸²à¸à¸™à¸µà¹‰ หน้านี้ประà¸à¸­à¸šà¸”้วยทรัพยาà¸à¸£à¸­à¸·à¹ˆà¸™à¹† ซึ่งไม่ปลอดภัย ผู้อื่นสามารถดูทรัพยาà¸à¸£à¹€à¸«à¸¥à¹ˆà¸²à¸™à¸µà¹‰à¸‚ณะถ่ายโอน à¹à¸¥à¸°à¸œà¸¹à¹‰à¸šà¸¸à¸à¸£à¸¸à¸à¸ªà¸²à¸¡à¸²à¸£à¸–à¹à¸à¹‰à¹„ขเพื่อเปลี่ยนà¸à¸²à¸£à¸—ำงานของหน้าได้</translation>
+<translation id="9040185888511745258">ผู้บุà¸à¸£à¸¸à¸à¹€à¸¡à¸·à¹ˆà¸­à¸§à¸±à¸™à¸—ี่ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> อาจพยายามหลอà¸à¸¥à¸§à¸‡à¹ƒà¸«à¹‰à¸„ุณติดตั้งโปรà¹à¸à¸£à¸¡à¸—ี่เป็นอันตรายต่อประสบà¸à¸²à¸£à¸“์ในà¸à¸²à¸£à¸—่องเว็บของคุณ (ตัวอย่างเช่น โดยà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡à¸«à¸™à¹‰à¸²à¹à¸£à¸à¸«à¸£à¸·à¸­à¹à¸ªà¸”งโฆษณาเพิ่มเติมบนเว็บไซต์ที่คุณเข้าชม)</translation>
<translation id="9050666287014529139">ข้อความรหัสผ่าน</translation>
<translation id="9065203028668620118">à¹à¸à¹‰à¹„ข</translation>
<translation id="9068849894565669697">เลือà¸à¸ªà¸µ</translation>
<translation id="9076283476770535406">เว็บไซต์นี้อาจมีเนื้อหาสำหรับผู้ใหà¸à¹ˆ</translation>
-<translation id="9092364396508701805">หน้า <ph name="HOST_NAME" /> ไม่ทำงาน</translation>
<translation id="9103872766612412690">โดยทั่วไป <ph name="SITE" /> จะใช้à¸à¸²à¸£à¹€à¸‚้ารหัสเพื่อปà¸à¸›à¹‰à¸­à¸‡à¸‚้อมูลของคุณ เมื่อ Chromium พยายามเชื่อมต่อà¸à¸±à¸š <ph name="SITE" /> ในครั้งนี้ เว็บไซต์ดังà¸à¸¥à¹ˆà¸²à¸§à¸ªà¹ˆà¸‡à¸‚้อมูลรับรองที่ผิดปà¸à¸•à¸´à¹à¸¥à¸°à¹„ม่ถูà¸à¸•à¹‰à¸­à¸‡à¸à¸¥à¸±à¸šà¸¡à¸² เหตุà¸à¸²à¸£à¸“์นี้อาจเà¸à¸´à¸”ขึ้นเมื่อผู้บุà¸à¸£à¸¸à¸à¸žà¸¢à¸²à¸¢à¸²à¸¡à¸›à¸¥à¸­à¸¡à¹€à¸›à¹‡à¸™ <ph name="SITE" /> หรือหน้าจอà¸à¸²à¸£à¸¥à¸‡à¸Šà¸·à¹ˆà¸­à¹€à¸‚้าใช้ Wi-Fi รบà¸à¸§à¸™à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­ ข้อมูลของคุณยังปลอดภัยอยู่เนื่องจาภChromium หยุดà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸à¹ˆà¸­à¸™à¸¡à¸µà¸à¸²à¸£à¹à¸¥à¸à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸‚้อมูล</translation>
<translation id="9137013805542155359">à¹à¸ªà¸”งหน้าเว็บเดิม</translation>
+<translation id="9137248913990643158">โปรดเปิดà¹à¸¥à¸°à¸¥à¸‡à¸Šà¸·à¹ˆà¸­à¹€à¸‚้าใช้ Chrome à¸à¹ˆà¸­à¸™à¹ƒà¸Šà¹‰à¹à¸­à¸›à¸™à¸µà¹‰</translation>
<translation id="9148507642005240123">&amp;เลิà¸à¸—ำà¸à¸²à¸£à¹à¸à¹‰à¹„ข</translation>
<translation id="9157595877708044936">à¸à¸³à¸¥à¸±à¸‡à¸•à¸±à¹‰à¸‡à¸„่า...</translation>
<translation id="9170848237812810038">เ&amp;ลิà¸à¸—ำ</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">ล้างฟอร์ม</translation>
<translation id="939736085109172342">โฟลเดอร์ใหม่</translation>
<translation id="941721044073577244">ดูเหมือนว่าคุณจะไม่มีสิทธิ์เข้าชมเว็บไซต์นี้</translation>
-<translation id="962701380617707048">ป้อนวันหมดอายุà¹à¸¥à¸° CVC สำหรับ <ph name="CREDIT_CARD" /> เพื่ออัปเดตรายละเอียดของบัตร</translation>
<translation id="969892804517981540">รุ่นที่เป็นทางà¸à¸²à¸£</translation>
<translation id="988159990683914416">รุ่นนัà¸à¸žà¸±à¸’นา</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 9d1b38ff825..ec894a03e38 100644
--- a/chromium/components/strings/components_strings_tr.xtb
+++ b/chromium/components/strings/components_strings_tr.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Kablosuz aÄŸa yeniden baÄŸlanma</translation>
<translation id="1175364870820465910">Ya&amp;zdır...</translation>
<translation id="1181037720776840403">Kaldır</translation>
+<translation id="1184214524891303587">Olası güvenlik olaylarının ayrıntılarını Google'a <ph name="BEGIN_WHITEPAPER_LINK" />otomatik olarak bildir<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Sonraki</translation>
<translation id="1201895884277373915">Bu siteden daha çok</translation>
<translation id="1206967143813997005">Başlangıç anahtarlı imza hatalı</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Camgöbeği</translation>
<translation id="1629803312968146339">Chrome'un bu kartı kaydetmesini istiyor musunuz?</translation>
<translation id="1640180200866533862">Kullanıcı politikaları</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />Sitenin ana sayfasını ziyaret etmeyi deneyin<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Ağ yapılandırması geçersiz, dolayısıyla içe aktarılamadı.</translation>
<translation id="1644574205037202324">Geçmiş</translation>
<translation id="1645368109819982629">Desteklenmeyen protokol</translation>
<translation id="1676269943528358898"><ph name="SITE" /> normalde bilgilerinizi korumak için şifreleme kullanmaktadır. Google Chrome bu sefer <ph name="SITE" /> sitesine bağlanmayı denediğinde, web sitesi sıra dışı ve yanlış kimlik bilgileri döndürdü. Bir saldırgan <ph name="SITE" /> gibi davranmaya çalışıyor olabilir ya da bir Kablosuz oturum açma ekranı bağlantıyı kesmiştir. Google Chrome herhangi bir veri alışverişinden önce bağlantıyı durdurduğu için bilgileriniz hâlâ güvendedir.</translation>
+<translation id="168328519870909584">Şu anda <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sitesinde bulunan saldırganlar, cihazınıza bilgilerinizi (örneğin fotoğraflar, şifreler, mesajlar ve kredi kartları) çalacak veya silecek tehlikeli uygulamalar yüklemeyi deneyebilir.</translation>
<translation id="168841957122794586">Sunucu sertifikasında zayıf bir şifreleme anahtarı var.</translation>
-<translation id="1701955595840307032">Önerilen içeriği al</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Bu siteyi ziyaret etmek için <ph name="NAME" /> size izin vermelidir</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Sayfa numarası</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Windows Ağ Teşhislerini çalıştırmayı deneyin<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Lütfen senkronizasyon parolanızı güncelleyin.</translation>
+<translation id="1787142507584202372">Açık sekmeleriniz burada görünür</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google Güvenli Tarama, yakın bir zamanda <ph name="SITE" /> web 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. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Geçersiz istek veya istek parametreleri</translation>
+<translation id="1834321415901700177">Bu site zararlı programlar içeriyor</translation>
<translation id="1838667051080421715">Bir web sayfasının kaynak kodunu görüyorsunuz.</translation>
<translation id="1871208020102129563">Proxy, bir .pac komut dosyası URL'sini değil, sabit proxy sunucuları kullanacak şekilde ayarlanır.</translation>
<translation id="1883255238294161206">Listeyi daralt</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">Posta kodu</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 öneri}other{# öneri}}</translation>
<translation id="2065985942032347596">Kimlik Doğrulaması Gerekiyor</translation>
-<translation id="2079545284768500474">Geri Al</translation>
+<translation id="2079545284768500474">Geri al</translation>
<translation id="20817612488360358">Sistem proxy ayarları kullanılmak üzere ayarlandı, ancak açık bir proxy yapılandırması da belirtildi.</translation>
<translation id="2086652334978798447">Google tarafından önerilen kişiselleştirilmiş içeriği almak için Chrome'da oturum açın.</translation>
<translation id="2089090684895656482">Daha az</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Mobil Yer Ä°ÅŸaretleri</translation>
<translation id="2148716181193084225">Bugün</translation>
-<translation id="2149973817440762519">Yer İşaretini Düzenle</translation>
<translation id="2154054054215849342">Senkronizasyon alan adınızda kullanılamıyor</translation>
<translation id="2166049586286450108">Tam Yönetici Erişimi</translation>
<translation id="2166378884831602661">Bu site güvenli bağlantı sağlayamıyor</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836"><ph name="SITE" /> web sitesi HSTS kullandığı için bu web sitesini şu anda ziyaret edemezsiniz. Ağ hataları ve saldırılar genellikle geçici olduğundan, bu sayfa muhtemelen daha sonra çalışacaktır. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155"><ph name="ENTRY_INDEX" /> dizininde yok sayılan geçersiz yer işareti</translation>
<translation id="2354001756790975382">DiÄŸer yer iÅŸaretleri</translation>
+<translation id="2355395290879513365">Saldırganlar bu sitede baktığınız resimleri görebilir ve bu resimler üzerinde değişiklik yaparak sizi kandırabilirler.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Hiçbir değer ayarlanmamış politikaları göster</translation>
<translation id="2396249848217231973">Silmeyi &amp;geri al</translation>
<translation id="2455981314101692989">Bu web sayfası bu form için otomatik doldurmayı devre dışı bıraktı.</translation>
+<translation id="2460160116472764928">Google Güvenli Tarama, yakın bir zamanda <ph name="SITE" /> web 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. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Doldur</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Ağ Teşhislerini Çalıştırma<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">Geçersiz arama URL'si.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Bu sayfaları geçmişinizden silmek istediğinizden emin misiniz?</translation>
<translation id="2677748264148917807">Çık</translation>
<translation id="269990154133806163">Sunucu, Sertifika Şeffaflığı politikası kullanılarak herkese açık bir şekilde sağlanmayan bir sertifika sundu. Güvenilir olduklarından emin olmak ve saldırganlara karşı korunmak için bazı sertifikalarda bu zorunludur. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Okuma Listesi</translation>
<translation id="2704283930420550640">Değer, biçimle eşleşmiyor.</translation>
<translation id="2704951214193499422">Chromium, şu anda kartınızı onaylayamıyor. Lütfen daha sonra tekrar deneyin.</translation>
<translation id="2705137772291741111">Bu sitenin kaydedilen (önbelleğe alınan) kopyası okunamadı.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082"><ph name="DOMAIN" /> alan adına ulaşmayı denediniz, ancak sunucunun sağladığı sertifika, sertifikayı veren tarafından iptal edildi. Bu durum, sunucunun sağladığı güvenlik kimlik bilgilerine kesinlikle güvenilmemesi gerektiği anlamına gelir. Bir saldırganla irtibat kuruyor olabilirsiniz. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Ä°zin iste</translation>
<translation id="2713444072780614174">Beyaz</translation>
+<translation id="2720342946869265578">Etrafımda</translation>
<translation id="2721148159707890343">İstek başarılı oldu</translation>
<translation id="2728127805433021124">Sunucunun sertifikası, zayıf bir imza algoritması kullanılarak imzalanmış.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Bağlantı Teşhislerini Çalıştırma<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Ayarlar sayfasından, bir bağlantı için yapılandırılmış proxy'leri devre dışı bırakabilirsiniz.</translation>
<translation id="2955913368246107853">Bulma çubuğunu kapat</translation>
<translation id="2958431318199492670">Ağ yapılandırması ONC standardıyla uyumlu değil. Yapılandırmanın bazı bölümleri içe aktarılamaz.</translation>
+<translation id="29611076221683977">Şu anda <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sitesinde bulunan saldırganlar, Mac makinenize bilgilerinizi (örneğin fotoğraflar, şifreler, mesajlar ve kredi kartları) çalacak veya silecek tehlikeli programlar yüklemeyi deneyebilir.</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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Ayrıntıları gizle</translation>
<translation id="3587482841069643663">Tümü</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Sunucu TLS yeniden anlaşma uzantısını desteklemiyor.</translation>
<translation id="36224234498066874">Göz Atma Verilerini Temizle...</translation>
<translation id="362276910939193118">Tam Geçmişi Göster</translation>
<translation id="3623476034248543066">Değeri göster</translation>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Lisanslar bitti</translation>
<translation id="3714780639079136834">Mobil veriyi veya kablosuz bağlantıyı açma</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Proxy, güvenlik duvarı ve DNS yapılandırmasını kontrol etme<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Güvenliğinizle ilgili riskleri anlıyorsanız tehlikeli programlar kaldırılmadan önce <ph name="BEGIN_LINK" />güvenli olmayan bu siteyi ziyaret edebilirsiniz<ph name="END_LINK" />.</translation>
<translation id="3739623965217189342">Kopyaladığınız bağlantı</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="3788090790273268753">Bu siteye ilişkin sertifikanın süresi 2016'da doluyor ve sertifika zinciri, SHA-1 kullanılarak imzalanmış bir sertifika içeriyor.</translation>
<translation id="382518646247711829">Proxy sunucu kullanıyorsanız...</translation>
<translation id="3828924085048779000">BoÅŸ parolaya izin verilmez.</translation>
<translation id="3845539888601087042">Oturum açtığınız cihazlardan geçmiş bilgileri gösteriliyor. <ph name="BEGIN_LINK" />Daha fazla bilgi edinin<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">"<ph name="SUBKEY" />" anahtarı: <ph name="ERROR" /></translation>
<translation id="4075732493274867456">İstemci ve sunucu, ortak bir SSL protokolü sürümünü veya şifre setini desteklemiyor.</translation>
<translation id="4079302484614802869">Proxy yapılandırması sabit proxy sunucuları değil, bir .pac komut dosyası URL'sini kullanmak üzere ayarlandı.</translation>
+<translation id="4098354747657067197">Yanıltıcı bir siteye girmek üzeresiniz</translation>
<translation id="4103249731201008433">Cihazın seri numarası geçersiz</translation>
<translation id="4103763322291513355">Kara listeye alınmış URL'lerin ve sistem yöneticinizin zorunlu tuttuğu diğer politikaların listesini görmek için &lt;strong&gt;chrome://policy&lt;/strong&gt; adresini ziyaret edin.</translation>
<translation id="4110615724604346410">Bu sunucu, <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Güvenlik sertifikası hatalar içeriyor. Bu durum, yanlış yapılandırmadan veya bağlantınıza müdahale eden bir saldırgandan kaynaklanıyor olabilir. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{yok}=1{1 uygulama ($1)}=2{2 uygulama ($1, $2)}other{# uygulama ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Kilitlenmeler</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Ağ Teşhislerini çalıştırmayı deneyin<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Bu siteye bağlantınız tam olarak güvenli değil</translation>
<translation id="4250680216510889253">Hayır</translation>
<translation id="425582637250725228">Yaptığınız değişiklikler kaydedilmemiş olabilir.</translation>
<translation id="4258748452823770588">İmza yanlış</translation>
<translation id="4269787794583293679">(Kullanıcı adı yok)</translation>
+<translation id="4280429058323657511">, son kullanma tarihi <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google Güvenli Tarama, yakın bir zamanda <ph name="SITE" /> web sitesinde <ph name="BEGIN_LINK" />zararlı programlar buldu<ph name="END_LINK" />. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Ebeveyn önerileri</translation>
<translation id="4304224509867189079">GiriÅŸ Yap</translation>
<translation id="432290197980158659">Sunucu, yerleşik beklentilerle eşleşmeyen bir sertifika sundu. Bu beklentiler sizi korumak amacıyla bazı yüksek güvenlikli web sitelerinde bulunur. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Makale bulunamadı</translation>
+<translation id="4326324639298822553">Son kullanma tarihini kontrol edip tekrar deneyin</translation>
<translation id="4331708818696583467">Güvenli Değil</translation>
+<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="4381091992796011497">Kullanıcı Adı:</translation>
<translation id="4394049700291259645">Devre dışı bırak</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614"><ph name="DOMAIN" /> alan adına erişmeyi denediniz, ancak sunucu geçersiz bir sertifika sundu. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">DiÄŸer cihazlardan sekmeler</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Bir uzantı sayfası görüntülüyorsunuz.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Politikaları yeniden yükle</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4744603770635761495">Çalıştırılabilir Yol</translation>
+<translation id="4750917950439032686">Bilgileriniz (örneğin şifreler veya kredi kartı numaraları), bu siteye gönderilirken gizli olur.</translation>
<translation id="4756388243121344051">&amp;Geçmiş</translation>
+<translation id="4759118997339041434">Ödeme bilgilerini otomatik doldurma devre dışı</translation>
<translation id="4764776831041365478"><ph name="URL" /> adresindeki web sayfası, geçici olarak kullanılamıyor veya kalıcı olarak yeni bir web adresine taşınmış olabilir.</translation>
<translation id="4771973620359291008">Bilinmeyen bir hata oluÅŸtu.</translation>
<translation id="4800132727771399293">Son kullanma tarihinizi ve CVC'nizi kontrol edip tekrar deneyin</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Ağ hatası</translation>
<translation id="4816492930507672669">Sayfaya sığdır</translation>
<translation id="4850886885716139402">Görüntüle</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{ve 1 web sayfası daha}other{ve # web sayfası daha}}</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="495170559598752135">Ä°ÅŸlemler</translation>
<translation id="4958444002117714549">Listeyi geniÅŸlet</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">Ä°ndiriliyor</translation>
<translation id="5190835502935405962">Yer İşareti Çubuğu</translation>
<translation id="5199729219167945352">Deneyler</translation>
-<translation id="5199841536747119669">Öneriler burada görünür</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="5299298092464848405">Politika ayrıştırma hatası</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Kilitlenme bildirme devre dışı.</translation>
<translation id="5317780077021120954">Kaydet</translation>
<translation id="5327248766486351172">Ad</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sitesindeki saldırganlar, yazılım yükleme veya kişisel bilgilerinizi (örneğin, şifreler, telefon numaraları veya kredi kartları) ortaya çıkarma gibi tehlikeli şeyler yapmak için sizi kandırabilir.</translation>
<translation id="5359637492792381994">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Güvenlik sertifikası şu anda geçerli değil. Bu durum, yanlış yapılandırmadan veya bağlantınıza müdahale eden bir saldırgandan kaynaklanıyor olabilir. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Politika ayarları saklanamadı</translation>
+<translation id="5386426401304769735">Bu sitenin sertifika zinciri, SHA-1 kullanılarak imzalanmış bir sertifika içeriyor.</translation>
<translation id="5421136146218899937">Tarama verilerini temizle...</translation>
<translation id="5430298929874300616">Yer işaretini kaldır</translation>
<translation id="5431657950005405462">Dosyanız bulunamadı</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> web sitesindeki yerleşik bir sayfanın mesajı:</translation>
<translation id="5556459405103347317">Yeniden Yükle</translation>
<translation id="5565735124758917034">Etkin</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>
+<translation id="5580958916614886209">Son kullanma tarihinin ayını kontrol edip tekrar deneyin</translation>
<translation id="560412284261940334">Yönetim desteklenmiyor</translation>
<translation id="5610142619324316209">Bağlantınızı kontrol etme</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> sizi çok fazla kez yönlendirdi.</translation>
<translation id="5622887735448669177">Bu siteden ayrılmak istiyor musunuz?</translation>
<translation id="5629630648637658800">Politika ayarları yüklenemedi</translation>
<translation id="5631439013527180824">Geçersiz cihaz yönetimi jetonu</translation>
+<translation id="5669703222995421982">Kişiselleştirilmiş içerikler alma</translation>
+<translation id="5675650730144413517">Bu sayfa çalışmıyor</translation>
<translation id="5677928146339483299">Engellenenler</translation>
<translation id="5694783966845939798"><ph name="DOMAIN" /> alan adına erişme girişiminde bulundunuz, ancak sunucu zayıf bir imza algoritması kullanılarak imzalanmış bir sertifika sağladı. Bu durum, sunucunun sağladığı güvenlik bilgilerinin sahte olabileceği anlamına gelir ve sunucu sizin beklediğiniz sunucu olmayabilir (bir saldırgan ile irtibat kuruyor olabilirsiniz). <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Bu web sitesinin kimliği doğrulanmadı.</translation>
<translation id="5720705177508910913">Geçerli kullanıcı</translation>
-<translation id="572328651809341494">Son sekmeler</translation>
<translation id="5732392974455271431">Ebeveynleriniz engellemeyi kaldırabilir</translation>
<translation id="5784606427469807560">Kartınız onaylanırken bir sorun oluştu. İnternet bağlantınızı kontrol edip tekrar deneyin.</translation>
<translation id="5785756445106461925">Ayrıca, bu sayfa güvenli olmayan başka kaynaklar içeriyor. Bu kaynaklar, aktarım sırasında başkaları tarafından görülebilir ve bir saldırgan tarafından sayfanın görünüşünü değiştirmek üzere kullanılabilir.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569"><ph name="DOMAIN" /> alan adına ulaşmayı denediniz, ancak sunucu zayıf anahtar içeren bir sertifika sundu. Bir saldırgan özel anahtarı ele geçirmiş olabilir ve sunucu beklediğiniz sunucu olmayabilir (bir saldırganla irtibat kuruyor olabilirsiniz). <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Yeni Klasör</translation>
<translation id="5869405914158311789">Bu siteye ulaşılamıyor</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Google Payments, bu satıcı için bu kart türünü desteklememektedir. Lütfen farklı bir kart seçin.</translation>
<translation id="59174027418879706">Etkin</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="5966707198760109579">Hafta</translation>
<translation id="5967867314010545767">Geçmişten kaldır.</translation>
<translation id="5975083100439434680">Uzaklaştır</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Aşağıdakileri deneyin:</translation>
<translation id="6151417162996330722">Sunucu sertifikasının geçerlilik dönemi çok uzun.</translation>
<translation id="6165508094623778733">Daha fazla bilgi edinin</translation>
+<translation id="6177128806592000436">Bu siteye bağlantınız güvenli değil</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="6251924700383757765">Gizlilik politikası</translation>
+<translation id="625755898061068298">Bu site için güvenlik uyarılarını devre dışı bırakmayı seçtiniz.</translation>
<translation id="6259156558325130047">Sıralama Değişikliğini &amp;Yeniden Yap</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> yer iÅŸaretleri</translation>
<translation id="6264485186158353794">Güvenliğe geri dön</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394"><ph name="URL" /> adresine ulaşılamıyor.</translation>
<translation id="6321917430147971392">DNS ayarlarınızı kontrol edin</translation>
<translation id="6328639280570009161">Ağ tahmin özelliğini devre dışı bırakmayı deneyin</translation>
+<translation id="6328786501058569169">Bu site yanıltıcıdır</translation>
<translation id="6337534724793800597">Politikalara ada göre filtre uygula</translation>
<translation id="6342069812937806050">Az önce</translation>
<translation id="6345221851280129312">bilinmeyen boyut</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254"><ph name="SITE" /> web sitesi Chromium'un işleyemediği karışık kimlik bilgileri gönderdiğinden bu web sitesini şu anda ziyaret edemezsiniz. Ağ hataları ve saldırılar genellikle geçici türdendir, dolayısıyla bu sayfa muhtemelen daha sonra çalışacaktır. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Sertifikanın iptal edilip edilmediği kontrol edilemiyor.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Varsayılan arama politika tarafından devre dışı bırakıldığı için yoksayıldı.</translation>
<translation id="6462969404041126431">Bu sunucu, <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Güvenlik sertifikası iptal edilmiş olabilir. Bu durum, yanlış yapılandırmadan veya bağlantınıza müdahale eden bir saldırgandan kaynaklanıyor olabilir. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">Silmeyi &amp;Yeniden Yap</translation>
<translation id="6534179046333460208">Fiziksel Web önerileri</translation>
<translation id="6550675742724504774">Seçenekler</translation>
+<translation id="6556239504065605927">Güvenli bağlantı</translation>
<translation id="6563469144985748109">Yöneticiniz henüz onaylamadı</translation>
<translation id="6593753688552673085"><ph name="UPPER_ESTIMATE" />'tan az</translation>
<translation id="6596325263575161958">Şifreleme seçenekleri</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Bu politika uygun bulunmadı.</translation>
<translation id="6652240803263749613">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Bilgisayarınızın işletim sistemi, sunucunun güvenlik sertifikasına güvenmiyor. Bu durum, yanlış yapılandırmadan veya bağlantınıza müdahale eden bir saldırgandan kaynaklanıyor olabilir. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Klasörü Düzenleyin</translation>
-<translation id="6660210980321319655"><ph name="CRASH_TIME" /> tarihinde otomatik olarak rapor edildi</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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Politika deÄŸeri</translation>
<translation id="6757797048963528358">Cihazınız uyku moduna geçti.</translation>
<translation id="6778737459546443941">Ebeveyniniz henüz onaylamadı</translation>
+<translation id="6810899417690483278">Özelleştirme Kimliği</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159"><ph name="URL" /> adresindeki web sayfası şu anda kullanılamıyor. Aşırı yüklenmiş veya bakım için kapatılmış olabilir.</translation>
<translation id="6831043979455480757">Çevir</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Ä°letiÅŸim bilgileri</translation>
+<translation id="7079718277001814089">Bu site kötü amaçlı yazılım içeriyor</translation>
<translation id="7087282848513945231">İlçe</translation>
<translation id="7088615885725309056">Daha eski</translation>
<translation id="7090678807593890770">Google'da <ph name="LINK" /> araması yapın</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> güvenlik standartlarına uymuyor.</translation>
<translation id="721197778055552897">Bu sorun hakkında <ph name="BEGIN_LINK" />daha fazla<ph name="END_LINK" /> bilgi edinin.</translation>
<translation id="7219179957768738017">Bağlantı <ph name="SSL_VERSION" /> kullanıyor.</translation>
+<translation id="724691107663265825">Gideceğiniz site kötü amaçlı yazılım içeriyor</translation>
<translation id="724975217298816891">Kart ayrıntılarınızı güncellemek için <ph name="CREDIT_CARD" /> numaralı karta ilişkin son kullanma tarihini ve CVC kodunu girin. Onayladığınızda kart ayrıntılarınız bu siteyle paylaşılacaktır.</translation>
<translation id="725866823122871198">Bilgisayarınızın tarih ve saati (<ph name="DATE_AND_TIME" />) yanlış olduğundan <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> alan adına gizli bir bağlantı kurulamıyor.</translation>
<translation id="7269802741830436641">Bu web sayfasında yönlendirme döngüsü var</translation>
@@ -611,6 +648,7 @@ Hatırlatma! Bir dahaki sefere Gizli mod <ph name="SHORTCUT_KEY" /> kullanışlÄ
<translation id="7658239707568436148">Ä°ptal</translation>
<translation id="7667346355482952095">Döndürülen politika jetonu boş veya mevcut jetonla eşleşmiyor</translation>
<translation id="7668654391829183341">Bilinmeyen cihaz</translation>
+<translation id="7669271284792375604">Bu sitedeki saldırganlar web'e göz atma deneyiminize zarar veren programlar yüklemeniz için sizi kandırmayı (örneğin ana sayfanızı değiştirerek ya da ziyaret ettiğiniz sitelerde ek reklamlar görüntüleyerek) deneyebilir.</translation>
<translation id="7674629440242451245">Chrome'daki etkileyici, yeni özellikler ilginizi çekiyor mu? chrome.com/dev adresinden geliştirici kanalımızı deneyin.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" /><ph name="SITE" /> sitesine ilerle (güvenli değil)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Bu site önbellekten yüklenemiyor</translation>
@@ -626,14 +664,17 @@ Hatırlatma! Bir dahaki sefere Gizli mod <ph name="SHORTCUT_KEY" /> kullanışlÄ
<translation id="7800304661137206267">Bağlantı, ileti kimlik doğrulaması için <ph name="MAC" /> ve anahtar değişim mekanizması olarak <ph name="KX" /> kullanılan <ph name="CIPHER" /> ile şifrelenmiştir.</translation>
<translation id="780301667611848630">Hayır, teşekkürler</translation>
<translation id="7805768142964895445">Durum</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Form önerisi Chrome'dan kaldırılsın mı?</translation>
<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="7887683347370398519">CVC'nizi kontrol edin ve tekrar deneyin</translation>
<translation id="7912024687060120840">Bulunduğu Klasör:</translation>
<translation id="7935318582918952113">DOM Ayrıştırıcı</translation>
<translation id="7938958445268990899">Sunucunun sertifikası henüz geçerli değil.</translation>
<translation id="7942349550061667556">Kırmızı</translation>
+<translation id="7947285636476623132">Son kullanma tarihinin yılını kontrol edip tekrar deneyin</translation>
<translation id="7951415247503192394">(32 bit)</translation>
<translation id="7956713633345437162">Mobil yer iÅŸaretleri</translation>
<translation id="7961015016161918242">Hiçbir Zaman</translation>
@@ -641,7 +682,10 @@ Hatırlatma! Bir dahaki sefere Gizli mod <ph name="SHORTCUT_KEY" /> kullanışlÄ
<translation id="7983301409776629893"><ph name="ORIGINAL_LANGUAGE" /> dilini her zaman <ph name="TARGET_LANGUAGE" /> diline çevir</translation>
<translation id="7995512525968007366">Belirtilmedi</translation>
<translation id="8012647001091218357">Şu anda ebeveynlerinize erişemedik. Lütfen tekrar deneyin.</translation>
+<translation id="8025119109950072390">Bu sitedeki saldırganlar sizi kandırarak yazılım yükleme veya kişisel bilgilerinizi (örneğin şifreler, telefon numaraları veya kredi kartları) ifşa etme gibi tehlikeli şeyler yaptırabilir.</translation>
+<translation id="803030522067524905">Google Güvenli Tarama yakın bir zamanda <ph name="SITE" /> web sitesinde kimlik avı yapıldığını tespit etti. Kimlik avı siteleri, sizi aldatmak için başka web siteleri gibi görünür. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Bu sayfa <ph name="SOURCE_LANGUAGE" /> dilinde. <ph name="TARGET_LANGUAGE" /> diline çevrilsin mi?</translation>
+<translation id="8041089156583427627">Görüş bildirin</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>
@@ -652,9 +696,11 @@ Hatırlatma! Bir dahaki sefere Gizli mod <ph name="SHORTCUT_KEY" /> kullanışlÄ
<translation id="8150722005171944719"><ph name="URL" /> konumundaki dosya okunamıyor. Kaldırılmış ya da taşınmış olabilir veya dosya izinleri erişimi önlüyordur.</translation>
<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="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="8230421197304563332">Şu anda <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sitesinde bulunan saldırganlar, bilgilerinizi (örneğin fotoğraflar, şifreler, mesajlar ve kredi kartları) çalacak veya silecek tehlikeli programları bilgisayarınıza yüklemeyi deneyebilirler.</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="8249320324621329438">Son getirilen:</translation>
<translation id="8261506727792406068">Sil</translation>
@@ -663,11 +709,13 @@ Hatırlatma! Bir dahaki sefere Gizli mod <ph name="SHORTCUT_KEY" /> kullanışlÄ
<translation id="8294431847097064396">Kaynak</translation>
<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="8349305172487531364">Yer işaretleri çubuğu</translation>
<translation id="8363502534493474904">Uçak modunu kapatma</translation>
<translation id="8364627913115013041">Henüz ayarlanmadı.</translation>
<translation id="8380941800586852976">Tehlikeli</translation>
<translation id="8382348898565613901">Son ziyaret ettiğiniz yer işaretleri burada görünür</translation>
+<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="8428213095426709021">Ayarlar</translation>
@@ -679,9 +727,9 @@ Hatırlatma! Bir dahaki sefere Gizli mod <ph name="SHORTCUT_KEY" /> kullanışlÄ
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ana makinesinin yanıt vermesi çok uzun sürdü.</translation>
<translation id="852346902619691059">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, yanlış yapılandırmadan veya bağlantınıza müdahale eden bir saldırgandan kaynaklanıyor olabilir. <ph name="BEGIN_LEARN_MORE_LINK" />Daha fazla bilgi edinin<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Google Payments, bu kart türünü desteklememektedir. Lütfen farklı bir kart seçin.</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="8553075262323480129">Sayfanın dili belirlenemediğinden çeviri başarısız oldu.</translation>
<translation id="8559762987265718583">Cihazınızın tarih ve saati (<ph name="DATE_AND_TIME" />) yanlış olduğundan <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> alan adına gizli bir bağlantı kurulamıyor.</translation>
-<translation id="856992080682148">Bu siteye ilişkin sertifikanın süresi 2017'de veya daha sonra doluyor ve sertifika zinciri, SHA-1 kullanılarak imzalanmış bir sertifika içeriyor.</translation>
<translation id="8571890674111243710">Sayfa <ph name="LANGUAGE" /> diline çevriliyor...</translation>
<translation id="859285277496340001">Sertifika, iptal edilip edilmediÄŸinin denetlenebileceÄŸi bir mekanizma belirtmiyor.</translation>
<translation id="8620436878122366504">Ebeveynleriniz henüz onaylamadı</translation>
@@ -694,10 +742,12 @@ Hatırlatma! Bir dahaki sefere Gizli mod <ph name="SHORTCUT_KEY" /> kullanışlÄ
<translation id="8740359287975076522"><ph name="HOST_NAME" /> ana makinesinin &lt;abbr id="dnsDefinition"&gt;DNS adresi&lt;/abbr&gt; bulunamadı. Sorun teşhis ediliyor.</translation>
<translation id="8790007591277257123">Silmeyi &amp;yeniden yap</translation>
<translation id="8798099450830957504">Varsayılan</translation>
+<translation id="8800988563907321413">Yakın çevrenizle ilgili öneriler burada görünür</translation>
<translation id="8804164990146287819">Gizlilik Politikası</translation>
<translation id="8820817407110198400">Favoriler</translation>
<translation id="8834246243508017242">Kişiler'i kullanarak Otomatik Doldur'u etkinleştir…</translation>
<translation id="883848425547221593">DiÄŸer Yer Ä°ÅŸaretleri</translation>
+<translation id="884264119367021077">Gönderim adresi</translation>
<translation id="884923133447025588">İptal mekanizması bulunamadı.</translation>
<translation id="885730110891505394">Google ile PaylaÅŸma</translation>
<translation id="8866481888320382733">Politika ayarlarını ayrıştırma hatası</translation>
@@ -715,18 +765,21 @@ Hatırlatma! Bir dahaki sefere Gizli mod <ph name="SHORTCUT_KEY" /> kullanışlÄ
<translation id="8971063699422889582">Sunucu sertifikasının süresi doldu.</translation>
<translation id="8987927404178983737">Ay</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Girmekte olduğunuz site zararlı programlar içermektedir</translation>
<translation id="9001074447101275817"><ph name="DOMAIN" /> proxy'si için kullanıcı adı ve şifre gerekiyor.</translation>
<translation id="901974403500617787">Tüm sistem için geçerli olan işaretler sadece sahibi <ph name="OWNER_EMAIL" /> tarafından ayarlanabilir.</translation>
<translation id="9020542370529661692">Bu sayfa <ph name="TARGET_LANGUAGE" /> diline çevrildi</translation>
+<translation id="9035022520814077154">Güvenlik hatası</translation>
<translation id="9038649477754266430">Sayfaları daha hızlı yüklemek için bir tahmin hizmeti kullan</translation>
<translation id="9039213469156557790">Ayrıca, bu sayfa güvenli olmayan başka kaynaklar içeriyor. Bu kaynaklar, aktarım sırasında başkaları tarafından görülebilir ve bir saldırgan tarafından sayfanın davranışını değiştirmek üzere kullanılabilir.</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> sitesindeki saldırganlar web'e göz atma deneyiminize zarar veren programları yüklemeniz için sizi kandırmayı (örneğin ana sayfanızı değiştirerek ya da ziyaret ettiğiniz sitelerde ek reklamlar görüntüleyerek) deneyebilirler.</translation>
<translation id="9050666287014529139">Parola</translation>
<translation id="9065203028668620118">Düzenle</translation>
<translation id="9068849894565669697">Renk seçin</translation>
<translation id="9076283476770535406">Yetişkin içeriği bulunabilir</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> sayfası çalışmıyor</translation>
<translation id="9103872766612412690"><ph name="SITE" /> normalde bilgilerinizi korumak için şifreleme kullanmaktadır. Chromium bu sefer <ph name="SITE" /> sitesine bağlanmayı denediğinde, web sitesi sıra dışı ve yanlış kimlik bilgileri döndürdü. Bir saldırgan <ph name="SITE" /> gibi davranmaya çalışıyor olabilir ya da bir Kablosuz oturum açma ekranı bağlantıyı kesmiştir. Chromium herhangi bir veri alışverişinden önce bağlantıyı durdurduğu için bilgileriniz hâlâ güvendedir.</translation>
<translation id="9137013805542155359">Orijinali göster</translation>
+<translation id="9137248913990643158">Lütfen bu uygulamayı kullanmadan önce Chrome'u başlatıp oturum açın.</translation>
<translation id="9148507642005240123">Düzenlemeyi &amp;geri al</translation>
<translation id="9157595877708044936">Ayarlanıyor...</translation>
<translation id="9170848237812810038">&amp;Geri al</translation>
@@ -739,7 +792,6 @@ Hatırlatma! Bir dahaki sefere Gizli mod <ph name="SHORTCUT_KEY" /> kullanışlÄ
<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="962701380617707048">Kart ayrıntılarınızı güncellemek için <ph name="CREDIT_CARD" /> numaralı karta ilişkin son kullanma tarihini ve CVC kodunu girin</translation>
<translation id="969892804517981540">Resmi Derleme</translation>
<translation id="988159990683914416">GeliÅŸtirici Derlemesi</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 cd4a1051afc..b168f1714f9 100644
--- a/chromium/components/strings/components_strings_uk.xtb
+++ b/chromium/components/strings/components_strings_uk.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">знову під’єднати приÑтрій до мережі Wi-Fi</translation>
<translation id="1175364870820465910">&amp;Друк...</translation>
<translation id="1181037720776840403">Видалити</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Ðвтоматично надÑилати<ph name="END_WHITEPAPER_LINK" /> в Google інформацію про можливі Ð¿Ð¾Ñ€ÑƒÑˆÐµÐ½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Далі</translation>
<translation id="1201895884277373915">Більше з цього Ñайту</translation>
<translation id="1206967143813997005">ÐедійÑний підпиÑ</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Бірюзовий</translation>
<translation id="1629803312968146339">Зберегти цю картку в Chrome?</translation>
<translation id="1640180200866533862">Правила кориÑтувача</translation>
+<translation id="1640244768702815859"><ph name="BEGIN_LINK" />Відвідайте домашню Ñторінку цього Ñайту<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð¼ÐµÑ€ÐµÐ¶Ñ– недійÑна та не може імпортуватиÑÑ.</translation>
<translation id="1644574205037202324">ІÑторіÑ</translation>
<translation id="1645368109819982629">Протокол не підтримуєтьÑÑ</translation>
<translation id="1676269943528358898">Веб-Ñайт <ph name="SITE" /> зазвичай викориÑтовує ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð·Ð°Ñ…Ð¸Ñту вашої інформації. Під Ñ‡Ð°Ñ Ñ†Ñ–Ñ”Ñ— Ñпроби Chrome під’єднатиÑÑ Ð´Ð¾ Ñторінки <ph name="SITE" /> з неї отримано незвичні й неправильні облікові дані. Це може ÑтатиÑÑ, коли зловмиÑник намагаєтьÑÑ Ð²Ð¸Ð´Ð°Ð²Ð°Ñ‚Ð¸ Ñебе за веб-Ñайт <ph name="SITE" /> або Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð°Ð½Ð¾ екраном входу Wi-Fi. Ваша Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð·Ð°Ð»Ð¸ÑˆÐ°Ñ”Ñ‚ÑŒÑÑ Ð·Ð°Ñ…Ð¸Ñ‰ÐµÐ½Ð¾ÑŽ, оÑкільки Chrome припинив Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð´Ð¾ того, Ñк почавÑÑ Ð¾Ð±Ð¼Ñ–Ð½ будь-Ñкими даними.</translation>
+<translation id="168328519870909584">ЗловмиÑники, Ñкі зараз перебувають на Ñайті <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, можуть намагатиÑÑ Ð²Ñтановити на ваш приÑтрій небезпечні додатки, що викрадають або видалÑÑŽÑ‚ÑŒ інформацію (Ñк-от фотографії, паролі, Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‡Ð¸ дані кредитних карток).</translation>
<translation id="168841957122794586">Сертифікат Ñервера міÑтить Ñлабкий криптографічний ключ.</translation>
-<translation id="1701955595840307032">Отримувати пропонований вміÑÑ‚</translation>
<translation id="1710259589646384581">ОС</translation>
<translation id="1721312023322545264">Вам потрібен дозвіл адмініÑтратора <ph name="NAME" />, щоб перейти на цей Ñайт</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Ðомер Ñторінки</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Проведіть діагноÑтику мережі Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Оновіть парольну фразу Ð´Ð»Ñ Ñинхронізації.</translation>
+<translation id="1787142507584202372">Тут відображатимутьÑÑ Ð²Ð°ÑˆÑ– відкриті вкладки</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Ð¤ÑƒÐ½ÐºÑ†Ñ–Ñ Ð±ÐµÐ·Ð¿ÐµÑ‡Ð½Ð¾Ð³Ð¾ переглÑду від Google нещодавно <ph name="BEGIN_LINK" />виÑвила зловмиÑне програмне забезпеченнÑ<ph name="END_LINK" /> на Ñайті <ph name="SITE" />. Іноді зловмиÑне програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð·Ð°Ñ€Ð°Ð¶Ð°Ñ” зазвичай безпечні веб-Ñайти. Шкідливий вміÑÑ‚ походить із хоÑту <ph name="SUBRESOURCE_HOST" /> – відомого розповÑюджувача зловмиÑного програмного забезпеченнÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">ÐедійÑний запит або параметри запиту</translation>
+<translation id="1834321415901700177">Цей Ñайт міÑтить шкідливі програми</translation>
<translation id="1838667051080421715">Ви бачите джерело веб-Ñторінки.</translation>
<translation id="1871208020102129563">ПрокÑÑ–-Ñервер налаштовано на викориÑÑ‚Ð°Ð½Ð½Ñ Ñ„Ñ–ÐºÑованих прокÑÑ–-Ñерверів, а не URL-адреÑи Ñценарію .pac.</translation>
<translation id="1883255238294161206">Згорнути ÑпиÑок</translation>
@@ -96,7 +101,7 @@
<translation id="2053553514270667976">Поштовий індекÑ</translation>
<translation id="2064691555167957331">{COUNT,plural, =1{1 пропозиціÑ}one{# пропозиціÑ}few{# пропозиції}many{# пропозицій}other{# пропозиції}}</translation>
<translation id="2065985942032347596">Потрібна автентифікаціÑ</translation>
-<translation id="2079545284768500474">СкаÑувати</translation>
+<translation id="2079545284768500474">Відмінити</translation>
<translation id="20817612488360358">СиÑтемні параметри прокÑÑ–-Ñервера налаштовано Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑтаннÑ, але чітко вказано Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾ÐºÑÑ–-Ñервера.</translation>
<translation id="2086652334978798447">Щоб отримувати перÑоналізовані пропозиції від Google, увійдіть в обліковий Ð·Ð°Ð¿Ð¸Ñ Chrome.</translation>
<translation id="2089090684895656482">Менше</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Закладки Ð´Ð»Ñ Ð¼Ð¾Ð±Ñ–Ð»ÑŒÐ½Ð¸Ñ… приÑтроїв</translation>
<translation id="2148716181193084225">Сьогодні</translation>
-<translation id="2149973817440762519">Редагувати закладку</translation>
<translation id="2154054054215849342">Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð½ÐµÐ´Ð¾Ñтупна Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ домену</translation>
<translation id="2166049586286450108">Повний адмініÑтративний доÑтуп</translation>
<translation id="2166378884831602661">Цей Ñайт не може забезпечити захищене з’єднаннÑ</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Зараз не можна перейти на Ñторінку <ph name="SITE" />, оÑкільки веб-Ñайт викориÑтовує протокол HSTS. Помилки мережі й атаки зазвичай тимчаÑові, тому Ñ†Ñ Ñторінка, Ñкоріш за вÑе, запрацює пізніше. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">ÐедійÑна закладка в індекÑÑ– <ph name="ENTRY_INDEX" /> ігноруєтьÑÑ</translation>
<translation id="2354001756790975382">Інші закладки</translation>
+<translation id="2355395290879513365">ЗловмиÑники можуть бачити зображеннÑ, Ñкі ви переглÑдаєте на цьому Ñайті, Ñ– змінювати Ñ—Ñ… із метою ошукати ваÑ.</translation>
<translation id="2359808026110333948">Продовжити</translation>
<translation id="2365563543831475020">Звіт про аварійне Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ о <ph name="CRASH_TIME" /> не завантажено</translation>
<translation id="2367567093518048410">Рівень</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Показувати правила, Ð´Ð»Ñ Ñких не вÑтановлено значеннÑ</translation>
<translation id="2396249848217231973">&amp;Відмінити видаленнÑ</translation>
<translation id="2455981314101692989">Ðа цій веб-Ñторінці вимкнено автоматичне Ð·Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— форми.</translation>
+<translation id="2460160116472764928">Ð¤ÑƒÐ½ÐºÑ†Ñ–Ñ Ð±ÐµÐ·Ð¿ÐµÑ‡Ð½Ð¾Ð³Ð¾ переглÑду від Google нещодавно <ph name="BEGIN_LINK" />виÑвила зловмиÑне програмне забезпеченнÑ<ph name="END_LINK" /> на Ñайті <ph name="SITE" />. Іноді зловмиÑне програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð·Ð°Ñ€Ð°Ð¶Ð°Ñ” зазвичай безпечні веб-Ñайти. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Заповнити</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />провеÑти діагноÑтику мережі<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">ÐедійÑна URL-адреÑа Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Ви дійÑно бажаєте видалити ці Ñторінки зі Ñвоєї Ñ–Ñторії?</translation>
<translation id="2677748264148917807">Вийти</translation>
<translation id="269990154133806163">Сервер надав Ñертифікат без інформації про перевірку. Це обов’Ñзкові дані Ð´Ð»Ñ Ð´ÐµÑких Ñертифікатів. Вони підтверджують надійніÑÑ‚ÑŒ Ñертифіката й захищають від атак зловмиÑників. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">СпиÑок читаннÑ</translation>
<translation id="2704283930420550640">Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð½Ðµ відповідає формату.</translation>
<translation id="2704951214193499422">Chromium не вдалоÑÑ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚Ð¸ дані вашої картки. Спробуйте пізніше.</translation>
<translation id="2705137772291741111">Ðе вдаєтьÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ збережену (кешовану) копію цього Ñайту.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Ви намагалиÑÑ Ð·Ð²â€™ÑзатиÑÑ Ð· доменом <ph name="DOMAIN" />, але Ñервер надав Ñертифікат, відкликаний його видавцем. Це означає, що обліковим даним безпеки, наданим Ñервером, не можна довірÑти. Ви можете передавати Ñвої дані зловмиÑнику. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Запитувати дозвіл</translation>
<translation id="2713444072780614174">Білий</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">УÑÑ– прокÑÑ–-Ñервери, налаштовані Ð´Ð»Ñ Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ, можна вимкнути на Ñторінці налаштувань.</translation>
<translation id="2955913368246107853">Закрити панель пошуку</translation>
<translation id="2958431318199492670">ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð¼ÐµÑ€ÐµÐ¶Ñ– не відповідає Ñтандарту ONC. Вона може імпортуватиÑÑ Ñ‡Ð°Ñтково.</translation>
+<translation id="29611076221683977">ЗловмиÑники, Ñкі зараз перебувають на Ñайті <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, можуть намагатиÑÑ Ð²Ñтановити на ваш приÑтрій з ОС Mac небезпечні програми, що викрадають або видалÑÑŽÑ‚ÑŒ інформацію (Ñк-от фотографії, паролі, Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‡Ð¸ кредитні картки).</translation>
<translation id="2969319727213777354">Щоб уÑтановити безпечне з’єднаннÑ, потрібно правильно налаштувати чаÑ, оÑкільки Ñертифікати, Ñкі підтверджують ÑправжніÑÑ‚ÑŒ веб-Ñайтів, дійÑні лише протÑгом певного періоду. Ðа вашому приÑтрої неправильно налаштовано чаÑ, тому Chrome не може перевірити Ñертифікати.</translation>
<translation id="2972581237482394796">&amp;Повторити</translation>
<translation id="2985306909656435243">Якщо цю функцію ввімкнено, Chromium зберігає копію даних вашої картки на приÑтрої, щоб ви могли швидше заповнювати форми.</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Сховати докладні дані</translation>
<translation id="3587482841069643663">Ð’Ñе</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Сервер не підтримує Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð½Ð¾Ð³Ð¾ ÑƒÐ·Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð»Ñƒ TLS.</translation>
<translation id="36224234498066874">ОчиÑтити дані веб-переглÑду...</translation>
<translation id="362276910939193118">Показати повну Ñ–Ñторію</translation>
<translation id="3623476034248543066">Показати значеннÑ</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">Якщо ви бачите це чаÑто, Ñпробуйте <ph name="HELP_LINK" />.</translation>
<translation id="3658742229777143148">РедакціÑ</translation>
<translation id="3678029195006412963">Ðе вдалоÑÑ Ð¿Ñ–Ð´Ð¿Ð¸Ñати запит</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Ліцензії вичерпано</translation>
<translation id="3714780639079136834">увімкнути мобільний Інтернет або Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />перевірити конфігурацію прокÑÑ–-Ñервера, брандмауера та DNS-Ñервера<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Якщо ви розумієте ризики, пов’Ñзані з безпекою, <ph name="BEGIN_LINK" />перейдіть на цей ненадійний Ñайт<ph name="END_LINK" /> до того, Ñк небезпечні програми буде видалено.</translation>
<translation id="3739623965217189342">Скопійоване поÑиланнÑ</translation>
<translation id="375403751935624634">Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ переклад через помилку Ñервера.</translation>
<translation id="3759461132968374835">У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” оÑтанніх повідомлень про аварійне Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸. Тут не відображатимутьÑÑ Ð²Ð¸Ð¿Ð°Ð´ÐºÐ¸ аварійного Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸, Ñкі ÑталиÑÑ, коли Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ аварійне Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ було вимкнено.</translation>
-<translation id="3788090790273268753">Термін дії Ñертифіката Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñайту закінчуєтьÑÑ 2016 року. Ланцюжок Ñертифіката міÑтить Ñертифікат, підпиÑаний за допомогою SHA-1.</translation>
<translation id="382518646247711829">Якщо ви викориÑтовуєте прокÑÑ–-Ñервер…</translation>
<translation id="3828924085048779000">ÐŸÐ¾Ñ€Ð¾Ð¶Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»ÑŒÐ½Ð° фраза заборонена.</translation>
<translation id="3845539888601087042">Показано Ñ–Ñторію з приÑтроїв, на Ñких ви ввійшли в обліковий запиÑ. <ph name="BEGIN_LINK" />Докладніше<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Ключ "<ph name="SUBKEY" />": <ph name="ERROR" /></translation>
<translation id="4075732493274867456">Клієнт Ñ– Ñервер підтримують різні верÑÑ–Ñ— протоколу SSL або набору шифрів.</translation>
<translation id="4079302484614802869">Конфігурацію прокÑÑ–-Ñервера налаштовано на викориÑÑ‚Ð°Ð½Ð½Ñ URL-адреÑи Ñценарію .pac, а не фікÑованих прокÑÑ–-Ñерверів.</translation>
+<translation id="4098354747657067197">Оманливий Ñайт</translation>
<translation id="4103249731201008433">ÐедійÑний Ñерійний номер приÑтрою</translation>
<translation id="4103763322291513355">Перейдіть на Ñторінку &lt;strong&gt;chrome://policy&lt;/strong&gt;, щоб переглÑнути ÑпиÑок URL-Ð°Ð´Ñ€ÐµÑ Ñ–Ð· "чорного" ÑпиÑку й інші правила, що примуÑово заÑтоÑовуєтьÑÑ ÑиÑтемним адмініÑтратором.</translation>
<translation id="4110615724604346410">Ðе вдалоÑÑ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚Ð¸, що це Ñервер <ph name="DOMAIN" />. Його Ñертифікат безпеки міÑтить помилки. Імовірні причини: Ñервер налаштовано неправильно або хтоÑÑŒ намагаєтьÑÑ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð¿Ð¸Ñ‚Ð¸ ваше з’єднаннÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{немає}=1{1 додаток ($1)}=2{2 додатки ($1, $2)}one{# додаток ($1, $2, $3)}few{# додатки ($1, $2, $3)}many{# додатків ($1, $2, $3)}other{# додатка ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Збої в роботі</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Проведіть діагноÑтику мережі<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Ваше Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· цим Ñайтом не повніÑÑ‚ÑŽ захищене</translation>
<translation id="4250680216510889253">ні</translation>
<translation id="425582637250725228">ВнеÑені зміни, можливо, не буде збережено.</translation>
<translation id="4258748452823770588">ÐедійÑний підпиÑ</translation>
<translation id="4269787794583293679">(Ðемає імені кориÑтувача)</translation>
+<translation id="4280429058323657511">, дійÑна до <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Ð¤ÑƒÐ½ÐºÑ†Ñ–Ñ Ð±ÐµÐ·Ð¿ÐµÑ‡Ð½Ð¾Ð³Ð¾ переглÑду від Google нещодавно <ph name="BEGIN_LINK" />виÑвила шкідливі програми<ph name="END_LINK" /> на Ñайті <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Поради Ð´Ð»Ñ Ð±Ð°Ñ‚ÑŒÐºÑ–Ð²</translation>
<translation id="4304224509867189079">Вхід</translation>
<translation id="432290197980158659">Сервер надав Ñертифікат, Ñкий не відповідає очікуваним вбудованим параметрам. Ці очікувані параметри включено Ð´Ð»Ñ Ð¿ÐµÐ²Ð½Ð¸Ñ… веб-Ñайтів із виÑоким рівнем безпеки, щоб захиÑтити ваÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Статтю не знайдено</translation>
+<translation id="4326324639298822553">Перевірте дату Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ñ‚ÐµÑ€Ð¼Ñ–Ð½Ñƒ дії та повторіть Ñпробу</translation>
<translation id="4331708818696583467">Ðенадійне</translation>
+<translation id="4356973930735388585">ЗловмиÑники на цьому Ñайті можуть намагатиÑÑ Ð²Ñтановити на ваш комп’ютер небезпечні програми, що викрадають або видалÑÑŽÑ‚ÑŒ інформацію (наприклад, фотографії, паролі, Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¹ дані кредитних карток).</translation>
<translation id="4372948949327679948">Очікуване значеннÑ: <ph name="VALUE_TYPE" />.</translation>
<translation id="4381091992796011497">Ð†Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача:</translation>
<translation id="4394049700291259645">Вимкнути</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Ви намагалиÑÑ Ð·Ð²â€™ÑзатиÑÑ Ð· доменом <ph name="DOMAIN" />, але Ñервер надав недійÑний Ñертифікат. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4592951414987517459">Ð—â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· доменом <ph name="DOMAIN" /> шифруєтьÑÑ Ð·Ð° допомогою ÑучаÑного набору шифрів.</translation>
<translation id="4594403342090139922">&amp;Відмінити видаленнÑ</translation>
+<translation id="4619615317237390068">Вкладки з інших приÑтроїв</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Ви переглÑдаєте Ñторінку розширень.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Перезавантажити правила</translation>
<translation id="4728558894243024398">Платформа</translation>
<translation id="4744603770635761495">Виконуваний шлÑÑ…</translation>
+<translation id="4750917950439032686">Ваша Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ (Ñк-от паролі та номери кредитних карток) залишаєтьÑÑ ÐºÐ¾Ð½Ñ„Ñ–Ð´ÐµÐ½Ñ†Ñ–Ð¹Ð½Ð¾ÑŽ, коли надÑилаєтьÑÑ Ð½Ð° цей Ñайт.</translation>
<translation id="4756388243121344051">&amp;ІÑторіÑ</translation>
+<translation id="4759118997339041434">ÐÐ²Ñ‚Ð¾Ð·Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ð¿Ð»Ð°Ñ‚Ñ–Ð¶Ð½Ð¸Ñ… даних вимкнено</translation>
<translation id="4764776831041365478">Веб-Ñторінка за адреÑою <ph name="URL" /> може бути тимчаÑово недоÑтупною або Ñ—Ñ— назавжди переміщено на нову веб-адреÑу.</translation>
<translation id="4771973620359291008">Виникла невідома помилка.</translation>
<translation id="4800132727771399293">Перевірте дату Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ñ‚ÐµÑ€Ð¼Ñ–Ð½Ñƒ дії та код CVC та повторіть Ñпробу</translation>
+<translation id="4803924862070940586"><ph name="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="4813512666221746211">Помилка мережі</translation>
<translation id="4816492930507672669">За розміром Ñторінки</translation>
<translation id="4850886885716139402">ПереглÑд</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{Ñ– ще 1 веб-Ñторінка}one{Ñ– ще # веб-Ñторінка}few{Ñ– ще # веб-Ñторінки}many{Ñ– ще # веб-Ñторінок}other{Ñ– ще # веб-Ñторінки}}</translation>
<translation id="4923417429809017348">Цю Ñторінку перекладено з невідомої мови оригіналу такою мовою: <ph name="LANGUAGE_LANGUAGE" /></translation>
+<translation id="4923459931733593730">Оплата</translation>
<translation id="4926049483395192435">Потрібно вказати.</translation>
<translation id="495170559598752135">Дії</translation>
<translation id="4958444002117714549">Розгорнути ÑпиÑок</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">ЗавантаженнÑ</translation>
<translation id="5190835502935405962">Панель закладок</translation>
<translation id="5199729219167945352">ЕкÑперименти</translation>
-<translation id="5199841536747119669">Тут відображатимутьÑÑ Ð¿Ñ€Ð¾Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ñ—</translation>
<translation id="5251803541071282808">Хмара</translation>
<translation id="5277279256032773186">КориÑтуєтеÑÑ Chrome на роботі? Компанії можуть налаштовувати Chrome Ð´Ð»Ñ Ñвоїх працівників. Докладніше</translation>
<translation id="5299298092464848405">Помилка аналізу правила</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ аварійне Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ вимкнено.</translation>
<translation id="5317780077021120954">Зберегти</translation>
<translation id="5327248766486351172">Ðазва</translation>
+<translation id="5337705430875057403">ЗловмиÑники на Ñайті <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> можуть обманом змуÑити Ð²Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ небезпечну дію, Ñк-от уÑтановити шкідливе програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ повідомити оÑобиÑту інформацію (наприклад, паролі, номери телефонів або кредитних карток).</translation>
<translation id="5359637492792381994">Ðе вдалоÑÑ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚Ð¸, що це Ñервер <ph name="DOMAIN" />. Його Ñертифікат безпеки зараз недійÑний. Імовірні причини: Ñервер налаштовано неправильно або хтоÑÑŒ намагаєтьÑÑ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð¿Ð¸Ñ‚Ð¸ ваше з’єднаннÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Помилка Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½ÑŒ правила</translation>
+<translation id="5386426401304769735">Ланцюжок Ñертифіката цього Ñайту міÑтить Ñертифікат, підпиÑаний за допомогою SHA-1.</translation>
<translation id="5421136146218899937">ОчиÑтити дані веб-переглÑду...</translation>
<translation id="5430298929874300616">Видалити закладку</translation>
<translation id="5431657950005405462">Файл не знайдено</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102">ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð· вбудованої Ñторінки Ñайту <ph name="SITE" />:</translation>
<translation id="5556459405103347317">Перезавантажити</translation>
<translation id="5565735124758917034">Ðктивний клієнт</translation>
+<translation id="5572851009514199876">Увійдіть в обліковий Ð·Ð°Ð¿Ð¸Ñ Chrome, щоб веб-переглÑдач міг перевірити, чи ви маєте дозвіл відвідувати цей Ñайт.</translation>
+<translation id="5580958916614886209">Перевірте міÑÑць Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ñ‚ÐµÑ€Ð¼Ñ–Ð½Ñƒ дії та повторіть Ñпробу</translation>
<translation id="560412284261940334">ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ðµ підтримуєтьÑÑ</translation>
<translation id="5610142619324316209">перевірити наÑвніÑÑ‚ÑŒ з’єднаннÑ</translation>
<translation id="5617949217645503996">ХоÑÑ‚ <ph name="HOST_NAME" /> переÑпрÑмував Ð²Ð°Ñ Ð·Ð°Ð±Ð°Ð³Ð°Ñ‚Ð¾ разів.</translation>
<translation id="5622887735448669177">Покинути цей Ñайт?</translation>
<translation id="5629630648637658800">Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½ÑŒ правила</translation>
<translation id="5631439013527180824">ÐедійÑний маркер ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ñтрою</translation>
+<translation id="5669703222995421982">Отримувати перÑоналізовані пропозиції</translation>
+<translation id="5675650730144413517">Сторінка не працює</translation>
<translation id="5677928146339483299">Заблоковано</translation>
<translation id="5694783966845939798">Ви намагалиÑÑ Ð·Ð²â€™ÑзатиÑÑ Ð· доменом <ph name="DOMAIN" />, але Ñервер надав Ñертифікат, підпиÑаний із викориÑтаннÑм Ñлабкого алгоритму підпиÑу (Ñк-от SHA-1). Це означає, що облікові дані безпеки, надані Ñервером, можуть бути ÑфальÑифікованими, а Ñервер – не тим, Ñкий вам потрібен (ви можете передавати Ñвої дані зловмиÑнику). <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Ідентифікаційну інформацію цього веб-Ñайта не було перевірено.</translation>
<translation id="5720705177508910913">Поточний кориÑтувач</translation>
-<translation id="572328651809341494">ОÑтанні вкладки</translation>
<translation id="5732392974455271431">Батьки можуть розблокувати його</translation>
<translation id="5784606427469807560">Ðе вдалоÑÑ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚Ð¸ дані картки. Перевірте Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Інтернетом Ñ– повторіть Ñпробу.</translation>
<translation id="5785756445106461925">Окрім цього, Ñторінка міÑтить незахищені реÑурÑи. Інші оÑоби можуть переглÑдати Ñ—Ñ… під Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…, а зловмиÑники можуть змінювати виглÑд Ñторінки.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Ви намагалиÑÑ Ð·Ð²â€™ÑзатиÑÑ Ð· доменом <ph name="DOMAIN" />, але Ñервер надав Ñертифікат, Ñкий міÑтить Ñлабкий ключ. Можливо, зловмиÑник зламав Ñекретний ключ, а Ñервер не Ñ” тим, Ñкий вам потрібен (ви можете передавати Ñвої дані зловмиÑнику). <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Ðова папка</translation>
<translation id="5869405914158311789">Ðемає зв’Ñзку із Ñайтом</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Цей продавець не приймає такий тип картки в Google Payments. Виберіть іншу картку.</translation>
<translation id="59174027418879706">Увімкнено</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="5966707198760109579">Тиждень</translation>
<translation id="5967867314010545767">Видалити з Ñ–Ñторії</translation>
<translation id="5975083100439434680">Зменшити маÑштаб</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Спробуйте:</translation>
<translation id="6151417162996330722">Сертифікат Ñервера має задовгий термін дії.</translation>
<translation id="6165508094623778733">Докладніше</translation>
+<translation id="6177128806592000436">Ваше Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· цим Ñайтом не захищене</translation>
<translation id="6203231073485539293">Перевірте Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Інтернетом</translation>
<translation id="6218753634732582820">Видалити адреÑу з Chromium?</translation>
+<translation id="6251924700383757765">Політика конфіденційноÑÑ‚Ñ–</translation>
+<translation id="625755898061068298">Ви вимкнули показ заÑтережень про небезпеку Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñайту.</translation>
<translation id="6259156558325130047">&amp;Повторити перевпорÑдкуваннÑ</translation>
<translation id="6263376278284652872">Закладки <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">ПовернутиÑÑ Ð´Ð¾ безпечного режиму</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">Сторінка <ph name="URL" /> недоÑтупна.</translation>
<translation id="6321917430147971392">Перевірте Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ DNS</translation>
<translation id="6328639280570009161">Спробуйте вимкнути Ð¿Ñ€Ð¾Ð³Ð½Ð¾Ð·ÑƒÐ²Ð°Ð½Ð½Ñ Ð¼ÐµÑ€ÐµÐ¶Ñ–.</translation>
+<translation id="6328786501058569169">Це оманливий Ñайт</translation>
<translation id="6337534724793800597">Фільтрувати правила за назвою</translation>
<translation id="6342069812937806050">Лише зараз</translation>
<translation id="6345221851280129312">невідомий розмір</translation>
@@ -489,6 +519,8 @@
<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="6404511346730675251">Редагувати закладку</translation>
+<translation id="6410264514553301377">Введіть дату Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ñ‚ÐµÑ€Ð¼Ñ–Ð½Ñƒ дії та код CVC картки <ph name="CREDIT_CARD" /></translation>
<translation id="6414888972213066896">Ви надіÑлали одному з батьків запит на переглÑд цього Ñайту</translation>
<translation id="6416403317709441254">Зараз не можна перейти на Ñторінку <ph name="SITE" />, оÑкільки веб-Ñайт надіÑлав зашифровані облікові дані, Ñкі Chromium не може обробити. Помилки мережі й атаки зазвичай тимчаÑові, тому Ñ†Ñ Ñторінка, Ñкоріш за вÑе, запрацює пізніше. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Ðеможливо перевірити, чи цей Ñертифікат було відкликано.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">ІгноруєтьÑÑ, оÑкільки пошук за умовчаннÑм вимкнено правилом.</translation>
<translation id="6462969404041126431">Ðе вдалоÑÑ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚Ð¸, що це Ñервер <ph name="DOMAIN" />. Можливо, його Ñертифікат безпеки відкликано. Імовірні причини: Ñервер налаштовано неправильно або хтоÑÑŒ намагаєтьÑÑ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð¿Ð¸Ñ‚Ð¸ ваше з’єднаннÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="647261751007945333">Правила приÑтрою</translation>
+<translation id="6477321094435799029">Chrome виÑвив на цій Ñторінці незвичний код Ñ– заблокував його, щоб захиÑтити вашу оÑобиÑту інформацію (наприклад, паролі, номери телефонів або кредитних карток).</translation>
<translation id="6489534406876378309">Почати Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… про аварійне Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸</translation>
<translation id="6529602333819889595">&amp;Повторити видаленнÑ</translation>
<translation id="6534179046333460208">Пропозиції ÑервіÑу "Інтернет навколо наÑ"</translation>
<translation id="6550675742724504774">Параметри</translation>
+<translation id="6556239504065605927">Захищене з’єднаннÑ</translation>
<translation id="6563469144985748109">ÐдмініÑтратор ще не Ñхвалив його</translation>
<translation id="6593753688552673085">менше <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Параметри шифруваннÑ</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Це правило більше не викориÑтовуєтьÑÑ.</translation>
<translation id="6652240803263749613">Ðе вдалоÑÑ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚Ð¸, що це Ñервер <ph name="DOMAIN" />. Операційна ÑиÑтема вашого комп’ютера вважає його Ñертифікат безпеки ненадійним. Імовірні причини: Ñервер налаштовано неправильно або хтоÑÑŒ намагаєтьÑÑ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð¿Ð¸Ñ‚Ð¸ ваше з’єднаннÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Редагувати папку</translation>
-<translation id="6660210980321319655">Ðвтоматичне повідомленнÑ: <ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">Видалити пропозицію Ð°Ð²Ñ‚Ð¾Ð·Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ñ„Ð¾Ñ€Ð¼ із Chromium?</translation>
<translation id="6685834062052613830">Вийдіть з облікового запиÑу та завершіть процедуру налаштуваннÑ</translation>
<translation id="6710213216561001401">Попереднє</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð°</translation>
<translation id="6757797048963528358">Ваш приÑтрій перейшов у режим Ñну.</translation>
<translation id="6778737459546443941">Батьки ще не Ñхвалили його</translation>
+<translation id="6810899417690483278">Ідентифікатор налаштуваннÑ</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Веб-Ñторінка за адреÑою <ph name="URL" /> наразі недоÑтупна. Можливо, вона перевантажена або перебуває на Ñтадії технічного обÑлуговуваннÑ.</translation>
<translation id="6831043979455480757">ПереклаÑти</translation>
@@ -532,7 +566,7 @@
<translation id="6897140037006041989">Ðгент кориÑтувача</translation>
<translation id="6915804003454593391">КориÑтувач:</translation>
<translation id="6957887021205513506">Схоже, що Ñертифікат Ñервера підроблено.</translation>
-<translation id="6965382102122355670">ОК</translation>
+<translation id="6965382102122355670">ТÐК</translation>
<translation id="6965978654500191972">ПриÑтрій</translation>
<translation id="6970216967273061347">Район або округ</translation>
<translation id="6973656660372572881">Указано фікÑовані прокÑÑ–-Ñервери та URL-адреÑа Ñценарію .pac.</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Контактна інформаціÑ</translation>
+<translation id="7079718277001814089">Цей Ñайт міÑтить зловмиÑне програмне забезпеченнÑ</translation>
<translation id="7087282848513945231">Округ або графÑтво</translation>
<translation id="7088615885725309056">Давніше</translation>
<translation id="7090678807593890770">Пошукайте за запитом "<ph name="LINK" />" у Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423">ХоÑÑ‚ <ph name="HOST_NAME" /> не відповідає Ñтандартам безпеки.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Докладніше<ph name="END_LINK" /> про цю проблему.</translation>
<translation id="7219179957768738017">З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñтовує верÑÑ–ÑŽ <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Сайт міÑтить зловмиÑне програмне забезпеченнÑ</translation>
<translation id="724975217298816891">Введіть термін дії та код CVC картки <ph name="CREDIT_CARD" />, щоб оновити Ñ—Ñ— дані. Щойно ви підтвердите дані картки, цей Ñайт отримає доÑтуп до них.</translation>
<translation id="725866823122871198">Ðе вдаєтьÑÑ Ð²Ñтановити конфіденційне Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, оÑкільки на комп’ютері вÑтановлено неправильні дату й Ñ‡Ð°Ñ (<ph name="DATE_AND_TIME" />).</translation>
<translation id="7269802741830436641">Ð¦Ñ Ð²ÐµÐ±-Ñторінка має цикл перенаправлень</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">СкаÑувати</translation>
<translation id="7667346355482952095">Отриманий маркер правила порожній або не збігаєтьÑÑ Ð· поточним маркером</translation>
<translation id="7668654391829183341">Ðевідомий приÑтрій</translation>
+<translation id="7669271284792375604">ЗловмиÑники на цьому Ñайті можуть обманом змуÑити Ð²Ð°Ñ ÑƒÑтановити програми, Ñкі погіршують роботу в Інтернеті (наприклад, змінюють вашу домашню Ñторінку або показують додаткову рекламу на Ñайтах, Ñкі ви відвідуєте).</translation>
<translation id="7674629440242451245">Хочете Ñпробувати нові цікаві функції Chrome? Завантажте верÑÑ–ÑŽ Ð´Ð»Ñ Ñ€Ð¾Ð·Ñ€Ð¾Ð±Ð½Ð¸ÐºÑ–Ð² зі Ñторінки chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Перейти на Ñайт <ph name="SITE" /> (небезпечно)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Ðе вдаєтьÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ цей Ñайт із кешу</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¾ з викориÑтаннÑм <ph name="CIPHER" /> з алгоритмом <ph name="MAC" /> Ð´Ð»Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‚Ð° ÐºÐ¾Ð´ÑƒÐ²Ð°Ð½Ð½Ñ <ph name="KX" /> Ñк механізму обміну ключами.</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="FORMATTED_TOTAL_AMOUNT" /> <ph name="CURRENCY_CODE" /></translation>
<translation id="7887683347370398519">Перевірте код CVC й повторіть Ñпробу</translation>
<translation id="7912024687060120840">У папці:</translation>
<translation id="7935318582918952113">ДиÑтилÑтор DOM</translation>
<translation id="7938958445268990899">Сертифікат Ñервера ще не дійÑний.</translation>
<translation id="7942349550061667556">Червоний</translation>
+<translation id="7947285636476623132">Перевірте рік Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ñ‚ÐµÑ€Ð¼Ñ–Ð½Ñƒ дії та повторіть Ñпробу</translation>
<translation id="7951415247503192394">(32-розрÑдна верÑÑ–Ñ)</translation>
<translation id="7956713633345437162">Закладки Ð´Ð»Ñ Ð¼Ð¾Ð±Ñ–Ð»ÑŒÐ½Ð¸Ñ… приÑтроїв</translation>
<translation id="7961015016161918242">Ðіколи</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">Завжди перекладати цю мовну пару: <ph name="ORIGINAL_LANGUAGE" /> – <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Ðе вказано</translation>
<translation id="8012647001091218357">Ðе вдалоÑÑ Ð·Ð²â€™ÑзатиÑÑ Ð· вашими батьками. Повторіть Ñпробу.</translation>
+<translation id="8025119109950072390">ЗловмиÑники на цьому Ñайті можуть обманом змуÑити Ð²Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ небезпечну дію, Ñк-от уÑтановити програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ повідомити оÑобиÑту інформацію (наприклад, паролі, номери телефонів або кредитних карток).</translation>
+<translation id="803030522067524905">Ð¤ÑƒÐ½ÐºÑ†Ñ–Ñ Ð±ÐµÐ·Ð¿ÐµÑ‡Ð½Ð¾Ð³Ð¾ переглÑду від Google нещодавно виÑвила фішинг на Ñайті <ph name="SITE" />. Фішингові Ñайти видають Ñебе за інші Ñайти, щоб ошукати ваÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Мова цієї Ñторінки: <ph name="SOURCE_LANGUAGE" />. ПереклаÑти Ñ—Ñ— такою мовою: <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">ÐадіÑлати відгук</translation>
<translation id="8088680233425245692">Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ³Ð»Ñнути Ñтаттю.</translation>
<translation id="8089520772729574115">менше 1 Мб</translation>
<translation id="8091372947890762290">ÐÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ Ð¾Ñ‡Ñ–ÐºÑƒÑ” на Ñервері</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719">Файл за адреÑою <ph name="URL" /> не читаєтьÑÑ. Можливо, його видалено, переміщено або доÑтуп заборонено дозволами файлу.</translation>
<translation id="8194797478851900357">&amp;Відмінити переміщеннÑ</translation>
<translation id="8201077131113104583">ÐедійÑна URL-адреÑа Ð´Ð»Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Ð· ідентифікатором "<ph name="EXTENSION_ID" />".</translation>
+<translation id="8202097416529803614">ПідÑумок замовленнÑ</translation>
<translation id="8218327578424803826">Указане міÑцезнаходженнÑ:</translation>
<translation id="8225771182978767009">КориÑтувач, Ñкий налаштував комп’ютер, заблокував цей Ñайт.</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />, <ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">ЗловмиÑники, Ñкі зараз перебувають на Ñайті <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" />, можуть намагатиÑÑ Ð²Ñтановити на ваш комп’ютер небезпечні програми, що викрадають або видалÑÑŽÑ‚ÑŒ інформацію (Ñк-от фотографії, паролі, Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‡Ð¸ кредитні картки).</translation>
<translation id="8241707690549784388">Сторінка, Ñку ви шукаєте, викориÑтовувала інформацію, введену вами. ÐŸÐ¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ Ð´Ð¾ такої Ñторінки може призвеÑти до Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¸Ñ… попередніх дій. Ви дійÑно бажаєте продовжити?</translation>
<translation id="8249320324621329438">ВоÑтаннє отримано:</translation>
<translation id="8261506727792406068">Видалити</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">Джерело</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="8349305172487531364">Панель закладок</translation>
<translation id="8363502534493474904">вимкнути режим польоту</translation>
<translation id="8364627913115013041">Ðе вÑтановлено.</translation>
<translation id="8380941800586852976">Ðебезпечна</translation>
<translation id="8382348898565613901">Тут відображатимутьÑÑ Ð½ÐµÑ‰Ð¾Ð´Ð°Ð²Ð½Ð¾ відкриті закладки</translation>
+<translation id="8398259832188219207">Дата Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð²Ñ–Ñ‚Ñƒ про аварійне Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸: <ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">Ðварійне Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">Потрібно двічі ввеÑти однакову парольну фразу.</translation>
<translation id="8428213095426709021">ÐалаштуваннÑ</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222">ХоÑÑ‚ <ph name="HOST_NAME" /> довго не відповідає.</translation>
<translation id="852346902619691059">Ðе вдалоÑÑ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚Ð¸, що це Ñервер <ph name="DOMAIN" />. Операційна ÑиÑтема вашого приÑтрою вважає його Ñертифікат безпеки ненадійним. Імовірні причини: Ñервер налаштовано неправильно або хтоÑÑŒ намагаєтьÑÑ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð¿Ð¸Ñ‚Ð¸ ваше з’єднаннÑ. <ph name="BEGIN_LEARN_MORE_LINK" />Докладніше<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Цей тип картки не підтримуєтьÑÑ Ð² Google Payments. Виберіть іншу картку.</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="8553075262323480129">Помилка перекладу. Ðеможливо визначити мову Ñторінки.</translation>
<translation id="8559762987265718583">Ðе вдаєтьÑÑ Ð²Ñтановити конфіденційне Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, оÑкільки на приÑтрої вÑтановлено неправильні дату й Ñ‡Ð°Ñ (<ph name="DATE_AND_TIME" />).</translation>
-<translation id="856992080682148">Термін дії Ñертифіката Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñайту закінчуєтьÑÑ 2017 року. Ланцюжок Ñертифіката міÑтить Ñертифікат, підпиÑаний за допомогою SHA-1.</translation>
<translation id="8571890674111243710">ВиконуєтьÑÑ Ð¿ÐµÑ€ÐµÐºÐ»Ð°Ð´ Ñторінки такою мовою: <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Сертифікат не вказує на механізм перевірки його відкликаннÑ.</translation>
<translation id="8620436878122366504">Батьки ще не Ñхвалили його</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522">&lt;abbr id="dnsDefinition"&gt;ÐдреÑу DNS&lt;/abbr&gt; хоÑту <ph name="HOST_NAME" /> не знайдено. ДіагноÑтика проблеми.</translation>
<translation id="8790007591277257123">&amp;Повторити видаленнÑ</translation>
<translation id="8798099450830957504">За умовчаннÑм</translation>
+<translation id="8800988563907321413">Тут відображатимутьÑÑ Ð¿Ñ€Ð¾Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ñ—</translation>
<translation id="8804164990146287819">Політика конфіденційноÑÑ‚Ñ–</translation>
<translation id="8820817407110198400">Закладки</translation>
<translation id="8834246243508017242">Увімкнути "ÐÐ²Ñ‚Ð¾Ð·Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ð·Ð° допомогою контактів"…</translation>
<translation id="883848425547221593">Інші закладки</translation>
+<translation id="884264119367021077">ÐдреÑа доÑтавки</translation>
<translation id="884923133447025588">Ðе знайдено механізм відкликаннÑ.</translation>
<translation id="885730110891505394">ÐÐ°Ð´Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— Ñлужбам Google</translation>
<translation id="8866481888320382733">Помилка аналізу налаштувань правила</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">Термін дії Ñертифіката Ñервера завершивÑÑ.</translation>
<translation id="8987927404178983737">МіÑÑць</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Сайт міÑтить шкідливі програми</translation>
<translation id="9001074447101275817">Ð”Ð»Ñ Ð¿Ñ€Ð¾ÐºÑÑ– <ph name="DOMAIN" /> потрібно ввеÑти Ñ–Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача та пароль.</translation>
<translation id="901974403500617787">Позначки, Ñкі заÑтоÑовуютьÑÑ Ð´Ð¾ вÑієї ÑиÑтеми, може вÑтановлювати лише влаÑник: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Цю Ñторінку перекладено такою мовою: <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Помилка ÑиÑтеми безпеки</translation>
<translation id="9038649477754266430">КориÑтуйтеÑÑ Ñлужбою передбаченнÑ, щоб Ñторінки завантажувалиÑÑ ÑˆÐ²Ð¸Ð´ÑˆÐµ</translation>
<translation id="9039213469156557790">Окрім цього, Ñторінка міÑтить незахищені реÑурÑи. Інші оÑоби можуть переглÑдати Ñ—Ñ… під Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…, а зловмиÑники можуть змінювати роботу Ñторінки.</translation>
+<translation id="9040185888511745258">ЗловмиÑники на Ñайті <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> можуть оманливим шлÑхом змуÑити Ð²Ð°Ñ ÑƒÑтановити на Ñвій приÑтрій програми, Ñкі погіршують переглÑд веб-Ñторінок (наприклад, змінюють вашу домашню Ñторінку або показують додаткову рекламу на Ñайтах, Ñкі ви відвідуєте).</translation>
<translation id="9050666287014529139">Парольна фраза</translation>
<translation id="9065203028668620118">Редагувати</translation>
<translation id="9068849894565669697">Вибрати колір</translation>
<translation id="9076283476770535406">Ðа ньому може бути вміÑÑ‚ Ð´Ð»Ñ Ð´Ð¾Ñ€Ð¾Ñлих</translation>
-<translation id="9092364396508701805">Сторінка хоÑту <ph name="HOST_NAME" /> не працює</translation>
<translation id="9103872766612412690">Веб-Ñайт <ph name="SITE" /> зазвичай викориÑтовує ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð·Ð°Ñ…Ð¸Ñту вашої інформації. Під Ñ‡Ð°Ñ Ñ†Ñ–Ñ”Ñ— Ñпроби Chromium під’єднатиÑÑ Ð´Ð¾ Ñторінки <ph name="SITE" /> з неї отримано незвичні й неправильні облікові дані. Це може ÑтатиÑÑ, коли зловмиÑник намагаєтьÑÑ Ð²Ð¸Ð´Ð°Ð²Ð°Ñ‚Ð¸ Ñебе за веб-Ñайт <ph name="SITE" /> або Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð°Ð½Ð¾ екраном входу Wi-Fi. Ваша Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð·Ð°Ð»Ð¸ÑˆÐ°Ñ”Ñ‚ÑŒÑÑ Ð·Ð°Ñ…Ð¸Ñ‰ÐµÐ½Ð¾ÑŽ, оÑкільки Chromium припинив Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð´Ð¾ того, Ñк почавÑÑ Ð¾Ð±Ð¼Ñ–Ð½ будь-Ñкими даними.</translation>
<translation id="9137013805542155359">Показати оригінал</translation>
+<translation id="9137248913990643158">Перш ніж кориÑтуватиÑÑ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð¼, увійдіть в обліковий Ð·Ð°Ð¿Ð¸Ñ Chrome.</translation>
<translation id="9148507642005240123">&amp;Відмінити редагуваннÑ</translation>
<translation id="9157595877708044936">ÐалаштуваннÑ...</translation>
<translation id="9170848237812810038">&amp;СкаÑувати</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">ОЧИСТИТИ ФОРМУ</translation>
<translation id="939736085109172342">Ðова папка</translation>
<translation id="941721044073577244">Схоже, у Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” дозволу, щоб перейти на цей Ñайт</translation>
-<translation id="962701380617707048">Введіть термін дії та код CVC картки <ph name="CREDIT_CARD" />, щоб оновити її дані</translation>
<translation id="969892804517981540">Розробка</translation>
<translation id="988159990683914416">КонÑÑ‚Ñ€ÑƒÐºÑ†Ñ–Ñ Ñ€Ð¾Ð·Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ°</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 1a8b1c35ed3..644d03aee98 100644
--- a/chromium/components/strings/components_strings_vi.xtb
+++ b/chromium/components/strings/components_strings_vi.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">Kết nối lại với Wi-Fi</translation>
<translation id="1175364870820465910">&amp;In...</translation>
<translation id="1181037720776840403">Xóa</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />Tự động báo cáo<ph name="END_WHITEPAPER_LINK" /> chi tiết các sự cố bảo mật có thể xảy ra với Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">Tiếp theo</translation>
<translation id="1201895884277373915">Thêm từ trang web này</translation>
<translation id="1206967143813997005">Chữ ký ban đầu không hợp lệ</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">Lục lam</translation>
<translation id="1629803312968146339">Bạn có muốn Chrome lưu thẻ này không?</translation>
<translation id="1640180200866533862">Chính sách ngÆ°á»i dùng</translation>
+<translation id="1640244768702815859">Hãy thử <ph name="BEGIN_LINK" />truy cập trang chủ của trang web<ph name="END_LINK" />.</translation>
<translation id="1644184664548287040">Cấu hình mạng không hợp lệ và không thể nhập được.</translation>
<translation id="1644574205037202324">Lịch sử</translation>
<translation id="1645368109819982629">Giao thức không được hỗ trợ</translation>
<translation id="1676269943528358898"><ph name="SITE" /> thÆ°á»ng sá»­ dụng mã hóa để bảo vệ thông tin của bạn. Khi Google Chrome tìm cách kết nối vá»›i <ph name="SITE" /> tại thá»i Ä‘iểm này, trang web đã gá»­i lại thông tin đăng nhập không chính xác và bất thÆ°á»ng. Äiá»u này có thể xảy ra khi kẻ tấn công Ä‘ang cố gắng giả mạo là <ph name="SITE" /> hoặc màn hình đăng nhập Wi-Fi đã làm gián Ä‘oạn kết nối. Thông tin của bạn vẫn an toàn do Google Chrome đã ngừng kết nối trÆ°á»›c khi bất kỳ dữ liệu nào được trao đổi.</translation>
+<translation id="168328519870909584">Những kẻ tấn công hiện đang truy cập <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> có thể cố cài đặt các ứng dụng nguy hiểm trên thiết bị của bạn để lấy 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="168841957122794586">Chứng chỉ máy chủ chứa khóa mật mã yếu.</translation>
-<translation id="1701955595840307032">Tải nội dung được đỠxuất</translation>
<translation id="1710259589646384581">OS</translation>
<translation id="1721312023322545264">Bạn cần được <ph name="NAME" /> cấp quyá»n để truy cập vào trang web này</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">Số trang</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />Thử chạy Chẩn đoán mạng của Windows<ph name="END_LINK" />.</translation>
<translation id="1783075131180517613">Vui lòng cập nhật cụm mật khẩu đồng bộ hóa của bạn.</translation>
+<translation id="1787142507584202372">Tab đang mở của bạn xuất hiện ở đây</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Gần đây, tính năng Duyệt web an toàn của Google <ph name="BEGIN_LINK" />đã phát hiện thấy phần má»m Ä‘á»™c hại<ph name="END_LINK" /> trên <ph name="SITE" />. Các trang web bình thÆ°á»ng vẫn an toàn đôi khi bị lây nhiá»…m phần má»m Ä‘á»™c hại. Ná»™i dung Ä‘á»™c hại đến từ <ph name="SUBRESOURCE_HOST" />, má»™t đối tượng phân phối phần má»m Ä‘á»™c hại đã biết. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="1821930232296380041">Yêu cầu hoặc tham số yêu cầu không hợp lệ</translation>
+<translation id="1834321415901700177">Trang web này có chứa các chương trình độc hại</translation>
<translation id="1838667051080421715">Bạn đang xem nguồn của trang web.</translation>
<translation id="1871208020102129563">Proxy được đặt để sử dụng máy chủ proxy cố định chứ không phải URL tập lệnh .pac.</translation>
<translation id="1883255238294161206">Thu gá»n danh sách</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">Dấu trang di động</translation>
<translation id="2148716181193084225">Hôm nay</translation>
-<translation id="2149973817440762519">Chỉnh sửa dấu trang</translation>
<translation id="2154054054215849342">Tính năng đồng bá»™ hóa không khả dụng cho miá»n của bạn</translation>
<translation id="2166049586286450108">Quyá»n truy cập quản trị đầy đủ</translation>
<translation id="2166378884831602661">Trang web này không thể cung cấp kết nối an toàn</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">Bạn không thể truy cập <ph name="SITE" /> ngay bây giá» vì trang web sá»­ dụng HSTS. Lá»—i mạng và các cuá»™c tấn công mạng thÆ°á»ng chỉ là tạm thá»i, do đó, trang này có thể sẽ hoạt Ä‘á»™ng lại sau. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2328300916057834155">Äã bá» qua dấu trang không hợp lệ tại chỉ mục <ph name="ENTRY_INDEX" /></translation>
<translation id="2354001756790975382">Dấu trang khác</translation>
+<translation id="2355395290879513365">Kẻ tấn công có thể thấy những hình ảnh mà bạn đang xem trên trang web này và lừa bạn bằng cách sửa đổi những hình ảnh đó.</translation>
<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>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">Hiển thị chính sách không có giá trị được đặt</translation>
<translation id="2396249848217231973">&amp;Hoàn tác xóa</translation>
<translation id="2455981314101692989">Trang web này đã vô hiệu hóa tính năng tá»± Ä‘á»™ng Ä‘iá»n cho biểu mẫu này.</translation>
+<translation id="2460160116472764928">Gần đây, tính năng Duyệt web an toàn của Google <ph name="BEGIN_LINK" />đã phát hiện thấy phần má»m Ä‘á»™c hại<ph name="END_LINK" /> trên <ph name="SITE" />. Các trang web bình thÆ°á»ng vẫn an toàn đôi khi bị lây nhiá»…m phần má»m Ä‘á»™c hại. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2463739503403862330">Äiá»n</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />Chạy Chẩn đoán mạng<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">URL tìm kiếm hợp lệ.</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">Bạn có chắc chắn muốn xóa những trang này khá»i lịch sá»­ duyệt web của mình không?</translation>
<translation id="2677748264148917807">Rá»i khá»i</translation>
<translation id="269990154133806163">Máy chủ đã xuất trình chứng chỉ không được tiết lá»™ công khai theo chính sách Tính minh bạch của chứng chỉ. Äây là yêu cầu đối vá»›i má»™t số chứng chỉ, để đảm bảo chúng đáng tin cậy và giúp chống lại những kẻ tấn công. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
+<translation id="2702801445560668637">Danh sách Ä‘á»c</translation>
<translation id="2704283930420550640">Giá trị không khớp với định dạng.</translation>
<translation id="2704951214193499422">Chromium không thể xác nhận thẻ của bạn tại thá»i Ä‘iểm này. Vui lòng thá»­ lại sau.</translation>
<translation id="2705137772291741111">Không thể Ä‘á»c được bản sao đã lÆ°u (đã lÆ°u vào bá»™ nhá»› cache) của trang web này.</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">Bạn đã cố gắng truy cập <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à thông tin đăng nhập 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 kết nối vá»›i kẻ tấn công. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="2712173769900027643">Xin phép</translation>
<translation id="2713444072780614174">Trắng</translation>
+<translation id="2720342946869265578">Lân cận</translation>
<translation id="2721148159707890343">Yêu cầu đã thành công</translation>
<translation id="2728127805433021124">Chứng chỉ của máy chủ đã được ký bằng thuật toán chữ ký yếu.</translation>
<translation id="2730326759066348565"><ph name="BEGIN_LINK" />Chạy Chẩn đoán kết nối<ph name="END_LINK" /></translation>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">Bạn có thể tắt má»i proxy được định cấu hình cho kết nối từ trang cài đặt.</translation>
<translation id="2955913368246107853">Äóng thanh tìm</translation>
<translation id="2958431318199492670">Cấu hình mạng không tuân thủ tiêu chuẩn ONC. Các bộ phận của cấu hình có thể không được nhập.</translation>
+<translation id="29611076221683977">Những kẻ tấn công hiện đang truy cập <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 trên máy Mac. 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="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="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>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">Ẩn chi tiết</translation>
<translation id="3587482841069643663">Tất cả</translation>
<translation id="3600246354004376029"><ph name="TITLE" />, <ph name="DOMAIN" />, <ph name="TIME" /></translation>
-<translation id="3609138628363401169">Máy chủ không hỗ trợ tiện ích đàm phán lại TLS.</translation>
<translation id="36224234498066874">Xóa Dữ liệu 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>
@@ -271,6 +279,7 @@
<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="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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">Giấy phép không đủ</translation>
<translation id="3714780639079136834">Bật dữ liệu di dộng hoặc Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />Kiểm tra proxy, tÆ°á»ng lá»­a và cấu hình DNS<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">Nếu bạn hiểu các rủi ro bảo mật, bạn có thể <ph name="BEGIN_LINK" />truy cập trang web không an toàn này<ph name="END_LINK" /> trước khi các chương trình nguy hiểm bị xóa.</translation>
<translation id="3739623965217189342">Liên kết đã sao chép</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="3788090790273268753">Chứng chỉ cho trang web này sẽ hết hạn vào năm 2016, đồng thá»i, chuá»—i chứng chỉ này chứa má»™t chứng chỉ đã ký bằng SHA-1.</translation>
<translation id="382518646247711829">Nếu bạn sử dụng máy chủ proxy...</translation>
<translation id="3828924085048779000">Không cho phép cụm mật khẩu trống.</translation>
<translation id="3845539888601087042">Hiển thị lịch sử từ các thiết bị bạn đã đăng nhập. <ph name="BEGIN_LINK" />Tìm hiểu thêm<ph name="END_LINK" />.</translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">Khóa "<ph name="SUBKEY" />": <ph name="ERROR" /></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="4103249731201008433">Số sê-ri thiết bị không hợp lệ</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>
<translation id="4110615724604346410">Máy chủ này không chứng minh được rằng đó là 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 cấu hình sai hoặc có kẻ tấn công chặn kết nối của bạn. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{không có gì}=1{1 ứng dụng ($1)}=2{2 ứng dụng ($1, $2)}other{# ứng dụng ($1, $2, $3)}}</translation>
<translation id="4220128509585149162">Sự cố</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />Thử chạy Chẩn đoán mạng<ph name="END_LINK" />.</translation>
+<translation id="4250431568374086873">Kết nối của bạn tới trang web này không đủ an toàn</translation>
<translation id="4250680216510889253">Không</translation>
<translation id="425582637250725228">Các thay đổi bạn đã thực hiện có thể không được lưu.</translation>
<translation id="4258748452823770588">Chữ ký không hợp lệ</translation>
<translation id="4269787794583293679">(Không có tên ngÆ°á»i dùng)</translation>
+<translation id="4280429058323657511">, hết hạn <ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Gần đây, tính năng Duyệt web an toàn của Google <ph name="BEGIN_LINK" />đã phát hiện thấy chương trình độc hại<ph name="END_LINK" /> trên <ph name="SITE" />. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4300246636397505754">Äá» xuất chính</translation>
<translation id="4304224509867189079">Äăng nhập</translation>
<translation id="432290197980158659">Máy chủ đã xuất trình chứng chỉ không khá»›p vá»›i các 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. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="4325863107915753736">Không tìm được bài viết</translation>
+<translation id="4326324639298822553">Kiểm tra ngày hết hạn của bạn và thử lại</translation>
<translation id="4331708818696583467">Không bảo mật</translation>
+<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="4381091992796011497">Tên NgÆ°á»i dùng:</translation>
<translation id="4394049700291259645">Vô hiệu hóa</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">Bạn đã cố gắng truy cập <ph name="DOMAIN" /> nhưng máy chủ đã xuất trình chứng chỉ không hợp lệ. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</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="4619615317237390068">Tab từ các thiết bị khác</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">Bạn đang xem trang tiện ích.</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">Tải lại chính sách</translation>
<translation id="4728558894243024398">Nền tảng</translation>
<translation id="4744603770635761495">ÄÆ°á»ng dẫn thá»±c thi</translation>
+<translation id="4750917950439032686">Thông tin của bạn (ví dụ: mật khẩu hoặc số thẻ tín dụng) sẽ được bảo mật khi được gửi tới trang web này.</translation>
<translation id="4756388243121344051">&amp;Lịch sử</translation>
+<translation id="4759118997339041434">Äã tắt tá»± Ä‘á»™ng Ä‘iá»n thanh toán</translation>
<translation id="4764776831041365478">Trang web tại <ph name="URL" /> có thể tạm thá»i không hoạt Ä‘á»™ng hay được chuyển vÄ©nh viá»…n sang địa chỉ web má»›i.</translation>
<translation id="4771973620359291008">Xảy ra lỗi chưa biết.</translation>
<translation id="4800132727771399293">Kiểm tra ngày hết hạn và CVC của bạn rồi thử lại</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">Lỗi mạng</translation>
<translation id="4816492930507672669">Vừa với trang</translation>
<translation id="4850886885716139402">Xem</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{và thêm 1 trang web}other{và thêm # trang web}}</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="495170559598752135">Tác vụ</translation>
<translation id="4958444002117714549">Mở rộng danh sách</translation>
@@ -395,7 +415,6 @@
<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="5199841536747119669">Äá» xuất của bạn sẽ xuất hiện ở đây</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="5299298092464848405">Lỗi phân tích cú pháp chính sách</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">Báo cáo sự cố bị tắt.</translation>
<translation id="5317780077021120954">LÆ°u</translation>
<translation id="5327248766486351172">Tên</translation>
+<translation id="5337705430875057403">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 má»™t 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 của bạn (ví dụ: mật khẩu, số Ä‘iện thoại hoặc thẻ tín dụng).</translation>
<translation id="5359637492792381994">Máy chủ này không chứng minh được rằng đó là <ph name="DOMAIN" />; hiện tại, chứng chỉ bảo mật của máy chủ này không hợp lệ. Ä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. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="536296301121032821">Không thể lưu trữ cài đặt chính sách</translation>
+<translation id="5386426401304769735">Chuỗi chứng chỉ cho trang web này có chứa một chứng chỉ đã ký bằng SHA-1.</translation>
<translation id="5421136146218899937">Xóa dữ liệu duyệt web...</translation>
<translation id="5430298929874300616">Xóa dấu trang</translation>
<translation id="5431657950005405462">Không tìm thấy tệp của bạn</translation>
@@ -425,17 +446,20 @@
<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="5565735124758917034">Äang hoaÌ£t động</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>
+<translation id="5580958916614886209">Kiểm tra tháng hết hạn của bạn và thử lại</translation>
<translation id="560412284261940334">Không hỗ trợ quản lý</translation>
<translation id="5610142619324316209">Kiểm tra kết nối</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> đã chuyển hÆ°á»›ng bạn quá nhiá»u lần.</translation>
<translation id="5622887735448669177">Bạn có muốn rá»i khá»i trang web này không?</translation>
<translation id="5629630648637658800">Không thể tải cài đặt chính sách</translation>
<translation id="5631439013527180824">Mã thông báo quản lý thiết bị không hợp lệ</translation>
+<translation id="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="5677928146339483299">Bị chặn</translation>
<translation id="5694783966845939798">Bạn đã cố gắng truy cập <ph name="DOMAIN" /> nhÆ°ng máy chủ xuất trình má»™t chứng chỉ được ký bằng má»™t thuật toán chữ ký yếu (chẳng hạn nhÆ° SHA-1). Äiá»u này có nghÄ©a là thông tin đăng nhập bảo mật mà máy chủ xuất trình có thể đã bị giả mạo và máy chủ đó có thể không phải là máy chủ mà bạn mong đợi (bạn có thể Ä‘ang kết nối vá»›i kẻ tấn công). <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5710435578057952990">Nhận dạng trang web này chưa được xác minh.</translation>
<translation id="5720705177508910913">NgÆ°á»i dùng hiện tại</translation>
-<translation id="572328651809341494">Các tab gần đây</translation>
<translation id="5732392974455271431">Cha mẹ của bạn có thể bỠchặn trang web cho bạn</translation>
<translation id="5784606427469807560">Äã xảy ra sá»± cố khi xác nhận thẻ của bạn. Hãy kiểm tra kết nối Internet của bạn và thá»­ lại.</translation>
<translation id="5785756445106461925">Ngoài ra, trang này bao gồm các tài nguyên khác không an toàn. Những tài nguyên này có thể bị ngÆ°á»i khác xem khi Ä‘ang gá»­i và có thể bị kẻ tấn công sá»­a đổi nhằm thay đổi giao diện của trang.</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">Bạn đã cố gắng truy cập <ph name="DOMAIN" /> nhưng máy chủ xuất trình chứng chỉ chứa khóa yếu. Kẻ tấn công có thể đã phá khóa cá nhân và máy chủ đó có thể không phải là máy chủ bạn mong đợi (bạn có thể đang kết nối với kẻ tấn công). <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="5857090052475505287">Thư mục Mới</translation>
<translation id="5869405914158311789">Không thể truy cập trang web này</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">Loại thẻ này không được Google Payments há»— trợ cho ngÆ°á»i bán này. Vui lòng chá»n thẻ khác.</translation>
<translation id="59174027418879706">Äã bật</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="5966707198760109579">Tuần</translation>
<translation id="5967867314010545767">Xóa khá»i lịch sá»­</translation>
<translation id="5975083100439434680">Thu nhá»</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">Hãy thử:</translation>
<translation id="6151417162996330722">Chứng chỉ máy chủ có thá»i gian hiệu lá»±c quá dài.</translation>
<translation id="6165508094623778733">Tìm hiểu thêm</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="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="6251924700383757765">ChiÌnh saÌch bảo mật</translation>
+<translation id="625755898061068298">Bạn đã chá»n tắt cảnh báo bảo mật cho trang web này.</translation>
<translation id="6259156558325130047">&amp;Làm lại sắp xếp lại</translation>
<translation id="6263376278284652872">Dấu trang trên <ph name="DOMAIN" /></translation>
<translation id="6264485186158353794">Quay lại an toàn</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">Không thể truy cập <ph name="URL" />.</translation>
<translation id="6321917430147971392">Kiểm tra cài đặt DNS của bạn</translation>
<translation id="6328639280570009161">Thử tắt dự đoán mạng</translation>
+<translation id="6328786501058569169">Äây là trang web lừa đảo</translation>
<translation id="6337534724793800597">Lá»c chính sách theo tên</translation>
<translation id="6342069812937806050">Vừa mới</translation>
<translation id="6345221851280129312">kích thước không xác định</translation>
@@ -489,6 +519,8 @@
<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="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>
<translation id="6416403317709441254">Bạn không thể truy cập <ph name="SITE" /> ngay bây giá» do trang web gá»­i thông tin đăng nhập đã mã hóa mà Chromium không thể xá»­ lý. Lá»—i mạng và các cuá»™c tấn công mạng thÆ°á»ng chỉ là tạm thá»i, do đó, trang này có thể sẽ hoạt Ä‘á»™ng lại sau. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6417515091412812850">Không thể kiểm tra liệu chứng chỉ đã bị thu hồi hay chưa.</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">Bị bỠqua vì chính sách đã tắt tìm kiếm mặc định.</translation>
<translation id="6462969404041126431">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ị thu hồi. Ä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. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<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="6529602333819889595">&amp;Làm lại xóa</translation>
<translation id="6534179046333460208">Äá» xuất Web trong cuá»™c sống</translation>
<translation id="6550675742724504774">Tùy chá»n</translation>
+<translation id="6556239504065605927">Kết nối an toàn</translation>
<translation id="6563469144985748109">NgÆ°á»i quản lý của bạn chÆ°a phê duyệt trang web</translation>
<translation id="6593753688552673085">dÆ°á»›i <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">Tùy chá»n mã hóa</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">Chính sách này không được chấp thuận.</translation>
<translation id="6652240803263749613">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 của máy tính tin 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. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="6656103420185847513">Chỉnh sửa thư mục</translation>
-<translation id="6660210980321319655"><ph name="CRASH_TIME" /> được báo cáo tự động</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>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">Giá trị chính sách</translation>
<translation id="6757797048963528358">Thiết bị của bạn đã chuyển sang chế độ ngủ.</translation>
<translation id="6778737459546443941">Cha mẹ của bạn chưa phê duyệt trang web</translation>
+<translation id="6810899417690483278">ID tùy chỉnh</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">Trang web tại <ph name="URL" /> hiện không có. Trang này có thể đã bị quá tải hoặc đang ngừng hoạt động để bảo trì.</translation>
<translation id="6831043979455480757">Dịch</translation>
@@ -542,6 +576,8 @@
<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>
+<translation id="7064851114919012435">Thông tin liên hệ</translation>
+<translation id="7079718277001814089">Trang web này có chứa phần má»m Ä‘á»™c hại</translation>
<translation id="7087282848513945231">Hạt</translation>
<translation id="7088615885725309056">Cũ hơn</translation>
<translation id="7090678807593890770">Tìm kiếm <ph name="LINK" /> trên Google</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> không tuân thủ các tiêu chuẩn bảo mật.</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />Tìm hiểu thêm<ph name="END_LINK" /> vỠsự cố này.</translation>
<translation id="7219179957768738017">Kết nối sử dụng <ph name="SSL_VERSION" />.</translation>
+<translation id="724691107663265825">Trang web bạn sắp truy cập chứa phần má»m Ä‘á»™c hại</translation>
<translation id="724975217298816891">Nhập ngày hết hạn và CVC cho <ph name="CREDIT_CARD" /> để cập nhật chi tiết thẻ của bạn. 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>
<translation id="725866823122871198">Không thể thiết lập kết nối riêng tư với <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> vì ngày và giỠ(<ph name="DATE_AND_TIME" />) trên máy tính của bạn không đúng.</translation>
<translation id="7269802741830436641">Trang web này lặp lại chuyển hướng</translation>
@@ -611,6 +648,7 @@ Lưu ý! Chế độ ẩn danh <ph name="SHORTCUT_KEY" /> có thể hữu ích v
<translation id="7658239707568436148">Hủy</translation>
<translation id="7667346355482952095">Mã thông báo chính sách trả vỠtrống hoặc không khớp với mã thông báo hiện tại</translation>
<translation id="7668654391829183341">Thiết bị không xác định</translation>
+<translation id="7669271284792375604">Những kẻ tấn công trên trang web này có thể đánh lừa bạn cài đặt các chương trình ảnh hưởng đến trải nghiệm duyệt web của bạn (ví dụ: bằng cách thay đổi trang chủ của bạn hoặc hiển thị thêm quảng cáo trên các trang web bạn truy cập).</translation>
<translation id="7674629440242451245">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 nhà phát triển của chúng tôi tại chrome.com/dev.</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />Tiếp tục truy cập <ph name="SITE" /> (không an toàn)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">Không thể tải trang web này từ bộ nhớ cache</translation>
@@ -626,14 +664,17 @@ Lưu ý! Chế độ ẩn danh <ph name="SHORTCUT_KEY" /> có thể hữu ích v
<translation id="7800304661137206267">Kết nối được mã hóa bằng <ph name="CIPHER" />, với <ph name="MAC" /> để xác thực thư và <ph name="KX" /> là cơ chế trao đổi chính.</translation>
<translation id="780301667611848630">Không, cảm ơn</translation>
<translation id="7805768142964895445">Trạng thái</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">Xóa Ä‘á» xuất biểu mẫu khá»i Chrome?</translation>
<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="7887683347370398519">Kiểm tra CVC của bạn và thử lại</translation>
<translation id="7912024687060120840">Trong thư mục:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">Chứng chỉ của máy chủ chưa hợp lệ.</translation>
<translation id="7942349550061667556">Äá»</translation>
+<translation id="7947285636476623132">Kiểm tra năm hết hạn của bạn và thử lại</translation>
<translation id="7951415247503192394">(32 bit)</translation>
<translation id="7956713633345437162">Dấu trang di động</translation>
<translation id="7961015016161918242">ChÆ°a bao giá»</translation>
@@ -641,7 +682,10 @@ Lưu ý! Chế độ ẩn danh <ph name="SHORTCUT_KEY" /> có thể hữu ích v
<translation id="7983301409776629893">Luôn dịch <ph name="ORIGINAL_LANGUAGE" /> sang <ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">Không chỉ định</translation>
<translation id="8012647001091218357">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>
+<translation id="8025119109950072390">Những kẻ tấn công trên trang web này có thể đánh lừa bạn làm má»™t 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 của bạn (ví dụ: mật khẩu, số Ä‘iện thoại hoặc thẻ tín dụng).</translation>
+<translation id="803030522067524905">Gần đây, tính năng Duyệt web an toàn của Google đã phát hiện thấy lừa đảo trên <ph name="SITE" />. Các trang web lừa đảo giả mạo các trang web khác để lừa bạn. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8034522405403831421">Trang này có ngôn ngữ là <ph name="SOURCE_LANGUAGE" />. Dịch trang này sang <ph name="TARGET_LANGUAGE" />?</translation>
+<translation id="8041089156583427627">Gửi phản 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>
@@ -652,9 +696,11 @@ Lưu ý! Chế độ ẩn danh <ph name="SHORTCUT_KEY" /> có thể hữu ích v
<translation id="8150722005171944719">Không thể Ä‘á»c được tệp tại <ph name="URL" />. Tệp này có thể đã bị xóa, di chuyển hoặc quyá»n tệp có thể Ä‘ang chặn truy cập.</translation>
<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="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="8230421197304563332">Những kẻ tấn công đang ở 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).</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="8249320324621329438">Tìm nạp lần cuối:</translation>
<translation id="8261506727792406068">Xóa</translation>
@@ -663,11 +709,13 @@ Lưu ý! Chế độ ẩn danh <ph name="SHORTCUT_KEY" /> có thể hữu ích v
<translation id="8294431847097064396">Nguồn</translation>
<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="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>
<translation id="8380941800586852976">Nguy hiểm</translation>
<translation id="8382348898565613901">Dấu trang bạn truy cập gần đây sẽ xuất hiện ở đây</translation>
+<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="8428213095426709021">Cài đặt</translation>
@@ -679,9 +727,9 @@ Lưu ý! Chế độ ẩn danh <ph name="SHORTCUT_KEY" /> có thể hữu ích v
<translation id="8498891568109133222"><ph name="HOST_NAME" /> mất quá nhiá»u thá»i gian để phản hồi.</translation>
<translation id="852346902619691059">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 của thiết bị tin 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. <ph name="BEGIN_LEARN_MORE_LINK" />Tìm hiểu thêm<ph name="END_LEARN_MORE_LINK" />.</translation>
<translation id="8530504477309582336">Google Payments không há»— trợ loại thẻ này. Vui lòng chá»n thẻ khác.</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="8553075262323480129">Dịch thất bại do ngôn ngữ của trang không được xác định.</translation>
<translation id="8559762987265718583">Không thể thiết lập kết nối riêng tư với <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> vì ngày và giỠ(<ph name="DATE_AND_TIME" />) trên thiết bị của bạn không đúng.</translation>
-<translation id="856992080682148">Chứng chỉ cho trang web này sẽ hết hạn vào năm 2017 hoặc muá»™n hÆ¡n, đồng thá»i, chuá»—i chứng chỉ này chứa má»™t chứng chỉ được ký bằng SHA-1.</translation>
<translation id="8571890674111243710">Äang dịch trang sang <ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">Chứng chỉ không ghi rõ cơ chế kiểm tra xem chứng chỉ đã bị thu hồi hay chưa.</translation>
<translation id="8620436878122366504">Cha mẹ của bạn chưa phê duyệt trang web</translation>
@@ -694,10 +742,12 @@ Lưu ý! Chế độ ẩn danh <ph name="SHORTCUT_KEY" /> có thể hữu ích v
<translation id="8740359287975076522">Không thể tìm thấy &lt;abbr id="dnsDefinition"&gt;địa chỉ DNS&lt;/abbr&gt; của <ph name="HOST_NAME" />. Äang chẩn Ä‘oán sá»± cố.</translation>
<translation id="8790007591277257123">&amp;Làm lại xóa</translation>
<translation id="8798099450830957504">Mặc định</translation>
+<translation id="8800988563907321413">Äá» xuất ở gần bạn xuất hiện ở đây</translation>
<translation id="8804164990146287819">Chính sách bảo mật</translation>
<translation id="8820817407110198400">Dấu trang</translation>
<translation id="8834246243508017242">Bật Tá»± Ä‘á»™ng Ä‘iá»n bằng Liên hệ…</translation>
<translation id="883848425547221593">Dấu trang Khác</translation>
+<translation id="884264119367021077">Ãịa chỉ giao hàng</translation>
<translation id="884923133447025588">Không tìm thấy cơ chế thu hồi.</translation>
<translation id="885730110891505394">Chia sẻ với Google</translation>
<translation id="8866481888320382733">Lỗi phân tích cú pháp cài đặt chính sách</translation>
@@ -715,18 +765,21 @@ Lưu ý! Chế độ ẩn danh <ph name="SHORTCUT_KEY" /> có thể hữu ích v
<translation id="8971063699422889582">Chứng chỉ của máy chủ đã hết hạn.</translation>
<translation id="8987927404178983737">Tháng</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">Trang web sắp truy cập chứa chương trình độc hại</translation>
<translation id="9001074447101275817">Proxy <ph name="DOMAIN" /> yêu cầu tên ngÆ°á»i dùng và mật khẩu.</translation>
<translation id="901974403500617787">Chỉ chủ sở hữu mới có thể đặt cỠáp dụng cho toàn hệ thống: <ph name="OWNER_EMAIL" />.</translation>
<translation id="9020542370529661692">Trang này đã được dịch sang <ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">Lỗi bảo mật</translation>
<translation id="9038649477754266430">Sử dụng dịch vụ gợi ý để tải trang nhanh hơn</translation>
<translation id="9039213469156557790">Ngoài ra, trang này bao gồm các tài nguyên khác không an toàn. Những tài nguyên này có thể bị ngÆ°á»i khác xem khi Ä‘ang gá»­i và có thể bị kẻ tấn công sá»­a đổi nhằm thay đổi hành vi của trang.</translation>
+<translation id="9040185888511745258">Những kẻ tấn công trên trang <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> có thể đánh lừa bạn cài đặt các chương trình ảnh hưởng đến trải nghiệm duyệt web của bạn (ví dụ: bằng cách thay đổi trang chủ của bạn hoặc hiển thị thêm quảng cáo trên các trang bạn truy cập).</translation>
<translation id="9050666287014529139">Cụm mật khẩu</translation>
<translation id="9065203028668620118">Chỉnh sửa</translation>
<translation id="9068849894565669697">Chá»n màu</translation>
<translation id="9076283476770535406">Trang web có thể có ná»™i dung ngÆ°á»i lá»›n</translation>
-<translation id="9092364396508701805">Trang <ph name="HOST_NAME" /> hiện không hoạt động</translation>
<translation id="9103872766612412690"><ph name="SITE" /> thÆ°á»ng sá»­ dụng mã hóa để bảo vệ thông tin của bạn. Khi Chromium cố gắng kết nối vá»›i <ph name="SITE" /> tại thá»i Ä‘iểm này, trang web đã gá»­i lại thông tin đăng nhập không chính xác và bất thÆ°á»ng. Äiá»u này có thể xảy ra khi kẻ tấn công Ä‘ang cố gắng giả mạo là <ph name="SITE" /> hoặc màn hình đăng nhập Wi-Fi đã làm gián Ä‘oạn kết nối. Thông tin của bạn vẫn an toàn do Chromium đã ngừng kết nối trÆ°á»›c khi bất kỳ dữ liệu nào được trao đổi.</translation>
<translation id="9137013805542155359">Hiển thị văn bản gốc</translation>
+<translation id="9137248913990643158">Vui lòng khởi động và đăng nhập vào Chrome trước khi sử dụng ứng dụng này.</translation>
<translation id="9148507642005240123">&amp;Hoàn tác chỉnh sửa</translation>
<translation id="9157595877708044936">Äang thiết lập...</translation>
<translation id="9170848237812810038">H&amp;oàn tác</translation>
@@ -739,7 +792,6 @@ Lưu ý! Chế độ ẩn danh <ph name="SHORTCUT_KEY" /> có thể hữu ích v
<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="962701380617707048">Nhập ngày hết hạn và CVC cho <ph name="CREDIT_CARD" /> để cập nhật chi tiết thẻ của bạn</translation>
<translation id="969892804517981540">Phiên bản Chính thức</translation>
<translation id="988159990683914416">Phiên bản dành cho Nhà phát triển</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 65564ce8c31..2872f300b30 100644
--- a/chromium/components/strings/components_strings_zh-CN.xtb
+++ b/chromium/components/strings/components_strings_zh-CN.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">é‡æ–°è¿žæŽ¥åˆ° Wi-Fi 网络</translation>
<translation id="1175364870820465910">打å°(&amp;P)...</translation>
<translation id="1181037720776840403">删除</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />è‡ªåŠ¨å‘ Google 报告<ph name="END_WHITEPAPER_LINK" />å¯èƒ½å‡ºçŽ°çš„安全事件的详细信æ¯ã€‚<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">下一步</translation>
<translation id="1201895884277373915">æ¥è‡ªè¯¥ç½‘站的更多内容</translation>
<translation id="1206967143813997005">åˆå§‹ç­¾åä¸æ­£ç¡®</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">é’色</translation>
<translation id="1629803312968146339">您希望 Chrome ä¿å­˜æ­¤ä¿¡ç”¨å¡å—?</translation>
<translation id="1640180200866533862">用户政策</translation>
+<translation id="1640244768702815859">请å°è¯•<ph name="BEGIN_LINK" />访问该网站的首页<ph name="END_LINK" />。</translation>
<translation id="1644184664548287040">网络é…置无效,无法导入。</translation>
<translation id="1644574205037202324">历å²è®°å½•</translation>
<translation id="1645368109819982629">åè®®ä¸å—支æŒ</translation>
<translation id="1676269943528358898"><ph name="SITE" /> 通常会使用加密技术æ¥ä¿æŠ¤æ‚¨çš„ä¿¡æ¯ã€‚Google Chrome 此次å°è¯•è¿žæŽ¥åˆ° <ph name="SITE" /> 时,此网站å‘回了异常的错误凭æ®ã€‚è¿™å¯èƒ½æ˜¯å› ä¸ºæœ‰æ”»å‡»è€…在试图冒充 <ph name="SITE" />,或 Wi-Fi 登录å±å¹•ä¸­æ–­äº†æ­¤æ¬¡è¿žæŽ¥ã€‚请放心,您的信æ¯ä»ç„¶æ˜¯å®‰å…¨çš„,因为 Google Chrome 尚未进行任何数æ®äº¤æ¢ä¾¿åœæ­¢äº†è¿žæŽ¥ã€‚</translation>
+<translation id="168328519870909584"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 上的现有攻击者å¯èƒ½ä¼šè¯•å›¾é€šè¿‡åœ¨æ‚¨çš„设备上安装å±é™©åº”用æ¥çªƒå–或删除您的信æ¯ï¼ˆå¦‚照片ã€å¯†ç ã€é€šè®¯å†…容和信用å¡ä¿¡æ¯ï¼‰ã€‚</translation>
<translation id="168841957122794586">æœåŠ¡å™¨è¯ä¹¦åŒ…å«å¼±åŠ å¯†å¯†é’¥ã€‚</translation>
-<translation id="1701955595840307032">获å–推è内容</translation>
<translation id="1710259589646384581">æ“作系统</translation>
<translation id="1721312023322545264">您需è¦èŽ·å¾—<ph name="NAME" />的许å¯ï¼Œç„¶åŽæ‰èƒ½è®¿é—®æ­¤ç½‘ç«™</translation>
<translation id="1734864079702812349">美国è¿é€šå¡</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">页ç </translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />å°è¯•è¿è¡Œ Windows 网络诊断<ph name="END_LINK" />。</translation>
<translation id="1783075131180517613">请更新您的åŒæ­¥å¯†ç ã€‚</translation>
+<translation id="1787142507584202372">您打开的标签页会显示在此处</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google 安全æµè§ˆåŠŸèƒ½æœ€è¿‘在 <ph name="SITE" /> 上<ph name="BEGIN_LINK" />检测到æ¶æ„软件<ph name="END_LINK" />。平常éžå¸¸å®‰å…¨çš„网站有时也会感染æ¶æ„软件。这些æ¶æ„内容æ¥è‡ªå·²çŸ¥çš„æ¶æ„软件散布网站 <ph name="SUBRESOURCE_HOST" />。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="1821930232296380041">请求或请求å‚数无效</translation>
+<translation id="1834321415901700177">此网站包å«æœ‰å®³ç¨‹åº</translation>
<translation id="1838667051080421715">您正在查看网页的æºä»£ç ã€‚</translation>
<translation id="1871208020102129563">代ç†å·²è®¾ç½®ä¸ºä½¿ç”¨å›ºå®šçš„代ç†æœåŠ¡å™¨ï¼Œè€Œä¸æ˜¯ .pac 脚本网å€ã€‚</translation>
<translation id="1883255238294161206">收起列表</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">移动设备书签</translation>
<translation id="2148716181193084225">今天</translation>
-<translation id="2149973817440762519">修改书签</translation>
<translation id="2154054054215849342">您的网域ä¸æ”¯æŒåŒæ­¥</translation>
<translation id="2166049586286450108">完全的管ç†å‘˜è®¿é—®æƒé™</translation>
<translation id="2166378884831602661">此网站无法æ供安全连接</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">您目å‰æ— æ³•è®¿é—® <ph name="SITE" />,因为此网站使用了 HSTS。网络错误和攻击行为通常是暂时的,因此,此网页ç¨åŽå¯èƒ½å°±ä¼šæ¢å¤æ­£å¸¸ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="2328300916057834155">已忽略索引<ph name="ENTRY_INDEX" />中的无效书签</translation>
<translation id="2354001756790975382">其他书签</translation>
+<translation id="2355395290879513365">攻击者å¯èƒ½èƒ½å¤Ÿçœ‹åˆ°æ‚¨æ­£åœ¨æ­¤ç½‘站上æµè§ˆçš„图片,并通过编辑这些图片让您å—骗。</translation>
<translation id="2359808026110333948">继续</translation>
<translation id="2365563543831475020">于 <ph name="CRASH_TIME" /> 获å–的崩溃报告未上传</translation>
<translation id="2367567093518048410">级别</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">显示未设定值的政策</translation>
<translation id="2396249848217231973">撤消删除(&amp;U)</translation>
<translation id="2455981314101692989">该网页已对该表å•åœç”¨è‡ªåŠ¨å¡«å……功能。</translation>
+<translation id="2460160116472764928">Google 安全æµè§ˆåŠŸèƒ½æœ€è¿‘在 <ph name="SITE" /> 上<ph name="BEGIN_LINK" />检测到æ¶æ„软件<ph name="END_LINK" />。平常éžå¸¸å®‰å…¨çš„网站有时也会感染æ¶æ„软件。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="2463739503403862330">å¡«å……</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />è¿è¡Œç½‘络诊断<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">æœç´¢ç½‘å€æ— æ•ˆã€‚</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">确定è¦ä»ŽåŽ†å²è®°å½•ä¸­åˆ é™¤è¿™äº›é¡µå—?</translation>
<translation id="2677748264148917807">离开</translation>
<translation id="269990154133806163">该æœåŠ¡å™¨æ供的è¯ä¹¦æœªæ ¹æ®è¯ä¹¦é€æ˜Žåº¦æ”¿ç­–公开披露。部分è¯ä¹¦å¿…须符åˆè¿™é¡¹è§„定,以确ä¿è¯ä¹¦å€¼å¾—信任,并ä¿æŠ¤ç”¨æˆ·ä¸ä¼šé­åˆ°æ”»å‡»ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
+<translation id="2702801445560668637">读å–列表</translation>
<translation id="2704283930420550640">值ä¸ç¬¦åˆæ ¼å¼è¦æ±‚。</translation>
<translation id="2704951214193499422">Chromium ç›®å‰æ— æ³•ç¡®è®¤æ‚¨çš„信用å¡ï¼Œè¯·ç¨åŽé‡è¯•ã€‚</translation>
<translation id="2705137772291741111">无法读å–此网站的已ä¿å­˜ï¼ˆç¼“存)副本。</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">您å°è¯•è¿žæŽ¥åˆ° <ph name="DOMAIN" />,但æœåŠ¡å™¨æ供的è¯ä¹¦å·²è¢«å…¶é¢å‘者撤消。在这ç§æƒ…况下,请勿信任æœåŠ¡å™¨æ供的安全凭æ®ã€‚您å¯èƒ½æ­£åœ¨ä¸Žæ”»å‡»è€…进行通信。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="2712173769900027643">请求批准</translation>
<translation id="2713444072780614174">白色</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">您å¯ä»¥åœ¨è®¾ç½®é¡µé¢ä¸­åœç”¨ä»»ä½•é’ˆå¯¹æŸä¸ªè¿žæŽ¥é…置的代ç†ã€‚</translation>
<translation id="2955913368246107853">关闭查找æ </translation>
<translation id="2958431318199492670">网络é…ç½®ä¸ç¬¦åˆ ONC 标准。无法导入é…置的æŸäº›éƒ¨åˆ†ã€‚</translation>
+<translation id="29611076221683977">攻击者å¯èƒ½ä¼šè¯•å›¾é€šè¿‡ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 在您的 Mac 上安装å±é™©ç¨‹åºï¼Œä»¥çªƒå–或删除您的信æ¯ï¼ˆå¦‚照片ã€å¯†ç ã€é€šè®¯å†…容和信用å¡ä¿¡æ¯ï¼‰ã€‚</translation>
<translation id="2969319727213777354">è¦å»ºç«‹å®‰å…¨è¿žæŽ¥ï¼Œæ‚¨çš„时钟设置必须正确。这是因为,网站用于è¯æ˜Žèº«ä»½çš„è¯ä¹¦ä»…在特定时间段有效。由于您设备的时钟ä¸æ­£ç¡®ï¼Œå› æ­¤ Google Chrome 无法验è¯è¿™äº›è¯ä¹¦ã€‚</translation>
<translation id="2972581237482394796">é‡åš(&amp;R)</translation>
<translation id="2985306909656435243">å¯ç”¨åŽï¼ŒChromium 会将您的信用å¡å‰¯æœ¬å­˜å‚¨åœ¨æ­¤è®¾å¤‡ä¸Šï¼Œä»¥åŠ å¿«è¡¨å•å¡«å†™é€Ÿåº¦ã€‚</translation>
@@ -257,7 +266,6 @@
<translation id="3586931643579894722">éšè—详细信æ¯</translation>
<translation id="3587482841069643663">全部</translation>
<translation id="3600246354004376029"><ph name="TITLE" />,<ph name="DOMAIN" />,<ph name="TIME" /></translation>
-<translation id="3609138628363401169">æœåŠ¡å™¨ä¸æ”¯æŒ TLS é‡æ–°å商修正。</translation>
<translation id="36224234498066874">清除æµè§ˆæ•°æ®...</translation>
<translation id="362276910939193118">显示所有历å²è®°å½•</translation>
<translation id="3623476034248543066">显示值</translation>
@@ -268,6 +276,7 @@
<translation id="3655670868607891010">如果您频ç¹é‡åˆ°æ­¤é—®é¢˜ï¼Œè¯·å°è¯•è¿™äº›<ph name="HELP_LINK" />。</translation>
<translation id="3658742229777143148">修订版本</translation>
<translation id="3678029195006412963">无法签署该请求</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>
@@ -277,10 +286,10 @@
<translation id="3712624925041724820">许å¯å·²ç”¨å°½</translation>
<translation id="3714780639079136834">å¼€å¯ç§»åŠ¨æ•°æ®ç½‘络或 Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />检查代ç†æœåŠ¡å™¨ã€é˜²ç«å¢™å’Œ DNS é…ç½®<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">如果您了解自己将é¢ä¸´çš„安全风险,则å¯ä»¥åœ¨å±é™©ç¨‹åºè¢«æ¸…除å‰<ph name="BEGIN_LINK" />访问这个ä¸å®‰å…¨çš„网站<ph name="END_LINK" />。</translation>
<translation id="3739623965217189342">您å¤åˆ¶çš„链接</translation>
<translation id="375403751935624634">由于æœåŠ¡å™¨å‡ºé”™ï¼Œç¿»è¯‘失败。</translation>
<translation id="3759461132968374835">您最近未收到崩溃报告。崩溃报告åœç”¨æ—¶å‘生的崩溃ä¸ä¼šåœ¨æ­¤å¤„显示。</translation>
-<translation id="3788090790273268753">此网站的è¯ä¹¦å°†äºŽ 2016 年过期,并且此网站的è¯ä¹¦é“¾åŒ…å«ä½¿ç”¨ SHA-1 签署的è¯ä¹¦ã€‚</translation>
<translation id="382518646247711829">如果您使用代ç†æœåŠ¡å™¨â€¦</translation>
<translation id="3828924085048779000">密ç è¾“入字段ä¸èƒ½ç•™ç©ºã€‚</translation>
<translation id="3845539888601087042">ç›®å‰æ˜¾ç¤ºçš„是您登录过的设备中的历å²è®°å½•ã€‚<ph name="BEGIN_LINK" />了解详情<ph name="END_LINK" />。</translation>
@@ -302,6 +311,7 @@
<translation id="4058922952496707368">“<ph name="SUBKEY" />â€é”®ï¼š<ph name="ERROR" /></translation>
<translation id="4075732493274867456">客户端和æœåŠ¡å™¨ä¸æ”¯æŒä¸€èˆ¬ SSL å议版本或加密套件。</translation>
<translation id="4079302484614802869">代ç†é…置已设置为使用 .pac 脚本网å€ï¼Œè€Œä¸æ˜¯å›ºå®šçš„代ç†æœåŠ¡å™¨ã€‚</translation>
+<translation id="4098354747657067197">您è¦è®¿é—®çš„网站是欺骗性网站</translation>
<translation id="4103249731201008433">设备åºåˆ—å·æ— æ•ˆ</translation>
<translation id="4103763322291513355">请访问 &lt;strong&gt;chrome:// 政策&lt;/strong&gt;,查看列入黑åå•çš„网å€åˆ—表以åŠæ‚¨çš„系统管ç†å‘˜å¼ºåˆ¶è¦æ±‚执行的其他政策。</translation>
<translation id="4110615724604346410">æ­¤æœåŠ¡å™¨æ— æ³•è¯æ˜Žå…¶æ‰€åœ¨ç½‘域是 <ph name="DOMAIN" />ï¼›æœåŠ¡å™¨çš„安全è¯ä¹¦æœ‰è¯¯ã€‚出现此问题的原因å¯èƒ½æ˜¯é…置有误,或是有攻击者拦截您的连接。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
@@ -319,15 +329,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{æ— }=1{1 个应用 ($1)}=2{2 个应用($1ã€$2)}other{# 个应用($1ã€$2,$3)}}</translation>
<translation id="4220128509585149162">崩溃</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />å°è¯•è¿è¡Œç½‘络诊断<ph name="END_LINK" />。</translation>
+<translation id="4250431568374086873">您与此网站之间建立的连接并éžå®Œå…¨å®‰å…¨</translation>
<translation id="4250680216510889253">å¦</translation>
<translation id="425582637250725228">系统å¯èƒ½ä¸ä¼šä¿å­˜æ‚¨æ‰€åšçš„更改。</translation>
<translation id="4258748452823770588">ç­¾å无效</translation>
<translation id="4269787794583293679">(无用户å)</translation>
+<translation id="4280429058323657511">,到期日期:<ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google 安全æµè§ˆåŠŸèƒ½æœ€è¿‘在 <ph name="SITE" /> 上<ph name="BEGIN_LINK" />å‘现有害程åº<ph name="END_LINK" />。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="4300246636397505754">家长建议</translation>
<translation id="4304224509867189079">登录</translation>
<translation id="432290197980158659">该æœåŠ¡å™¨æ供的è¯ä¹¦ä¸ç¬¦åˆå†…置的预期æ¡ä»¶ã€‚我们针对特定的高安全性网站内置了这些预期æ¡ä»¶ï¼Œä»¥ç¡®ä¿æ‚¨çš„æ•°æ®å®‰å…¨æ— è™žã€‚<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="4325863107915753736">找ä¸åˆ°æ–‡ç« </translation>
+<translation id="4326324639298822553">请检查您的信用å¡åˆ°æœŸæ—¥æœŸï¼Œç„¶åŽé‡è¯•</translation>
<translation id="4331708818696583467">ä¸å®‰å…¨</translation>
+<translation id="4356973930735388585">此网站上的攻击者å¯èƒ½ä¼šè¯•å›¾åœ¨æ‚¨çš„计算机上安装å±é™©ç¨‹åºï¼Œä»¥çªƒå–或删除您的信æ¯ï¼ˆä¾‹å¦‚:照片ã€å¯†ç ã€é€šè®¯å†…容和信用å¡ä¿¡æ¯ï¼‰ã€‚</translation>
<translation id="4372948949327679948">应使用<ph name="VALUE_TYPE" />值。</translation>
<translation id="4381091992796011497">用户å:</translation>
<translation id="4394049700291259645">åœç”¨</translation>
@@ -345,6 +360,7 @@
<translation id="4589078953350245614">您å°è¯•è¿žæŽ¥åˆ° <ph name="DOMAIN" />,但æœåŠ¡å™¨æ供的è¯ä¹¦æ— æ•ˆã€‚<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="4592951414987517459">您与 <ph name="DOMAIN" /> 之间的连接采用新型加密套件进行了加密。</translation>
<translation id="4594403342090139922">撤消删除(&amp;U)</translation>
+<translation id="4619615317237390068">从其他设备打开的标签页</translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">您正在查看的是扩展程åºé¡µé¢ã€‚</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /><ph name="SENTENCE2" /></translation>
@@ -353,10 +369,13 @@
<translation id="4726672564094551039">é‡æ–°åŠ è½½æ”¿ç­–</translation>
<translation id="4728558894243024398">å¹³å°</translation>
<translation id="4744603770635761495">å¯æ‰§è¡Œæ–‡ä»¶è·¯å¾„</translation>
+<translation id="4750917950439032686">您å‘é€ç»™è¿™ä¸ªç½‘站的信æ¯ï¼ˆä¾‹å¦‚密ç æˆ–信用å¡å·ï¼‰ä¸ä¼šå¤–泄。</translation>
<translation id="4756388243121344051">历å²è®°å½•(&amp;H)</translation>
+<translation id="4759118997339041434">付款信æ¯è‡ªåŠ¨å¡«å……功能已åœç”¨</translation>
<translation id="4764776831041365478">网å€ä¸º <ph name="URL" /> 的网页å¯èƒ½æš‚时无法连接,或者它已永久性地移动到了新网å€ã€‚</translation>
<translation id="4771973620359291008">å‘生未知错误。</translation>
<translation id="4800132727771399293">请检查您的到期日期和银行å¡éªŒè¯ç  (CVC),然åŽé‡è¯•</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">网络错误</translation>
<translation id="4816492930507672669">适åˆé¡µé¢å¤§å°</translation>
<translation id="4850886885716139402">视图</translation>
@@ -365,6 +384,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />ã€<ph name="TYPE_2" />ã€<ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{å’Œå¦å¤– 1 个网页}other{å’Œå¦å¤– # 个网页}}</translation>
<translation id="4923417429809017348">翻译æœåŠ¡å™¨å·²å°†æ­¤ç½‘页从æŸç§æœªçŸ¥è¯­è¨€ç¿»è¯‘æˆäº†<ph name="LANGUAGE_LANGUAGE" />。</translation>
+<translation id="4923459931733593730">付款</translation>
<translation id="4926049483395192435">必须指定。</translation>
<translation id="495170559598752135">æ“作</translation>
<translation id="4958444002117714549">展开列表</translation>
@@ -392,7 +412,6 @@
<translation id="5181140330217080051">正在下载</translation>
<translation id="5190835502935405962">书签æ </translation>
<translation id="5199729219167945352">实验</translation>
-<translation id="5199841536747119669">为您推è的内容会显示在此处</translation>
<translation id="5251803541071282808">云端</translation>
<translation id="5277279256032773186">使用 Chrome 办公?ä¼ä¸šå¯ä»¥ä¸ºå…¶å‘˜å·¥ç®¡ç† Chrome 设置。了解详情</translation>
<translation id="5299298092464848405">解æžç­–略时出错</translation>
@@ -400,8 +419,10 @@
<translation id="5308689395849655368">å·²åœç”¨å´©æºƒæŠ¥å‘Šã€‚</translation>
<translation id="5317780077021120954">ä¿å­˜</translation>
<translation id="5327248766486351172">å称</translation>
+<translation id="5337705430875057403"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 上的攻击者å¯èƒ½ä¼šè¯±éª—您åšä¸€äº›å±é™©çš„事情,如安装软件或泄露您的个人信æ¯ï¼ˆå¦‚密ç ã€ç”µè¯å·ç æˆ–信用å¡ä¿¡æ¯ï¼‰ã€‚</translation>
<translation id="5359637492792381994">æ­¤æœåŠ¡å™¨æ— æ³•è¯æ˜Žå…¶æ‰€åœ¨ç½‘域是 <ph name="DOMAIN" />ï¼›æœåŠ¡å™¨çš„安全è¯ä¹¦ç›®å‰æ— æ•ˆã€‚出现此问题的原因å¯èƒ½æ˜¯é…置有误,或是有攻击者拦截您的连接。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="536296301121032821">无法存储策略设置</translation>
+<translation id="5386426401304769735">此网站的è¯ä¹¦é“¾åŒ…å«ä½¿ç”¨ SHA-1 签署的è¯ä¹¦ã€‚</translation>
<translation id="5421136146218899937">清除æµè§ˆæ•°æ®...</translation>
<translation id="5430298929874300616">移除书签</translation>
<translation id="5431657950005405462">找ä¸åˆ°æ‚¨çš„文件</translation>
@@ -422,17 +443,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> 上的嵌入å¼é¡µé¢æ˜¾ç¤ºï¼š</translation>
<translation id="5556459405103347317">é‡æ–°åŠ è½½</translation>
<translation id="5565735124758917034">主动</translation>
+<translation id="5572851009514199876">请å¯åŠ¨å¹¶ç™»å½• Chrome,以便 Chrome 能够检查您是å¦å¯ä»¥è®¿é—®æ­¤ç½‘站。</translation>
+<translation id="5580958916614886209">请检查您的信用å¡åˆ°æœŸæœˆä»½ï¼Œç„¶åŽé‡è¯•</translation>
<translation id="560412284261940334">ä¸æ”¯æŒç®¡ç†</translation>
<translation id="5610142619324316209">检查网络连接</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> 将您é‡å®šå‘的次数过多。</translation>
<translation id="5622887735448669177">è¦ç¦»å¼€æ­¤ç½‘ç«™å—?</translation>
<translation id="5629630648637658800">无法加载策略设置</translation>
<translation id="5631439013527180824">设备管ç†ä»¤ç‰Œæ— æ•ˆ</translation>
+<translation id="5669703222995421982">获å–个性化内容</translation>
+<translation id="5675650730144413517">该网页无法正常è¿ä½œ</translation>
<translation id="5677928146339483299">å·²å±è”½</translation>
<translation id="5694783966845939798">您å°è¯•è¿žæŽ¥åˆ° <ph name="DOMAIN" />,但相应æœåŠ¡å™¨æ供的è¯ä¹¦æ˜¯ä½¿ç”¨é˜²æŠ¤åŠ›è–„弱的签å算法(如 SHA-1)签署的。这æ„味ç€è¯¥æœåŠ¡å™¨æ供的安全凭æ®å¯èƒ½æ˜¯ä¼ªé€ çš„,而且该æœåŠ¡å™¨å¯èƒ½å¹¶ä¸æ˜¯æ‚¨æƒ³è®¿é—®çš„æœåŠ¡å™¨ï¼ˆæ‚¨å¯èƒ½æ­£åœ¨ä¸Žæ”»å‡»è€…通信)。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="5710435578057952990">此网站尚未ç»è¿‡èº«ä»½éªŒè¯ã€‚</translation>
<translation id="5720705177508910913">当å‰ç”¨æˆ·</translation>
-<translation id="572328651809341494">最近打开的标签页</translation>
<translation id="5732392974455271431">您的父æ¯å¯ä¸ºæ‚¨å–消å±è”½æ­¤ç½‘ç«™</translation>
<translation id="5784606427469807560">确认您的信用å¡æ—¶å‡ºçŽ°é—®é¢˜ã€‚请检查您的互è”网连接,然åŽé‡è¯•ã€‚</translation>
<translation id="5785756445106461925">而且,此页中包å«å…¶ä»–ä¸å®‰å…¨çš„资æºã€‚他人能在这些资æºä¼ è¾“过程中进行查看,攻击者也å¯ä»¥ä¿®æ”¹è¿™äº›èµ„æºï¼Œä»Žè€Œæ”¹å˜æ­¤é¡µçš„外观。</translation>
@@ -441,6 +465,7 @@
<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="5843436854350372569">您å°è¯•è®¿é—® <ph name="DOMAIN" />,但æœåŠ¡å™¨æ供的è¯ä¹¦åŒ…å«é˜²æŠ¤åŠ›è–„弱的密钥。攻击者å¯èƒ½å·²ç ´è§£ç§é’¥ï¼Œè€Œä¸”这个æœåŠ¡å™¨å¯èƒ½å¹¶ä¸æ˜¯æ‚¨æƒ³è¦è®¿é—®çš„æœåŠ¡å™¨ï¼ˆæ‚¨å¯èƒ½æ­£åœ¨ä¸Žæ”»å‡»è€…进行通信)。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="5857090052475505287">新文件夹</translation>
<translation id="5869405914158311789">无法访问此网站</translation>
@@ -450,6 +475,7 @@
<translation id="59107663811261420">Google Payments ä¸æ”¯æŒä½¿ç”¨è¿™ç§ç±»åž‹çš„信用å¡å‘此商家付款,请选择其他å¡ã€‚</translation>
<translation id="59174027418879706">å·²å¯ç”¨</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="5966707198760109579">周</translation>
<translation id="5967867314010545767">从历å²è®°å½•ä¸­ç§»é™¤</translation>
<translation id="5975083100439434680">缩å°</translation>
@@ -467,8 +493,11 @@
<translation id="614940544461990577">请试试以下办法:</translation>
<translation id="6151417162996330722">该æœåŠ¡å™¨è¯ä¹¦çš„有效期过长。</translation>
<translation id="6165508094623778733">了解详情</translation>
+<translation id="6177128806592000436">您与此网站之间建立的连接ä¸å®‰å…¨</translation>
<translation id="6203231073485539293">请检查您的互è”网连接是å¦æ­£å¸¸</translation>
<translation id="6218753634732582820">è¦ä»Ž Chromium 中移除地å€å—?</translation>
+<translation id="6251924700383757765">éšç§æƒæ”¿ç­–</translation>
+<translation id="625755898061068298">您已选择针对此网站åœç”¨å®‰å…¨è­¦å‘ŠåŠŸèƒ½ã€‚</translation>
<translation id="6259156558325130047">æ¢å¤é¡ºåºè°ƒæ•´(&amp;R)</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> 书签</translation>
<translation id="6264485186158353794">返回安全连接</translation>
@@ -477,6 +506,7 @@
<translation id="6305205051461490394">无法访问 <ph name="URL" />。</translation>
<translation id="6321917430147971392">请检查您的DNS设置是å¦æ­£ç¡®</translation>
<translation id="6328639280570009161">请å°è¯•åœç”¨ç½‘络è”想查询功能</translation>
+<translation id="6328786501058569169">此网站是欺骗性网站</translation>
<translation id="6337534724793800597">按å称过滤政策</translation>
<translation id="6342069812937806050">刚刚</translation>
<translation id="6345221851280129312">大å°æœªçŸ¥</translation>
@@ -485,6 +515,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{1 æ¡å…¶ä»–建议}other{# æ¡å…¶ä»–建议}}</translation>
<translation id="6387478394221739770">想试试超酷的 Chrome 新功能?欢迎访问 chrome.com/beta,试用我们的测试版ï¼</translation>
<translation id="6389758589412724634">Chromium 在å°è¯•æ˜¾ç¤ºæ­¤ç½‘页时内存ä¸è¶³ã€‚</translation>
+<translation id="6404511346730675251">修改书签</translation>
+<translation id="6410264514553301377">请输入“<ph name="CREDIT_CARD" />â€çš„到期日期和银行å¡éªŒè¯ç  (CVC)</translation>
<translation id="6414888972213066896">您已å‘父亲/æ¯äº²å‘é€è¯·æ±‚,询问其是å¦å…许您访问此网站</translation>
<translation id="6416403317709441254">您目å‰æ— æ³•è®¿é—® <ph name="SITE" />,因为此网站å‘é€çš„凭æ®æ˜¯ä¹±ç ï¼ŒChromium 无法处ç†ã€‚网络错误和攻击行为通常是暂时的,因此,此网页ç¨åŽå¯èƒ½å°±ä¼šæ¢å¤æ­£å¸¸ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="6417515091412812850">无法检查è¯ä¹¦æ˜¯å¦å·²åŠé”€ã€‚</translation>
@@ -494,10 +526,12 @@
<translation id="6458467102616083041">由于默认æœç´¢è¢«æ”¿ç­–åœç”¨ï¼Œæ”¿ç­–值已被忽略。</translation>
<translation id="6462969404041126431">æ­¤æœåŠ¡å™¨æ— æ³•è¯æ˜Žå…¶æ‰€åœ¨ç½‘域是 <ph name="DOMAIN" />ï¼›æœåŠ¡å™¨çš„安全è¯ä¹¦å¯èƒ½é­åˆ°æ’¤æ¶ˆã€‚出现此问题的原因å¯èƒ½æ˜¯é…置有误,或是有攻击者拦截您的连接。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="647261751007945333">设备政策</translation>
+<translation id="6477321094435799029">Chrome 在此网页上检测到了异常代ç ã€‚为ä¿æŠ¤æ‚¨çš„个人信æ¯ï¼ˆä¾‹å¦‚密ç ã€ç”µè¯å·ç å’Œä¿¡ç”¨å¡ä¿¡æ¯ï¼‰ï¼ŒChrome 已将该网页拦截。</translation>
<translation id="6489534406876378309">开始上传崩溃数æ®</translation>
<translation id="6529602333819889595">æ¢å¤åˆ é™¤(&amp;R)</translation>
<translation id="6534179046333460208">实物网建议</translation>
<translation id="6550675742724504774">选项</translation>
+<translation id="6556239504065605927">安全连接</translation>
<translation id="6563469144985748109">您的管ç†å‘˜å°šæœªæ‰¹å‡†æ­¤ç½‘ç«™</translation>
<translation id="6593753688552673085">ä¸åˆ° <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">加密选项</translation>
@@ -506,7 +540,6 @@
<translation id="6644283850729428850">此政策已弃用。</translation>
<translation id="6652240803263749613">æ­¤æœåŠ¡å™¨æ— æ³•è¯æ˜Žå…¶æ‰€åœ¨ç½‘域是 <ph name="DOMAIN" />;您计算机的æ“作系统ä¸ä¿¡ä»»æœåŠ¡å™¨çš„安全è¯ä¹¦ã€‚出现此问题的原因å¯èƒ½æ˜¯é…置有误,或是有攻击者拦截您的连接。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="6656103420185847513">修改文件夹</translation>
-<translation id="6660210980321319655">自动报告时间:<ph name="CRASH_TIME" /></translation>
<translation id="6671697161687535275">è¦ä»Ž Chromium 中移除表å•å¡«å†™å»ºè®®å—?</translation>
<translation id="6685834062052613830">请退出并完æˆè®¾ç½®</translation>
<translation id="6710213216561001401">上一个</translation>
@@ -518,6 +551,7 @@
<translation id="6753269504797312559">政策值</translation>
<translation id="6757797048963528358">您的设备已进入休眠模å¼ã€‚</translation>
<translation id="6778737459546443941">您的父亲/æ¯äº²å°šæœªæ‰¹å‡†æ­¤ç½‘ç«™</translation>
+<translation id="6810899417690483278">自定义 ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">网å€ä¸º <ph name="URL" /> 的网页目å‰æ— æ³•è®¿é—®ã€‚该网页å¯èƒ½å·²è¶…载或关闭进行维护。</translation>
<translation id="6831043979455480757">翻译</translation>
@@ -538,6 +572,8 @@
<translation id="7012363358306927923">中国银è”</translation>
<translation id="7012372675181957985">您的 Google å¸å·åœ¨ <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> 上å¯èƒ½æœ‰å…¶ä»–å½¢å¼çš„æµè§ˆè®°å½•</translation>
<translation id="7029809446516969842">密ç </translation>
+<translation id="7064851114919012435">è”系信æ¯</translation>
+<translation id="7079718277001814089">此网站包å«æ¶æ„软件</translation>
<translation id="7087282848513945231">县/郡</translation>
<translation id="7088615885725309056">å¾€å‰</translation>
<translation id="7090678807593890770">请在 Google 中æœç´¢â€œ<ph name="LINK" />â€</translation>
@@ -552,6 +588,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> ä¸ç¬¦åˆç›¸å…³å®‰å…¨æ ‡å‡†ã€‚</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />详细了解<ph name="END_LINK" />此问题。</translation>
<translation id="7219179957768738017">该连接使用 <ph name="SSL_VERSION" />。</translation>
+<translation id="724691107663265825">您è¦è®¿é—®çš„网站包å«æ¶æ„软件</translation>
<translation id="724975217298816891">输入“<ph name="CREDIT_CARD" />â€çš„过期日期和银行å¡éªŒè¯ç  (CVC) 以更新您的信用å¡è¯¦æƒ…。在您确认åŽï¼Œæ‚¨çš„信用å¡è¯¦æƒ…将与此网站共享。</translation>
<translation id="725866823122871198">您计算机的日期和时间(<ph name="DATE_AND_TIME" />)ä¸æ­£ç¡®ï¼Œå› æ­¤æ— æ³•ä¸Ž <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> 建立ç§å¯†è¿žæŽ¥ã€‚</translation>
<translation id="7269802741830436641">此网页包å«é‡å®šå‘循环</translation>
@@ -607,6 +644,7 @@
<translation id="7658239707568436148">å–消</translation>
<translation id="7667346355482952095">返回的政策令牌为空或与当å‰ä»¤ç‰Œä¸åŒ¹é…</translation>
<translation id="7668654391829183341">未知设备</translation>
+<translation id="7669271284792375604">此网站上的攻击者å¯èƒ½ä¼šè¯•å›¾è¯±éª—您安装有æŸæµè§ˆä½“验的程åºï¼ˆä¾‹å¦‚:通过更改您的主页或在您访问的网站上显示é¢å¤–的广告)。</translation>
<translation id="7674629440242451245">想试试超酷的 Chrome 新功能?欢迎访问 chrome.com/dev,试用我们的测试版ï¼</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />继续å‰å¾€<ph name="SITE" />(ä¸å®‰å…¨ï¼‰<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">无法从缓存中加载此网站</translation>
@@ -622,14 +660,17 @@
<translation id="7800304661137206267">该连接是使用 <ph name="CIPHER" /> 进行加密的,åŒæ—¶ä½¿ç”¨ <ph name="MAC" /> 进行讯æ¯èº«ä»½éªŒè¯å¹¶ä½¿ç”¨ <ph name="KX" /> 作为密钥交æ¢æœºåˆ¶ã€‚</translation>
<translation id="780301667611848630">ä¸ç”¨äº†ï¼Œè°¢è°¢</translation>
<translation id="7805768142964895445">状æ€</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">从 Chrome 中移除表å•å»ºè®®ï¼Ÿ</translation>
<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="7887683347370398519">请检查您的银行å¡éªŒè¯ç  (CVC),然åŽé‡è¯•</translation>
<translation id="7912024687060120840">在以下文件夹中:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">æœåŠ¡å™¨çš„è¯ä¹¦å°šæœªç”Ÿæ•ˆã€‚</translation>
<translation id="7942349550061667556">红色</translation>
+<translation id="7947285636476623132">请检查您的信用å¡åˆ°æœŸå¹´ä»½ï¼Œç„¶åŽé‡è¯•</translation>
<translation id="7951415247503192394">(32 ä½ï¼‰</translation>
<translation id="7956713633345437162">移动设备书签</translation>
<translation id="7961015016161918242">一律ä¸</translation>
@@ -637,7 +678,10 @@
<translation id="7983301409776629893">一律将<ph name="ORIGINAL_LANGUAGE" />翻译æˆ<ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">未指定</translation>
<translation id="8012647001091218357">我们暂时无法与您父æ¯å–å¾—è”系,请é‡è¯•ã€‚</translation>
+<translation id="8025119109950072390">此网站上的攻击者å¯èƒ½ä¼šè¯±éª—您åšå‡ºä¸€äº›è¯¸å¦‚安装软件或泄露个人信æ¯ï¼ˆä¾‹å¦‚:密ç ã€ç”µè¯å·ç æˆ–信用å¡ä¿¡æ¯ï¼‰ä¹‹ç±»çš„å±é™©äº‹æƒ…。</translation>
+<translation id="803030522067524905">Google 安全æµè§ˆåŠŸèƒ½æœ€è¿‘在 <ph name="SITE" /> 上检测到网上诱骗行为。网上诱骗网站会å‡å†’其他网站æ¥æ¬ºéª—您。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="8034522405403831421">此网页的æºè¯­è¨€ä¸º<ph name="SOURCE_LANGUAGE" />,è¦å°†å…¶ç¿»è¯‘æˆ<ph name="TARGET_LANGUAGE" />å—?</translation>
+<translation id="8041089156583427627">å‘é€å馈</translation>
<translation id="8088680233425245692">无法查看文章。</translation>
<translation id="8089520772729574115">å°äºŽ 1 MB</translation>
<translation id="8091372947890762290">正等待在æœåŠ¡å™¨ä¸Šæ¿€æ´»</translation>
@@ -648,9 +692,11 @@
<translation id="8150722005171944719">æ— æ³•è¯»å– <ph name="URL" /> 上的文件。该文件å¯èƒ½å·²é­åˆ°åˆ é™¤ã€ç§»åŠ¨ï¼Œæˆ–者文件æƒé™ä¸å…许进行访问。</translation>
<translation id="8194797478851900357">撤消移动(&amp;U)</translation>
<translation id="8201077131113104583">ID 为“<ph name="EXTENSION_ID" />â€çš„扩展程åºçš„更新网å€æ— æ•ˆã€‚</translation>
+<translation id="8202097416529803614">订å•æ‘˜è¦</translation>
<translation id="8218327578424803826">分é…çš„ä½ç½®ï¼š</translation>
<translation id="8225771182978767009">设置此计算机的用户已选择å±è”½æ­¤ç½‘站。</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />ã€<ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">攻击者å¯èƒ½ä¼šè¯•å›¾é€šè¿‡ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 在您的计算机上安装å±é™©ç¨‹åºï¼Œä»¥çªƒå–或删除您的信æ¯ï¼ˆå¦‚照片ã€å¯†ç ã€é€šè®¯å†…容和信用å¡ä¿¡æ¯ï¼‰ã€‚</translation>
<translation id="8241707690549784388">您所查找的网页è¦ä½¿ç”¨å·²è¾“入的信æ¯ã€‚返回此页å¯èƒ½éœ€è¦é‡å¤å·²è¿›è¡Œçš„所有æ“作。是å¦è¦ç»§ç»­æ“作?</translation>
<translation id="8249320324621329438">最åŽä¸€æ¬¡æŠ“å–时间:</translation>
<translation id="8261506727792406068">删除</translation>
@@ -659,11 +705,13 @@
<translation id="8294431847097064396">æ¥æº</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="8349305172487531364">书签æ </translation>
<translation id="8363502534493474904">关闭飞行模å¼</translation>
<translation id="8364627913115013041">未设置。</translation>
<translation id="8380941800586852976">å±é™©</translation>
<translation id="8382348898565613901">您最近访问过的书签会显示在此处</translation>
+<translation id="8398259832188219207">崩溃报告的上传时间:<ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">崩溃次数(<ph name="CRASH_COUNT" /> 次)</translation>
<translation id="8412392972487953978">您两次输入的密ç å¿…须相åŒã€‚</translation>
<translation id="8428213095426709021">设置</translation>
@@ -675,9 +723,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> çš„å“应时间过长。</translation>
<translation id="852346902619691059">æ­¤æœåŠ¡å™¨æ— æ³•è¯æ˜Žå…¶æ‰€åœ¨ç½‘域是 <ph name="DOMAIN" />;您设备的æ“作系统ä¸ä¿¡ä»»æœåŠ¡å™¨çš„安全è¯ä¹¦ã€‚出现此问题的原因å¯èƒ½æ˜¯é…置有误,或是有攻击者拦截您的连接。<ph name="BEGIN_LEARN_MORE_LINK" />了解详情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="8530504477309582336">Google Payments ä¸æ”¯æŒè¿™ç§ç±»åž‹çš„信用å¡ï¼Œè¯·é€‰æ‹©å…¶ä»–å¡ã€‚</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="8553075262323480129">系统无法确定该网页的语言,因此无法进行翻译。</translation>
<translation id="8559762987265718583">您设备的日期和时间(<ph name="DATE_AND_TIME" />)ä¸æ­£ç¡®ï¼Œå› æ­¤æ— æ³•ä¸Ž <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> 建立ç§å¯†è¿žæŽ¥ã€‚</translation>
-<translation id="856992080682148">此网站的è¯ä¹¦å°†äºŽ 2017 年或之åŽè¿‡æœŸï¼Œå¹¶ä¸”此网站的è¯ä¹¦é“¾åŒ…å«ä½¿ç”¨ SHA-1 签署的è¯ä¹¦ã€‚</translation>
<translation id="8571890674111243710">正在将网页翻译æˆ<ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">è¯ä¹¦æœªæŒ‡å®šç”¨äºŽæ£€æŸ¥æ˜¯å¦å·²åŠé”€è¯ä¹¦çš„机制。</translation>
<translation id="8620436878122366504">您的父æ¯å°šæœªæ‰¹å‡†æ­¤è¯·æ±‚</translation>
@@ -690,10 +738,12 @@
<translation id="8740359287975076522">无法找到 <ph name="HOST_NAME" /> çš„ &lt;abbr id="dnsDefinition"&gt;DNS 地å€&lt;/abbr&gt;。正在诊断该问题。</translation>
<translation id="8790007591277257123">æ¢å¤åˆ é™¤(&amp;R)</translation>
<translation id="8798099450830957504">默认</translation>
+<translation id="8800988563907321413">此处将显示系统建议您æµè§ˆçš„附近网页</translation>
<translation id="8804164990146287819">éšç§æƒæ”¿ç­–</translation>
<translation id="8820817407110198400">书签</translation>
<translation id="8834246243508017242">å…许利用通讯录中的信æ¯è‡ªåŠ¨å¡«å………</translation>
<translation id="883848425547221593">其他书签</translation>
+<translation id="884264119367021077">é€è´§åœ°å€</translation>
<translation id="884923133447025588">未找到任何åŠé”€æœºåˆ¶ã€‚</translation>
<translation id="885730110891505394">与 Google 分享</translation>
<translation id="8866481888320382733">解æžç­–略设置时出错</translation>
@@ -711,18 +761,21 @@
<translation id="8971063699422889582">æœåŠ¡å™¨çš„è¯ä¹¦å·²è¿‡æœŸã€‚</translation>
<translation id="8987927404178983737">月</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">您è¦è®¿é—®çš„网站包å«æœ‰å®³ç¨‹åº</translation>
<translation id="9001074447101275817">ä»£ç† (<ph name="DOMAIN" />) è¦æ±‚æ供用户å和密ç ã€‚</translation>
<translation id="901974403500617787">应用于整个系统的设置åªèƒ½ç”±ä»¥ä¸‹æ‰€æœ‰è€…设定:<ph name="OWNER_EMAIL" />。</translation>
<translation id="9020542370529661692">已将此网页内容翻译æˆ<ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">安全错误</translation>
<translation id="9038649477754266430">使用è”想查询æœåŠ¡æ›´å¿«é€Ÿåœ°åŠ è½½ç½‘页</translation>
<translation id="9039213469156557790">而且,此页中包å«å…¶ä»–ä¸å®‰å…¨çš„资æºã€‚他人能在这些资æºä¼ è¾“过程中进行查看,而攻击者å¯ä»¥ä¿®æ”¹è¿™äº›èµ„æºï¼Œä»Žè€Œæ”¹å˜æ­¤é¡µçš„行为。</translation>
+<translation id="9040185888511745258"><ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 上的攻击者å¯èƒ½ä¼šè¯•å›¾éª—您安装有æŸæµè§ˆä½“验的程åºï¼ˆä¾‹å¦‚更改您的主页或在您访问的网站上显示é¢å¤–的广告)。</translation>
<translation id="9050666287014529139">密ç </translation>
<translation id="9065203028668620118">修改</translation>
<translation id="9068849894565669697">选择颜色</translation>
<translation id="9076283476770535406">此网站å¯èƒ½åŒ…å«æˆäººå†…容</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> 网页无法正常è¿ä½œ</translation>
<translation id="9103872766612412690"><ph name="SITE" /> 通常会使用加密技术æ¥ä¿æŠ¤æ‚¨çš„ä¿¡æ¯ã€‚Chromium 此次å°è¯•è¿žæŽ¥åˆ° <ph name="SITE" /> 时,此网站å‘回了异常的错误凭æ®ã€‚è¿™å¯èƒ½æ˜¯å› ä¸ºæœ‰æ”»å‡»è€…在试图冒充 <ph name="SITE" />,或 Wi-Fi 登录å±å¹•ä¸­æ–­äº†æ­¤æ¬¡è¿žæŽ¥ã€‚请放心,您的信æ¯ä»ç„¶æ˜¯å®‰å…¨çš„,因为 Chromium 尚未进行任何数æ®äº¤æ¢ä¾¿åœæ­¢äº†è¿žæŽ¥ã€‚</translation>
<translation id="9137013805542155359">显示原始网页</translation>
+<translation id="9137248913990643158">在使用此应用å‰ï¼Œè¯·å…ˆå¯åŠ¨å¹¶ç™»å½• Chrome。</translation>
<translation id="9148507642005240123">撤消修改(&amp;U)</translation>
<translation id="9157595877708044936">正在设置...</translation>
<translation id="9170848237812810038">撤消(&amp;U)</translation>
@@ -735,7 +788,6 @@
<translation id="935608979562296692">清除表å•å†…容</translation>
<translation id="939736085109172342">新建文件夹</translation>
<translation id="941721044073577244">您似乎无æƒè®¿é—®æ­¤ç½‘ç«™</translation>
-<translation id="962701380617707048">输入“<ph name="CREDIT_CARD" />â€çš„过期日期和银行å¡éªŒè¯ç  (CVC) 以更新您的信用å¡è¯¦æƒ…</translation>
<translation id="969892804517981540">æ­£å¼ç‰ˆæœ¬</translation>
<translation id="988159990683914416">å¼€å‘者内部版本</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 a9e0b1c4ba7..a07b23d917a 100644
--- a/chromium/components/strings/components_strings_zh-TW.xtb
+++ b/chromium/components/strings/components_strings_zh-TW.xtb
@@ -24,6 +24,7 @@
<translation id="1161325031994447685">é‡æ–°é€£ç·šè‡³ Wi-Fi 網路</translation>
<translation id="1175364870820465910">列å°(&amp;P)...</translation>
<translation id="1181037720776840403">移除</translation>
+<translation id="1184214524891303587"><ph name="BEGIN_WHITEPAPER_LINK" />è‡ªå‹•å‘ Google 回報<ph name="END_WHITEPAPER_LINK" />疑似安全性事件的詳細資料。<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="1201402288615127009">繼續</translation>
<translation id="1201895884277373915">這個網站的更多內容</translation>
<translation id="1206967143813997005">縮寫簽å無效</translation>
@@ -64,12 +65,13 @@
<translation id="161042844686301425">é’色</translation>
<translation id="1629803312968146339">您希望 Chrome 儲存這張信用å¡å—Žï¼Ÿ</translation>
<translation id="1640180200866533862">使用者政策</translation>
+<translation id="1640244768702815859">請嘗試<ph name="BEGIN_LINK" />造訪網站首é <ph name="END_LINK" />。</translation>
<translation id="1644184664548287040">網路設定無效,無法匯入。</translation>
<translation id="1644574205037202324">æ­·å²ç´€éŒ„</translation>
<translation id="1645368109819982629">ä¸æ”¯æ´çš„通訊å”定</translation>
<translation id="1676269943528358898"><ph name="SITE" /> 通常使用加密方å¼ä¿è­·æ‚¨çš„資訊。但 Google Chrome 這次嘗試連線到 <ph name="SITE" /> 時,該網站傳回了異常且錯誤的憑證。這å¯èƒ½æ˜¯å› ç‚ºæœ‰æ”»æ“Šè€…ä¼åœ–å½è£æˆ <ph name="SITE" />,或是å—到 Wi-Fi 登入畫é¢å½±éŸ¿è€Œé€ æˆé€£ç·šä¸­æ–·ã€‚ä¸éŽè«‹æ”¾å¿ƒï¼ŒGoogle Chrome å·²åŠæ™‚åœæ­¢é€£ç·šï¼Œä¸¦æœªå‚³è¼¸ä»»ä½•è³‡æ–™ï¼Œå› æ­¤æ‚¨çš„資訊ä»ç„¶å®‰å…¨ç„¡è™žã€‚</translation>
+<translation id="168328519870909584">攻擊者目å‰å¯èƒ½æœƒè©¦åœ–é€éŽ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 在您的è£ç½®ä¸Šå®‰è£å±éšªçš„應用程å¼ï¼Œè—‰æ­¤ç«Šå–或刪除您的資訊 (例如相片ã€å¯†ç¢¼ã€éƒµä»¶æˆ–信用å¡è³‡æ–™)。</translation>
<translation id="168841957122794586">伺æœå™¨æ†‘è­‰å«æœ‰é˜²è­·åŠ›è–„弱的加密編譯金鑰。</translation>
-<translation id="1701955595840307032">å–得推薦內容</translation>
<translation id="1710259589646384581">作業系統</translation>
<translation id="1721312023322545264">ä½ å¿…é ˆç²å¾—<ph name="NAME" />授權,æ‰èƒ½é€ è¨ªé€™å€‹ç¶²ç«™</translation>
<translation id="1734864079702812349">Amex</translation>
@@ -79,8 +81,11 @@
<translation id="1753706481035618306">é ç¢¼</translation>
<translation id="1768211456781949159"><ph name="BEGIN_LINK" />嘗試執行 Windows 網路診斷<ph name="END_LINK" />。</translation>
<translation id="1783075131180517613">請更新您的åŒæ­¥é€šé—œå¯†èªžã€‚</translation>
+<translation id="1787142507584202372">這裡會顯示你最近開啟的分é </translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
+<translation id="1803678881841855883">Google 安全ç€è¦½åŠŸèƒ½æœ€è¿‘在 <ph name="SITE" /> 上<ph name="BEGIN_LINK" />åµæ¸¬åˆ°æƒ¡æ„軟體<ph name="END_LINK" />。å³ä½¿æ˜¯å¹³å¸¸å¯ä»¥å®‰å…¨ä½¿ç”¨çš„網站,有時也會é­åˆ°æƒ¡æ„軟體感染。這些惡æ„內容來自已知的惡æ„軟體散佈網站 <ph name="SUBRESOURCE_HOST" />。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="1821930232296380041">è¦æ±‚或è¦æ±‚åƒæ•¸ç„¡æ•ˆ</translation>
+<translation id="1834321415901700177">這個網站å«æœ‰æœ‰å®³ç¨‹å¼</translation>
<translation id="1838667051080421715">ç›®å‰é¡¯ç¤ºçš„是網é åŽŸå§‹ç¢¼ã€‚</translation>
<translation id="1871208020102129563">Proxy 設定為使用固定的 Proxy 伺æœå™¨ï¼Œè€Œéž .pac 指令碼網å€ã€‚</translation>
<translation id="1883255238294161206">收åˆæ¸…å–®</translation>
@@ -107,7 +112,6 @@
<translation id="2128531968068887769">Native Client</translation>
<translation id="213826338245044447">行動版書籤</translation>
<translation id="2148716181193084225">今天</translation>
-<translation id="2149973817440762519">編輯書籤</translation>
<translation id="2154054054215849342">你的網域無法使用åŒæ­¥åŠŸèƒ½</translation>
<translation id="2166049586286450108">完整管ç†å“¡å­˜å–權</translation>
<translation id="2166378884831602661">這個網站無法æ供安全連線</translation>
@@ -128,6 +132,7 @@
<translation id="2318774815570432836">ç›®å‰ç„¡æ³•é€ è¨ª <ph name="SITE" />,因為這個網站使用 HSTS。網路錯誤和攻擊行為通常是暫時性的,因此這個網é å¯èƒ½ç¨å¾Œå°±æœƒæ¢å¾©æ­£å¸¸ç‹€æ…‹ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2328300916057834155">已忽略索引「<ph name="ENTRY_INDEX" />ã€çš„無效書籤</translation>
<translation id="2354001756790975382">其他書籤</translation>
+<translation id="2355395290879513365">攻擊者å¯èƒ½æœƒçœ‹åˆ°ä½ æ­£åœ¨é€™å€‹ç¶²ç«™ä¸Šç€è¦½çš„圖片,並以修改圖片內容的方å¼è®“ä½ å—騙。</translation>
<translation id="2359808026110333948">繼續</translation>
<translation id="2365563543831475020">當機報告擷å–時間:<ph name="CRASH_TIME" /> (尚未上傳)</translation>
<translation id="2367567093518048410">等級</translation>
@@ -138,6 +143,7 @@
<translation id="2392959068659972793">顯示尚未設定任何值的政策</translation>
<translation id="2396249848217231973">復原刪除(&amp;U)</translation>
<translation id="2455981314101692989">這個網é å·²åœç”¨é€™å€‹è¡¨å–®çš„自動填入功能。</translation>
+<translation id="2460160116472764928">Google 安全ç€è¦½åŠŸèƒ½æœ€è¿‘在 <ph name="SITE" /> 上<ph name="BEGIN_LINK" />åµæ¸¬åˆ°æƒ¡æ„軟體<ph name="END_LINK" />。å³ä½¿æ˜¯å¹³å¸¸å¯ä»¥å®‰å…¨ä½¿ç”¨çš„網站,有時也會é­åˆ°æƒ¡æ„軟體感染。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2463739503403862330">å¡«å…¥</translation>
<translation id="2467694685043708798"><ph name="BEGIN_LINK" />執行網路診斷<ph name="END_LINK" /></translation>
<translation id="2479410451996844060">無效的æœå°‹ç¶²å€ã€‚</translation>
@@ -161,6 +167,7 @@
<translation id="2674170444375937751">確定è¦å¾žæ‚¨çš„紀錄中刪除這些網é å—Žï¼Ÿ</translation>
<translation id="2677748264148917807">離開</translation>
<translation id="269990154133806163">這個伺æœå™¨çš„憑證並未根據憑證é€æ˜ŽåŒ–政策å°å¤–公開。部分憑證必須符åˆé€™é …è¦å®šï¼Œä»¥ç¢ºä¿æ†‘證值得信任,ä¸æœƒè®“使用者é­åˆ°æ”»æ“Šã€‚<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="2702801445560668637">閱讀清單</translation>
<translation id="2704283930420550640">政策值格å¼ä¸ç¬¦ã€‚</translation>
<translation id="2704951214193499422">Chromium ç›®å‰ç„¡æ³•é©—證您的信用å¡ï¼Œè«‹ç¨å¾Œå†è©¦ã€‚</translation>
<translation id="2705137772291741111">已儲存 (å¿«å–) 這個網站的複本,但無法讀å–。</translation>
@@ -168,6 +175,7 @@
<translation id="2712118517637785082">你嘗試連上 <ph name="DOMAIN" />,但伺æœå™¨çš„憑證已é­åˆ°æ ¸ç™¼å–®ä½æ’¤éŠ·ã€‚在這種情æ³ä¸‹ï¼Œè«‹å‹¿ä¿¡ä»»ä¼ºæœå™¨æ供的安全性憑證。你的連線å°è±¡å¯èƒ½æ˜¯æ”»æ“Šè€…的電腦。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="2712173769900027643">è¦æ±‚權é™</translation>
<translation id="2713444072780614174">白色</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>
@@ -186,6 +194,7 @@
<translation id="2948083400971632585">您å¯ä»¥åœ¨è¨­å®šé é¢åœç”¨ä»»ä½•ç‚ºé€£ç·šè¨­ç½®çš„ Proxy。</translation>
<translation id="2955913368246107853">關閉æœå°‹åˆ—</translation>
<translation id="2958431318199492670">網路設定未éµå¾ª ONC 標準,系統å¯èƒ½ç„¡æ³•åŒ¯å…¥éƒ¨åˆ†è¨­å®šã€‚</translation>
+<translation id="29611076221683977">攻擊者目å‰å¯èƒ½æœƒè©¦åœ–é€éŽ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 在您的 Mac 上安è£å±éšªç¨‹å¼ï¼Œè—‰æ­¤ç«Šå–或刪除您的資訊 (例如相片ã€å¯†ç¢¼ã€éƒµä»¶æˆ–信用å¡è³‡æ–™)。</translation>
<translation id="2969319727213777354">您必須正確設定時é˜ï¼Œæ‰èƒ½å»ºç«‹å®‰å…¨é€£ç·šã€‚這是因為網站驗證身分時所使用的憑證僅於特定一段時間內有效。由於您è£ç½®çš„時é˜ä¸æ­£ç¢ºï¼Œå› æ­¤ Google Chrome 無法驗證這些憑證。</translation>
<translation id="2972581237482394796">é‡åš(&amp;R)</translation>
<translation id="2985306909656435243">啟用後,Chromium 會將您的信用å¡è³‡æ–™å„²å­˜åœ¨é€™å€‹è£ç½®ä¸Šï¼Œä»¥åŠ å¿«è¡¨å–®å¡«å¯«é€Ÿåº¦ã€‚</translation>
@@ -259,7 +268,6 @@
<translation id="3586931643579894722">éš±è—詳細資訊</translation>
<translation id="3587482841069643663">全部</translation>
<translation id="3600246354004376029"><ph name="TITLE" />,<ph name="DOMAIN" />,<ph name="TIME" /></translation>
-<translation id="3609138628363401169">伺æœå™¨ä¸¦ä¸æ”¯æ´ TLS é‡æ–°äº¤æ¶‰æ“´å……功能。</translation>
<translation id="36224234498066874">清除ç€è¦½è³‡æ–™...</translation>
<translation id="362276910939193118">顯示完整記錄</translation>
<translation id="3623476034248543066">顯示政策值</translation>
@@ -271,6 +279,7 @@
<translation id="3655670868607891010">如果您經常看到這個é é¢ï¼Œè«‹åƒè€ƒé€™äº›èªªæ˜Žï¼š<ph name="HELP_LINK" />。</translation>
<translation id="3658742229777143148">修訂版本</translation>
<translation id="3678029195006412963">無法簽署è¦æ±‚</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>
@@ -280,10 +289,10 @@
<translation id="3712624925041724820">授權已用盡</translation>
<translation id="3714780639079136834">開啟行動數據或 Wi-Fi</translation>
<translation id="3717027428350673159"><ph name="BEGIN_LINK" />檢查 Proxyã€é˜²ç«ç‰†å’Œ DNS 設定<ph name="END_LINK" /></translation>
+<translation id="3736520371357197498">如果您瞭解安全性風險,也å¯ä»¥é¸æ“‡åœ¨å±éšªç¨‹å¼å°šæœªé­åˆ°ç§»é™¤çš„狀態下<ph name="BEGIN_LINK" />造訪這個ä¸å®‰å…¨çš„網站<ph name="END_LINK" />。</translation>
<translation id="3739623965217189342">您複製的連çµ</translation>
<translation id="375403751935624634">伺æœå™¨éŒ¯èª¤ï¼Œç¿»è­¯ä½œæ¥­å¤±æ•—。</translation>
<translation id="3759461132968374835">最近沒有收到當機資訊。當機回報功能åœç”¨æ™‚發生的當機ä¸æœƒåˆ—在這裡。</translation>
-<translation id="3788090790273268753">這個網站的憑證將於 2016 年到期,且憑證éˆçµåŒ…å«ä½¿ç”¨ SHA-1 進行簽署的憑證。</translation>
<translation id="382518646247711829">如果您使用 Proxy 伺æœå™¨...</translation>
<translation id="3828924085048779000">通關密語欄ä½ä¸å¾—留空。</translation>
<translation id="3845539888601087042">ç›®å‰é¡¯ç¤ºçš„æ­·å²ç´€éŒ„來æºåŒ…括您已登入帳戶的所有è£ç½®ã€‚<ph name="BEGIN_LINK" />瞭解詳情<ph name="END_LINK" /></translation>
@@ -305,6 +314,7 @@
<translation id="4058922952496707368">éµã€Œ<ph name="SUBKEY" />ã€ï¼š<ph name="ERROR" /></translation>
<translation id="4075732493274867456">用戶端和伺æœå™¨ä¸æ”¯æ´ä¸€èˆ¬ SSL 通訊å”定版本或加密套件。</translation>
<translation id="4079302484614802869">Proxy 設定已設為使用 .pac 指令碼網å€ï¼Œè€Œéžå›ºå®šçš„ Proxy 伺æœå™¨ã€‚</translation>
+<translation id="4098354747657067197">您å³å°‡å‰å¾€è©é¨™ç¶²ç«™</translation>
<translation id="4103249731201008433">è£ç½®åºè™Ÿç„¡æ•ˆ</translation>
<translation id="4103763322291513355">è«‹å‰å¾€ &lt;strong&gt;chrome://policy&lt;/strong&gt; 查看列入黑å單的網å€æ¸…單,以åŠå…¶ä»–系統管ç†å“¡å¼·åˆ¶åŸ·è¡Œçš„政策。</translation>
<translation id="4110615724604346410">這個伺æœå™¨ç„¡æ³•è­‰æ˜Žæ‰€åœ¨ç¶²åŸŸæ˜¯ <ph name="DOMAIN" />;伺æœå™¨çš„安全性憑證å«æœ‰éŒ¯èª¤ã€‚這å¯èƒ½æ˜¯å› ç‚ºè¨­å®šéŒ¯èª¤ï¼Œæˆ–是有攻擊者攔截你的連線。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
@@ -322,15 +332,20 @@
<translation id="4206349416402437184">{COUNT,plural, =0{ç„¡}=1{1 å€‹æ‡‰ç”¨ç¨‹å¼ ($1)}=2{2 å€‹æ‡‰ç”¨ç¨‹å¼ ($1ã€$2)}other{# å€‹æ‡‰ç”¨ç¨‹å¼ ($1ã€$2ã€$3)}}</translation>
<translation id="4220128509585149162">當機</translation>
<translation id="4226937834893929579"><ph name="BEGIN_LINK" />嘗試執行網路診斷<ph name="END_LINK" />。</translation>
+<translation id="4250431568374086873">你與這個網站的連線å¯èƒ½æœ‰å®‰å…¨æ¼æ´ž</translation>
<translation id="4250680216510889253">å¦</translation>
<translation id="425582637250725228">系統å¯èƒ½ä¸æœƒå„²å­˜æ‚¨æ‰€åšçš„變更。</translation>
<translation id="4258748452823770588">ç°½å有誤</translation>
<translation id="4269787794583293679">(沒有使用者å稱)</translation>
+<translation id="4280429058323657511">,到期日:<ph name="EXPIRATION_DATE_ABBR" /></translation>
+<translation id="4295944351946828969">Google 安全ç€è¦½åŠŸèƒ½æœ€è¿‘在 <ph name="SITE" /> 上<ph name="BEGIN_LINK" />找到有害的應用程å¼<ph name="END_LINK" />。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4300246636397505754">家長建議</translation>
<translation id="4304224509867189079">登入</translation>
<translation id="432290197980158659">這個伺æœå™¨çš„憑證ä¸ç¬¦åˆå…§å»ºçš„é æœŸæ¢ä»¶ã€‚我們é‡å°ç‰¹å®šçš„高安全性網站內建了這些é æœŸæ¢ä»¶ï¼Œä»¥ç¢ºä¿ä½ çš„資料安全無虞。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4325863107915753736">找ä¸åˆ°æ–‡ç« </translation>
+<translation id="4326324639298822553">請檢查信用å¡åˆ°æœŸæ—¥ï¼Œç„¶å¾Œå†è©¦ä¸€æ¬¡</translation>
<translation id="4331708818696583467">ä¸å®‰å…¨</translation>
+<translation id="4356973930735388585">攻擊者å¯èƒ½æœƒè©¦åœ–é€éŽé€™å€‹ç¶²ç«™åœ¨ä½ çš„電腦上安è£å±éšªç¨‹å¼ï¼Œè—‰æ­¤ç«Šå–或刪除你的資訊 (例如相片ã€å¯†ç¢¼ã€éƒµä»¶å’Œä¿¡ç”¨å¡è³‡æ–™)。</translation>
<translation id="4372948949327679948">é æœŸçš„「<ph name="VALUE_TYPE" />ã€å€¼ã€‚</translation>
<translation id="4381091992796011497">使用者å稱:</translation>
<translation id="4394049700291259645">åœç”¨</translation>
@@ -348,6 +363,7 @@
<translation id="4589078953350245614">你嘗試連上 <ph name="DOMAIN" />,但伺æœå™¨æ供的憑證無效。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="4592951414987517459">您的 <ph name="DOMAIN" /> 連線使用新型加密套件進行加密。</translation>
<translation id="4594403342090139922">復原刪除(&amp;U)</translation>
+<translation id="4619615317237390068">在其他è£ç½®ä¸Šé–‹å•Ÿçš„分é </translation>
<translation id="4668929960204016307">,</translation>
<translation id="4670097147947922288">您正在ç€è¦½æ“´å……功能é é¢ã€‚</translation>
<translation id="4701488924964507374"><ph name="SENTENCE1" /> <ph name="SENTENCE2" /></translation>
@@ -356,10 +372,13 @@
<translation id="4726672564094551039">é‡æ–°è¼‰å…¥æ”¿ç­–</translation>
<translation id="4728558894243024398">å¹³å°</translation>
<translation id="4744603770635761495">å¯åŸ·è¡Œæª”的路徑</translation>
+<translation id="4750917950439032686">你傳é€çµ¦é€™å€‹ç¶²ç«™çš„資訊 (例如密碼或信用å¡è™Ÿç¢¼) ä¸æœƒå¤–洩。</translation>
<translation id="4756388243121344051">記錄(&amp;H)</translation>
+<translation id="4759118997339041434">付款資訊自動填入功能已åœç”¨</translation>
<translation id="4764776831041365478"><ph name="URL" /> 的網é å¯èƒ½æš«æ™‚無法使用或被永久移至新網å€ã€‚</translation>
<translation id="4771973620359291008">發生ä¸æ˜Žçš„錯誤。</translation>
<translation id="4800132727771399293">請檢查您的有效期é™å’Œä¿¡ç”¨å¡å®‰å…¨ç¢¼ï¼Œç„¶å¾Œå†è©¦ä¸€æ¬¡</translation>
+<translation id="4803924862070940586"><ph name="CURRENCY_CODE" /> <ph name="FORMATTED_TOTAL_AMOUNT" /></translation>
<translation id="4813512666221746211">網路錯誤</translation>
<translation id="4816492930507672669">ä¾é é¢å¤§å°è‡ªå‹•èª¿æ•´</translation>
<translation id="4850886885716139402">檢視</translation>
@@ -368,6 +387,7 @@
<translation id="4895877746940133817"><ph name="TYPE_1" />ã€<ph name="TYPE_2" />,<ph name="TYPE_3" /></translation>
<translation id="4914479371620770914">{URL_count,plural, =1{還有å¦å¤– 1 個網é }other{還有å¦å¤– # 個網é }}</translation>
<translation id="4923417429809017348">系統已將此網é å¾žä¸æ˜Žèªžè¨€ç¿»è­¯æˆ<ph name="LANGUAGE_LANGUAGE" /></translation>
+<translation id="4923459931733593730">付款</translation>
<translation id="4926049483395192435">必須指定。</translation>
<translation id="495170559598752135">動作</translation>
<translation id="4958444002117714549">展開清單</translation>
@@ -395,7 +415,6 @@
<translation id="5181140330217080051">下載中</translation>
<translation id="5190835502935405962">書籤列</translation>
<translation id="5199729219167945352">實驗性功能</translation>
-<translation id="5199841536747119669">這裡會顯示系統建議你ç€è¦½çš„內容</translation>
<translation id="5251803541071282808">雲端</translation>
<translation id="5277279256032773186">在工作環境使用 Chrome 嗎?ä¼æ¥­å¯ä»¥ç®¡ç†å“¡å·¥çš„ Chrome 設定。瞭解詳情</translation>
<translation id="5299298092464848405">解æžæ”¿ç­–時發生錯誤</translation>
@@ -403,8 +422,10 @@
<translation id="5308689395849655368">當機報告功能已åœç”¨ã€‚</translation>
<translation id="5317780077021120954">儲存</translation>
<translation id="5327248766486351172">å稱</translation>
+<translation id="5337705430875057403">攻擊者å¯èƒ½æœƒè©¦åœ–é€éŽ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 誘使您åšä¸€äº›å±éšªçš„事,例如安è£è»Ÿé«”或æ供個人資訊 (包括密碼ã€é›»è©±è™Ÿç¢¼æˆ–信用å¡è³‡æ–™)。</translation>
<translation id="5359637492792381994">這個伺æœå™¨ç„¡æ³•è­‰æ˜Žæ‰€åœ¨ç¶²åŸŸæ˜¯ <ph name="DOMAIN" />;伺æœå™¨çš„安全性憑證目å‰ç„¡æ•ˆã€‚這å¯èƒ½æ˜¯å› ç‚ºè¨­å®šéŒ¯èª¤ï¼Œæˆ–是有攻擊者攔截你的連線。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="536296301121032821">無法儲存政策設定</translation>
+<translation id="5386426401304769735">這個網站的憑證éˆçµåŒ…å«ä½¿ç”¨ SHA-1 進行簽署的憑證。</translation>
<translation id="5421136146218899937">清除ç€è¦½è³‡æ–™...</translation>
<translation id="5430298929874300616">移除書籤</translation>
<translation id="5431657950005405462">找ä¸åˆ°æ‚¨çš„檔案</translation>
@@ -425,17 +446,20 @@
<translation id="5544037170328430102"><ph name="SITE" /> 的嵌入å¼ç¶²é é¡¯ç¤ºï¼š</translation>
<translation id="5556459405103347317">é‡æ–°è¼‰å…¥</translation>
<translation id="5565735124758917034">管ç†ä¸­</translation>
+<translation id="5572851009514199876">è«‹å•Ÿå‹• Chrome 並登入帳戶,Chrome 將確èªä½ æ˜¯å¦å¯å­˜å–這個網站。</translation>
+<translation id="5580958916614886209">請檢查信用å¡åˆ°æœŸæœˆä»½ï¼Œç„¶å¾Œå†è©¦ä¸€æ¬¡</translation>
<translation id="560412284261940334">系統ä¸æ”¯æ´ç®¡ç†</translation>
<translation id="5610142619324316209">檢查連線狀態</translation>
<translation id="5617949217645503996"><ph name="HOST_NAME" /> 將您é‡æ–°å°Žå‘的次數éŽå¤šã€‚</translation>
<translation id="5622887735448669177">您è¦é›¢é–‹é€™å€‹ç¶²ç«™å—Žï¼Ÿ</translation>
<translation id="5629630648637658800">無法載入政策設定</translation>
<translation id="5631439013527180824">è£ç½®ç®¡ç†ç¬¦è¨˜ç„¡æ•ˆ</translation>
+<translation id="5669703222995421982">å–得個人化內容</translation>
+<translation id="5675650730144413517">這個網é ç„¡æ³•æ­£å¸¸é‹ä½œ</translation>
<translation id="5677928146339483299">å·²å°éŽ–</translation>
<translation id="5694783966845939798">你嘗試連上 <ph name="DOMAIN" />,但伺æœå™¨çš„憑證是以防護力薄弱的簽章演算法 (例如 SHA-1) 進行簽署。這代表伺æœå™¨æ供的安全性憑證å¯èƒ½é­åˆ°å½é€ ï¼Œè€Œä¸”這個伺æœå™¨å¯èƒ½ä¸¦ä¸æ˜¯ä½ çš„目標伺æœå™¨ (你的連線å°è±¡å¯èƒ½æ˜¯æ”»æ“Šè€…的電腦)。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" />。</translation>
<translation id="5710435578057952990">此網é çš„身分未經驗證。</translation>
<translation id="5720705177508910913">ç›®å‰ä½¿ç”¨è€…</translation>
-<translation id="572328651809341494">最近開啟的分é </translation>
<translation id="5732392974455271431">你的家長å¯ä»¥ç‚ºä½ è§£é™¤å°éŽ–這個網站</translation>
<translation id="5784606427469807560">驗證您的信用å¡æ™‚發生å•é¡Œã€‚請檢查網際網路連線,然後å†è©¦ä¸€æ¬¡ã€‚</translation>
<translation id="5785756445106461925">此外,這個網é å«æœ‰å…¶ä»–ä¸å®‰å…¨çš„資æºã€‚其他人å¯èƒ½æœƒåœ¨è³‡æºå‚³è¼¸æœŸé–“檢視這些資æºï¼Œæ”»æ“Šè€…也å¯èƒ½æœƒä¿®æ”¹é€™äº›è³‡æºï¼Œé€²è€Œè®Šæ›´ç¶²é å¤–觀。</translation>
@@ -444,6 +468,7 @@
<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="5843436854350372569">你嘗試連上 <ph name="DOMAIN" />,但伺æœå™¨çš„憑證å«æœ‰é˜²è­·åŠ›è–„弱的金鑰。攻擊者å¯èƒ½å·²ç ´è§£ç§å¯†é‡‘鑰,而且這個伺æœå™¨å¯èƒ½ä¸¦ä¸æ˜¯ä½ çš„目標伺æœå™¨ (你的連線å°è±¡å¯èƒ½æ˜¯æ”»æ“Šè€…的電腦)。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="5857090052475505287">新資料夾</translation>
<translation id="5869405914158311789">無法連上這個網站</translation>
@@ -453,6 +478,7 @@
<translation id="59107663811261420">這個商家的 Google Payments ä¸æ”¯æ´æ­¤é¡žåž‹å¡ç‰‡ï¼Œè«‹é¸å–其他å¡ç‰‡ã€‚</translation>
<translation id="59174027418879706">已啟用</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="5966707198760109579">週</translation>
<translation id="5967867314010545767">從紀錄中移除</translation>
<translation id="5975083100439434680">縮å°</translation>
@@ -471,8 +497,11 @@
<translation id="614940544461990577">建議åšæ³•ï¼š</translation>
<translation id="6151417162996330722">伺æœå™¨æ†‘證的有效期é™å¤ªé•·ã€‚</translation>
<translation id="6165508094623778733">瞭解詳情</translation>
+<translation id="6177128806592000436">你與這個網站的連線ä¸å®‰å…¨</translation>
<translation id="6203231073485539293">檢查網際網路連線</translation>
<translation id="6218753634732582820">è¦å¾ž Chromium 中移除地å€å—Žï¼Ÿ</translation>
+<translation id="6251924700383757765">éš±ç§æ¬Šæ”¿ç­–</translation>
+<translation id="625755898061068298">ä½ å·²é¸æ“‡é‡å°é€™å€‹ç¶²ç«™åœç”¨å®‰å…¨æ€§è­¦å‘ŠåŠŸèƒ½ã€‚</translation>
<translation id="6259156558325130047">é‡åšé‡æ–°æŽ’åº(&amp;R)</translation>
<translation id="6263376278284652872"><ph name="DOMAIN" /> 書籤</translation>
<translation id="6264485186158353794">返回安全性ç€è¦½</translation>
@@ -481,6 +510,7 @@
<translation id="6305205051461490394">無法連上 <ph name="URL" />。</translation>
<translation id="6321917430147971392">檢查 DNS 設定</translation>
<translation id="6328639280570009161">建議åœç”¨ç¶²è·¯é æ¸¬åŠŸèƒ½</translation>
+<translation id="6328786501058569169">這是è©æ¬ºç¶²ç«™</translation>
<translation id="6337534724793800597">ä¾å稱篩é¸æ”¿ç­–</translation>
<translation id="6342069812937806050">剛剛</translation>
<translation id="6345221851280129312">大å°ä¸æ˜Ž</translation>
@@ -489,6 +519,8 @@
<translation id="6386120369904791316">{COUNT,plural, =1{以åŠå¦å¤– 1 個建議項目}other{以åŠå¦å¤– # 個建議項目}}</translation>
<translation id="6387478394221739770">想æ¶å…ˆè©¦ç”¨é…·ç‚«çš„ Chrome 新功能嗎?請å‰å¾€ chrome.com/beta 安è£æ¸¬è©¦ç‰ˆã€‚</translation>
<translation id="6389758589412724634">Chromium 嘗試顯示這個網é æ™‚用盡了記憶體。</translation>
+<translation id="6404511346730675251">編輯書籤</translation>
+<translation id="6410264514553301377">輸入 <ph name="CREDIT_CARD" /> 的到期日和信用å¡å®‰å…¨ç¢¼</translation>
<translation id="6414888972213066896">你已詢å•å®¶é•·æ˜¯å¦åŒæ„你造訪這個網站</translation>
<translation id="6416403317709441254">ç›®å‰ç„¡æ³•é€ è¨ª <ph name="SITE" />,因為這個網站傳é€çš„憑證å«æœ‰äº‚碼,Chromium 無法處ç†ã€‚網路錯誤和攻擊行為通常是暫時性的,因此這個網é å¯èƒ½ç¨å¾Œå°±æœƒæ¢å¾©æ­£å¸¸ç‹€æ…‹ã€‚<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6417515091412812850">無法檢查憑證是å¦å·²é­æ’¤éŠ·ã€‚</translation>
@@ -498,10 +530,12 @@
<translation id="6458467102616083041">由於政策åœç”¨äº†é è¨­æœå°‹ï¼Œå› æ­¤é­åˆ°ç•¥éŽã€‚</translation>
<translation id="6462969404041126431">這個伺æœå™¨ç„¡æ³•è­‰æ˜Žæ‰€åœ¨ç¶²åŸŸæ˜¯ <ph name="DOMAIN" />;伺æœå™¨çš„安全性憑證å¯èƒ½é­åˆ°æ’¤éŠ·ã€‚這å¯èƒ½æ˜¯å› ç‚ºè¨­å®šéŒ¯èª¤ï¼Œæˆ–是有攻擊者攔截你的連線。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="647261751007945333">è£ç½®æ”¿ç­–</translation>
+<translation id="6477321094435799029">Chrome 在這個網é ä¸Šåµæ¸¬åˆ°ç•°å¸¸ä»£ç¢¼ã€‚為了ä¿è­·ä½ çš„個人資訊 (包括密碼ã€é›»è©±è™Ÿç¢¼æˆ–信用å¡è³‡æ–™),Chrome å·²å°éŽ–這個網é ã€‚</translation>
<translation id="6489534406876378309">開始上傳當機報告</translation>
<translation id="6529602333819889595">é‡åšåˆªé™¤(&amp;R)</translation>
<translation id="6534179046333460208">實體化網路建議</translation>
<translation id="6550675742724504774">é¸é …</translation>
+<translation id="6556239504065605927">安全連線</translation>
<translation id="6563469144985748109">你的管ç†å“¡å°šæœªæ ¸å‡†é€™å€‹ç¶²ç«™</translation>
<translation id="6593753688552673085">ä¸åˆ° <ph name="UPPER_ESTIMATE" /></translation>
<translation id="6596325263575161958">加密é¸é …</translation>
@@ -510,7 +544,6 @@
<translation id="6644283850729428850">這項政策已é­å–代。</translation>
<translation id="6652240803263749613">這個伺æœå™¨ç„¡æ³•è­‰æ˜Žæ‰€åœ¨ç¶²åŸŸæ˜¯ <ph name="DOMAIN" />;電腦的作業系統ä¸ä¿¡ä»»ä¼ºæœå™¨çš„安全性憑證。這å¯èƒ½æ˜¯å› ç‚ºè¨­å®šéŒ¯èª¤ï¼Œæˆ–是有攻擊者攔截你的連線。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6656103420185847513">編輯資料夾</translation>
-<translation id="6660210980321319655">已自動回報 (<ph name="CRASH_TIME" />)</translation>
<translation id="6671697161687535275">è¦å¾ž Chromium 中移除表單填寫建議嗎?</translation>
<translation id="6685834062052613830">請登出並完æˆè¨­å®šç¨‹åº</translation>
<translation id="6710213216561001401">返回</translation>
@@ -522,6 +555,7 @@
<translation id="6753269504797312559">政策值</translation>
<translation id="6757797048963528358">您的è£ç½®å·²é€²å…¥ç¡çœ æ¨¡å¼ã€‚</translation>
<translation id="6778737459546443941">你的家長尚未核准這個網站</translation>
+<translation id="6810899417690483278">自訂 ID</translation>
<translation id="6820686453637990663">CVC</translation>
<translation id="6830600606572693159">ä½æ–¼ <ph name="URL" /> 的網é ç›®å‰ç„¡æ³•ä½¿ç”¨ï¼Œå¯èƒ½å› ç‚ºè¶…載或為了維護而關閉中。</translation>
<translation id="6831043979455480757">翻譯</translation>
@@ -542,6 +576,8 @@
<translation id="7012363358306927923">中國銀è¯</translation>
<translation id="7012372675181957985">ä½ ä»å¯å‰å¾€ <ph name="BEGIN_LINK" />history.google.com<ph name="END_LINK" /> å­˜å– Google 帳戶中ä¿å­˜çš„å„種ç€è¦½ç´€éŒ„</translation>
<translation id="7029809446516969842">密碼</translation>
+<translation id="7064851114919012435">è¯çµ¡è³‡è¨Š</translation>
+<translation id="7079718277001814089">這個網站å«æœ‰æƒ¡æ„軟體</translation>
<translation id="7087282848513945231">郡</translation>
<translation id="7088615885725309056">較舊記錄</translation>
<translation id="7090678807593890770">è«‹é€éŽ Google æœå°‹ã€Œ<ph name="LINK" />ã€</translation>
@@ -556,6 +592,7 @@
<translation id="7210863904660874423"><ph name="HOST_NAME" /> ä¸ç¬¦åˆå®‰å…¨æ€§æ¨™æº–。</translation>
<translation id="721197778055552897"><ph name="BEGIN_LINK" />進一步瞭解<ph name="END_LINK" />這個å•é¡Œã€‚</translation>
<translation id="7219179957768738017">這個連線使用 <ph name="SSL_VERSION" />。</translation>
+<translation id="724691107663265825">您è¦é€ è¨ªçš„網站å«æœ‰æƒ¡æ„軟體</translation>
<translation id="724975217298816891">請輸入 <ph name="CREDIT_CARD" /> 的有效日期和信用å¡å®‰å…¨ç¢¼ï¼Œæ›´æ–°æ‚¨çš„信用å¡è©³ç´°è³‡è¨Šã€‚完æˆé©—證後,這個網站就會å–得您的信用å¡è©³ç´°è³‡è¨Šã€‚</translation>
<translation id="725866823122871198">您電腦的日期和時間 (<ph name="DATE_AND_TIME" />) ä¸æ­£ç¢ºï¼Œå› æ­¤ç„¡æ³•èˆ‡ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> 建立ç§äººé€£ç·šã€‚</translation>
<translation id="7269802741830436641">這個網é å«æœ‰é‡æ–°å°Žå‘迴圈</translation>
@@ -611,6 +648,7 @@
<translation id="7658239707568436148">å–消</translation>
<translation id="7667346355482952095">傳回的政策憑證沒有任何內容,或是與目å‰çš„憑證ä¸ç¬¦</translation>
<translation id="7668654391829183341">ä¸æ˜Žçš„è£ç½®</translation>
+<translation id="7669271284792375604">攻擊者å¯èƒ½æœƒè©¦åœ–é€éŽé€™å€‹ç¶²ç«™èª˜ä½¿ä½ å®‰è£å°ç€è¦½é«”驗有害 (例如變更你的首é ï¼Œæˆ–是在你造訪的網站上顯示多餘的廣告) 的程å¼ã€‚</translation>
<translation id="7674629440242451245">想æ¶å…ˆè©¦ç”¨é…·ç‚«çš„ Chrome 新功能嗎?請å‰å¾€ chrome.com/dev 安è£é–‹ç™¼äººå“¡ç‰ˆã€‚</translation>
<translation id="7704050614460855821"><ph name="BEGIN_LINK" />繼續å‰å¾€ <ph name="SITE" /> 網站 (ä¸å®‰å…¨)<ph name="END_LINK" /></translation>
<translation id="7716424297397655342">無法從快å–載入這個網站</translation>
@@ -626,14 +664,17 @@
<translation id="7800304661137206267">連線採用 <ph name="CIPHER" /> 加密,並設有 <ph name="MAC" /> 訊æ¯é©—è­‰åŠ <ph name="KX" /> 金鑰交æ›æ©Ÿåˆ¶ã€‚</translation>
<translation id="780301667611848630">ä¸ç”¨äº†ï¼Œè¬è¬</translation>
<translation id="7805768142964895445">狀態</translation>
+<translation id="7812922009395017822">Mir</translation>
<translation id="7813600968533626083">è¦å¾ž Chrome 中移除建議嗎?</translation>
<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="7887683347370398519">請檢查您的 CVC,然後å†è©¦ä¸€æ¬¡</translation>
<translation id="7912024687060120840">在以下資料夾中:</translation>
<translation id="7935318582918952113">DOM Distiller</translation>
<translation id="7938958445268990899">伺æœå™¨æ†‘證尚未生效。</translation>
<translation id="7942349550061667556">紅色</translation>
+<translation id="7947285636476623132">請檢查信用å¡åˆ°æœŸå¹´ä»½ï¼Œç„¶å¾Œå†è©¦ä¸€æ¬¡</translation>
<translation id="7951415247503192394">(32 ä½å…ƒ)</translation>
<translation id="7956713633345437162">行動版書籤</translation>
<translation id="7961015016161918242">一律ä¸è¦</translation>
@@ -641,7 +682,10 @@
<translation id="7983301409776629893">一律將網é å…§å®¹ç”±<ph name="ORIGINAL_LANGUAGE" />翻譯æˆ<ph name="TARGET_LANGUAGE" /></translation>
<translation id="7995512525968007366">未指定</translation>
<translation id="8012647001091218357">我們暫時無法與您的家長è¯çµ¡ï¼Œè«‹å†è©¦ä¸€æ¬¡ã€‚</translation>
+<translation id="8025119109950072390">攻擊者å¯èƒ½æœƒè©¦åœ–é€éŽé€™å€‹ç¶²ç«™èª˜ä½¿ä½ åšä¸€äº›å±éšªçš„行為,例如安è£è»Ÿé«”或æ供個人資訊 (包括密碼ã€é›»è©±è™Ÿç¢¼æˆ–信用å¡è³‡æ–™)。</translation>
+<translation id="803030522067524905">Google 安全ç€è¦½åŠŸèƒ½æœ€è¿‘在 <ph name="SITE" /> 上åµæ¸¬åˆ°ç¶²è·¯è©é¨™è¡Œç‚ºã€‚è©é¨™ç¶²ç«™æœƒå½è£æˆå…¶ä»–網站,藉此騙å–你的資訊。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8034522405403831421">這是<ph name="SOURCE_LANGUAGE" />網é ï¼Œéœ€è¦ç¿»è­¯æˆ<ph name="TARGET_LANGUAGE" />嗎?</translation>
+<translation id="8041089156583427627">æä¾›æ„見</translation>
<translation id="8088680233425245692">無法查看文章。</translation>
<translation id="8089520772729574115">ä¸åˆ° 1 MB</translation>
<translation id="8091372947890762290">尚未在伺æœå™¨ä¸Šå•Ÿå‹•</translation>
@@ -652,9 +696,11 @@
<translation id="8150722005171944719">無法讀å–ä½æ–¼ <ph name="URL" /> 的檔案。這個檔案å¯èƒ½å·²é­ç§»é™¤æˆ–移動ä½ç½®ï¼Œæˆ–者檔案權é™ç‚ºç¦æ­¢å­˜å–。</translation>
<translation id="8194797478851900357">復原移動(&amp;U)</translation>
<translation id="8201077131113104583">擴充功能 (ID:「<ph name="EXTENSION_ID" />ã€) 的更新網å€ç„¡æ•ˆã€‚</translation>
+<translation id="8202097416529803614">訂單摘è¦</translation>
<translation id="8218327578424803826">指派的ä½ç½®ï¼š</translation>
<translation id="8225771182978767009">設定這部電腦的使用者é¸æ“‡å°éŽ–這個網站。</translation>
<translation id="822964464349305906"><ph name="TYPE_1" />ã€<ph name="TYPE_2" /></translation>
+<translation id="8230421197304563332">攻擊者目å‰å¯èƒ½æœƒè©¦åœ–é€éŽ <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 在您的電腦上安è£å±éšªç¨‹å¼ï¼Œè—‰æ­¤ç«Šå–或刪除您的資訊 (例如相片ã€å¯†ç¢¼ã€éƒµä»¶æˆ–信用å¡è³‡æ–™)。</translation>
<translation id="8241707690549784388">您尋找的網é ä½¿ç”¨äº†æ‚¨è¼¸å…¥çš„資料。返回該é æœƒé‡è¤‡æ‚¨å‰›æ‰çš„行動。您確定è¦ç¹¼çºŒå—Žï¼Ÿ</translation>
<translation id="8249320324621329438">上次擷å–時間:</translation>
<translation id="8261506727792406068">刪除</translation>
@@ -663,11 +709,13 @@
<translation id="8294431847097064396">來æº</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="8349305172487531364">書籤列</translation>
<translation id="8363502534493474904">關閉飛航模å¼</translation>
<translation id="8364627913115013041">未設定。</translation>
<translation id="8380941800586852976">ä¸å®‰å…¨</translation>
<translation id="8382348898565613901">這裡會顯示你最近造訪éŽçš„書籤</translation>
+<translation id="8398259832188219207">當機報告上傳時間:<ph name="UPLOAD_TIME" /></translation>
<translation id="8412145213513410671">當機次數 (<ph name="CRASH_COUNT" />)</translation>
<translation id="8412392972487953978">您必須輸入兩次相åŒçš„通關密語。</translation>
<translation id="8428213095426709021">設定</translation>
@@ -679,9 +727,9 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> 的回應時間éŽé•·ã€‚</translation>
<translation id="852346902619691059">這個伺æœå™¨ç„¡æ³•è­‰æ˜Žæ‰€åœ¨ç¶²åŸŸæ˜¯ <ph name="DOMAIN" />ï¼›è£ç½®çš„作業系統ä¸ä¿¡ä»»ä¼ºæœå™¨çš„安全性憑證。這å¯èƒ½æ˜¯å› ç‚ºè¨­å®šéŒ¯èª¤ï¼Œæˆ–是有攻擊者攔截你的連線。<ph name="BEGIN_LEARN_MORE_LINK" />瞭解詳情<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="8530504477309582336">Google Payments ä¸æ”¯æ´æ­¤é¡žåž‹å¡ç‰‡ï¼Œè«‹é¸å–其他å¡ç‰‡ã€‚</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="8553075262323480129">無法判定網é çš„語言,翻譯作業失敗。</translation>
<translation id="8559762987265718583">您è£ç½®çš„日期和時間 (<ph name="DATE_AND_TIME" />) ä¸æ­£ç¢ºï¼Œå› æ­¤ç„¡æ³•èˆ‡ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> 建立ç§äººé€£ç·šã€‚</translation>
-<translation id="856992080682148">這個網站的憑證將於 2017 å¹´ (或之後) 到期,且憑證éˆçµåŒ…å«ä½¿ç”¨ SHA-1 進行簽署的憑證。</translation>
<translation id="8571890674111243710">正在將網é ç¿»è­¯æˆ<ph name="LANGUAGE" />...</translation>
<translation id="859285277496340001">憑證未指定負責檢查其本身是å¦å·²é­åˆ°æ’¤éŠ·çš„機制。</translation>
<translation id="8620436878122366504">你的家長尚未核准這個網站</translation>
@@ -694,10 +742,12 @@
<translation id="8740359287975076522">找ä¸åˆ° <ph name="HOST_NAME" /> çš„ &lt;abbr id="dnsDefinition"&gt;DNS ä½å€&lt;/abbr&gt;,正在診斷å•é¡Œã€‚</translation>
<translation id="8790007591277257123">é‡åšåˆªé™¤(&amp;R)</translation>
<translation id="8798099450830957504">é è¨­</translation>
+<translation id="8800988563907321413">這裡會顯示系統建議你ç€è¦½çš„鄰近網é </translation>
<translation id="8804164990146287819">éš±ç§æ¬Šæ”¿ç­–</translation>
<translation id="8820817407110198400">書籤</translation>
<translation id="8834246243508017242">啟用「使用è¯çµ¡äººè³‡æ–™è‡ªå‹•å¡«å…¥ã€åŠŸèƒ½â€¦</translation>
<translation id="883848425547221593">其他書籤</translation>
+<translation id="884264119367021077">é‹é€åœ°å€</translation>
<translation id="884923133447025588">未發ç¾æ’¤éŠ·æ©Ÿåˆ¶ã€‚</translation>
<translation id="885730110891505394">與 Google 分享</translation>
<translation id="8866481888320382733">解æžæ”¿ç­–設定時發生錯誤</translation>
@@ -715,18 +765,21 @@
<translation id="8971063699422889582">伺æœå™¨æ†‘證已éŽæœŸã€‚</translation>
<translation id="8987927404178983737">月</translation>
<translation id="8989148748219918422"><ph name="ORGANIZATION" /> [<ph name="COUNTRY" />]</translation>
+<translation id="8996941253935762404">您è¦ç€è¦½çš„網站å«æœ‰æœ‰å®³ç¨‹å¼</translation>
<translation id="9001074447101275817"><ph name="DOMAIN" /> Proxy è¦æ±‚æ供使用者å稱和密碼。</translation>
<translation id="901974403500617787">這些設定會套用至整個系統,åªæœ‰ä»¥ä¸‹ä½¿ç”¨è€…å¯è¨­å®šï¼š<ph name="OWNER_EMAIL" />。</translation>
<translation id="9020542370529661692">此網é å…§å®¹å·²ç¿»è­¯æˆ<ph name="TARGET_LANGUAGE" /></translation>
+<translation id="9035022520814077154">安全性錯誤</translation>
<translation id="9038649477754266430">使用é æ¸¬æŸ¥è©¢å­—串æœå‹™ï¼Œè®“系統更快載入網é </translation>
<translation id="9039213469156557790">此外,這個網é å«æœ‰å…¶ä»–ä¸å®‰å…¨çš„資æºã€‚其他人å¯èƒ½æœƒåœ¨è³‡æºå‚³è¼¸æœŸé–“檢視這些資æºï¼Œæ”»æ“Šè€…也å¯èƒ½æœƒä¿®æ”¹é€™äº›è³‡æºï¼Œé€²è€Œè®Šæ›´ç¶²é è¡Œç‚ºã€‚</translation>
+<translation id="9040185888511745258">攻擊者ä¼åœ–在 <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> 誘使您安è£æœ‰å®³ç€è¦½é«”é©—çš„ç¨‹å¼ (比如竄改您的首é ï¼Œæˆ–在您造訪的網站上å¦å¤–顯示廣告)。</translation>
<translation id="9050666287014529139">通關密語</translation>
<translation id="9065203028668620118">編輯</translation>
<translation id="9068849894565669697">é¸å–é¡è‰²</translation>
<translation id="9076283476770535406">這個網站å¯èƒ½å«æœ‰æˆäººå…§å®¹</translation>
-<translation id="9092364396508701805"><ph name="HOST_NAME" /> é é¢ç„¡æ³•æ­£å¸¸é‹ä½œ</translation>
<translation id="9103872766612412690"><ph name="SITE" /> 通常使用加密方å¼ä¿è­·æ‚¨çš„資訊。但 Chromium 這次嘗試連線到 <ph name="SITE" /> 時,該網站傳回了異常且錯誤的憑證。這å¯èƒ½æ˜¯å› ç‚ºæœ‰æ”»æ“Šè€…ä¼åœ–å½è£æˆ <ph name="SITE" />,或是å—到 Wi-Fi 登入畫é¢å½±éŸ¿è€Œé€ æˆé€£ç·šä¸­æ–·ã€‚ä¸éŽè«‹æ”¾å¿ƒï¼ŒChromium å·²åŠæ™‚åœæ­¢é€£ç·šï¼Œä¸¦æœªå‚³è¼¸ä»»ä½•è³‡æ–™ï¼Œå› æ­¤æ‚¨çš„資訊ä»ç„¶å®‰å…¨ç„¡è™žã€‚</translation>
<translation id="9137013805542155359">顯示原文</translation>
+<translation id="9137248913990643158">使用這個應用程å¼å‰ï¼Œè«‹å…ˆå•Ÿå‹• Chrome 並登入帳戶。</translation>
<translation id="9148507642005240123">復原編輯(&amp;U)</translation>
<translation id="9157595877708044936">設定中...</translation>
<translation id="9170848237812810038">å–消(&amp;U)</translation>
@@ -739,7 +792,6 @@
<translation id="935608979562296692">清除表單</translation>
<translation id="939736085109172342">新增資料夾</translation>
<translation id="941721044073577244">你沒有這個網站的ç€è¦½æ¬Šé™</translation>
-<translation id="962701380617707048">輸入 <ph name="CREDIT_CARD" /> 的有效日期和信用å¡å®‰å…¨ç¢¼ï¼Œæ›´æ–°æ‚¨çš„信用å¡è©³ç´°è³‡è¨Š</translation>
<translation id="969892804517981540">æ­£å¼ç‰ˆæœ¬</translation>
<translation id="988159990683914416">開發人員版本</translation>
</translationbundle> \ No newline at end of file
diff --git a/chromium/components/subresource_filter/OWNERS b/chromium/components/subresource_filter/OWNERS
index ef69995b94d..c21f3df9824 100644
--- a/chromium/components/subresource_filter/OWNERS
+++ b/chromium/components/subresource_filter/OWNERS
@@ -1,3 +1,4 @@
battre@chromium.org
engedy@chromium.org
melandory@chromium.org
+pkalinnikov@chromium.org
diff --git a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver.cc b/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver.cc
index 54992698916..421c4b54a05 100644
--- a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver.cc
+++ b/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver.cc
@@ -17,11 +17,13 @@ ContentSubresourceFilterDriver::~ContentSubresourceFilterDriver() {}
void ContentSubresourceFilterDriver::ActivateForProvisionalLoad(
ActivationState activation_state,
- const GURL& url) {
+ const GURL& url,
+ bool measure_performance) {
// Must use legacy IPC to ensure the activation message arrives in-order, i.e.
// before the load is committed on the renderer side.
render_frame_host_->Send(new SubresourceFilterMsg_ActivateForProvisionalLoad(
- render_frame_host_->GetRoutingID(), activation_state, url));
+ render_frame_host_->GetRoutingID(), activation_state, url,
+ measure_performance));
}
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver.h b/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver.h
index 10d0043b63c..dff92cd30ee 100644
--- a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver.h
+++ b/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver.h
@@ -27,7 +27,8 @@ class ContentSubresourceFilterDriver {
// Instructs the agent on the renderer to set up the subresource filter for
// the currently ongoing provisional document load in the frame.
virtual void ActivateForProvisionalLoad(ActivationState activation_state,
- const GURL& url);
+ const GURL& url,
+ bool measure_performance);
private:
// The RenderFrameHost that this driver belongs to.
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 57bd1de7c13..e658b39c513 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
@@ -5,11 +5,14 @@
#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
#include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
+#include "base/time/time.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_driver.h"
#include "components/subresource_filter/content/common/subresource_filter_messages.h"
#include "components/subresource_filter/core/browser/subresource_filter_client.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
#include "components/subresource_filter/core/common/activation_list.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"
@@ -18,10 +21,28 @@
namespace subresource_filter {
-// static
-const char ContentSubresourceFilterDriverFactory::kWebContentsUserDataKey[] =
+namespace {
+
+const char kWebContentsUserDataKey[] =
"web_contents_subresource_filter_driver_factory";
+std::string DistillURLToHostAndPath(const GURL& url) {
+ return url.host() + url.path();
+}
+
+// Returns true with a probability of GetPerformanceMeasurementRate() if
+// ThreadTicks is supported, otherwise returns false.
+bool ShouldMeasurePerformanceForPageLoad() {
+ if (!base::ThreadTicks::IsSupported())
+ return false;
+ // TODO(pkalinnikov): Cache |rate| and other variation params in
+ // ContentSubresourceFilterDriverFactory.
+ const double rate = GetPerformanceMeasurementRate();
+ return rate == 1 || (rate > 0 && base::RandDouble() < rate);
+}
+
+} // namespace
+
// static
void ContentSubresourceFilterDriverFactory::CreateForWebContents(
content::WebContents* web_contents,
@@ -46,7 +67,8 @@ ContentSubresourceFilterDriverFactory::ContentSubresourceFilterDriverFactory(
std::unique_ptr<SubresourceFilterClient> client)
: content::WebContentsObserver(web_contents),
client_(std::move(client)),
- activation_state_(ActivationState::DISABLED) {
+ activation_state_(ActivationState::DISABLED),
+ measure_performance_(false) {
content::RenderFrameHost* main_frame_host = web_contents->GetMainFrame();
if (main_frame_host && main_frame_host->IsRenderFrameLive())
CreateDriverForFrameHostIfNeeded(main_frame_host);
@@ -66,76 +88,86 @@ void ContentSubresourceFilterDriverFactory::CreateDriverForFrameHostIfNeeded(
}
void ContentSubresourceFilterDriverFactory::OnFirstSubresourceLoadDisallowed() {
- client_->ToggleNotificationVisibility(activation_state() ==
+ client_->ToggleNotificationVisibility(activation_state_ ==
ActivationState::ENABLED);
}
+void ContentSubresourceFilterDriverFactory::OnDocumentLoadStatistics(
+ const DocumentLoadStatistics& statistics) {
+ // Note: Chances of overflow are negligible.
+ aggregated_document_statistics_.num_loads_total += statistics.num_loads_total;
+ aggregated_document_statistics_.num_loads_evaluated +=
+ statistics.num_loads_evaluated;
+ aggregated_document_statistics_.num_loads_matching_rules +=
+ statistics.num_loads_matching_rules;
+ aggregated_document_statistics_.num_loads_disallowed +=
+ statistics.num_loads_disallowed;
+
+ aggregated_document_statistics_.evaluation_total_wall_duration +=
+ statistics.evaluation_total_wall_duration;
+ aggregated_document_statistics_.evaluation_total_cpu_duration +=
+ statistics.evaluation_total_cpu_duration;
+}
+
bool ContentSubresourceFilterDriverFactory::IsWhitelisted(
const GURL& url) const {
return whitelisted_hosts_.find(url.host()) != whitelisted_hosts_.end();
}
-bool ContentSubresourceFilterDriverFactory::IsHit(const GURL& url) const {
- return safe_browsing_blacklisted_patterns_.find(url.host() + url.path()) !=
- safe_browsing_blacklisted_patterns_.end();
-}
-
-
void ContentSubresourceFilterDriverFactory::
OnMainResourceMatchedSafeBrowsingBlacklist(
const GURL& url,
const std::vector<GURL>& redirect_urls,
safe_browsing::SBThreatType threat_type,
safe_browsing::ThreatPatternType threat_type_metadata) {
- bool proceed = false;
- if (GetCurrentActivationList() ==
- ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL) {
- proceed = (threat_type_metadata ==
- safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS);
- } else if (GetCurrentActivationList() ==
- ActivationList::PHISHING_INTERSTITIAL) {
- proceed = (threat_type == safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
+ bool is_phishing_interstitial =
+ (threat_type == safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
+ bool is_soc_engineering_ads_interstitial =
+ threat_type_metadata ==
+ safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS;
+
+ if (is_phishing_interstitial) {
+ if (is_soc_engineering_ads_interstitial) {
+ AddActivationListMatch(url, ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL);
+ }
+ AddActivationListMatch(url, ActivationList::PHISHING_INTERSTITIAL);
}
- if (!proceed)
- return;
- AddToActivationHitsSet(url);
}
void ContentSubresourceFilterDriverFactory::AddHostOfURLToWhitelistSet(
const GURL& url) {
- if (!url.host().empty() && url.SchemeIsHTTPOrHTTPS())
+ if (url.has_host() && url.SchemeIsHTTPOrHTTPS())
whitelisted_hosts_.insert(url.host());
}
-void ContentSubresourceFilterDriverFactory::AddToActivationHitsSet(
- const GURL& url) {
- if (!url.host().empty() && url.SchemeIsHTTPOrHTTPS())
- safe_browsing_blacklisted_patterns_.insert(url.host() + url.path());
-}
-
bool ContentSubresourceFilterDriverFactory::ShouldActivateForMainFrameURL(
const GURL& url) const {
- if (GetCurrentActivationScope() == ActivationScope::ALL_SITES)
- return !IsWhitelisted(url);
- else if (GetCurrentActivationScope() == ActivationScope::ACTIVATION_LIST)
- return IsHit(url) && !IsWhitelisted(url);
- return false;
+ switch (GetCurrentActivationScope()) {
+ case ActivationScope::ALL_SITES:
+ return !IsWhitelisted(url);
+ case ActivationScope::ACTIVATION_LIST:
+ return DidURLMatchCurrentActivationList(url) && !IsWhitelisted(url);
+ default:
+ return false;
+ }
}
void ContentSubresourceFilterDriverFactory::ActivateForFrameHostIfNeeded(
content::RenderFrameHost* render_frame_host,
const GURL& url) {
if (activation_state_ != ActivationState::DISABLED) {
- DriverFromFrameHost(render_frame_host)
- ->ActivateForProvisionalLoad(GetMaximumActivationState(), url);
+ auto* driver = DriverFromFrameHost(render_frame_host);
+ DCHECK(driver);
+ driver->ActivateForProvisionalLoad(GetMaximumActivationState(), url,
+ measure_performance_);
}
}
void ContentSubresourceFilterDriverFactory::OnReloadRequested() {
UMA_HISTOGRAM_BOOLEAN("SubresourceFilter.Prompt.NumReloads", true);
- const GURL whitelist_url(web_contents()->GetLastCommittedURL());
+ const GURL& whitelist_url = web_contents()->GetLastCommittedURL();
AddHostOfURLToWhitelistSet(whitelist_url);
- web_contents()->GetController().Reload(true);
+ web_contents()->GetController().Reload(content::ReloadType::NORMAL, true);
}
void ContentSubresourceFilterDriverFactory::SetDriverForFrameHostForTesting(
@@ -153,6 +185,27 @@ ContentSubresourceFilterDriverFactory::DriverFromFrameHost(
return iterator == frame_drivers_.end() ? nullptr : iterator->second.get();
}
+void ContentSubresourceFilterDriverFactory::DidStartNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (navigation_handle->IsInMainFrame() && !navigation_handle->IsSamePage()) {
+ navigation_chain_.clear();
+ activation_list_matches_.clear();
+ navigation_chain_.push_back(navigation_handle->GetURL());
+
+ client_->ToggleNotificationVisibility(false);
+ activation_state_ = ActivationState::DISABLED;
+ measure_performance_ = false;
+ aggregated_document_statistics_ = DocumentLoadStatistics();
+ }
+}
+
+void ContentSubresourceFilterDriverFactory::DidRedirectNavigation(
+ content::NavigationHandle* navigation_handle) {
+ DCHECK(!navigation_handle->IsSamePage());
+ if (navigation_handle->IsInMainFrame())
+ navigation_chain_.push_back(navigation_handle->GetURL());
+}
+
void ContentSubresourceFilterDriverFactory::RenderFrameCreated(
content::RenderFrameHost* render_frame_host) {
CreateDriverForFrameHostIfNeeded(render_frame_host);
@@ -163,23 +216,54 @@ void ContentSubresourceFilterDriverFactory::RenderFrameDeleted(
frame_drivers_.erase(render_frame_host);
}
-void ContentSubresourceFilterDriverFactory::DidStartNavigation(
- content::NavigationHandle* navigation_handle) {
- safe_browsing_blacklisted_patterns_.clear();
- if (navigation_handle->IsInMainFrame()) {
- client_->ToggleNotificationVisibility(false);
- activation_state_ = ActivationState::DISABLED;
- }
-}
-
void ContentSubresourceFilterDriverFactory::ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) {
+ DCHECK(!navigation_handle->IsSamePage());
content::RenderFrameHost* render_frame_host =
navigation_handle->GetRenderFrameHost();
GURL url = navigation_handle->GetURL();
ReadyToCommitNavigationInternal(render_frame_host, url);
}
+void ContentSubresourceFilterDriverFactory::DidFinishLoad(
+ content::RenderFrameHost* render_frame_host,
+ const GURL& validated_url) {
+ if (render_frame_host->GetParent())
+ return;
+
+ UMA_HISTOGRAM_COUNTS_1000(
+ "SubresourceFilter.PageLoad.NumSubresourceLoads.Total",
+ aggregated_document_statistics_.num_loads_total);
+ UMA_HISTOGRAM_COUNTS_1000(
+ "SubresourceFilter.PageLoad.NumSubresourceLoads.Evaluated",
+ aggregated_document_statistics_.num_loads_evaluated);
+ UMA_HISTOGRAM_COUNTS_1000(
+ "SubresourceFilter.PageLoad.NumSubresourceLoads.MatchedRules",
+ aggregated_document_statistics_.num_loads_matching_rules);
+ UMA_HISTOGRAM_COUNTS_1000(
+ "SubresourceFilter.PageLoad.NumSubresourceLoads.Disallowed",
+ aggregated_document_statistics_.num_loads_disallowed);
+
+ if (measure_performance_) {
+ DCHECK(activation_state_ != ActivationState::DISABLED);
+ UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
+ "SubresourceFilter.PageLoad.SubresourceEvaluation.TotalWallDuration",
+ aggregated_document_statistics_.evaluation_total_wall_duration,
+ base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
+ 50);
+ UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
+ "SubresourceFilter.PageLoad.SubresourceEvaluation.TotalCPUDuration",
+ aggregated_document_statistics_.evaluation_total_cpu_duration,
+ base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
+ 50);
+ } else {
+ DCHECK(aggregated_document_statistics_.evaluation_total_wall_duration
+ .is_zero());
+ DCHECK(aggregated_document_statistics_.evaluation_total_cpu_duration
+ .is_zero());
+ }
+}
+
bool ContentSubresourceFilterDriverFactory::OnMessageReceived(
const IPC::Message& message,
content::RenderFrameHost* render_frame_host) {
@@ -187,6 +271,8 @@ bool ContentSubresourceFilterDriverFactory::OnMessageReceived(
IPC_BEGIN_MESSAGE_MAP(ContentSubresourceFilterDriverFactory, message)
IPC_MESSAGE_HANDLER(SubresourceFilterHostMsg_DidDisallowFirstSubresource,
OnFirstSubresourceLoadDisallowed)
+ IPC_MESSAGE_HANDLER(SubresourceFilterHostMsg_DocumentLoadStatistics,
+ OnDocumentLoadStatistics)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@@ -196,15 +282,70 @@ void ContentSubresourceFilterDriverFactory::ReadyToCommitNavigationInternal(
content::RenderFrameHost* render_frame_host,
const GURL& url) {
if (!render_frame_host->GetParent()) {
+ RecordRedirectChainMatchPattern();
if (ShouldActivateForMainFrameURL(url)) {
activation_state_ = GetMaximumActivationState();
+ measure_performance_ = activation_state_ != ActivationState::DISABLED &&
+ ShouldMeasurePerformanceForPageLoad();
ActivateForFrameHostIfNeeded(render_frame_host, url);
} else {
activation_state_ = ActivationState::DISABLED;
+ measure_performance_ = false;
+ aggregated_document_statistics_ = DocumentLoadStatistics();
}
} else {
ActivateForFrameHostIfNeeded(render_frame_host, url);
}
}
+bool ContentSubresourceFilterDriverFactory::DidURLMatchCurrentActivationList(
+ const GURL& url) const {
+ auto match_types =
+ activation_list_matches_.find(DistillURLToHostAndPath(url));
+ return match_types != activation_list_matches_.end() &&
+ match_types->second.find(GetCurrentActivationList()) !=
+ match_types->second.end();
+}
+
+void ContentSubresourceFilterDriverFactory::AddActivationListMatch(
+ const GURL& url,
+ ActivationList match_type) {
+ if (!url.host().empty() && url.SchemeIsHTTPOrHTTPS())
+ activation_list_matches_[DistillURLToHostAndPath(url)].insert(match_type);
+}
+
+void ContentSubresourceFilterDriverFactory::RecordRedirectChainMatchPattern()
+ const {
+ int hits_pattern = 0;
+ const int kInitialURLHitMask = 0x4;
+ const int kRedirectURLHitMask = 0x2;
+ const int kFinalURLHitMask = 0x1;
+ if (navigation_chain_.size() > 1) {
+ if (DidURLMatchCurrentActivationList(navigation_chain_.back()))
+ hits_pattern |= kFinalURLHitMask;
+ if (DidURLMatchCurrentActivationList(navigation_chain_.front()))
+ hits_pattern |= kInitialURLHitMask;
+
+ // Examine redirects.
+ for (size_t i = 1; i < navigation_chain_.size() - 1; ++i) {
+ if (DidURLMatchCurrentActivationList(navigation_chain_[i])) {
+ hits_pattern |= kRedirectURLHitMask;
+ break;
+ }
+ }
+ } else {
+ if (navigation_chain_.size() &&
+ DidURLMatchCurrentActivationList(navigation_chain_.front())) {
+ hits_pattern = 0x8; // One url hit.
+ }
+ }
+ if (!hits_pattern)
+ return;
+ UMA_HISTOGRAM_ENUMERATION(
+ "SubresourceFilter.PageLoad.RedirectChainMatchPattern", hits_pattern,
+ 0x10 /* max value */);
+ UMA_HISTOGRAM_COUNTS("SubresourceFilter.PageLoad.RedirectChainLength",
+ navigation_chain_.size());
+}
+
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h b/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h
index 2e1056e3998..7b6e20d22ed 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
@@ -13,7 +13,9 @@
#include "base/macros.h"
#include "base/supports_user_data.h"
+#include "base/time/time.h"
#include "components/safe_browsing_db/util.h"
+#include "components/subresource_filter/content/common/document_load_statistics.h"
#include "content/public/browser/web_contents_observer.h"
#include "url/gurl.h"
@@ -22,19 +24,23 @@ class WebContents;
class RenderFrameHost;
} // namespace content
-namespace subresource_filter {
+namespace safe_browsing {
+class SafeBrowsingServiceTest;
+};
-using HostSet = std::set<std::string>;
+namespace subresource_filter {
class ContentSubresourceFilterDriver;
class SubresourceFilterClient;
enum class ActivationState;
+enum class ActivationList;
+
+using HostPathSet = std::set<std::string>;
+using URLToActivationListsMap =
+ std::unordered_map<std::string, std::set<ActivationList>>;
// Controls the activation of subresource filtering for each page load in a
// WebContents and manufactures the per-frame ContentSubresourceFilterDrivers.
-// TODO(melandory): Once https://crbug.com/621856 is fixed this class should
-// take care of passing the activation information not only to the main frame,
-// but also to the subframes.
class ContentSubresourceFilterDriverFactory
: public base::SupportsUserData::Data,
public content::WebContentsObserver {
@@ -50,19 +56,11 @@ class ContentSubresourceFilterDriverFactory
std::unique_ptr<SubresourceFilterClient> client);
~ContentSubresourceFilterDriverFactory() override;
- ContentSubresourceFilterDriver* DriverFromFrameHost(
- content::RenderFrameHost* render_frame_host);
-
- bool IsWhitelisted(const GURL& url) const;
- bool IsBlacklisted(const GURL& url) const;
-
// Whitelists the host of |url|, so that page loads with the main-frame
// document being loaded from this host will be exempted from subresource
// filtering for the lifetime of this WebContents.
void AddHostOfURLToWhitelistSet(const GURL& url);
- void AddToActivationHitsSet(const GURL& url);
-
// Called when Safe Browsing detects that the |url| corresponding to the load
// of the main frame belongs to the blacklist with |threat_type|. If the
// blacklist is the Safe Browsing Social Engineering ads landing, then |url|
@@ -73,17 +71,12 @@ class ContentSubresourceFilterDriverFactory
safe_browsing::SBThreatType threat_type,
safe_browsing::ThreatPatternType threat_type_metadata);
- // Reloads the page and inserts the url to the whitelist.
+ // Reloads the page and inserts the host of its URL to the whitelist.
void OnReloadRequested();
- const HostSet& safe_browsing_blacklisted_patterns_set() const {
- return safe_browsing_blacklisted_patterns_;
- }
- ActivationState activation_state() { return activation_state_; }
-
private:
friend class ContentSubresourceFilterDriverFactoryTest;
- friend class SubresourceFilterNavigationThrottleTest;
+ friend class safe_browsing::SafeBrowsingServiceTest;
typedef std::map<content::RenderFrameHost*,
std::unique_ptr<ContentSubresourceFilterDriver>>
@@ -95,16 +88,26 @@ class ContentSubresourceFilterDriverFactory
void CreateDriverForFrameHostIfNeeded(
content::RenderFrameHost* render_frame_host);
+ ContentSubresourceFilterDriver* DriverFromFrameHost(
+ content::RenderFrameHost* render_frame_host);
void OnFirstSubresourceLoadDisallowed();
+ void OnDocumentLoadStatistics(const DocumentLoadStatistics& statistics);
+
+ bool IsWhitelisted(const GURL& url) const;
+
// content::WebContentsObserver:
- void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
- void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override;
+ void DidRedirectNavigation(
+ content::NavigationHandle* navigation_handle) override;
+ void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+ void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
void ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) override;
+ void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+ const GURL& validated_url) override;
bool OnMessageReceived(const IPC::Message& message,
content::RenderFrameHost* render_frame_host) override;
@@ -120,23 +123,27 @@ class ContentSubresourceFilterDriverFactory
content::RenderFrameHost* render_frame_host,
const GURL& url);
- bool IsHit(const GURL& url) const;
+ bool DidURLMatchCurrentActivationList(const GURL& url) const;
- static const char kWebContentsUserDataKey[];
+ void AddActivationListMatch(const GURL& url, ActivationList match_type);
+ void RecordRedirectChainMatchPattern() const;
FrameHostToOwnedDriverMap frame_drivers_;
std::unique_ptr<SubresourceFilterClient> client_;
- HostSet whitelisted_hosts_;
-
- // Host+path list of the URLs, where the Safe Browsing detected hit to the
- // threat list of interest. When the navigation is commited
- // |safe_browsing_blacklisted_patterns_| is used to determine whenever
- // the activation signal should be sent. All entities are deleted from the
- // list on navigation commit event.
- HostSet safe_browsing_blacklisted_patterns_;
+ HostPathSet whitelisted_hosts_;
ActivationState activation_state_;
+ bool measure_performance_;
+
+ // The URLs in the navigation chain.
+ std::vector<GURL> navigation_chain_;
+
+ URLToActivationListsMap activation_list_matches_;
+
+ // Statistics about subresource loads, aggregated across all frames of the
+ // current page.
+ DocumentLoadStatistics aggregated_document_statistics_;
DISALLOW_COPY_AND_ASSIGN(ContentSubresourceFilterDriverFactory);
};
diff --git a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc b/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc
index 91991880da0..75db9c7231a 100644
--- a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc
+++ b/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc
@@ -5,6 +5,7 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
+#include "base/test/histogram_tester.h"
#include "components/safe_browsing_db/util.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_driver.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
@@ -12,21 +13,48 @@
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
#include "components/subresource_filter/core/browser/subresource_filter_features_test_support.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/browser_side_navigation_policy.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+namespace subresource_filter {
+
namespace {
const char kExampleUrlWithParams[] = "https://example.com/soceng?q=engsoc";
-const char kTestUrl[] = "https://test.com";
const char kExampleUrl[] = "https://example.com";
const char kExampleLoginUrl[] = "https://example.com/login";
+const char kMatchesPatternHistogramName[] =
+ "SubresourceFilter.PageLoad.RedirectChainMatchPattern";
+const char kNavigationChainSize[] =
+ "SubresourceFilter.PageLoad.RedirectChainLength";
+const char kUrlA[] = "https://example_a.com";
+const char kUrlB[] = "https://example_b.com";
+const char kUrlC[] = "https://example_c.com";
+const char kUrlD[] = "https://example_d.com";
+
+// Human readable representation of expected redirect chain match patterns.
+// The explanations for the buckets given for the following redirect chain:
+// A->B->C->D, where A is initial URL and D is a final URL.
+enum RedirectChainMatchPattern {
+ EMPTY, // No histograms were recorded.
+ F0M0L1, // D is a Safe Browsing match.
+ F0M1L0, // B or C, or both are Safe Browsing matches.
+ F0M1L1, // B or C, or both and D are Safe Browsing matches.
+ F1M0L0, // A is Safe Browsing match
+ F1M0L1, // A and D are Safe Browsing matches.
+ F1M1L0, // B and/or C and A are Safe Browsing matches.
+ F1M1L1, // B and/or C and A and D are Safe Browsing matches.
+ NO_REDIRECTS_HIT, // Redirect chain consists of single URL, aka no redirects
+ // has happened, and this URL was a Safe Browsing hit.
+ NUM_HIT_PATTERNS,
+};
struct ActivationListTestData {
- bool should_add;
+ bool expected_activation;
const char* const activation_list;
safe_browsing::SBThreatType threat_type;
safe_browsing::ThreatPatternType threat_type_metadata;
@@ -44,9 +72,6 @@ const ActivationListTestData kActivationListTestData[] = {
{false, subresource_filter::kActivationListSocialEngineeringAdsInterstitial,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
safe_browsing::ThreatPatternType::MALWARE_DISTRIBUTION},
- {true, subresource_filter::kActivationListSocialEngineeringAdsInterstitial,
- safe_browsing::SB_THREAT_TYPE_URL_MALWARE,
- safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
{false, subresource_filter::kActivationListPhishingInterstitial,
safe_browsing::SB_THREAT_TYPE_API_ABUSE,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
@@ -74,11 +99,40 @@ const ActivationListTestData kActivationListTestData[] = {
{true, subresource_filter::kActivationListPhishingInterstitial,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
safe_browsing::ThreatPatternType::NONE},
+ {true, subresource_filter::kActivationListSocialEngineeringAdsInterstitial,
+ safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
+ safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
};
-} // namespace
+struct ActivationScopeTestData {
+ bool expected_activation;
+ bool url_matches_activation_list;
+ const char* const activation_scope;
+};
-namespace subresource_filter {
+const ActivationScopeTestData kActivationScopeTestData[] = {
+ {true /* expected_activation */, false /* url_matches_activation_list */,
+ kActivationScopeAllSites},
+ {true /* expected_activation */, true /* url_matches_activation_list */,
+ kActivationScopeAllSites},
+ {false /* expected_activation */, true /* url_matches_activation_list */,
+ kActivationScopeNoSites},
+ {true /* expected_activation */, true /* url_matches_activation_list */,
+ kActivationScopeActivationList},
+ {false /* expected_activation */, false /* url_matches_activation_list */,
+ kActivationScopeActivationList},
+};
+
+struct ActivationStateTestData {
+ bool expected_activation;
+ const char* const activation_state;
+};
+
+const ActivationStateTestData kActivationStateTestData[] = {
+ {true /* expected_activation */, kActivationStateDryRun},
+ {true /* expected_activation */, kActivationStateEnabled},
+ {false /* expected_activation */, kActivationStateDisabled},
+};
class MockSubresourceFilterDriver : public ContentSubresourceFilterDriver {
public:
@@ -88,7 +142,8 @@ class MockSubresourceFilterDriver : public ContentSubresourceFilterDriver {
~MockSubresourceFilterDriver() override = default;
- MOCK_METHOD2(ActivateForProvisionalLoad, void(ActivationState, const GURL&));
+ MOCK_METHOD3(ActivateForProvisionalLoad,
+ void(ActivationState, const GURL&, bool));
private:
DISALLOW_COPY_AND_ASSIGN(MockSubresourceFilterDriver);
@@ -106,6 +161,8 @@ class MockSubresourceFilterClient : public SubresourceFilterClient {
DISALLOW_COPY_AND_ASSIGN(MockSubresourceFilterClient);
};
+} // namespace
+
class ContentSubresourceFilterDriverFactoryTest
: public content::RenderViewHostTestHarness {
public:
@@ -149,37 +206,65 @@ class ContentSubresourceFilterDriverFactoryTest
content::RenderFrameHost* subframe_rfh() { return subframe_rfh_; }
void BlacklistURLWithRedirectsNavigateAndCommit(
- const GURL& bad_url,
- const std::vector<GURL>& redirects,
- const GURL& url,
- bool should_activate) {
+ const std::vector<bool>& blacklisted_urls,
+ const std::vector<GURL>& navigation_chain,
+ safe_browsing::SBThreatType threat_type,
+ safe_browsing::ThreatPatternType threat_type_metadata,
+ RedirectChainMatchPattern extected_pattern,
+ bool expected_activation) {
+ base::HistogramTester tester;
EXPECT_CALL(*client(), ToggleNotificationVisibility(false)).Times(1);
- content::WebContentsTester::For(web_contents())->StartNavigation(url);
+ content::RenderFrameHostTester* rfh_tester =
+ content::RenderFrameHostTester::For(main_rfh());
+
+ rfh_tester->SimulateNavigationStart(navigation_chain.front());
+ if (blacklisted_urls.front()) {
+ factory()->OnMainResourceMatchedSafeBrowsingBlacklist(
+ navigation_chain.front(), navigation_chain, threat_type,
+ threat_type_metadata);
+ }
::testing::Mock::VerifyAndClearExpectations(client());
- factory()->OnMainResourceMatchedSafeBrowsingBlacklist(
- bad_url, redirects, safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
- safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS);
- ActivateAndExpectForFrameHostForUrl(driver(), main_rfh(), url,
- should_activate);
- content::RenderFrameHostTester::For(main_rfh())
- ->SimulateNavigationCommit(url);
- }
+ for (size_t i = 1; i < navigation_chain.size(); ++i) {
+ const GURL url = navigation_chain[i];
+ if (i < blacklisted_urls.size() && blacklisted_urls[i]) {
+ factory()->OnMainResourceMatchedSafeBrowsingBlacklist(
+ url, navigation_chain, threat_type, threat_type_metadata);
+ }
+ rfh_tester->SimulateRedirect(url);
+ }
+ EXPECT_CALL(*driver(),
+ ActivateForProvisionalLoad(::testing::_, ::testing::_,
+ expected_measure_performance()))
+ .Times(expected_activation);
+ if (!content::IsBrowserSideNavigationEnabled()) {
+ factory()->ReadyToCommitNavigationInternal(main_rfh(),
+ navigation_chain.back());
+ }
+
+ rfh_tester->SimulateNavigationCommit(navigation_chain.back());
+ ::testing::Mock::VerifyAndClearExpectations(driver());
- void ActivateAndExpectForFrameHostForUrl(MockSubresourceFilterDriver* driver,
- content::RenderFrameHost* rfh,
- const GURL& url,
- bool should_activate) {
- EXPECT_CALL(*driver, ActivateForProvisionalLoad(::testing::_, ::testing::_))
- .Times(should_activate);
- factory()->ReadyToCommitNavigationInternal(rfh, url);
- ::testing::Mock::VerifyAndClearExpectations(driver);
+ if (extected_pattern != EMPTY) {
+ EXPECT_THAT(tester.GetAllSamples(kMatchesPatternHistogramName),
+ ::testing::ElementsAre(base::Bucket(extected_pattern, 1)));
+ EXPECT_THAT(
+ tester.GetAllSamples(kNavigationChainSize),
+ ::testing::ElementsAre(base::Bucket(navigation_chain.size(), 1)));
+
+ } else {
+ EXPECT_THAT(tester.GetAllSamples(kMatchesPatternHistogramName),
+ ::testing::IsEmpty());
+ EXPECT_THAT(tester.GetAllSamples(kNavigationChainSize),
+ ::testing::IsEmpty());
+ }
}
- void NavigateAndCommitSubframe(const GURL& url, bool should_activate) {
+ void NavigateAndCommitSubframe(const GURL& url, bool expected_activation) {
EXPECT_CALL(*subframe_driver(),
- ActivateForProvisionalLoad(::testing::_, ::testing::_))
- .Times(should_activate);
+ ActivateForProvisionalLoad(::testing::_, ::testing::_,
+ expected_measure_performance()))
+ .Times(expected_activation);
EXPECT_CALL(*client(), ToggleNotificationVisibility(::testing::_)).Times(0);
factory()->ReadyToCommitNavigationInternal(subframe_rfh(), url);
@@ -187,34 +272,48 @@ class ContentSubresourceFilterDriverFactoryTest
::testing::Mock::VerifyAndClearExpectations(client());
}
- void BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- const GURL& bad_url,
- const std::vector<GURL>& redirects,
- const GURL& url,
- bool should_activate) {
- BlacklistURLWithRedirectsNavigateAndCommit(bad_url, redirects, url,
- should_activate);
+ void NavigateAndExpectActivation(
+ const std::vector<bool>& blacklisted_urls,
+ const std::vector<GURL>& navigation_chain,
+ safe_browsing::SBThreatType threat_type,
+ safe_browsing::ThreatPatternType threat_type_metadata,
+ RedirectChainMatchPattern extected_pattern,
+ bool expected_activation) {
+ BlacklistURLWithRedirectsNavigateAndCommit(
+ blacklisted_urls, navigation_chain, threat_type, threat_type_metadata,
+ extected_pattern, expected_activation);
+
+ NavigateAndCommitSubframe(GURL(kExampleLoginUrl), expected_activation);
+ }
- NavigateAndCommitSubframe(GURL(kExampleLoginUrl), should_activate);
+ void NavigateAndExpectActivation(const std::vector<bool>& blacklisted_urls,
+ const std::vector<GURL>& navigation_chain,
+ RedirectChainMatchPattern extected_pattern,
+ bool expected_activation) {
+ NavigateAndExpectActivation(
+ blacklisted_urls, navigation_chain,
+ safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
+ safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS,
+ extected_pattern, expected_activation);
}
- void SpecialCaseNavSeq(const GURL& bad_url,
- const std::vector<GURL>& redirects,
- bool should_activate) {
- // This test-case examinse the nevigation woth following sequence of event:
+ void EmulateInPageNavigation(const std::vector<bool>& blacklisted_urls,
+ RedirectChainMatchPattern extected_pattern,
+ bool expected_activation) {
+ // This test examines the navigation with the following sequence of events:
// DidStartProvisional(main, "example.com")
- // ReadyToCommitMainFrameNavigation(“example.comâ€)
+ // ReadyToCommitNavigation(“example.comâ€)
// DidCommitProvisional(main, "example.com")
// DidStartProvisional(sub, "example.com/login")
// DidCommitProvisional(sub, "example.com/login")
// DidCommitProvisional(main, "example.com#ref")
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- bad_url, redirects, GURL(kExampleUrl), should_activate);
- EXPECT_CALL(*driver(),
- ActivateForProvisionalLoad(::testing::_, ::testing::_))
+ NavigateAndExpectActivation(blacklisted_urls, {GURL(kExampleUrl)},
+ extected_pattern, expected_activation);
+ EXPECT_CALL(*driver(), ActivateForProvisionalLoad(
+ ::testing::_, ::testing::_, ::testing::_))
.Times(0);
- EXPECT_CALL(*client(), ToggleNotificationVisibility(::testing::_)).Times(1);
+ EXPECT_CALL(*client(), ToggleNotificationVisibility(::testing::_)).Times(0);
content::RenderFrameHostTester::For(main_rfh())
->SimulateNavigationCommit(GURL(kExampleUrl));
::testing::Mock::VerifyAndClearExpectations(driver());
@@ -222,6 +321,13 @@ class ContentSubresourceFilterDriverFactoryTest
}
private:
+ static bool expected_measure_performance() {
+ const double rate = GetPerformanceMeasurementRate();
+ // Note: The case when 0 < rate < 1 is not deterministic, don't test it.
+ EXPECT_TRUE(rate == 0 || rate == 1);
+ return rate == 1;
+ }
+
// Owned by the factory.
MockSubresourceFilterClient* client_;
MockSubresourceFilterDriver* driver_;
@@ -243,197 +349,236 @@ class ContentSubresourceFilterDriverFactoryThreatTypeTest
DISALLOW_COPY_AND_ASSIGN(ContentSubresourceFilterDriverFactoryThreatTypeTest);
};
-TEST_F(ContentSubresourceFilterDriverFactoryTest,
- ActivateForFrameHostNotNeeded) {
- base::FieldTrialList field_trial_list(nullptr);
- testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
- base::FeatureList::OVERRIDE_DISABLE_FEATURE, kActivationStateEnabled,
- kActivationScopeNoSites, kActivationListSocialEngineeringAdsInterstitial);
- ActivateAndExpectForFrameHostForUrl(driver(), main_rfh(), GURL(kTestUrl),
- false /* should_activate */);
- const GURL url(kExampleUrlWithParams);
- BlacklistURLWithRedirectsNavigateAndCommit(url, std::vector<GURL>(), url,
- false /* should_activate */);
- BlacklistURLWithRedirectsNavigateAndCommit(url, std::vector<GURL>(),
- GURL("https://not-example.com"),
- false /* should_activate */);
-}
-
-TEST_F(ContentSubresourceFilterDriverFactoryTest, ActivateForFrameHostNeeded) {
- base::FieldTrialList field_trial_list(nullptr);
- testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
- base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateEnabled,
- kActivationScopeActivationList,
- kActivationListSocialEngineeringAdsInterstitial);
-
- const GURL url(kExampleUrlWithParams);
- BlacklistURLWithRedirectsNavigateAndCommit(url, std::vector<GURL>(), url,
- true /* should_activate */);
- BlacklistURLWithRedirectsNavigateAndCommit(
- url, std::vector<GURL>(), GURL(kExampleUrl), false /* should_activate */);
-}
-
-TEST_P(ContentSubresourceFilterDriverFactoryThreatTypeTest, NonSocEngHit) {
- const ActivationListTestData& test_data = GetParam();
- base::FieldTrialList field_trial_list(nullptr);
- testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
- base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateEnabled,
- kActivationScopeNoSites, test_data.activation_list);
+class ContentSubresourceFilterDriverFactoryActivationScopeTest
+ : public ContentSubresourceFilterDriverFactoryTest,
+ public ::testing::WithParamInterface<ActivationScopeTestData> {
+ public:
+ ContentSubresourceFilterDriverFactoryActivationScopeTest() {}
+ ~ContentSubresourceFilterDriverFactoryActivationScopeTest() override {}
- std::vector<GURL> redirects;
- redirects.push_back(GURL("https://example1.com"));
- redirects.push_back(GURL("https://example2.com"));
- redirects.push_back(GURL("https://example3.com"));
+ private:
+ DISALLOW_COPY_AND_ASSIGN(
+ ContentSubresourceFilterDriverFactoryActivationScopeTest);
+};
- const GURL test_url("https://example.com/nonsoceng?q=engsocnon");
+class ContentSubresourceFilterDriverFactoryActivationStateTest
+ : public ContentSubresourceFilterDriverFactoryTest,
+ public ::testing::WithParamInterface<ActivationStateTestData> {
+ public:
+ ContentSubresourceFilterDriverFactoryActivationStateTest() {}
+ ~ContentSubresourceFilterDriverFactoryActivationStateTest() override {}
- BlacklistURLWithRedirectsNavigateAndCommit(GURL::EmptyGURL(),
- std::vector<GURL>(), test_url,
- false /* should_activate */);
- EXPECT_CALL(*client(), ToggleNotificationVisibility(false)).Times(1);
- content::WebContentsTester::For(web_contents())->StartNavigation(test_url);
- ::testing::Mock::VerifyAndClearExpectations(client());
- factory()->OnMainResourceMatchedSafeBrowsingBlacklist(
- test_url, redirects, test_data.threat_type,
- test_data.threat_type_metadata);
- ActivateAndExpectForFrameHostForUrl(driver(), main_rfh(), test_url, false);
- content::RenderFrameHostTester::For(main_rfh())
- ->SimulateNavigationCommit(test_url);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(
+ ContentSubresourceFilterDriverFactoryActivationStateTest);
};
TEST_F(ContentSubresourceFilterDriverFactoryTest,
- ActivationPromptNotShownForNonMainFrames) {
+ ActivateForFrameHostDisabledFeature) {
+ // Activation scope is set to NONE => no activation should happen even if URL
+ // which is visited was a SB hit.
base::FieldTrialList field_trial_list(nullptr);
testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
- base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateEnabled,
- kActivationScopeActivationList,
- kActivationListSocialEngineeringAdsInterstitial);
- BlacklistURLWithRedirectsNavigateAndCommit(
- GURL::EmptyGURL(), std::vector<GURL>(), GURL(kExampleUrl),
- false /* should_prompt */);
-}
-
-TEST_F(ContentSubresourceFilterDriverFactoryTest,
- DidStartProvisionalLoadScopeAllSitesStateDryRun) {
- base::FieldTrialList field_trial_list(nullptr);
- testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
- base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateDryRun,
+ base::FeatureList::OVERRIDE_DISABLE_FEATURE, kActivationStateEnabled,
kActivationScopeAllSites,
kActivationListSocialEngineeringAdsInterstitial);
const GURL url(kExampleUrlWithParams);
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- url, std::vector<GURL>(), url, true /* should_activate */);
+ NavigateAndExpectActivation({true}, {url}, EMPTY,
+ false /* expected_activation */);
factory()->AddHostOfURLToWhitelistSet(url);
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- url, std::vector<GURL>(), GURL(kExampleUrlWithParams),
- false /* should_activate */);
+ NavigateAndExpectActivation({true}, {url}, EMPTY,
+ false /* expected_activation */);
}
-TEST_F(ContentSubresourceFilterDriverFactoryTest,
- DidStartProvisionalLoadScopeAllSitesStateEnabled) {
+TEST_F(ContentSubresourceFilterDriverFactoryTest, NoActivationWhenNoMatch) {
base::FieldTrialList field_trial_list(nullptr);
testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateEnabled,
- kActivationScopeAllSites,
+ kActivationScopeActivationList,
kActivationListSocialEngineeringAdsInterstitial);
- const GURL url(kExampleUrlWithParams);
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- url, std::vector<GURL>(), url, true /* should_activate */);
- factory()->AddHostOfURLToWhitelistSet(url);
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- url, std::vector<GURL>(), GURL(kExampleUrlWithParams),
- false /* should_activate */);
+ NavigateAndExpectActivation({false}, {GURL(kExampleUrl)}, EMPTY,
+ false /* should_prompt */);
}
TEST_F(ContentSubresourceFilterDriverFactoryTest,
- DidStartProvisionalLoadScopeActivationListSitesStateEnabled) {
+ SpecialCaseNavigationAllSitesEnabled) {
+ // Check that when the experiment is enabled for all site, the activation
+ // signal is always sent.
base::FieldTrialList field_trial_list(nullptr);
testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateEnabled,
- kActivationScopeActivationList,
- kActivationListSocialEngineeringAdsInterstitial);
- const GURL url(kExampleUrlWithParams);
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- url, std::vector<GURL>(), url, true /* should_activate */);
- factory()->AddHostOfURLToWhitelistSet(url);
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- url, std::vector<GURL>(), GURL(kExampleUrlWithParams),
- false /* should_activate */);
+ kActivationScopeAllSites);
+ EmulateInPageNavigation({false}, EMPTY, true /* expected_activation */);
}
TEST_F(ContentSubresourceFilterDriverFactoryTest,
- DidStartProvisionalLoadScopeActivationListSitesStateDryRun) {
+ SpecialCaseNavigationActivationListEnabled) {
base::FieldTrialList field_trial_list(nullptr);
testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
- base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateDryRun,
+ base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateEnabled,
kActivationScopeActivationList,
kActivationListSocialEngineeringAdsInterstitial);
- const GURL url(kExampleUrlWithParams);
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- url, std::vector<GURL>(), url, true /* should_activate */);
- factory()->AddHostOfURLToWhitelistSet(url);
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- url, std::vector<GURL>(), url, false /* should_activate */);
+ EmulateInPageNavigation({true}, NO_REDIRECTS_HIT,
+ true /* expected_activation */);
}
TEST_F(ContentSubresourceFilterDriverFactoryTest,
- DidStartProvisionalLoadScopeNoSites) {
+ SpecialCaseNavigationActivationListEnabledWithPerformanceMeasurement) {
base::FieldTrialList field_trial_list(nullptr);
testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
- base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateDryRun,
- kActivationScopeNoSites, kActivationListSocialEngineeringAdsInterstitial);
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- GURL::EmptyGURL(), std::vector<GURL>(), GURL(kExampleUrlWithParams),
- false /* should_activate */);
+ base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateEnabled,
+ kActivationScopeActivationList,
+ kActivationListSocialEngineeringAdsInterstitial,
+ "1" /* performance_measurement_rate */);
+ EmulateInPageNavigation({true}, NO_REDIRECTS_HIT,
+ true /* expected_activation */);
}
-TEST_F(ContentSubresourceFilterDriverFactoryTest,
- DidStartProvisionalLoadScopeActivationList) {
+TEST_F(ContentSubresourceFilterDriverFactoryTest, RedirectPatternTest) {
base::FieldTrialList field_trial_list(nullptr);
testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
- base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateDisabled,
+ base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateEnabled,
kActivationScopeActivationList,
kActivationListSocialEngineeringAdsInterstitial);
- BlacklistURLWithRedirectsNavigateMainFrameAndSubrame(
- GURL::EmptyGURL(), std::vector<GURL>(), GURL(kExampleUrlWithParams),
- false /* should_activate */);
+ struct RedirectRedirectChainMatchPatternTestData {
+ std::vector<bool> blacklisted_urls;
+ std::vector<GURL> navigation_chain;
+ RedirectChainMatchPattern hit_extected_pattern;
+ bool expected_activation;
+ } kRedirectRedirectChainMatchPatternTestData[] = {
+ {{false}, {GURL(kUrlA)}, EMPTY, false},
+ {{true}, {GURL(kUrlA)}, NO_REDIRECTS_HIT, true},
+ {{false, false}, {GURL(kUrlA), GURL(kUrlB)}, EMPTY, false},
+ {{false, true}, {GURL(kUrlA), GURL(kUrlB)}, F0M0L1, true},
+ {{true, false}, {GURL(kUrlA), GURL(kUrlB)}, F1M0L0, false},
+ {{true, true}, {GURL(kUrlA), GURL(kUrlB)}, F1M0L1, true},
+
+ {{false, false, false},
+ {GURL(kUrlA), GURL(kUrlB), GURL(kUrlC)},
+ EMPTY,
+ false},
+ {{false, false, true},
+ {GURL(kUrlA), GURL(kUrlB), GURL(kUrlC)},
+ F0M0L1,
+ true},
+ {{false, true, false},
+ {GURL(kUrlA), GURL(kUrlB), GURL(kUrlC)},
+ F0M1L0,
+ false},
+ {{false, true, true},
+ {GURL(kUrlA), GURL(kUrlB), GURL(kUrlC)},
+ F0M1L1,
+ true},
+ {{true, false, false},
+ {GURL(kUrlA), GURL(kUrlB), GURL(kUrlC)},
+ F1M0L0,
+ false},
+ {{true, false, true},
+ {GURL(kUrlA), GURL(kUrlB), GURL(kUrlC)},
+ F1M0L1,
+ true},
+ {{true, true, false},
+ {GURL(kUrlA), GURL(kUrlB), GURL(kUrlC)},
+ F1M1L0,
+ false},
+ {{true, true, true},
+ {GURL(kUrlA), GURL(kUrlB), GURL(kUrlC)},
+ F1M1L1,
+ true},
+ {{false, true, false, false},
+ {GURL(kUrlA), GURL(kUrlB), GURL(kUrlC), GURL(kUrlD)},
+ F0M1L0,
+ false},
+ };
+
+ for (size_t i = 0U; i < arraysize(kRedirectRedirectChainMatchPatternTestData);
+ ++i) {
+ auto test_data = kRedirectRedirectChainMatchPatternTestData[i];
+ NavigateAndExpectActivation(
+ test_data.blacklisted_urls, test_data.navigation_chain,
+ safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
+ safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS,
+ test_data.hit_extected_pattern, test_data.expected_activation);
+ NavigateAndExpectActivation({false}, {GURL("https://dummy.com")}, EMPTY,
+ false);
+ }
}
-TEST_F(ContentSubresourceFilterDriverFactoryTest,
- SpecialCaseNavigationAllSitesDryRun) {
+TEST_P(ContentSubresourceFilterDriverFactoryActivationStateTest,
+ ActivateForFrameState) {
+ const ActivationStateTestData& test_data = GetParam();
base::FieldTrialList field_trial_list(nullptr);
testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
- base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateDryRun,
- kActivationScopeAllSites);
- SpecialCaseNavSeq(GURL::EmptyGURL(), std::vector<GURL>(),
- true /* should_activate */);
+ base::FeatureList::OVERRIDE_ENABLE_FEATURE, test_data.activation_state,
+ kActivationScopeActivationList,
+ kActivationListSocialEngineeringAdsInterstitial);
+
+ const GURL url(kExampleUrlWithParams);
+ NavigateAndExpectActivation({true}, {url}, NO_REDIRECTS_HIT,
+ test_data.expected_activation);
+ factory()->AddHostOfURLToWhitelistSet(url);
+ NavigateAndExpectActivation({true}, {GURL(kExampleUrlWithParams)},
+ NO_REDIRECTS_HIT,
+ false /* expected_activation */);
}
-TEST_F(ContentSubresourceFilterDriverFactoryTest,
- SpecialCaseNavigationAllSitesEnabled) {
- // Check that when the experiment is enabled for all site, the activation
- // signal is always sent.
+TEST_P(ContentSubresourceFilterDriverFactoryThreatTypeTest,
+ ActivateForTheListType) {
+ // Sets up the experiment in a way that the activation decision depends on the
+ // list for which the Safe Browsing hit has happened.
+ const ActivationListTestData& test_data = GetParam();
base::FieldTrialList field_trial_list(nullptr);
testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateEnabled,
- kActivationScopeAllSites);
- SpecialCaseNavSeq(GURL::EmptyGURL(), std::vector<GURL>(),
- true /* should_activate */);
-}
+ kActivationScopeActivationList, test_data.activation_list);
-TEST_F(ContentSubresourceFilterDriverFactoryTest,
- SpecialCaseNavigationActivationListEnabled) {
+ const GURL test_url("https://example.com/nonsoceng?q=engsocnon");
+ std::vector<GURL> navigation_chain;
+
+ NavigateAndExpectActivation({false, false, false, true},
+ {GURL(kUrlA), GURL(kUrlB), GURL(kUrlC), test_url},
+ test_data.threat_type,
+ test_data.threat_type_metadata,
+ test_data.expected_activation ? F0M0L1 : EMPTY,
+ test_data.expected_activation);
+};
+
+TEST_P(ContentSubresourceFilterDriverFactoryActivationScopeTest,
+ ActivateForScopeType) {
+ const ActivationScopeTestData& test_data = GetParam();
base::FieldTrialList field_trial_list(nullptr);
testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationStateEnabled,
- kActivationScopeActivationList,
+ test_data.activation_scope,
kActivationListSocialEngineeringAdsInterstitial);
- SpecialCaseNavSeq(GURL(kExampleUrl), std::vector<GURL>(),
- true /* should_activate */);
-}
-INSTANTIATE_TEST_CASE_P(NoSonEngHit,
+ const GURL test_url(kExampleUrlWithParams);
+
+ RedirectChainMatchPattern extected_pattern =
+ test_data.url_matches_activation_list ? NO_REDIRECTS_HIT : EMPTY;
+ NavigateAndExpectActivation({test_data.url_matches_activation_list},
+ {test_url}, extected_pattern,
+ test_data.expected_activation);
+ if (test_data.url_matches_activation_list) {
+ factory()->AddHostOfURLToWhitelistSet(test_url);
+ NavigateAndExpectActivation({test_data.url_matches_activation_list},
+ {GURL(kExampleUrlWithParams)}, extected_pattern,
+ false /* expected_activation */);
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(NoSocEngHit,
ContentSubresourceFilterDriverFactoryThreatTypeTest,
::testing::ValuesIn(kActivationListTestData));
+INSTANTIATE_TEST_CASE_P(
+ ActivationScopeTest,
+ ContentSubresourceFilterDriverFactoryActivationScopeTest,
+ ::testing::ValuesIn(kActivationScopeTestData));
+
+INSTANTIATE_TEST_CASE_P(
+ ActivationStateTest,
+ ContentSubresourceFilterDriverFactoryActivationStateTest,
+ ::testing::ValuesIn(kActivationStateTestData));
+
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/common/BUILD.gn b/chromium/components/subresource_filter/content/common/BUILD.gn
index a6aee28496a..09e15ba07fc 100644
--- a/chromium/components/subresource_filter/content/common/BUILD.gn
+++ b/chromium/components/subresource_filter/content/common/BUILD.gn
@@ -4,6 +4,7 @@
static_library("common") {
sources = [
+ "document_load_statistics.h",
"subresource_filter_message_generator.cc",
"subresource_filter_message_generator.h",
"subresource_filter_messages.h",
diff --git a/chromium/components/subresource_filter/content/common/document_load_statistics.h b/chromium/components/subresource_filter/content/common/document_load_statistics.h
new file mode 100644
index 00000000000..e05f31666f7
--- /dev/null
+++ b/chromium/components/subresource_filter/content/common/document_load_statistics.h
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SUBRESOURCE_FILTER_CONTENT_COMMON_DOCUMENT_LOAD_STATISTICS_H_
+#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_COMMON_DOCUMENT_LOAD_STATISTICS_H_
+
+#include <stdint.h>
+
+#include "base/time/time.h"
+
+namespace subresource_filter {
+
+// Contains statistics and performance metrics collected by the
+// DocumentSubresourceFilter.
+struct DocumentLoadStatistics {
+ // The number of subresource loads that went through the
+ // DocumentSubresouceFilter::allowLoad method.
+ int32_t num_loads_total = 0;
+
+ // Statistics on the number of subresource loads that were evaluated, were
+ // matched by filtering rules, and were disallowed, respectively, during the
+ // lifetime of a DocumentSubresourceFilter.
+ int32_t num_loads_evaluated = 0;
+ int32_t num_loads_matching_rules = 0;
+ int32_t num_loads_disallowed = 0;
+
+ // Total time spent in allowLoad() calls while evaluating subresource loads.
+ base::TimeDelta evaluation_total_wall_duration;
+ base::TimeDelta evaluation_total_cpu_duration;
+};
+
+} // namespace subresource_filter
+
+#endif // COMPONENTS_SUBRESOURCE_FILTER_CONTENT_COMMON_DOCUMENT_LOAD_STATISTICS_H_
diff --git a/chromium/components/subresource_filter/content/common/subresource_filter_messages.h b/chromium/components/subresource_filter/content/common/subresource_filter_messages.h
index d70e6fea7d0..c1c92acf6c0 100644
--- a/chromium/components/subresource_filter/content/common/subresource_filter_messages.h
+++ b/chromium/components/subresource_filter/content/common/subresource_filter_messages.h
@@ -4,6 +4,8 @@
// Message definition file, included multiple times, hence no include guard.
+#include "base/time/time.h"
+#include "components/subresource_filter/content/common/document_load_statistics.h"
#include "components/subresource_filter/core/common/activation_state.h"
#include "content/public/common/common_param_traits_macros.h"
#include "ipc/ipc_message_macros.h"
@@ -16,6 +18,15 @@
IPC_ENUM_TRAITS_MAX_VALUE(subresource_filter::ActivationState,
subresource_filter::ActivationState::LAST);
+IPC_STRUCT_TRAITS_BEGIN(subresource_filter::DocumentLoadStatistics)
+ IPC_STRUCT_TRAITS_MEMBER(num_loads_total)
+ IPC_STRUCT_TRAITS_MEMBER(num_loads_evaluated)
+ IPC_STRUCT_TRAITS_MEMBER(num_loads_matching_rules)
+ IPC_STRUCT_TRAITS_MEMBER(num_loads_disallowed)
+ IPC_STRUCT_TRAITS_MEMBER(evaluation_total_wall_duration)
+ IPC_STRUCT_TRAITS_MEMBER(evaluation_total_cpu_duration)
+IPC_STRUCT_TRAITS_END()
+
// ----------------------------------------------------------------------------
// Messages sent from the browser to the renderer.
// ----------------------------------------------------------------------------
@@ -30,9 +41,10 @@ IPC_MESSAGE_CONTROL1(SubresourceFilterMsg_SetRulesetForProcess,
// ongoing provisional document load in a frame. The message must arrive after
// the provisional load starts, but before it is committed on the renderer side.
// If no message arrives, the default behavior is ActivationState::DISABLED.
-IPC_MESSAGE_ROUTED2(SubresourceFilterMsg_ActivateForProvisionalLoad,
+IPC_MESSAGE_ROUTED3(SubresourceFilterMsg_ActivateForProvisionalLoad,
subresource_filter::ActivationState /* activation_state */,
- GURL /* url */);
+ GURL /* url */,
+ bool /* measure_performance */);
// ----------------------------------------------------------------------------
// Messages sent from the renderer to the browser.
@@ -41,4 +53,14 @@ IPC_MESSAGE_ROUTED2(SubresourceFilterMsg_ActivateForProvisionalLoad,
// Sent to the browser the first time a subresource load is disallowed for the
// most recently commited document load in a frame. It is used to trigger a
// UI prompt to inform the user and allow them to turn off filtering.
-IPC_MESSAGE_ROUTED0(SubresourceFilterHostMsg_DidDisallowFirstSubresource)
+IPC_MESSAGE_ROUTED0(SubresourceFilterHostMsg_DidDisallowFirstSubresource);
+
+// This is sent to a RenderFrameHost in the browser when a document load is
+// finished, just before the DidFinishLoad message, and contains statistics
+// collected by the DocumentSubresourceFilter up until that point: the number of
+// subresources evaluated/disallowed/etc, and total time spent on evaluating
+// subresource loads in its allowLoad method. The time metrics are equal to zero
+// if performance measurements were disabled for the load.
+IPC_MESSAGE_ROUTED1(
+ SubresourceFilterHostMsg_DocumentLoadStatistics,
+ subresource_filter::DocumentLoadStatistics /* statistics */);
diff --git a/chromium/components/subresource_filter/content/renderer/document_subresource_filter.cc b/chromium/components/subresource_filter/content/renderer/document_subresource_filter.cc
index 8888befb49b..3debaeaec4e 100644
--- a/chromium/components/subresource_filter/content/renderer/document_subresource_filter.cc
+++ b/chromium/components/subresource_filter/content/renderer/document_subresource_filter.cc
@@ -12,6 +12,8 @@
#include "base/trace_event/trace_event.h"
#include "components/subresource_filter/core/common/first_party_origin.h"
#include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
+#include "components/subresource_filter/core/common/scoped_timers.h"
+#include "components/subresource_filter/core/common/time_measurements.h"
#include "third_party/WebKit/public/platform/WebURL.h"
namespace subresource_filter {
@@ -75,10 +77,12 @@ proto::ElementType ToElementType(
DocumentSubresourceFilter::DocumentSubresourceFilter(
ActivationState activation_state,
+ bool measure_performance,
const scoped_refptr<const MemoryMappedRuleset>& ruleset,
const std::vector<GURL>& ancestor_document_urls,
const base::Closure& first_disallowed_load_callback)
: activation_state_(activation_state),
+ measure_performance_(measure_performance),
ruleset_(ruleset),
ruleset_matcher_(ruleset_->data(), ruleset_->length()),
first_disallowed_load_callback_(first_disallowed_load_callback) {
@@ -87,6 +91,11 @@ DocumentSubresourceFilter::DocumentSubresourceFilter(
? std::string()
: ancestor_document_urls[0].spec());
+ SCOPED_UMA_HISTOGRAM_MICRO_TIMER(
+ "SubresourceFilter.DocumentLoad.Activation.WallDuration");
+ SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER(
+ "SubresourceFilter.DocumentLoad.Activation.CPUDuration");
+
DCHECK_NE(activation_state_, ActivationState::DISABLED);
DCHECK(ruleset);
@@ -124,7 +133,21 @@ bool DocumentSubresourceFilter::allowLoad(
TRACE_EVENT1("loader", "DocumentSubresourceFilter::allowLoad", "url",
resourceUrl.string().utf8());
- ++num_loads_total_;
+ auto wall_duration_timer = ScopedTimers::StartIf(
+ measure_performance_ && ScopedThreadTimers::IsSupported(),
+ [this](base::TimeDelta delta) {
+ statistics_.evaluation_total_wall_duration += delta;
+ UMA_HISTOGRAM_MICRO_TIMES(
+ "SubresourceFilter.SubresourceLoad.Evaluation.WallDuration", delta);
+ });
+ auto cpu_duration_timer = ScopedThreadTimers::StartIf(
+ measure_performance_, [this](base::TimeDelta delta) {
+ statistics_.evaluation_total_cpu_duration += delta;
+ UMA_HISTOGRAM_MICRO_TIMES(
+ "SubresourceFilter.SubresourceLoad.Evaluation.CPUDuration", delta);
+ });
+
+ ++statistics_.num_loads_total;
if (filtering_disabled_for_document_)
return true;
@@ -132,19 +155,19 @@ bool DocumentSubresourceFilter::allowLoad(
if (resourceUrl.protocolIs(url::kDataScheme))
return true;
- ++num_loads_evaluated_;
+ ++statistics_.num_loads_evaluated;
DCHECK(document_origin_);
if (ruleset_matcher_.ShouldDisallowResourceLoad(
GURL(resourceUrl), *document_origin_, ToElementType(request_context),
generic_blocking_rules_disabled_)) {
- ++num_loads_matching_rules_;
+ ++statistics_.num_loads_matching_rules;
if (activation_state_ == ActivationState::ENABLED) {
if (!first_disallowed_load_callback_.is_null()) {
- DCHECK_EQ(num_loads_disallowed_, 0u);
+ DCHECK_EQ(statistics_.num_loads_disallowed, 0);
first_disallowed_load_callback_.Run();
first_disallowed_load_callback_.Reset();
}
- ++num_loads_disallowed_;
+ ++statistics_.num_loads_disallowed;
return false;
}
}
diff --git a/chromium/components/subresource_filter/content/renderer/document_subresource_filter.h b/chromium/components/subresource_filter/content/renderer/document_subresource_filter.h
index 6cea6bc24b0..8c15cb8fe1f 100644
--- a/chromium/components/subresource_filter/content/renderer/document_subresource_filter.h
+++ b/chromium/components/subresource_filter/content/renderer/document_subresource_filter.h
@@ -13,6 +13,8 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "components/subresource_filter/content/common/document_load_statistics.h"
#include "components/subresource_filter/core/common/activation_state.h"
#include "components/subresource_filter/core/common/indexed_ruleset.h"
#include "third_party/WebKit/public/platform/WebDocumentSubresourceFilter.h"
@@ -41,26 +43,22 @@ class DocumentSubresourceFilter
// first disallowed subresource load.
DocumentSubresourceFilter(
ActivationState activation_state,
+ bool measure_performance,
const scoped_refptr<const MemoryMappedRuleset>& ruleset,
const std::vector<GURL>& ancestor_document_urls,
const base::Closure& first_disallowed_load_callback);
~DocumentSubresourceFilter() override;
+ const DocumentLoadStatistics& statistics() const { return statistics_; }
+
// blink::WebDocumentSubresourceFilter:
bool allowLoad(const blink::WebURL& resourceUrl,
blink::WebURLRequest::RequestContext) override;
- // The number of subresource loads that went through the allowLoad method.
- size_t num_loads_total() const { return num_loads_total_; }
- // Statistics on the number of subresource loads that were evaluated, were
- // matched by filtering rules, and were disallowed, respectively, during the
- // lifetime of |this| filter.
- size_t num_loads_evaluated() const { return num_loads_evaluated_; }
- size_t num_loads_matching_rules() const { return num_loads_matching_rules_; }
- size_t num_loads_disallowed() const { return num_loads_disallowed_; }
-
private:
- ActivationState activation_state_;
+ const ActivationState activation_state_;
+ const bool measure_performance_;
+
scoped_refptr<const MemoryMappedRuleset> ruleset_;
IndexedRulesetMatcher ruleset_matcher_;
@@ -69,11 +67,6 @@ class DocumentSubresourceFilter
base::Closure first_disallowed_load_callback_;
- size_t num_loads_total_ = 0;
- size_t num_loads_evaluated_ = 0;
- size_t num_loads_matching_rules_ = 0;
- size_t num_loads_disallowed_ = 0;
-
// Even when subresource filtering is activated at the page level by the
// |activation_state| passed into the constructor, the current document or
// ancestors thereof may still match special filtering rules that specifically
@@ -89,6 +82,8 @@ class DocumentSubresourceFilter
// |filtering_disabled_for_document_|.
bool generic_blocking_rules_disabled_ = false;
+ DocumentLoadStatistics statistics_;
+
DISALLOW_COPY_AND_ASSIGN(DocumentSubresourceFilter);
};
diff --git a/chromium/components/subresource_filter/content/renderer/document_subresource_filter_unittest.cc b/chromium/components/subresource_filter/content/renderer/document_subresource_filter_unittest.cc
index 97ecc439161..6bb11d74897 100644
--- a/chromium/components/subresource_filter/content/renderer/document_subresource_filter_unittest.cc
+++ b/chromium/components/subresource_filter/content/renderer/document_subresource_filter_unittest.cc
@@ -79,30 +79,48 @@ TEST_F(DocumentSubresourceFilterTest, DryRun) {
blink::WebURLRequest::RequestContextImage;
TestCallbackReceiver first_disallowed_load_callback_receiver;
DocumentSubresourceFilter filter(
- ActivationState::DRYRUN, ruleset(), std::vector<GURL>(),
+ ActivationState::DRYRUN, true, ruleset(), std::vector<GURL>(),
first_disallowed_load_callback_receiver.closure());
EXPECT_TRUE(filter.allowLoad(GURL(kTestAlphaURL), request_context));
EXPECT_TRUE(filter.allowLoad(GURL(kTestAlphaDataURI), request_context));
EXPECT_TRUE(filter.allowLoad(GURL(kTestBetaURL), request_context));
- EXPECT_EQ(3u, filter.num_loads_total());
- EXPECT_EQ(2u, filter.num_loads_evaluated());
- EXPECT_EQ(1u, filter.num_loads_matching_rules());
- EXPECT_EQ(0u, filter.num_loads_disallowed());
+
+ const auto& statistics = filter.statistics();
+ EXPECT_EQ(3, statistics.num_loads_total);
+ EXPECT_EQ(2, statistics.num_loads_evaluated);
+ EXPECT_EQ(1, statistics.num_loads_matching_rules);
+ EXPECT_EQ(0, statistics.num_loads_disallowed);
+
EXPECT_EQ(0u, first_disallowed_load_callback_receiver.callback_count());
}
TEST_F(DocumentSubresourceFilterTest, Enabled) {
- blink::WebURLRequest::RequestContext request_context =
- blink::WebURLRequest::RequestContextImage;
- DocumentSubresourceFilter filter(ActivationState::ENABLED, ruleset(),
- std::vector<GURL>(), base::Closure());
- EXPECT_FALSE(filter.allowLoad(GURL(kTestAlphaURL), request_context));
- EXPECT_TRUE(filter.allowLoad(GURL(kTestAlphaDataURI), request_context));
- EXPECT_TRUE(filter.allowLoad(GURL(kTestBetaURL), request_context));
- EXPECT_EQ(3u, filter.num_loads_total());
- EXPECT_EQ(2u, filter.num_loads_evaluated());
- EXPECT_EQ(1u, filter.num_loads_matching_rules());
- EXPECT_EQ(1u, filter.num_loads_disallowed());
+ auto test_impl = [this](bool measure_performance) {
+ blink::WebURLRequest::RequestContext request_context =
+ blink::WebURLRequest::RequestContextImage;
+ DocumentSubresourceFilter filter(ActivationState::ENABLED,
+ measure_performance, ruleset(),
+ std::vector<GURL>(), base::Closure());
+ EXPECT_FALSE(filter.allowLoad(GURL(kTestAlphaURL), request_context));
+ EXPECT_TRUE(filter.allowLoad(GURL(kTestAlphaDataURI), request_context));
+ EXPECT_TRUE(filter.allowLoad(GURL(kTestBetaURL), request_context));
+
+ const auto& statistics = filter.statistics();
+ EXPECT_EQ(3, statistics.num_loads_total);
+ EXPECT_EQ(2, statistics.num_loads_evaluated);
+ EXPECT_EQ(1, statistics.num_loads_matching_rules);
+ EXPECT_EQ(1, statistics.num_loads_disallowed);
+
+ if (!measure_performance) {
+ EXPECT_TRUE(statistics.evaluation_total_cpu_duration.is_zero());
+ EXPECT_TRUE(statistics.evaluation_total_wall_duration.is_zero());
+ }
+ // Otherwise, don't expect |total_duration| to be non-zero, although it
+ // practically is (when timer is supported).
+ };
+
+ test_impl(true /* measure_performance */);
+ test_impl(false /* measure_performance */);
}
TEST_F(DocumentSubresourceFilterTest,
@@ -111,7 +129,7 @@ TEST_F(DocumentSubresourceFilterTest,
blink::WebURLRequest::RequestContextImage;
TestCallbackReceiver first_disallowed_load_callback_receiver;
DocumentSubresourceFilter filter(
- ActivationState::ENABLED, ruleset(), std::vector<GURL>(),
+ ActivationState::ENABLED, true, ruleset(), std::vector<GURL>(),
first_disallowed_load_callback_receiver.closure());
EXPECT_TRUE(filter.allowLoad(GURL(kTestAlphaDataURI), request_context));
EXPECT_EQ(0u, first_disallowed_load_callback_receiver.callback_count());
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 7dd1d8cffb1..48f19565897 100644
--- a/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc
+++ b/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc
@@ -7,10 +7,13 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_macros.h"
+#include "base/time/time.h"
#include "components/subresource_filter/content/common/subresource_filter_messages.h"
#include "components/subresource_filter/content/renderer/document_subresource_filter.h"
#include "components/subresource_filter/content/renderer/ruleset_dealer.h"
#include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
+#include "components/subresource_filter/core/common/scoped_timers.h"
+#include "components/subresource_filter/core/common/time_measurements.h"
#include "content/public/common/browser_side_navigation_policy.h"
#include "content/public/renderer/render_frame.h"
#include "ipc/ipc_message.h"
@@ -58,11 +61,19 @@ void SubresourceFilterAgent::
render_frame()->GetRoutingID()));
}
+void SubresourceFilterAgent::SendDocumentLoadStatistics(
+ const DocumentLoadStatistics& statistics) {
+ render_frame()->Send(new SubresourceFilterHostMsg_DocumentLoadStatistics(
+ render_frame()->GetRoutingID(), statistics));
+}
+
void SubresourceFilterAgent::OnActivateForProvisionalLoad(
ActivationState activation_state,
- const GURL& url) {
+ const GURL& url,
+ bool measure_performance) {
activation_state_for_provisional_load_ = activation_state;
url_for_provisional_load_ = url;
+ measure_performance_ = measure_performance;
}
void SubresourceFilterAgent::RecordHistogramsOnLoadCommitted() {
@@ -79,18 +90,41 @@ void SubresourceFilterAgent::RecordHistogramsOnLoadCommitted() {
void SubresourceFilterAgent::RecordHistogramsOnLoadFinished() {
DCHECK(filter_for_last_committed_load_);
+ const auto& statistics = filter_for_last_committed_load_->statistics();
+
UMA_HISTOGRAM_COUNTS_1000(
"SubresourceFilter.DocumentLoad.NumSubresourceLoads.Total",
- filter_for_last_committed_load_->num_loads_total());
+ statistics.num_loads_total);
UMA_HISTOGRAM_COUNTS_1000(
"SubresourceFilter.DocumentLoad.NumSubresourceLoads.Evaluated",
- filter_for_last_committed_load_->num_loads_evaluated());
+ statistics.num_loads_evaluated);
UMA_HISTOGRAM_COUNTS_1000(
"SubresourceFilter.DocumentLoad.NumSubresourceLoads.MatchedRules",
- filter_for_last_committed_load_->num_loads_matching_rules());
+ statistics.num_loads_matching_rules);
UMA_HISTOGRAM_COUNTS_1000(
"SubresourceFilter.DocumentLoad.NumSubresourceLoads.Disallowed",
- filter_for_last_committed_load_->num_loads_disallowed());
+ statistics.num_loads_disallowed);
+
+ // If ThreadTicks is not supported or performance measuring is switched off,
+ // then no time measurements have been collected.
+ if (measure_performance_ && ScopedThreadTimers::IsSupported()) {
+ UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
+ "SubresourceFilter.DocumentLoad.SubresourceEvaluation."
+ "TotalWallDuration",
+ statistics.evaluation_total_wall_duration,
+ base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
+ 50);
+ UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
+ "SubresourceFilter.DocumentLoad.SubresourceEvaluation.TotalCPUDuration",
+ statistics.evaluation_total_cpu_duration,
+ base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
+ 50);
+ } else {
+ DCHECK(statistics.evaluation_total_wall_duration.is_zero());
+ DCHECK(statistics.evaluation_total_cpu_duration.is_zero());
+ }
+
+ SendDocumentLoadStatistics(statistics);
}
void SubresourceFilterAgent::OnDestruct() {
@@ -108,8 +142,10 @@ void SubresourceFilterAgent::DidStartProvisionalLoad() {
: nullptr;
if (!content::IsBrowserSideNavigationEnabled() ||
(!ds ||
- static_cast<GURL>(ds->request().url()) != url_for_provisional_load_)) {
+ static_cast<GURL>(ds->getRequest().url()) !=
+ url_for_provisional_load_)) {
activation_state_for_provisional_load_ = ActivationState::DISABLED;
+ measure_performance_ = false;
} else {
url_for_provisional_load_ = GURL();
}
@@ -119,21 +155,27 @@ void SubresourceFilterAgent::DidStartProvisionalLoad() {
void SubresourceFilterAgent::DidCommitProvisionalLoad(
bool is_new_navigation,
bool is_same_page_navigation) {
- RecordHistogramsOnLoadCommitted();
- if (activation_state_for_provisional_load_ != ActivationState::DISABLED &&
- ruleset_dealer_->IsRulesetAvailable()) {
- std::vector<GURL> ancestor_document_urls = GetAncestorDocumentURLs();
- base::Closure first_disallowed_load_callback(
- base::Bind(&SubresourceFilterAgent::
- SignalFirstSubresourceDisallowedForCommittedLoad,
- AsWeakPtr()));
- std::unique_ptr<DocumentSubresourceFilter> filter(
- new DocumentSubresourceFilter(activation_state_for_provisional_load_,
- ruleset_dealer_->GetRuleset(),
- ancestor_document_urls,
- first_disallowed_load_callback));
- filter_for_last_committed_load_ = filter->AsWeakPtr();
- SetSubresourceFilterForCommittedLoad(std::move(filter));
+ if (is_same_page_navigation)
+ return;
+
+ std::vector<GURL> ancestor_document_urls = GetAncestorDocumentURLs();
+ if (ancestor_document_urls.front().SchemeIsHTTPOrHTTPS() ||
+ ancestor_document_urls.front().SchemeIsFile()) {
+ RecordHistogramsOnLoadCommitted();
+ if (activation_state_for_provisional_load_ != ActivationState::DISABLED &&
+ ruleset_dealer_->IsRulesetAvailable()) {
+ base::Closure first_disallowed_load_callback(
+ base::Bind(&SubresourceFilterAgent::
+ SignalFirstSubresourceDisallowedForCommittedLoad,
+ AsWeakPtr()));
+ std::unique_ptr<DocumentSubresourceFilter> filter(
+ new DocumentSubresourceFilter(
+ activation_state_for_provisional_load_, measure_performance_,
+ ruleset_dealer_->GetRuleset(), ancestor_document_urls,
+ first_disallowed_load_callback));
+ filter_for_last_committed_load_ = filter->AsWeakPtr();
+ SetSubresourceFilterForCommittedLoad(std::move(filter));
+ }
}
activation_state_for_provisional_load_ = ActivationState::DISABLED;
}
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 864e197d5ad..62afdc4c2f5 100644
--- a/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.h
+++ b/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.h
@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "components/subresource_filter/content/common/document_load_statistics.h"
#include "components/subresource_filter/core/common/activation_state.h"
#include "content/public/renderer/render_frame_observer.h"
#include "url/gurl.h"
@@ -56,9 +57,14 @@ class SubresourceFilterAgent
// the most recently committed load. Not called if all resources are allowed.
virtual void SignalFirstSubresourceDisallowedForCommittedLoad();
+ // Sends statistics about the DocumentSubresourceFilter's work to the browser.
+ virtual void SendDocumentLoadStatistics(
+ const DocumentLoadStatistics& statistics);
+
private:
void OnActivateForProvisionalLoad(ActivationState activation_state,
- const GURL& url);
+ const GURL& url,
+ bool measure_performance);
void RecordHistogramsOnLoadCommitted();
void RecordHistogramsOnLoadFinished();
@@ -75,6 +81,7 @@ class SubresourceFilterAgent
ActivationState activation_state_for_provisional_load_;
GURL url_for_provisional_load_;
+ bool measure_performance_ = false;
base::WeakPtr<DocumentSubresourceFilter> filter_for_last_committed_load_;
DISALLOW_COPY_AND_ASSIGN(SubresourceFilterAgent);
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 12c51c73117..46626ddf702 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
@@ -11,8 +11,11 @@
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "base/test/histogram_tester.h"
+#include "base/time/time.h"
+#include "components/subresource_filter/content/common/document_load_statistics.h"
#include "components/subresource_filter/content/common/subresource_filter_messages.h"
#include "components/subresource_filter/content/renderer/ruleset_dealer.h"
+#include "components/subresource_filter/core/common/scoped_timers.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -44,6 +47,8 @@ class SubresourceFilterAgentUnderTest : public SubresourceFilterAgent {
MOCK_METHOD0(GetAncestorDocumentURLs, std::vector<GURL>());
MOCK_METHOD0(OnSetSubresourceFilterForCommittedLoadCalled, void());
MOCK_METHOD0(SignalFirstSubresourceDisallowedForCommittedLoad, void());
+ MOCK_METHOD1(SendDocumentLoadStatistics, void(const DocumentLoadStatistics&));
+
void SetSubresourceFilterForCommittedLoad(
std::unique_ptr<blink::WebDocumentSubresourceFilter> filter) override {
last_injected_filter_ = std::move(filter);
@@ -64,11 +69,29 @@ class SubresourceFilterAgentUnderTest : public SubresourceFilterAgent {
DISALLOW_COPY_AND_ASSIGN(SubresourceFilterAgentUnderTest);
};
-const char kTestFirstURL[] = "http://example.com/alpha";
-const char kTestSecondURL[] = "http://example.com/beta";
-const char kTestFirstURLPathSuffix[] = "alpha";
-const char kTestSecondURLPathSuffix[] = "beta";
-const char kTestBothURLsPathSuffix[] = "a";
+constexpr const char kTestFirstURL[] = "http://example.com/alpha";
+constexpr const char kTestSecondURL[] = "http://example.com/beta";
+constexpr const char kTestFirstURLPathSuffix[] = "alpha";
+constexpr const char kTestSecondURLPathSuffix[] = "beta";
+constexpr const char kTestBothURLsPathSuffix[] = "a";
+
+// Histogram names.
+constexpr const char kDocumentLoadRulesetIsAvailable[] =
+ "SubresourceFilter.DocumentLoad.RulesetIsAvailable";
+constexpr const char kDocumentLoadActivationState[] =
+ "SubresourceFilter.DocumentLoad.ActivationState";
+constexpr const char kSubresourcesEvaluated[] =
+ "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Evaluated";
+constexpr const char kSubresourcesTotal[] =
+ "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Total";
+constexpr const char kSubresourcesMatchedRules[] =
+ "SubresourceFilter.DocumentLoad.NumSubresourceLoads.MatchedRules";
+constexpr const char kSubresourcesDisallowed[] =
+ "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Disallowed";
+constexpr const char kEvaluationTotalWallDuration[] =
+ "SubresourceFilter.DocumentLoad.SubresourceEvaluation.TotalWallDuration";
+constexpr const char kEvaluationTotalCPUDuration[] =
+ "SubresourceFilter.DocumentLoad.SubresourceEvaluation.TotalCPUDuration";
} // namespace
@@ -102,11 +125,19 @@ class SubresourceFilterAgentTest : public ::testing::Test {
true /* is_new_navigation */, false /* is_same_page_navigation */);
}
- void StartLoadAndSetActivationState(ActivationState activation_state) {
+ void PerformSamePageNavigationWithoutSettingActivationState() {
+ agent_as_rfo()->DidStartProvisionalLoad();
+ agent_as_rfo()->DidCommitProvisionalLoad(
+ true /* is_new_navigation */, true /* is_same_page_navigation */);
+ // No DidFinishLoad is called in this case.
+ }
+
+ void StartLoadAndSetActivationState(ActivationState activation_state,
+ bool measure_performance = false) {
agent_as_rfo()->DidStartProvisionalLoad();
EXPECT_TRUE(agent_as_rfo()->OnMessageReceived(
- SubresourceFilterMsg_ActivateForProvisionalLoad(0, activation_state,
- GURL())));
+ SubresourceFilterMsg_ActivateForProvisionalLoad(
+ 0, activation_state, GURL(), measure_performance)));
agent_as_rfo()->DidCommitProvisionalLoad(
true /* is_new_navigation */, false /* is_same_page_navigation */);
}
@@ -118,20 +149,26 @@ class SubresourceFilterAgentTest : public ::testing::Test {
EXPECT_CALL(*agent(), OnSetSubresourceFilterForCommittedLoadCalled());
}
- void ExpectSignalAboutFirstSubresourceDisallowed() {
- EXPECT_CALL(*agent(), SignalFirstSubresourceDisallowedForCommittedLoad());
- }
-
void ExpectNoSubresourceFilterGetsInjected() {
+ EXPECT_CALL(*agent(), GetAncestorDocumentURLs())
+ .Times(::testing::AtLeast(0));
EXPECT_CALL(*agent(), OnSetSubresourceFilterForCommittedLoadCalled())
.Times(0);
}
+ void ExpectSignalAboutFirstSubresourceDisallowed() {
+ EXPECT_CALL(*agent(), SignalFirstSubresourceDisallowedForCommittedLoad());
+ }
+
void ExpectNoSignalAboutFirstSubresourceDisallowed() {
EXPECT_CALL(*agent(), SignalFirstSubresourceDisallowedForCommittedLoad())
.Times(0);
}
+ void ExpectDocumentLoadStatisticsSent() {
+ EXPECT_CALL(*agent(), SendDocumentLoadStatistics(::testing::_));
+ }
+
void ExpectLoadAllowed(base::StringPiece url_spec, bool allowed) {
blink::WebURL url = GURL(url_spec);
blink::WebURLRequest::RequestContext request_context =
@@ -154,11 +191,25 @@ class SubresourceFilterAgentTest : public ::testing::Test {
};
TEST_F(SubresourceFilterAgentTest, DisabledByDefault_NoFilterIsInjected) {
+ base::HistogramTester histogram_tester;
ASSERT_NO_FATAL_FAILURE(
SetTestRulesetToDisallowURLsWithPathSuffix(kTestBothURLsPathSuffix));
ExpectNoSubresourceFilterGetsInjected();
StartLoadWithoutSettingActivationState();
FinishLoad();
+
+ histogram_tester.ExpectUniqueSample(
+ kDocumentLoadActivationState, static_cast<int>(ActivationState::DISABLED),
+ 1);
+ histogram_tester.ExpectTotalCount(kDocumentLoadRulesetIsAvailable, 0);
+
+ histogram_tester.ExpectTotalCount(kSubresourcesTotal, 0);
+ histogram_tester.ExpectTotalCount(kSubresourcesEvaluated, 0);
+ histogram_tester.ExpectTotalCount(kSubresourcesMatchedRules, 0);
+ histogram_tester.ExpectTotalCount(kSubresourcesDisallowed, 0);
+
+ histogram_tester.ExpectTotalCount(kEvaluationTotalWallDuration, 0);
+ histogram_tester.ExpectTotalCount(kEvaluationTotalCPUDuration, 0);
}
TEST_F(SubresourceFilterAgentTest, Disabled_NoFilterIsInjected) {
@@ -171,12 +222,48 @@ TEST_F(SubresourceFilterAgentTest, Disabled_NoFilterIsInjected) {
TEST_F(SubresourceFilterAgentTest,
EnabledButRulesetUnavailable_NoFilterIsInjected) {
+ base::HistogramTester histogram_tester;
+ ExpectNoSubresourceFilterGetsInjected();
+ StartLoadAndSetActivationState(ActivationState::ENABLED);
+ FinishLoad();
+
+ histogram_tester.ExpectUniqueSample(
+ kDocumentLoadActivationState, static_cast<int>(ActivationState::ENABLED),
+ 1);
+ histogram_tester.ExpectUniqueSample(kDocumentLoadRulesetIsAvailable, 0, 1);
+
+ histogram_tester.ExpectTotalCount(kSubresourcesTotal, 0);
+ histogram_tester.ExpectTotalCount(kSubresourcesEvaluated, 0);
+ histogram_tester.ExpectTotalCount(kSubresourcesMatchedRules, 0);
+ histogram_tester.ExpectTotalCount(kSubresourcesDisallowed, 0);
+
+ histogram_tester.ExpectTotalCount(kEvaluationTotalWallDuration, 0);
+ histogram_tester.ExpectTotalCount(kEvaluationTotalCPUDuration, 0);
+}
+
+TEST_F(SubresourceFilterAgentTest, EmptyDocumentLoad_NoFilterIsInjected) {
+ base::HistogramTester histogram_tester;
ExpectNoSubresourceFilterGetsInjected();
+ EXPECT_CALL(*agent(), GetAncestorDocumentURLs())
+ .WillOnce(::testing::Return(
+ std::vector<GURL>({GURL("about:blank"), GURL("http://outer.com/")})));
StartLoadAndSetActivationState(ActivationState::ENABLED);
FinishLoad();
+
+ histogram_tester.ExpectTotalCount(kDocumentLoadActivationState, 0);
+ histogram_tester.ExpectTotalCount(kDocumentLoadRulesetIsAvailable, 0);
+
+ histogram_tester.ExpectTotalCount(kSubresourcesTotal, 0);
+ histogram_tester.ExpectTotalCount(kSubresourcesEvaluated, 0);
+ histogram_tester.ExpectTotalCount(kSubresourcesMatchedRules, 0);
+ histogram_tester.ExpectTotalCount(kSubresourcesDisallowed, 0);
+
+ histogram_tester.ExpectTotalCount(kEvaluationTotalWallDuration, 0);
+ histogram_tester.ExpectTotalCount(kEvaluationTotalCPUDuration, 0);
}
TEST_F(SubresourceFilterAgentTest, Enabled_FilteringIsInEffectForOneLoad) {
+ base::HistogramTester histogram_tester;
ASSERT_NO_FATAL_FAILURE(
SetTestRulesetToDisallowURLsWithPathSuffix(kTestFirstURLPathSuffix));
@@ -187,11 +274,85 @@ TEST_F(SubresourceFilterAgentTest, Enabled_FilteringIsInEffectForOneLoad) {
ExpectSignalAboutFirstSubresourceDisallowed();
ExpectLoadAllowed(kTestFirstURL, false);
ExpectLoadAllowed(kTestSecondURL, true);
+ ExpectDocumentLoadStatisticsSent();
FinishLoad();
+ // In-page navigation should not count as a new load.
+ ExpectNoSubresourceFilterGetsInjected();
+ ExpectNoSignalAboutFirstSubresourceDisallowed();
+ PerformSamePageNavigationWithoutSettingActivationState();
+ ExpectLoadAllowed(kTestFirstURL, false);
+ ExpectLoadAllowed(kTestSecondURL, true);
+
ExpectNoSubresourceFilterGetsInjected();
StartLoadWithoutSettingActivationState();
FinishLoad();
+
+ // Resource loads after the in-page navigation should not be counted toward
+ // the figures below, as they came after the original page load event.
+ histogram_tester.ExpectUniqueSample(kSubresourcesTotal, 2, 1);
+ histogram_tester.ExpectUniqueSample(kSubresourcesEvaluated, 2, 1);
+ histogram_tester.ExpectUniqueSample(kSubresourcesMatchedRules, 1, 1);
+ histogram_tester.ExpectUniqueSample(kSubresourcesDisallowed, 1, 1);
+ EXPECT_THAT(histogram_tester.GetAllSamples(kDocumentLoadActivationState),
+ ::testing::ElementsAre(
+ base::Bucket(static_cast<int>(ActivationState::DISABLED), 1),
+ base::Bucket(static_cast<int>(ActivationState::ENABLED), 1)));
+ histogram_tester.ExpectUniqueSample(kDocumentLoadRulesetIsAvailable, 1, 1);
+}
+
+TEST_F(SubresourceFilterAgentTest, Enabled_HistogramSamplesOverTwoLoads) {
+ for (const bool measure_performance : {false, true}) {
+ base::HistogramTester histogram_tester;
+ ASSERT_NO_FATAL_FAILURE(
+ SetTestRulesetToDisallowURLsWithPathSuffix(kTestFirstURLPathSuffix));
+ ExpectSubresourceFilterGetsInjected();
+ StartLoadAndSetActivationState(ActivationState::ENABLED,
+ measure_performance);
+ ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(agent()));
+
+ ExpectSignalAboutFirstSubresourceDisallowed();
+ ExpectLoadAllowed(kTestFirstURL, false);
+ ExpectNoSignalAboutFirstSubresourceDisallowed();
+ ExpectLoadAllowed(kTestFirstURL, false);
+ ExpectNoSignalAboutFirstSubresourceDisallowed();
+ ExpectLoadAllowed(kTestSecondURL, true);
+ ExpectDocumentLoadStatisticsSent();
+ FinishLoad();
+
+ ExpectSubresourceFilterGetsInjected();
+ StartLoadAndSetActivationState(ActivationState::ENABLED,
+ measure_performance);
+ ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(agent()));
+
+ ExpectNoSignalAboutFirstSubresourceDisallowed();
+ ExpectLoadAllowed(kTestSecondURL, true);
+ ExpectSignalAboutFirstSubresourceDisallowed();
+ ExpectLoadAllowed(kTestFirstURL, false);
+ ExpectDocumentLoadStatisticsSent();
+ FinishLoad();
+
+ histogram_tester.ExpectUniqueSample(
+ kDocumentLoadActivationState,
+ static_cast<int>(ActivationState::ENABLED), 2);
+ histogram_tester.ExpectUniqueSample(kDocumentLoadRulesetIsAvailable, 1, 2);
+
+ EXPECT_THAT(histogram_tester.GetAllSamples(kSubresourcesTotal),
+ ::testing::ElementsAre(base::Bucket(2, 1), base::Bucket(3, 1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(kSubresourcesEvaluated),
+ ::testing::ElementsAre(base::Bucket(2, 1), base::Bucket(3, 1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(kSubresourcesMatchedRules),
+ ::testing::ElementsAre(base::Bucket(1, 1), base::Bucket(2, 1)));
+ EXPECT_THAT(histogram_tester.GetAllSamples(kSubresourcesDisallowed),
+ ::testing::ElementsAre(base::Bucket(1, 1), base::Bucket(2, 1)));
+
+ base::HistogramBase::Count expected_total_count =
+ measure_performance && base::ThreadTicks::IsSupported() ? 2 : 0;
+ histogram_tester.ExpectTotalCount(kEvaluationTotalWallDuration,
+ expected_total_count);
+ histogram_tester.ExpectTotalCount(kEvaluationTotalCPUDuration,
+ expected_total_count);
+ }
}
TEST_F(SubresourceFilterAgentTest, Enabled_NewRulesetIsPickedUpAtNextLoad) {
@@ -209,6 +370,7 @@ TEST_F(SubresourceFilterAgentTest, Enabled_NewRulesetIsPickedUpAtNextLoad) {
ExpectSignalAboutFirstSubresourceDisallowed();
ExpectLoadAllowed(kTestFirstURL, false);
ExpectLoadAllowed(kTestSecondURL, true);
+ ExpectDocumentLoadStatisticsSent();
FinishLoad();
ExpectSubresourceFilterGetsInjected();
@@ -218,6 +380,7 @@ TEST_F(SubresourceFilterAgentTest, Enabled_NewRulesetIsPickedUpAtNextLoad) {
ExpectSignalAboutFirstSubresourceDisallowed();
ExpectLoadAllowed(kTestFirstURL, true);
ExpectLoadAllowed(kTestSecondURL, false);
+ ExpectDocumentLoadStatisticsSent();
FinishLoad();
}
@@ -233,7 +396,7 @@ TEST_F(SubresourceFilterAgentTest,
agent_as_rfo()->DidStartProvisionalLoad();
EXPECT_TRUE(agent_as_rfo()->OnMessageReceived(
SubresourceFilterMsg_ActivateForProvisionalLoad(
- 0, ActivationState::ENABLED, GURL())));
+ 0, ActivationState::ENABLED, GURL(), true)));
agent_as_rfo()->DidStartProvisionalLoad();
agent_as_rfo()->DidCommitProvisionalLoad(true /* is_new_navigation */,
false /* is_same_page_navigation */);
@@ -241,15 +404,35 @@ TEST_F(SubresourceFilterAgentTest,
}
TEST_F(SubresourceFilterAgentTest, DryRun_ResourcesAreEvaluatedButNotFiltered) {
+ base::HistogramTester histogram_tester;
ASSERT_NO_FATAL_FAILURE(
SetTestRulesetToDisallowURLsWithPathSuffix(kTestFirstURLPathSuffix));
ExpectSubresourceFilterGetsInjected();
StartLoadAndSetActivationState(ActivationState::DRYRUN);
ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(agent()));
+ // In dry-run mode, loads to the first URL should be recorded as
+ // `MatchedRules`, but still be allowed to proceed and not recorded as
+ // `Disallowed`.
+ ExpectLoadAllowed(kTestFirstURL, true);
ExpectLoadAllowed(kTestFirstURL, true);
ExpectLoadAllowed(kTestSecondURL, true);
+ ExpectDocumentLoadStatisticsSent();
FinishLoad();
+
+ histogram_tester.ExpectUniqueSample(kDocumentLoadActivationState,
+ static_cast<int>(ActivationState::DRYRUN),
+ 1);
+ histogram_tester.ExpectUniqueSample(kDocumentLoadRulesetIsAvailable, 1, 1);
+
+ histogram_tester.ExpectUniqueSample(kSubresourcesTotal, 3, 1);
+ histogram_tester.ExpectUniqueSample(kSubresourcesEvaluated, 3, 1);
+ histogram_tester.ExpectUniqueSample(kSubresourcesMatchedRules, 2, 1);
+ histogram_tester.ExpectUniqueSample(kSubresourcesDisallowed, 0, 1);
+
+ // Performance measurement is switched off.
+ histogram_tester.ExpectTotalCount(kEvaluationTotalWallDuration, 0);
+ histogram_tester.ExpectTotalCount(kEvaluationTotalCPUDuration, 0);
}
TEST_F(SubresourceFilterAgentTest,
@@ -265,6 +448,7 @@ TEST_F(SubresourceFilterAgentTest,
ExpectNoSignalAboutFirstSubresourceDisallowed();
ExpectLoadAllowed(kTestFirstURL, false);
ExpectLoadAllowed(kTestSecondURL, true);
+ ExpectDocumentLoadStatisticsSent();
FinishLoad();
ExpectSubresourceFilterGetsInjected();
@@ -274,6 +458,7 @@ TEST_F(SubresourceFilterAgentTest,
ExpectLoadAllowed(kTestSecondURL, true);
ExpectSignalAboutFirstSubresourceDisallowed();
ExpectLoadAllowed(kTestFirstURL, false);
+ ExpectDocumentLoadStatisticsSent();
FinishLoad();
}
@@ -291,121 +476,4 @@ TEST_F(SubresourceFilterAgentTest,
blink::WebURLRequest::RequestContextImage));
}
-TEST_F(SubresourceFilterAgentTest, Disabled_HistogramSamples) {
- base::HistogramTester histogram_tester;
- ASSERT_NO_FATAL_FAILURE(
- SetTestRulesetToDisallowURLsWithPathSuffix(kTestBothURLsPathSuffix));
- StartLoadWithoutSettingActivationState();
- FinishLoad();
-
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.ActivationState",
- static_cast<int>(ActivationState::DISABLED), 1);
- histogram_tester.ExpectTotalCount(
- "SubresourceFilter.DocumentLoad.RulesetIsAvailable", 0);
- histogram_tester.ExpectTotalCount(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Total", 0);
- histogram_tester.ExpectTotalCount(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Evaluated", 0);
- histogram_tester.ExpectTotalCount(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Disallowed", 0);
-}
-
-TEST_F(SubresourceFilterAgentTest,
- EnabledButRulesetUnavailable_HistogramSamples) {
- base::HistogramTester histogram_tester;
- StartLoadAndSetActivationState(ActivationState::ENABLED);
- FinishLoad();
-
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.ActivationState",
- static_cast<int>(ActivationState::ENABLED), 1);
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.RulesetIsAvailable", 0, 1);
- histogram_tester.ExpectTotalCount(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Total", 0);
- histogram_tester.ExpectTotalCount(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Evaluated", 0);
- histogram_tester.ExpectTotalCount(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.MatchedRules", 0);
- histogram_tester.ExpectTotalCount(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Disallowed", 0);
-}
-
-TEST_F(SubresourceFilterAgentTest, Enabled_HistogramSamples) {
- base::HistogramTester histogram_tester;
- ASSERT_NO_FATAL_FAILURE(
- SetTestRulesetToDisallowURLsWithPathSuffix(kTestFirstURLPathSuffix));
- ExpectSubresourceFilterGetsInjected();
- StartLoadAndSetActivationState(ActivationState::ENABLED);
- ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(agent()));
-
- ExpectSignalAboutFirstSubresourceDisallowed();
- ExpectLoadAllowed(kTestFirstURL, false);
- ExpectLoadAllowed(kTestFirstURL, false);
- ExpectLoadAllowed(kTestSecondURL, true);
- FinishLoad();
-
- ExpectSubresourceFilterGetsInjected();
- StartLoadAndSetActivationState(ActivationState::ENABLED);
- ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(agent()));
-
- ExpectSignalAboutFirstSubresourceDisallowed();
- ExpectLoadAllowed(kTestFirstURL, false);
- ExpectLoadAllowed(kTestSecondURL, true);
- FinishLoad();
-
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.ActivationState",
- static_cast<int>(ActivationState::ENABLED), 2);
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.RulesetIsAvailable", 1, 2);
- EXPECT_THAT(histogram_tester.GetAllSamples(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Total"),
- ::testing::ElementsAre(base::Bucket(2, 1), base::Bucket(3, 1)));
- EXPECT_THAT(
- histogram_tester.GetAllSamples(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Evaluated"),
- ::testing::ElementsAre(base::Bucket(2, 1), base::Bucket(3, 1)));
- EXPECT_THAT(
- histogram_tester.GetAllSamples(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.MatchedRules"),
- ::testing::ElementsAre(base::Bucket(1, 1), base::Bucket(2, 1)));
- EXPECT_THAT(
- histogram_tester.GetAllSamples(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Disallowed"),
- ::testing::ElementsAre(base::Bucket(1, 1), base::Bucket(2, 1)));
-}
-
-TEST_F(SubresourceFilterAgentTest, DryRun_HistogramSamples) {
- base::HistogramTester histogram_tester;
- ASSERT_NO_FATAL_FAILURE(
- SetTestRulesetToDisallowURLsWithPathSuffix(kTestFirstURLPathSuffix));
- ExpectSubresourceFilterGetsInjected();
- StartLoadAndSetActivationState(ActivationState::DRYRUN);
- ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(agent()));
-
- // In dry-run mode, loads to the first URL should be recorded as
- // `MatchedRules`, but still be allowed to proceed and not recorded as
- // `Disallowed`.
- ExpectLoadAllowed(kTestFirstURL, true);
- ExpectLoadAllowed(kTestFirstURL, true);
- ExpectLoadAllowed(kTestSecondURL, true);
- FinishLoad();
-
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.ActivationState",
- static_cast<int>(ActivationState::DRYRUN), 1);
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.RulesetIsAvailable", 1, 1);
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Total", 3, 1);
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Evaluated", 3, 1);
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.MatchedRules", 2, 1);
- histogram_tester.ExpectUniqueSample(
- "SubresourceFilter.DocumentLoad.NumSubresourceLoads.Disallowed", 0, 1);
-}
-
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/browser/BUILD.gn b/chromium/components/subresource_filter/core/browser/BUILD.gn
index 39518b48516..a137defa711 100644
--- a/chromium/components/subresource_filter/core/browser/BUILD.gn
+++ b/chromium/components/subresource_filter/core/browser/BUILD.gn
@@ -52,5 +52,6 @@ source_set("unit_tests") {
"//components/subresource_filter/core/common:test_support",
"//testing/gmock",
"//testing/gtest",
+ "//third_party/protobuf:protobuf_lite",
]
}
diff --git a/chromium/components/subresource_filter/core/browser/ruleset_service.cc b/chromium/components/subresource_filter/core/browser/ruleset_service.cc
index 3a0c5fcb3a4..179375b5069 100644
--- a/chromium/components/subresource_filter/core/browser/ruleset_service.cc
+++ b/chromium/components/subresource_filter/core/browser/ruleset_service.cc
@@ -28,6 +28,7 @@
#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 "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
@@ -344,8 +345,12 @@ IndexedRulesetVersion RulesetService::IndexAndWriteRuleset(
bool RulesetService::IndexRuleset(base::File unindexed_ruleset_file,
RulesetIndexer* indexer) {
SCOPED_UMA_HISTOGRAM_TIMER("SubresourceFilter.IndexRuleset.WallDuration");
+ SCOPED_UMA_HISTOGRAM_THREAD_TIMER(
+ "SubresourceFilter.IndexRuleset.CPUDuration");
int64_t unindexed_ruleset_size = unindexed_ruleset_file.GetLength();
+ if (unindexed_ruleset_size < 0)
+ return false;
CopyingFileInputStream copying_stream(std::move(unindexed_ruleset_file));
google::protobuf::io::CopyingInputStreamAdaptor zero_copy_stream_adaptor(
&copying_stream, 4096 /* buffer_size */);
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 e85213c37a6..9ac0acf6992 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -6,6 +6,7 @@
#include <string>
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "components/variations/variations_associated_data.h"
@@ -30,6 +31,9 @@ const char kActivationListSocialEngineeringAdsInterstitial[] =
"social_engineering_ads_interstitial";
const char kActivationListPhishingInterstitial[] = "phishing_interstitial";
+const char kPerformanceMeasurementRateParameterName[] =
+ "performance_measurement_rate";
+
ActivationState GetMaximumActivationState() {
std::string activation_state = variations::GetVariationParamValueByFeature(
kSafeBrowsingSubresourceFilter, kActivationStateParameterName);
@@ -70,4 +74,13 @@ ActivationList GetCurrentActivationList() {
return activation_list_type;
}
+double GetPerformanceMeasurementRate() {
+ const std::string rate = variations::GetVariationParamValueByFeature(
+ kSafeBrowsingSubresourceFilter, kPerformanceMeasurementRateParameterName);
+ double value = 0;
+ if (!base::StringToDouble(rate, &value) || value < 0)
+ return 0;
+ return value < 1 ? value : 1;
+}
+
} // namespace subresource_filter
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 b19b2dc1333..11f54ffab9a 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features.h
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features.h
@@ -30,6 +30,8 @@ extern const char kActivationListsParameterName[];
extern const char kActivationListSocialEngineeringAdsInterstitial[];
extern const char kActivationListPhishingInterstitial[];
+extern const char kPerformanceMeasurementRateParameterName[];
+
// Returns the maximum degree to which subresource filtering should be activated
// on any RenderFrame. This will be ActivationState::DISABLED unless the feature
// is enabled and variation parameters prescribe a higher activation state.
@@ -46,6 +48,11 @@ ActivationScope GetCurrentActivationScope();
// variation param is empty, returns most conservative ActivationList::NONE.
ActivationList GetCurrentActivationList();
+// Returns a number in the range [0, 1], indicating the fraction of page loads
+// that should have extended performance measurements enabled. The rate will be
+// 0 unless a greater frequency is specified by variation parameters.
+double GetPerformanceMeasurementRate();
+
} // namespace subresource_filter
#endif // COMPONENTS_SUBRESOURCE_FILTER_SUBRESOURCE_FILTER_FEATURES_H_
diff --git a/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc b/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc
index 5396c019832..a6be80b0d30 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc
@@ -16,30 +16,29 @@ namespace subresource_filter {
namespace testing {
namespace {
-const char kTestFieldTrialName[] = "FieldTrialNameShouldNotMatter";
-const char kTestExperimentGroupName[] = "GroupNameShouldNotMatter";
+constexpr const char kTestFieldTrialName[] = "FieldTrialNameShouldNotMatter";
+constexpr const char kTestExperimentGroupName[] = "GroupNameShouldNotMatter";
} // namespace
ScopedSubresourceFilterFeatureToggle::ScopedSubresourceFilterFeatureToggle(
base::FeatureList::OverrideState feature_state,
const std::string& maximum_activation_state,
- const std::string& activation_scope)
- : ScopedSubresourceFilterFeatureToggle(feature_state,
- maximum_activation_state,
- activation_scope,
- std::string()) {}
+ const std::string& activation_scope,
+ const std::string& activation_lists,
+ const std::string& performance_measurement_rate)
+ : ScopedSubresourceFilterFeatureToggle(
+ feature_state,
+ {{kActivationStateParameterName, maximum_activation_state},
+ {kActivationScopeParameterName, activation_scope},
+ {kActivationListsParameterName, activation_lists},
+ {kPerformanceMeasurementRateParameterName,
+ performance_measurement_rate}}) {}
ScopedSubresourceFilterFeatureToggle::ScopedSubresourceFilterFeatureToggle(
base::FeatureList::OverrideState feature_state,
- const std::string& maximum_activation_state,
- const std::string& activation_scope,
- const std::string& activation_lists) {
+ std::map<std::string, std::string> variation_params) {
variations::testing::ClearAllVariationParams();
- std::map<std::string, std::string> variation_params;
- variation_params[kActivationStateParameterName] = maximum_activation_state;
- variation_params[kActivationScopeParameterName] = activation_scope;
- variation_params[kActivationListsParameterName] = activation_lists;
EXPECT_TRUE(variations::AssociateVariationParams(
kTestFieldTrialName, kTestExperimentGroupName, variation_params));
diff --git a/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.h b/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.h
index f2065e9cb40..6fe38513854 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.h
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_BROWSER_SUBRESOURCE_FILTER_FEATURES_TEST_SUPPORT_H_
#define COMPONENTS_SUBRESOURCE_FILTER_CORE_BROWSER_SUBRESOURCE_FILTER_FEATURES_TEST_SUPPORT_H_
+#include <map>
#include <string>
#include "base/feature_list.h"
@@ -14,21 +15,21 @@
namespace subresource_filter {
namespace testing {
-// Helper to override the state of the |kSafeBrowsingSubresourceFilter| feature
-// and the maximum activation state during tests. Expects a pre-existing global
-// base::FieldTrialList singleton.
+// Helper to override the state of the |kSafeBrowsingSubresourceFilter| feature,
+// and its variation parameters, e.g., maximum activation state and activation
+// scope. Expects a pre-existing global base::FieldTrialList singleton.
class ScopedSubresourceFilterFeatureToggle {
public:
ScopedSubresourceFilterFeatureToggle(
base::FeatureList::OverrideState feature_state,
const std::string& maximum_activation_state,
- const std::string& activation_scope);
+ const std::string& activation_scope,
+ const std::string& activation_lists = std::string(),
+ const std::string& performance_measurement_rate = std::string());
ScopedSubresourceFilterFeatureToggle(
base::FeatureList::OverrideState feature_state,
- const std::string& maximum_activation_state,
- const std::string& activation_scope,
- const std::string& activation_lists);
+ std::map<std::string, std::string> variation_params);
~ScopedSubresourceFilterFeatureToggle();
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 51a3d58c827..d2ab51d073e 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
@@ -183,4 +183,40 @@ TEST(SubresourceFilterFeaturesTest, ActivationList) {
}
}
+TEST(SubresourceFilterFeaturesTest, PerfMeasurementRate) {
+ const struct {
+ bool feature_enabled;
+ const char* perf_measurement_param;
+ double expected_perf_measurement_rate;
+ } kTestCases[] = {{false, "not_a_number", 0},
+ {false, "0", 0},
+ {false, "1", 0},
+ {true, "not_a_number", 0},
+ {true, "0.5not_a_number", 0},
+ {true, "0", 0},
+ {true, "0.000", 0},
+ {true, "0.05", 0.05},
+ {true, "0.5", 0.5},
+ {true, "1", 1},
+ {true, "1.0", 1},
+ {true, "0.333", 0.333},
+ {true, "1e0", 1}};
+
+ for (const auto& test_case : kTestCases) {
+ SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled);
+ SCOPED_TRACE(::testing::Message("PerfMeasurementParam = \"")
+ << test_case.perf_measurement_param << "\"");
+
+ base::FieldTrialList field_trial_list(nullptr /* entropy_provider */);
+ testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
+ test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
+ : base::FeatureList::OVERRIDE_USE_DEFAULT,
+ {{kPerformanceMeasurementRateParameterName,
+ test_case.perf_measurement_param}});
+
+ EXPECT_EQ(test_case.expected_perf_measurement_rate,
+ GetPerformanceMeasurementRate());
+ }
+}
+
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/BUILD.gn b/chromium/components/subresource_filter/core/common/BUILD.gn
index b48b5550311..5e260b78f30 100644
--- a/chromium/components/subresource_filter/core/common/BUILD.gn
+++ b/chromium/components/subresource_filter/core/common/BUILD.gn
@@ -4,6 +4,8 @@
static_library("common") {
sources = [
+ "activation_list.cc",
+ "activation_list.h",
"activation_scope.cc",
"activation_scope.h",
"activation_state.cc",
@@ -21,7 +23,9 @@ static_library("common") {
"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",
@@ -49,6 +53,8 @@ static_library("test_support") {
sources = [
"test_ruleset_creator.cc",
"test_ruleset_creator.h",
+ "test_ruleset_utils.cc",
+ "test_ruleset_utils.h",
]
deps = [
":common",
@@ -67,6 +73,7 @@ source_set("unit_tests") {
"indexed_ruleset_unittest.cc",
"knuth_morris_pratt_unittest.cc",
"ngram_extractor_unittest.cc",
+ "scoped_timers_unittest.cc",
"string_splitter_unittest.cc",
"unindexed_ruleset_unittest.cc",
"url_pattern_matching_unittest.cc",
@@ -74,6 +81,7 @@ source_set("unit_tests") {
deps = [
":common",
"//base",
+ "//base/test:test_support",
"//testing/gtest",
"//third_party/protobuf:protobuf_lite",
"//url",
diff --git a/chromium/components/subresource_filter/core/common/activation_list.cc b/chromium/components/subresource_filter/core/common/activation_list.cc
new file mode 100644
index 00000000000..090eeae134c
--- /dev/null
+++ b/chromium/components/subresource_filter/core/common/activation_list.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/subresource_filter/core/common/activation_list.h"
+
+#include <ostream>
+
+#include "base/logging.h"
+
+namespace subresource_filter {
+
+std::ostream& operator<<(std::ostream& os, const ActivationList& type) {
+ switch (type) {
+ case ActivationList::NONE:
+ os << "NONE";
+ break;
+ case ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL:
+ os << "SOCIAL_ENG_ADS_INTERSTITIAL";
+ break;
+ case ActivationList::PHISHING_INTERSTITIAL:
+ os << "PHISHING_INTERSTITIAL";
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ return os;
+}
+
+} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/activation_list.h b/chromium/components/subresource_filter/core/common/activation_list.h
index 87b6408de09..c24118ba715 100644
--- a/chromium/components/subresource_filter/core/common/activation_list.h
+++ b/chromium/components/subresource_filter/core/common/activation_list.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_ACTIVATION_LIST_H_
#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_ACTIVATION_LIST_H_
+#include <iosfwd>
+
namespace subresource_filter {
enum class ActivationList {
@@ -14,6 +16,9 @@ enum class ActivationList {
LAST = PHISHING_INTERSTITIAL,
};
+// For logging use only.
+std::ostream& operator<<(std::ostream& os, const ActivationList& type);
+
} // namespace subresource_filter
#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_ACTIVATION_LIST_H_
diff --git a/chromium/components/subresource_filter/core/common/scoped_timers.h b/chromium/components/subresource_filter/core/common/scoped_timers.h
new file mode 100644
index 00000000000..6eb617b195f
--- /dev/null
+++ b/chromium/components/subresource_filter/core/common/scoped_timers.h
@@ -0,0 +1,156 @@
+// 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 provides a ScopedTimerImpl class which measures its lifetime using
+// different time providers, and can report the measurement via user-defined
+// callbacks. The class comes with ScopedTimerImplFactory which has convenient
+// factory methods returning timer instances.
+//
+// Although the classes can be used directly, the most common usages are covered
+// by the ScopedTimers and ScopedThreadTimers type aliases. See comments to
+// these aliases below.
+//
+// TODO(pkalinnikov): Consider moving this file to "base/metrics/".
+
+#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_SCOPED_TIMERS_
+#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_SCOPED_TIMERS_
+
+#include <type_traits>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+
+namespace subresource_filter {
+
+namespace impl {
+
+// ScopedTimerImpl is a multi-purpose scoped timer. It measures time delta from
+// its construction until its destruction. For example, by putting an instance
+// of this class to the beginning of a scope it is possible to measure how long
+// the scope is being executed.
+//
+// The obtained time measurement is reported via ExportFunctor, which takes
+// base::TimeDelta as a parameter.
+//
+// Time is obtained by means of the TimeProvider static interface:
+// * static bool IsSupported();
+// - Idempotently returns whether the system supports such type of provider.
+// * static void WaitUntilInitialized();
+// - Waits until the provider can be used.
+// * static TimeType Now();
+// - Returns the current time of some TimeType, e.g., base::TimeTicks.
+//
+// The ExportFunctor is invoked exactly once, upon destruction, if both
+// |activate| and TimeProvider::IsSupported() are true. Otherwise, the
+// ExportFunctor is not called and no time measurements are performed, making
+// the overhead of an instance practically zero.
+template <typename TimeProvider, typename ExportFunctor>
+class ScopedTimerImpl {
+ public:
+ // Constructs a timer reporting its measurement to |export_functor|. The timer
+ // will be activated iff |activate| && TimeProvider::IsSupported().
+ explicit ScopedTimerImpl(ExportFunctor&& export_functor, bool activate = true)
+ : export_functor_(std::forward<ExportFunctor>(export_functor)),
+ activated_(activate && TimeProvider::IsSupported()) {
+ if (activated_) {
+ TimeProvider::WaitUntilInitialized();
+ construction_time_ = TimeProvider::Now();
+ }
+ }
+
+ // The class is moveable. The |rhs| becomes deactivated when it is moved from,
+ // so that no time measurement will be reported upon its destruction.
+ ScopedTimerImpl(ScopedTimerImpl&& rhs)
+ : export_functor_(std::forward<ExportFunctor>(rhs.export_functor_)),
+ construction_time_(std::move(rhs.construction_time_)),
+ activated_(rhs.activated_) {
+ rhs.activated_ = false;
+ }
+
+ // If |this| was activated before assignment, it reports its measurement
+ // before stealing the one from |rhs|.
+ ScopedTimerImpl& operator=(ScopedTimerImpl&& rhs) {
+ if (&rhs != this) {
+ this->~ScopedTimerImpl();
+ ::new (this) ScopedTimerImpl(std::move(rhs));
+ }
+ return *this;
+ }
+
+ ~ScopedTimerImpl() {
+ if (activated_)
+ export_functor_(TimeProvider::Now() - construction_time_);
+ }
+
+ private:
+ using TimeType =
+ typename std::remove_reference<decltype(TimeProvider::Now())>::type;
+
+ ExportFunctor export_functor_;
+ TimeType construction_time_;
+ bool activated_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTimerImpl);
+};
+
+// TimeProvider implementations ------------------------------------------------
+
+class TimeTicksProvider {
+ public:
+ static bool IsSupported() { return true; }
+ static void WaitUntilInitialized() {}
+ static base::TimeTicks Now() { return base::TimeTicks::Now(); }
+};
+
+using ThreadTicksProvider = base::ThreadTicks;
+
+// ScopedTimerImpl factories ---------------------------------------------------
+
+// The class used to produce scoped timers using a certain |TimeProvider|.
+template <typename TimeProvider>
+class ScopedTimerImplFactory {
+ public:
+ // Returns a scoped timer which uses |export_functor| to report its
+ // measurement. The timer is activated iff TimeProvider::IsSupported().
+ template <typename ExportFunctor>
+ static ScopedTimerImpl<TimeProvider, ExportFunctor> Start(
+ ExportFunctor&& export_functor) {
+ return ScopedTimerImpl<TimeProvider, ExportFunctor>(
+ std::forward<ExportFunctor>(export_functor));
+ }
+
+ // Similar to the Start method above, but the timer is activated iff
+ // |condition| && TimeProvider::IsSupported().
+ template <typename ExportFunctor>
+ static ScopedTimerImpl<TimeProvider, ExportFunctor> StartIf(
+ bool condition,
+ ExportFunctor&& export_functor) {
+ return ScopedTimerImpl<TimeProvider, ExportFunctor>(
+ std::forward<ExportFunctor>(export_functor), condition);
+ }
+
+ static bool IsSupported() { return TimeProvider::IsSupported(); }
+};
+
+} // namespace impl
+
+// A factory to produce scoped timers that measure time by means of TimeTicks.
+//
+// Example usage:
+// void foo() {
+// auto scoped_timer = ScopedTimers::Start([](base::TimeDelta duration) {
+// LOG(INFO) << "Duration: " << duration.InMicroseconds() << " us";
+// });
+// ... Some time-consuming code goes here ...
+// }
+using ScopedTimers = impl::ScopedTimerImplFactory<impl::TimeTicksProvider>;
+
+// Similar to ScopedTimers, but the produced timers use ThreadTicks.
+using ScopedThreadTimers =
+ impl::ScopedTimerImplFactory<impl::ThreadTicksProvider>;
+
+} // namespace subresource_filter
+
+#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_SCOPED_TIMERS_
diff --git a/chromium/components/subresource_filter/core/common/scoped_timers_unittest.cc b/chromium/components/subresource_filter/core/common/scoped_timers_unittest.cc
new file mode 100644
index 00000000000..4a10af758ed
--- /dev/null
+++ b/chromium/components/subresource_filter/core/common/scoped_timers_unittest.cc
@@ -0,0 +1,187 @@
+// 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/scoped_timers.h"
+
+#include "base/test/histogram_tester.h"
+#include "base/time/time.h"
+#include "components/subresource_filter/core/common/time_measurements.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MockExportFunctor {
+ public:
+ int number_of_calls() const { return number_of_calls_; }
+ void operator()(base::TimeDelta) { ++number_of_calls_; }
+
+ private:
+ int number_of_calls_ = 0;
+};
+
+template <typename TimerFactory>
+void ExpectFunctorIsCalledOnceOnDestruction() {
+ MockExportFunctor export_functor;
+ {
+ auto scoped_timer = TimerFactory::Start(export_functor);
+ EXPECT_EQ(0, export_functor.number_of_calls());
+ }
+ const int expected_number_of_calls = TimerFactory::IsSupported() ? 1 : 0;
+ EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
+}
+
+template <typename TimerFactory>
+void ExpectStoredLambdaIsInvokedOnceOnDestruction() {
+ bool export_is_called = false;
+ auto export_functor = [&export_is_called](base::TimeDelta) {
+ EXPECT_FALSE(export_is_called);
+ export_is_called = true;
+ };
+
+ {
+ auto scoped_timer = TimerFactory::Start(export_functor);
+ EXPECT_FALSE(export_is_called);
+ }
+ EXPECT_EQ(TimerFactory::IsSupported(), export_is_called);
+}
+
+template <typename TimerFactory>
+void ExpectInlineLambdaIsInvokedOnceOnDestruction() {
+ bool export_is_called = false;
+ {
+ auto scoped_timer =
+ TimerFactory::Start([&export_is_called](base::TimeDelta) {
+ EXPECT_FALSE(export_is_called);
+ export_is_called = true;
+ });
+ EXPECT_FALSE(export_is_called);
+ }
+ EXPECT_EQ(TimerFactory::IsSupported(), export_is_called);
+}
+
+template <typename TimerFactory>
+void ExpectWellBehavedStartIf(bool condition) {
+ bool export_is_called = false;
+ auto export_functor = [&export_is_called](base::TimeDelta) {
+ EXPECT_FALSE(export_is_called);
+ export_is_called = true;
+ };
+
+ {
+ auto scoped_timer = TimerFactory::StartIf(condition, export_functor);
+ EXPECT_FALSE(export_is_called);
+ }
+ EXPECT_EQ(condition && TimerFactory::IsSupported(), export_is_called);
+}
+
+template <typename TimerFactory>
+void ExpectWellBehavedMoveContructor() {
+ MockExportFunctor export_functor;
+ const int expected_number_of_calls = TimerFactory::IsSupported() ? 1 : 0;
+ {
+ auto scoped_timer = TimerFactory::Start(export_functor);
+ EXPECT_EQ(0, export_functor.number_of_calls());
+ {
+ auto another_scoped_timer = std::move(scoped_timer);
+ EXPECT_EQ(0, export_functor.number_of_calls());
+ }
+ // |another_scoped_timer| should have called |export_functor|.
+ EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
+ }
+ // But |scoped_timer| should have not since then.
+ EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
+}
+
+template <typename TimerFactory>
+void ExpectWellBehavedMoveAssignment() {
+ MockExportFunctor export_functor;
+ const int expected_number_of_calls = TimerFactory::IsSupported() ? 1 : 0;
+ {
+ auto scoped_timer = TimerFactory::Start(export_functor);
+ EXPECT_EQ(0, export_functor.number_of_calls());
+ {
+ auto another_scoped_timer = std::move(scoped_timer);
+ scoped_timer = std::move(another_scoped_timer);
+ EXPECT_EQ(0, export_functor.number_of_calls());
+ }
+ // |another_scoped_timer| shouldn't have called |export_functor|.
+ EXPECT_EQ(0, export_functor.number_of_calls());
+ }
+ // But |scoped_timer| should have because it owns the measurement.
+ EXPECT_EQ(expected_number_of_calls, export_functor.number_of_calls());
+}
+
+} // namespace
+
+namespace subresource_filter {
+
+TEST(ScopedTimersTest, CallsFunctor) {
+ ExpectFunctorIsCalledOnceOnDestruction<ScopedTimers>();
+ ExpectFunctorIsCalledOnceOnDestruction<ScopedThreadTimers>();
+}
+
+TEST(ScopedTimersTest, CallsStoredLambdaFunctor) {
+ ExpectStoredLambdaIsInvokedOnceOnDestruction<ScopedTimers>();
+ ExpectStoredLambdaIsInvokedOnceOnDestruction<ScopedThreadTimers>();
+}
+
+TEST(ScopedTimersTest, CallsInlineLambdaFunctor) {
+ ExpectInlineLambdaIsInvokedOnceOnDestruction<ScopedTimers>();
+ ExpectInlineLambdaIsInvokedOnceOnDestruction<ScopedThreadTimers>();
+}
+
+TEST(ScopedTimersTest, StartIf) {
+ ExpectWellBehavedStartIf<ScopedTimers>(false);
+ ExpectWellBehavedStartIf<ScopedTimers>(true);
+
+ ExpectWellBehavedStartIf<ScopedThreadTimers>(false);
+ ExpectWellBehavedStartIf<ScopedThreadTimers>(true);
+}
+
+TEST(ScopedTimersTest, MoveConstructTimer) {
+ ExpectWellBehavedMoveContructor<ScopedTimers>();
+ ExpectWellBehavedMoveContructor<ScopedThreadTimers>();
+}
+
+TEST(ScopedTimersTest, MoveAssignTimer) {
+ ExpectWellBehavedMoveAssignment<ScopedTimers>();
+ ExpectWellBehavedMoveAssignment<ScopedThreadTimers>();
+}
+
+// Below are tests for macros in "time_measurements.h" -------------------------
+// TODO(pkalinnikov): Move these to a separate file?
+
+TEST(ScopedTimersTest, ScopedUmaHistogramMacros) {
+ base::HistogramTester tester;
+ {
+ SCOPED_UMA_HISTOGRAM_THREAD_TIMER("ScopedTimers.ThreadTimer");
+ SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER("ScopedTimers.MicroThreadTimer");
+ SCOPED_UMA_HISTOGRAM_MICRO_TIMER("ScopedTimers.MicroTimer");
+
+ tester.ExpectTotalCount("ScopedTimers.ThreadTimer", 0);
+ tester.ExpectTotalCount("ScopedTimers.MicroThreadTimer", 0);
+ tester.ExpectTotalCount("ScopedTimers.MicroTimer", 0);
+ }
+
+ int expected_count = ScopedTimers::IsSupported() ? 1 : 0;
+ tester.ExpectTotalCount("ScopedTimers.MicroTimer", expected_count);
+
+ expected_count = ScopedThreadTimers::IsSupported() ? 1 : 0;
+ tester.ExpectTotalCount("ScopedTimers.ThreadTimer", expected_count);
+ tester.ExpectTotalCount("ScopedTimers.MicroThreadTimer", expected_count);
+}
+
+TEST(ScopedTimersTest, UmaHistogramMicroTimesFromExportFunctor) {
+ base::HistogramTester tester;
+ auto export_functor = [](base::TimeDelta delta) {
+ UMA_HISTOGRAM_MICRO_TIMES("ScopedTimers.MicroTimes", delta);
+ };
+ {
+ auto scoped_timer = ScopedTimers::Start(export_functor);
+ tester.ExpectTotalCount("ScopedTimers.MicroTimes", 0);
+ }
+ tester.ExpectTotalCount("ScopedTimers.MicroTimes", 1);
+}
+
+} // 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 81dd7b3d211..08cd6271e92 100644
--- a/chromium/components/subresource_filter/core/common/test_ruleset_creator.cc
+++ b/chromium/components/subresource_filter/core/common/test_ruleset_creator.cc
@@ -9,6 +9,7 @@
#include "base/strings/string_number_conversions.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 "testing/gtest/include/gtest/gtest.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
@@ -20,34 +21,29 @@ namespace {
// The methods below assume that char and uint8_t are interchangeable.
static_assert(CHAR_BIT == 8, "Assumed char was 8 bits.");
-proto::UrlRule CreateSuffixRule(base::StringPiece suffix) {
- proto::UrlRule rule;
- rule.set_semantics(proto::RULE_SEMANTICS_BLACKLIST);
- rule.set_source_type(proto::SOURCE_TYPE_ANY);
- rule.set_element_types(proto::ELEMENT_TYPE_ALL);
- rule.set_url_pattern_type(proto::URL_PATTERN_TYPE_SUBSTRING);
- rule.set_anchor_left(proto::ANCHOR_TYPE_NONE);
- rule.set_anchor_right(proto::ANCHOR_TYPE_BOUNDARY);
- rule.set_url_pattern(suffix.as_string());
- return rule;
-}
-
-std::vector<uint8_t> SerializeUnindexedRulesetWithSingleRule(
- const proto::UrlRule& rule) {
+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);
- ruleset_writer.AddUrlRule(rule);
+ for (const auto& rule : rules)
+ ruleset_writer.AddUrlRule(rule);
ruleset_writer.Finish();
auto data = reinterpret_cast<const uint8_t*>(ruleset_contents.data());
return std::vector<uint8_t>(data, data + ruleset_contents.size());
}
-std::vector<uint8_t> SerializeIndexedRulesetWithSingleRule(
+std::vector<uint8_t> SerializeUnindexedRulesetWithSingleRule(
const proto::UrlRule& rule) {
+ return SerializeUnindexedRulesetWithMultipleRules({rule});
+}
+
+std::vector<uint8_t> SerializeIndexedRulesetWithMultipleRules(
+ const std::vector<proto::UrlRule>& rules) {
RulesetIndexer indexer;
- EXPECT_TRUE(indexer.AddUrlRule(rule));
+ for (const auto& rule : rules)
+ EXPECT_TRUE(indexer.AddUrlRule(rule));
indexer.Finish();
return std::vector<uint8_t>(indexer.data(), indexer.data() + indexer.size());
}
@@ -84,12 +80,7 @@ void TestRulesetCreator::CreateRulesetToDisallowURLsWithPathSuffix(
TestRulesetPair* test_ruleset_pair) {
DCHECK(test_ruleset_pair);
proto::UrlRule suffix_rule = CreateSuffixRule(suffix);
- ASSERT_NO_FATAL_FAILURE(CreateTestRulesetFromContents(
- SerializeUnindexedRulesetWithSingleRule(suffix_rule),
- &test_ruleset_pair->unindexed));
- ASSERT_NO_FATAL_FAILURE(CreateTestRulesetFromContents(
- SerializeIndexedRulesetWithSingleRule(suffix_rule),
- &test_ruleset_pair->indexed));
+ CreateRulesetWithRules({suffix_rule}, test_ruleset_pair);
}
void TestRulesetCreator::CreateUnindexedRulesetToDisallowURLsWithPathSuffix(
@@ -102,6 +93,17 @@ void TestRulesetCreator::CreateUnindexedRulesetToDisallowURLsWithPathSuffix(
test_unindexed_ruleset));
}
+void TestRulesetCreator::CreateRulesetWithRules(
+ const std::vector<proto::UrlRule>& rules,
+ TestRulesetPair* test_ruleset_pair) {
+ ASSERT_NO_FATAL_FAILURE(CreateTestRulesetFromContents(
+ SerializeUnindexedRulesetWithMultipleRules(rules),
+ &test_ruleset_pair->unindexed));
+ ASSERT_NO_FATAL_FAILURE(CreateTestRulesetFromContents(
+ SerializeIndexedRulesetWithMultipleRules(rules),
+ &test_ruleset_pair->indexed));
+}
+
void TestRulesetCreator::GetUniqueTemporaryPath(base::FilePath* path) {
DCHECK(path);
ASSERT_TRUE(scoped_temp_dir_.IsValid() ||
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 83c4cd3bff4..eef00b0e56f 100644
--- a/chromium/components/subresource_filter/core/common/test_ruleset_creator.h
+++ b/chromium/components/subresource_filter/core/common/test_ruleset_creator.h
@@ -14,6 +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"
namespace subresource_filter {
namespace testing {
@@ -60,6 +61,9 @@ class TestRulesetCreator {
base::StringPiece suffix,
TestRulesetPair* test_ruleset_pair);
+ void CreateRulesetWithRules(const std::vector<proto::UrlRule>& rules,
+ TestRulesetPair* test_ruleset_pair);
+
// Same as above, but only creates an unindexed ruleset.
void CreateUnindexedRulesetToDisallowURLsWithPathSuffix(
base::StringPiece suffix,
diff --git a/chromium/components/subresource_filter/core/common/test_ruleset_utils.cc b/chromium/components/subresource_filter/core/common/test_ruleset_utils.cc
new file mode 100644
index 00000000000..081a4a19993
--- /dev/null
+++ b/chromium/components/subresource_filter/core/common/test_ruleset_utils.cc
@@ -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.
+
+#include "components/subresource_filter/core/common/test_ruleset_utils.h"
+
+namespace subresource_filter {
+namespace testing {
+
+proto::UrlRule CreateSuffixRule(base::StringPiece suffix) {
+ proto::UrlRule rule;
+ rule.set_semantics(proto::RULE_SEMANTICS_BLACKLIST);
+ rule.set_source_type(proto::SOURCE_TYPE_ANY);
+ rule.set_element_types(proto::ELEMENT_TYPE_ALL);
+ rule.set_url_pattern_type(proto::URL_PATTERN_TYPE_SUBSTRING);
+ rule.set_anchor_left(proto::ANCHOR_TYPE_NONE);
+ rule.set_anchor_right(proto::ANCHOR_TYPE_BOUNDARY);
+ rule.set_url_pattern(suffix.as_string());
+ return rule;
+}
+
+proto::UrlRule CreateWhitelistRuleForDocument(base::StringPiece pattern) {
+ proto::UrlRule rule;
+ rule.set_source_type(proto::SOURCE_TYPE_ANY);
+ rule.set_element_types(proto::ELEMENT_TYPE_ALL);
+ rule.set_url_pattern_type(proto::URL_PATTERN_TYPE_SUBSTRING);
+ rule.set_anchor_left(proto::ANCHOR_TYPE_NONE);
+ rule.set_anchor_right(proto::ANCHOR_TYPE_NONE);
+ rule.set_semantics(proto::RULE_SEMANTICS_WHITELIST);
+ rule.set_activation_types(proto::ACTIVATION_TYPE_DOCUMENT);
+ rule.set_url_pattern(pattern.as_string());
+ return rule;
+}
+
+} // namespace testing
+} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/test_ruleset_utils.h b/chromium/components/subresource_filter/core/common/test_ruleset_utils.h
new file mode 100644
index 00000000000..97c2452eed4
--- /dev/null
+++ b/chromium/components/subresource_filter/core/common/test_ruleset_utils.h
@@ -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.
+
+#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TEST_RULESET_UTILS_H_
+#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TEST_RULESET_UTILS_H_
+
+#include "base/strings/string_piece.h"
+#include "components/subresource_filter/core/common/proto/rules.pb.h"
+
+namespace subresource_filter {
+namespace testing {
+
+proto::UrlRule CreateSuffixRule(base::StringPiece suffix);
+
+proto::UrlRule CreateWhitelistRuleForDocument(base::StringPiece pattern);
+
+} // namespace testing
+} // namespace subresource_filter
+
+#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TEST_RULESET_UTILS_H_
diff --git a/chromium/components/subresource_filter/core/common/time_measurements.h b/chromium/components/subresource_filter/core/common/time_measurements.h
new file mode 100644
index 00000000000..e682ffce237
--- /dev/null
+++ b/chromium/components/subresource_filter/core/common/time_measurements.h
@@ -0,0 +1,150 @@
+// 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 provides tools for measuring time intervals and reporting them to
+// UMA histograms.
+// WARNING: *UMA_HISTOGRAM_* macros in this file are not thread-safe.
+// See also: "base/metrics/histogram_macros*.h".
+//
+// TODO(pkalinnikov): Consider moving content of this file to "base/metrics/*"
+// after some refactoring. Note that most of the code generated by the macros
+// below is not thread-safe.
+
+#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TIME_MEASUREMENTS_
+#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TIME_MEASUREMENTS_
+
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+#include "components/subresource_filter/core/common/scoped_timers.h"
+
+namespace subresource_filter {
+
+// Creates a scoped object that measures its lifetime using base::ThreadTicks,
+// and reports the result in milliseconds as a UMA statistic to a histogram with
+// the provided |name| which is expected to be a runtime constant. The histogram
+// collects times up to 10 seconds in 50 buckets.
+//
+// Under the hood there is a static base::HistogramBase* pointer initialized
+// right before the scoped object. The pointer is used by a specific
+// |export_functor| passed in to a SCOPED_THREAD_TIMER (see it above).
+//
+// Example:
+// void Function() {
+// SCOPED_UMA_HISTOGRAM_THREAD_TIMER("Component.FunctionTime");
+// ... Useful things happen here ...
+// }
+//
+// WARNING: The generated code is not thread-safe.
+#define SCOPED_UMA_HISTOGRAM_THREAD_TIMER(name) \
+ IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER( \
+ name, impl::ThreadTicksProvider, impl::ExportMillisecondsToHistogram, \
+ 10 * 1000, __COUNTER__)
+
+// Similar to SCOPED_UMA_HISTOGRAM_THREAD_TIMER above, but the histogram
+// collects times in microseconds, up to 1 second, and using 50 buckets.
+//
+// WARNING: The generated code is not thread-safe.
+#define SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER(name) \
+ IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER( \
+ name, impl::ThreadTicksProvider, impl::ExportMicrosecondsToHistogram, \
+ 1000 * 1000, __COUNTER__)
+
+// Similar to SCOPED_UMA_HISTOGRAM_TIMER in "base/metrics/histogram_macros.h",
+// but the histogram stores times in microseconds, up to 1 second, in 50
+// buckets.
+//
+// WARNING: The generated code is not thread-safe.
+#define SCOPED_UMA_HISTOGRAM_MICRO_TIMER(name) \
+ IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER( \
+ name, impl::TimeTicksProvider, impl::ExportMicrosecondsToHistogram, \
+ 1000 * 1000, __COUNTER__)
+
+// Similar to UMA_HISTOGRAM_TIMES in "base/metrics/histogram_macros.h", but
+// the histogram stores times in microseconds, up to 1 second, in 50 buckets.
+//
+// WARNING: The generated code is not thread-safe.
+#define UMA_HISTOGRAM_MICRO_TIMES(name, sample) \
+ UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(name, sample, \
+ base::TimeDelta::FromMicroseconds(1), \
+ base::TimeDelta::FromSeconds(1), 50)
+
+// This can be used when the default ranges are not sufficient. This macro lets
+// the metric developer customize the min and max of the sampled range, as well
+// as the number of buckets recorded.
+#define UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(name, sample, min, max, bucket_count) \
+ IMPL_UMA_HISTOGRAM_ADD(name, sample.InMicroseconds(), min.InMicroseconds(), \
+ max.InMicroseconds(), bucket_count)
+
+// -----------------------------------------------------------------------------
+// Below are helpers used by other macros. Shouldn't be used directly. ---------
+
+// This is necessary to expand __COUNTER__ to an actual value.
+#define IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER( \
+ name, time_provider, histogram_exporter, max_value, suffix) \
+ IMPL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE( \
+ name, time_provider, histogram_exporter, max_value, suffix)
+
+// Creates a static histogram pointer and a scoped object referring to it
+// throught the |histogram_exporter| functor. Both the pointer and the scoped
+// object are uniquely-named, using the unique |suffix| passed in.
+#define IMPL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE( \
+ name, time_provider, histogram_exporter, max_value, suffix) \
+ IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, 1, max_value, 50, suffix) \
+ auto scoped_uma_histogram_timer_##suffix = \
+ impl::ScopedTimerImplFactory<time_provider>::Start( \
+ histogram_exporter(histogram_##suffix));
+
+// This is necessary to expand __COUNTER__ to an actual value.
+#define IMPL_UMA_HISTOGRAM_MICRO_TIMES_EXPANDER(name, max_value, suffix, \
+ sample) \
+ IMPL_UMA_HISTOGRAM_MICRO_TIMES_UNIQUE(name, max_value, suffix, sample)
+
+// Defines a static UMA histogram pointer and writes a |sample| to it.
+#define IMPL_UMA_HISTOGRAM_ADD(name, sample, min, max, bucket_count) \
+ do { \
+ IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, min, max, bucket_count, 0) \
+ histogram_0->Add(sample); \
+ } while (0)
+
+// Defines a static pointer to a UMA histogram.
+//
+// WARNING: Static local variable initialization is deliberately *not*
+// thread-safe in Chrome builds. See the "-fno-threadsafe-statics" flag in
+// "build/config/compiler/BUILD.gn" and "/Zc:threadSafeInit-" in
+// "build/config/win/BUILD.gn" for details.
+#define IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, min, max, bucket_count, \
+ suffix) \
+ static base::HistogramBase* histogram_##suffix = \
+ base::Histogram::FactoryGet( \
+ name, min, max, bucket_count, \
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+
+namespace impl {
+
+// ExportFunctor implementation that puts measurements into a UMA |histogram|.
+template <bool is_microsec_precision>
+class ExportTimeDeltaToHistogram {
+ public:
+ ExportTimeDeltaToHistogram(base::HistogramBase* histogram)
+ : histogram_(histogram) {}
+
+ void operator()(base::TimeDelta duration) {
+ if (is_microsec_precision)
+ histogram_->Add(duration.InMicroseconds());
+ else
+ histogram_->Add(duration.InMilliseconds());
+ }
+
+ private:
+ base::HistogramBase* histogram_;
+};
+
+using ExportMillisecondsToHistogram = ExportTimeDeltaToHistogram<false>;
+using ExportMicrosecondsToHistogram = ExportTimeDeltaToHistogram<true>;
+
+} // namespace impl
+
+} // namespace subresource_filter
+
+#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TIME_MEASUREMENTS_
diff --git a/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc b/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc
index 641a73d906d..12a4f5d446b 100644
--- a/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc
+++ b/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc
@@ -4,6 +4,7 @@
#include "components/subresource_filter/core/common/unindexed_ruleset.h"
+#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
namespace subresource_filter {
diff --git a/chromium/components/suggestions/BUILD.gn b/chromium/components/suggestions/BUILD.gn
index ed539dbae4a..32fb84f2996 100644
--- a/chromium/components/suggestions/BUILD.gn
+++ b/chromium/components/suggestions/BUILD.gn
@@ -11,8 +11,9 @@ static_library("suggestions") {
"image_manager.h",
"suggestions_pref_names.cc",
"suggestions_pref_names.h",
- "suggestions_service.cc",
"suggestions_service.h",
+ "suggestions_service_impl.cc",
+ "suggestions_service_impl.h",
"suggestions_store.cc",
"suggestions_store.h",
]
@@ -51,7 +52,7 @@ source_set("unit_tests") {
sources = [
"blacklist_store_unittest.cc",
"image_manager_unittest.cc",
- "suggestions_service_unittest.cc",
+ "suggestions_service_impl_unittest.cc",
"suggestions_store_unittest.cc",
]
deps = [
@@ -59,10 +60,10 @@ source_set("unit_tests") {
"//base/test:test_support",
"//components/image_fetcher",
"//components/leveldb_proto:test_support",
- "//components/pref_registry:test_support",
"//components/signin/core/browser:test_support",
"//components/sync",
"//components/sync:test_support_driver",
+ "//components/sync_preferences:test_support",
"//net:test_support",
"//testing/gmock",
"//testing/gtest",
diff --git a/chromium/components/suggestions/DEPS b/chromium/components/suggestions/DEPS
index 78c843337ca..aa0c78e9f5c 100644
--- a/chromium/components/suggestions/DEPS
+++ b/chromium/components/suggestions/DEPS
@@ -8,6 +8,7 @@ include_rules = [
"+components/prefs",
"+components/signin/core/browser",
"+components/sync/driver",
+ "+components/sync_preferences",
"+components/variations",
"+google_apis",
"+net",
diff --git a/chromium/components/suggestions/blacklist_store_unittest.cc b/chromium/components/suggestions/blacklist_store_unittest.cc
index fdcc0da9072..14caf0f6491 100644
--- a/chromium/components/suggestions/blacklist_store_unittest.cc
+++ b/chromium/components/suggestions/blacklist_store_unittest.cc
@@ -10,11 +10,11 @@
#include "base/macros.h"
#include "base/test/histogram_tester.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
#include "components/suggestions/proto/suggestions.pb.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
-using user_prefs::TestingPrefServiceSyncable;
+using sync_preferences::TestingPrefServiceSyncable;
namespace suggestions {
@@ -53,18 +53,18 @@ void ValidateSuggestions(const SuggestionsProfile& expected,
class BlacklistStoreTest : public testing::Test {
public:
BlacklistStoreTest()
- : pref_service_(new user_prefs::TestingPrefServiceSyncable) {}
+ : pref_service_(new sync_preferences::TestingPrefServiceSyncable) {}
void SetUp() override {
BlacklistStore::RegisterProfilePrefs(pref_service()->registry());
}
- user_prefs::TestingPrefServiceSyncable* pref_service() {
+ sync_preferences::TestingPrefServiceSyncable* pref_service() {
return pref_service_.get();
}
private:
- std::unique_ptr<user_prefs::TestingPrefServiceSyncable> pref_service_;
+ std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_;
DISALLOW_COPY_AND_ASSIGN(BlacklistStoreTest);
};
diff --git a/chromium/components/suggestions/image_manager.h b/chromium/components/suggestions/image_manager.h
index 200e3dfacc5..68589cf4305 100644
--- a/chromium/components/suggestions/image_manager.h
+++ b/chromium/components/suggestions/image_manager.h
@@ -32,10 +32,6 @@ namespace image_fetcher {
class ImageFetcher;
}
-namespace net {
-class URLRequestContextGetter;
-}
-
namespace suggestions {
class ImageData;
diff --git a/chromium/components/suggestions/proto/suggestions.proto b/chromium/components/suggestions/proto/suggestions.proto
index 12621e4b7eb..27f5d141c33 100644
--- a/chromium/components/suggestions/proto/suggestions.proto
+++ b/chromium/components/suggestions/proto/suggestions.proto
@@ -52,10 +52,6 @@ message ChromeSuggestion {
// The timestamp (usec) at which this suggestion ceases to be valid.
optional int64 expiry_ts = 7;
-
- // URL that should be pinged back when the suggestion is shown/clicked.
- optional string impression_url = 13;
- optional string click_url = 14;
}
// A list of URLs that should be filtered from the SuggestionsProfile.
diff --git a/chromium/components/suggestions/suggestions_service.h b/chromium/components/suggestions/suggestions_service.h
index 5b118e11d2a..9773ee07418 100644
--- a/chromium/components/suggestions/suggestions_service.h
+++ b/chromium/components/suggestions/suggestions_service.h
@@ -5,54 +5,24 @@
#ifndef COMPONENTS_SUGGESTIONS_SUGGESTIONS_SERVICE_H_
#define COMPONENTS_SUGGESTIONS_SUGGESTIONS_SERVICE_H_
-#include <stdint.h>
-
#include <memory>
-#include <string>
#include "base/callback.h"
#include "base/callback_list.h"
-#include "base/gtest_prod_util.h"
#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
-#include "base/threading/thread_checker.h"
-#include "base/time/time.h"
+#include "base/optional.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/suggestions/proto/suggestions.pb.h"
-#include "components/sync/driver/sync_service_observer.h"
-#include "net/url_request/url_fetcher_delegate.h"
#include "url/gurl.h"
-class OAuth2TokenService;
-class SigninManagerBase;
-
namespace gfx {
class Image;
} // namespce gfx
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
-
-namespace syncer {
-class SyncService;
-} // namespace syncer
-
-namespace user_prefs {
-class PrefRegistrySyncable;
-} // namespace user_prefs
-
namespace suggestions {
-class BlacklistStore;
-class ImageManager;
-class SuggestionsStore;
-
// An interface to fetch server suggestions asynchronously.
-class SuggestionsService : public KeyedService,
- public net::URLFetcherDelegate,
- public syncer::SyncServiceObserver {
+class SuggestionsService : public KeyedService {
public:
using ResponseCallback = base::Callback<void(const SuggestionsProfile&)>;
using BitmapCallback = base::Callback<void(const GURL&, const gfx::Image&)>;
@@ -60,171 +30,41 @@ class SuggestionsService : public KeyedService,
using ResponseCallbackList =
base::CallbackList<void(const SuggestionsProfile&)>;
- SuggestionsService(const SigninManagerBase* signin_manager,
- OAuth2TokenService* token_service,
- syncer::SyncService* sync_service,
- net::URLRequestContextGetter* url_request_context,
- std::unique_ptr<SuggestionsStore> suggestions_store,
- std::unique_ptr<ImageManager> thumbnail_manager,
- std::unique_ptr<BlacklistStore> blacklist_store);
- ~SuggestionsService() override;
-
// Initiates a network request for suggestions if sync state allows and there
// is no pending request. Returns true iff sync state allowed for a request,
// whether a new request was actually sent or not.
- bool FetchSuggestionsData();
+ virtual bool FetchSuggestionsData() = 0;
// Returns the current set of suggestions from the cache.
- SuggestionsProfile GetSuggestionsDataFromCache() const;
+ virtual base::Optional<SuggestionsProfile> GetSuggestionsDataFromCache()
+ const = 0;
// Adds a callback that is called when the suggestions are updated.
- std::unique_ptr<ResponseCallbackList::Subscription> AddCallback(
- const ResponseCallback& callback) WARN_UNUSED_RESULT;
+ virtual std::unique_ptr<ResponseCallbackList::Subscription> AddCallback(
+ const ResponseCallback& callback) WARN_UNUSED_RESULT = 0;
// Retrieves stored thumbnail for website |url| asynchronously. Calls
// |callback| with Bitmap pointer if found, and NULL otherwise.
- void GetPageThumbnail(const GURL& url, const BitmapCallback& callback);
+ virtual void GetPageThumbnail(const GURL& url,
+ const BitmapCallback& callback) = 0;
// A version of |GetPageThumbnail| that explicitly supplies the download URL
// for the thumbnail. Replaces any pre-existing thumbnail URL with the
// supplied one.
- void GetPageThumbnailWithURL(const GURL& url,
- const GURL& thumbnail_url,
- const BitmapCallback& callback);
+ virtual void GetPageThumbnailWithURL(const GURL& url,
+ const GURL& thumbnail_url,
+ const BitmapCallback& callback) = 0;
// Adds a URL to the blacklist cache, returning true on success or false on
// failure. The URL will eventually be uploaded to the server.
- bool BlacklistURL(const GURL& candidate_url);
+ virtual bool BlacklistURL(const GURL& candidate_url) = 0;
// Removes a URL from the local blacklist, returning true on success or false
// on failure.
- bool UndoBlacklistURL(const GURL& url);
+ virtual bool UndoBlacklistURL(const GURL& url) = 0;
// Removes all URLs from the blacklist.
- void ClearBlacklist();
-
- // Determines which URL a blacklist request was for, irrespective of the
- // request's status. Returns false if |request| is not a blacklist request.
- static bool GetBlacklistedUrl(const net::URLFetcher& request, GURL* url);
-
- // Register SuggestionsService related prefs in the Profile prefs.
- static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
- private:
- friend class SuggestionsServiceTest;
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, FetchSuggestionsData);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
- FetchSuggestionsDataSyncDisabled);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
- FetchSuggestionsDataNoAccessToken);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
- IssueRequestIfNoneOngoingError);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
- IssueRequestIfNoneOngoingResponseNotOK);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, BlacklistURL);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, BlacklistURLRequestFails);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, ClearBlacklist);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, UndoBlacklistURL);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, GetBlacklistedUrl);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, UpdateBlacklistDelay);
- FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, CheckDefaultTimeStamps);
-
- // Helpers to build the various suggestions URLs. These are static members
- // rather than local functions in the .cc file to make them accessible to
- // tests.
- static GURL BuildSuggestionsURL();
- static std::string BuildSuggestionsBlacklistURLPrefix();
- static GURL BuildSuggestionsBlacklistURL(const GURL& candidate_url);
- static GURL BuildSuggestionsBlacklistClearURL();
-
- // syncer::SyncServiceObserver implementation.
- void OnStateChanged() override;
-
- // Sets default timestamp for suggestions which do not have expiry timestamp.
- void SetDefaultExpiryTimestamp(SuggestionsProfile* suggestions,
- int64_t timestamp_usec);
-
- // Issues a network request if there isn't already one happening.
- void IssueRequestIfNoneOngoing(const GURL& url);
-
- // Issues a network request for suggestions (fetch, blacklist, or clear
- // blacklist, depending on |url|). |access_token| is used only if OAuth2
- // authentication is enabled.
- void IssueSuggestionsRequest(const GURL& url,
- const std::string& access_token);
-
- // Creates a request to the suggestions service, properly setting headers.
- // If OAuth2 authentication is enabled, |access_token| should be a valid
- // OAuth2 access token, and will be written into an auth header.
- std::unique_ptr<net::URLFetcher> CreateSuggestionsRequest(
- const GURL& url,
- const std::string& access_token);
-
- // net::URLFetcherDelegate implementation.
- // Called when fetch request completes. Parses the received suggestions data,
- // and dispatches them to callbacks stored in queue.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
-
- // KeyedService implementation.
- void Shutdown() override;
-
- // Schedules a blacklisting request if the local blacklist isn't empty.
- void ScheduleBlacklistUpload();
-
- // If the local blacklist isn't empty, picks a URL from it and issues a
- // blacklist request for it.
- void UploadOneFromBlacklist();
-
- // Updates |scheduling_delay_| based on the success of the last request.
- void UpdateBlacklistDelay(bool last_request_successful);
-
- // Adds extra data to suggestions profile.
- void PopulateExtraData(SuggestionsProfile* suggestions);
-
- // Test seams.
- base::TimeDelta blacklist_delay() const { return scheduling_delay_; }
- void set_blacklist_delay(base::TimeDelta delay) {
- scheduling_delay_ = delay; }
-
- base::ThreadChecker thread_checker_;
-
- syncer::SyncService* sync_service_;
- ScopedObserver<syncer::SyncService, syncer::SyncServiceObserver>
- sync_service_observer_;
-
- net::URLRequestContextGetter* url_request_context_;
-
- // The cache for the suggestions.
- std::unique_ptr<SuggestionsStore> suggestions_store_;
-
- // Used to obtain server thumbnails, if available.
- std::unique_ptr<ImageManager> thumbnail_manager_;
-
- // The local cache for temporary blacklist, until uploaded to the server.
- std::unique_ptr<BlacklistStore> blacklist_store_;
-
- // Delay used when scheduling a blacklisting task.
- base::TimeDelta scheduling_delay_;
-
- // Helper for fetching OAuth2 access tokens.
- class AccessTokenFetcher;
- std::unique_ptr<AccessTokenFetcher> token_fetcher_;
-
- // Contains the current suggestions fetch request. Will only have a value
- // while a request is pending, and will be reset by |OnURLFetchComplete| or
- // if cancelled.
- std::unique_ptr<net::URLFetcher> pending_request_;
-
- // The start time of the previous suggestions request. This is used to measure
- // the latency of requests. Initially zero.
- base::TimeTicks last_request_started_time_;
-
- ResponseCallbackList callback_list_;
-
- // For callbacks may be run after destruction.
- base::WeakPtrFactory<SuggestionsService> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(SuggestionsService);
+ virtual void ClearBlacklist() = 0;
};
} // namespace suggestions
diff --git a/chromium/components/suggestions/suggestions_service.cc b/chromium/components/suggestions/suggestions_service_impl.cc
index b5850f0da8c..5f30527c68b 100644
--- a/chromium/components/suggestions/suggestions_service.cc
+++ b/chromium/components/suggestions/suggestions_service_impl.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/suggestions/suggestions_service.h"
+#include "components/suggestions/suggestions_service_impl.h"
#include <memory>
#include <utility>
@@ -66,7 +66,7 @@ enum SyncState {
};
SyncState GetSyncState(syncer::SyncService* sync) {
- if (!sync || !sync->CanSyncStart())
+ if (!sync || !sync->CanSyncStart() || sync->IsLocalSyncEnabled())
return SYNC_OR_HISTORY_SYNC_DISABLED;
if (!sync->IsSyncActive() || !sync->ConfigurationDone())
return NOT_INITIALIZED_ENABLED;
@@ -111,8 +111,7 @@ GURL GetGoogleBaseURL() {
// Format strings for the various suggestions URLs. They all have two string
// params: The Google base URL and the device type.
// TODO(mathp): Put this in TemplateURL.
-const char kSuggestionsURLFormat[] =
- "%schromesuggestions?t=%s";
+const char kSuggestionsURLFormat[] = "%schromesuggestions?t=%s";
const char kSuggestionsBlacklistURLPrefixFormat[] =
"%schromesuggestions/blacklist?t=%s&url=";
const char kSuggestionsBlacklistClearURLFormat[] =
@@ -132,9 +131,6 @@ const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s";
const char kFaviconURL[] =
"https://s2.googleusercontent.com/s2/favicons?domain_url=%s&alt=s&sz=32";
-const char kPingURL[] =
- "https://www.google.com/chromesuggestions/click?q=%lld&cd=%d";
-
// The default expiry timeout is 168 hours.
const int64_t kDefaultExpiryUsec = 168 * base::Time::kMicrosecondsPerHour;
@@ -143,7 +139,7 @@ const int64_t kDefaultExpiryUsec = 168 * base::Time::kMicrosecondsPerHour;
// Helper class for fetching OAuth2 access tokens.
// To get a token, call |GetAccessToken|. Does not support multiple concurrent
// token requests, i.e. check |HasPendingRequest| first.
-class SuggestionsService::AccessTokenFetcher
+class SuggestionsServiceImpl::AccessTokenFetcher
: public OAuth2TokenService::Consumer {
public:
using TokenCallback = base::Callback<void(const std::string&)>;
@@ -165,9 +161,7 @@ class SuggestionsService::AccessTokenFetcher
token_request_ = token_service_->StartRequest(account_id, scopes, this);
}
- bool HasPendingRequest() const {
- return !!token_request_.get();
- }
+ bool HasPendingRequest() const { return !!token_request_.get(); }
private:
void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
@@ -193,7 +187,7 @@ class SuggestionsService::AccessTokenFetcher
std::unique_ptr<OAuth2TokenService::Request> token_request_;
};
-SuggestionsService::SuggestionsService(
+SuggestionsServiceImpl::SuggestionsServiceImpl(
const SigninManagerBase* signin_manager,
OAuth2TokenService* token_service,
syncer::SyncService* sync_service,
@@ -218,9 +212,9 @@ SuggestionsService::SuggestionsService(
OnStateChanged();
}
-SuggestionsService::~SuggestionsService() {}
+SuggestionsServiceImpl::~SuggestionsServiceImpl() {}
-bool SuggestionsService::FetchSuggestionsData() {
+bool SuggestionsServiceImpl::FetchSuggestionsData() {
DCHECK(thread_checker_.CalledOnValidThread());
// If sync state allows, issue a network request to refresh the suggestions.
if (GetSyncState(sync_service_) != INITIALIZED_ENABLED_HISTORY)
@@ -229,26 +223,29 @@ bool SuggestionsService::FetchSuggestionsData() {
return true;
}
-SuggestionsProfile SuggestionsService::GetSuggestionsDataFromCache() const {
+base::Optional<SuggestionsProfile>
+SuggestionsServiceImpl::GetSuggestionsDataFromCache() const {
SuggestionsProfile suggestions;
- // In case of empty cache or error, |suggestions| stays empty.
- suggestions_store_->LoadSuggestions(&suggestions);
+ // In case of empty cache or error, return empty.
+ if (!suggestions_store_->LoadSuggestions(&suggestions)) {
+ return base::Optional<SuggestionsProfile>();
+ }
thumbnail_manager_->Initialize(suggestions);
blacklist_store_->FilterSuggestions(&suggestions);
- return suggestions;
+ return base::Optional<SuggestionsProfile>(suggestions);
}
-std::unique_ptr<SuggestionsService::ResponseCallbackList::Subscription>
-SuggestionsService::AddCallback(const ResponseCallback& callback) {
+std::unique_ptr<SuggestionsServiceImpl::ResponseCallbackList::Subscription>
+SuggestionsServiceImpl::AddCallback(const ResponseCallback& callback) {
return callback_list_.Add(callback);
}
-void SuggestionsService::GetPageThumbnail(const GURL& url,
- const BitmapCallback& callback) {
+void SuggestionsServiceImpl::GetPageThumbnail(const GURL& url,
+ const BitmapCallback& callback) {
thumbnail_manager_->GetImageForURL(url, callback);
}
-void SuggestionsService::GetPageThumbnailWithURL(
+void SuggestionsServiceImpl::GetPageThumbnailWithURL(
const GURL& url,
const GURL& thumbnail_url,
const BitmapCallback& callback) {
@@ -256,13 +253,14 @@ void SuggestionsService::GetPageThumbnailWithURL(
GetPageThumbnail(url, callback);
}
-bool SuggestionsService::BlacklistURL(const GURL& candidate_url) {
+bool SuggestionsServiceImpl::BlacklistURL(const GURL& candidate_url) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!blacklist_store_->BlacklistUrl(candidate_url))
return false;
- callback_list_.Notify(GetSuggestionsDataFromCache());
+ callback_list_.Notify(
+ GetSuggestionsDataFromCache().value_or(SuggestionsProfile()));
// Blacklist uploads are scheduled on any request completion, so only schedule
// an upload if there is no ongoing request.
@@ -272,7 +270,7 @@ bool SuggestionsService::BlacklistURL(const GURL& candidate_url) {
return true;
}
-bool SuggestionsService::UndoBlacklistURL(const GURL& url) {
+bool SuggestionsServiceImpl::UndoBlacklistURL(const GURL& url) {
DCHECK(thread_checker_.CalledOnValidThread());
TimeDelta time_delta;
if (blacklist_store_->GetTimeUntilURLReadyForUpload(url, &time_delta) &&
@@ -280,33 +278,35 @@ bool SuggestionsService::UndoBlacklistURL(const GURL& url) {
blacklist_store_->RemoveUrl(url)) {
// The URL was not yet candidate for upload to the server and could be
// removed from the blacklist.
- callback_list_.Notify(GetSuggestionsDataFromCache());
+ callback_list_.Notify(
+ GetSuggestionsDataFromCache().value_or(SuggestionsProfile()));
return true;
}
return false;
}
-void SuggestionsService::ClearBlacklist() {
+void SuggestionsServiceImpl::ClearBlacklist() {
DCHECK(thread_checker_.CalledOnValidThread());
blacklist_store_->ClearBlacklist();
- callback_list_.Notify(GetSuggestionsDataFromCache());
+ callback_list_.Notify(
+ GetSuggestionsDataFromCache().value_or(SuggestionsProfile()));
IssueRequestIfNoneOngoing(BuildSuggestionsBlacklistClearURL());
}
// static
-bool SuggestionsService::GetBlacklistedUrl(const net::URLFetcher& request,
- GURL* url) {
+bool SuggestionsServiceImpl::GetBlacklistedUrl(const net::URLFetcher& request,
+ GURL* url) {
bool is_blacklist_request = base::StartsWith(
request.GetOriginalURL().spec(), BuildSuggestionsBlacklistURLPrefix(),
base::CompareCase::SENSITIVE);
- if (!is_blacklist_request) return false;
+ if (!is_blacklist_request)
+ return false;
// Extract the blacklisted URL from the blacklist request.
std::string blacklisted;
- if (!net::GetValueForKeyInQuery(
- request.GetOriginalURL(),
- kSuggestionsBlacklistURLParam,
- &blacklisted)) {
+ if (!net::GetValueForKeyInQuery(request.GetOriginalURL(),
+ kSuggestionsBlacklistURLParam,
+ &blacklisted)) {
return false;
}
@@ -316,40 +316,39 @@ bool SuggestionsService::GetBlacklistedUrl(const net::URLFetcher& request,
}
// static
-void SuggestionsService::RegisterProfilePrefs(
+void SuggestionsServiceImpl::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
SuggestionsStore::RegisterProfilePrefs(registry);
BlacklistStore::RegisterProfilePrefs(registry);
}
// static
-GURL SuggestionsService::BuildSuggestionsURL() {
- return GURL(base::StringPrintf(kSuggestionsURLFormat,
- GetGoogleBaseURL().spec().c_str(),
- kDeviceType));
+GURL SuggestionsServiceImpl::BuildSuggestionsURL() {
+ return GURL(base::StringPrintf(
+ kSuggestionsURLFormat, GetGoogleBaseURL().spec().c_str(), kDeviceType));
}
// static
-std::string SuggestionsService::BuildSuggestionsBlacklistURLPrefix() {
+std::string SuggestionsServiceImpl::BuildSuggestionsBlacklistURLPrefix() {
return base::StringPrintf(kSuggestionsBlacklistURLPrefixFormat,
GetGoogleBaseURL().spec().c_str(), kDeviceType);
}
// static
-GURL SuggestionsService::BuildSuggestionsBlacklistURL(
+GURL SuggestionsServiceImpl::BuildSuggestionsBlacklistURL(
const GURL& candidate_url) {
return GURL(BuildSuggestionsBlacklistURLPrefix() +
net::EscapeQueryParamValue(candidate_url.spec(), true));
}
// static
-GURL SuggestionsService::BuildSuggestionsBlacklistClearURL() {
+GURL SuggestionsServiceImpl::BuildSuggestionsBlacklistClearURL() {
return GURL(base::StringPrintf(kSuggestionsBlacklistClearURLFormat,
GetGoogleBaseURL().spec().c_str(),
kDeviceType));
}
-void SuggestionsService::OnStateChanged() {
+void SuggestionsServiceImpl::OnStateChanged() {
switch (GetSyncState(sync_service_)) {
case SYNC_OR_HISTORY_SYNC_DISABLED:
// Cancel any ongoing request, to stop interacting with the server.
@@ -369,7 +368,7 @@ void SuggestionsService::OnStateChanged() {
}
}
-void SuggestionsService::SetDefaultExpiryTimestamp(
+void SuggestionsServiceImpl::SetDefaultExpiryTimestamp(
SuggestionsProfile* suggestions,
int64_t default_timestamp_usec) {
for (int i = 0; i < suggestions->suggestions_size(); ++i) {
@@ -382,7 +381,7 @@ void SuggestionsService::SetDefaultExpiryTimestamp(
}
}
-void SuggestionsService::IssueRequestIfNoneOngoing(const GURL& url) {
+void SuggestionsServiceImpl::IssueRequestIfNoneOngoing(const GURL& url) {
// If there is an ongoing request, let it complete.
if (pending_request_.get()) {
return;
@@ -392,11 +391,11 @@ void SuggestionsService::IssueRequestIfNoneOngoing(const GURL& url) {
return;
}
token_fetcher_->GetAccessToken(
- base::Bind(&SuggestionsService::IssueSuggestionsRequest,
+ base::Bind(&SuggestionsServiceImpl::IssueSuggestionsRequest,
base::Unretained(this), url));
}
-void SuggestionsService::IssueSuggestionsRequest(
+void SuggestionsServiceImpl::IssueSuggestionsRequest(
const GURL& url,
const std::string& access_token) {
if (access_token.empty()) {
@@ -409,7 +408,8 @@ void SuggestionsService::IssueSuggestionsRequest(
last_request_started_time_ = TimeTicks::Now();
}
-std::unique_ptr<net::URLFetcher> SuggestionsService::CreateSuggestionsRequest(
+std::unique_ptr<net::URLFetcher>
+SuggestionsServiceImpl::CreateSuggestionsRequest(
const GURL& url,
const std::string& access_token) {
std::unique_ptr<net::URLFetcher> request =
@@ -423,8 +423,8 @@ std::unique_ptr<net::URLFetcher> SuggestionsService::CreateSuggestionsRequest(
request->SetRequestContext(url_request_context_);
// Add Chrome experiment state to the request headers.
net::HttpRequestHeaders headers;
- // Note: It's fine to pass in |is_signed_in| false, which does not affect
- // transmission of experiment ids coming from the variations server.
+ // 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(request->GetOriginalURL(), false, false,
is_signed_in, &headers);
@@ -436,7 +436,7 @@ std::unique_ptr<net::URLFetcher> SuggestionsService::CreateSuggestionsRequest(
return request;
}
-void SuggestionsService::OnURLFetchComplete(const net::URLFetcher* source) {
+void SuggestionsServiceImpl::OnURLFetchComplete(const net::URLFetcher* source) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(pending_request_.get(), source);
@@ -499,50 +499,42 @@ void SuggestionsService::OnURLFetchComplete(const net::URLFetcher* source) {
LogResponseState(RESPONSE_INVALID);
}
- callback_list_.Notify(GetSuggestionsDataFromCache());
+ callback_list_.Notify(
+ GetSuggestionsDataFromCache().value_or(SuggestionsProfile()));
UpdateBlacklistDelay(true);
ScheduleBlacklistUpload();
}
-void SuggestionsService::PopulateExtraData(SuggestionsProfile* suggestions) {
+void SuggestionsServiceImpl::PopulateExtraData(
+ SuggestionsProfile* suggestions) {
for (int i = 0; i < suggestions->suggestions_size(); ++i) {
suggestions::ChromeSuggestion* s = suggestions->mutable_suggestions(i);
if (!s->has_favicon_url() || s->favicon_url().empty()) {
s->set_favicon_url(base::StringPrintf(kFaviconURL, s->url().c_str()));
}
- if (!s->has_impression_url() || s->impression_url().empty()) {
- s->set_impression_url(
- base::StringPrintf(
- kPingURL, static_cast<long long>(suggestions->timestamp()), -1));
- }
-
- if (!s->has_click_url() || s->click_url().empty()) {
- s->set_click_url(base::StringPrintf(
- kPingURL, static_cast<long long>(suggestions->timestamp()), i));
- }
}
}
-void SuggestionsService::Shutdown() {
+void SuggestionsServiceImpl::Shutdown() {
// Cancel pending request.
pending_request_.reset(nullptr);
}
-void SuggestionsService::ScheduleBlacklistUpload() {
+void SuggestionsServiceImpl::ScheduleBlacklistUpload() {
DCHECK(thread_checker_.CalledOnValidThread());
TimeDelta time_delta;
if (blacklist_store_->GetTimeUntilReadyForUpload(&time_delta)) {
// Blacklist cache is not empty: schedule.
base::Closure blacklist_cb =
- base::Bind(&SuggestionsService::UploadOneFromBlacklist,
+ base::Bind(&SuggestionsServiceImpl::UploadOneFromBlacklist,
weak_ptr_factory_.GetWeakPtr());
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, blacklist_cb, time_delta + scheduling_delay_);
}
}
-void SuggestionsService::UploadOneFromBlacklist() {
+void SuggestionsServiceImpl::UploadOneFromBlacklist() {
DCHECK(thread_checker_.CalledOnValidThread());
GURL blacklisted_url;
@@ -558,7 +550,8 @@ void SuggestionsService::UploadOneFromBlacklist() {
ScheduleBlacklistUpload();
}
-void SuggestionsService::UpdateBlacklistDelay(bool last_request_successful) {
+void SuggestionsServiceImpl::UpdateBlacklistDelay(
+ bool last_request_successful) {
DCHECK(thread_checker_.CalledOnValidThread());
if (last_request_successful) {
diff --git a/chromium/components/suggestions/suggestions_service_impl.h b/chromium/components/suggestions/suggestions_service_impl.h
new file mode 100644
index 00000000000..fe304719c37
--- /dev/null
+++ b/chromium/components/suggestions/suggestions_service_impl.h
@@ -0,0 +1,203 @@
+// 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_SUGGESTIONS_SUGGESTIONS_SERVICE_IMPL_H_
+#define COMPONENTS_SUGGESTIONS_SUGGESTIONS_SERVICE_IMPL_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/callback_list.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/scoped_observer.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "components/suggestions/proto/suggestions.pb.h"
+#include "components/suggestions/suggestions_service.h"
+#include "components/sync/driver/sync_service_observer.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
+
+class OAuth2TokenService;
+class SigninManagerBase;
+
+namespace net {
+class URLRequestContextGetter;
+} // namespace net
+
+namespace syncer {
+class SyncService;
+} // namespace syncer
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+} // namespace user_prefs
+
+namespace suggestions {
+
+class BlacklistStore;
+class ImageManager;
+class SuggestionsStore;
+
+// Actual (non-test) implementation of the SuggestionsService interface.
+class SuggestionsServiceImpl : public SuggestionsService,
+ public net::URLFetcherDelegate,
+ public syncer::SyncServiceObserver {
+ public:
+ SuggestionsServiceImpl(const SigninManagerBase* signin_manager,
+ OAuth2TokenService* token_service,
+ syncer::SyncService* sync_service,
+ net::URLRequestContextGetter* url_request_context,
+ std::unique_ptr<SuggestionsStore> suggestions_store,
+ std::unique_ptr<ImageManager> thumbnail_manager,
+ std::unique_ptr<BlacklistStore> blacklist_store);
+ ~SuggestionsServiceImpl() override;
+
+ // SuggestionsService implementation.
+ bool FetchSuggestionsData() override;
+ base::Optional<SuggestionsProfile> GetSuggestionsDataFromCache()
+ const override;
+ std::unique_ptr<ResponseCallbackList::Subscription> AddCallback(
+ const ResponseCallback& callback) override WARN_UNUSED_RESULT;
+ void GetPageThumbnail(const GURL& url,
+ const BitmapCallback& callback) override;
+ void GetPageThumbnailWithURL(const GURL& url,
+ const GURL& thumbnail_url,
+ const BitmapCallback& callback) override;
+ bool BlacklistURL(const GURL& candidate_url) override;
+ bool UndoBlacklistURL(const GURL& url) override;
+ void ClearBlacklist() override;
+
+ // Determines which URL a blacklist request was for, irrespective of the
+ // request's status. Returns false if |request| is not a blacklist request.
+ static bool GetBlacklistedUrl(const net::URLFetcher& request, GURL* url);
+
+ // Register SuggestionsService related prefs in the Profile prefs.
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ private:
+ friend class SuggestionsServiceTest;
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, FetchSuggestionsData);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
+ FetchSuggestionsDataSyncDisabled);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
+ FetchSuggestionsDataNoAccessToken);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
+ IssueRequestIfNoneOngoingError);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest,
+ IssueRequestIfNoneOngoingResponseNotOK);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, BlacklistURL);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, BlacklistURLRequestFails);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, ClearBlacklist);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, UndoBlacklistURL);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, GetBlacklistedUrl);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, UpdateBlacklistDelay);
+ FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, CheckDefaultTimeStamps);
+
+ // Helpers to build the various suggestions URLs. These are static members
+ // rather than local functions in the .cc file to make them accessible to
+ // tests.
+ static GURL BuildSuggestionsURL();
+ static std::string BuildSuggestionsBlacklistURLPrefix();
+ static GURL BuildSuggestionsBlacklistURL(const GURL& candidate_url);
+ static GURL BuildSuggestionsBlacklistClearURL();
+
+ // syncer::SyncServiceObserver implementation.
+ void OnStateChanged() override;
+
+ // Sets default timestamp for suggestions which do not have expiry timestamp.
+ void SetDefaultExpiryTimestamp(SuggestionsProfile* suggestions,
+ int64_t timestamp_usec);
+
+ // Issues a network request if there isn't already one happening.
+ void IssueRequestIfNoneOngoing(const GURL& url);
+
+ // Issues a network request for suggestions (fetch, blacklist, or clear
+ // blacklist, depending on |url|). |access_token| is used only if OAuth2
+ // authentication is enabled.
+ void IssueSuggestionsRequest(const GURL& url,
+ const std::string& access_token);
+
+ // Creates a request to the suggestions service, properly setting headers.
+ // If OAuth2 authentication is enabled, |access_token| should be a valid
+ // OAuth2 access token, and will be written into an auth header.
+ std::unique_ptr<net::URLFetcher> CreateSuggestionsRequest(
+ const GURL& url,
+ const std::string& access_token);
+
+ // net::URLFetcherDelegate implementation.
+ // Called when fetch request completes. Parses the received suggestions data,
+ // and dispatches them to callbacks stored in queue.
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ // KeyedService implementation.
+ void Shutdown() override;
+
+ // Schedules a blacklisting request if the local blacklist isn't empty.
+ void ScheduleBlacklistUpload();
+
+ // If the local blacklist isn't empty, picks a URL from it and issues a
+ // blacklist request for it.
+ void UploadOneFromBlacklist();
+
+ // Updates |scheduling_delay_| based on the success of the last request.
+ void UpdateBlacklistDelay(bool last_request_successful);
+
+ // Adds extra data to suggestions profile.
+ void PopulateExtraData(SuggestionsProfile* suggestions);
+
+ // Test seams.
+ base::TimeDelta blacklist_delay() const { return scheduling_delay_; }
+ void set_blacklist_delay(base::TimeDelta delay) { scheduling_delay_ = delay; }
+
+ base::ThreadChecker thread_checker_;
+
+ syncer::SyncService* sync_service_;
+ ScopedObserver<syncer::SyncService, syncer::SyncServiceObserver>
+ sync_service_observer_;
+
+ net::URLRequestContextGetter* url_request_context_;
+
+ // The cache for the suggestions.
+ std::unique_ptr<SuggestionsStore> suggestions_store_;
+
+ // Used to obtain server thumbnails, if available.
+ std::unique_ptr<ImageManager> thumbnail_manager_;
+
+ // The local cache for temporary blacklist, until uploaded to the server.
+ std::unique_ptr<BlacklistStore> blacklist_store_;
+
+ // Delay used when scheduling a blacklisting task.
+ base::TimeDelta scheduling_delay_;
+
+ // Helper for fetching OAuth2 access tokens.
+ class AccessTokenFetcher;
+ std::unique_ptr<AccessTokenFetcher> token_fetcher_;
+
+ // Contains the current suggestions fetch request. Will only have a value
+ // while a request is pending, and will be reset by |OnURLFetchComplete| or
+ // if cancelled.
+ std::unique_ptr<net::URLFetcher> pending_request_;
+
+ // The start time of the previous suggestions request. This is used to measure
+ // the latency of requests. Initially zero.
+ base::TimeTicks last_request_started_time_;
+
+ ResponseCallbackList callback_list_;
+
+ // For callbacks may be run after destruction.
+ base::WeakPtrFactory<SuggestionsServiceImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SuggestionsServiceImpl);
+};
+
+} // namespace suggestions
+
+#endif // COMPONENTS_SUGGESTIONS_SUGGESTIONS_SERVICE_IMPL_H_
diff --git a/chromium/components/suggestions/suggestions_service_unittest.cc b/chromium/components/suggestions/suggestions_service_impl_unittest.cc
index 4ba50733719..538ff3d4314 100644
--- a/chromium/components/suggestions/suggestions_service_unittest.cc
+++ b/chromium/components/suggestions/suggestions_service_impl_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/suggestions/suggestions_service.h"
+#include "components/suggestions/suggestions_service_impl.h"
#include <stdint.h>
@@ -50,10 +50,6 @@ const char kTestUrl[] = "http://go.com";
const char kTestFaviconUrl[] =
"https://s2.googleusercontent.com/s2/favicons?domain_url="
"http://go.com&alt=s&sz=32";
-const char kTestImpressionUrl[] =
- "https://www.google.com/chromesuggestions/click?q=123&cd=-1";
-const char kTestClickUrl[] =
- "https://www.google.com/chromesuggestions/click?q=123&cd=0";
const char kBlacklistedUrl[] = "http://blacklist.com";
const char kBlacklistedUrlAlt[] = "http://blacklist-atl.com";
const int64_t kTestDefaultExpiry = 1402200000000000;
@@ -130,9 +126,7 @@ class MockSyncService : public syncer::FakeSyncService {
class TestSuggestionsStore : public suggestions::SuggestionsStore {
public:
- TestSuggestionsStore() {
- cached_suggestions = CreateSuggestionsProfile();
- }
+ TestSuggestionsStore() { cached_suggestions = CreateSuggestionsProfile(); }
bool LoadSuggestions(SuggestionsProfile* suggestions) override {
suggestions->CopyFrom(cached_suggestions);
return cached_suggestions.suggestions_size();
@@ -188,9 +182,6 @@ class SuggestionsServiceTest : public testing::Test {
EXPECT_EQ(kTestUrl, suggestions_profile.suggestions(0).url());
EXPECT_EQ(kTestFaviconUrl,
suggestions_profile.suggestions(0).favicon_url());
- EXPECT_EQ(kTestImpressionUrl,
- suggestions_profile.suggestions(0).impression_url());
- EXPECT_EQ(kTestClickUrl, suggestions_profile.suggestions(0).click_url());
}
int suggestions_data_callback_count_;
@@ -220,7 +211,7 @@ class SuggestionsServiceTest : public testing::Test {
new net::TestURLRequestContextGetter(io_message_loop_.task_runner());
}
- SuggestionsService* CreateSuggestionsServiceWithMocks() {
+ std::unique_ptr<SuggestionsServiceImpl> CreateSuggestionsServiceWithMocks() {
mock_sync_service_.reset(new MockSyncService);
ON_CALL(*mock_sync_service_, CanSyncStart()).WillByDefault(Return(true));
ON_CALL(*mock_sync_service_, IsSyncActive()).WillByDefault(Return(true));
@@ -235,7 +226,7 @@ class SuggestionsServiceTest : public testing::Test {
test_suggestions_store_ = new TestSuggestionsStore();
mock_thumbnail_manager_ = new StrictMock<MockImageManager>();
mock_blacklist_store_ = new StrictMock<MockBlacklistStore>();
- return new SuggestionsService(
+ return base::MakeUnique<SuggestionsServiceImpl>(
nullptr /* signin_manager */, &token_service_, mock_sync_service_.get(),
request_context_.get(), base::WrapUnique(test_suggestions_store_),
base::WrapUnique(mock_thumbnail_manager_),
@@ -254,7 +245,7 @@ class SuggestionsServiceTest : public testing::Test {
// the case where the URL is no longer in the local blacklist or the case
// in which it's not yet candidate for upload.
void UndoBlacklistURLFailsHelper(bool is_uploaded) {
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
EXPECT_TRUE(suggestions_service != nullptr);
// Ensure scheduling the request doesn't happen before undo.
@@ -297,7 +288,8 @@ class SuggestionsServiceTest : public testing::Test {
EXPECT_TRUE(undo_blacklisting_failed_);
}
- bool HasPendingSuggestionsRequest(SuggestionsService* suggestions_service) {
+ bool HasPendingSuggestionsRequest(
+ SuggestionsServiceImpl* suggestions_service) {
return !!suggestions_service->pending_request_.get();
}
@@ -326,7 +318,7 @@ TEST_F(SuggestionsServiceTest, FetchSuggestionsData) {
SuggestionsProfile suggestions_profile = CreateSuggestionsProfile();
// Set up net::FakeURLFetcherFactory.
- factory_.SetFakeResponse(SuggestionsService::BuildSuggestionsURL(),
+ factory_.SetFakeResponse(SuggestionsServiceImpl::BuildSuggestionsURL(),
suggestions_profile.SerializeAsString(),
net::HTTP_OK, net::URLRequestStatus::SUCCESS);
@@ -376,7 +368,7 @@ TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) {
}
TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncDisabled) {
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
ASSERT_TRUE(suggestions_service != nullptr);
EXPECT_CALL(*mock_sync_service_, CanSyncStart())
@@ -407,7 +399,7 @@ TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncDisabled) {
TEST_F(SuggestionsServiceTest, FetchSuggestionsDataNoAccessToken) {
token_service_.RevokeCredentials(kAccountId);
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
ASSERT_TRUE(suggestions_service != nullptr);
@@ -426,12 +418,12 @@ TEST_F(SuggestionsServiceTest, FetchSuggestionsDataNoAccessToken) {
}
TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingError) {
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
ASSERT_TRUE(suggestions_service != nullptr);
// Fake a request error.
- factory_.SetFakeResponse(SuggestionsService::BuildSuggestionsURL(),
+ factory_.SetFakeResponse(SuggestionsServiceImpl::BuildSuggestionsURL(),
"irrelevant", net::HTTP_OK,
net::URLRequestStatus::FAILED);
@@ -440,19 +432,19 @@ TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingError) {
// Send the request. Empty data will be returned to the callback.
suggestions_service->IssueRequestIfNoneOngoing(
- SuggestionsService::BuildSuggestionsURL());
+ SuggestionsServiceImpl::BuildSuggestionsURL());
// (Testing only) wait until suggestion fetch is complete.
base::RunLoop().RunUntilIdle();
}
TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingResponseNotOK) {
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
ASSERT_TRUE(suggestions_service != nullptr);
// Fake a non-200 response code.
- factory_.SetFakeResponse(SuggestionsService::BuildSuggestionsURL(),
+ factory_.SetFakeResponse(SuggestionsServiceImpl::BuildSuggestionsURL(),
"irrelevant", net::HTTP_BAD_REQUEST,
net::URLRequestStatus::SUCCESS);
@@ -462,7 +454,7 @@ TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingResponseNotOK) {
// Send the request. Empty data will be returned to the callback.
suggestions_service->IssueRequestIfNoneOngoing(
- SuggestionsService::BuildSuggestionsURL());
+ SuggestionsServiceImpl::BuildSuggestionsURL());
// (Testing only) wait until suggestion fetch is complete.
base::RunLoop().RunUntilIdle();
@@ -473,7 +465,7 @@ TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingResponseNotOK) {
}
TEST_F(SuggestionsServiceTest, BlacklistURL) {
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
EXPECT_TRUE(suggestions_service != nullptr);
base::TimeDelta no_delay = base::TimeDelta::FromSeconds(0);
@@ -484,10 +476,9 @@ TEST_F(SuggestionsServiceTest, BlacklistURL) {
GURL blacklisted_url(kBlacklistedUrl);
GURL request_url(
- SuggestionsService::BuildSuggestionsBlacklistURL(blacklisted_url));
+ SuggestionsServiceImpl::BuildSuggestionsBlacklistURL(blacklisted_url));
SuggestionsProfile suggestions_profile = CreateSuggestionsProfile();
- factory_.SetFakeResponse(request_url,
- suggestions_profile.SerializeAsString(),
+ factory_.SetFakeResponse(request_url, suggestions_profile.SerializeAsString(),
net::HTTP_OK, net::URLRequestStatus::SUCCESS);
EXPECT_CALL(*mock_thumbnail_manager_, Initialize(_)).Times(2);
@@ -537,7 +528,7 @@ TEST_F(SuggestionsServiceTest, BlacklistURLFails) {
// Initial blacklist request fails, triggering a second which succeeds.
TEST_F(SuggestionsServiceTest, BlacklistURLRequestFails) {
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
ASSERT_TRUE(suggestions_service != nullptr);
base::TimeDelta no_delay = base::TimeDelta::FromSeconds(0);
@@ -548,10 +539,10 @@ TEST_F(SuggestionsServiceTest, BlacklistURLRequestFails) {
GURL blacklisted_url(kBlacklistedUrl);
GURL request_url(
- SuggestionsService::BuildSuggestionsBlacklistURL(blacklisted_url));
+ SuggestionsServiceImpl::BuildSuggestionsBlacklistURL(blacklisted_url));
GURL blacklisted_url_alt(kBlacklistedUrlAlt);
- GURL request_url_alt(
- SuggestionsService::BuildSuggestionsBlacklistURL(blacklisted_url_alt));
+ GURL request_url_alt(SuggestionsServiceImpl::BuildSuggestionsBlacklistURL(
+ blacklisted_url_alt));
SuggestionsProfile suggestions_profile = CreateSuggestionsProfile();
// Note: we want to set the response for the blacklist URL to first
@@ -593,7 +584,7 @@ TEST_F(SuggestionsServiceTest, BlacklistURLRequestFails) {
}
TEST_F(SuggestionsServiceTest, UndoBlacklistURL) {
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
ASSERT_TRUE(suggestions_service != nullptr);
// Ensure scheduling the request doesn't happen before undo.
@@ -612,8 +603,7 @@ TEST_F(SuggestionsServiceTest, UndoBlacklistURL) {
EXPECT_CALL(*mock_thumbnail_manager_,
Initialize(EqualsProto(suggestions_profile)))
.Times(AnyNumber());
- EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_))
- .Times(AnyNumber());
+ EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)).Times(AnyNumber());
EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_))
.WillOnce(DoAll(SetArgPointee<0>(delay), Return(true)));
// Undo expectations.
@@ -621,7 +611,7 @@ TEST_F(SuggestionsServiceTest, UndoBlacklistURL) {
GetTimeUntilURLReadyForUpload(Eq(blacklisted_url), _))
.WillOnce(DoAll(SetArgPointee<1>(delay), Return(true)));
EXPECT_CALL(*mock_blacklist_store_, RemoveUrl(Eq(blacklisted_url)))
- .WillOnce(Return(true));
+ .WillOnce(Return(true));
Blacklist(suggestions_service.get(), blacklisted_url);
UndoBlacklist(suggestions_service.get(), blacklisted_url);
@@ -632,7 +622,7 @@ TEST_F(SuggestionsServiceTest, UndoBlacklistURL) {
}
TEST_F(SuggestionsServiceTest, ClearBlacklist) {
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
ASSERT_TRUE(suggestions_service != nullptr);
// Ensure scheduling the request doesn't happen before undo.
@@ -646,7 +636,7 @@ TEST_F(SuggestionsServiceTest, ClearBlacklist) {
GURL blacklisted_url(kBlacklistedUrl);
factory_.SetFakeResponse(
- SuggestionsService::BuildSuggestionsBlacklistClearURL(),
+ SuggestionsServiceImpl::BuildSuggestionsBlacklistClearURL(),
suggestions_profile.SerializeAsString(), net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
@@ -685,25 +675,27 @@ TEST_F(SuggestionsServiceTest, GetBlacklistedUrl) {
request_url.reset(new GURL("http://not-blacklisting.com/a?b=c"));
fetcher = CreateURLFetcher(*request_url, nullptr, "", net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- EXPECT_FALSE(SuggestionsService::GetBlacklistedUrl(*fetcher, &retrieved_url));
+ EXPECT_FALSE(
+ SuggestionsServiceImpl::GetBlacklistedUrl(*fetcher, &retrieved_url));
// An actual blacklist request.
std::string blacklisted_url = "http://blacklisted.com/a?b=c&d=e";
std::string encoded_blacklisted_url =
"http%3A%2F%2Fblacklisted.com%2Fa%3Fb%3Dc%26d%3De";
std::string blacklist_request_prefix(
- SuggestionsService::BuildSuggestionsBlacklistURLPrefix());
+ SuggestionsServiceImpl::BuildSuggestionsBlacklistURLPrefix());
request_url.reset(
new GURL(blacklist_request_prefix + encoded_blacklisted_url));
fetcher.reset();
fetcher = CreateURLFetcher(*request_url, nullptr, "", net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
- EXPECT_TRUE(SuggestionsService::GetBlacklistedUrl(*fetcher, &retrieved_url));
+ EXPECT_TRUE(
+ SuggestionsServiceImpl::GetBlacklistedUrl(*fetcher, &retrieved_url));
EXPECT_EQ(blacklisted_url, retrieved_url.spec());
}
TEST_F(SuggestionsServiceTest, UpdateBlacklistDelay) {
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
base::TimeDelta initial_delay = suggestions_service->blacklist_delay();
@@ -721,7 +713,7 @@ TEST_F(SuggestionsServiceTest, UpdateBlacklistDelay) {
}
TEST_F(SuggestionsServiceTest, CheckDefaultTimeStamps) {
- std::unique_ptr<SuggestionsService> suggestions_service(
+ std::unique_ptr<SuggestionsServiceImpl> suggestions_service(
CreateSuggestionsServiceWithMocks());
SuggestionsProfile suggestions =
CreateSuggestionsProfileWithExpiryTimestamps();
@@ -747,7 +739,6 @@ TEST_F(SuggestionsServiceTest, GetPageThumbnail) {
EXPECT_CALL(*mock_thumbnail_manager_, GetImageForURL(test_url, _));
suggestions_service->GetPageThumbnailWithURL(test_url, thumbnail_url,
dummy_callback);
-
}
} // namespace suggestions
diff --git a/chromium/components/suggestions/suggestions_store_unittest.cc b/chromium/components/suggestions/suggestions_store_unittest.cc
index dd95eacd0da..0a991d05b31 100644
--- a/chromium/components/suggestions/suggestions_store_unittest.cc
+++ b/chromium/components/suggestions/suggestions_store_unittest.cc
@@ -12,11 +12,11 @@
#include "base/memory/ptr_util.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
-#include "components/pref_registry/testing_pref_service_syncable.h"
#include "components/suggestions/proto/suggestions.pb.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
-using user_prefs::TestingPrefServiceSyncable;
+using sync_preferences::TestingPrefServiceSyncable;
namespace suggestions {
@@ -82,7 +82,7 @@ void ValidateSuggestions(const SuggestionsProfile& expected,
class SuggestionsStoreTest : public testing::Test {
public:
SuggestionsStoreTest()
- : pref_service_(new user_prefs::TestingPrefServiceSyncable) {}
+ : pref_service_(new sync_preferences::TestingPrefServiceSyncable) {}
void SetUp() override {
SuggestionsStore::RegisterProfilePrefs(pref_service_->registry());
@@ -95,7 +95,7 @@ class SuggestionsStoreTest : public testing::Test {
}
protected:
- std::unique_ptr<user_prefs::TestingPrefServiceSyncable> pref_service_;
+ std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_;
std::unique_ptr<SuggestionsStore> suggestions_store_;
base::Time current_time;
diff --git a/chromium/components/supervised_user_error_page/BUILD.gn b/chromium/components/supervised_user_error_page/BUILD.gn
index 0940bfbcb32..834851e00a5 100644
--- a/chromium/components/supervised_user_error_page/BUILD.gn
+++ b/chromium/components/supervised_user_error_page/BUILD.gn
@@ -2,6 +2,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+if (is_android) {
+ import("//build/config/android/rules.gni")
+}
+
static_library("supervised_user_error_page") {
sources = [
"supervised_user_error_page.cc",
@@ -40,6 +44,14 @@ if (target_os == "android") {
}
}
+if (is_android) {
+ java_cpp_enum("enums_srcjar") {
+ sources = [
+ "supervised_user_error_page.h",
+ ]
+ }
+}
+
source_set("unit_tests") {
testonly = true
sources = [
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 085581bc875..ac28f6f3453 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
@@ -61,6 +61,8 @@ int GetBlockMessageID(FilteringBehaviorReason reason,
if (single_parent)
return IDS_CHILD_BLOCK_MESSAGE_MANUAL_SINGLE_PARENT;
return IDS_CHILD_BLOCK_MESSAGE_MANUAL_MULTI_PARENT;
+ case NOT_SIGNED_IN:
+ return IDS_SUPERVISED_USER_NOT_SIGNED_IN;
}
NOTREACHED();
return 0;
@@ -96,7 +98,10 @@ std::string BuildHtml(bool allow_access_requests,
base::UTF8ToUTF16(second_custodian_email));
base::string16 block_header;
base::string16 block_message;
- if (allow_access_requests) {
+ if (reason == FilteringBehaviorReason::NOT_SIGNED_IN) {
+ block_header =
+ l10n_util::GetStringUTF16(IDS_BLOCK_INTERSTITIAL_HEADER_NOT_SIGNED_IN);
+ } else if (allow_access_requests) {
if (is_child_account) {
block_header =
l10n_util::GetStringUTF16(IDS_CHILD_BLOCK_INTERSTITIAL_HEADER);
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 01ca3e21951..66456f2b2fc 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
@@ -9,12 +9,15 @@
namespace supervised_user_error_page {
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.supervisedusererrorpage
enum FilteringBehaviorReason {
- DEFAULT,
- ASYNC_CHECKER,
- BLACKLIST,
- MANUAL,
- WHITELIST
+ DEFAULT = 0,
+ ASYNC_CHECKER = 1,
+ BLACKLIST = 2,
+ MANUAL = 3,
+ WHITELIST = 4,
+ NOT_SIGNED_IN = 5,
};
int GetBlockMessageID(
diff --git a/chromium/components/supervised_user_error_page_strings.grdp b/chromium/components/supervised_user_error_page_strings.grdp
index 4f881a81242..de4c44a167b 100644
--- a/chromium/components/supervised_user_error_page_strings.grdp
+++ b/chromium/components/supervised_user_error_page_strings.grdp
@@ -13,6 +13,9 @@
<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
</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
</message>
@@ -73,5 +76,8 @@
<message name="IDS_SUPERVISED_USER_BLOCK_MESSAGE_MANUAL" desc="Message to be shown to a supervised user when a site is blocked due to a manual blacklist entry">
Your manager can unblock it for you
</message>
+ <message name="IDS_SUPERVISED_USER_NOT_SIGNED_IN" desc="Message to be shown to a supervised user who is not signed in to Chrome">
+ Please start and sign in to Chrome so that Chrome can check whether you are allowed to access this site.
+ </message>
</grit-part>
diff --git a/chromium/components/sync/BUILD.gn b/chromium/components/sync/BUILD.gn
index 5bd464d93c8..2a552cd7662 100644
--- a/chromium/components/sync/BUILD.gn
+++ b/chromium/components/sync/BUILD.gn
@@ -99,8 +99,8 @@ static_library("sync") {
"device_info/local_device_info_provider_impl.h",
"driver/about_sync_util.cc",
"driver/about_sync_util.h",
- "driver/backend_data_type_configurer.cc",
- "driver/backend_data_type_configurer.h",
+ "driver/async_directory_type_controller.cc",
+ "driver/async_directory_type_controller.h",
"driver/backend_migrator.cc",
"driver/backend_migrator.h",
"driver/data_type_controller.cc",
@@ -122,25 +122,15 @@ static_library("sync") {
"driver/generic_change_processor.h",
"driver/generic_change_processor_factory.cc",
"driver/generic_change_processor_factory.h",
- "driver/glue/browser_thread_model_worker.cc",
- "driver/glue/browser_thread_model_worker.h",
- "driver/glue/sync_backend_host.cc",
- "driver/glue/sync_backend_host.h",
"driver/glue/sync_backend_host_core.cc",
"driver/glue/sync_backend_host_core.h",
"driver/glue/sync_backend_host_impl.cc",
"driver/glue/sync_backend_host_impl.h",
- "driver/glue/sync_backend_registrar.cc",
- "driver/glue/sync_backend_registrar.h",
- "driver/glue/ui_model_worker.cc",
- "driver/glue/ui_model_worker.h",
"driver/model_association_manager.cc",
"driver/model_association_manager.h",
"driver/model_associator.h",
"driver/model_type_controller.cc",
"driver/model_type_controller.h",
- "driver/non_ui_data_type_controller.cc",
- "driver/non_ui_data_type_controller.h",
"driver/proxy_data_type_controller.cc",
"driver/proxy_data_type_controller.h",
"driver/shared_change_processor.cc",
@@ -158,10 +148,10 @@ static_library("sync") {
"driver/sync_driver_switches.h",
"driver/sync_error_controller.cc",
"driver/sync_error_controller.h",
- "driver/sync_frontend.cc",
- "driver/sync_frontend.h",
"driver/sync_service.cc",
"driver/sync_service.h",
+ "driver/sync_service_base.cc",
+ "driver/sync_service_base.h",
"driver/sync_service_observer.cc",
"driver/sync_service_observer.h",
"driver/sync_service_utils.cc",
@@ -171,8 +161,6 @@ static_library("sync") {
"driver/sync_type_preference_provider.h",
"driver/sync_util.cc",
"driver/sync_util.h",
- "driver/ui_data_type_controller.cc",
- "driver/ui_data_type_controller.h",
"driver/user_selectable_sync_type.h",
"engine/activation_context.cc",
"engine/activation_context.h",
@@ -194,6 +182,8 @@ 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",
@@ -224,6 +214,8 @@ static_library("sync") {
"engine/events/protocol_event_observer.h",
"engine/model_safe_worker.cc",
"engine/model_safe_worker.h",
+ "engine/model_type_configurer.cc",
+ "engine/model_type_configurer.h",
"engine/model_type_connector.cc",
"engine/model_type_connector.h",
"engine/model_type_processor.cc",
@@ -245,8 +237,14 @@ static_library("sync") {
"engine/polling_constants.h",
"engine/shutdown_reason.h",
"engine/sync_auth_provider.h",
+ "engine/sync_backend_registrar.cc",
+ "engine/sync_backend_registrar.h",
"engine/sync_encryption_handler.cc",
"engine/sync_encryption_handler.h",
+ "engine/sync_engine.cc",
+ "engine/sync_engine.h",
+ "engine/sync_engine_host.cc",
+ "engine/sync_engine_host.h",
"engine/sync_manager.cc",
"engine/sync_manager.h",
"engine/sync_manager_factory.cc",
@@ -255,6 +253,8 @@ static_library("sync") {
"engine/sync_status.h",
"engine/sync_string_conversions.cc",
"engine/sync_string_conversions.h",
+ "engine/ui_model_worker.cc",
+ "engine/ui_model_worker.h",
"engine_impl/all_status.cc",
"engine_impl/all_status.h",
"engine_impl/apply_control_data_updates.cc",
@@ -336,6 +336,20 @@ static_library("sync") {
"engine_impl/js_sync_encryption_handler_observer.h",
"engine_impl/js_sync_manager_observer.cc",
"engine_impl/js_sync_manager_observer.h",
+ "engine_impl/loopback_server/loopback_connection_manager.cc",
+ "engine_impl/loopback_server/loopback_connection_manager.h",
+ "engine_impl/loopback_server/loopback_server.cc",
+ "engine_impl/loopback_server/loopback_server.h",
+ "engine_impl/loopback_server/loopback_server_entity.cc",
+ "engine_impl/loopback_server/loopback_server_entity.h",
+ "engine_impl/loopback_server/persistent_bookmark_entity.cc",
+ "engine_impl/loopback_server/persistent_bookmark_entity.h",
+ "engine_impl/loopback_server/persistent_permanent_entity.cc",
+ "engine_impl/loopback_server/persistent_permanent_entity.h",
+ "engine_impl/loopback_server/persistent_tombstone_entity.cc",
+ "engine_impl/loopback_server/persistent_tombstone_entity.h",
+ "engine_impl/loopback_server/persistent_unique_client_entity.cc",
+ "engine_impl/loopback_server/persistent_unique_client_entity.h",
"engine_impl/model_type_connector_proxy.cc",
"engine_impl/model_type_connector_proxy.h",
"engine_impl/model_type_registry.cc",
@@ -422,6 +436,8 @@ static_library("sync") {
"model/metadata_batch.cc",
"model/metadata_batch.h",
"model/metadata_change_list.h",
+ "model/model_error.cc",
+ "model/model_error.h",
"model/model_type_change_processor.cc",
"model/model_type_change_processor.h",
"model/model_type_debug_info.cc",
@@ -467,6 +483,8 @@ static_library("sync") {
"model_impl/shared_model_type_processor.h",
"protocol/proto_enum_conversions.cc",
"protocol/proto_enum_conversions.h",
+ "protocol/proto_memory_estimations.cc",
+ "protocol/proto_memory_estimations.h",
"protocol/proto_value_conversions.cc",
"protocol/proto_value_conversions.h",
"protocol/proto_visitors.h",
@@ -568,6 +586,7 @@ static_library("sync") {
"//components/os_crypt",
"//components/pref_registry",
"//components/prefs",
+ "//components/reading_list/core:reading_list_enable_flags",
"//components/signin/core/browser",
"//components/sync/engine_impl/attachments/proto",
"//components/version_info",
@@ -604,26 +623,7 @@ static_library("sync") {
]
}
- if (is_win) {
- sources += [
- "engine_impl/loopback_server/loopback_connection_manager.cc",
- "engine_impl/loopback_server/loopback_connection_manager.h",
- "engine_impl/loopback_server/loopback_server.cc",
- "engine_impl/loopback_server/loopback_server.h",
- "engine_impl/loopback_server/loopback_server_entity.cc",
- "engine_impl/loopback_server/loopback_server_entity.h",
- "engine_impl/loopback_server/persistent_bookmark_entity.cc",
- "engine_impl/loopback_server/persistent_bookmark_entity.h",
- "engine_impl/loopback_server/persistent_permanent_entity.cc",
- "engine_impl/loopback_server/persistent_permanent_entity.h",
- "engine_impl/loopback_server/persistent_tombstone_entity.cc",
- "engine_impl/loopback_server/persistent_tombstone_entity.h",
- "engine_impl/loopback_server/persistent_unique_client_entity.cc",
- "engine_impl/loopback_server/persistent_unique_client_entity.h",
- ]
- }
-
- if (enable_configuration_policy) {
+ if (!is_ios) {
sources += [
"driver/sync_policy_handler.cc",
"driver/sync_policy_handler.h",
@@ -682,6 +682,8 @@ static_library("test_support_engine") {
"engine/fake_model_type_processor.h",
"engine/fake_sync_manager.cc",
"engine/fake_sync_manager.h",
+ "engine/sync_engine_host_stub.cc",
+ "engine/sync_engine_host_stub.h",
"engine/sync_manager_factory_for_profile_sync_test.cc",
"engine/sync_manager_factory_for_profile_sync_test.h",
"engine/test_engine_components_factory.cc",
@@ -758,6 +760,8 @@ static_library("test_support_model") {
"model/mock_model_type_store.h",
"model/model_type_store_test_util.cc",
"model/model_type_store_test_util.h",
+ "model/recording_model_type_change_processor.cc",
+ "model/recording_model_type_change_processor.h",
"model/stub_model_type_sync_bridge.cc",
"model/stub_model_type_sync_bridge.h",
"model/sync_change_processor_wrapper_for_test.cc",
@@ -782,6 +786,8 @@ static_library("test_support_driver") {
sources = [
"device_info/local_device_info_provider_mock.cc",
"device_info/local_device_info_provider_mock.h",
+ "driver/async_directory_type_controller_mock.cc",
+ "driver/async_directory_type_controller_mock.h",
"driver/data_type_controller_mock.cc",
"driver/data_type_controller_mock.h",
"driver/data_type_manager_mock.cc",
@@ -796,14 +802,12 @@ static_library("test_support_driver") {
"driver/fake_sync_service.h",
"driver/frontend_data_type_controller_mock.cc",
"driver/frontend_data_type_controller_mock.h",
- "driver/glue/sync_backend_host_mock.cc",
- "driver/glue/sync_backend_host_mock.h",
"driver/model_associator_mock.cc",
"driver/model_associator_mock.h",
- "driver/non_ui_data_type_controller_mock.cc",
- "driver/non_ui_data_type_controller_mock.h",
"driver/sync_api_component_factory_mock.cc",
"driver/sync_api_component_factory_mock.h",
+ "engine/fake_sync_engine.cc",
+ "engine/fake_sync_engine.h",
"model/change_processor_mock.cc",
"model/change_processor_mock.h",
]
@@ -850,31 +854,30 @@ source_set("unit_tests") {
"device_info/device_info_util_unittest.cc",
"device_info/local_device_info_provider_impl_unittest.cc",
"driver/about_sync_util_unittest.cc",
+ "driver/async_directory_type_controller_unittest.cc",
"driver/backend_migrator_unittest.cc",
"driver/data_type_manager_impl_unittest.cc",
"driver/frontend_data_type_controller_unittest.cc",
"driver/generic_change_processor_unittest.cc",
- "driver/glue/browser_thread_model_worker_unittest.cc",
"driver/glue/sync_backend_host_impl_unittest.cc",
- "driver/glue/sync_backend_registrar_unittest.cc",
- "driver/glue/ui_model_worker_unittest.cc",
"driver/model_association_manager_unittest.cc",
"driver/model_type_controller_unittest.cc",
- "driver/non_ui_data_type_controller_unittest.cc",
"driver/shared_change_processor_unittest.cc",
"driver/startup_controller_unittest.cc",
"driver/sync_stopped_reporter_unittest.cc",
"driver/sync_util_unittest.cc",
- "driver/ui_data_type_controller_unittest.cc",
"engine/attachments/attachment_store_frontend_unittest.cc",
"engine/attachments/attachment_store_test_template.h",
"engine/attachments/fake_attachment_downloader_unittest.cc",
"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/sync_backend_registrar_unittest.cc",
+ "engine/ui_model_worker_unittest.cc",
"engine_impl/apply_control_data_updates_unittest.cc",
"engine_impl/attachments/attachment_downloader_impl_unittest.cc",
"engine_impl/attachments/attachment_uploader_impl_unittest.cc",
@@ -957,7 +960,6 @@ source_set("unit_tests") {
"//base/test:test_support",
"//components/invalidation/impl",
"//components/os_crypt:test_support",
- "//components/pref_registry:test_support",
"//components/prefs:test_support",
"//components/signin/core/browser:test_support",
"//components/sync/engine_impl/attachments/proto",
@@ -991,7 +993,7 @@ source_set("unit_tests") {
sources += [ "engine_impl/loopback_server/loopback_server_unittest.cc" ]
}
- if (enable_configuration_policy) {
+ if (!is_ios) {
sources += [ "driver/sync_policy_handler_unittest.cc" ]
deps += [
"//components/policy:generated",
diff --git a/chromium/components/sync/android/BUILD.gn b/chromium/components/sync/android/BUILD.gn
index f386884a2d9..5896fb4228f 100644
--- a/chromium/components/sync/android/BUILD.gn
+++ b/chromium/components/sync/android/BUILD.gn
@@ -42,6 +42,7 @@ android_library("sync_javatests") {
"//components/signin/core/browser/android:java",
"//components/signin/core/browser/android:signin_java_test_support",
"//components/sync:sync_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
"//third_party/cacheinvalidation:cacheinvalidation_javalib",
"//third_party/cacheinvalidation:cacheinvalidation_proto_java",
"//third_party/jsr-305:jsr_305_javalib",
diff --git a/chromium/components/sync_bookmarks/bookmark_change_processor.h b/chromium/components/sync_bookmarks/bookmark_change_processor.h
index cdefdc7f70e..54270710397 100644
--- a/chromium/components/sync_bookmarks/bookmark_change_processor.h
+++ b/chromium/components/sync_bookmarks/bookmark_change_processor.h
@@ -20,8 +20,6 @@
#include "components/sync/model/data_type_error_handler.h"
#include "components/sync_bookmarks/bookmark_model_associator.h"
-class Profile;
-
namespace base {
class RefCountedMemory;
} // namespace base
diff --git a/chromium/components/sync_preferences/BUILD.gn b/chromium/components/sync_preferences/BUILD.gn
index f9982556afd..d04fbd82744 100644
--- a/chromium/components/sync_preferences/BUILD.gn
+++ b/chromium/components/sync_preferences/BUILD.gn
@@ -26,10 +26,7 @@ static_library("sync_preferences") {
"//components/sync",
]
- if (enable_configuration_policy) {
- # This define is only used for compiling the .cc files in this target.
- defines = [ "SYNC_PREFERENCES_USE_POLICY" ]
-
+ if (!is_ios) {
deps += [ "//components/policy/core/browser" ]
}
}
diff --git a/chromium/components/sync_preferences/pref_model_associator.cc b/chromium/components/sync_preferences/pref_model_associator.cc
index a203da372ca..7f893cf9432 100644
--- a/chromium/components/sync_preferences/pref_model_associator.cc
+++ b/chromium/components/sync_preferences/pref_model_associator.cc
@@ -101,7 +101,7 @@ void PrefModelAssociator::InitPrefAndAssociate(
// Update the local preference based on what we got from the
// sync server. Note: this only updates the user value store, which is
// ignored if the preference is policy controlled.
- if (new_value->IsType(base::Value::TYPE_NULL)) {
+ if (new_value->IsType(base::Value::Type::NONE)) {
LOG(WARNING) << "Sync has null value for pref " << pref_name.c_str();
pref_service_->ClearPref(pref_name.c_str());
} else if (!new_value->IsType(user_pref_value->GetType())) {
@@ -124,7 +124,7 @@ void PrefModelAssociator::InitPrefAndAssociate(
sync_changes->push_back(syncer::SyncChange(
FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data));
}
- } else if (!sync_value->IsType(base::Value::TYPE_NULL)) {
+ } else if (!sync_value->IsType(base::Value::Type::NONE)) {
// Only a server value exists. Just set the local user value.
pref_service_->Set(pref_name.c_str(), *sync_value);
} else {
@@ -251,7 +251,7 @@ bool PrefModelAssociator::CreatePrefSyncData(
const std::string& name,
const base::Value& value,
syncer::SyncData* sync_data) const {
- if (value.IsType(base::Value::TYPE_NULL)) {
+ if (value.IsType(base::Value::Type::NONE)) {
LOG(ERROR) << "Attempting to sync a null pref value for " << name;
return false;
}
@@ -277,13 +277,13 @@ bool PrefModelAssociator::CreatePrefSyncData(
base::Value* PrefModelAssociator::MergeListValues(const base::Value& from_value,
const base::Value& to_value) {
- if (from_value.GetType() == base::Value::TYPE_NULL)
+ if (from_value.GetType() == base::Value::Type::NONE)
return to_value.DeepCopy();
- if (to_value.GetType() == base::Value::TYPE_NULL)
+ if (to_value.GetType() == base::Value::Type::NONE)
return from_value.DeepCopy();
- DCHECK(from_value.GetType() == base::Value::TYPE_LIST);
- DCHECK(to_value.GetType() == base::Value::TYPE_LIST);
+ DCHECK(from_value.GetType() == base::Value::Type::LIST);
+ DCHECK(to_value.GetType() == base::Value::Type::LIST);
const base::ListValue& from_list_value =
static_cast<const base::ListValue&>(from_value);
const base::ListValue& to_list_value =
@@ -299,13 +299,13 @@ base::Value* PrefModelAssociator::MergeListValues(const base::Value& from_value,
base::Value* PrefModelAssociator::MergeDictionaryValues(
const base::Value& from_value,
const base::Value& to_value) {
- if (from_value.GetType() == base::Value::TYPE_NULL)
+ if (from_value.GetType() == base::Value::Type::NONE)
return to_value.DeepCopy();
- if (to_value.GetType() == base::Value::TYPE_NULL)
+ if (to_value.GetType() == base::Value::Type::NONE)
return from_value.DeepCopy();
- DCHECK_EQ(from_value.GetType(), base::Value::TYPE_DICTIONARY);
- DCHECK_EQ(to_value.GetType(), base::Value::TYPE_DICTIONARY);
+ DCHECK_EQ(from_value.GetType(), base::Value::Type::DICTIONARY);
+ DCHECK_EQ(to_value.GetType(), base::Value::Type::DICTIONARY);
const base::DictionaryValue& from_dict_value =
static_cast<const base::DictionaryValue&>(from_value);
const base::DictionaryValue& to_dict_value =
@@ -317,8 +317,8 @@ base::Value* PrefModelAssociator::MergeDictionaryValues(
const base::Value* from_key_value = &it.value();
base::Value* to_key_value;
if (result->GetWithoutPathExpansion(it.key(), &to_key_value)) {
- if (from_key_value->GetType() == base::Value::TYPE_DICTIONARY &&
- to_key_value->GetType() == base::Value::TYPE_DICTIONARY) {
+ if (from_key_value->GetType() == base::Value::Type::DICTIONARY &&
+ to_key_value->GetType() == base::Value::Type::DICTIONARY) {
base::Value* merged_value =
MergeDictionaryValues(*from_key_value, *to_key_value);
result->SetWithoutPathExpansion(it.key(), merged_value);
diff --git a/chromium/components/sync_preferences/pref_model_associator.h b/chromium/components/sync_preferences/pref_model_associator.h
index 9dd29fa8c2d..033b19c8dfc 100644
--- a/chromium/components/sync_preferences/pref_model_associator.h
+++ b/chromium/components/sync_preferences/pref_model_associator.h
@@ -32,7 +32,6 @@ class PreferenceSpecifics;
namespace sync_preferences {
class PrefModelAssociatorClient;
-class PrefRegistrySyncable;
class PrefServiceSyncable;
// Contains all preference sync related logic.
diff --git a/chromium/components/sync_preferences/pref_model_associator_unittest.cc b/chromium/components/sync_preferences/pref_model_associator_unittest.cc
index dbfd06e7c5a..22577e0b5af 100644
--- a/chromium/components/sync_preferences/pref_model_associator_unittest.cc
+++ b/chromium/components/sync_preferences/pref_model_associator_unittest.cc
@@ -81,9 +81,9 @@ class AbstractPreferenceMergeTest : public testing::Test {
pref_service_->FindPreference(pref_name.c_str());
ASSERT_TRUE(pref);
base::Value::Type type = pref->GetType();
- if (type == base::Value::TYPE_DICTIONARY)
+ if (type == base::Value::Type::DICTIONARY)
empty_value.reset(new base::DictionaryValue);
- else if (type == base::Value::TYPE_LIST)
+ else if (type == base::Value::Type::LIST)
empty_value.reset(new base::ListValue);
else
FAIL();
diff --git a/chromium/components/sync_preferences/pref_service_syncable_factory.cc b/chromium/components/sync_preferences/pref_service_syncable_factory.cc
index be3377e0694..2d314f51952 100644
--- a/chromium/components/sync_preferences/pref_service_syncable_factory.cc
+++ b/chromium/components/sync_preferences/pref_service_syncable_factory.cc
@@ -5,13 +5,14 @@
#include "components/sync_preferences/pref_service_syncable_factory.h"
#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/default_pref_store.h"
#include "components/prefs/pref_notifier_impl.h"
#include "components/prefs/pref_value_store.h"
#include "components/sync_preferences/pref_service_syncable.h"
-#if defined(SYNC_PREFERENCES_USE_POLICY)
+#if !defined(OS_IOS)
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/browser/configuration_policy_pref_store.h"
#include "components/policy/core/common/policy_service.h" // nogncheck
@@ -27,7 +28,7 @@ PrefServiceSyncableFactory::~PrefServiceSyncableFactory() {}
void PrefServiceSyncableFactory::SetManagedPolicies(
policy::PolicyService* service,
policy::BrowserPolicyConnector* connector) {
-#if defined(SYNC_PREFERENCES_USE_POLICY)
+#if !defined(OS_IOS)
set_managed_prefs(new policy::ConfigurationPolicyPrefStore(
service, connector->GetHandlerList(), policy::POLICY_LEVEL_MANDATORY));
#else
@@ -38,7 +39,7 @@ void PrefServiceSyncableFactory::SetManagedPolicies(
void PrefServiceSyncableFactory::SetRecommendedPolicies(
policy::PolicyService* service,
policy::BrowserPolicyConnector* connector) {
-#if defined(SYNC_PREFERENCES_USE_POLICY)
+#if !defined(OS_IOS)
set_recommended_prefs(new policy::ConfigurationPolicyPrefStore(
service, connector->GetHandlerList(), policy::POLICY_LEVEL_RECOMMENDED));
#else
diff --git a/chromium/components/sync_preferences/pref_service_syncable_factory.h b/chromium/components/sync_preferences/pref_service_syncable_factory.h
index fb23dccde47..9a8994b6c6a 100644
--- a/chromium/components/sync_preferences/pref_service_syncable_factory.h
+++ b/chromium/components/sync_preferences/pref_service_syncable_factory.h
@@ -10,10 +10,6 @@
#include "base/macros.h"
#include "components/prefs/pref_service_factory.h"
-namespace base {
-class CommandLine;
-}
-
namespace policy {
class BrowserPolicyConnector;
class PolicyService;
diff --git a/chromium/components/sync_preferences/testing_pref_service_syncable.cc b/chromium/components/sync_preferences/testing_pref_service_syncable.cc
index 2fc6d3e1676..e1598e9c408 100644
--- a/chromium/components/sync_preferences/testing_pref_service_syncable.cc
+++ b/chromium/components/sync_preferences/testing_pref_service_syncable.cc
@@ -14,6 +14,7 @@ template <>
TestingPrefServiceBase<sync_preferences::PrefServiceSyncable,
user_prefs::PrefRegistrySyncable>::
TestingPrefServiceBase(TestingPrefStore* managed_prefs,
+ TestingPrefStore* extension_prefs,
TestingPrefStore* user_prefs,
TestingPrefStore* recommended_prefs,
user_prefs::PrefRegistrySyncable* pref_registry,
@@ -21,9 +22,9 @@ TestingPrefServiceBase<sync_preferences::PrefServiceSyncable,
: sync_preferences::PrefServiceSyncable(
pref_notifier,
new PrefValueStore(managed_prefs,
- nullptr, // supervised_user_prefs
- nullptr, // extension_prefs
- nullptr, // command_line_prefs
+ nullptr, // supervised_user_prefs
+ extension_prefs, // extension_prefs
+ nullptr, // command_line_prefs
user_prefs,
recommended_prefs,
pref_registry->defaults().get(),
@@ -36,6 +37,7 @@ TestingPrefServiceBase<sync_preferences::PrefServiceSyncable,
user_prefs::PrefRegistrySyncable>::HandleReadError),
false),
managed_prefs_(managed_prefs),
+ extension_prefs_(extension_prefs),
user_prefs_(user_prefs),
recommended_prefs_(recommended_prefs) {}
@@ -47,11 +49,13 @@ TestingPrefServiceSyncable::TestingPrefServiceSyncable()
new TestingPrefStore(),
new TestingPrefStore(),
new TestingPrefStore(),
+ new TestingPrefStore(),
new user_prefs::PrefRegistrySyncable(),
new PrefNotifierImpl()) {}
TestingPrefServiceSyncable::TestingPrefServiceSyncable(
TestingPrefStore* managed_prefs,
+ TestingPrefStore* extension_prefs,
TestingPrefStore* user_prefs,
TestingPrefStore* recommended_prefs,
user_prefs::PrefRegistrySyncable* pref_registry,
@@ -59,6 +63,7 @@ TestingPrefServiceSyncable::TestingPrefServiceSyncable(
: TestingPrefServiceBase<PrefServiceSyncable,
user_prefs::PrefRegistrySyncable>(
managed_prefs,
+ extension_prefs,
user_prefs,
recommended_prefs,
pref_registry,
diff --git a/chromium/components/sync_preferences/testing_pref_service_syncable.h b/chromium/components/sync_preferences/testing_pref_service_syncable.h
index ceb373a26f8..cf83573fcdf 100644
--- a/chromium/components/sync_preferences/testing_pref_service_syncable.h
+++ b/chromium/components/sync_preferences/testing_pref_service_syncable.h
@@ -15,8 +15,6 @@ class PrefRegistrySyncable;
namespace sync_preferences {
-class PrefModelAssociatorClient;
-
// Test version of PrefServiceSyncable.
class TestingPrefServiceSyncable
: public TestingPrefServiceBase<PrefServiceSyncable,
@@ -24,6 +22,7 @@ class TestingPrefServiceSyncable
public:
TestingPrefServiceSyncable();
TestingPrefServiceSyncable(TestingPrefStore* managed_prefs,
+ TestingPrefStore* extension_prefs,
TestingPrefStore* user_prefs,
TestingPrefStore* recommended_prefs,
user_prefs::PrefRegistrySyncable* pref_registry,
@@ -47,6 +46,7 @@ template <>
TestingPrefServiceBase<sync_preferences::PrefServiceSyncable,
user_prefs::PrefRegistrySyncable>::
TestingPrefServiceBase(TestingPrefStore* managed_prefs,
+ TestingPrefStore* extension_prefs,
TestingPrefStore* user_prefs,
TestingPrefStore* recommended_prefs,
user_prefs::PrefRegistrySyncable* pref_registry,
diff --git a/chromium/components/sync_sessions/revisit/bookmarks_page_revisit_observer.h b/chromium/components/sync_sessions/revisit/bookmarks_page_revisit_observer.h
index 5ae4bba00d8..e0161495e13 100644
--- a/chromium/components/sync_sessions/revisit/bookmarks_page_revisit_observer.h
+++ b/chromium/components/sync_sessions/revisit/bookmarks_page_revisit_observer.h
@@ -14,7 +14,6 @@
class GURL;
namespace bookmarks {
-class BookmarkModel;
class BookmarkNode;
} // namespace bookmarks
diff --git a/chromium/components/sync_sessions/revisit/sessions_page_revisit_observer.h b/chromium/components/sync_sessions/revisit/sessions_page_revisit_observer.h
index a865ef49926..87d653e2d0c 100644
--- a/chromium/components/sync_sessions/revisit/sessions_page_revisit_observer.h
+++ b/chromium/components/sync_sessions/revisit/sessions_page_revisit_observer.h
@@ -14,14 +14,8 @@
class GURL;
-namespace sessions {
-struct SessionTab;
-} // namespace sessions
-
namespace sync_sessions {
-class CurrentTabMatcher;
-class OffsetTabMatcher;
struct SyncedSession;
// A simple interface to abstract away who is providing sessions.
diff --git a/chromium/components/sync_sessions/session_data_type_controller.cc b/chromium/components/sync_sessions/session_data_type_controller.cc
index ec144668b07..07705c3c1a2 100644
--- a/chromium/components/sync_sessions/session_data_type_controller.cc
+++ b/chromium/components/sync_sessions/session_data_type_controller.cc
@@ -6,6 +6,7 @@
#include <set>
+#include "base/threading/thread_task_runner_handle.h"
#include "components/prefs/pref_service.h"
#include "components/sync/driver/sync_client.h"
#include "components/sync_sessions/sync_sessions_client.h"
@@ -19,7 +20,11 @@ SessionDataTypeController::SessionDataTypeController(
syncer::SyncClient* sync_client,
syncer::LocalDeviceInfoProvider* local_device,
const char* history_disabled_pref_name)
- : UIDataTypeController(syncer::SESSIONS, dump_stack, sync_client),
+ : AsyncDirectoryTypeController(syncer::SESSIONS,
+ dump_stack,
+ sync_client,
+ syncer::GROUP_UI,
+ base::ThreadTaskRunnerHandle::Get()),
sync_client_(sync_client),
local_device_(local_device),
history_disabled_pref_name_(history_disabled_pref_name),
@@ -81,7 +86,7 @@ bool SessionDataTypeController::IsWaiting() {
}
void SessionDataTypeController::MaybeCompleteLoading() {
- if (state_ == MODEL_STARTING && !IsWaiting()) {
+ if (state() == MODEL_STARTING && !IsWaiting()) {
OnModelLoaded();
}
}
diff --git a/chromium/components/sync_sessions/session_data_type_controller.h b/chromium/components/sync_sessions/session_data_type_controller.h
index 44222a2564b..db217f94863 100644
--- a/chromium/components/sync_sessions/session_data_type_controller.h
+++ b/chromium/components/sync_sessions/session_data_type_controller.h
@@ -10,14 +10,14 @@
#include "base/macros.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/sync/device_info/local_device_info_provider.h"
-#include "components/sync/driver/ui_data_type_controller.h"
+#include "components/sync/driver/async_directory_type_controller.h"
namespace sync_sessions {
// Overrides StartModels to avoid sync contention with sessions during
// a session restore operation at startup and to wait for the local
// device info to become available.
-class SessionDataTypeController : public syncer::UIDataTypeController {
+class SessionDataTypeController : public syncer::AsyncDirectoryTypeController {
public:
// |dump_stack| is called when an unrecoverable error occurs.
SessionDataTypeController(const base::Closure& dump_stack,
@@ -26,7 +26,7 @@ class SessionDataTypeController : public syncer::UIDataTypeController {
const char* history_disabled_pref_name);
~SessionDataTypeController() override;
- // UIDataTypeController interface.
+ // AsyncDirectoryTypeController implementation.
bool StartModels() override;
void StopModels() override;
bool ReadyForStart() const override;
diff --git a/chromium/components/sync_sessions/sessions_sync_manager.cc b/chromium/components/sync_sessions/sessions_sync_manager.cc
index acb6882b7bc..04e01b70a32 100644
--- a/chromium/components/sync_sessions/sessions_sync_manager.cc
+++ b/chromium/components/sync_sessions/sessions_sync_manager.cc
@@ -92,6 +92,7 @@ SessionsSyncManager::SessionsSyncManager(
local_tab_pool_out_of_sync_(true),
sync_prefs_(sync_prefs),
local_device_(local_device),
+ 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)),
@@ -121,6 +122,19 @@ syncer::SyncMergeResult SessionsSyncManager::MergeDataAndStartSyncing(
error_handler_ = std::move(error_handler);
sync_processor_ = std::move(sync_processor);
+ // SessionDataTypeController ensures that the local device info
+ // is available before activating this datatype.
+ DCHECK(local_device_);
+ const DeviceInfo* local_device_info = local_device_->GetLocalDeviceInfo();
+ if (!local_device_info) {
+ merge_result.set_error(error_handler_->CreateAndUploadError(
+ FROM_HERE, "Failed to get local device info."));
+ return merge_result;
+ }
+
+ current_session_name_ = local_device_info->client_name();
+ current_device_type_ = local_device_info->device_type();
+
// It's possible(via RebuildAssociations) for lost_navigations_recorder_ to
// persist between sync being stopped and started. If it did persist, it's
// already associated with |sync_processor|, so leave it alone.
@@ -136,19 +150,7 @@ syncer::SyncMergeResult SessionsSyncManager::MergeDataAndStartSyncing(
// a conveniently safe time to assert sync is ready and the cache_guid is
// initialized.
if (current_machine_tag_.empty()) {
- InitializeCurrentMachineTag();
- }
-
- // SessionDataTypeController ensures that the local device info
- // is available before activating this datatype.
- DCHECK(local_device_);
- const DeviceInfo* local_device_info = local_device_->GetLocalDeviceInfo();
- if (local_device_info) {
- current_session_name_ = local_device_info->client_name();
- } else {
- merge_result.set_error(error_handler_->CreateAndUploadError(
- FROM_HERE, "Failed to get local device info."));
- return merge_result;
+ InitializeCurrentMachineTag(local_device_->GetLocalSyncCacheGUID());
}
session_tracker_.SetLocalSessionTag(current_machine_tag_);
@@ -164,7 +166,7 @@ syncer::SyncMergeResult SessionsSyncManager::MergeDataAndStartSyncing(
base_specifics->set_session_tag(current_machine_tag());
sync_pb::SessionHeader* header_s = base_specifics->mutable_header();
header_s->set_client_name(current_session_name_);
- header_s->set_device_type(local_device_info->device_type());
+ header_s->set_device_type(current_device_type_);
syncer::SyncData data = syncer::SyncData::CreateLocalData(
current_machine_tag(), current_session_name_, specifics);
new_changes.push_back(
@@ -200,11 +202,7 @@ void SessionsSyncManager::AssociateWindows(
SyncedSession* current_session = session_tracker_.GetSession(local_tag);
current_session->modified_time = base::Time::Now();
header_s->set_client_name(current_session_name_);
- // SessionDataTypeController ensures that the local device info
- // is available before activating this datatype.
- DCHECK(local_device_);
- const DeviceInfo* local_device_info = local_device_->GetLocalDeviceInfo();
- header_s->set_device_type(local_device_info->device_type());
+ header_s->set_device_type(current_device_type_);
session_tracker_.ResetSessionTracking(local_tag);
std::set<const SyncedWindowDelegate*> windows =
@@ -389,7 +387,7 @@ void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab,
base::Time::Now();
}
-void SessionsSyncManager::RebuildAssociations() {
+bool SessionsSyncManager::RebuildAssociations() {
syncer::SyncDataList data(sync_processor_->GetAllSyncData(syncer::SESSIONS));
std::unique_ptr<syncer::SyncErrorFactory> error_handler(
std::move(error_handler_));
@@ -397,8 +395,9 @@ void SessionsSyncManager::RebuildAssociations() {
std::move(sync_processor_));
StopSyncing(syncer::SESSIONS);
- MergeDataAndStartSyncing(syncer::SESSIONS, data, std::move(processor),
- std::move(error_handler));
+ syncer::SyncMergeResult merge_result = MergeDataAndStartSyncing(
+ syncer::SESSIONS, data, std::move(processor), std::move(error_handler));
+ return !merge_result.error().IsSet();
}
bool SessionsSyncManager::IsValidSessionHeader(
@@ -434,8 +433,8 @@ void SessionsSyncManager::OnLocalTabModified(SyncedTabDelegate* modified_tab) {
if (local_tab_pool_out_of_sync_) {
// If our tab pool is corrupt, pay the price of a full re-association to
// fix things up. This takes care of the new tab modification as well.
- RebuildAssociations();
- DCHECK(!local_tab_pool_out_of_sync_);
+ bool rebuild_association_succeeded = RebuildAssociations();
+ DCHECK(!rebuild_association_succeeded || !local_tab_pool_out_of_sync_);
return;
}
@@ -767,7 +766,8 @@ void SessionsSyncManager::UpdateTrackerWithForeignSession(
}
}
-void SessionsSyncManager::InitializeCurrentMachineTag() {
+void SessionsSyncManager::InitializeCurrentMachineTag(
+ const std::string& cache_guid) {
DCHECK(current_machine_tag_.empty());
std::string persisted_guid;
persisted_guid = sync_prefs_->GetSyncSessionsGUID();
@@ -775,8 +775,6 @@ void SessionsSyncManager::InitializeCurrentMachineTag() {
current_machine_tag_ = persisted_guid;
DVLOG(1) << "Restoring persisted session sync guid: " << persisted_guid;
} else {
- DCHECK(local_device_);
- std::string cache_guid = local_device_->GetLocalSyncCacheGUID();
DCHECK(!cache_guid.empty());
current_machine_tag_ = BuildMachineTag(cache_guid);
DVLOG(1) << "Creating session sync guid: " << current_machine_tag_;
diff --git a/chromium/components/sync_sessions/sessions_sync_manager.h b/chromium/components/sync_sessions/sessions_sync_manager.h
index b78a1b401b2..66ec40e916f 100644
--- a/chromium/components/sync_sessions/sessions_sync_manager.h
+++ b/chromium/components/sync_sessions/sessions_sync_manager.h
@@ -43,7 +43,6 @@ class SessionHeader;
class SessionSpecifics;
class SessionTab;
class SessionWindow;
-class TabNavigation;
} // namespace sync_pb
namespace extensions {
@@ -181,7 +180,7 @@ class SessionsSyncManager : public syncer::SyncableService,
ProcessRemoteDeleteOfLocalSession);
FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, SetVariationIds);
- void InitializeCurrentMachineTag();
+ void InitializeCurrentMachineTag(const std::string& cache_guid);
// Load and add window or tab data for a foreign session to our internal
// tracking.
@@ -300,9 +299,10 @@ class SessionsSyncManager : public syncer::SyncableService,
const syncer::SyncDataList& restored_tabs,
syncer::SyncChangeList* change_output);
- // Stops and re-starts syncing to rebuild association mappings.
+ // Stops and re-starts syncing to rebuild association mappings. Returns true
+ // when re-starting succeeds.
// See |local_tab_pool_out_of_sync_|.
- void RebuildAssociations();
+ bool RebuildAssociations();
// Validates the content of a SessionHeader protobuf.
// Returns false if validation fails.
@@ -352,8 +352,9 @@ class SessionsSyncManager : public syncer::SyncableService,
// Unique client tag.
std::string current_machine_tag_;
- // User-visible machine name.
+ // User-visible machine name and device type to populate header.
std::string current_session_name_;
+ sync_pb::SyncEnums::DeviceType current_device_type_;
// SyncID for the sync node containing all the window information for this
// client.
diff --git a/chromium/components/sync_sessions/synced_tab_delegate.h b/chromium/components/sync_sessions/synced_tab_delegate.h
index c78c7e42627..aa81540baeb 100644
--- a/chromium/components/sync_sessions/synced_tab_delegate.h
+++ b/chromium/components/sync_sessions/synced_tab_delegate.h
@@ -13,8 +13,6 @@
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
-class Profile;
-
namespace sync_sessions {
class SyncSessionsClient;
}
diff --git a/chromium/components/sync_ui_strings.grdp b/chromium/components/sync_ui_strings.grdp
index 72c0e6827ed..e3f3d49a6f3 100644
--- a/chromium/components/sync_ui_strings.grdp
+++ b/chromium/components/sync_ui_strings.grdp
@@ -25,6 +25,9 @@
<message name="IDS_SYNC_DATATYPE_TYPED_URLS" desc="History, one of the data types that we allow syncing.">
History
</message>
+ <message name="IDS_SYNC_DATATYPE_READING_LIST" desc="Reading List, one of the data types that we allow syncing">
+ Reading List
+ </message>
<message name="IDS_SYNC_EMPTY_PASSPHRASE_ERROR" desc="Error message when the passphrase is empty.">
Empty passphrase is not allowed.
</message>
diff --git a/chromium/components/task_scheduler_util/browser/BUILD.gn b/chromium/components/task_scheduler_util/browser/BUILD.gn
new file mode 100644
index 00000000000..502d832e378
--- /dev/null
+++ b/chromium/components/task_scheduler_util/browser/BUILD.gn
@@ -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.
+
+static_library("browser") {
+ sources = [
+ "initialization.cc",
+ "initialization.h",
+ ]
+
+ deps = [
+ "//base",
+ "//base:base_static",
+ "//components/task_scheduler_util/common",
+ "//components/variations",
+ ]
+}
diff --git a/chromium/components/task_scheduler_util/browser/DEPS b/chromium/components/task_scheduler_util/browser/DEPS
new file mode 100644
index 00000000000..80f6f908e13
--- /dev/null
+++ b/chromium/components/task_scheduler_util/browser/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/variations",
+]
diff --git a/chromium/components/task_scheduler_util/browser/initialization.cc b/chromium/components/task_scheduler_util/browser/initialization.cc
new file mode 100644
index 00000000000..00bab9cabf8
--- /dev/null
+++ b/chromium/components/task_scheduler_util/browser/initialization.cc
@@ -0,0 +1,89 @@
+// 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/task_scheduler_util/browser/initialization.h"
+
+#include <map>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/task_scheduler/scheduler_worker_params.h"
+#include "base/task_scheduler/switches.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "components/task_scheduler_util/common/variations_util.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace task_scheduler_util {
+
+namespace {
+
+constexpr char kFieldTrialName[] = "BrowserScheduler";
+
+enum WorkerPoolType : size_t {
+ BACKGROUND = 0,
+ BACKGROUND_FILE_IO,
+ FOREGROUND,
+ FOREGROUND_FILE_IO,
+ WORKER_POOL_COUNT // Always last.
+};
+
+} // namespace
+
+std::vector<base::SchedulerWorkerPoolParams>
+GetBrowserWorkerPoolParamsFromVariations() {
+ using ThreadPriority = base::ThreadPriority;
+
+ std::map<std::string, std::string> variation_params;
+ if (!::variations::GetVariationParams(kFieldTrialName, &variation_params))
+ return std::vector<base::SchedulerWorkerPoolParams>();
+
+ std::vector<SchedulerImmutableWorkerPoolParams> immutable_worker_pool_params;
+ DCHECK_EQ(BACKGROUND, immutable_worker_pool_params.size());
+ immutable_worker_pool_params.emplace_back("Background",
+ ThreadPriority::BACKGROUND);
+ DCHECK_EQ(BACKGROUND_FILE_IO, immutable_worker_pool_params.size());
+ immutable_worker_pool_params.emplace_back("BackgroundFileIO",
+ ThreadPriority::BACKGROUND);
+ DCHECK_EQ(FOREGROUND, immutable_worker_pool_params.size());
+ immutable_worker_pool_params.emplace_back("Foreground",
+ ThreadPriority::NORMAL);
+ // Tasks posted to SequencedWorkerPool or BrowserThreadImpl may be redirected
+ // to this pool. Since COM STA is initialized in these environments, it must
+ // also be initialized in this pool.
+ DCHECK_EQ(FOREGROUND_FILE_IO, immutable_worker_pool_params.size());
+ immutable_worker_pool_params.emplace_back(
+ "ForegroundFileIO", ThreadPriority::NORMAL,
+ base::SchedulerBackwardCompatibility::INIT_COM_STA);
+
+ return GetWorkerPoolParams(immutable_worker_pool_params, variation_params);
+}
+
+size_t BrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits) {
+ const bool is_background =
+ traits.priority() == base::TaskPriority::BACKGROUND;
+ if (traits.may_block() || traits.with_base_sync_primitives())
+ return is_background ? BACKGROUND_FILE_IO : FOREGROUND_FILE_IO;
+ return is_background ? BACKGROUND : FOREGROUND;
+}
+
+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
new file mode 100644
index 00000000000..d4584eeca4a
--- /dev/null
+++ b/chromium/components/task_scheduler_util/browser/initialization.h
@@ -0,0 +1,35 @@
+// 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_TASK_SCHEDULER_UTIL_BROWSER_INITIALIZATION_H_
+#define COMPONENTS_TASK_SCHEDULER_UTIL_BROWSER_INITIALIZATION_H_
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+
+namespace base {
+class TaskTraits;
+}
+
+namespace task_scheduler_util {
+
+// Gets a vector of SchedulerWorkerPoolParams to initialize TaskScheduler in the
+// browser based off variations. Returns an empty vector on failure.
+std::vector<base::SchedulerWorkerPoolParams>
+GetBrowserWorkerPoolParamsFromVariations();
+
+// Maps |traits| to the index of a browser worker pool vector provided by
+// GetBrowserWorkerPoolParamsFromVariations().
+size_t BrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits);
+
+// 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/task_scheduler_util/common/BUILD.gn b/chromium/components/task_scheduler_util/common/BUILD.gn
new file mode 100644
index 00000000000..94bbb32322b
--- /dev/null
+++ b/chromium/components/task_scheduler_util/common/BUILD.gn
@@ -0,0 +1,28 @@
+# 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("common") {
+ sources = [
+ "variations_util.cc",
+ "variations_util.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/variations",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "variations_util_unittest.cc",
+ ]
+ deps = [
+ ":common",
+ "//base",
+ "//components/variations",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/task_scheduler_util/common/DEPS b/chromium/components/task_scheduler_util/common/DEPS
new file mode 100644
index 00000000000..80f6f908e13
--- /dev/null
+++ b/chromium/components/task_scheduler_util/common/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/variations",
+]
diff --git a/chromium/components/task_scheduler_util/common/variations_util.cc b/chromium/components/task_scheduler_util/common/variations_util.cc
new file mode 100644
index 00000000000..bdca8c8f961
--- /dev/null
+++ b/chromium/components/task_scheduler_util/common/variations_util.cc
@@ -0,0 +1,180 @@
+// 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/task_scheduler_util/common/variations_util.h"
+
+#include "base/command_line.h"
+#include "base/logging.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 "base/task_scheduler/initialization_util.h"
+#include "base/time/time.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace task_scheduler_util {
+
+namespace {
+
+struct SchedulerCustomizableWorkerPoolParams {
+ base::SchedulerWorkerPoolParams::StandbyThreadPolicy standby_thread_policy;
+ int max_threads = 0;
+ base::TimeDelta detach_period;
+};
+
+#if !defined(OS_IOS)
+constexpr char kTaskSchedulerVariationParamsSwitch[] =
+ "task-scheduler-variation-params";
+
+constexpr char kSeparator[] = "|";
+
+bool ContainsSeparator(const std::string& str) {
+ return str.find(kSeparator) != std::string::npos;
+}
+#endif // !defined(OS_IOS)
+
+// Converts |pool_descriptor| to a SchedulerWorkerPoolVariableParams. Returns a
+// default SchedulerWorkerPoolVariableParams on failure.
+//
+// |pool_descriptor| is a semi-colon separated value string with the following
+// items:
+// 0. Minimum Thread Count (int)
+// 1. Maximum Thread Count (int)
+// 2. Thread Count Multiplier (double)
+// 3. Thread Count Offset (int)
+// 4. Detach Time in Milliseconds (int)
+// 5. Standby Thread Policy (string)
+// Additional values may appear as necessary and will be ignored.
+SchedulerCustomizableWorkerPoolParams StringToVariableWorkerPoolParams(
+ const base::StringPiece pool_descriptor) {
+ using StandbyThreadPolicy =
+ base::SchedulerWorkerPoolParams::StandbyThreadPolicy;
+ const std::vector<base::StringPiece> tokens = SplitStringPiece(
+ pool_descriptor, ";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ // Normally, we wouldn't initialize the values below because we don't read
+ // from them before we write to them. However, some compilers (like MSVC)
+ // complain about uninitialized variables due to the as_string() call below.
+ int min = 0;
+ int max = 0;
+ double cores_multiplier = 0.0;
+ int offset = 0;
+ int detach_milliseconds = 0;
+ // Checking for a size greater than the expected amount allows us to be
+ // forward compatible if we add more variation values.
+ if (tokens.size() >= 5 && base::StringToInt(tokens[0], &min) &&
+ base::StringToInt(tokens[1], &max) &&
+ base::StringToDouble(tokens[2].as_string(), &cores_multiplier) &&
+ base::StringToInt(tokens[3], &offset) &&
+ base::StringToInt(tokens[4], &detach_milliseconds)) {
+ SchedulerCustomizableWorkerPoolParams params;
+ params.max_threads = base::RecommendedMaxNumberOfThreadsInPool(
+ min, max, cores_multiplier, offset);
+ params.detach_period =
+ base::TimeDelta::FromMilliseconds(detach_milliseconds);
+ params.standby_thread_policy = (tokens.size() >= 6 && tokens[5] == "lazy")
+ ? StandbyThreadPolicy::LAZY
+ : StandbyThreadPolicy::ONE;
+ return params;
+ }
+ DLOG(ERROR) << "Invalid Worker Pool Descriptor: " << pool_descriptor;
+ return SchedulerCustomizableWorkerPoolParams();
+}
+
+} // namespace
+
+SchedulerImmutableWorkerPoolParams::SchedulerImmutableWorkerPoolParams(
+ const char* name,
+ base::ThreadPriority priority_hint,
+ base::SchedulerBackwardCompatibility backward_compatibility)
+ : name_(name),
+ priority_hint_(priority_hint),
+ backward_compatibility_(backward_compatibility) {}
+
+std::vector<base::SchedulerWorkerPoolParams> GetWorkerPoolParams(
+ const std::vector<SchedulerImmutableWorkerPoolParams>&
+ constant_worker_pool_params_vector,
+ const std::map<std::string, std::string>& variation_params) {
+ std::vector<base::SchedulerWorkerPoolParams> worker_pool_params_vector;
+ for (const auto& constant_worker_pool_params :
+ constant_worker_pool_params_vector) {
+ const char* const worker_pool_name = constant_worker_pool_params.name();
+ auto it = variation_params.find(worker_pool_name);
+ if (it == variation_params.end()) {
+ // Non-branded builds don't have access to external worker pool
+ // configurations.
+ return std::vector<base::SchedulerWorkerPoolParams>();
+ }
+ const auto variable_worker_pool_params =
+ StringToVariableWorkerPoolParams(it->second);
+ if (variable_worker_pool_params.max_threads <= 0 ||
+ variable_worker_pool_params.detach_period <= base::TimeDelta()) {
+ DLOG(ERROR) << "Invalid Worker Pool Configuration: " << worker_pool_name
+ << " [" << it->second << "]";
+ return std::vector<base::SchedulerWorkerPoolParams>();
+ }
+ worker_pool_params_vector.emplace_back(
+ worker_pool_name, constant_worker_pool_params.priority_hint(),
+ variable_worker_pool_params.standby_thread_policy,
+ variable_worker_pool_params.max_threads,
+ variable_worker_pool_params.detach_period,
+ constant_worker_pool_params.backward_compatibility());
+ }
+ return worker_pool_params_vector;
+}
+
+#if !defined(OS_IOS)
+void AddVariationParamsToCommandLine(base::StringPiece key_prefix,
+ base::CommandLine* command_line) {
+ DCHECK(command_line);
+
+ std::map<std::string, std::string> variation_params;
+ if (!variations::GetVariationParams("BrowserScheduler", &variation_params))
+ return;
+
+ std::vector<std::string> parts;
+ for (const auto& key_value : variation_params) {
+ if (base::StartsWith(key_value.first, key_prefix,
+ base::CompareCase::SENSITIVE)) {
+ if (ContainsSeparator(key_value.first) ||
+ ContainsSeparator(key_value.second)) {
+ DLOG(ERROR)
+ << "Unexpected Character in Task Scheduler Variation Params: "
+ << key_value.first << " [" << key_value.second << "]";
+ return;
+ }
+ parts.push_back(key_value.first);
+ parts.push_back(key_value.second);
+ }
+ }
+
+ if (!parts.empty()) {
+ command_line->AppendSwitchASCII(kTaskSchedulerVariationParamsSwitch,
+ base::JoinString(parts, kSeparator));
+ }
+}
+
+std::map<std::string, std::string> GetVariationParamsFromCommandLine(
+ const base::CommandLine& command_line) {
+ const auto serialized_variation_params =
+ command_line.GetSwitchValueASCII(kTaskSchedulerVariationParamsSwitch);
+ const auto parts =
+ base::SplitStringPiece(serialized_variation_params, kSeparator,
+ base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ std::map<std::string, std::string> variation_params;
+ for (auto it = parts.begin(); it != parts.end(); ++it) {
+ base::StringPiece key = *it;
+ ++it;
+ if (it == parts.end()) {
+ NOTREACHED();
+ return std::map<std::string, std::string>();
+ }
+ base::StringPiece value = *it;
+ variation_params[key.as_string()] = value.as_string();
+ }
+ return variation_params;
+}
+#endif // !defined(OS_IOS)
+
+} // namespace task_scheduler_util
diff --git a/chromium/components/task_scheduler_util/common/variations_util.h b/chromium/components/task_scheduler_util/common/variations_util.h
new file mode 100644
index 00000000000..f92b2977008
--- /dev/null
+++ b/chromium/components/task_scheduler_util/common/variations_util.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.
+
+#ifndef COMPONENTS_TASK_SCHEDULER_UTIL_COMMON_VARIATIONS_UTIL_H_
+#define COMPONENTS_TASK_SCHEDULER_UTIL_COMMON_VARIATIONS_UTIL_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "build/build_config.h"
+#include "base/strings/string_piece.h"
+#include "base/task_scheduler/scheduler_worker_params.h"
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+class CommandLine;
+}
+
+namespace task_scheduler_util {
+
+class SchedulerImmutableWorkerPoolParams {
+ public:
+ SchedulerImmutableWorkerPoolParams(
+ const char* name,
+ base::ThreadPriority priority_hint,
+ base::SchedulerBackwardCompatibility backward_compatibility =
+ base::SchedulerBackwardCompatibility::DISABLED);
+
+ const char* name() const { return name_; }
+ base::ThreadPriority priority_hint() const { return priority_hint_; }
+ base::SchedulerBackwardCompatibility backward_compatibility() const {
+ return backward_compatibility_;
+ }
+
+ private:
+ const char* name_;
+ base::ThreadPriority priority_hint_;
+ base::SchedulerBackwardCompatibility backward_compatibility_;
+};
+
+// Returns a SchedulerWorkerPoolParams vector to initialize pools specified in
+// |constant_worker_pool_params_vector|. SchedulerWorkerPoolParams members
+// without a counterpart in SchedulerImmutableWorkerPoolParams are initialized
+// based of |variation_params|. Returns an empty vector on failure.
+std::vector<base::SchedulerWorkerPoolParams> GetWorkerPoolParams(
+ const std::vector<SchedulerImmutableWorkerPoolParams>&
+ constant_worker_pool_params_vector,
+ const std::map<std::string, std::string>& variation_params);
+
+#if !defined(OS_IOS)
+// Serializes variation params from the BrowserScheduler field trial whose key
+// start with |prefix| to the --task-scheduler-variation-params switch of
+// |command_line|.
+void AddVariationParamsToCommandLine(base::StringPiece key_prefix,
+ base::CommandLine* command_line);
+
+// Returns a map of key-value pairs deserialized from the
+// --task-scheduler-variation-params switch of |command_line|.
+std::map<std::string, std::string> GetVariationParamsFromCommandLine(
+ const base::CommandLine& command_line);
+#endif // !defined(OS_IOS)
+
+} // namespace task_scheduler_util
+
+#endif // COMPONENTS_TASK_SCHEDULER_UTIL_COMMON_VARIATIONS_UTIL_H_
diff --git a/chromium/components/task_scheduler_util/common/variations_util_unittest.cc b/chromium/components/task_scheduler_util/common/variations_util_unittest.cc
new file mode 100644
index 00000000000..5618106542a
--- /dev/null
+++ b/chromium/components/task_scheduler_util/common/variations_util_unittest.cc
@@ -0,0 +1,266 @@
+// 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/task_scheduler_util/common/variations_util.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "build/build_config.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/metrics/field_trial.h"
+#include "base/task_scheduler/scheduler_worker_params.h"
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+#include "base/threading/platform_thread.h"
+#include "components/variations/variations_associated_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace task_scheduler_util {
+
+namespace {
+
+using StandbyThreadPolicy =
+ base::SchedulerWorkerPoolParams::StandbyThreadPolicy;
+using ThreadPriority = base::ThreadPriority;
+
+#if !defined(OS_IOS)
+constexpr char kFieldTrialName[] = "BrowserScheduler";
+constexpr char kFieldTrialTestGroup[] = "Test";
+constexpr char kTaskSchedulerVariationParamsSwitch[] =
+ "task-scheduler-variation-params";
+#endif // !defined(OS_IOS)
+
+std::vector<SchedulerImmutableWorkerPoolParams> GetImmutableWorkerPoolParams() {
+ std::vector<SchedulerImmutableWorkerPoolParams> constant_worker_pool_params;
+ constant_worker_pool_params.emplace_back("Background",
+ ThreadPriority::BACKGROUND);
+ constant_worker_pool_params.emplace_back("BackgroundFileIO",
+ ThreadPriority::BACKGROUND);
+ constant_worker_pool_params.emplace_back("Foreground",
+ ThreadPriority::NORMAL);
+ constant_worker_pool_params.emplace_back(
+ "ForegroundFileIO", ThreadPriority::NORMAL,
+ base::SchedulerBackwardCompatibility::INIT_COM_STA);
+ return constant_worker_pool_params;
+}
+
+class TaskSchedulerUtilVariationsUtilTest : public testing::Test {
+ public:
+ TaskSchedulerUtilVariationsUtilTest() : field_trial_list_(nullptr) {}
+ ~TaskSchedulerUtilVariationsUtilTest() override {
+ // Ensure that the maps are cleared between tests, since they are stored as
+ // process singletons.
+ variations::testing::ClearAllVariationIDs();
+ variations::testing::ClearAllVariationParams();
+ }
+
+ private:
+ base::FieldTrialList field_trial_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskSchedulerUtilVariationsUtilTest);
+};
+
+} // namespace
+
+TEST_F(TaskSchedulerUtilVariationsUtilTest, OrderingParams5) {
+ std::map<std::string, std::string> variation_params;
+ variation_params["Background"] = "1;1;1;0;42";
+ variation_params["BackgroundFileIO"] = "2;2;1;0;52";
+ variation_params["Foreground"] = "4;4;1;0;62";
+ variation_params["ForegroundFileIO"] = "8;8;1;0;72";
+
+ auto params_vector =
+ GetWorkerPoolParams(GetImmutableWorkerPoolParams(), variation_params);
+ ASSERT_EQ(4U, params_vector.size());
+
+ EXPECT_EQ("Background", params_vector[0].name());
+ EXPECT_EQ(ThreadPriority::BACKGROUND, params_vector[0].priority_hint());
+ EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[0].standby_thread_policy());
+ EXPECT_EQ(1U, params_vector[0].max_threads());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(42),
+ params_vector[0].suggested_reclaim_time());
+ EXPECT_EQ(base::SchedulerBackwardCompatibility::DISABLED,
+ params_vector[0].backward_compatibility());
+
+ EXPECT_EQ("BackgroundFileIO", params_vector[1].name());
+ EXPECT_EQ(ThreadPriority::BACKGROUND, params_vector[1].priority_hint());
+ EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[1].standby_thread_policy());
+ EXPECT_EQ(2U, params_vector[1].max_threads());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(52),
+ params_vector[1].suggested_reclaim_time());
+ EXPECT_EQ(base::SchedulerBackwardCompatibility::DISABLED,
+ params_vector[1].backward_compatibility());
+
+ EXPECT_EQ("Foreground", params_vector[2].name());
+ EXPECT_EQ(ThreadPriority::NORMAL, params_vector[2].priority_hint());
+ EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[2].standby_thread_policy());
+ EXPECT_EQ(4U, params_vector[2].max_threads());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(62),
+ params_vector[2].suggested_reclaim_time());
+ EXPECT_EQ(base::SchedulerBackwardCompatibility::DISABLED,
+ params_vector[2].backward_compatibility());
+
+ EXPECT_EQ("ForegroundFileIO", params_vector[3].name());
+ EXPECT_EQ(ThreadPriority::NORMAL, params_vector[3].priority_hint());
+ EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[3].standby_thread_policy());
+ EXPECT_EQ(8U, params_vector[3].max_threads());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(72),
+ params_vector[3].suggested_reclaim_time());
+ EXPECT_EQ(base::SchedulerBackwardCompatibility::INIT_COM_STA,
+ params_vector[3].backward_compatibility());
+}
+
+TEST_F(TaskSchedulerUtilVariationsUtilTest, OrderingParams6) {
+ std::map<std::string, std::string> variation_params;
+ variation_params["Background"] = "1;1;1;0;42;lazy";
+ variation_params["BackgroundFileIO"] = "2;2;1;0;52;one";
+ variation_params["Foreground"] = "4;4;1;0;62;lazy";
+ variation_params["ForegroundFileIO"] = "8;8;1;0;72;one";
+
+ auto params_vector =
+ GetWorkerPoolParams(GetImmutableWorkerPoolParams(), variation_params);
+ ASSERT_EQ(4U, params_vector.size());
+
+ EXPECT_EQ("Background", params_vector[0].name());
+ EXPECT_EQ(ThreadPriority::BACKGROUND, params_vector[0].priority_hint());
+ EXPECT_EQ(StandbyThreadPolicy::LAZY,
+ params_vector[0].standby_thread_policy());
+ EXPECT_EQ(1U, params_vector[0].max_threads());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(42),
+ params_vector[0].suggested_reclaim_time());
+ EXPECT_EQ(base::SchedulerBackwardCompatibility::DISABLED,
+ params_vector[0].backward_compatibility());
+
+ EXPECT_EQ("BackgroundFileIO", params_vector[1].name());
+ EXPECT_EQ(ThreadPriority::BACKGROUND, params_vector[1].priority_hint());
+ EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[1].standby_thread_policy());
+ EXPECT_EQ(2U, params_vector[1].max_threads());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(52),
+ params_vector[1].suggested_reclaim_time());
+ EXPECT_EQ(base::SchedulerBackwardCompatibility::DISABLED,
+ params_vector[1].backward_compatibility());
+
+ EXPECT_EQ("Foreground", params_vector[2].name());
+ EXPECT_EQ(ThreadPriority::NORMAL, params_vector[2].priority_hint());
+ EXPECT_EQ(StandbyThreadPolicy::LAZY,
+ params_vector[2].standby_thread_policy());
+ EXPECT_EQ(4U, params_vector[2].max_threads());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(62),
+ params_vector[2].suggested_reclaim_time());
+ EXPECT_EQ(base::SchedulerBackwardCompatibility::DISABLED,
+ params_vector[2].backward_compatibility());
+
+ EXPECT_EQ("ForegroundFileIO", params_vector[3].name());
+ EXPECT_EQ(ThreadPriority::NORMAL, params_vector[3].priority_hint());
+ EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[3].standby_thread_policy());
+ EXPECT_EQ(8U, params_vector[3].max_threads());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(72),
+ params_vector[3].suggested_reclaim_time());
+ EXPECT_EQ(base::SchedulerBackwardCompatibility::INIT_COM_STA,
+ params_vector[3].backward_compatibility());
+}
+
+TEST_F(TaskSchedulerUtilVariationsUtilTest, NoData) {
+ EXPECT_TRUE(GetWorkerPoolParams(GetImmutableWorkerPoolParams(),
+ std::map<std::string, std::string>())
+ .empty());
+}
+
+TEST_F(TaskSchedulerUtilVariationsUtilTest, IncompleteParameters) {
+ std::map<std::string, std::string> variation_params;
+ variation_params["Background"] = "1;1;1;0";
+ variation_params["BackgroundFileIO"] = "2;2;1;0";
+ variation_params["Foreground"] = "4;4;1;0";
+ variation_params["ForegroundFileIO"] = "8;8;1;0";
+ EXPECT_TRUE(
+ GetWorkerPoolParams(GetImmutableWorkerPoolParams(), variation_params)
+ .empty());
+}
+
+TEST_F(TaskSchedulerUtilVariationsUtilTest, InvalidParameters) {
+ std::map<std::string, std::string> variation_params;
+ variation_params["Background"] = "a;b;c;d;e";
+ variation_params["BackgroundFileIO"] = "a;b;c;d;e";
+ variation_params["Foreground"] = "a;b;c;d;e";
+ variation_params["ForegroundFileIO"] = "a;b;c;d;e";
+ EXPECT_TRUE(
+ GetWorkerPoolParams(GetImmutableWorkerPoolParams(), variation_params)
+ .empty());
+}
+
+#if !defined(OS_IOS)
+// Verify that AddVariationParamsToCommandLine() serializes BrowserScheduler
+// variation params that start with the specified prefix to the command line and
+// that GetVariationParamsFromCommandLine() correctly deserializes them.
+TEST_F(TaskSchedulerUtilVariationsUtilTest, CommandLine) {
+ std::map<std::string, std::string> in_variation_params;
+ in_variation_params["PrefixFoo"] = "Foo";
+ in_variation_params["PrefixBar"] = "Bar";
+ in_variation_params["NoPrefix"] = "NoPrefix";
+ ASSERT_TRUE(variations::AssociateVariationParams(
+ kFieldTrialName, kFieldTrialTestGroup, in_variation_params));
+ base::FieldTrialList::CreateFieldTrial(kFieldTrialName, kFieldTrialTestGroup);
+
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ AddVariationParamsToCommandLine("Prefix", &command_line);
+ const std::map<std::string, std::string> out_variation_params =
+ GetVariationParamsFromCommandLine(command_line);
+
+ std::map<std::string, std::string> expected_out_variation_params;
+ expected_out_variation_params["PrefixFoo"] = "Foo";
+ expected_out_variation_params["PrefixBar"] = "Bar";
+ EXPECT_EQ(expected_out_variation_params, out_variation_params);
+}
+
+// Verify that AddVariationParamsToCommandLine() doesn't add anything to the
+// command line when a BrowserScheduler variation param key contains |. A key
+// that contains | wouldn't be deserialized correctly by
+// GetVariationParamsFromCommandLine().
+TEST_F(TaskSchedulerUtilVariationsUtilTest,
+ CommandLineSeparatorInVariationParamsKey) {
+ std::map<std::string, std::string> in_variation_params;
+ in_variation_params["PrefixFoo"] = "Foo";
+ in_variation_params["PrefixKey|"] = "Value";
+ ASSERT_TRUE(variations::AssociateVariationParams(
+ kFieldTrialName, kFieldTrialTestGroup, in_variation_params));
+ base::FieldTrialList::CreateFieldTrial(kFieldTrialName, kFieldTrialTestGroup);
+
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ AddVariationParamsToCommandLine("Prefix", &command_line);
+ EXPECT_TRUE(
+ command_line.GetSwitchValueASCII(kTaskSchedulerVariationParamsSwitch)
+ .empty());
+}
+
+// Verify that AddVariationParamsToCommandLine() doesn't add anything to the
+// command line when a BrowserScheduler variation param value contains |. A
+// value that contains | wouldn't be deserialized correctly by
+// GetVariationParamsFromCommandLine().
+TEST_F(TaskSchedulerUtilVariationsUtilTest,
+ CommandLineSeparatorInVariationParamsValue) {
+ std::map<std::string, std::string> in_variation_params;
+ in_variation_params["PrefixFoo"] = "Foo";
+ in_variation_params["PrefixKey"] = "Value|";
+ ASSERT_TRUE(variations::AssociateVariationParams(
+ kFieldTrialName, kFieldTrialTestGroup, in_variation_params));
+ base::FieldTrialList::CreateFieldTrial(kFieldTrialName, kFieldTrialTestGroup);
+
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ AddVariationParamsToCommandLine("Prefix", &command_line);
+ EXPECT_TRUE(
+ command_line.GetSwitchValueASCII(kTaskSchedulerVariationParamsSwitch)
+ .empty());
+}
+
+// Verify that GetVariationParamsFromCommandLine() returns an empty map when the
+// command line doesn't have a --task-scheduler-variation-params switch.
+TEST_F(TaskSchedulerUtilVariationsUtilTest, CommandLineNoSwitch) {
+ base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+ EXPECT_TRUE(GetVariationParamsFromCommandLine(command_line).empty());
+}
+#endif // !defined(OS_IOS)
+
+} // namespace task_scheduler_util
diff --git a/chromium/components/task_scheduler_util/initialization_util.cc b/chromium/components/task_scheduler_util/initialization_util.cc
deleted file mode 100644
index 748eae3a21b..00000000000
--- a/chromium/components/task_scheduler_util/initialization_util.cc
+++ /dev/null
@@ -1,153 +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/task_scheduler_util/initialization_util.h"
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
-#include "base/task_scheduler/initialization_util.h"
-#include "base/task_scheduler/scheduler_worker_pool_params.h"
-#include "base/task_scheduler/task_scheduler.h"
-#include "base/task_scheduler/task_traits.h"
-#include "base/time/time.h"
-
-namespace task_scheduler_util {
-
-namespace {
-
-using StandbyThreadPolicy =
- base::SchedulerWorkerPoolParams::StandbyThreadPolicy;
-
-enum WorkerPoolType : size_t {
- BACKGROUND_WORKER_POOL = 0,
- BACKGROUND_FILE_IO_WORKER_POOL,
- FOREGROUND_WORKER_POOL,
- FOREGROUND_FILE_IO_WORKER_POOL,
- WORKER_POOL_COUNT // Always last.
-};
-
-struct WorkerPoolVariationValues {
- StandbyThreadPolicy standby_thread_policy;
- int threads = 0;
- base::TimeDelta detach_period;
-};
-
-// Converts |pool_descriptor| to a WorkerPoolVariationValues. Returns a default
-// WorkerPoolVariationValues on failure.
-//
-// |pool_descriptor| is a semi-colon separated value string with the following
-// items:
-// 0. Minimum Thread Count (int)
-// 1. Maximum Thread Count (int)
-// 2. Thread Count Multiplier (double)
-// 3. Thread Count Offset (int)
-// 4. Detach Time in Milliseconds (milliseconds)
-// 5. Standby Thread Policy (string)
-// Additional values may appear as necessary and will be ignored.
-WorkerPoolVariationValues StringToWorkerPoolVariationValues(
- const base::StringPiece pool_descriptor) {
- const std::vector<std::string> tokens =
- SplitString(pool_descriptor, ";",
- base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- int min;
- int max;
- double cores_multiplier;
- int offset;
- int detach_milliseconds;
- // Checking for a size greater than the expected amount allows us to be
- // forward compatible if we add more variation values.
- if (tokens.size() >= 5 && base::StringToInt(tokens[0], &min) &&
- base::StringToInt(tokens[1], &max) &&
- base::StringToDouble(tokens[2], &cores_multiplier) &&
- base::StringToInt(tokens[3], &offset) &&
- base::StringToInt(tokens[4], &detach_milliseconds)) {
- WorkerPoolVariationValues values;
- values.threads = base::RecommendedMaxNumberOfThreadsInPool(
- min, max, cores_multiplier, offset);
- values.detach_period =
- base::TimeDelta::FromMilliseconds(detach_milliseconds);
- values.standby_thread_policy =
- (tokens.size() >= 6 && tokens[5] == "lazy")
- ? StandbyThreadPolicy::LAZY
- : StandbyThreadPolicy::ONE;
- return values;
- }
- DLOG(ERROR) << "Invalid Worker Pool Descriptor: " << pool_descriptor;
- return WorkerPoolVariationValues();
-}
-
-// Returns the worker pool index for |traits| defaulting to
-// FOREGROUND_WORKER_POOL or FOREGROUND_FILE_IO_WORKER_POOL on unknown
-// priorities.
-size_t WorkerPoolIndexForTraits(const base::TaskTraits& traits) {
- const bool is_background =
- traits.priority() == base::TaskPriority::BACKGROUND;
- if (traits.with_file_io()) {
- return is_background ? BACKGROUND_FILE_IO_WORKER_POOL
- : FOREGROUND_FILE_IO_WORKER_POOL;
- }
- return is_background ? BACKGROUND_WORKER_POOL : FOREGROUND_WORKER_POOL;
-}
-
-} // namespace
-
-bool InitializeDefaultTaskScheduler(
- const std::map<std::string, std::string>& variation_params) {
- using ThreadPriority = base::ThreadPriority;
- using IORestriction = base::SchedulerWorkerPoolParams::IORestriction;
- struct SchedulerWorkerPoolPredefinedParams {
- const char* name;
- ThreadPriority priority_hint;
- IORestriction io_restriction;
- };
- static const SchedulerWorkerPoolPredefinedParams kAllPredefinedParams[] = {
- {"Background", ThreadPriority::BACKGROUND, IORestriction::DISALLOWED},
- {"BackgroundFileIO", ThreadPriority::BACKGROUND, IORestriction::ALLOWED},
- {"Foreground", ThreadPriority::NORMAL, IORestriction::DISALLOWED},
- {"ForegroundFileIO", ThreadPriority::NORMAL, IORestriction::ALLOWED},
- };
- static_assert(arraysize(kAllPredefinedParams) == WORKER_POOL_COUNT,
- "Mismatched Worker Pool Types and Predefined Parameters");
-
- std::vector<base::SchedulerWorkerPoolParams> params_vector;
- for (const auto& predefined_params : kAllPredefinedParams) {
- const auto pair = variation_params.find(predefined_params.name);
- if (pair == variation_params.end()) {
- DLOG(ERROR) << "Missing Worker Pool Configuration: "
- << predefined_params.name;
- return false;
- }
-
- const WorkerPoolVariationValues variation_values =
- StringToWorkerPoolVariationValues(pair->second);
-
- if (variation_values.threads <= 0 ||
- variation_values.detach_period.is_zero()) {
- DLOG(ERROR) << "Invalid Worker Pool Configuration: " <<
- predefined_params.name << " [" << pair->second << "]";
- return false;
- }
-
- params_vector.emplace_back(predefined_params.name,
- predefined_params.priority_hint,
- predefined_params.io_restriction,
- variation_values.standby_thread_policy,
- variation_values.threads,
- variation_values.detach_period);
- }
-
- DCHECK_EQ(WORKER_POOL_COUNT, params_vector.size());
-
- base::TaskScheduler::CreateAndSetDefaultTaskScheduler(
- params_vector, base::Bind(WorkerPoolIndexForTraits));
-
- return true;
-}
-
-} // namespace task_scheduler_util
diff --git a/chromium/components/task_scheduler_util/initialization_util.h b/chromium/components/task_scheduler_util/initialization_util.h
deleted file mode 100644
index cb501751b3e..00000000000
--- a/chromium/components/task_scheduler_util/initialization_util.h
+++ /dev/null
@@ -1,22 +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_TASK_SCHEDULER_UTIL_INITIALIZATION_UTIL_H_
-#define COMPONENTS_TASK_SCHEDULER_UTIL_INITIALIZATION_UTIL_H_
-
-#include <map>
-#include <string>
-
-namespace task_scheduler_util {
-
-// Calls base::TaskScheduler::CreateAndSetDefaultTaskScheduler with arguments
-// derived from |variation_params|. Returns false on failure. |variation_params|
-// is expected to come from the variations component and map a thread pool name
-// to thread pool parameters.
-bool InitializeDefaultTaskScheduler(
- const std::map<std::string, std::string>& variation_params);
-
-} // namespace task_scheduler_util
-
-#endif // BASE_TASK_SCHEDULER_INITIALIZATION_UTIL_H_
diff --git a/chromium/components/task_scheduler_util/renderer/BUILD.gn b/chromium/components/task_scheduler_util/renderer/BUILD.gn
new file mode 100644
index 00000000000..473fcd24bce
--- /dev/null
+++ b/chromium/components/task_scheduler_util/renderer/BUILD.gn
@@ -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.
+
+if (!is_ios) {
+ static_library("renderer") {
+ sources = [
+ "initialization.cc",
+ "initialization.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/task_scheduler_util/common",
+ ]
+ }
+}
diff --git a/chromium/components/task_scheduler_util/renderer/initialization.cc b/chromium/components/task_scheduler_util/renderer/initialization.cc
new file mode 100644
index 00000000000..0bef44603c6
--- /dev/null
+++ b/chromium/components/task_scheduler_util/renderer/initialization.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 "components/task_scheduler_util/renderer/initialization.h"
+
+#include <map>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/threading/platform_thread.h"
+#include "components/task_scheduler_util/common/variations_util.h"
+
+namespace task_scheduler_util {
+
+namespace {
+
+enum WorkerPoolType : size_t {
+ BACKGROUND = 0,
+ BACKGROUND_BLOCKING,
+ FOREGROUND,
+ FOREGROUND_BLOCKING,
+ WORKER_POOL_COUNT // Always last.
+};
+
+} // namespace
+
+std::vector<base::SchedulerWorkerPoolParams> GetRendererWorkerPoolParams() {
+ using ThreadPriority = base::ThreadPriority;
+ std::vector<SchedulerImmutableWorkerPoolParams> immutable_worker_pool_params;
+ DCHECK_EQ(BACKGROUND, immutable_worker_pool_params.size());
+ immutable_worker_pool_params.emplace_back("RendererBackground",
+ ThreadPriority::BACKGROUND);
+ DCHECK_EQ(BACKGROUND_BLOCKING, immutable_worker_pool_params.size());
+ immutable_worker_pool_params.emplace_back("RendererBackgroundBlocking",
+ ThreadPriority::BACKGROUND);
+ DCHECK_EQ(FOREGROUND, immutable_worker_pool_params.size());
+ immutable_worker_pool_params.emplace_back("RendererForeground",
+ ThreadPriority::NORMAL);
+ DCHECK_EQ(FOREGROUND_BLOCKING, immutable_worker_pool_params.size());
+ immutable_worker_pool_params.emplace_back("RendererForegroundBlocking",
+ ThreadPriority::NORMAL);
+ return GetWorkerPoolParams(immutable_worker_pool_params,
+ GetVariationParamsFromCommandLine(
+ *base::CommandLine::ForCurrentProcess()));
+}
+
+size_t RendererWorkerPoolIndexForTraits(const base::TaskTraits& traits) {
+ const bool is_background =
+ traits.priority() == base::TaskPriority::BACKGROUND;
+ if (traits.may_block() || traits.with_base_sync_primitives())
+ return is_background ? BACKGROUND_BLOCKING : FOREGROUND_BLOCKING;
+ return is_background ? BACKGROUND : FOREGROUND;
+}
+
+} // namespace task_scheduler_util
diff --git a/chromium/components/task_scheduler_util/renderer/initialization.h b/chromium/components/task_scheduler_util/renderer/initialization.h
new file mode 100644
index 00000000000..6f351507eec
--- /dev/null
+++ b/chromium/components/task_scheduler_util/renderer/initialization.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_TASK_SCHEDULER_UTIL_RENDERER_INITIALIZATION_H_
+#define COMPONENTS_TASK_SCHEDULER_UTIL_RENDERER_INITIALIZATION_H_
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+
+namespace base {
+class TaskTraits;
+}
+
+namespace task_scheduler_util {
+
+// Gets a vector of SchedulerWorkerPoolParams to initialize TaskScheduler in a
+// renderer based off variation params specified on the command line. Returns an
+// empty vector if variation params specified on the command line are incomplete
+// or invalid.
+std::vector<base::SchedulerWorkerPoolParams> GetRendererWorkerPoolParams();
+
+// Maps |traits| to the index of a renderer worker pool vector provided by
+// GetRendererWorkerPoolParams().
+size_t RendererWorkerPoolIndexForTraits(const base::TaskTraits& traits);
+
+} // namespace task_scheduler_util
+
+#endif // COMPONENTS_TASK_SCHEDULER_UTIL_RENDERER_INITIALIZATION_H_
diff --git a/chromium/components/test_runner/BUILD.gn b/chromium/components/test_runner/BUILD.gn
index 3ac89552bd9..5cc0c62499e 100644
--- a/chromium/components/test_runner/BUILD.gn
+++ b/chromium/components/test_runner/BUILD.gn
@@ -107,6 +107,7 @@ component("test_runner") {
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
deps = [
+ ":resources",
"//base:base",
"//base:i18n",
"//cc",
@@ -115,7 +116,7 @@ component("test_runner") {
"//gin",
"//gpu",
"//gpu/command_buffer/client:gles2_interface",
- "//media/midi:mojo_cpp_sources",
+ "//media/midi:mojo",
"//net",
"//skia",
"//third_party/WebKit/public:blink",
@@ -190,18 +191,23 @@ if (is_mac) {
}
group("resources") {
+ deps = []
data_deps = []
if (is_mac) {
+ deps += [ ":test_runner_bundle_data" ]
data_deps += [ ":test_runner_bundle_data" ]
} else {
+ deps += [ ":copy_ahem" ]
data_deps += [ ":copy_ahem" ]
}
if (use_x11) {
+ deps += [ ":copy_x11_fonts" ]
data_deps += [ ":copy_x11_fonts" ]
}
if (is_android) {
+ deps += [ ":copy_android_fonts" ]
data_deps += [ ":copy_android_fonts" ]
}
}
diff --git a/chromium/components/toolbar/toolbar_model_delegate.h b/chromium/components/toolbar/toolbar_model_delegate.h
index a32623b7ae1..cf88095a6f2 100644
--- a/chromium/components/toolbar/toolbar_model_delegate.h
+++ b/chromium/components/toolbar/toolbar_model_delegate.h
@@ -13,6 +13,10 @@
class GURL;
+namespace gfx {
+enum class VectorIconId;
+}
+
namespace net {
class X509Certificate;
}
@@ -46,6 +50,12 @@ 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.
+ virtual gfx::VectorIconId GetVectorIconOverride() const = 0;
+
protected:
virtual ~ToolbarModelDelegate() {}
};
diff --git a/chromium/components/toolbar/toolbar_model_impl.cc b/chromium/components/toolbar/toolbar_model_impl.cc
index b57c64b61b7..e18b120675d 100644
--- a/chromium/components/toolbar/toolbar_model_impl.cc
+++ b/chromium/components/toolbar/toolbar_model_impl.cc
@@ -69,6 +69,10 @@ security_state::SecurityLevel ToolbarModelImpl::GetSecurityLevel(
gfx::VectorIconId ToolbarModelImpl::GetVectorIcon() const {
#if !defined(OS_ANDROID) && !defined(OS_IOS)
+ const auto icon_override = delegate_->GetVectorIconOverride();
+ if (icon_override != gfx::VectorIconId::VECTOR_ICON_NONE)
+ return icon_override;
+
switch (GetSecurityLevel(false)) {
case security_state::NONE:
case security_state::HTTP_SHOW_WARNING:
diff --git a/chromium/components/tracing/browser/trace_config_file.cc b/chromium/components/tracing/browser/trace_config_file.cc
index 62220d6e678..97f8a16810a 100644
--- a/chromium/components/tracing/browser/trace_config_file.cc
+++ b/chromium/components/tracing/browser/trace_config_file.cc
@@ -98,7 +98,7 @@ TraceConfigFile::~TraceConfigFile() {
bool TraceConfigFile::ParseTraceConfigFileContent(const std::string& content) {
std::unique_ptr<base::Value> value(base::JSONReader::Read(content));
- if (!value || !value->IsType(base::Value::TYPE_DICTIONARY))
+ if (!value || !value->IsType(base::Value::Type::DICTIONARY))
return false;
std::unique_ptr<base::DictionaryValue> dict(
diff --git a/chromium/components/tracing/child/child_trace_message_filter_browsertest.cc b/chromium/components/tracing/child/child_trace_message_filter_browsertest.cc
index 156de40fef4..d0d2d020a88 100644
--- a/chromium/components/tracing/child/child_trace_message_filter_browsertest.cc
+++ b/chromium/components/tracing/child/child_trace_message_filter_browsertest.cc
@@ -55,7 +55,8 @@ class ChildTracingTest : public content::RenderViewTest, public IPC::Listener {
// registered itself by now; this cannot be prevented easily.
mock_dump_provider_.reset(new MockDumpProvider());
MemoryDumpManager::GetInstance()->RegisterDumpProvider(
- mock_dump_provider_.get(), "MockDumpProvider", nullptr);
+ mock_dump_provider_.get(), "MockDumpProvider",
+ base::ThreadTaskRunnerHandle::Get());
MemoryDumpManager::GetInstance()
->set_dumper_registrations_ignored_for_testing(true);
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 d2c00c347f0..593755bada1 100644
--- a/chromium/components/tracing/common/process_metrics_memory_dump_provider.cc
+++ b/chromium/components/tracing/common/process_metrics_memory_dump_provider.cc
@@ -155,6 +155,18 @@ uint32_t ReadLinuxProcSmapsFile(FILE* smaps_file,
}
return num_valid_regions;
}
+
+bool GetResidentSizeFromStatmFile(int fd, uint64_t* resident_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, resident_pages);
+ return num_scanned == 1;
+}
+
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
std::unique_ptr<base::ProcessMetrics> CreateProcessMetrics(
@@ -183,6 +195,9 @@ uint64_t ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing = 0;
// 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) {
@@ -211,6 +226,7 @@ void ProcessMetricsMemoryDumpProvider::RegisterForProcess(
new ProcessMetricsMemoryDumpProvider(process));
base::trace_event::MemoryDumpProvider::Options options;
options.target_pid = process;
+ options.is_fast_polling_supported = true;
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
metrics_provider.get(), "ProcessMemoryMetrics", nullptr, options);
bool did_insert =
@@ -229,9 +245,8 @@ void ProcessMetricsMemoryDumpProvider::RegisterForProcess(
void ProcessMetricsMemoryDumpProvider::UnregisterForProcess(
base::ProcessId process) {
auto iter = g_dump_providers_map.Get().find(process);
- if (iter == g_dump_providers_map.Get().end()) {
+ 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);
@@ -298,7 +313,20 @@ bool ProcessMetricsMemoryDumpProvider::DumpProcessTotals(
nullptr /* shared_bytes */);
if (res)
pmd->process_totals()->SetExtraFieldInBytes("private_bytes", private_bytes);
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+#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);
@@ -309,4 +337,40 @@ bool ProcessMetricsMemoryDumpProvider::DumpProcessTotals(
return true;
}
+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()) {
+ std::string name = "/proc/" + (process_ == base::kNullProcessId
+ ? "self"
+ : base::IntToString(process_)) +
+ "/statm";
+ fast_polling_statm_fd_.reset(open(name.c_str(), O_RDONLY));
+ DCHECK(fast_polling_statm_fd_.is_valid());
+ }
+ statm_fd = fast_polling_statm_fd_.get();
+ }
+ if (statm_fd == -1)
+ return;
+
+ uint64_t rss_pages = 0;
+ if (!GetResidentSizeFromStatmFile(statm_fd, &rss_pages))
+ return;
+
+ static size_t page_size = base::GetPageSize();
+ *memory_total = rss_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
+}
+
} // 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 b47bdec142b..4874685f62d 100644
--- a/chromium/components/tracing/common/process_metrics_memory_dump_provider.h
+++ b/chromium/components/tracing/common/process_metrics_memory_dump_provider.h
@@ -7,6 +7,7 @@
#include <memory>
+#include "base/files/scoped_file.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/process/process_handle.h"
@@ -33,11 +34,15 @@ class TRACING_EXPORT ProcessMetricsMemoryDumpProvider
// 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;
private:
FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest,
ParseProcSmaps);
FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest, DumpRSS);
+ FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest,
+ TestPollFastMemoryTotal);
ProcessMetricsMemoryDumpProvider(base::ProcessId process);
@@ -50,6 +55,9 @@ class TRACING_EXPORT ProcessMetricsMemoryDumpProvider
#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_;
#endif
base::ProcessId process_;
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
index 821f3c22ae4..5b1b25f1d0e 100644
--- a/chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc
+++ b/chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc
@@ -9,6 +9,8 @@
#include <memory>
#include "base/files/file_util.h"
+#include "base/memory/ptr_util.h"
+#include "base/process/process_metrics.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"
@@ -107,15 +109,16 @@ const char kTestSmaps2[] =
"Locked: 0 kB\n"
"VmFlags: rd wr mr mw me ac sd\n";
-void CreateAndSetSmapsFileForTesting(const char* smaps_string,
- base::ScopedFILE& file) {
+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);
+ file->reset(temp_file);
ASSERT_TRUE(temp_file);
- ASSERT_TRUE(base::WriteFileDescriptor(fileno(temp_file), smaps_string,
- strlen(smaps_string)));
+ ASSERT_TRUE(
+ base::WriteFileDescriptor(fileno(temp_file), contents, strlen(contents)));
}
} // namespace
@@ -182,7 +185,7 @@ TEST(ProcessMetricsMemoryDumpProviderTest, ParseProcSmaps) {
base::trace_event::ProcessMemoryDump pmd_1(nullptr /* session_state */,
dump_args);
base::ScopedFILE temp_file1;
- CreateAndSetSmapsFileForTesting(kTestSmaps1, 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());
@@ -215,7 +218,7 @@ TEST(ProcessMetricsMemoryDumpProviderTest, ParseProcSmaps) {
base::trace_event::ProcessMemoryDump pmd_2(nullptr /* session_state */,
dump_args);
base::ScopedFILE temp_file2;
- CreateAndSetSmapsFileForTesting(kTestSmaps2, 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());
@@ -234,4 +237,36 @@ TEST(ProcessMetricsMemoryDumpProviderTest, ParseProcSmaps) {
}
#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
+}
+
} // namespace tracing
diff --git a/chromium/components/translate/content/common/translate.mojom b/chromium/components/translate/content/common/translate.mojom
index e0916071873..9df5ce09fbb 100644
--- a/chromium/components/translate/content/common/translate.mojom
+++ b/chromium/components/translate/content/common/translate.mojom
@@ -4,7 +4,7 @@
module translate.mojom;
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
import "url/mojo/url.mojom";
enum TranslateError {
diff --git a/chromium/components/translate/core/browser/BUILD.gn b/chromium/components/translate/core/browser/BUILD.gn
index dfdc5a5320a..fc31a04facd 100644
--- a/chromium/components/translate/core/browser/BUILD.gn
+++ b/chromium/components/translate/core/browser/BUILD.gn
@@ -103,9 +103,10 @@ source_set("unit_tests") {
"//base",
"//components/infobars/core",
"//components/metrics/proto",
- "//components/pref_registry:test_support",
+ "//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/variations",
diff --git a/chromium/components/translate/ios/browser/BUILD.gn b/chromium/components/translate/ios/browser/BUILD.gn
index 1eefbb02408..b514e9169f6 100644
--- a/chromium/components/translate/ios/browser/BUILD.gn
+++ b/chromium/components/translate/ios/browser/BUILD.gn
@@ -5,6 +5,7 @@
import("//ios/web/js_compile.gni")
source_set("browser") {
+ configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"ios_translate_driver.h",
"ios_translate_driver.mm",
@@ -14,6 +15,8 @@ source_set("browser") {
"js_translate_manager.mm",
"language_detection_controller.h",
"language_detection_controller.mm",
+ "string_clipping_util.cc",
+ "string_clipping_util.h",
"translate_controller.h",
"translate_controller.mm",
]
@@ -44,6 +47,7 @@ source_set("unit_tests") {
sources = [
"js_translate_manager_unittest.mm",
"language_detection_controller_unittest.mm",
+ "string_clipping_util_unittest.cc",
"translate_controller_unittest.mm",
]
diff --git a/chromium/components/typemaps.gni b/chromium/components/typemaps.gni
index da1f6d9a1cc..57cb150542d 100644
--- a/chromium/components/typemaps.gni
+++ b/chromium/components/typemaps.gni
@@ -4,7 +4,9 @@
typemaps = [
"//components/autofill/content/common/autofill_types.typemap",
+ "//components/content_settings/core/common/content_settings.typemap",
+ "//components/nacl/common/nacl.typemap",
"//components/password_manager/content/common/credential_manager.typemap",
- "//components/safe_json/public/interfaces/safe_json.typemap",
+ "//components/signin/public/interfaces/account_id.typemap",
"//components/translate/content/common/translate.typemap",
]
diff --git a/chromium/components/ui_devtools/devtools_client.cc b/chromium/components/ui_devtools/devtools_client.cc
index 1e0cdc40f2a..4c2a1fc0e7d 100644
--- a/chromium/components/ui_devtools/devtools_client.cc
+++ b/chromium/components/ui_devtools/devtools_client.cc
@@ -51,14 +51,17 @@ void UiDevToolsClient::DisableAllAgents() {
agent->Disable();
}
-void UiDevToolsClient::sendProtocolResponse(int callId, const String& message) {
+void UiDevToolsClient::sendProtocolResponse(
+ int callId,
+ std::unique_ptr<protocol::Serializable> message) {
if (connected())
- server_->SendOverWebSocket(connection_id_, message);
+ server_->SendOverWebSocket(connection_id_, message->serialize());
}
-void UiDevToolsClient::sendProtocolNotification(const String& message) {
+void UiDevToolsClient::sendProtocolNotification(
+ std::unique_ptr<protocol::Serializable> message) {
if (connected())
- server_->SendOverWebSocket(connection_id_, message);
+ server_->SendOverWebSocket(connection_id_, message->serialize());
}
void UiDevToolsClient::flushProtocolNotifications() {
diff --git a/chromium/components/ui_devtools/devtools_client.h b/chromium/components/ui_devtools/devtools_client.h
index 36ad2018634..18125561549 100644
--- a/chromium/components/ui_devtools/devtools_client.h
+++ b/chromium/components/ui_devtools/devtools_client.h
@@ -40,8 +40,11 @@ class UiDevToolsClient : public protocol::FrontendChannel {
void DisableAllAgents();
// protocol::FrontendChannel
- void sendProtocolResponse(int callId, const String& message) override;
- void sendProtocolNotification(const String& message) override;
+ void sendProtocolResponse(
+ int callId,
+ std::unique_ptr<protocol::Serializable> message) override;
+ void sendProtocolNotification(
+ std::unique_ptr<protocol::Serializable> message) override;
void flushProtocolNotifications() override;
std::string name_;
diff --git a/chromium/components/ui_devtools/devtools_server.cc b/chromium/components/ui_devtools/devtools_server.cc
index ff0616d52c9..5ed57945189 100644
--- a/chromium/components/ui_devtools/devtools_server.cc
+++ b/chromium/components/ui_devtools/devtools_server.cc
@@ -11,6 +11,7 @@
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "components/ui_devtools/switches.h"
#include "net/base/net_errors.h"
@@ -25,54 +26,91 @@ namespace devtools {
namespace {
const char kChromeDeveloperToolsPrefix[] =
"chrome-devtools://devtools/bundled/inspector.html?ws=";
+
+bool IsUiDevToolsEnabled() {
+ return base::CommandLine::ForCurrentProcess()->HasSwitch(kEnableUiDevTools);
+}
+
+int GetUiDevToolsPort() {
+ DCHECK(IsUiDevToolsEnabled());
+ constexpr int kDefaultPort = 9223;
+ int port;
+ if (!base::StringToInt(
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ kEnableUiDevTools),
+ &port))
+ port = kDefaultPort;
+ return port;
+}
+
} // namespace
+UiDevToolsServer* UiDevToolsServer::devtools_server_ = nullptr;
+
UiDevToolsServer::UiDevToolsServer(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner)
- : task_runner_(task_runner) {
- if (task_runner_)
+ scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
+ : io_thread_task_runner_(io_thread_task_runner) {
+ DCHECK(!devtools_server_);
+ main_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ devtools_server_ = this;
+ if (io_thread_task_runner_)
return;
- // If task_runner not passed in, create an I/O thread the server can run on
+ // If io_thread_task_runner not passed in, create an I/O thread
thread_.reset(new base::Thread("UiDevToolsServerThread"));
base::Thread::Options options;
options.message_loop_type = base::MessageLoop::TYPE_IO;
CHECK(thread_->StartWithOptions(options));
- task_runner_ = thread_->task_runner();
+ io_thread_task_runner_ = thread_->task_runner();
}
-UiDevToolsServer::~UiDevToolsServer() {}
+UiDevToolsServer::~UiDevToolsServer() {
+ devtools_server_ = nullptr;
+}
// static
std::unique_ptr<UiDevToolsServer> UiDevToolsServer::Create(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner) {
std::unique_ptr<UiDevToolsServer> server;
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(kEnableUiDevTools)) {
+ if (IsUiDevToolsEnabled() && !devtools_server_) {
// TODO(mhashmi): Change port if more than one inspectable clients
- int port = 9223; // Default port is 9223
- base::StringToInt(
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- kEnableUiDevTools),
- &port);
- server.reset(new UiDevToolsServer(task_runner));
- server->Start("127.0.0.1", port);
+ server.reset(new UiDevToolsServer(io_thread_task_runner));
+ server->Start("127.0.0.1", GetUiDevToolsPort());
}
return server;
}
+// static
+std::vector<UiDevToolsServer::NameUrlPair>
+UiDevToolsServer::GetClientNamesAndUrls() {
+ std::vector<NameUrlPair> pairs;
+ if (!devtools_server_)
+ return pairs;
+
+ for (ClientsList::size_type i = 0; i != devtools_server_->clients_.size();
+ i++) {
+ pairs.push_back(std::pair<std::string, std::string>(
+ devtools_server_->clients_[i]->name(),
+ base::StringPrintf("%slocalhost:%d/%" PRIuS,
+ kChromeDeveloperToolsPrefix, GetUiDevToolsPort(),
+ i)));
+ }
+ return pairs;
+}
+
void UiDevToolsServer::AttachClient(std::unique_ptr<UiDevToolsClient> client) {
clients_.push_back(std::move(client));
}
void UiDevToolsServer::SendOverWebSocket(int connection_id,
const String& message) {
- task_runner_->PostTask(
+ io_thread_task_runner_->PostTask(
FROM_HERE,
base::Bind(&net::HttpServer::SendOverWebSocket,
base::Unretained(server_.get()), connection_id, message));
}
void UiDevToolsServer::Start(const std::string& address_string, uint16_t port) {
- task_runner_->PostTask(
+ io_thread_task_runner_->PostTask(
FROM_HERE, base::Bind(&UiDevToolsServer::StartServer,
base::Unretained(this), address_string, port));
}
@@ -96,32 +134,7 @@ void UiDevToolsServer::OnConnect(int connection_id) {
void UiDevToolsServer::OnHttpRequest(int connection_id,
const net::HttpServerRequestInfo& info) {
- // Display a simple html page with all the clients and the corresponding
- // devtools links
- // TODO(mhashmi): Remove and display all clients under chrome://inspect/#other
- if (info.path.empty() || info.path == "/") {
- std::string clientHTML = "<html>";
- clientHTML +=
- "<h3>Copy paste the corresponding links in your browser to inspect "
- "them:</h3>";
- net::IPEndPoint ip;
- server_->GetLocalAddress(&ip);
- for (ClientsList::size_type i = 0; i != clients_.size(); i++) {
- clientHTML += base::StringPrintf(
- "<p><strong>%s</strong> (%s%s/%" PRIuS ")</p>",
- clients_[i]->name().c_str(), kChromeDeveloperToolsPrefix,
- ip.ToString().c_str(), i);
- }
- clientHTML += "</html>";
- task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&net::HttpServer::Send200, base::Unretained(server_.get()),
- connection_id, clientHTML, "text/html"));
- return;
- }
- task_runner_->PostTask(
- FROM_HERE, base::Bind(&net::HttpServer::Send404,
- base::Unretained(server_.get()), connection_id));
+ NOTIMPLEMENTED();
}
void UiDevToolsServer::OnWebSocketRequest(
@@ -139,7 +152,7 @@ void UiDevToolsServer::OnWebSocketRequest(
return;
client->set_connection_id(connection_id);
connections_[connection_id] = client;
- task_runner_->PostTask(
+ io_thread_task_runner_->PostTask(
FROM_HERE,
base::Bind(&net::HttpServer::AcceptWebSocket,
base::Unretained(server_.get()), connection_id, info));
@@ -151,8 +164,9 @@ void UiDevToolsServer::OnWebSocketMessage(int connection_id,
DCHECK(it != connections_.end());
UiDevToolsClient* client = it->second;
DCHECK(client);
- task_runner_->PostTask(FROM_HERE, base::Bind(&UiDevToolsClient::Dispatch,
- base::Unretained(client), data));
+ main_thread_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UiDevToolsClient::Dispatch, base::Unretained(client), data));
}
void UiDevToolsServer::OnClose(int connection_id) {
@@ -161,7 +175,9 @@ void UiDevToolsServer::OnClose(int connection_id) {
return;
UiDevToolsClient* client = it->second;
DCHECK(client);
- client->Disconnect();
+ main_thread_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UiDevToolsClient::Disconnect, base::Unretained(client)));
connections_.erase(it);
}
diff --git a/chromium/components/ui_devtools/devtools_server.h b/chromium/components/ui_devtools/devtools_server.h
index b101ed1c1b8..7dfc02cb5e1 100644
--- a/chromium/components/ui_devtools/devtools_server.h
+++ b/chromium/components/ui_devtools/devtools_server.h
@@ -7,6 +7,7 @@
#include <vector>
+#include "base/compiler_specific.h"
#include "base/threading/thread.h"
#include "components/ui_devtools/DOM.h"
#include "components/ui_devtools/Forward.h"
@@ -22,16 +23,21 @@ class UiDevToolsServer : public net::HttpServer::Delegate {
public:
~UiDevToolsServer() override;
- // Returns an empty unique_ptr if ui devtools flag isn't enabled.
+ // Returns an empty unique_ptr if ui devtools flag isn't enabled or if a
+ // server instance has already been created.
static std::unique_ptr<UiDevToolsServer> Create(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner);
+
+ // Returns a list of attached UiDevToolsClient name + URL
+ using NameUrlPair = std::pair<std::string, std::string>;
+ static std::vector<NameUrlPair> GetClientNamesAndUrls();
void AttachClient(std::unique_ptr<UiDevToolsClient> client);
void SendOverWebSocket(int connection_id, const String& message);
private:
explicit UiDevToolsServer(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner);
void Start(const std::string& address_string, uint16_t port);
void StartServer(const std::string& address_string, uint16_t port);
@@ -52,7 +58,11 @@ class UiDevToolsServer : public net::HttpServer::Delegate {
std::unique_ptr<base::Thread> thread_;
std::unique_ptr<net::HttpServer> server_;
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
+ // The server (owned by ash for now)
+ static UiDevToolsServer* devtools_server_;
DISALLOW_COPY_AND_ASSIGN(UiDevToolsServer);
};
diff --git a/chromium/components/ui_devtools/protocol.json b/chromium/components/ui_devtools/protocol.json
index 3ccbc2de9b4..e69dd13c3cc 100644
--- a/chromium/components/ui_devtools/protocol.json
+++ b/chromium/components/ui_devtools/protocol.json
@@ -20,7 +20,29 @@
"name": "root"
}
]
+ },
+ {
+ "name": "highlightNode",
+ "parameters": [
+ {
+ "name": "highlightConfig",
+ "$ref": "HighlightConfig",
+ "description": "A descriptor for the highlight appearance."
+ },
+ {
+ "description": "Identifier of the node to highlight.",
+ "optional": true,
+ "name": "nodeId",
+ "$ref": "NodeId"
+ }
+ ],
+ "description": "Highlights DOM node with given id or with the given JavaScript object wrapper. Either nodeId or objectId must be specified."
+ },
+ {
+ "name": "hideHighlight",
+ "description": "Hides DOM node highlight."
}
+
],
"events": [
{
@@ -126,7 +148,54 @@
}
],
"type": "object"
- }
+ },
+ {
+ "id": "RGBA",
+ "type": "object",
+ "properties": [
+ {
+ "name": "r",
+ "type": "integer",
+ "description": "The red component, in the [0-255] range."
+ },
+ {
+ "name": "g",
+ "type": "integer",
+ "description": "The green component, in the [0-255] range."
+ },
+ {
+ "name": "b",
+ "type": "integer",
+ "description": "The blue component, in the [0-255] range."
+ },
+ {
+ "name": "a",
+ "type": "number",
+ "optional": true,
+ "description": "The alpha component, in the [0-1] range (default: 1)."
+ }
+ ],
+ "description": "A structure holding an RGBA color."
+ },
+ {
+ "id": "HighlightConfig",
+ "type": "object",
+ "properties": [
+ {
+ "name": "contentColor",
+ "$ref": "RGBA",
+ "optional": true,
+ "description": "The content box highlight fill color (default: transparent)."
+ },
+ {
+ "name": "borderColor",
+ "$ref": "RGBA",
+ "optional": true,
+ "description": "The border highlight fill color (default: transparent)."
+ }
+ ],
+ "description": "Configuration data for the highlighting of page elements."
+ }
]
},
{
@@ -155,12 +224,96 @@
"name": "inlineStyle",
"optional": true
}
+
]
+ },
+ {
+ "description": "Applies specified style edits one after another in the given order.",
+ "name": "setStyleTexts",
+ "parameters": [
+ {
+ "items": {
+ "$ref": "StyleDeclarationEdit"
+ },
+ "type": "array",
+ "name": "edits"
+ }
+ ],
+ "returns": [
+ {
+ "items": {
+ "$ref": "CSSStyle"
+ },
+ "type": "array",
+ "name": "styles",
+ "description": "The resulting styles after modification."
+ }
+ ]
+ }
+ ],
+ "events": [
+ {
+ "name": "styleSheetChanged",
+ "parameters": [
+ {
+ "name": "styleSheetId",
+ "$ref": "StyleSheetId"
+ }
+ ],
+ "description": "Fired whenever any bounds are updated for any object."
}
],
"domain": "CSS",
"types": [
{
+ "id": "StyleSheetId",
+ "type": "string"
+ },
+ {
+ "description": "Text range within a resource. All numbers are zero-based.",
+ "type": "object",
+ "id": "SourceRange",
+ "properties": [
+ {
+ "type": "integer",
+ "name": "startLine",
+ "description": "Start line of range."
+ },
+ {
+ "type": "integer",
+ "name": "startColumn",
+ "description": "Start column of range (inclusive)."
+ },
+ {
+ "type": "integer",
+ "name": "endLine",
+ "description": "End line of range"
+ },
+ {
+ "type": "integer",
+ "name": "endColumn",
+ "description": "End column of range (exclusive)."
+ }
+ ]
+ },
+ {
+ "description": "A descriptor of operation to mutate style declaration text.",
+ "type": "object",
+ "id": "StyleDeclarationEdit",
+ "properties": [
+ {
+ "description": "The css style sheet identifier (same as NodeId for UI DevTools).",
+ "name": "styleSheetId",
+ "$ref": "StyleSheetId"
+ },
+ {
+ "type": "string",
+ "name": "text",
+ "description": "New style text."
+ }
+ ]
+ },
+ {
"id": "CSSProperty",
"properties": [
{
@@ -172,6 +325,12 @@
"description": "The property value.",
"name": "value",
"type": "string"
+ },
+ {
+ "name": "range",
+ "$ref": "SourceRange",
+ "optional": true,
+ "description": "The entire property range in the enclosing style declaration (if available)."
}
],
"type": "object"
@@ -181,6 +340,12 @@
"id": "CSSStyle",
"properties": [
{
+ "description": "The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from. For UI devtools, this is simply equivalent to the NodeId.",
+ "optional": true,
+ "name": "styleSheetId",
+ "$ref": "StyleSheetId"
+ },
+ {
"description": "CSS properties in the style.",
"items": {
"$ref": "CSSProperty"
@@ -195,6 +360,12 @@
},
"name": "shorthandEntries",
"type": "array"
+ },
+ {
+ "description": "Style declaration range in the enclosing stylesheet (if available).",
+ "optional": true,
+ "name": "range",
+ "$ref": "SourceRange"
}
],
"type": "object"
diff --git a/chromium/components/update_client/action.cc b/chromium/components/update_client/action.cc
index 39d15d8b262..7fab8a8d8ed 100644
--- a/chromium/components/update_client/action.cc
+++ b/chromium/components/update_client/action.cc
@@ -145,7 +145,7 @@ void ActionImpl::UpdateCrx() {
FROM_HERE, base::Bind(&Action::Run, base::Unretained(update_action.get()),
update_context_, callback_));
- update_context_->current_action.reset(update_action.release());
+ update_context_->current_action = std::move(update_action);
}
void ActionImpl::UpdateCrxComplete(CrxUpdateItem* item) {
@@ -177,7 +177,7 @@ void ActionImpl::UpdateCrxComplete(CrxUpdateItem* item) {
FROM_HERE, base::Bind(&Action::Run, base::Unretained(action_wait.get()),
update_context_, callback_));
- update_context_->current_action.reset(action_wait.release());
+ update_context_->current_action = std::move(action_wait);
}
}
diff --git a/chromium/components/update_client/action.h b/chromium/components/update_client/action.h
index 1b46106e333..46397b993b5 100644
--- a/chromium/components/update_client/action.h
+++ b/chromium/components/update_client/action.h
@@ -17,7 +17,6 @@
namespace update_client {
-class Configurator;
enum class Error;
struct CrxUpdateItem;
struct UpdateContext;
diff --git a/chromium/components/update_client/action_update.cc b/chromium/components/update_client/action_update.cc
index be64b23288a..3888d7c1214 100644
--- a/chromium/components/update_client/action_update.cc
+++ b/chromium/components/update_client/action_update.cc
@@ -248,7 +248,7 @@ void ActionUpdateDiff::TryUpdateFull() {
FROM_HERE, base::Bind(&Action::Run, base::Unretained(update_action.get()),
update_context_, callback_));
- update_context_->current_action.reset(update_action.release());
+ update_context_->current_action = std::move(update_action);
}
bool ActionUpdateDiff::IsBackgroundDownload(const CrxUpdateItem* item) {
diff --git a/chromium/components/update_client/action_update.h b/chromium/components/update_client/action_update.h
index 7b6297153f6..95061a68f86 100644
--- a/chromium/components/update_client/action_update.h
+++ b/chromium/components/update_client/action_update.h
@@ -26,7 +26,6 @@ class FilePath;
namespace update_client {
-class UpdateChecker;
enum class UnpackError;
// Defines a template method design pattern for ActionUpdate. This class
diff --git a/chromium/components/update_client/component_patcher.cc b/chromium/components/update_client/component_patcher.cc
index cb18042908e..afafd8744e9 100644
--- a/chromium/components/update_client/component_patcher.cc
+++ b/chromium/components/update_client/component_patcher.cc
@@ -34,7 +34,7 @@ base::ListValue* ReadCommands(const base::FilePath& unpack_path) {
JSONFileValueDeserializer deserializer(commands);
std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, NULL);
- return (root.get() && root->IsType(base::Value::TYPE_LIST))
+ return (root.get() && root->IsType(base::Value::Type::LIST))
? static_cast<base::ListValue*>(root.release())
: NULL;
}
diff --git a/chromium/components/update_client/component_patcher_unittest.h b/chromium/components/update_client/component_patcher_unittest.h
index f21d104e8f2..d3d09eec309 100644
--- a/chromium/components/update_client/component_patcher_unittest.h
+++ b/chromium/components/update_client/component_patcher_unittest.h
@@ -16,7 +16,6 @@
namespace update_client {
-class MockComponentPatcher;
class ReadOnlyTestInstaller;
const char binary_output_hash[] =
diff --git a/chromium/components/update_client/component_unpacker.cc b/chromium/components/update_client/component_unpacker.cc
index 24e9b482c88..6dd555cc2ea 100644
--- a/chromium/components/update_client/component_unpacker.cc
+++ b/chromium/components/update_client/component_unpacker.cc
@@ -49,7 +49,7 @@ std::unique_ptr<base::DictionaryValue> ReadManifest(
std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, &error);
if (!root.get())
return std::unique_ptr<base::DictionaryValue>();
- if (!root->IsType(base::Value::TYPE_DICTIONARY))
+ if (!root->IsType(base::Value::Type::DICTIONARY))
return std::unique_ptr<base::DictionaryValue>();
return std::unique_ptr<base::DictionaryValue>(
static_cast<base::DictionaryValue*>(root.release()));
diff --git a/chromium/components/update_client/test_configurator.h b/chromium/components/update_client/test_configurator.h
index 89132f5a771..7a103f47941 100644
--- a/chromium/components/update_client/test_configurator.h
+++ b/chromium/components/update_client/test_configurator.h
@@ -34,8 +34,6 @@ namespace update_client {
#define POST_INTERCEPT_HOSTNAME "localhost2"
#define POST_INTERCEPT_PATH "/update2"
-struct CrxComponent;
-
// component 1 has extension id "jebgalgnebhfojomionfpkfelancnnkf", and
// the RSA public key the following hash:
const uint8_t jebg_hash[] = {0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec,
diff --git a/chromium/components/update_client/update_checker.h b/chromium/components/update_client/update_checker.h
index 23aef59fae1..7500147beed 100644
--- a/chromium/components/update_client/update_checker.h
+++ b/chromium/components/update_client/update_checker.h
@@ -16,8 +16,6 @@
#include "components/update_client/update_response.h"
#include "url/gurl.h"
-class GURL;
-
namespace update_client {
class PersistedData;
diff --git a/chromium/components/update_client/update_client.h b/chromium/components/update_client/update_client.h
index f58e82092fa..cdcd956fcc0 100644
--- a/chromium/components/update_client/update_client.h
+++ b/chromium/components/update_client/update_client.h
@@ -130,7 +130,6 @@
// as the CRX undergoes a series of state transitions in the process of
// being checked for updates and applying the update.
-class ComponentsUI;
class PrefRegistrySimple;
namespace base {
diff --git a/chromium/components/update_client/update_client_internal.h b/chromium/components/update_client/update_client_internal.h
index 0a24c149fbb..2f675a80e60 100644
--- a/chromium/components/update_client/update_client_internal.h
+++ b/chromium/components/update_client/update_client_internal.h
@@ -19,11 +19,6 @@
#include "components/update_client/update_checker.h"
#include "components/update_client/update_client.h"
-namespace base {
-class SequencedTaskRunner;
-class SingleThreadTaskRunner;
-} // namespace base
-
namespace update_client {
class Configurator;
@@ -31,7 +26,6 @@ class PingManager;
class Task;
class UpdateEngine;
enum class Error;
-struct TaskContext;
class UpdateClientImpl : public UpdateClient {
public:
diff --git a/chromium/components/update_client/update_engine.cc b/chromium/components/update_client/update_engine.cc
index c0bf0ae8c62..5e28be89460 100644
--- a/chromium/components/update_client/update_engine.cc
+++ b/chromium/components/update_client/update_engine.cc
@@ -100,7 +100,7 @@ void UpdateEngine::Update(
(*update_context->update_checker_factory)(config_, metadata_.get()),
config_->GetBrowserVersion(), config_->ExtraRequestParams()));
- update_context->current_action.reset(update_check_action.release());
+ update_context->current_action = std::move(update_check_action);
update_contexts_.insert(update_context.get());
update_context->current_action->Run(
diff --git a/chromium/components/update_client/update_query_params.cc b/chromium/components/update_client/update_query_params.cc
index 42c8c91ff52..5cd745b1198 100644
--- a/chromium/components/update_client/update_query_params.cc
+++ b/chromium/components/update_client/update_query_params.cc
@@ -6,10 +6,13 @@
#include "base/logging.h"
#include "base/strings/stringprintf.h"
-#include "base/win/windows_version.h"
#include "build/build_config.h"
#include "components/update_client/update_query_params_delegate.h"
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
namespace update_client {
namespace {
diff --git a/chromium/components/update_client/url_request_post_interceptor.h b/chromium/components/update_client/url_request_post_interceptor.h
index 15672bcf6f5..bead17c42fa 100644
--- a/chromium/components/update_client/url_request_post_interceptor.h
+++ b/chromium/components/update_client/url_request_post_interceptor.h
@@ -22,10 +22,6 @@ class FilePath;
class SequencedTaskRunner;
}
-namespace net {
-class URLRequest;
-}
-
namespace update_client {
// Intercepts requests to a file path, counts them, and captures the body of
diff --git a/chromium/components/update_client/utils.cc b/chromium/components/update_client/utils.cc
index 6af11aebf18..0abcbb273c3 100644
--- a/chromium/components/update_client/utils.cc
+++ b/chromium/components/update_client/utils.cc
@@ -23,7 +23,6 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
-#include "base/win/windows_version.h"
#include "build/build_config.h"
#include "components/crx_file/id_util.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
@@ -41,6 +40,10 @@
#include "net/url_request/url_request_status.h"
#include "url/gurl.h"
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
namespace update_client {
namespace {
diff --git a/chromium/components/update_client/utils.h b/chromium/components/update_client/utils.h
index f82fb929a2d..1c9e7414541 100644
--- a/chromium/components/update_client/utils.h
+++ b/chromium/components/update_client/utils.h
@@ -17,7 +17,6 @@
class GURL;
namespace base {
-class DictionaryValue;
class FilePath;
}
@@ -29,7 +28,6 @@ class URLRequestContextGetter;
namespace update_client {
-class Configurator;
struct CrxComponent;
struct CrxUpdateItem;
diff --git a/chromium/components/url_formatter/elide_url_unittest.cc b/chromium/components/url_formatter/elide_url_unittest.cc
index 56058fe78fb..e8a92019f06 100644
--- a/chromium/components/url_formatter/elide_url_unittest.cc
+++ b/chromium/components/url_formatter/elide_url_unittest.cc
@@ -6,7 +6,6 @@
#include <stddef.h>
-#include "base/ios/ios_util.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
@@ -21,6 +20,10 @@
#include "ui/gfx/text_utils.h" // nogncheck
#endif
+#if defined(OS_IOS)
+#include "base/ios/ios_util.h"
+#endif
+
namespace {
struct Testcase {
diff --git a/chromium/components/url_matcher/url_matcher.h b/chromium/components/url_matcher/url_matcher.h
index adc4ee65d06..961c7449137 100644
--- a/chromium/components/url_matcher/url_matcher.h
+++ b/chromium/components/url_matcher/url_matcher.h
@@ -19,10 +19,6 @@
class GURL;
-namespace base {
-class DictionaryValue;
-}
-
namespace url_matcher {
// This class represents a single URL matching condition, e.g. a match on the
diff --git a/chromium/components/user_manager/BUILD.gn b/chromium/components/user_manager/BUILD.gn
index 46067e52ae4..00fe3d0ab4b 100644
--- a/chromium/components/user_manager/BUILD.gn
+++ b/chromium/components/user_manager/BUILD.gn
@@ -11,6 +11,7 @@ component("user_manager") {
"user_manager_export.h",
"user_names.cc",
"user_names.h",
+ "user_type.h",
]
deps = [
@@ -36,7 +37,6 @@ component("user_manager") {
"user_manager.h",
"user_manager_base.cc",
"user_manager_base.h",
- "user_type.h",
]
deps += [
"//components/prefs",
diff --git a/chromium/components/user_manager/DEPS b/chromium/components/user_manager/DEPS
index f8187bc799f..e64e44f5510 100644
--- a/chromium/components/user_manager/DEPS
+++ b/chromium/components/user_manager/DEPS
@@ -5,6 +5,7 @@ include_rules = [
"+google_apis/gaia/gaia_auth_util.h",
"+third_party/skia/include",
"+ui/gfx/codec",
+"+ui/gfx/geometry",
"+ui/gfx/image",
"+url",
]
diff --git a/chromium/components/user_manager/known_user.cc b/chromium/components/user_manager/known_user.cc
index 8ad90614b9f..815d105e187 100644
--- a/chromium/components/user_manager/known_user.cc
+++ b/chromium/components/user_manager/known_user.cc
@@ -32,6 +32,12 @@ const char kCanonicalEmail[] = "email";
// Key of obfuscated GAIA id value.
const char kGAIAIdKey[] = "gaia_id";
+// Key of obfuscated object guid value for Active Directory accounts.
+const char kObjGuidKey[] = "obj_guid";
+
+// Key of account type.
+const char kAccountTypeKey[] = "account_type";
+
// Key of whether this user ID refers to a SAML user.
const char kUsingSAMLKey[] = "using_saml";
@@ -58,11 +64,29 @@ PrefService* GetLocalState() {
bool UserMatches(const AccountId& account_id,
const base::DictionaryValue& dict) {
std::string value;
+ if (account_id.GetAccountType() != AccountType::UNKNOWN &&
+ dict.GetString(kAccountTypeKey, &value) &&
+ account_id.GetAccountType() != AccountId::StringToAccountType(value)) {
+ return false;
+ }
// TODO(alemate): update code once user id is really a struct.
- bool has_gaia_id = dict.GetString(kGAIAIdKey, &value);
- if (has_gaia_id && account_id.GetGaiaId() == value)
- return true;
+ switch (account_id.GetAccountType()) {
+ case AccountType::GOOGLE: {
+ bool has_gaia_id = dict.GetString(kGAIAIdKey, &value);
+ if (has_gaia_id && account_id.GetGaiaId() == value)
+ return true;
+ break;
+ }
+ case AccountType::ACTIVE_DIRECTORY: {
+ bool has_obj_guid = dict.GetString(kObjGuidKey, &value);
+ if (has_obj_guid && account_id.GetObjGuid() == value)
+ return true;
+ break;
+ }
+ case AccountType::UNKNOWN: {
+ }
+ }
bool has_email = dict.GetString(kCanonicalEmail, &value);
if (has_email && account_id.GetUserEmail() == value)
@@ -76,8 +100,20 @@ void UpdateIdentity(const AccountId& account_id, base::DictionaryValue& dict) {
if (!account_id.GetUserEmail().empty())
dict.SetString(kCanonicalEmail, account_id.GetUserEmail());
- if (!account_id.GetGaiaId().empty())
- dict.SetString(kGAIAIdKey, account_id.GetGaiaId());
+ switch (account_id.GetAccountType()) {
+ case AccountType::GOOGLE:
+ if (!account_id.GetGaiaId().empty())
+ dict.SetString(kGAIAIdKey, account_id.GetGaiaId());
+ break;
+ case AccountType::ACTIVE_DIRECTORY:
+ if (!account_id.GetObjGuid().empty())
+ dict.SetString(kObjGuidKey, account_id.GetObjGuid());
+ break;
+ case AccountType::UNKNOWN:
+ return;
+ }
+ dict.SetString(kAccountTypeKey,
+ AccountId::AccountTypeToString(account_id.GetAccountType()));
}
} // namespace
@@ -216,48 +252,76 @@ void SetIntegerPref(const AccountId& account_id,
}
AccountId GetAccountId(const std::string& user_email,
- const std::string& gaia_id) {
+ const std::string& id,
+ const AccountType& account_type) {
+ DCHECK((id.empty() && account_type == AccountType::UNKNOWN) ||
+ (!id.empty() && account_type != AccountType::UNKNOWN));
// In tests empty accounts are possible.
- if (user_email.empty() && gaia_id.empty())
+ if (user_email.empty() && id.empty() &&
+ account_type == AccountType::UNKNOWN) {
return EmptyAccountId();
+ }
AccountId result(EmptyAccountId());
// UserManager is usually NULL in unit tests.
- if (UserManager::IsInitialized() &&
- UserManager::Get()->GetPlatformKnownUserId(user_email, gaia_id,
- &result)) {
+ if (account_type == AccountType::UNKNOWN && UserManager::IsInitialized() &&
+ UserManager::Get()->GetPlatformKnownUserId(user_email, id, &result)) {
return result;
}
- // We can have several users with the same gaia_id but different e-mails.
- // The opposite case is not possible.
std::string stored_gaia_id;
+ std::string stored_obj_guid;
const std::string sanitized_email =
user_email.empty()
? std::string()
: gaia::CanonicalizeEmail(gaia::SanitizeEmail(user_email));
- if (!sanitized_email.empty() &&
- GetStringPref(AccountId::FromUserEmail(sanitized_email), kGAIAIdKey,
- &stored_gaia_id)) {
- if (!gaia_id.empty() && gaia_id != stored_gaia_id)
- LOG(ERROR) << "User gaia id has changed. Sync will not work.";
+ if (!sanitized_email.empty()) {
+ if (GetStringPref(AccountId::FromUserEmail(sanitized_email), kGAIAIdKey,
+ &stored_gaia_id)) {
+ if (!id.empty()) {
+ DCHECK(account_type == AccountType::GOOGLE);
+ if (id != stored_gaia_id)
+ LOG(ERROR) << "User gaia id has changed. Sync will not work.";
+ }
+
+ // gaia_id is associated with cryptohome.
+ return AccountId::FromUserEmailGaiaId(sanitized_email, stored_gaia_id);
+ }
- // gaia_id is associated with cryptohome.
- return AccountId::FromUserEmailGaiaId(sanitized_email, stored_gaia_id);
+ if (GetStringPref(AccountId::FromUserEmail(sanitized_email), kObjGuidKey,
+ &stored_obj_guid)) {
+ if (!id.empty()) {
+ DCHECK(account_type == AccountType::ACTIVE_DIRECTORY);
+ if (id != stored_obj_guid)
+ LOG(ERROR) << "User object guid has changed. Sync will not work.";
+ }
+
+ // obj_guid is associated with cryptohome.
+ return AccountId::AdFromUserEmailObjGuid(sanitized_email,
+ stored_obj_guid);
+ }
}
std::string stored_email;
- // GetStringPref() returns the first user record that matches
- // given ID. So we will get the first one if there are multiples.
- if (!gaia_id.empty() && GetStringPref(AccountId::FromGaiaId(gaia_id),
- kCanonicalEmail, &stored_email)) {
- return AccountId::FromUserEmailGaiaId(stored_email, gaia_id);
+ switch (account_type) {
+ case AccountType::GOOGLE:
+ if (GetStringPref(AccountId::FromGaiaId(id), kCanonicalEmail,
+ &stored_email)) {
+ return AccountId::FromUserEmailGaiaId(stored_email, id);
+ }
+ return AccountId::FromUserEmailGaiaId(sanitized_email, id);
+ case AccountType::ACTIVE_DIRECTORY:
+ if (GetStringPref(AccountId::AdFromObjGuid(id), kCanonicalEmail,
+ &stored_email)) {
+ return AccountId::AdFromUserEmailObjGuid(stored_email, id);
+ }
+ return AccountId::AdFromUserEmailObjGuid(sanitized_email, id);
+ case AccountType::UNKNOWN:
+ return AccountId::FromUserEmail(sanitized_email);
}
-
- return (gaia_id.empty()
- ? AccountId::FromUserEmail(user_email)
- : AccountId::FromUserEmailGaiaId(user_email, gaia_id));
+ NOTREACHED();
+ return EmptyAccountId();
}
std::vector<AccountId> GetKnownAccountIds() {
@@ -274,10 +338,30 @@ std::vector<AccountId> GetKnownAccountIds() {
if (known_users->GetDictionary(i, &element)) {
std::string email;
std::string gaia_id;
+ std::string obj_guid;
const bool has_email = element->GetString(kCanonicalEmail, &email);
const bool has_gaia_id = element->GetString(kGAIAIdKey, &gaia_id);
- if (has_email || has_gaia_id)
- result.push_back(AccountId::FromUserEmailGaiaId(email, gaia_id));
+ const bool has_obj_guid = element->GetString(kObjGuidKey, &obj_guid);
+ AccountType account_type = AccountType::GOOGLE;
+ std::string account_type_string;
+ if (element->GetString(kAccountTypeKey, &account_type_string)) {
+ account_type = AccountId::StringToAccountType(account_type_string);
+ }
+ switch (account_type) {
+ case AccountType::GOOGLE:
+ if (has_email || has_gaia_id) {
+ result.push_back(AccountId::FromUserEmailGaiaId(email, gaia_id));
+ }
+ break;
+ case AccountType::ACTIVE_DIRECTORY:
+ if (has_email && has_obj_guid) {
+ result.push_back(
+ AccountId::AdFromUserEmailObjGuid(email, obj_guid));
+ }
+ break;
+ default:
+ NOTREACHED() << "Unknown account type";
+ }
}
}
return result;
@@ -304,6 +388,23 @@ void SetGaiaIdMigrationStatusDone(const AccountId& account_id,
void UpdateGaiaID(const AccountId& account_id, const std::string& gaia_id) {
SetStringPref(account_id, kGAIAIdKey, gaia_id);
+ SetStringPref(account_id, kAccountTypeKey,
+ AccountId::AccountTypeToString(AccountType::GOOGLE));
+}
+
+void UpdateId(const AccountId& account_id) {
+ switch (account_id.GetAccountType()) {
+ case AccountType::GOOGLE:
+ SetStringPref(account_id, kGAIAIdKey, account_id.GetGaiaId());
+ break;
+ case AccountType::ACTIVE_DIRECTORY:
+ SetStringPref(account_id, kObjGuidKey, account_id.GetObjGuid());
+ break;
+ case AccountType::UNKNOWN:
+ return;
+ }
+ SetStringPref(account_id, kAccountTypeKey,
+ AccountId::AccountTypeToString(account_id.GetAccountType()));
}
bool FindGaiaID(const AccountId& account_id, std::string* out_value) {
diff --git a/chromium/components/user_manager/known_user.h b/chromium/components/user_manager/known_user.h
index 8a16cbe146f..245ed573f38 100644
--- a/chromium/components/user_manager/known_user.h
+++ b/chromium/components/user_manager/known_user.h
@@ -11,12 +11,11 @@
#include "components/user_manager/user_manager_export.h"
class AccountId;
+enum class AccountType;
class PrefRegistrySimple;
namespace base {
class DictionaryValue;
-class ListValue;
-class TaskRunner;
}
namespace user_manager {
@@ -76,7 +75,8 @@ std::vector<AccountId> USER_MANAGER_EXPORT GetKnownAccountIds();
// gaia_id.
// This is a temporary call while migrating to AccountId.
AccountId USER_MANAGER_EXPORT GetAccountId(const std::string& user_email,
- const std::string& gaia_id);
+ const std::string& id,
+ const AccountType& account_type);
// Returns true if |subsystem| data was migrated to GaiaId for the |account_id|.
bool USER_MANAGER_EXPORT GetGaiaIdMigrationStatus(const AccountId& account_id,
@@ -93,6 +93,10 @@ SetGaiaIdMigrationStatusDone(const AccountId& account_id,
void USER_MANAGER_EXPORT UpdateGaiaID(const AccountId& account_id,
const std::string& gaia_id);
+// Updates |account_id.account_type_| and |account_id.GetGaiaId()| or
+// |account_id.GetObjGuid()| for user with |account_id|.
+void USER_MANAGER_EXPORT UpdateId(const AccountId& account_id);
+
// Find GAIA ID for user with |account_id|, fill in |out_value| and return
// true
// if GAIA ID was found or false otherwise.
diff --git a/chromium/components/user_manager/user.cc b/chromium/components/user_manager/user.cc
index 3cd2d49c3d5..11aba12cb85 100644
--- a/chromium/components/user_manager/user.cc
+++ b/chromium/components/user_manager/user.cc
@@ -53,6 +53,15 @@ class RegularUser : public User {
DISALLOW_COPY_AND_ASSIGN(RegularUser);
};
+class ActiveDirectoryUser : public RegularUser {
+ public:
+ explicit ActiveDirectoryUser(const AccountId& account_id);
+ ~ActiveDirectoryUser() override;
+ // Overridden from User:
+ UserType GetType() const override;
+ bool CanSyncImage() const override;
+};
+
class GuestUser : public User {
public:
explicit GuestUser(const AccountId& guest_account_id);
@@ -169,6 +178,10 @@ bool User::HasGaiaAccount() const {
return TypeHasGaiaAccount(GetType());
}
+bool User::IsActiveDirectoryUser() const {
+ return GetType() == user_manager::USER_TYPE_ACTIVE_DIRECTORY;
+}
+
bool User::IsSupervised() const {
UserType type = GetType();
return type == USER_TYPE_SUPERVISED ||
@@ -223,6 +236,8 @@ bool User::IsDeviceLocalAccount() const {
}
User* User::CreateRegularUser(const AccountId& account_id) {
+ if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY)
+ return new ActiveDirectoryUser(account_id);
return new RegularUser(account_id);
}
@@ -271,14 +286,27 @@ void User::SetStubImage(std::unique_ptr<UserImage> stub_user_image,
image_is_loading_ = is_loading;
}
+UserType ActiveDirectoryUser::GetType() const {
+ return user_manager::USER_TYPE_ACTIVE_DIRECTORY;
+}
+
+bool ActiveDirectoryUser::CanSyncImage() const {
+ return false;
+}
+
RegularUser::RegularUser(const AccountId& account_id) : User(account_id) {
set_can_lock(true);
set_display_email(account_id.GetUserEmail());
}
+ActiveDirectoryUser::ActiveDirectoryUser(const AccountId& account_id)
+ : RegularUser(account_id) {}
+
RegularUser::~RegularUser() {
}
+ActiveDirectoryUser::~ActiveDirectoryUser() {}
+
UserType RegularUser::GetType() const {
return is_child_ ? user_manager::USER_TYPE_CHILD :
user_manager::USER_TYPE_REGULAR;
@@ -376,8 +404,8 @@ UserType PublicAccountUser::GetType() const {
}
bool User::has_gaia_account() const {
- static_assert(user_manager::NUM_USER_TYPES == 8,
- "NUM_USER_TYPES should equal 8");
+ static_assert(user_manager::NUM_USER_TYPES == 9,
+ "NUM_USER_TYPES should equal 9");
switch (GetType()) {
case user_manager::USER_TYPE_REGULAR:
case user_manager::USER_TYPE_CHILD:
@@ -387,6 +415,7 @@ bool User::has_gaia_account() const {
case user_manager::USER_TYPE_SUPERVISED:
case user_manager::USER_TYPE_KIOSK_APP:
case user_manager::USER_TYPE_ARC_KIOSK_APP:
+ case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
return false;
default:
NOTREACHED();
diff --git a/chromium/components/user_manager/user.h b/chromium/components/user_manager/user.h
index be6b8eaaa9b..26c52599632 100644
--- a/chromium/components/user_manager/user.h
+++ b/chromium/components/user_manager/user.h
@@ -78,7 +78,10 @@ class USER_MANAGER_EXPORT User : public UserInfo {
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.
- WALLPAPER_TYPE_COUNT = 7
+ 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.
@@ -104,6 +107,9 @@ class USER_MANAGER_EXPORT User : public UserInfo {
// USER_TYPE_REGULAR and USER_TYPE_CHILD.
virtual bool HasGaiaAccount() const;
+ // Returns true if it's Active Directory user.
+ virtual bool IsActiveDirectoryUser() const;
+
// Returns true if user is supervised.
virtual bool IsSupervised() const;
@@ -135,9 +141,14 @@ class USER_MANAGER_EXPORT User : public UserInfo {
int image_index() const { return image_index_; }
bool has_image_bytes() const { return user_image_->has_image_bytes(); }
// Returns bytes representation of static user image for WebUI.
- const UserImage::Bytes& image_bytes() const {
+ scoped_refptr<base::RefCountedBytes> image_bytes() const {
return user_image_->image_bytes();
}
+ // Returns image format of the bytes representation of static user image
+ // for WebUI.
+ UserImage::ImageFormat image_format() const {
+ return user_image_->image_format();
+ }
// Whether |user_image_| contains data in format that is considered safe to
// decode in sensitive environment (on Login screen).
diff --git a/chromium/components/user_manager/user_image/user_image.cc b/chromium/components/user_manager/user_image/user_image.cc
index c06c847405f..23c763ff733 100644
--- a/chromium/components/user_manager/user_image/user_image.cc
+++ b/chromium/components/user_manager/user_image/user_image.cc
@@ -8,6 +8,8 @@
#include "base/trace_event/trace_event.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/geometry/size.h"
namespace user_manager {
@@ -19,58 +21,76 @@ const int kDefaultEncodingQuality = 90;
} // namespace
// static
-std::unique_ptr<UserImage::Bytes> UserImage::Encode(const SkBitmap& bitmap) {
+scoped_refptr<base::RefCountedBytes> UserImage::Encode(
+ const SkBitmap& bitmap,
+ ImageFormat image_format) {
TRACE_EVENT2("oobe", "UserImage::Encode",
"width", bitmap.width(), "height", bitmap.height());
SkAutoLockPixels lock_bitmap(bitmap);
- std::unique_ptr<Bytes> output(new Bytes);
- if (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.get())) {
- return output;
+ 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)) {
+ return base::RefCountedBytes::TakeVector(&output);
+ }
+ } else if (image_format == FORMAT_PNG) {
+ if (gfx::PNGCodec::Encode(
+ bitmap_data,
+ gfx::PNGCodec::FORMAT_SkBitmap,
+ gfx::Size(bitmap.width(), bitmap.height()),
+ bitmap.width() * bitmap.bytesPerPixel(),
+ false, // discard_transparency
+ std::vector<gfx::PNGCodec::Comment>(), &output)) {
+ return base::RefCountedBytes::TakeVector(&output);
+ }
} else {
- return nullptr;
+ LOG(FATAL) << "Invalid image format: " << image_format;
}
+ return nullptr;
}
// static
std::unique_ptr<UserImage> UserImage::CreateAndEncode(
- const gfx::ImageSkia& image) {
+ const gfx::ImageSkia& image,
+ ImageFormat image_format) {
if (image.isNull())
return base::WrapUnique(new UserImage);
- std::unique_ptr<Bytes> image_bytes = Encode(*image.bitmap());
+ scoped_refptr<base::RefCountedBytes> image_bytes = Encode(*image.bitmap(),
+ image_format);
if (image_bytes) {
- // TODO(crbug.com/593251): Remove the data copy via |image_bytes|.
- std::unique_ptr<UserImage> result(new UserImage(image, *image_bytes));
+ std::unique_ptr<UserImage> result(
+ new UserImage(image, image_bytes, image_format));
result->MarkAsSafe();
return result;
}
return base::WrapUnique(new UserImage(image));
}
-UserImage::UserImage()
- : has_image_bytes_(false),
- is_safe_format_(false) {
+// static
+UserImage::ImageFormat UserImage::ChooseImageFormat(const SkBitmap& bitmap) {
+ return SkBitmap::ComputeIsOpaque(bitmap) ? FORMAT_JPEG : FORMAT_PNG;
+}
+
+UserImage::UserImage() {
}
UserImage::UserImage(const gfx::ImageSkia& image)
- : image_(image),
- has_image_bytes_(false),
- is_safe_format_(false) {
+ : image_(image) {
}
UserImage::UserImage(const gfx::ImageSkia& image,
- const Bytes& image_bytes)
+ scoped_refptr<base::RefCountedBytes> image_bytes,
+ ImageFormat image_format)
: image_(image),
- has_image_bytes_(false),
- is_safe_format_(false) {
- has_image_bytes_ = true;
- image_bytes_ = image_bytes;
+ image_bytes_(image_bytes),
+ image_format_(image_format) {
}
UserImage::~UserImage() {}
diff --git a/chromium/components/user_manager/user_image/user_image.h b/chromium/components/user_manager/user_image/user_image.h
index 7d822c4d3bf..55e4978d143 100644
--- a/chromium/components/user_manager/user_image/user_image.h
+++ b/chromium/components/user_manager/user_image/user_image.h
@@ -10,6 +10,7 @@
#include <vector>
#include "base/files/file_path.h"
+#include "base/memory/ref_counted_memory.h"
#include "components/user_manager/user_manager_export.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
@@ -21,19 +22,31 @@ namespace user_manager {
// profile images and user wallpapers.
class USER_MANAGER_EXPORT UserImage {
public:
- // Used to store bytes representation for WebUI.
- // TODO(ivankr): replace with RefCountedMemory to prevent copying.
- typedef std::vector<unsigned char> Bytes;
-
- // Encodes the given bitmap to bytes representation for WebUI. Returns null
- // on failure.
- static std::unique_ptr<Bytes> Encode(const SkBitmap& bitmap);
+ // The format of the user image's bytes representation. PNG can support
+ // transparent background that's not supported with JPEG.
+ enum ImageFormat {
+ FORMAT_JPEG,
+ FORMAT_PNG,
+ FORMAT_UNKNOWN,
+ };
+
+ // Encodes the given bitmap to bytes representation in |image_format| for
+ // WebUI. Returns nullptr on failure.
+ static scoped_refptr<base::RefCountedBytes> Encode(const SkBitmap& bitmap,
+ ImageFormat image_format);
// Creates a new instance from a given still frame and tries to encode it
- // to bytes representation for WebUI. Always returns a non-null result.
+ // to bytes representation in |image_format| for WebUI. Always returns a
+ // non-null result.
// TODO(ivankr): remove eventually.
static std::unique_ptr<UserImage> CreateAndEncode(
- const gfx::ImageSkia& image);
+ const gfx::ImageSkia& image,
+ ImageFormat image_format);
+
+ // Choose the image format suitable for the given bitmap. Returns
+ // FORMAT_PNG if the bitmap contains transparent/translucent
+ // pixels. Otherwise, returns FORMAT_JPEG.
+ static ImageFormat ChooseImageFormat(const SkBitmap& bitmap);
// Create instance with an empty still frame and no bytes
// representation for WebUI.
@@ -45,16 +58,21 @@ class USER_MANAGER_EXPORT UserImage {
// Creates a new instance from a given still frame and bytes
// representation for WebUI.
- // TODO(crbug.com/593251): Remove the data copy via |image_bytes|.
- UserImage(const gfx::ImageSkia& image, const Bytes& image_bytes);
+ UserImage(const gfx::ImageSkia& image,
+ scoped_refptr<base::RefCountedBytes> image_bytes,
+ ImageFormat image_format);
virtual ~UserImage();
const gfx::ImageSkia& image() const { return image_; }
// Optional bytes representation of the still image for WebUI.
- bool has_image_bytes() const { return has_image_bytes_; }
- const Bytes& image_bytes() const { return image_bytes_; }
+ bool has_image_bytes() const { return image_bytes_ != nullptr; }
+ scoped_refptr<base::RefCountedBytes> image_bytes() const {
+ return image_bytes_;
+ }
+ // Format of the bytes representation.
+ ImageFormat image_format() const { return image_format_; }
// URL from which this image was originally downloaded, if any.
void set_url(const GURL& url) { url_ = url; }
@@ -72,13 +90,13 @@ class USER_MANAGER_EXPORT UserImage {
private:
gfx::ImageSkia image_;
- bool has_image_bytes_;
- Bytes image_bytes_;
+ scoped_refptr<base::RefCountedBytes> image_bytes_;
GURL url_;
// If image was loaded from the local file, file path is stored here.
base::FilePath file_path_;
- bool is_safe_format_;
+ bool is_safe_format_ = false;
+ ImageFormat image_format_ = FORMAT_UNKNOWN;
DISALLOW_COPY_AND_ASSIGN(UserImage);
};
diff --git a/chromium/components/user_manager/user_manager.cc b/chromium/components/user_manager/user_manager.cc
index b0cd217dd43..22b33c0ae91 100644
--- a/chromium/components/user_manager/user_manager.cc
+++ b/chromium/components/user_manager/user_manager.cc
@@ -9,13 +9,19 @@
namespace user_manager {
-UserManager* UserManager::instance = NULL;
+UserManager* UserManager::instance = nullptr;
-UserManager::Observer::~Observer() {
-}
+UserManager::Observer::~Observer() = default;
-void UserManager::Observer::LocalStateChanged(UserManager* user_manager) {
-}
+void UserManager::Observer::LocalStateChanged(UserManager* user_manager) {}
+
+void UserManager::Observer::OnUserImageChanged(const User& user) {}
+
+void UserManager::Observer::OnUserProfileImageUpdateFailed(const User& user) {}
+
+void UserManager::Observer::OnUserProfileImageUpdated(
+ const User& user,
+ const gfx::ImageSkia& profile_image) {}
void UserManager::UserSessionStateObserver::ActiveUserChanged(
const User* active_user) {
@@ -57,7 +63,7 @@ bool UserManager::IsInitialized() {
void UserManager::Destroy() {
DCHECK(UserManager::instance == this);
- UserManager::SetInstance(NULL);
+ UserManager::SetInstance(nullptr);
}
// static
diff --git a/chromium/components/user_manager/user_manager.h b/chromium/components/user_manager/user_manager.h
index ed4a524087d..fbf4cc185bf 100644
--- a/chromium/components/user_manager/user_manager.h
+++ b/chromium/components/user_manager/user_manager.h
@@ -17,19 +17,10 @@
class AccountId;
class PrefService;
-namespace base {
-class DictionaryValue;
-}
-
namespace chromeos {
-class LoginState;
class ScopedUserManagerEnabler;
}
-namespace cryptohome {
-class AsyncMethodCaller;
-}
-
namespace user_manager {
class RemoveUserDelegate;
@@ -48,6 +39,18 @@ class USER_MANAGER_EXPORT UserManager {
// Called when the local state preferences is changed.
virtual void LocalStateChanged(UserManager* user_manager);
+ // Called when the image of the given user is changed.
+ virtual void OnUserImageChanged(const User& user);
+
+ // Called when the profile image download for the given user fails or
+ // user has the default profile image or no porfile image at all.
+ virtual void OnUserProfileImageUpdateFailed(const User& user);
+
+ // Called when the profile image for the given user is downloaded.
+ // |profile_image| contains the downloaded profile image.
+ virtual void OnUserProfileImageUpdated(const User& user,
+ const gfx::ImageSkia& profile_image);
+
protected:
virtual ~Observer();
};
@@ -309,6 +312,11 @@ class USER_MANAGER_EXPORT UserManager {
virtual void RemoveSessionStateObserver(UserSessionStateObserver* obs) = 0;
virtual void NotifyLocalStateChanged() = 0;
+ virtual void NotifyUserImageChanged(const User& user) = 0;
+ virtual void NotifyUserProfileImageUpdateFailed(const User& user) = 0;
+ virtual void NotifyUserProfileImageUpdated(
+ const User& user,
+ const gfx::ImageSkia& profile_image) = 0;
// Changes the child status and notifies observers.
virtual void ChangeUserChildStatus(User* user, bool is_child) = 0;
diff --git a/chromium/components/user_manager/user_manager_base.cc b/chromium/components/user_manager/user_manager_base.cc
index 2c7b3ffddaf..5144ed5cfe7 100644
--- a/chromium/components/user_manager/user_manager_base.cc
+++ b/chromium/components/user_manager/user_manager_base.cc
@@ -182,14 +182,18 @@ void UserManagerBase::UserLoggedIn(const AccountId& account_id,
active_user_->set_is_active(true);
active_user_->set_username_hash(username_hash);
- // Place user who just signed in to the top of the logged in users.
- logged_in_users_.insert(logged_in_users_.begin(), active_user_);
+ logged_in_users_.push_back(active_user_);
SetLRUUser(active_user_);
if (!primary_user_) {
primary_user_ = active_user_;
if (primary_user_->HasGaiaAccount())
SendGaiaUserLoginMetrics(account_id);
+ } else if (primary_user_ != active_user_) {
+ // This is only needed for tests where a new user session is created
+ // for non-existent user.
+ SetIsCurrentUserNew(true);
+ NotifyUserAddedToSession(active_user_, true /* user switch pending */);
}
UMA_HISTOGRAM_ENUMERATION(
@@ -490,7 +494,8 @@ void UserManagerBase::ParseUserList(const base::ListValue& users_list,
continue;
}
- const AccountId account_id = known_user::GetAccountId(email, std::string());
+ const AccountId account_id = known_user::GetAccountId(
+ email, std::string() /* id */, AccountType::UNKNOWN);
if (existing_users.find(account_id) != existing_users.end() ||
!users_set->insert(account_id).second) {
@@ -669,9 +674,31 @@ void UserManagerBase::NotifyLocalStateChanged() {
observer.LocalStateChanged(this);
}
+void UserManagerBase::NotifyUserImageChanged(const User& user) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ for (auto& observer : observer_list_)
+ observer.OnUserImageChanged(user);
+}
+
+void UserManagerBase::NotifyUserProfileImageUpdateFailed(const User& user) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ for (auto& observer : observer_list_)
+ observer.OnUserProfileImageUpdateFailed(user);
+}
+
+void UserManagerBase::NotifyUserProfileImageUpdated(
+ const User& user,
+ const gfx::ImageSkia& profile_image) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ for (auto& observer : observer_list_)
+ observer.OnUserProfileImageUpdated(user, profile_image);
+}
+
bool UserManagerBase::CanUserBeRemoved(const User* user) const {
// Only regular and supervised users are allowed to be manually removed.
- if (!user || !(user->HasGaiaAccount() || user->IsSupervised()))
+ if (!user ||
+ !(user->HasGaiaAccount() || user->IsSupervised() ||
+ user->IsActiveDirectoryUser()))
return false;
// Sanity check: we must not remove single user unless it's an enterprise
@@ -952,7 +979,8 @@ User* UserManagerBase::RemoveRegularOrSupervisedUserFromList(
user = *it;
it = users_.erase(it);
} else {
- if ((*it)->HasGaiaAccount() || (*it)->IsSupervised()) {
+ if ((*it)->HasGaiaAccount() || (*it)->IsSupervised() ||
+ (*it)->IsActiveDirectoryUser()) {
const std::string user_email = (*it)->GetAccountId().GetUserEmail();
prefs_users_update->AppendString(user_email);
}
diff --git a/chromium/components/user_manager/user_manager_base.h b/chromium/components/user_manager/user_manager_base.h
index 3c479f17b1f..b30015f0ecc 100644
--- a/chromium/components/user_manager/user_manager_base.h
+++ b/chromium/components/user_manager/user_manager_base.h
@@ -22,11 +22,9 @@
#include "components/user_manager/user_manager_export.h"
#include "components/user_manager/user_type.h"
-class PrefService;
class PrefRegistrySimple;
namespace base {
-class DictionaryValue;
class ListValue;
class TaskRunner;
}
@@ -106,6 +104,11 @@ class USER_MANAGER_EXPORT UserManagerBase : public UserManager {
void RemoveSessionStateObserver(
UserManager::UserSessionStateObserver* obs) override;
void NotifyLocalStateChanged() override;
+ void NotifyUserImageChanged(const User& user) override;
+ void NotifyUserProfileImageUpdateFailed(const User& user) override;
+ void NotifyUserProfileImageUpdated(
+ const User& user,
+ const gfx::ImageSkia& profile_image) override;
void ChangeUserChildStatus(User* user, bool is_child) override;
void Initialize() override;
diff --git a/chromium/components/user_manager/user_type.h b/chromium/components/user_manager/user_type.h
index 8509eac14b3..9e2352d7c7d 100644
--- a/chromium/components/user_manager/user_type.h
+++ b/chromium/components/user_manager/user_type.h
@@ -25,8 +25,10 @@ typedef enum {
USER_TYPE_CHILD = 6,
// Android app in kiosk mode, logs in without authentication.
USER_TYPE_ARC_KIOSK_APP = 7,
+ // Active Directory user. Authenticates against Active Directory server.
+ USER_TYPE_ACTIVE_DIRECTORY = 8,
// Maximum histogram value.
- NUM_USER_TYPES = 8
+ NUM_USER_TYPES = 9
} UserType;
} // namespace user_manager
diff --git a/chromium/components/user_prefs/tracked/pref_hash_calculator_unittest.cc b/chromium/components/user_prefs/tracked/pref_hash_calculator_unittest.cc
index 1b3ecd793f5..4d29146ada9 100644
--- a/chromium/components/user_prefs/tracked/pref_hash_calculator_unittest.cc
+++ b/chromium/components/user_prefs/tracked/pref_hash_calculator_unittest.cc
@@ -111,15 +111,15 @@ TEST(PrefHashCalculatorTest, CatchHashChanges) {
list_value->AppendInteger(100);
list_value->AppendDouble(1.0);
- ASSERT_EQ(base::Value::TYPE_NULL, null_value->GetType());
- ASSERT_EQ(base::Value::TYPE_BOOLEAN, bool_value->GetType());
- ASSERT_EQ(base::Value::TYPE_INTEGER, int_value->GetType());
- ASSERT_EQ(base::Value::TYPE_DOUBLE, double_value->GetType());
- ASSERT_EQ(base::Value::TYPE_STRING, string_value->GetType());
- ASSERT_EQ(base::Value::TYPE_DICTIONARY, dict_value->GetType());
- ASSERT_EQ(base::Value::TYPE_LIST, list_value->GetType());
-
- // Test every value type independently. Intentionally omits TYPE_BINARY which
+ ASSERT_EQ(base::Value::Type::NONE, null_value->GetType());
+ ASSERT_EQ(base::Value::Type::BOOLEAN, bool_value->GetType());
+ ASSERT_EQ(base::Value::Type::INTEGER, int_value->GetType());
+ ASSERT_EQ(base::Value::Type::DOUBLE, double_value->GetType());
+ ASSERT_EQ(base::Value::Type::STRING, string_value->GetType());
+ ASSERT_EQ(base::Value::Type::DICTIONARY, dict_value->GetType());
+ ASSERT_EQ(base::Value::Type::LIST, list_value->GetType());
+
+ // Test every value type independently. Intentionally omits Type::BINARY which
// isn't even allowed in JSONWriter's input.
static const char kExpectedNullValue[] =
"82A9F3BBC7F9FF84C76B033C854E79EEB162783FA7B3E99FF9372FA8E12C44F7";
diff --git a/chromium/components/user_prefs/tracked/pref_hash_filter.cc b/chromium/components/user_prefs/tracked/pref_hash_filter.cc
index 1101080cd76..6ae59af8e21 100644
--- a/chromium/components/user_prefs/tracked/pref_hash_filter.cc
+++ b/chromium/components/user_prefs/tracked/pref_hash_filter.cc
@@ -34,12 +34,10 @@ void CleanupDeprecatedTrackedPreferences(
// Add deprecated previously tracked preferences below for them to be cleaned
// up from both the pref files and the hash store.
static const char* const kDeprecatedTrackedPreferences[] = {
- // TODO(grt): Remove in M44+.
- "safebrowsing.incident_report_sent",
- // TODO(mad): Remove in M48+.
- "software_reporter.prompt_reason",
- // TODO(zea): Remove in M52+,
- "sync.remaining_rollback_tries"
+ // TODO(a-v-y): Remove in M60+,
+ "default_search_provider.search_url",
+ "default_search_provider.name",
+ "default_search_provider.keyword"
};
for (size_t i = 0; i < arraysize(kDeprecatedTrackedPreferences); ++i) {
@@ -101,8 +99,10 @@ PrefHashFilter::PrefHashFilter(
}
DCHECK(tracked_preference);
- bool is_new =
- tracked_paths_.add(metadata.name, std::move(tracked_preference)).second;
+ bool is_new = tracked_paths_
+ .insert(std::make_pair(metadata.name,
+ std::move(tracked_preference)))
+ .second;
DCHECK(is_new);
}
}
@@ -148,10 +148,9 @@ void PrefHashFilter::Initialize(base::DictionaryValue* pref_store_contents) {
DictionaryHashStoreContents dictionary_contents(pref_store_contents);
std::unique_ptr<PrefHashStoreTransaction> hash_store_transaction(
pref_hash_store_->BeginTransaction(&dictionary_contents));
- for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin();
- it != tracked_paths_.end(); ++it) {
+ for (auto it = tracked_paths_.begin(); it != tracked_paths_.end(); ++it) {
const std::string& initialized_path = it->first;
- const TrackedPreference* initialized_preference = it->second;
+ const TrackedPreference* initialized_preference = it->second.get();
const base::Value* value = nullptr;
pref_store_contents->Get(initialized_path, &value);
initialized_preference->OnNewValue(value, hash_store_transaction.get());
@@ -161,9 +160,9 @@ void PrefHashFilter::Initialize(base::DictionaryValue* pref_store_contents) {
// Marks |path| has having changed if it is part of |tracked_paths_|. A new hash
// will be stored for it the next time FilterSerializeData() is invoked.
void PrefHashFilter::FilterUpdate(const std::string& path) {
- TrackedPreferencesMap::const_iterator it = tracked_paths_.find(path);
+ auto it = tracked_paths_.find(path);
if (it != tracked_paths_.end())
- changed_paths_.insert(std::make_pair(path, it->second));
+ changed_paths_.insert(std::make_pair(path, it->second.get()));
}
// Updates the stored hashes for |changed_paths_| before serializing data to
@@ -236,8 +235,7 @@ void PrefHashFilter::FinalizeFilterOnLoad(
hash_store_transaction->IsSuperMACValid());
}
- for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin();
- it != tracked_paths_.end(); ++it) {
+ for (auto it = tracked_paths_.begin(); it != tracked_paths_.end(); ++it) {
if (it->second->EnforceAndReport(
pref_store_contents.get(), hash_store_transaction.get(),
external_validation_hash_store_transaction.get())) {
diff --git a/chromium/components/user_prefs/tracked/pref_hash_filter.h b/chromium/components/user_prefs/tracked/pref_hash_filter.h
index 07c92db9506..3f68011c161 100644
--- a/chromium/components/user_prefs/tracked/pref_hash_filter.h
+++ b/chromium/components/user_prefs/tracked/pref_hash_filter.h
@@ -10,11 +10,11 @@
#include <map>
#include <memory>
#include <set>
+#include <unordered_map>
#include <vector>
#include "base/callback.h"
#include "base/compiler_specific.h"
-#include "base/containers/scoped_ptr_hash_map.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/optional.h"
@@ -24,13 +24,11 @@
class PrefHashStore;
class PrefService;
-class PrefStore;
class TrackedPreferenceValidationDelegate;
namespace base {
class DictionaryValue;
class Time;
-class Value;
} // namespace base
namespace user_prefs {
@@ -146,13 +144,12 @@ class PrefHashFilter : public InterceptablePrefFilter {
// A map of paths to TrackedPreferences; this map owns this individual
// TrackedPreference objects.
- typedef base::ScopedPtrHashMap<std::string,
- std::unique_ptr<TrackedPreference>>
- TrackedPreferencesMap;
+ using TrackedPreferencesMap =
+ std::unordered_map<std::string, std::unique_ptr<TrackedPreference>>;
// A map from changed paths to their corresponding TrackedPreferences (which
// aren't owned by this map).
- typedef std::map<std::string, const TrackedPreference*> ChangedPathsMap;
+ using ChangedPathsMap = std::map<std::string, const TrackedPreference*>;
std::unique_ptr<PrefHashStore> pref_hash_store_;
diff --git a/chromium/components/user_prefs/tracked/tracked_preferences_migration.h b/chromium/components/user_prefs/tracked/tracked_preferences_migration.h
index 3c96668a88d..290e5fbdb65 100644
--- a/chromium/components/user_prefs/tracked/tracked_preferences_migration.h
+++ b/chromium/components/user_prefs/tracked/tracked_preferences_migration.h
@@ -12,7 +12,6 @@
#include "base/callback_forward.h"
#include "components/user_prefs/tracked/pref_hash_store.h"
-class HashStoreContents;
class InterceptablePrefFilter;
class PrefHashStore;
diff --git a/chromium/components/variations/BUILD.gn b/chromium/components/variations/BUILD.gn
index 9a3e5eb10b9..bc7008a0fb3 100644
--- a/chromium/components/variations/BUILD.gn
+++ b/chromium/components/variations/BUILD.gn
@@ -18,6 +18,8 @@ static_library("variations") {
"android/variations_seed_bridge.h",
"caching_permuted_entropy_provider.cc",
"caching_permuted_entropy_provider.h",
+ "child_process_field_trial_syncer.cc",
+ "child_process_field_trial_syncer.h",
"entropy_provider.cc",
"entropy_provider.h",
"experiment_labels.cc",
@@ -91,11 +93,28 @@ if (is_android) {
}
}
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "variations_params_manager.cc",
+ "variations_params_manager.h",
+ ]
+
+ public_deps = [
+ ":variations",
+ ]
+
+ deps = [
+ "//base/test:test_support",
+ ]
+}
+
source_set("unit_tests") {
testonly = true
sources = [
"active_field_trials_unittest.cc",
"caching_permuted_entropy_provider_unittest.cc",
+ "child_process_field_trial_syncer_unittest.cc",
"entropy_provider_unittest.cc",
"experiment_labels_unittest.cc",
"metrics_util_unittest.cc",
diff --git a/chromium/components/variations/android/BUILD.gn b/chromium/components/variations/android/BUILD.gn
index 641b584de1c..6ad49364d5b 100644
--- a/chromium/components/variations/android/BUILD.gn
+++ b/chromium/components/variations/android/BUILD.gn
@@ -7,7 +7,7 @@ import("//build/config/android/rules.gni")
android_library("variations_java") {
deps = [
"//base:base_java",
- "//third_party/android_tools:android_support_v4_java",
+ "//third_party/android_tools:android_support_core_utils_java",
]
java_files = [
"java/src/org/chromium/components/variations/VariationsAssociatedData.java",
diff --git a/chromium/components/variations/child_process_field_trial_syncer.cc b/chromium/components/variations/child_process_field_trial_syncer.cc
new file mode 100644
index 00000000000..49014e08109
--- /dev/null
+++ b/chromium/components/variations/child_process_field_trial_syncer.cc
@@ -0,0 +1,66 @@
+// 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/variations/child_process_field_trial_syncer.h"
+
+#include <set>
+#include <utility>
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "components/variations/variations_util.h"
+
+namespace variations {
+
+ChildProcessFieldTrialSyncer::ChildProcessFieldTrialSyncer(
+ base::FieldTrialList::Observer* observer)
+ : observer_(observer) {}
+
+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;
+
+ // Set up initial set of crash dump data for field trials in this process.
+ SetVariationListCrashKeys();
+
+ // Listen for field trial activations to report them to the browser.
+ base::FieldTrialList::AddObserver(observer_);
+
+ // Some field trials may have been activated before this point. Notify the
+ // browser of these activations now. To detect these, take the set difference
+ // of currently active trials with the initially active trials.
+ base::FieldTrial::ActiveGroups initially_active_trials;
+ base::FieldTrialList::GetInitiallyActiveFieldTrials(command_line,
+ &initially_active_trials);
+ std::set<std::string> initially_active_trials_set;
+ for (const auto& entry : initially_active_trials) {
+ initially_active_trials_set.insert(std::move(entry.trial_name));
+ }
+
+ base::FieldTrial::ActiveGroups current_active_trials;
+ base::FieldTrialList::GetActiveFieldTrialGroups(&current_active_trials);
+ for (const auto& trial : current_active_trials) {
+ if (!base::ContainsKey(initially_active_trials_set, trial.trial_name))
+ observer_->OnFieldTrialGroupFinalized(trial.trial_name, trial.group_name);
+ }
+}
+
+void ChildProcessFieldTrialSyncer::OnSetFieldTrialGroup(
+ const std::string& trial_name,
+ const std::string& group_name) {
+ base::FieldTrial* trial =
+ base::FieldTrialList::CreateFieldTrial(trial_name, group_name);
+ // Ensure the trial is marked as "used" by calling group() on it if it is
+ // marked as activated.
+ trial->group();
+ SetVariationListCrashKeys();
+}
+
+} // namespace variations
diff --git a/chromium/components/variations/child_process_field_trial_syncer.h b/chromium/components/variations/child_process_field_trial_syncer.h
new file mode 100644
index 00000000000..1a93d368df9
--- /dev/null
+++ b/chromium/components/variations/child_process_field_trial_syncer.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_VARIATIONS_FIELD_TRIAL_SYNCER_H_
+#define COMPONENTS_VARIATIONS_FIELD_TRIAL_SYNCER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/metrics/field_trial.h"
+
+namespace base {
+class CommandLine;
+}
+
+namespace variations {
+
+// ChildProcessFieldTrialSyncer is a utility class that's responsible for
+// syncing the "activated" state of field trials between browser and child
+// processes. Specifically, when a field trial is activated in the browser, it
+// also activates it in the child process and when a field trial is activated
+// in the child process, it notifies the browser process to activate it.
+class ChildProcessFieldTrialSyncer {
+ public:
+ // ChildProcessFieldTrialSyncer constructor taking an externally owned
+ // |observer| param that's responsible for sending IPCs to the browser process
+ // when a trial is activated. The |observer| must outlive this object.
+ explicit ChildProcessFieldTrialSyncer(
+ base::FieldTrialList::Observer* observer);
+ ~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);
+
+ // Handler for messages from the browser process notifying the child process
+ // that a field trial was activated.
+ void OnSetFieldTrialGroup(const std::string& trial_name,
+ const std::string& group_name);
+
+ private:
+ base::FieldTrialList::Observer* const observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChildProcessFieldTrialSyncer);
+};
+
+} // namespace variations
+
+#endif // COMPONENTS_VARIATIONS_FIELD_TRIAL_SYNCER_H_
diff --git a/chromium/components/variations/child_process_field_trial_syncer_unittest.cc b/chromium/components/variations/child_process_field_trial_syncer_unittest.cc
new file mode 100644
index 00000000000..49b574ce8f6
--- /dev/null
+++ b/chromium/components/variations/child_process_field_trial_syncer_unittest.cc
@@ -0,0 +1,98 @@
+// 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/variations/child_process_field_trial_syncer.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/field_trial.h"
+#include "base/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace variations {
+
+namespace {
+
+// TestFieldTrialObserver to listen to be notified by the child process syncer.
+class TestFieldTrialObserver : public base::FieldTrialList::Observer {
+ public:
+ TestFieldTrialObserver() {}
+ ~TestFieldTrialObserver() override {}
+
+ // base::FieldTrial::Observer:
+ void OnFieldTrialGroupFinalized(const std::string& trial_name,
+ const std::string& group_name) override {
+ observed_entries_.push_back(std::make_pair(trial_name, group_name));
+ }
+
+ size_t observed_entries_count() const { return observed_entries_.size(); }
+
+ std::pair<std::string, std::string> get_observed_entry(int i) const {
+ return observed_entries_[i];
+ }
+
+ private:
+ std::vector<std::pair<std::string, std::string>> observed_entries_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFieldTrialObserver);
+};
+
+// Needed because make_pair("a", "b") doesn't convert to std::string pair.
+std::pair<std::string, std::string> MakeStringPair(const std::string& a,
+ const std::string& b) {
+ return std::make_pair(a, b);
+}
+
+} // namespace
+
+TEST(ChildProcessFieldTrialSyncerTest, FieldTrialState) {
+ base::MessageLoop message_loop;
+ base::FieldTrialList field_trial_list(nullptr);
+ // We don't use the descriptor here anyways so it's ok to pass -1.
+ base::FieldTrialList::CreateTrialsFromCommandLine(
+ *base::CommandLine::ForCurrentProcess(), "field_trial_handle_switch", -1);
+
+ base::FieldTrial* trial1 = base::FieldTrialList::CreateFieldTrial("A", "G1");
+ base::FieldTrial* trial2 = base::FieldTrialList::CreateFieldTrial("B", "G2");
+ base::FieldTrial* trial3 = base::FieldTrialList::CreateFieldTrial("C", "G3");
+ // Activate trial3 before command line is produced.
+ trial1->group();
+
+ std::string states_string;
+ base::FieldTrialList::AllStatesToString(&states_string);
+
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kForceFieldTrials, states_string);
+ EXPECT_EQ("*A/G1/B/G2/C/G3/", states_string);
+
+ // Active trial 2 before creating the syncer.
+ trial2->group();
+
+ TestFieldTrialObserver observer;
+ ChildProcessFieldTrialSyncer syncer(&observer);
+ syncer.InitFieldTrialObserving(*base::CommandLine::ForCurrentProcess(),
+ "single_process");
+
+ // 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
+ // command line and so its state shouldn't be notified.)
+ ASSERT_EQ(1U, observer.observed_entries_count());
+ EXPECT_EQ(MakeStringPair("B", "G2"), observer.get_observed_entry(0));
+
+ // Now, activate trial 3, which should also get reflected.
+ trial3->group();
+ // Notifications from field trial activation actually happen via posted tasks,
+ // so invoke the run loop.
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(2U, observer.observed_entries_count());
+ EXPECT_EQ(MakeStringPair("C", "G3"), observer.get_observed_entry(1));
+}
+
+} // namespace variations
diff --git a/chromium/components/variations/entropy_provider.cc b/chromium/components/variations/entropy_provider.cc
index d07277f7add..64c3cfc85da 100644
--- a/chromium/components/variations/entropy_provider.cc
+++ b/chromium/components/variations/entropy_provider.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
#include "base/sys_byteorder.h"
#include "components/variations/metrics_util.h"
@@ -87,7 +88,10 @@ double SHA1EntropyProvider::GetEntropyForTrial(
// it has been observed that this method does not result in a uniform
// distribution given the same |trial_name|. When using such a low entropy
// source, PermutedEntropyProvider should be used instead.
- std::string input(entropy_source_ + trial_name);
+ std::string input(entropy_source_);
+ input.append(randomization_seed == 0 ? trial_name : base::UintToString(
+ randomization_seed));
+
unsigned char sha1_hash[base::kSHA1Length];
base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
input.size(),
diff --git a/chromium/components/variations/entropy_provider.h b/chromium/components/variations/entropy_provider.h
index 27e73eb3564..41276019bf1 100644
--- a/chromium/components/variations/entropy_provider.h
+++ b/chromium/components/variations/entropy_provider.h
@@ -41,10 +41,10 @@ void PermuteMappingUsingRandomizationSeed(uint32_t randomization_seed,
} // namespace internal
-// SHA1EntropyProvider is an entropy provider suitable for high entropy
-// sources. It works by taking the first 64 bits of the SHA1 hash of the
-// entropy source concatenated with the trial name and using that for the
-// final entropy value.
+// SHA1EntropyProvider is an entropy provider suitable for high entropy sources.
+// It works by taking the first 64 bits of the SHA1 hash of the entropy source
+// concatenated with the trial name, or randomization seed and using that for
+// the final entropy value.
class SHA1EntropyProvider : public base::FieldTrial::EntropyProvider {
public:
// Creates a SHA1EntropyProvider with the given |entropy_source|, which
@@ -64,10 +64,10 @@ class SHA1EntropyProvider : public base::FieldTrial::EntropyProvider {
};
// PermutedEntropyProvider is an entropy provider suitable for low entropy
-// sources (below 16 bits). It uses the field trial name to generate a
-// permutation of a mapping array from an initial entropy value to a new value.
-// Note: This provider's performance is O(2^n), where n is the number of bits
-// in the entropy source.
+// sources (below 16 bits). It uses the field trial name or randomization seed
+// to generate a permutation of a mapping array from an initial entropy value to
+// a new value. Note: This provider's performance is O(2^n), where n is the
+// number of bits in the entropy source.
class PermutedEntropyProvider : public base::FieldTrial::EntropyProvider {
public:
// Creates a PermutedEntropyProvider with the given |low_entropy_source|,
diff --git a/chromium/components/variations/entropy_provider_unittest.cc b/chromium/components/variations/entropy_provider_unittest.cc
index cde08e0aed9..57aae6a1bb7 100644
--- a/chromium/components/variations/entropy_provider_unittest.cc
+++ b/chromium/components/variations/entropy_provider_unittest.cc
@@ -270,6 +270,33 @@ TEST(EntropyProviderTest, UseOneTimeRandomizationWithCustomSeedPermuted) {
EXPECT_EQ(trials[0]->group_name(), trials[1]->group_name());
}
+TEST(EntropyProviderTest, UseOneTimeRandomizationWithCustomSeedSHA1) {
+ // Ensures that two trials with different names but the same custom seed used
+ // for one time randomization produce the same group assignments.
+ base::FieldTrialList field_trial_list(
+ base::MakeUnique<SHA1EntropyProvider>("client_id"));
+ const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
+ const uint32_t kCustomSeed = 9001;
+ scoped_refptr<base::FieldTrial> trials[] = {
+ base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
+ "one", 100, "default", kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, NULL, NULL),
+ base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
+ "two", 100, "default", kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, NULL, NULL),
+ };
+
+ for (size_t i = 0; i < arraysize(trials); ++i) {
+ for (int j = 0; j < 100; ++j)
+ trials[i]->AppendGroup(std::string(), 1);
+ }
+
+ // Normally, these trials should produce different groups, but if the same
+ // custom seed is used, they should produce the same group assignment.
+ EXPECT_EQ(trials[0]->group(), trials[1]->group());
+ EXPECT_EQ(trials[0]->group_name(), trials[1]->group_name());
+}
+
TEST(EntropyProviderTest, SHA1Entropy) {
const double results[] = { GenerateSHA1Entropy("hi", "1"),
GenerateSHA1Entropy("there", "1") };
diff --git a/chromium/components/variations/processed_study.cc b/chromium/components/variations/processed_study.cc
index 8c1f8d0f8a1..245a5835992 100644
--- a/chromium/components/variations/processed_study.cc
+++ b/chromium/components/variations/processed_study.cc
@@ -21,11 +21,6 @@ bool ValidateStudyAndComputeTotalProbability(
base::FieldTrial::Probability* total_probability,
bool* all_assignments_to_one_group,
std::string* single_feature_name) {
- // At the moment, a missing default_experiment_name makes the study invalid.
- if (study.default_experiment_name().empty()) {
- DVLOG(1) << study.name() << " has no default experiment defined.";
- return false;
- }
if (study.filter().has_min_version() &&
!base::Version::IsValidWildcardString(study.filter().min_version())) {
DVLOG(1) << study.name() << " has invalid min version: "
@@ -92,9 +87,11 @@ bool ValidateStudyAndComputeTotalProbability(
found_default_group = true;
}
- if (!found_default_group) {
- DVLOG(1) << study.name() << " is missing default experiment in its "
- << "experiment list";
+ // Specifying a default experiment is optional, so finding it in the
+ // experiment list is only required when it is specified.
+ if (!study.default_experiment_name().empty() && !found_default_group) {
+ DVLOG(1) << study.name() << " is missing default experiment ("
+ << study.default_experiment_name() << ") in its experiment list";
// The default group was not found in the list of groups. This study is not
// valid.
return false;
@@ -113,6 +110,10 @@ bool ValidateStudyAndComputeTotalProbability(
} // namespace
+// static
+const char ProcessedStudy::kGenericDefaultExperimentName[] =
+ "VariationsDefaultExperiment";
+
ProcessedStudy::ProcessedStudy()
: study_(NULL),
total_probability_(0),
@@ -150,6 +151,13 @@ int ProcessedStudy::GetExperimentIndexByName(const std::string& name) const {
return -1;
}
+const char* ProcessedStudy::GetDefaultExperimentName() const {
+ if (study_->default_experiment_name().empty())
+ return kGenericDefaultExperimentName;
+
+ return study_->default_experiment_name().c_str();
+}
+
// static
bool ProcessedStudy::ValidateAndAppendStudy(
const Study* study,
diff --git a/chromium/components/variations/processed_study.h b/chromium/components/variations/processed_study.h
index 0607d064a9f..e7807f5cf77 100644
--- a/chromium/components/variations/processed_study.h
+++ b/chromium/components/variations/processed_study.h
@@ -18,6 +18,10 @@ class Study;
// such as whether the study is expired and its total probability.
class ProcessedStudy {
public:
+ // The default group used when a study doesn't specify one. This is needed
+ // because the field trial api requires a default group name.
+ static const char kGenericDefaultExperimentName[];
+
ProcessedStudy();
~ProcessedStudy();
@@ -43,6 +47,10 @@ class ProcessedStudy {
// experiment is found.
int GetExperimentIndexByName(const std::string& name) const;
+ // Gets the default experiment name for the study, or a generic one if none is
+ // specified.
+ const char* GetDefaultExperimentName() const;
+
static bool ValidateAndAppendStudy(
const Study* study,
bool is_expired,
diff --git a/chromium/components/variations/proto/study.proto b/chromium/components/variations/proto/study.proto
index d880a0075d4..2b2f61162f1 100644
--- a/chromium/components/variations/proto/study.proto
+++ b/chromium/components/variations/proto/study.proto
@@ -17,6 +17,7 @@ message Study {
// Ex: "my_study"
required string name = 1;
+ // DEPRECATED: Prefer end_date instead.
// The expiry date of the study in Unix time format. (Seconds since midnight
// January 1, 1970 UTC). See: http://en.wikipedia.org/wiki/Unix_time
//
@@ -38,13 +39,14 @@ message Study {
optional Consistency consistency = 7 [default = SESSION];
// Name of the experiment that gets the default experience. This experiment
- // must be included in the list below.
+ // must be included in the list below. If not specified, a generic default
+ // experiment name is used.
// Ex: "default"
optional string default_experiment_name = 8;
// An experiment within the study.
//
- // Next tag: 13
+ // Next tag: 14
message Experiment {
// A named parameter value for this experiment.
//
@@ -198,13 +200,22 @@ message Study {
// Filtering criteria specifying whether this study is applicable to a given
// Chrome instance.
//
- // Next tag: 13
+ // Next tag: 14
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
// Ex: 1330893974 (corresponds to 2012-03-04 20:46:14Z)
optional int64 start_date = 1;
+ // The end date of the study in Unix time format. (Seconds since midnight
+ // January 1, 1970 UTC). See: http://en.wikipedia.org/wiki/Unix_time
+ // Ex: 1330893974 (corresponds to 2012-03-04 20:46:14Z)
+ // Mutually exclusive with expiry_date. The difference between end_date and
+ // expiry_date is that, when end_date is past, the field trial will not be
+ // created. When expiry_date is past, the trial is still created, but will
+ // be disabled, causing it to select its default group.
+ optional int64 end_date = 13;
+
// The minimum Chrome version for this study, allowing a trailing '*'
// character for pattern matching. Inclusive. (To check for a match, iterate
// over each component checking >= until a * or end of string is reached.)
diff --git a/chromium/components/variations/study_filtering.cc b/chromium/components/variations/study_filtering.cc
index 159b544f4e7..510925132e1 100644
--- a/chromium/components/variations/study_filtering.cc
+++ b/chromium/components/variations/study_filtering.cc
@@ -143,6 +143,16 @@ bool CheckStudyStartDate(const Study_Filter& filter,
return true;
}
+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());
+ return end_date >= date_time;
+ }
+
+ return true;
+}
+
bool CheckStudyVersion(const Study_Filter& filter,
const base::Version& version) {
if (filter.has_min_version()) {
@@ -225,6 +235,11 @@ bool ShouldAddStudy(
return false;
}
+ if (!CheckStudyEndDate(study.filter(), reference_date)) {
+ DVLOG(1) << "Filtered out study " << study.name() << " due to end date.";
+ return false;
+ }
+
if (!CheckStudyHardwareClass(study.filter(), hardware_class)) {
DVLOG(1) << "Filtered out study " << study.name() <<
" due to hardware_class.";
diff --git a/chromium/components/variations/study_filtering.h b/chromium/components/variations/study_filtering.h
index 030d91918c3..9917ec7e828 100644
--- a/chromium/components/variations/study_filtering.h
+++ b/chromium/components/variations/study_filtering.h
@@ -43,6 +43,9 @@ bool CheckStudyPlatform(const Study_Filter& filter, Study_Platform platform);
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);
+
// Checks whether a study is applicable for the given version per |filter|.
bool CheckStudyVersion(const Study_Filter& filter,
const base::Version& version);
diff --git a/chromium/components/variations/study_filtering_unittest.cc b/chromium/components/variations/study_filtering_unittest.cc
index d8008cf2695..98490f016ce 100644
--- a/chromium/components/variations/study_filtering_unittest.cc
+++ b/chromium/components/variations/study_filtering_unittest.cc
@@ -227,9 +227,11 @@ TEST(VariationsStudyFilteringTest, CheckStudyStartDate) {
const base::Time start_date;
bool expected_result;
} start_test_cases[] = {
- { now - delta, true },
- { now, true },
- { now + delta, false },
+ {now - delta, true},
+ // Note, the proto start_date is truncated to seconds, but the reference
+ // date isn't.
+ {now, true},
+ {now + delta, false},
};
Study_Filter filter;
@@ -245,6 +247,29 @@ TEST(VariationsStudyFilteringTest, CheckStudyStartDate) {
}
}
+TEST(VariationsStudyFilteringTest, CheckStudyEndDate) {
+ const base::Time now = base::Time::Now();
+ const base::TimeDelta delta = base::TimeDelta::FromHours(1);
+ const struct {
+ const base::Time end_date;
+ bool expected_result;
+ } start_test_cases[] = {
+ {now - delta, false}, {now + delta, true},
+ };
+
+ Study_Filter filter;
+
+ // End date not set should result in true.
+ EXPECT_TRUE(internal::CheckStudyEndDate(filter, now));
+
+ for (size_t i = 0; i < arraysize(start_test_cases); ++i) {
+ filter.set_end_date(TimeToProtoTime(start_test_cases[i].end_date));
+ const bool result = internal::CheckStudyEndDate(filter, now);
+ EXPECT_EQ(start_test_cases[i].expected_result, result) << "Case " << i
+ << " failed!";
+ }
+}
+
TEST(VariationsStudyFilteringTest, CheckStudyVersion) {
const struct {
const char* min_version;
@@ -573,8 +598,9 @@ TEST(VariationsStudyFilteringTest, ValidateStudy) {
study.mutable_filter()->set_max_version("2.3.4");
EXPECT_TRUE(processed_study.Init(&study, false));
+ // A blank default study is allowed.
study.clear_default_experiment_name();
- EXPECT_FALSE(processed_study.Init(&study, false));
+ EXPECT_TRUE(processed_study.Init(&study, false));
study.set_default_experiment_name("xyz");
EXPECT_FALSE(processed_study.Init(&study, false));
diff --git a/chromium/components/variations/variations_associated_data.cc b/chromium/components/variations/variations_associated_data.cc
index b4e6b013df5..ec696a3e37f 100644
--- a/chromium/components/variations/variations_associated_data.cc
+++ b/chromium/components/variations/variations_associated_data.cc
@@ -11,7 +11,9 @@
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
+#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_param_associator.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "components/variations/variations_http_header_provider.h"
@@ -19,8 +21,6 @@ namespace variations {
namespace {
-const char kGroupTesting[] = "Testing";
-
// The internal singleton accessor for the map, used to keep it thread-safe.
class GroupMapAccessor {
public:
@@ -199,30 +199,68 @@ std::string GetVariationParamValueByFeature(const base::Feature& feature,
return GetVariationParamValue(trial->trial_name(), param_name);
}
-// Functions below are exposed for testing explicitly behind this namespace.
-// They simply wrap existing functions in this file.
-namespace testing {
-
-VariationParamsManager::VariationParamsManager(
- const std::string& trial_name,
- const std::map<std::string, std::string>& params) {
- SetVariationParams(trial_name, params);
+int GetVariationParamByFeatureAsInt(const base::Feature& feature,
+ const std::string& param_name,
+ int default_value) {
+ std::string value_as_string =
+ GetVariationParamValueByFeature(feature, param_name);
+ int value_as_int = 0;
+ if (!base::StringToInt(value_as_string, &value_as_int)) {
+ if (!value_as_string.empty()) {
+ DLOG(WARNING) << "Failed to parse variation param " << param_name
+ << " with string value " << value_as_string
+ << " under feature " << feature.name
+ << " into an int. Falling back to default value of "
+ << default_value;
+ }
+ value_as_int = default_value;
+ }
+ return value_as_int;
}
-VariationParamsManager::~VariationParamsManager() {
- ClearAllVariationIDs();
- ClearAllVariationParams();
- field_trial_list_.reset();
+double GetVariationParamByFeatureAsDouble(const base::Feature& feature,
+ const std::string& param_name,
+ double default_value) {
+ std::string value_as_string =
+ GetVariationParamValueByFeature(feature, param_name);
+ double value_as_double = 0;
+ if (!base::StringToDouble(value_as_string, &value_as_double)) {
+ if (!value_as_string.empty()) {
+ DLOG(WARNING) << "Failed to parse variation param " << param_name
+ << " with string value " << value_as_string
+ << " under feature " << feature.name
+ << " into a double. Falling back to default value of "
+ << default_value;
+ }
+ value_as_double = default_value;
+ }
+ return value_as_double;
}
-void VariationParamsManager::SetVariationParams(
- const std::string& trial_name,
- const std::map<std::string, std::string>& params) {
- field_trial_list_.reset(new base::FieldTrialList(nullptr));
- variations::AssociateVariationParams(trial_name, kGroupTesting, params);
- base::FieldTrialList::CreateFieldTrial(trial_name, kGroupTesting);
+bool GetVariationParamByFeatureAsBool(const base::Feature& feature,
+ const std::string& param_name,
+ bool default_value) {
+ std::string value_as_string =
+ variations::GetVariationParamValueByFeature(feature, param_name);
+ if (value_as_string == "true")
+ return true;
+ if (value_as_string == "false")
+ return false;
+
+ if (!value_as_string.empty()) {
+ DLOG(WARNING) << "Failed to parse variation param " << param_name
+ << " with string value " << value_as_string
+ << " under feature " << feature.name
+ << " into a bool. Falling back to default value of "
+ << default_value;
+ }
+ return default_value;
}
+// Functions below are exposed for testing explicitly behind this namespace.
+// They simply wrap existing functions in this file.
+namespace testing {
+
void ClearAllVariationIDs() {
GroupMapAccessor::GetInstance()->ClearAllMapsForTesting();
}
diff --git a/chromium/components/variations/variations_associated_data.h b/chromium/components/variations/variations_associated_data.h
index e80ebeca438..8b1b25f5f82 100644
--- a/chromium/components/variations/variations_associated_data.h
+++ b/chromium/components/variations/variations_associated_data.h
@@ -10,7 +10,6 @@
#include <string>
#include <vector>
-#include "base/metrics/field_trial.h"
#include "components/variations/active_field_trials.h"
// This file provides various helpers that extend the functionality around
@@ -45,12 +44,11 @@
namespace base {
struct Feature;
-}
+} // namespace base
namespace variations {
typedef int VariationID;
-class VariationsHttpHeaderProvider;
const VariationID EMPTY_ID = 0;
@@ -157,45 +155,49 @@ std::string GetVariationParamValue(const std::string& trial_name,
// with at most one variation, through the variation's associated field trial,
// and selected group. See base/feature_list.h for more information on
// features. If the feature is not enabled, or the specified parameter does not
-// exist, returns an empty string.Calling this function will result in the
+// exist, returns an empty string. Calling this function will result in the
// associated field trial being marked as active if found (i.e. group() will be
// called on it), if it wasn't already. Currently, this information is only
// available from the browser process. Thread safe.
std::string GetVariationParamValueByFeature(const base::Feature& feature,
const std::string& param_name);
+// Same as GetVariationParamValueByFeature(). On top of that, it converts the
+// string value into an int using base::StringToInt() and returns it, if
+// successful. Otherwise, it returns |default_value|. If the string value is not
+// empty and the conversion does not succeed, it produces a warning to LOG.
+int GetVariationParamByFeatureAsInt(const base::Feature& feature,
+ const std::string& param_name,
+ int default_value);
+
+// Same as GetVariationParamValueByFeature(). On top of that, it converts the
+// string value into a double using base::StringToDouble() and returns it, if
+// successful. Otherwise, it returns |default_value|. If the string value is not
+// empty and the conversion does not succeed, it produces a warning to LOG.
+double GetVariationParamByFeatureAsDouble(const base::Feature& feature,
+ const std::string& param_name,
+ double default_value);
+
+// Same as GetVariationParamValueByFeature(). On top of that, it converts the
+// string value into a boolean and returns it, if successful. Otherwise, it
+// returns |default_value|. The only string representations accepted here are
+// "true" and "false". If the string value is not empty and the conversion does
+// not succeed, it produces a warning to LOG.
+bool GetVariationParamByFeatureAsBool(const base::Feature& feature,
+ const std::string& param_name,
+ bool default_value);
+
// Expose some functions for testing.
namespace testing {
-// Use this class as a member in your test class to set variation params for
-// your tests. You can directly set the parameters in the constructor (if they
-// are used by other members upon construction). You can change them later
-// arbitrarily many times using the SetVariationParams function. Internally, it
-// creates a FieldTrialList as a member. It works well for multiple tests of a
-// given test class, as it clears the parameters when this class is destructed.
-// Note that it clears all parameters (not just those registered here).
-class VariationParamsManager {
- public:
- VariationParamsManager(const std::string& trial_name,
- const std::map<std::string, std::string>& params);
- ~VariationParamsManager();
-
- // Associates |params| with the given |trial_name|. It creates a new group,
- // used only for testing. Between two calls of this function,
- // ClearAllVariationParams() has to be called.
- void SetVariationParams(const std::string& trial_name,
- const std::map<std::string, std::string>& params);
-
- private:
- std::unique_ptr<base::FieldTrialList> field_trial_list_;
-
- DISALLOW_COPY_AND_ASSIGN(VariationParamsManager);
-};
-
-// Clears all of the mapped associations.
+// Clears all of the mapped associations. Deprecated, try to use
+// VariationParamsManager instead as it does a lot of work for you
+// automatically.
void ClearAllVariationIDs();
-// Clears all of the associated params.
+// Clears all of the associated params. Deprecated, try to use
+// VariationParamsManager instead as it does a lot of work for you
+// automatically.
void ClearAllVariationParams();
} // namespace testing
diff --git a/chromium/components/variations/variations_associated_data_unittest.cc b/chromium/components/variations/variations_associated_data_unittest.cc
index 40ff746d80a..2217bd5fb91 100644
--- a/chromium/components/variations/variations_associated_data_unittest.cc
+++ b/chromium/components/variations/variations_associated_data_unittest.cc
@@ -403,4 +403,89 @@ TEST_F(VariationsAssociatedDataTest, GetVariationParamValueByFeature_Disable) {
EXPECT_EQ(std::string(), GetVariationParamValueByFeature(kFeature, "x"));
}
+TEST_F(VariationsAssociatedDataTest, GetVariationParamByFeatureAsInt) {
+ const std::string kTrialName = "GetVariationParamsByFeature";
+ const base::Feature kFeature{"TestFeature",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+ std::map<std::string, std::string> params;
+ params["a"] = "1";
+ params["b"] = "1.5";
+ params["c"] = "foo";
+ params["d"] = "";
+ // "e" is not registered
+ variations::AssociateVariationParams(kTrialName, "A", params);
+ scoped_refptr<base::FieldTrial> trial(
+ CreateFieldTrial(kTrialName, 100, "A", NULL));
+
+ CreateFeatureWithTrial(kFeature, base::FeatureList::OVERRIDE_ENABLE_FEATURE,
+ trial.get());
+
+ std::map<std::string, std::string> actualParams;
+ EXPECT_EQ(1, GetVariationParamByFeatureAsInt(kFeature, "a", 0));
+ EXPECT_EQ(0, GetVariationParamByFeatureAsInt(kFeature, "b", 0)); // invalid
+ EXPECT_EQ(0, GetVariationParamByFeatureAsInt(kFeature, "c", 0)); // invalid
+ EXPECT_EQ(0, GetVariationParamByFeatureAsInt(kFeature, "d", 0)); // empty
+ EXPECT_EQ(0, GetVariationParamByFeatureAsInt(kFeature, "e", 0)); // empty
+}
+
+TEST_F(VariationsAssociatedDataTest, GetVariationParamByFeatureAsDouble) {
+ const std::string kTrialName = "GetVariationParamsByFeature";
+ const base::Feature kFeature{"TestFeature",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+ std::map<std::string, std::string> params;
+ params["a"] = "1";
+ params["b"] = "1.5";
+ params["c"] = "1.0e-10";
+ params["d"] = "foo";
+ params["e"] = "";
+ // "f" is not registered
+ variations::AssociateVariationParams(kTrialName, "A", params);
+ scoped_refptr<base::FieldTrial> trial(
+ CreateFieldTrial(kTrialName, 100, "A", NULL));
+
+ CreateFeatureWithTrial(kFeature, base::FeatureList::OVERRIDE_ENABLE_FEATURE,
+ trial.get());
+
+ std::map<std::string, std::string> actualParams;
+ EXPECT_EQ(1, GetVariationParamByFeatureAsDouble(kFeature, "a", 0));
+ EXPECT_EQ(1.5, GetVariationParamByFeatureAsDouble(kFeature, "b", 0));
+ EXPECT_EQ(1.0e-10, GetVariationParamByFeatureAsDouble(kFeature, "c", 0));
+ EXPECT_EQ(0,
+ GetVariationParamByFeatureAsDouble(kFeature, "d", 0)); // invalid
+ EXPECT_EQ(0, GetVariationParamByFeatureAsDouble(kFeature, "e", 0)); // empty
+ EXPECT_EQ(0, GetVariationParamByFeatureAsDouble(kFeature, "f", 0)); // empty
+}
+
+TEST_F(VariationsAssociatedDataTest, GetVariationParamByFeatureAsBool) {
+ const std::string kTrialName = "GetVariationParamsByFeature";
+ const base::Feature kFeature{"TestFeature",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+ std::map<std::string, std::string> params;
+ params["a"] = "true";
+ params["b"] = "false";
+ params["c"] = "1";
+ params["d"] = "False";
+ params["e"] = "";
+ // "f" is not registered
+ variations::AssociateVariationParams(kTrialName, "A", params);
+ scoped_refptr<base::FieldTrial> trial(
+ CreateFieldTrial(kTrialName, 100, "A", NULL));
+
+ CreateFeatureWithTrial(kFeature, base::FeatureList::OVERRIDE_ENABLE_FEATURE,
+ trial.get());
+
+ std::map<std::string, std::string> actualParams;
+ EXPECT_TRUE(GetVariationParamByFeatureAsBool(kFeature, "a", false));
+ EXPECT_FALSE(GetVariationParamByFeatureAsBool(kFeature, "b", true));
+ EXPECT_FALSE(
+ GetVariationParamByFeatureAsBool(kFeature, "c", false)); // invalid
+ EXPECT_TRUE(
+ GetVariationParamByFeatureAsBool(kFeature, "d", true)); // invalid
+ EXPECT_TRUE(GetVariationParamByFeatureAsBool(kFeature, "e", true)); // empty
+ EXPECT_TRUE(GetVariationParamByFeatureAsBool(kFeature, "f", true)); // empty
+}
+
} // namespace variations
diff --git a/chromium/components/variations/variations_params_manager.cc b/chromium/components/variations/variations_params_manager.cc
new file mode 100644
index 00000000000..18ebdeed0ad
--- /dev/null
+++ b/chromium/components/variations/variations_params_manager.cc
@@ -0,0 +1,96 @@
+// 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/variations/variations_params_manager.h"
+
+#include <utility>
+
+#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/variations_associated_data.h"
+
+namespace variations {
+namespace testing {
+
+namespace {
+
+// The fixed testing group created in the provided trail when setting up params.
+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);
+ return base::FieldTrialList::CreateFieldTrial(trial_name, kGroupTesting);
+}
+
+} // namespace
+
+VariationParamsManager::VariationParamsManager()
+ : field_trial_list_(new base::FieldTrialList(nullptr)),
+ scoped_feature_list_(new base::test::ScopedFeatureList()) {}
+
+VariationParamsManager::VariationParamsManager(
+ const std::string& trial_name,
+ const std::map<std::string, std::string>& param_values)
+ : VariationParamsManager() {
+ SetVariationParams(trial_name, param_values);
+}
+
+VariationParamsManager::VariationParamsManager(
+ const std::string& trial_name,
+ const std::map<std::string, std::string>& param_values,
+ const std::set<std::string>& associated_features)
+ : VariationParamsManager() {
+ SetVariationParamsWithFeatureAssociations(trial_name, param_values,
+ associated_features);
+}
+
+VariationParamsManager::~VariationParamsManager() {
+ ClearAllVariationIDs();
+ ClearAllVariationParams();
+}
+
+void VariationParamsManager::SetVariationParams(
+ const std::string& trial_name,
+ const std::map<std::string, std::string>& param_values) {
+ CreateFieldTrialWithParams(trial_name, param_values);
+}
+
+void VariationParamsManager::SetVariationParamsWithFeatureAssociations(
+ const std::string& trial_name,
+ const std::map<std::string, std::string>& param_values,
+ const std::set<std::string>& associated_features) {
+ base::FieldTrial* field_trial =
+ CreateFieldTrialWithParams(trial_name, param_values);
+
+ std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+ for (const std::string& feature_name : associated_features) {
+ feature_list->RegisterFieldTrialOverride(
+ feature_name,
+ base::FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE,
+ field_trial);
+ }
+
+ scoped_feature_list_->InitWithFeatureList(std::move(feature_list));
+}
+
+void VariationParamsManager::ClearAllVariationIDs() {
+ variations::testing::ClearAllVariationIDs();
+}
+
+void VariationParamsManager::ClearAllVariationParams() {
+ variations::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());
+ // Ensure the destructor is called properly, so it can be freshly recreated.
+ field_trial_list_.reset();
+ field_trial_list_ = base::MakeUnique<base::FieldTrialList>(nullptr);
+}
+
+} // namespace testing
+} // namespace variations
diff --git a/chromium/components/variations/variations_params_manager.h b/chromium/components/variations/variations_params_manager.h
new file mode 100644
index 00000000000..6a65e189abf
--- /dev/null
+++ b/chromium/components/variations/variations_params_manager.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_VARIATIONS_VARIATIONS_PARAMS_MANAGER_H_
+#define COMPONENTS_VARIATIONS_VARIATIONS_PARAMS_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/macros.h"
+#include "base/metrics/field_trial.h"
+
+namespace base {
+class FieldTrialList;
+
+namespace test {
+class ScopedFeatureList;
+} // namespace test
+
+} // namespace base
+
+namespace variations {
+namespace testing {
+
+// Use this class as a member in your test class to set variation params for
+// your tests. You can directly set the parameters in the constructor (if they
+// are used by other members upon construction). You can change them later
+// arbitrarily many times using the SetVariationParams function. Internally, it
+// creates a FieldTrialList as a member. It works well for multiple tests of a
+// given test class, as it clears the parameters when this class is destructed.
+// Note that it clears all parameters (not just those registered here).
+class VariationParamsManager {
+ public:
+ // Does not associate any parameters.
+ VariationParamsManager();
+ // Calls directly SetVariationParams with the provided arguments.
+ VariationParamsManager(
+ const std::string& trial_name,
+ const std::map<std::string, std::string>& param_values);
+ // Calls directly SetVariationParamsWithFeatures with the provided arguments.
+ VariationParamsManager(
+ const std::string& trial_name,
+ const std::map<std::string, std::string>& param_values,
+ const std::set<std::string>& associated_features);
+ ~VariationParamsManager();
+
+ // Associates |param_values| with the given |trial_name|. |param_values| maps
+ // parameter names to their values. The function creates a new trial group,
+ // used only for testing. Between two calls of this function,
+ // ClearAllVariationParams() has to be called.
+ void SetVariationParams(
+ const std::string& trial_name,
+ const std::map<std::string, std::string>& param_values);
+
+ // Like SetVariationParams(). |associated_features| lists names of features
+ // to be associated to the newly created trial group. As a result, all
+ // parameters from |param_values| can be accessed via any of the feature from
+ // |associated_features|.
+ void SetVariationParamsWithFeatureAssociations(
+ const std::string& trial_name,
+ const std::map<std::string, std::string>& param_values,
+ const std::set<std::string>& associated_features);
+
+ // Clears all of the mapped associations.
+ void ClearAllVariationIDs();
+
+ // Clears all of the associated params.
+ void ClearAllVariationParams();
+
+ private:
+ std::unique_ptr<base::FieldTrialList> field_trial_list_;
+ std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(VariationParamsManager);
+};
+
+} // namespace testing
+} // namespace variations
+
+#endif // COMPONENTS_VARIATIONS_VARIATIONS_PARAMS_MANAGER_H_
diff --git a/chromium/components/variations/variations_seed_processor.cc b/chromium/components/variations/variations_seed_processor.cc
index 50c5cdc7dc3..5c88264c00a 100644
--- a/chromium/components/variations/variations_seed_processor.cc
+++ b/chromium/components/variations/variations_seed_processor.cc
@@ -86,6 +86,9 @@ void ForceExperimentState(
RegisterExperimentParams(study, experiment);
RegisterVariationIds(experiment, study.name());
if (study.activation_type() == Study_ActivationType_ACTIVATION_AUTO) {
+ // This call must happen after all params have been registered for the
+ // trial. Otherwise, since we look up params by trial and group name, the
+ // params won't be registered under the correct key.
trial->group();
// UI Strings can only be overridden from ACTIVATION_AUTO experiments.
ApplyUIStringOverrides(experiment, override_callback);
@@ -271,7 +274,7 @@ void VariationsSeedProcessor::CreateTrialFromStudy(
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
study.name(), processed_study.total_probability(),
- study.default_experiment_name(),
+ processed_study.GetDefaultExperimentName(),
base::FieldTrialList::kNoExpirationYear, 1, 1, randomization_type,
randomization_seed, NULL,
ShouldStudyUseLowEntropy(study) ? low_entropy_provider : NULL));
@@ -310,6 +313,9 @@ void VariationsSeedProcessor::CreateTrialFromStudy(
RegisterFeatureOverrides(processed_study, trial.get(), feature_list);
if (study.activation_type() == Study_ActivationType_ACTIVATION_AUTO) {
+ // This call must happen after all params have been registered for the
+ // trial. Otherwise, since we look up params by trial and group name, the
+ // params won't be registered under the correct key.
const std::string& group_name = trial->group_name();
// Don't try to apply overrides if none of the experiments in this study had
diff --git a/chromium/components/variations/variations_seed_processor_unittest.cc b/chromium/components/variations/variations_seed_processor_unittest.cc
index 7703eed3b1a..01b36af19c3 100644
--- a/chromium/components/variations/variations_seed_processor_unittest.cc
+++ b/chromium/components/variations/variations_seed_processor_unittest.cc
@@ -378,8 +378,9 @@ TEST_F(VariationsSeedProcessorTest, ValidateStudy) {
study.mutable_filter()->set_max_version("2.3.4");
EXPECT_TRUE(processed_study.Init(&study, false));
+ // A blank default study is allowed.
study.clear_default_experiment_name();
- EXPECT_FALSE(processed_study.Init(&study, false));
+ EXPECT_TRUE(processed_study.Init(&study, false));
study.set_default_experiment_name("xyz");
EXPECT_FALSE(processed_study.Init(&study, false));
@@ -839,12 +840,29 @@ TEST_F(VariationsSeedProcessorTest, FeaturesInExpiredStudies) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatureList(std::move(feature_list));
- // Tthe feature should not be enabled, because the study is expired.
+ // The feature should not be enabled, because the study is expired.
EXPECT_EQ(test_case.expected_feature_enabled,
base::FeatureList::IsEnabled(test_case.feature));
}
}
+TEST_F(VariationsSeedProcessorTest, NoDefaultExperiment) {
+ base::FieldTrialList field_trial_list(nullptr);
+
+ Study study;
+ study.set_name("Study1");
+
+ AddExperiment("A", 1, &study);
+
+ EXPECT_TRUE(CreateTrialFromStudy(study));
+
+ base::FieldTrial* trial = base::FieldTrialList::Find("Study1");
+ trial->Disable();
+
+ EXPECT_EQ(ProcessedStudy::kGenericDefaultExperimentName,
+ base::FieldTrialList::FindFullName("Study1"));
+}
+
TEST_F(VariationsSeedProcessorTest, LowEntropyStudyTest) {
const std::string kTrial1Name = "A";
const std::string kTrial2Name = "B";
diff --git a/chromium/components/variations/variations_seed_store.cc b/chromium/components/variations/variations_seed_store.cc
index 8067651b435..647cc8dbf9c 100644
--- a/chromium/components/variations/variations_seed_store.cc
+++ b/chromium/components/variations/variations_seed_store.cc
@@ -139,6 +139,7 @@ enum FirstRunResult {
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,
};
@@ -367,7 +368,11 @@ void VariationsSeedStore::ImportFirstRunJavaSeed() {
}
base::Time current_date;
- base::Time::FromUTCString(response_date.c_str(), &current_date);
+ if (!base::Time::FromUTCString(response_date.c_str(), &current_date)) {
+ RecordFirstRunResult(FIRST_RUN_SEED_IMPORT_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)) {
diff --git a/chromium/components/version_ui/resources/about_version.html b/chromium/components/version_ui/resources/about_version.html
index 9900014c133..a151b906a05 100644
--- a/chromium/components/version_ui/resources/about_version.html
+++ b/chromium/components/version_ui/resources/about_version.html
@@ -33,10 +33,11 @@ about:version template page
<body>
<div id="outer">
<div id="logo">
-<if expr="enable_themes">
+<if expr="not is_android and not is_ios">
+ <!-- Version for themes. -->
<img src="chrome://theme/IDR_PRODUCT_LOGO">
</if>
-<if expr="not enable_themes">
+<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>
@@ -58,17 +59,26 @@ about:version template page
</td>
</tr>
<if expr="not chromeos">
- <tr><td class="label" i18n-content="os_name"></td>
+ <tr>
+ <td class="label" i18n-content="os_name"></td>
<td class="version" id="os_type">
<span i18n-content="os_type"></span>
<span id="os_version" i18n-content="os_version"></span>
</td>
+ </tr>
</if>
<if expr="chromeos">
- <tr><td class="label" i18n-content="platform"></td>
+ <tr>
+ <td class="label" i18n-content="platform"></td>
<td class="version" id="os_type">
<span id="os_version" i18n-content="os_version"></span>
</td>
+ </tr>
+ <tr id="customization_id_holder" hidden>
+ <td class="label" i18n-content="customization_id"></td>
+ <td class="version">
+ <span id="customization_id"></span>
+ </td>
</if>
</tr>
<tr id="arc_holder"><td class="label" i18n-content="arc_label"></td>
diff --git a/chromium/components/version_ui/resources/about_version.js b/chromium/components/version_ui/resources/about_version.js
index c335b6360cd..9a987f7ccd4 100644
--- a/chromium/components/version_ui/resources/about_version.js
+++ b/chromium/components/version_ui/resources/about_version.js
@@ -40,7 +40,6 @@ function returnOsVersion(osVersion) {
$('os_version').textContent = osVersion;
}
-
/**
* Callback from the backend with the ARC version to display.
* @param {string} arcVersion The ARC version to display.
@@ -50,10 +49,23 @@ function returnARCVersion(arcVersion) {
$('arc_holder').hidden = !arcVersion;
}
+/**
+ * Callback from chromeosInfoPrivate with the value of the customization ID.
+ * @param {!{customizationId: string}} response
+ */
+function returnCustomizationId(response) {
+ if (!response.customizationId)
+ return;
+ $('customization_id_holder').hidden = false;
+ $('customization_id').textContent = response.customizationId;
+}
+
/* All the work we do onload. */
function onLoadWork() {
chrome.send('requestVersionInfo');
$('arc_holder').hidden = true;
+ if (cr.isChromeOS)
+ chrome.chromeosInfoPrivate.get(['customizationId'], returnCustomizationId);
}
document.addEventListener('DOMContentLoaded', onLoadWork);
diff --git a/chromium/components/version_ui/version_ui_constants.cc b/chromium/components/version_ui/version_ui_constants.cc
index b60803c9415..5c4e824fc62 100644
--- a/chromium/components/version_ui/version_ui_constants.cc
+++ b/chromium/components/version_ui/version_ui_constants.cc
@@ -24,6 +24,9 @@ const char kCommandLine[] = "command_line";
const char kCommandLineName[] = "command_line_name";
const char kCompany[] = "company";
const char kCopyright[] = "copyright";
+#if defined(OS_CHROMEOS)
+const char kCustomizationId[] = "customization_id";
+#endif
const char kExecutablePath[] = "executable_path";
const char kExecutablePathName[] = "executable_path_name";
const char kFlashPlugin[] = "flash_plugin";
diff --git a/chromium/components/version_ui/version_ui_constants.h b/chromium/components/version_ui/version_ui_constants.h
index d922674858d..7f0d3e85b11 100644
--- a/chromium/components/version_ui/version_ui_constants.h
+++ b/chromium/components/version_ui/version_ui_constants.h
@@ -29,6 +29,9 @@ extern const char kCommandLine[];
extern const char kCommandLineName[];
extern const char kCompany[];
extern const char kCopyright[];
+#if defined(OS_CHROMEOS)
+extern const char kCustomizationId[];
+#endif
extern const char kExecutablePath[];
extern const char kExecutablePathName[];
extern const char kFlashPlugin[];
diff --git a/chromium/components/version_ui_strings.grdp b/chromium/components/version_ui_strings.grdp
index e3f01ec54c9..9d3b72cd610 100644
--- a/chromium/components/version_ui_strings.grdp
+++ b/chromium/components/version_ui_strings.grdp
@@ -31,6 +31,9 @@
<message name="IDS_VERSION_UI_BUILD_DATE" desc="label for build date on the about:version page">
Build Date
</message>
+ <message name="IDS_VERSION_UI_CUSTOMIZATION_ID" desc="label for the device customization ID if present on the about:version page">
+ Customization ID
+ </message>
</if>
<message name="IDS_VERSION_UI_EXECUTABLE_PATH" desc="label for the executable path on the about:version page">
Executable Path
diff --git a/chromium/components/visitedlink/browser/visitedlink_delegate.h b/chromium/components/visitedlink/browser/visitedlink_delegate.h
index 6adf7de5718..f5939336cdb 100644
--- a/chromium/components/visitedlink/browser/visitedlink_delegate.h
+++ b/chromium/components/visitedlink/browser/visitedlink_delegate.h
@@ -9,10 +9,6 @@
class GURL;
-namespace content {
-class BrowserContext;
-}
-
namespace visitedlink {
// Delegate class that clients of VisitedLinkMaster must implement.
diff --git a/chromium/components/wallpaper/wallpaper_manager_base.cc b/chromium/components/wallpaper/wallpaper_manager_base.cc
index 38549abeb52..01c4ca88f54 100644
--- a/chromium/components/wallpaper/wallpaper_manager_base.cc
+++ b/chromium/components/wallpaper/wallpaper_manager_base.cc
@@ -18,7 +18,7 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
-#include "base/threading/worker_pool.h"
+#include "base/task_scheduler/post_task.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/prefs/pref_registry_simple.h"
@@ -323,6 +323,7 @@ void WallpaperManagerBase::EnsureLoggedInUserWallpaperLoaded() {
if (GetLoggedInUserWallpaperInfo(&info)) {
UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", info.type,
user_manager::User::WALLPAPER_TYPE_COUNT);
+ RecordWallpaperAppType();
if (info == current_user_wallpaper_info_)
return;
}
@@ -473,7 +474,13 @@ void WallpaperManagerBase::OnPolicyCleared(const std::string& policy,
GetUserWallpaperInfo(account_id, &info);
info.type = user_manager::User::DEFAULT;
SetUserWallpaperInfo(account_id, info, true /* is_persistent */);
- SetDefaultWallpaperNow(account_id);
+ // If the user's policy is cleared, try to set the device wallpaper first.
+ // Note We have to modify the user wallpaper info first. Otherwise, we won't
+ // be able to override the current user policy wallpaper. The wallpaper info
+ // will be set correctly if the device wallpaper is set successfully.
+ if (!SetDeviceWallpaperIfApplicable(account_id)) {
+ SetDefaultWallpaperNow(account_id);
+ }
}
// static
@@ -615,6 +622,15 @@ void WallpaperManagerBase::InitInitialUserWallpaper(const AccountId& account_id,
current_user_wallpaper_info_.type = user_manager::User::DEFAULT;
current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight();
+ std::string device_wallpaper_url;
+ std::string device_wallpaper_hash;
+ if (ShouldSetDeviceWallpaper(account_id, &device_wallpaper_url,
+ &device_wallpaper_hash)) {
+ current_user_wallpaper_info_.location =
+ GetDeviceWallpaperFilePath().value();
+ current_user_wallpaper_info_.type = user_manager::User::DEVICE;
+ }
+
WallpaperInfo info = current_user_wallpaper_info_;
SetUserWallpaperInfo(account_id, info, is_persistent);
}
@@ -708,10 +724,15 @@ 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) {
- const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
- base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
- wallpaper_path = wallpaper_path.Append(info.location);
+ info.type == user_manager::User::POLICY ||
+ info.type == user_manager::User::DEVICE) {
+ base::FilePath wallpaper_path;
+ if (info.type == user_manager::User::DEVICE) {
+ wallpaper_path = GetDeviceWallpaperFilePath();
+ } else {
+ const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution();
+ wallpaper_path = GetCustomWallpaperDir(sub_dir).Append(info.location);
+ }
// Set the path to the cache.
wallpaper_cache_[account_id] =
CustomWallpaperElement(wallpaper_path, gfx::ImageSkia());
@@ -759,8 +780,13 @@ void WallpaperManagerBase::DeleteUserWallpapers(
wallpaper_path = wallpaper_path.Append(path_to_file);
file_to_remove.push_back(wallpaper_path);
- base::WorkerPool::PostTask(
- FROM_HERE, base::Bind(&DeleteWallpaperInList, file_to_remove), false);
+ base::PostTaskWithTraits(
+ FROM_HERE, base::TaskTraits()
+ .WithShutdownBehavior(
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
+ .WithPriority(base::TaskPriority::BACKGROUND)
+ .MayBlock(),
+ base::Bind(&DeleteWallpaperInList, file_to_remove));
}
void WallpaperManagerBase::SetCommandLineForTesting(
@@ -941,7 +967,6 @@ void WallpaperManagerBase::OnCustomizedDefaultWallpaperDecoded(
std::unique_ptr<gfx::ImageSkia> small_wallpaper_image(new gfx::ImageSkia);
std::unique_ptr<gfx::ImageSkia> large_wallpaper_image(new gfx::ImageSkia);
- // TODO(bshe): This may break if Bytes becomes RefCountedMemory.
base::Closure resize_closure = base::Bind(
&WallpaperManagerBase::ResizeCustomizedDefaultWallpaper,
base::Passed(&deep_copy),
diff --git a/chromium/components/wallpaper/wallpaper_manager_base.h b/chromium/components/wallpaper/wallpaper_manager_base.h
index a734d2f5f03..4e9aed774ff 100644
--- a/chromium/components/wallpaper/wallpaper_manager_base.h
+++ b/chromium/components/wallpaper/wallpaper_manager_base.h
@@ -354,6 +354,14 @@ class WALLPAPER_EXPORT WallpaperManagerBase {
// Ruturns files identifier for the |account_id|.
virtual WallpaperFilesId GetFilesId(const AccountId& account_id) const = 0;
+ // If the device is enterprise managed and we're at the login screen, set the
+ // device wallpaper as the login screen wallpaper. If the device is enterprise
+ // managed and we're in a user session, only set the device wallpaper if there
+ // is no user policy wallpaper and the user hasn't changed the default or the
+ // device wallpaper. Returns true if the device wallpaper should be set as the
+ // wallpaper, otherwise returns false.
+ virtual bool SetDeviceWallpaperIfApplicable(const AccountId& account_id) = 0;
+
protected:
friend class TestApi;
friend class WallpaperManagerBrowserTest;
@@ -402,7 +410,8 @@ class WALLPAPER_EXPORT WallpaperManagerBase {
gfx::ImageSkia* large_wallpaper_image);
// Initialize wallpaper for the specified user to default and saves this
- // settings in local state.
+ // settings in local state. Note if the device policy controlled wallpaper
+ // exists, use the device wallpaper as the default wallpaper.
virtual void InitInitialUserWallpaper(const AccountId& account_id,
bool is_persistent);
@@ -472,6 +481,17 @@ class WALLPAPER_EXPORT WallpaperManagerBase {
virtual bool GetUserWallpaperInfo(const AccountId& account_id,
WallpaperInfo* info) const = 0;
+ // Returns true if the device wallpaper should be set for the account.
+ virtual bool ShouldSetDeviceWallpaper(const AccountId& account_id,
+ std::string* url,
+ std::string* hash) = 0;
+
+ // Returns the file directory where the downloaded device wallpaper is saved.
+ virtual base::FilePath GetDeviceWallpaperDir() = 0;
+
+ // Returns the full path for the downloaded device wallpaper.
+ virtual base::FilePath GetDeviceWallpaperFilePath() = 0;
+
// Sets wallpaper to the decoded wallpaper if |update_wallpaper| is true.
// Otherwise, cache wallpaper to memory if not logged in. (Takes a UserImage
// because that's the callback interface provided by UserImageLoader.)
@@ -553,6 +573,9 @@ class WALLPAPER_EXPORT WallpaperManagerBase {
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 wallpaper subdirectory name for current resolution.
virtual const char* GetCustomWallpaperSubdirForCurrentResolution();
diff --git a/chromium/components/web_cache/browser/web_cache_manager.cc b/chromium/components/web_cache/browser/web_cache_manager.cc
index 97d1e196e63..fe215e6a3c3 100644
--- a/chromium/components/web_cache/browser/web_cache_manager.cc
+++ b/chromium/components/web_cache/browser/web_cache_manager.cc
@@ -135,21 +135,15 @@ void WebCacheManager::ObserveActivity(int renderer_id) {
}
void WebCacheManager::ObserveStats(int renderer_id,
- uint64_t min_dead_capacity,
- uint64_t max_dead_capacity,
uint64_t capacity,
- uint64_t live_size,
- uint64_t dead_size) {
+ uint64_t size) {
StatsMap::iterator entry = stats_.find(renderer_id);
if (entry == stats_.end())
return; // We might see stats for a renderer that has been destroyed.
// Record the updated stats.
entry->second.capacity = capacity;
- entry->second.dead_size = dead_size;
- entry->second.live_size = live_size;
- entry->second.max_dead_capacity = max_dead_capacity;
- entry->second.min_dead_capacity = min_dead_capacity;
+ entry->second.size = size;
}
void WebCacheManager::SetGlobalSizeLimit(uint64_t bytes) {
@@ -199,62 +193,47 @@ uint64_t WebCacheManager::GetDefaultGlobalSizeLimit() {
void WebCacheManager::GatherStats(const std::set<int>& renderers,
uint64_t* capacity,
- uint64_t* live_size,
- uint64_t* dead_size) {
- *capacity = *live_size = *dead_size = 0;
+ uint64_t* size) {
+ *capacity = *size = 0;
std::set<int>::const_iterator iter = renderers.begin();
while (iter != renderers.end()) {
StatsMap::iterator elmt = stats_.find(*iter);
if (elmt != stats_.end()) {
*capacity += elmt->second.capacity;
- *live_size += elmt->second.live_size;
- *dead_size += elmt->second.dead_size;
+ *size += elmt->second.size;
}
++iter;
}
}
// static
-uint64_t WebCacheManager::GetSize(AllocationTactic tactic,
- uint64_t live_size,
- uint64_t dead_size) {
+uint64_t WebCacheManager::GetSize(AllocationTactic tactic, uint64_t size) {
switch (tactic) {
case DIVIDE_EVENLY:
// We aren't going to reserve any space for existing objects.
return 0;
case KEEP_CURRENT_WITH_HEADROOM:
// We need enough space for our current objects, plus some headroom.
- return 3 * GetSize(KEEP_CURRENT, live_size, dead_size) / 2;
+ return 3 * GetSize(KEEP_CURRENT, size) / 2;
case KEEP_CURRENT:
// We need enough space to keep our current objects.
- return live_size + dead_size;
- case KEEP_LIVE_WITH_HEADROOM:
- // We need enough space to keep out live resources, plus some headroom.
- return 3 * GetSize(KEEP_LIVE, live_size, dead_size) / 2;
- case KEEP_LIVE:
- // We need enough space to keep our live resources.
- return live_size;
+ return size;
default:
NOTREACHED() << "Unknown cache allocation tactic";
return 0;
}
}
-bool WebCacheManager::AttemptTactic(
- AllocationTactic active_tactic,
- uint64_t active_live_size,
- uint64_t active_dead_size,
- AllocationTactic inactive_tactic,
- uint64_t inactive_live_size,
- uint64_t inactive_dead_size,
- AllocationStrategy* strategy) {
+bool WebCacheManager::AttemptTactic(AllocationTactic active_tactic,
+ uint64_t active_used_size,
+ AllocationTactic inactive_tactic,
+ uint64_t inactive_used_size,
+ AllocationStrategy* strategy) {
DCHECK(strategy);
- uint64_t active_size = GetSize(active_tactic, active_live_size,
- active_dead_size);
- uint64_t inactive_size = GetSize(inactive_tactic, inactive_live_size,
- inactive_dead_size);
+ uint64_t active_size = GetSize(active_tactic, active_used_size);
+ uint64_t inactive_size = GetSize(inactive_tactic, inactive_used_size);
// Give up if we don't have enough space to use this tactic.
if (global_size_limit_ < active_size + inactive_size)
@@ -307,8 +286,7 @@ void WebCacheManager::AddToStrategy(const std::set<int>& renderers,
// Add in the space required to implement |tactic|.
StatsMap::iterator elmt = stats_.find(*iter);
if (elmt != stats_.end()) {
- cache_size += GetSize(tactic, elmt->second.live_size,
- elmt->second.dead_size);
+ cache_size += GetSize(tactic, elmt->second.size);
}
// Record the allocation in our strategy.
@@ -327,25 +305,12 @@ void WebCacheManager::EnactStrategy(const AllocationStrategy& strategy) {
// This is the capacity this renderer has been allocated.
uint64_t capacity = allocation->second;
- // We don't reserve any space for dead objects in the cache. Instead, we
- // prefer to keep live objects around. There is probably some performance
- // tuning to be done here.
- uint64_t min_dead_capacity = 0;
-
- // We allow the dead objects to consume up to half of the cache capacity.
- uint64_t max_dead_capacity = capacity / 2;
- if (base::SysInfo::IsLowEndDevice()) {
- max_dead_capacity = std::min(static_cast<uint64_t>(512 * 1024u),
- max_dead_capacity);
- }
-
// Find the WebCachePtr by renderer process id.
auto it = web_cache_services_.find(allocation->first);
DCHECK(it != web_cache_services_.end());
const mojom::WebCachePtr& service = it->second;
DCHECK(service);
- service->SetCacheCapacities(min_dead_capacity, max_dead_capacity,
- capacity);
+ service->SetCacheCapacity(capacity);
}
++allocation;
}
@@ -383,27 +348,19 @@ void WebCacheManager::ReviseAllocationStrategy() {
FindInactiveRenderers();
// Gather statistics
- uint64_t active_capacity, active_live_size, active_dead_size,
- inactive_capacity, inactive_live_size, inactive_dead_size;
- GatherStats(active_renderers_, &active_capacity, &active_live_size,
- &active_dead_size);
- GatherStats(inactive_renderers_, &inactive_capacity, &inactive_live_size,
- &inactive_dead_size);
+ uint64_t active_capacity, active_size, inactive_capacity, inactive_size;
+ GatherStats(active_renderers_, &active_capacity, &active_size);
+ GatherStats(inactive_renderers_, &inactive_capacity, &inactive_size);
UMA_HISTOGRAM_COUNTS_100("Cache.ActiveTabs", active_renderers_.size());
UMA_HISTOGRAM_COUNTS_100("Cache.InactiveTabs", inactive_renderers_.size());
UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveCapacityMB",
active_capacity / 1024 / 1024);
- UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveDeadSizeMB",
- active_dead_size / 1024 / 1024);
- UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveLiveSizeMB",
- active_live_size / 1024 / 1024);
+ UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveLiveSizeMB", active_size / 1024 / 1024);
UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveCapacityMB",
inactive_capacity / 1024 / 1024);
- UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveDeadSizeMB",
- inactive_dead_size / 1024 / 1024);
UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveLiveSizeMB",
- inactive_live_size / 1024 / 1024);
+ inactive_size / 1024 / 1024);
// Compute an allocation strategy.
//
@@ -420,30 +377,21 @@ void WebCacheManager::ReviseAllocationStrategy() {
// we've found a workable strategy.
AllocationStrategy strategy;
if ( // Ideally, we'd like to give the active renderers some headroom and
- // keep all our current objects.
- AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active_live_size,
- active_dead_size, KEEP_CURRENT, inactive_live_size,
- inactive_dead_size, &strategy) ||
- // If we can't have that, then we first try to evict the dead objects in
- // the caches of inactive renderers.
- AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active_live_size,
- active_dead_size, KEEP_LIVE, inactive_live_size,
- inactive_dead_size, &strategy) ||
- // Next, we try to keep the live objects in the active renders (with some
- // room for new objects) and give whatever is left to the inactive
+ // keep all our current objects.
+ AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active_size, KEEP_CURRENT,
+ inactive_size, &strategy) ||
+ // Next, we try to keep the current objects in the active renders (with
+ // some room for new objects) and give whatever is left to the inactive
// renderers.
- AttemptTactic(KEEP_LIVE_WITH_HEADROOM, active_live_size,
- active_dead_size, DIVIDE_EVENLY, inactive_live_size,
- inactive_dead_size, &strategy) ||
+ AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active_size, DIVIDE_EVENLY,
+ inactive_size, &strategy) ||
// If we've gotten this far, then we are very tight on memory. Let's try
// to at least keep around the live objects for the active renderers.
- AttemptTactic(KEEP_LIVE, active_live_size, active_dead_size,
- DIVIDE_EVENLY, inactive_live_size, inactive_dead_size,
+ AttemptTactic(KEEP_CURRENT, active_size, DIVIDE_EVENLY, inactive_size,
&strategy) ||
// We're basically out of memory. The best we can do is just divide up
// what we have and soldier on.
- AttemptTactic(DIVIDE_EVENLY, active_live_size, active_dead_size,
- DIVIDE_EVENLY, inactive_live_size, inactive_dead_size,
+ AttemptTactic(DIVIDE_EVENLY, active_size, DIVIDE_EVENLY, inactive_size,
&strategy)) {
// Having found a workable strategy, we enact it.
EnactStrategy(strategy);
diff --git a/chromium/components/web_cache/browser/web_cache_manager.h b/chromium/components/web_cache/browser/web_cache_manager.h
index e93af6584d3..d2020dcc7e5 100644
--- a/chromium/components/web_cache/browser/web_cache_manager.h
+++ b/chromium/components/web_cache/browser/web_cache_manager.h
@@ -28,8 +28,6 @@ template<typename Type>
struct DefaultSingletonTraits;
} // namespace base
-class PrefRegistrySimple;
-
namespace web_cache {
// Note: memory usage uses uint64_t because potentially the browser could be
@@ -84,12 +82,7 @@ class WebCacheManager : public content::NotificationObserver {
// Periodically, renderers should inform the cache manager of their current
// statistics. The more up-to-date the cache manager's statistics, the
// better it can allocate cache resources.
- void ObserveStats(int renderer_id,
- uint64_t min_dead_capacity,
- uint64_t max_dead_capacity,
- uint64_t capacity,
- uint64_t live_size,
- uint64_t dead_size);
+ void ObserveStats(int renderer_id, uint64_t capacity, uint64_t size);
// The global limit on the number of bytes in all the in-memory caches.
uint64_t global_size_limit() const { return global_size_limit_; }
@@ -125,11 +118,8 @@ class WebCacheManager : public content::NotificationObserver {
struct RendererInfo {
// The access time for this renderer.
base::Time access;
- uint64_t min_dead_capacity;
- uint64_t max_dead_capacity;
uint64_t capacity;
- uint64_t live_size;
- uint64_t dead_size;
+ uint64_t size;
};
typedef std::map<int, RendererInfo> StatsMap;
@@ -177,14 +167,6 @@ class WebCacheManager : public content::NotificationObserver {
// Allow each renderer to keep its current set of cached resources.
KEEP_CURRENT,
-
- // Allow each renderer to keep cache resources it believes are currently
- // being used, with some extra allocation to store new objects.
- KEEP_LIVE_WITH_HEADROOM,
-
- // Allow each renderer to keep cache resources it believes are currently
- // being used, but instruct the renderer to discard all other data.
- KEEP_LIVE,
};
// Helper functions for devising an allocation strategy
@@ -193,15 +175,12 @@ class WebCacheManager : public content::NotificationObserver {
// in the given parameters.
void GatherStats(const std::set<int>& renderers,
uint64_t* capacity,
- uint64_t* live_size,
- uint64_t* dead_size);
+ uint64_t* size);
// Get the amount of memory that would be required to implement |tactic|
// using the specified allocation tactic. This function defines the
// semantics for each of the tactics.
- static uint64_t GetSize(AllocationTactic tactic,
- uint64_t live_size,
- uint64_t dead_size);
+ static uint64_t GetSize(AllocationTactic tactic, uint64_t size);
// Attempt to use the specified tactics to compute an allocation strategy
// and place the result in |strategy|. |active_stats| and |inactive_stats|
@@ -211,11 +190,9 @@ class WebCacheManager : public content::NotificationObserver {
// Returns |true| on success and |false| on failure. Does not modify
// |strategy| on failure.
bool AttemptTactic(AllocationTactic active_tactic,
- uint64_t active_live_size,
- uint64_t active_dead_size,
+ uint64_t active_size,
AllocationTactic inactive_tactic,
- uint64_t inactive_live_size,
- uint64_t inactive_dead_size,
+ uint64_t inactive_size,
AllocationStrategy* strategy);
// For each renderer in |renderers|, computes its allocation according to
diff --git a/chromium/components/web_cache/browser/web_cache_manager_unittest.cc b/chromium/components/web_cache/browser/web_cache_manager_unittest.cc
index 2989021a449..e697b36e217 100644
--- a/chromium/components/web_cache/browser/web_cache_manager_unittest.cc
+++ b/chromium/components/web_cache/browser/web_cache_manager_unittest.cc
@@ -54,14 +54,12 @@ class WebCacheManagerTest : public testing::Test {
std::set<int> renderers,
WebCacheManager::RendererInfo* stats) {
memset(stats, 0, sizeof(WebCacheManager::RendererInfo));
- h->GatherStats(renderers, &stats->capacity, &stats->live_size,
- &stats->dead_size);
+ h->GatherStats(renderers, &stats->capacity, &stats->size);
}
static uint64_t GetSize(int tactic,
const WebCacheManager::RendererInfo& stats) {
return WebCacheManager::GetSize(
- static_cast<WebCacheManager::AllocationTactic>(tactic), stats.live_size,
- stats.dead_size);
+ static_cast<WebCacheManager::AllocationTactic>(tactic), stats.size);
}
static bool AttemptTactic(WebCacheManager* h,
int active_tactic,
@@ -71,9 +69,9 @@ class WebCacheManagerTest : public testing::Test {
std::list<std::pair<int, uint64_t>>* strategy) {
return h->AttemptTactic(
static_cast<WebCacheManager::AllocationTactic>(active_tactic),
- active_stats.live_size, active_stats.dead_size,
+ active_stats.size,
static_cast<WebCacheManager::AllocationTactic>(inactive_tactic),
- inactive_stats.live_size, inactive_stats.dead_size, strategy);
+ inactive_stats.size, strategy);
}
static void AddToStrategy(WebCacheManager* h,
std::set<int> renderers,
@@ -88,16 +86,13 @@ class WebCacheManagerTest : public testing::Test {
static bool RendererInfoEqual(const WebCacheManager::RendererInfo& lhs,
const WebCacheManager::RendererInfo& rhs) {
- return lhs.capacity == rhs.capacity && lhs.live_size == rhs.live_size &&
- lhs.dead_size == rhs.dead_size;
+ return lhs.capacity == rhs.capacity && lhs.size == rhs.size;
}
enum {
DIVIDE_EVENLY = WebCacheManager::DIVIDE_EVENLY,
KEEP_CURRENT_WITH_HEADROOM = WebCacheManager::KEEP_CURRENT_WITH_HEADROOM,
KEEP_CURRENT = WebCacheManager::KEEP_CURRENT,
- KEEP_LIVE_WITH_HEADROOM = WebCacheManager::KEEP_LIVE_WITH_HEADROOM,
- KEEP_LIVE = WebCacheManager::KEEP_LIVE,
};
WebCacheManager* manager() { return &manager_; }
@@ -116,12 +111,12 @@ const int WebCacheManagerTest::kRendererID2 = 245;
// static
const WebCacheManager::RendererInfo WebCacheManagerTest::kStats = {
- base::Time(), 0, 1024 * 1024, 1024 * 1024, 256 * 1024, 512,
+ base::Time(), 1024 * 1024, 256 * 1024 + 512,
};
// static
const WebCacheManager::RendererInfo WebCacheManagerTest::kStats2 = {
- base::Time(), 0, 2 * 1024 * 1024, 2 * 1024 * 1024, 2 * 256 * 1024, 2 * 512,
+ base::Time(), 2 * 1024 * 1024, 2 * 256 * 1024 + 2 * 512,
};
TEST_F(WebCacheManagerTest, AddRemoveRendererTest) {
@@ -160,9 +155,7 @@ TEST_F(WebCacheManagerTest, ObserveStatsTest) {
EXPECT_EQ(1U, stats(manager()).size());
- manager()->ObserveStats(kRendererID, kStats.min_dead_capacity,
- kStats.max_dead_capacity, kStats.capacity,
- kStats.live_size, kStats.dead_size);
+ manager()->ObserveStats(kRendererID, kStats.capacity, kStats.size);
EXPECT_EQ(1U, stats(manager()).size());
EXPECT_TRUE(RendererInfoEqual(kStats, stats(manager())[kRendererID]));
@@ -183,12 +176,8 @@ TEST_F(WebCacheManagerTest, GatherStatsTest) {
manager()->Add(kRendererID);
manager()->Add(kRendererID2);
- manager()->ObserveStats(kRendererID, kStats.min_dead_capacity,
- kStats.max_dead_capacity, kStats.capacity,
- kStats.live_size, kStats.dead_size);
- manager()->ObserveStats(kRendererID2, kStats2.min_dead_capacity,
- kStats2.max_dead_capacity, kStats2.capacity,
- kStats2.live_size, kStats2.dead_size);
+ manager()->ObserveStats(kRendererID, kStats.capacity, kStats.size);
+ manager()->ObserveStats(kRendererID2, kStats2.capacity, kStats2.size);
std::set<int> renderer_set;
renderer_set.insert(kRendererID);
@@ -202,11 +191,8 @@ TEST_F(WebCacheManagerTest, GatherStatsTest) {
GatherStats(manager(), renderer_set, &stats);
WebCacheManager::RendererInfo expected_stats = kStats;
- expected_stats.min_dead_capacity += kStats2.min_dead_capacity;
- expected_stats.max_dead_capacity += kStats2.max_dead_capacity;
expected_stats.capacity += kStats2.capacity;
- expected_stats.live_size += kStats2.live_size;
- expected_stats.dead_size += kStats2.dead_size;
+ expected_stats.size += kStats2.size;
EXPECT_TRUE(RendererInfoEqual(expected_stats, stats));
@@ -218,8 +204,6 @@ TEST_F(WebCacheManagerTest, GetSizeTest) {
EXPECT_EQ(0U, GetSize(DIVIDE_EVENLY, kStats));
EXPECT_LT(256 * 1024u + 512, GetSize(KEEP_CURRENT_WITH_HEADROOM, kStats));
EXPECT_EQ(256 * 1024u + 512, GetSize(KEEP_CURRENT, kStats));
- EXPECT_LT(256 * 1024u, GetSize(KEEP_LIVE_WITH_HEADROOM, kStats));
- EXPECT_EQ(256 * 1024u, GetSize(KEEP_LIVE, kStats));
}
TEST_F(WebCacheManagerTest, AttemptTacticTest) {
@@ -229,18 +213,12 @@ TEST_F(WebCacheManagerTest, AttemptTacticTest) {
manager()->ObserveActivity(kRendererID);
SimulateInactivity(manager(), kRendererID2);
- manager()->ObserveStats(kRendererID, kStats.min_dead_capacity,
- kStats.max_dead_capacity, kStats.capacity,
- kStats.live_size, kStats.dead_size);
- manager()->ObserveStats(kRendererID2, kStats2.min_dead_capacity,
- kStats2.max_dead_capacity, kStats2.capacity,
- kStats2.live_size, kStats2.dead_size);
-
- manager()->SetGlobalSizeLimit(kStats.live_size + kStats.dead_size +
- kStats2.live_size + kStats2.dead_size / 2);
+ manager()->ObserveStats(kRendererID, kStats.capacity, kStats.size);
+ manager()->ObserveStats(kRendererID2, kStats2.capacity, kStats2.size);
AllocationStrategy strategy;
+ manager()->SetGlobalSizeLimit(kStats.size + kStats2.size - 1);
EXPECT_FALSE(AttemptTactic(manager(),
KEEP_CURRENT,
kStats,
@@ -249,20 +227,17 @@ TEST_F(WebCacheManagerTest, AttemptTacticTest) {
&strategy));
EXPECT_TRUE(strategy.empty());
- EXPECT_TRUE(AttemptTactic(manager(),
- KEEP_CURRENT,
- kStats,
- KEEP_LIVE,
- kStats2,
- &strategy));
+ manager()->SetGlobalSizeLimit(kStats.size + kStats2.size);
+ EXPECT_TRUE(AttemptTactic(manager(), KEEP_CURRENT, kStats, KEEP_CURRENT,
+ kStats2, &strategy));
EXPECT_EQ(2U, strategy.size());
AllocationStrategy::iterator iter = strategy.begin();
while (iter != strategy.end()) {
if (iter->first == kRendererID)
- EXPECT_LE(kStats.live_size + kStats.dead_size, iter->second);
+ EXPECT_LE(kStats.size, iter->second);
else if (iter->first == kRendererID2)
- EXPECT_LE(kStats2.live_size, iter->second);
+ EXPECT_LE(kStats2.size, iter->second);
else
ADD_FAILURE(); // Unexpected entry in strategy.
++iter;
@@ -280,12 +255,8 @@ TEST_F(WebCacheManagerTest, AddToStrategyTest) {
renderer_set.insert(kRendererID);
renderer_set.insert(kRendererID2);
- manager()->ObserveStats(kRendererID, kStats.min_dead_capacity,
- kStats.max_dead_capacity, kStats.capacity,
- kStats.live_size, kStats.dead_size);
- manager()->ObserveStats(kRendererID2, kStats2.min_dead_capacity,
- kStats2.max_dead_capacity, kStats2.capacity,
- kStats2.live_size, kStats2.dead_size);
+ manager()->ObserveStats(kRendererID, kStats.capacity, kStats.size);
+ manager()->ObserveStats(kRendererID2, kStats2.capacity, kStats2.size);
const uint64_t kExtraBytesToAllocate = 10 * 1024;
@@ -304,17 +275,16 @@ TEST_F(WebCacheManagerTest, AddToStrategyTest) {
total_bytes += iter->second;
if (iter->first == kRendererID)
- EXPECT_LE(kStats.live_size + kStats.dead_size, iter->second);
+ EXPECT_LE(kStats.size, iter->second);
else if (iter->first == kRendererID2)
- EXPECT_LE(kStats2.live_size + kStats2.dead_size, iter->second);
+ EXPECT_LE(kStats2.size, iter->second);
else
ADD_FAILURE(); // Unexpected entry in strategy.
++iter;
}
- uint64_t expected_total_bytes = kExtraBytesToAllocate + kStats.live_size +
- kStats.dead_size + kStats2.live_size +
- kStats2.dead_size;
+ uint64_t expected_total_bytes =
+ kExtraBytesToAllocate + kStats.size + kStats2.size;
EXPECT_GE(expected_total_bytes, total_bytes);
diff --git a/chromium/components/web_cache/public/interfaces/web_cache.mojom b/chromium/components/web_cache/public/interfaces/web_cache.mojom
index 64af4fc343d..893510b8224 100644
--- a/chromium/components/web_cache/public/interfaces/web_cache.mojom
+++ b/chromium/components/web_cache/public/interfaces/web_cache.mojom
@@ -6,9 +6,7 @@ module web_cache.mojom;
interface WebCache {
// Set cache capacity values.
- SetCacheCapacities(uint64 min_dead_capacity,
- uint64 max_dead_capacity,
- uint64 capacity);
+ SetCacheCapacity(uint64 capacity);
// Clear cache.
ClearCache(bool on_navigation);
};
diff --git a/chromium/components/web_cache/renderer/web_cache_impl.cc b/chromium/components/web_cache/renderer/web_cache_impl.cc
index 7acf69dc11c..936d0205a0d 100644
--- a/chromium/components/web_cache/renderer/web_cache_impl.cc
+++ b/chromium/components/web_cache/renderer/web_cache_impl.cc
@@ -42,15 +42,10 @@ void WebCacheImpl::ExecutePendingClearCache() {
}
}
-void WebCacheImpl::SetCacheCapacities(uint64_t min_dead_capacity,
- uint64_t max_dead_capacity,
- uint64_t capacity64) {
- size_t min_dead_capacity2 = base::checked_cast<size_t>(min_dead_capacity);
- size_t max_dead_capacity2 = base::checked_cast<size_t>(max_dead_capacity);
+void WebCacheImpl::SetCacheCapacity(uint64_t capacity64) {
size_t capacity = base::checked_cast<size_t>(capacity64);
- blink::WebCache::setCapacities(min_dead_capacity2, max_dead_capacity2,
- capacity);
+ blink::WebCache::setCapacity(capacity);
}
void WebCacheImpl::ClearCache(bool on_navigation) {
diff --git a/chromium/components/web_cache/renderer/web_cache_impl.h b/chromium/components/web_cache/renderer/web_cache_impl.h
index 8f3dfdaedea..3b629cc250e 100644
--- a/chromium/components/web_cache/renderer/web_cache_impl.h
+++ b/chromium/components/web_cache/renderer/web_cache_impl.h
@@ -35,9 +35,7 @@ class WebCacheImpl : public mojom::WebCache {
};
// mojom::WebCache methods:
- void SetCacheCapacities(uint64_t min_dead_capacity,
- uint64_t max_dead_capacity,
- uint64_t capacity) override;
+ void SetCacheCapacity(uint64_t capacity) override;
// If |on_navigation| is true, the clearing is delayed until the next
// navigation event.
void ClearCache(bool on_navigation) override;
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 7cad0daf7f9..386ca757914 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
@@ -184,11 +184,13 @@ void WebContentsDelegateAndroid::RendererResponsive(WebContents* source) {
}
bool WebContentsDelegateAndroid::ShouldCreateWebContents(
- WebContents* web_contents,
+ content::WebContents* web_contents,
+ content::SiteInstance* source_site_instance,
int32_t route_id,
int32_t main_frame_route_id,
int32_t main_frame_widget_route_id,
WindowContainerType window_container_type,
+ const GURL& opener_url,
const std::string& frame_name,
const GURL& target_url,
const std::string& partition_id,
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 f25c48f93cb..93da6c0310a 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
@@ -81,10 +81,12 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate {
content::WebContents* new_contents) override;
bool ShouldCreateWebContents(
content::WebContents* web_contents,
+ content::SiteInstance* source_site_instance,
int32_t route_id,
int32_t main_frame_route_id,
int32_t main_frame_widget_route_id,
WindowContainerType window_container_type,
+ const GURL& opener_url,
const std::string& frame_name,
const GURL& target_url,
const std::string& partition_id,
diff --git a/chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.h b/chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.h
index 76f74354de5..d11fc8e8eac 100644
--- a/chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.h
+++ b/chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.h
@@ -9,10 +9,6 @@ namespace content {
class WebContents;
}
-namespace gfx {
-class Point;
-}
-
namespace web_modal {
class WebContentsModalDialogHost;
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 fe3b6809ae3..5a0af86ea61 100644
--- a/chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.cc
+++ b/chromium/components/web_restrictions/browser/web_restrictions_mojo_implementation.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/memory/ptr_util.h"
#include "components/web_restrictions/browser/web_restrictions_client.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
diff --git a/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle.cc b/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle.cc
index 6f83f08faee..1f78a4a5ed2 100644
--- a/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle.cc
+++ b/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle.cc
@@ -6,7 +6,6 @@
#include "base/bind.h"
#include "components/web_restrictions/browser/web_restrictions_client.h"
-#include "content/public/browser/resource_controller.h"
#include "net/base/net_errors.h"
#include "net/url_request/redirect_info.h"
#include "net/url_request/url_request.h"
@@ -53,9 +52,9 @@ bool WebRestrictionsResourceThrottle::ShouldDefer(const GURL& url) {
void WebRestrictionsResourceThrottle::OnCheckResult(const bool should_proceed) {
if (should_proceed) {
- controller()->Resume();
+ Resume();
} else {
- controller()->CancelWithError(net::ERR_BLOCKED_BY_ADMINISTRATOR);
+ CancelWithError(net::ERR_BLOCKED_BY_ADMINISTRATOR);
}
}
diff --git a/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle.h b/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle.h
index f2dfd374904..ee5e2a61b43 100644
--- a/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle.h
+++ b/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle.h
@@ -10,10 +10,6 @@
#include "content/public/browser/resource_throttle.h"
#include "url/gurl.h"
-namespace net {
-class URLRequest;
-}
-
namespace web_restrictions {
class WebRestrictionsClient;
diff --git a/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle_unittest.cc b/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle_unittest.cc
index d3b54b882fb..f92951cd739 100644
--- a/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle_unittest.cc
+++ b/chromium/components/web_restrictions/browser/web_restrictions_resource_throttle_unittest.cc
@@ -7,7 +7,7 @@
#include "components/web_restrictions/browser/mock_web_restrictions_client.h"
#include "components/web_restrictions/browser/web_restrictions_client.h"
#include "components/web_restrictions/browser/web_restrictions_resource_throttle.h"
-#include "content/public/browser/resource_controller.h"
+#include "content/public/browser/resource_throttle.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "net/base/net_errors.h"
#include "net/url_request/redirect_info.h"
@@ -17,9 +17,10 @@ namespace web_restrictions {
namespace {
-class TestResourceController : public content::ResourceController {
+class TestResourceThrottleDelegate
+ : public content::ResourceThrottle::Delegate {
public:
- TestResourceController(const base::Closure& quit_closure)
+ TestResourceThrottleDelegate(const base::Closure& quit_closure)
: resume_called_(false),
cancel_with_error_called_(false),
error_code_(0),
@@ -56,8 +57,8 @@ class WebRestrictionsResourceThrottleTest : public testing::Test {
protected:
WebRestrictionsResourceThrottleTest()
: throttle_(&provider_, GURL("http://example.com"), true),
- controller_(run_loop_.QuitClosure()) {
- throttle_.set_controller_for_testing(&controller_);
+ delegate_(run_loop_.QuitClosure()) {
+ throttle_.set_delegate_for_testing(&delegate_);
}
void SetAuthority(std::string authority) {
@@ -78,7 +79,7 @@ class WebRestrictionsResourceThrottleTest : public testing::Test {
WebRestrictionsClient provider_;
WebRestrictionsResourceThrottle throttle_;
base::RunLoop run_loop_;
- TestResourceController controller_;
+ TestResourceThrottleDelegate delegate_;
};
TEST_F(WebRestrictionsResourceThrottleTest, WillStartRequest_NoAuthority) {
@@ -98,8 +99,8 @@ TEST_F(WebRestrictionsResourceThrottleTest, WillStartRequest_DeferredAllow) {
throttle_.WillStartRequest(&defer);
EXPECT_TRUE(defer);
run_loop_.Run();
- EXPECT_TRUE(controller_.ResumeCalled());
- EXPECT_FALSE(controller_.CancelWithErrorCalled());
+ EXPECT_TRUE(delegate_.ResumeCalled());
+ EXPECT_FALSE(delegate_.CancelWithErrorCalled());
}
TEST_F(WebRestrictionsResourceThrottleTest, WillStartRequest_DeferredForbid) {
@@ -108,14 +109,14 @@ TEST_F(WebRestrictionsResourceThrottleTest, WillStartRequest_DeferredForbid) {
throttle_.WillStartRequest(&defer);
EXPECT_TRUE(defer);
run_loop_.Run();
- EXPECT_FALSE(controller_.ResumeCalled());
- EXPECT_TRUE(controller_.CancelWithErrorCalled());
- EXPECT_EQ(net::ERR_BLOCKED_BY_ADMINISTRATOR, controller_.GetErrorCode());
+ EXPECT_FALSE(delegate_.ResumeCalled());
+ EXPECT_TRUE(delegate_.CancelWithErrorCalled());
+ EXPECT_EQ(net::ERR_BLOCKED_BY_ADMINISTRATOR, delegate_.GetErrorCode());
}
TEST_F(WebRestrictionsResourceThrottleTest, WillStartRequest_Subresource) {
// Only the main frame should be deferred.
- // Initialization of the controller is asynchronous, and this will only work
+ // Initialization of the delegate is asynchronous, and this will only work
// correctly if the provider is initialized. Run a main frame through this
// first to ensure that everything is initialized.
StartProvider();
@@ -123,8 +124,8 @@ TEST_F(WebRestrictionsResourceThrottleTest, WillStartRequest_Subresource) {
WebRestrictionsResourceThrottle throttle(
&provider_, GURL("http://example.com/sub"), false);
base::RunLoop test_run_loop;
- TestResourceController test_controller(test_run_loop.QuitClosure());
- throttle.set_controller_for_testing(&test_controller);
+ TestResourceThrottleDelegate test_delegate(test_run_loop.QuitClosure());
+ throttle.set_delegate_for_testing(&test_delegate);
bool defer;
throttle.WillStartRequest(&defer);
ASSERT_FALSE(defer);
@@ -148,8 +149,8 @@ TEST_F(WebRestrictionsResourceThrottleTest, WillRedirectRequest_NewUrl) {
net::RedirectInfo redirect;
redirect.new_url = GURL("http://example.com/2");
base::RunLoop test_run_loop;
- TestResourceController test_controller(test_run_loop.QuitClosure());
- throttle_.set_controller_for_testing(&test_controller);
+ TestResourceThrottleDelegate test_delegate(test_run_loop.QuitClosure());
+ throttle_.set_delegate_for_testing(&test_delegate);
bool defer;
throttle_.WillRedirectRequest(redirect, &defer);
ASSERT_TRUE(defer);
diff --git a/chromium/components/webcrypto/algorithm_dispatch.h b/chromium/components/webcrypto/algorithm_dispatch.h
index 2c2c72f97c0..815587f46f9 100644
--- a/chromium/components/webcrypto/algorithm_dispatch.h
+++ b/chromium/components/webcrypto/algorithm_dispatch.h
@@ -14,7 +14,6 @@
namespace webcrypto {
-class AlgorithmImplementation;
class CryptoData;
class GenerateKeyResult;
class Status;
diff --git a/chromium/components/webcrypto/algorithm_implementation.h b/chromium/components/webcrypto/algorithm_implementation.h
index b6461dcf4be..1489c5374d1 100644
--- a/chromium/components/webcrypto/algorithm_implementation.h
+++ b/chromium/components/webcrypto/algorithm_implementation.h
@@ -159,6 +159,7 @@ class AlgorithmImplementation {
// * Use a stable format (a serialized key must forever be de-serializable,
// and be able to survive future migrations to crypto libraries)
// * Work for all keys (including ones marked as non-extractable).
+ // * Gracefully handle invalid inputs
//
// Tests to verify structured cloning are available in:
// LayoutTests/crypto/clone-*.html
@@ -168,6 +169,25 @@ class AlgorithmImplementation {
Status SerializeKeyForClone(const blink::WebCryptoKey& key,
blink::WebVector<uint8_t>* key_data) const;
+ // Deserializes key data from Blink (used for structured cloning).
+ //
+ // The inputs to this function originate from Blink, and may not be
+ // consistent or valid. Implementations must return a failure when processing
+ // invalid or adversarially constructed inputs.
+ //
+ // The ONLY guarantee implementations can assume is that |algorithm.id()|
+ // corresponds with that which the AlgorithmImplementation was registered
+ // under.
+ //
+ // Implementations must be prepared to handle:
+ //
+ // * |type| being invalid for this algorithm's key type(s)
+ // * |algorithm.params()| being inconsistent with the |algorithm.id()|
+ // * |usages| being inconsistent with the key type
+ // * |extractable| being inconsistent with the key type
+ // * |key_data| containing an incorrect serialized format
+ // * Backwards-compatibility: the inputs may have been produced by older
+ // versions of the code.
virtual Status DeserializeKeyForClone(
const blink::WebCryptoKeyAlgorithm& algorithm,
blink::WebCryptoKeyType type,
diff --git a/chromium/components/webcrypto/algorithms/aes.cc b/chromium/components/webcrypto/algorithms/aes.cc
index f46ea15a119..1a21dc5ca1a 100644
--- a/chromium/components/webcrypto/algorithms/aes.cc
+++ b/chromium/components/webcrypto/algorithms/aes.cc
@@ -199,6 +199,10 @@ Status AesAlgorithm::DeserializeKeyForClone(
blink::WebCryptoKeyUsageMask usages,
const CryptoData& key_data,
blink::WebCryptoKey* key) const {
+ if (algorithm.paramsType() != blink::WebCryptoKeyAlgorithmParamsTypeAes ||
+ type != blink::WebCryptoKeyTypeSecret)
+ return Status::ErrorUnexpected();
+
return ImportKeyRaw(key_data, SynthesizeImportAlgorithmForClone(algorithm),
extractable, usages, key);
}
diff --git a/chromium/components/webcrypto/algorithms/aes_cbc.cc b/chromium/components/webcrypto/algorithms/aes_cbc.cc
index c9e941aa7ee..678aa5ac2ce 100644
--- a/chromium/components/webcrypto/algorithms/aes_cbc.cc
+++ b/chromium/components/webcrypto/algorithms/aes_cbc.cc
@@ -1,3 +1,4 @@
+
// 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.
@@ -55,7 +56,8 @@ Status AesCbcEncryptDecrypt(EncryptOrDecrypt cipher_operation,
if (!output_max_len.IsValid())
return Status::ErrorDataTooLarge();
- const unsigned remainder = output_max_len.ValueOrDie() % AES_BLOCK_SIZE;
+ const unsigned remainder =
+ base::ValueOrDieForType<unsigned>(output_max_len % AES_BLOCK_SIZE);
if (remainder != 0)
output_max_len += AES_BLOCK_SIZE - remainder;
if (!output_max_len.IsValid())
@@ -71,7 +73,7 @@ Status AesCbcEncryptDecrypt(EncryptOrDecrypt cipher_operation,
return Status::OperationError();
}
- buffer->resize(output_max_len.ValueOrDie());
+ buffer->resize(base::ValueOrDieForType<size_t>(output_max_len));
int output_len = 0;
if (!EVP_CipherUpdate(context.get(), buffer->data(), &output_len,
diff --git a/chromium/components/webcrypto/algorithms/aes_ctr.cc b/chromium/components/webcrypto/algorithms/aes_ctr.cc
index dd7f012f254..3525c5ff37d 100644
--- a/chromium/components/webcrypto/algorithms/aes_ctr.cc
+++ b/chromium/components/webcrypto/algorithms/aes_ctr.cc
@@ -166,7 +166,7 @@ Status AesCtrEncryptDecrypt(const blink::WebCryptoAlgorithm& algorithm,
return Status::ErrorUnexpected();
const CryptoData counter_block(params->counter());
- buffer->resize(output_max_len.ValueOrDie());
+ buffer->resize(base::ValueOrDieForType<size_t>(output_max_len));
// The total number of possible counter values is pow(2, counter_length_bits)
bssl::UniquePtr<BIGNUM> num_counter_values(BN_new());
diff --git a/chromium/components/webcrypto/algorithms/ec.cc b/chromium/components/webcrypto/algorithms/ec.cc
index 1436ad19b57..2a6d312de3a 100644
--- a/chromium/components/webcrypto/algorithms/ec.cc
+++ b/chromium/components/webcrypto/algorithms/ec.cc
@@ -656,6 +656,9 @@ Status EcAlgorithm::DeserializeKeyForClone(
blink::WebCryptoKeyUsageMask usages,
const CryptoData& key_data,
blink::WebCryptoKey* key) const {
+ if (algorithm.paramsType() != blink::WebCryptoKeyAlgorithmParamsTypeEc)
+ return Status::ErrorUnexpected();
+
blink::WebCryptoAlgorithm import_algorithm =
SynthesizeImportAlgorithmForClone(algorithm);
diff --git a/chromium/components/webcrypto/algorithms/hkdf.cc b/chromium/components/webcrypto/algorithms/hkdf.cc
index 90a204513d3..f5ac563e3db 100644
--- a/chromium/components/webcrypto/algorithms/hkdf.cc
+++ b/chromium/components/webcrypto/algorithms/hkdf.cc
@@ -105,6 +105,10 @@ class HkdfImplementation : public AlgorithmImplementation {
blink::WebCryptoKeyUsageMask usages,
const CryptoData& key_data,
blink::WebCryptoKey* key) const override {
+ if (algorithm.paramsType() != blink::WebCryptoKeyAlgorithmParamsTypeNone ||
+ type != blink::WebCryptoKeyTypeSecret)
+ return Status::ErrorUnexpected();
+
// NOTE: Unlike ImportKeyRaw(), this does not enforce extractable==false.
// This is intentional. Although keys cannot currently be created with
// extractable==true, earlier implementations permitted this, so
diff --git a/chromium/components/webcrypto/algorithms/hmac.cc b/chromium/components/webcrypto/algorithms/hmac.cc
index 05caa7c6c5c..9678654b630 100644
--- a/chromium/components/webcrypto/algorithms/hmac.cc
+++ b/chromium/components/webcrypto/algorithms/hmac.cc
@@ -291,6 +291,10 @@ class HmacImplementation : public AlgorithmImplementation {
blink::WebCryptoKeyUsageMask usages,
const CryptoData& key_data,
blink::WebCryptoKey* key) const override {
+ if (algorithm.paramsType() != blink::WebCryptoKeyAlgorithmParamsTypeHmac ||
+ type != blink::WebCryptoKeyTypeSecret)
+ return Status::ErrorUnexpected();
+
return CreateWebCryptoSecretKey(key_data, algorithm, extractable, usages,
key);
}
diff --git a/chromium/components/webcrypto/algorithms/pbkdf2.cc b/chromium/components/webcrypto/algorithms/pbkdf2.cc
index 5e62bc567fc..53e65ce0748 100644
--- a/chromium/components/webcrypto/algorithms/pbkdf2.cc
+++ b/chromium/components/webcrypto/algorithms/pbkdf2.cc
@@ -110,6 +110,10 @@ class Pbkdf2Implementation : public AlgorithmImplementation {
blink::WebCryptoKeyUsageMask usages,
const CryptoData& key_data,
blink::WebCryptoKey* key) const override {
+ if (algorithm.paramsType() != blink::WebCryptoKeyAlgorithmParamsTypeNone ||
+ type != blink::WebCryptoKeyTypeSecret)
+ return Status::ErrorUnexpected();
+
// NOTE: Unlike ImportKeyRaw(), this does not enforce extractable==false.
// This is intentional. Although keys cannot currently be created with
// extractable==true, earlier implementations permitted this, so
diff --git a/chromium/components/webcrypto/algorithms/rsa.cc b/chromium/components/webcrypto/algorithms/rsa.cc
index bd3be5ba159..35053d889c9 100644
--- a/chromium/components/webcrypto/algorithms/rsa.cc
+++ b/chromium/components/webcrypto/algorithms/rsa.cc
@@ -532,6 +532,9 @@ Status RsaHashedAlgorithm::DeserializeKeyForClone(
blink::WebCryptoKeyUsageMask usages,
const CryptoData& key_data,
blink::WebCryptoKey* key) const {
+ if (algorithm.paramsType() != blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed)
+ return Status::ErrorUnexpected();
+
blink::WebCryptoAlgorithm import_algorithm =
SynthesizeImportAlgorithmForClone(algorithm);
diff --git a/chromium/components/webcrypto/algorithms/test_helpers.cc b/chromium/components/webcrypto/algorithms/test_helpers.cc
index 8751735496e..8138f0d5493 100644
--- a/chromium/components/webcrypto/algorithms/test_helpers.cc
+++ b/chromium/components/webcrypto/algorithms/test_helpers.cc
@@ -391,7 +391,7 @@ std::unique_ptr<base::DictionaryValue> GetJwkDictionary(
json.size());
std::unique_ptr<base::Value> value = base::JSONReader::Read(json_string);
EXPECT_TRUE(value.get());
- EXPECT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+ EXPECT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
return std::unique_ptr<base::DictionaryValue>(
static_cast<base::DictionaryValue*>(value.release()));
diff --git a/chromium/components/webcrypto/algorithms/util.h b/chromium/components/webcrypto/algorithms/util.h
index 51ecd66079d..a0342f6fcf4 100644
--- a/chromium/components/webcrypto/algorithms/util.h
+++ b/chromium/components/webcrypto/algorithms/util.h
@@ -21,7 +21,6 @@
namespace webcrypto {
class CryptoData;
-class GenerateKeyResult;
class Status;
// Returns the EVP_MD that corresponds with |hash_algorithm|, or nullptr on
diff --git a/chromium/components/webdata/common/BUILD.gn b/chromium/components/webdata/common/BUILD.gn
index 736e02a4fb7..7de1b1cd976 100644
--- a/chromium/components/webdata/common/BUILD.gn
+++ b/chromium/components/webdata/common/BUILD.gn
@@ -53,6 +53,10 @@ bundle_data("unit_tests_bundle_data") {
"//components/test/data/web_database/version_64.sql",
"//components/test/data/web_database/version_65.sql",
"//components/test/data/web_database/version_66.sql",
+ "//components/test/data/web_database/version_67.sql",
+ "//components/test/data/web_database/version_68.sql",
+ "//components/test/data/web_database/version_69.sql",
+ "//components/test/data/web_database/version_70.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 d8a271d4a8b..3615379a758 100644
--- a/chromium/components/webdata/common/web_data_request_manager.cc
+++ b/chromium/components/webdata/common/web_data_request_manager.cc
@@ -18,16 +18,8 @@
//
////////////////////////////////////////////////////////////////////////////////
-WebDataRequest::WebDataRequest(WebDataServiceConsumer* consumer,
- WebDataRequestManager* manager)
- : manager_(manager), cancelled_(false), consumer_(consumer) {
- handle_ = manager_->GetNextRequestHandle();
- task_runner_ = base::ThreadTaskRunnerHandle::Get();
- manager_->RegisterRequest(this);
-}
-
WebDataRequest::~WebDataRequest() {
- if (manager_) {
+ if (IsActive()) {
manager_->CancelRequest(handle_);
}
}
@@ -36,37 +28,41 @@ WebDataServiceBase::Handle WebDataRequest::GetHandle() const {
return handle_;
}
-WebDataServiceConsumer* WebDataRequest::GetConsumer() const {
- return consumer_;
-}
-
-scoped_refptr<base::SingleThreadTaskRunner> WebDataRequest::GetTaskRunner()
- const {
- return task_runner_;
+bool WebDataRequest::IsActive() const {
+ return manager_ != nullptr;
}
-bool WebDataRequest::IsCancelled() const {
- base::AutoLock l(cancel_lock_);
- return cancelled_;
+WebDataRequest::WebDataRequest(WebDataRequestManager* manager,
+ WebDataServiceConsumer* consumer)
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ manager_(manager),
+ consumer_(consumer),
+ handle_(0) {
+ DCHECK(IsActive());
}
-void WebDataRequest::Cancel() {
- base::AutoLock l(cancel_lock_);
- cancelled_ = true;
- consumer_ = NULL;
- manager_ = NULL;
+void WebDataRequest::AssertThreadSafe() const {
+ // Manipulations of the request state should only happen when the request is
+ // active and the manager's lock is held (i.e., during request cancellation
+ // or completion).
+ DCHECK(IsActive());
+ manager_->AssertLockedByCurrentThread();
}
-void WebDataRequest::OnComplete() {
- manager_= NULL;
+WebDataServiceConsumer* WebDataRequest::GetConsumer() const {
+ AssertThreadSafe();
+ return consumer_;
}
-void WebDataRequest::SetResult(std::unique_ptr<WDTypedResult> r) {
- result_ = std::move(r);
+scoped_refptr<base::SingleThreadTaskRunner> WebDataRequest::GetTaskRunner()
+ const {
+ return task_runner_;
}
-std::unique_ptr<WDTypedResult> WebDataRequest::GetResult() {
- return std::move(result_);
+void WebDataRequest::MarkAsInactive() {
+ AssertThreadSafe();
+ consumer_ = nullptr;
+ manager_ = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
@@ -79,74 +75,106 @@ WebDataRequestManager::WebDataRequestManager()
: next_request_handle_(1) {
}
-WebDataRequestManager::~WebDataRequestManager() {
- base::AutoLock l(pending_lock_);
- for (auto i = pending_requests_.begin(); i != pending_requests_.end(); ++i)
- i->second->Cancel();
- pending_requests_.clear();
-}
+std::unique_ptr<WebDataRequest> WebDataRequestManager::NewRequest(
+ WebDataServiceConsumer* consumer) {
+ std::unique_ptr<WebDataRequest> request(new WebDataRequest(this, consumer));
-void WebDataRequestManager::RegisterRequest(WebDataRequest* request) {
+ // Grab the lock to get the next request handle and register the request.
base::AutoLock l(pending_lock_);
- pending_requests_[request->GetHandle()] = request;
-}
+ request->handle_ = next_request_handle_++;
+ pending_requests_[request->handle_] = request.get();
-int WebDataRequestManager::GetNextRequestHandle() {
- base::AutoLock l(pending_lock_);
- return ++next_request_handle_;
+ return request;
}
void WebDataRequestManager::CancelRequest(WebDataServiceBase::Handle h) {
base::AutoLock l(pending_lock_);
+ // If the request was already cancelled, or has already completed, it won't
+ // be in the pending_requests_ collection any more.
auto i = pending_requests_.find(h);
- DCHECK(i != pending_requests_.end());
- i->second->Cancel();
+ if (i == pending_requests_.end())
+ return;
+ i->second->MarkAsInactive();
pending_requests_.erase(i);
}
void WebDataRequestManager::RequestCompleted(
- std::unique_ptr<WebDataRequest> request) {
+ std::unique_ptr<WebDataRequest> request,
+ std::unique_ptr<WDTypedResult> result) {
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
request->GetTaskRunner();
task_runner->PostTask(
- FROM_HERE, base::Bind(&WebDataRequestManager::RequestCompletedOnThread,
- this, base::Passed(&request)));
+ FROM_HERE,
+ base::Bind(&WebDataRequestManager::RequestCompletedOnThread, this,
+ base::Passed(&request), base::Passed(&result)));
+}
+
+void WebDataRequestManager::AssertLockedByCurrentThread() const {
+ pending_lock_.AssertAcquired();
+}
+
+WebDataRequestManager::~WebDataRequestManager() {
+ base::AutoLock l(pending_lock_);
+ for (auto i = pending_requests_.begin(); i != pending_requests_.end(); ++i)
+ i->second->MarkAsInactive();
+ pending_requests_.clear();
}
void WebDataRequestManager::RequestCompletedOnThread(
- std::unique_ptr<WebDataRequest> request) {
- if (request->IsCancelled())
+ std::unique_ptr<WebDataRequest> request,
+ std::unique_ptr<WDTypedResult> result) {
+ // If the request is already inactive, early exit to avoid contending for the
+ // lock (aka, the double checked locking pattern).
+ if (!request->IsActive())
return;
- // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
- // fixed.
- tracked_objects::ScopedTracker tracking_profile1(
- FROM_HERE_WITH_EXPLICIT_FUNCTION(
- "422460 WebDataRequestManager::RequestCompletedOnThread::UpdateMap"));
+ // Used to export the consumer from the locked region below so that the
+ // consumer can be notified outside of the lock and after the request has
+ // been marked as inactive.
+ WebDataServiceConsumer* consumer;
+
+ // Manipulate the pending_requests_ collection while holding the lock.
{
base::AutoLock l(pending_lock_);
+
+ // Re-check whether the request is active. It might have been cancelled in
+ // another thread before the lock was acquired.
+ if (!request->IsActive())
+ return;
+
+ // Remember the consumer for notification below.
+ consumer = request->GetConsumer();
+
+ // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "422460 "
+ "WebDataRequestManager::RequestCompletedOnThread::UpdateMap"));
+
auto i = pending_requests_.find(request->GetHandle());
DCHECK(i != pending_requests_.end());
// Take ownership of the request object and remove it from the map.
pending_requests_.erase(i);
- }
- // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
- // fixed.
- tracked_objects::ScopedTracker tracking_profile2(
- FROM_HERE_WITH_EXPLICIT_FUNCTION(
- "422460 "
- "WebDataRequestManager::RequestCompletedOnThread::NotifyConsumer"));
+ // The request is no longer active.
+ request->MarkAsInactive();
+ }
// Notify the consumer if needed.
- if (!request->IsCancelled()) {
- WebDataServiceConsumer* consumer = request->GetConsumer();
- request->OnComplete();
- if (consumer) {
- consumer->OnWebDataServiceRequestDone(request->GetHandle(),
- request->GetResult());
- }
+ //
+ // NOTE: The pending_lock_ is no longer held here. It's up to the consumer to
+ // be appropriately thread safe.
+ if (consumer) {
+ // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "422460 "
+ "WebDataRequestManager::RequestCompletedOnThread::NotifyConsumer"));
+
+ consumer->OnWebDataServiceRequestDone(request->GetHandle(),
+ std::move(result));
}
-
}
diff --git a/chromium/components/webdata/common/web_data_request_manager.h b/chromium/components/webdata/common/web_data_request_manager.h
index 836c12e0d5e..87b55091583 100644
--- a/chromium/components/webdata/common/web_data_request_manager.h
+++ b/chromium/components/webdata/common/web_data_request_manager.h
@@ -26,70 +26,64 @@ class WebDataRequestManager;
//////////////////////////////////////////////////////////////////////////////
//
-// Webdata requests
+// WebData requests
//
// Every request is processed using a request object. The object contains
// both the request parameters and the results.
//////////////////////////////////////////////////////////////////////////////
class WebDataRequest {
public:
- WebDataRequest(WebDataServiceConsumer* consumer,
- WebDataRequestManager* manager);
-
virtual ~WebDataRequest();
+ // Returns the identifier for this request.
WebDataServiceBase::Handle GetHandle() const;
+ // Returns |true| if the request is active and |false| if the request has been
+ // cancelled or has already completed.
+ bool IsActive() const;
+
+ private:
+ // For access to the web request mutable state under the manager's lock.
+ friend class WebDataRequestManager;
+
+ // Private constructor called for WebDataRequestManager::NewRequest.
+ WebDataRequest(WebDataRequestManager* manager,
+ WebDataServiceConsumer* consumer);
+
+ // Internal debugging helper to assert that the request is active and that the
+ // manager's lock is held by the current thread.
+ void AssertThreadSafe() const;
+
// Retrieves the |consumer_| set in the constructor.
WebDataServiceConsumer* GetConsumer() const;
// Retrieves the original task runner of the request.
scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() const;
- // Returns |true| if the request was cancelled via the |Cancel()| method.
- bool IsCancelled() const;
+ // Marks the current request as inactive, either due to cancellation or
+ // completion.
+ void MarkAsInactive();
- // This can be invoked from any thread. From this point we assume that
- // our consumer_ reference is invalid.
- void Cancel();
-
- // Invoked when the request has been completed.
- void OnComplete();
-
- // The result is owned by the request.
- void SetResult(std::unique_ptr<WDTypedResult> r);
-
- // Transfers ownership pof result to caller. Should only be called once per
- // result.
- std::unique_ptr<WDTypedResult> GetResult();
+ // Tracks task runner that the request originated on.
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- private:
// Used to notify manager if request is cancelled. Uses a raw ptr instead of
- // a ref_ptr so that it can be set to NULL when a request is cancelled.
+ // a ref_ptr so that it can be set to null when a request is cancelled or
+ // completed.
WebDataRequestManager* manager_;
- // Tracks task runner that the request originated on.
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
- // Identifier for this request.
- WebDataServiceBase::Handle handle_;
-
- // A lock to protect against simultaneous cancellations of the request.
- // Cancellation affects both the |cancelled_| flag and |consumer_|.
- mutable base::Lock cancel_lock_;
- bool cancelled_;
-
// The originator of the service request.
WebDataServiceConsumer* consumer_;
- std::unique_ptr<WDTypedResult> result_;
+ // Identifier for this request.
+ WebDataServiceBase::Handle handle_;
DISALLOW_COPY_AND_ASSIGN(WebDataRequest);
};
//////////////////////////////////////////////////////////////////////////////
//
-// Webdata Request Manager
+// WebData Request Manager
//
// Tracks all WebDataRequests for a WebDataService.
//
@@ -100,17 +94,19 @@ class WebDataRequestManager
public:
WebDataRequestManager();
+ // Factory function to create a new WebDataRequest.
+ std::unique_ptr<WebDataRequest> NewRequest(WebDataServiceConsumer* consumer);
+
// Cancel any pending request.
void CancelRequest(WebDataServiceBase::Handle h);
// Invoked by the WebDataService when |request| has been completed.
- void RequestCompleted(std::unique_ptr<WebDataRequest> request);
-
- // Register the request as a pending request.
- void RegisterRequest(WebDataRequest* request);
+ void RequestCompleted(std::unique_ptr<WebDataRequest> request,
+ std::unique_ptr<WDTypedResult> result);
- // Return the next request handle.
- int GetNextRequestHandle();
+ // A debugging aid to assert that the pending_lock_ is held by the current
+ // thread.
+ void AssertLockedByCurrentThread() const;
private:
friend class base::RefCountedThreadSafe<WebDataRequestManager>;
@@ -119,7 +115,8 @@ class WebDataRequestManager
// This will notify the consumer in whatever thread was used to create this
// request.
- void RequestCompletedOnThread(std::unique_ptr<WebDataRequest> request);
+ void RequestCompletedOnThread(std::unique_ptr<WebDataRequest> request,
+ std::unique_ptr<WDTypedResult> result);
// A lock to protect pending requests and next request handle.
base::Lock pending_lock_;
diff --git a/chromium/components/webdata/common/web_data_service_base.cc b/chromium/components/webdata/common/web_data_service_base.cc
index 3418eb4dc8d..f3cb0d58999 100644
--- a/chromium/components/webdata/common/web_data_service_base.cc
+++ b/chromium/components/webdata/common/web_data_service_base.cc
@@ -23,10 +23,9 @@ WebDataServiceBase::WebDataServiceBase(
scoped_refptr<WebDatabaseService> wdbs,
const ProfileErrorCallback& callback,
const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread)
- : base::RefCountedDeleteOnMessageLoop<WebDataServiceBase>(ui_thread),
+ : base::RefCountedDeleteOnSequence<WebDataServiceBase>(ui_thread),
wdbs_(wdbs),
- profile_error_callback_(callback) {
-}
+ profile_error_callback_(callback) {}
void WebDataServiceBase::ShutdownOnUIThread() {
}
diff --git a/chromium/components/webdata/common/web_data_service_base.h b/chromium/components/webdata/common/web_data_service_base.h
index 2783beb2d81..ab9a4d42d22 100644
--- a/chromium/components/webdata/common/web_data_service_base.h
+++ b/chromium/components/webdata/common/web_data_service_base.h
@@ -9,24 +9,22 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/ref_counted_delete_on_message_loop.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
#include "components/webdata/common/webdata_export.h"
#include "sql/init_status.h"
class WebDatabase;
class WebDatabaseService;
-class WebDatabaseTable;
namespace base {
// TODO(skyostil): Migrate to SingleThreadTaskRunner (crbug.com/465354).
class SingleThreadTaskRunner;
-class Thread;
}
// Base for WebDataService class hierarchy.
// WebDataServiceBase is destroyed on the UI thread.
class WEBDATA_EXPORT WebDataServiceBase
- : public base::RefCountedDeleteOnMessageLoop<WebDataServiceBase> {
+ : public base::RefCountedDeleteOnSequence<WebDataServiceBase> {
public:
// All requests return an opaque handle of the following type.
typedef int Handle;
@@ -86,7 +84,7 @@ class WEBDATA_EXPORT WebDataServiceBase
virtual WebDatabase* GetDatabase();
protected:
- friend class base::RefCountedDeleteOnMessageLoop<WebDataServiceBase>;
+ friend class base::RefCountedDeleteOnSequence<WebDataServiceBase>;
friend class base::DeleteHelper<WebDataServiceBase>;
virtual ~WebDataServiceBase();
diff --git a/chromium/components/webdata/common/web_database.cc b/chromium/components/webdata/common/web_database.cc
index c731270a2f7..1923176050d 100644
--- a/chromium/components/webdata/common/web_database.cc
+++ b/chromium/components/webdata/common/web_database.cc
@@ -13,13 +13,13 @@
// corresponding changes must happen in the unit tests, and new migration test
// added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
// static
-const int WebDatabase::kCurrentVersionNumber = 67;
+const int WebDatabase::kCurrentVersionNumber = 71;
const int WebDatabase::kDeprecatedVersionNumber = 51;
namespace {
-const int kCompatibleVersionNumber = 61;
+const int kCompatibleVersionNumber = 71;
// Change the version number and possibly the compatibility version of
// |meta_table_|.
diff --git a/chromium/components/webdata/common/web_database_backend.cc b/chromium/components/webdata/common/web_database_backend.cc
index a4446061f91..547ec875736 100644
--- a/chromium/components/webdata/common/web_database_backend.cc
+++ b/chromium/components/webdata/common/web_database_backend.cc
@@ -21,7 +21,7 @@ WebDatabaseBackend::WebDatabaseBackend(
const FilePath& path,
Delegate* delegate,
const scoped_refptr<base::SingleThreadTaskRunner>& db_thread)
- : base::RefCountedDeleteOnMessageLoop<WebDatabaseBackend>(db_thread),
+ : base::RefCountedDeleteOnSequence<WebDatabaseBackend>(db_thread),
db_path_(path),
request_manager_(new WebDataRequestManager()),
init_status_(sql::INIT_FAILURE),
@@ -52,11 +52,11 @@ void WebDatabaseBackend::ShutdownDatabase() {
void WebDatabaseBackend::DBWriteTaskWrapper(
const WebDatabaseService::WriteTask& task,
std::unique_ptr<WebDataRequest> request) {
- if (request->IsCancelled())
+ if (!request->IsActive())
return;
ExecuteWriteTask(task);
- request_manager_->RequestCompleted(std::move(request));
+ request_manager_->RequestCompleted(std::move(request), nullptr);
}
void WebDatabaseBackend::ExecuteWriteTask(
@@ -72,11 +72,11 @@ void WebDatabaseBackend::ExecuteWriteTask(
void WebDatabaseBackend::DBReadTaskWrapper(
const WebDatabaseService::ReadTask& task,
std::unique_ptr<WebDataRequest> request) {
- if (request->IsCancelled())
+ if (!request->IsActive())
return;
- request->SetResult(ExecuteReadTask(task));
- request_manager_->RequestCompleted(std::move(request));
+ std::unique_ptr<WDTypedResult> result = ExecuteReadTask(task);
+ request_manager_->RequestCompleted(std::move(request), std::move(result));
}
std::unique_ptr<WDTypedResult> WebDatabaseBackend::ExecuteReadTask(
diff --git a/chromium/components/webdata/common/web_database_backend.h b/chromium/components/webdata/common/web_database_backend.h
index dbb694aaab3..8c2df1a7ef2 100644
--- a/chromium/components/webdata/common/web_database_backend.h
+++ b/chromium/components/webdata/common/web_database_backend.h
@@ -12,7 +12,7 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/ref_counted_delete_on_message_loop.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/memory/scoped_vector.h"
#include "base/single_thread_task_runner.h"
#include "components/webdata/common/web_database_service.h"
@@ -23,16 +23,12 @@ class WebDatabaseTable;
class WebDataRequest;
class WebDataRequestManager;
-namespace tracked_objects {
-class Location;
-}
-
// WebDatabaseBackend handles all database tasks posted by
// WebDatabaseService. It is refcounted to allow asynchronous destruction on the
// DB thread.
class WEBDATA_EXPORT WebDatabaseBackend
- : public base::RefCountedDeleteOnMessageLoop<WebDatabaseBackend> {
+ : public base::RefCountedDeleteOnSequence<WebDatabaseBackend> {
public:
class Delegate {
public:
@@ -82,7 +78,7 @@ class WEBDATA_EXPORT WebDatabaseBackend
WebDatabase* database() { return db_.get(); }
protected:
- friend class base::RefCountedDeleteOnMessageLoop<WebDatabaseBackend>;
+ friend class base::RefCountedDeleteOnSequence<WebDatabaseBackend>;
friend class base::DeleteHelper<WebDatabaseBackend>;
virtual ~WebDatabaseBackend();
diff --git a/chromium/components/webdata/common/web_database_migration_unittest.cc b/chromium/components/webdata/common/web_database_migration_unittest.cc
index 996f9594d70..3229f86ea0e 100644
--- a/chromium/components/webdata/common/web_database_migration_unittest.cc
+++ b/chromium/components/webdata/common/web_database_migration_unittest.cc
@@ -130,7 +130,7 @@ class WebDatabaseMigrationTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest);
};
-const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 67;
+const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 71;
void WebDatabaseMigrationTest::LoadDatabase(
const base::FilePath::StringType& file) {
@@ -164,6 +164,7 @@ TEST_F(WebDatabaseMigrationTest, VersionXxSqlFilesAreGolden) {
ASSERT_NO_FATAL_FAILURE(LoadDatabase(file_name.value()))
<< "Failed to load " << file_name.MaybeAsASCII();
DoMigration();
+
EXPECT_EQ(expected_schema, RemoveQuotes(connection.GetSchema()))
<< "For version " << i;
}
@@ -1028,6 +1029,7 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion65ToCurrent) {
}
// Tests addition of masked server credit card billing address.
+// That column was moved to server_card_metadata in version 71.
TEST_F(WebDatabaseMigrationTest, MigrateVersion66ToCurrent) {
ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_66.sql")));
@@ -1059,13 +1061,184 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion66ToCurrent) {
// Check version.
EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
- EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards",
+ // The column was moved to server_card_metadata in version 71.
+ EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
+ "billing_address_id"));
+ EXPECT_TRUE(connection.DoesColumnExist("server_card_metadata",
+ "billing_address_id"));
+ }
+}
+
+// Tests deletion of show_in_default_list column in keywords table.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion67ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_67.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, 67, 67));
+
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "show_in_default_list"));
+ }
+
+ 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_FALSE(
+ connection.DoesColumnExist("keywords", "show_in_default_list"));
+ }
+}
+
+// Tests addition of last_visited column in keywords table.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion68ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_68.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, 68, 68));
+
+ EXPECT_FALSE(connection.DoesColumnExist("keywords", "last_visited"));
+ }
+
+ 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("keywords", "last_visited"));
+ }
+}
+
+// Tests addition of sync metadata and model type state tables.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion69ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_69.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, 69, 69));
+
+ EXPECT_FALSE(connection.DoesTableExist("autofill_sync_metadata"));
+ EXPECT_FALSE(connection.DoesTableExist("autofill_model_type_state"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ EXPECT_TRUE(connection.DoesTableExist("autofill_sync_metadata"));
+ EXPECT_TRUE(connection.DoesTableExist("autofill_model_type_state"));
+ }
+}
+
+// Tests addition of billing_address_id to server_card_metadata and
+// has_converted to server_profile_metadata and tests that the
+// billing_address_id values were moved from the masked_credit_cards table to
+// the server_card_metadata table.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion70ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_70.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, 70, 70));
+
+ EXPECT_FALSE(connection.DoesColumnExist("server_card_metadata",
+ "billing_address_id"));
+ EXPECT_FALSE(
+ connection.DoesColumnExist("server_address_metadata", "has_converted"));
+ }
+
+ 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 billing_address_id column should have moved from masked_credit_cards
+ // to server_card_metadata.
+ EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
+ "billing_address_id"));
+ EXPECT_TRUE(connection.DoesColumnExist("server_card_metadata",
"billing_address_id"));
- sql::Statement read_masked(connection.GetUniqueStatement(
- "SELECT name_on_card, billing_address_id FROM masked_credit_cards"));
- ASSERT_TRUE(read_masked.Step());
- EXPECT_EQ("Alice", read_masked.ColumnString(0));
- EXPECT_TRUE(read_masked.ColumnString(1).empty());
+ // The has_converted column should have been added in
+ // server_address_metadata.
+ EXPECT_TRUE(
+ connection.DoesColumnExist("server_address_metadata", "has_converted"));
+
+ // Make sure that the billing_address_id was moved from the
+ // masked_credit_cards table to the server_card_metadata table. The values
+ // are added to the table in version_70.sql.
+ sql::Statement s_cards_metadata(connection.GetUniqueStatement(
+ "SELECT id, billing_address_id FROM server_card_metadata"));
+ ASSERT_TRUE(s_cards_metadata.Step());
+ EXPECT_EQ("card_1", s_cards_metadata.ColumnString(0));
+ EXPECT_EQ("address_1", s_cards_metadata.ColumnString(1));
+
+ // Make sure that the has_converted column was set to false.
+ sql::Statement s_addresses_metadata(connection.GetUniqueStatement(
+ "SELECT id, has_converted FROM server_address_metadata"));
+ ASSERT_TRUE(s_addresses_metadata.Step());
+ EXPECT_EQ("address_1", s_addresses_metadata.ColumnString(0));
+ EXPECT_FALSE(s_addresses_metadata.ColumnBool(1));
+
+ // Make sure that the values in masked_credit_cards are still present except
+ // for the billing_address_id. The values are added to the table in
+ // version_70.sql.
+ sql::Statement s_masked_cards(connection.GetUniqueStatement(
+ "SELECT id, status, name_on_card, type, last_four, exp_month, exp_year "
+ "FROM masked_credit_cards"));
+ ASSERT_TRUE(s_masked_cards.Step());
+ EXPECT_EQ("card_1", s_masked_cards.ColumnString(0));
+ EXPECT_EQ("status", s_masked_cards.ColumnString(1));
+ EXPECT_EQ("bob", s_masked_cards.ColumnString(2));
+ EXPECT_EQ("MASKED", s_masked_cards.ColumnString(3));
+ EXPECT_EQ("1234", s_masked_cards.ColumnString(4));
+ EXPECT_EQ(12, s_masked_cards.ColumnInt(5));
+ EXPECT_EQ(2050, s_masked_cards.ColumnInt(6));
}
}
diff --git a/chromium/components/webdata/common/web_database_service.cc b/chromium/components/webdata/common/web_database_service.cc
index 79bc298022c..412ee9bdb04 100644
--- a/chromium/components/webdata/common/web_database_service.cc
+++ b/chromium/components/webdata/common/web_database_service.cc
@@ -42,7 +42,7 @@ WebDatabaseService::WebDatabaseService(
const base::FilePath& path,
scoped_refptr<base::SingleThreadTaskRunner> ui_thread,
scoped_refptr<base::SingleThreadTaskRunner> db_thread)
- : base::RefCountedDeleteOnMessageLoop<WebDatabaseService>(ui_thread),
+ : base::RefCountedDeleteOnSequence<WebDatabaseService>(ui_thread),
path_(path),
db_loaded_(false),
db_thread_(db_thread),
@@ -94,8 +94,8 @@ void WebDatabaseService::ScheduleDBTask(
const tracked_objects::Location& from_here,
const WriteTask& task) {
DCHECK(web_db_backend_.get());
- std::unique_ptr<WebDataRequest> request(
- new WebDataRequest(NULL, web_db_backend_->request_manager().get()));
+ std::unique_ptr<WebDataRequest> request =
+ web_db_backend_->request_manager()->NewRequest(nullptr);
db_thread_->PostTask(
from_here, Bind(&WebDatabaseBackend::DBWriteTaskWrapper, web_db_backend_,
task, base::Passed(&request)));
@@ -107,8 +107,8 @@ WebDataServiceBase::Handle WebDatabaseService::ScheduleDBTaskWithResult(
WebDataServiceConsumer* consumer) {
DCHECK(consumer);
DCHECK(web_db_backend_.get());
- std::unique_ptr<WebDataRequest> request(
- new WebDataRequest(consumer, web_db_backend_->request_manager().get()));
+ std::unique_ptr<WebDataRequest> request =
+ web_db_backend_->request_manager()->NewRequest(consumer);
WebDataServiceBase::Handle handle = request->GetHandle();
db_thread_->PostTask(
from_here, Bind(&WebDatabaseBackend::DBReadTaskWrapper, web_db_backend_,
diff --git a/chromium/components/webdata/common/web_database_service.h b/chromium/components/webdata/common/web_database_service.h
index 5ce19a05709..7a507ba3f69 100644
--- a/chromium/components/webdata/common/web_database_service.h
+++ b/chromium/components/webdata/common/web_database_service.h
@@ -16,7 +16,7 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/ref_counted_delete_on_message_loop.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/single_thread_task_runner.h"
@@ -25,11 +25,6 @@
#include "components/webdata/common/webdata_export.h"
class WebDatabaseBackend;
-class WebDataRequestManager;
-
-namespace content {
-class BrowserContext;
-}
namespace tracked_objects {
class Location;
@@ -48,7 +43,7 @@ class WebDataServiceConsumer;
////////////////////////////////////////////////////////////////////////////////
class WEBDATA_EXPORT WebDatabaseService
- : public base::RefCountedDeleteOnMessageLoop<WebDatabaseService> {
+ : public base::RefCountedDeleteOnSequence<WebDatabaseService> {
public:
using ReadTask = base::Callback<std::unique_ptr<WDTypedResult>(WebDatabase*)>;
using WriteTask = base::Callback<WebDatabase::State(WebDatabase*)>;
@@ -117,7 +112,7 @@ class WEBDATA_EXPORT WebDatabaseService
private:
class BackendDelegate;
friend class BackendDelegate;
- friend class base::RefCountedDeleteOnMessageLoop<WebDatabaseService>;
+ friend class base::RefCountedDeleteOnSequence<WebDatabaseService>;
friend class base::DeleteHelper<WebDatabaseService>;
using LoadedCallbacks = std::vector<DBLoadedCallback>;
diff --git a/chromium/components/webdata_services/web_data_service_wrapper.cc b/chromium/components/webdata_services/web_data_service_wrapper.cc
index 1b9b41ab263..a8ff689fe48 100644
--- a/chromium/components/webdata_services/web_data_service_wrapper.cc
+++ b/chromium/components/webdata_services/web_data_service_wrapper.cc
@@ -6,10 +6,12 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "build/build_config.h"
+#include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
#include "components/autofill/core/browser/webdata/autocomplete_syncable_service.h"
#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h"
#include "components/autofill/core/browser/webdata/autofill_table.h"
@@ -21,6 +23,7 @@
#include "components/search_engines/keyword_web_data_service.h"
#include "components/signin/core/browser/webdata/token_service_table.h"
#include "components/signin/core/browser/webdata/token_web_data.h"
+#include "components/sync/driver/sync_driver_switches.h"
#include "components/webdata/common/web_database_service.h"
#include "components/webdata/common/webdata_constants.h"
@@ -41,10 +44,16 @@ void InitSyncableServicesOnDBThread(
// Currently only Autocomplete and Autofill profiles use the new Sync API, but
// all the database data should migrate to this API over time.
- autofill::AutocompleteSyncableService::CreateForWebDataServiceAndBackend(
- autofill_web_data.get(), autofill_backend);
- autofill::AutocompleteSyncableService::FromWebDataService(
- autofill_web_data.get())->InjectStartSyncFlare(sync_flare);
+ if (base::FeatureList::IsEnabled(switches::kSyncUSSAutocomplete)) {
+ autofill::AutocompleteSyncBridge::CreateForWebDataServiceAndBackend(
+ autofill_web_data.get(), autofill_backend);
+ } else {
+ autofill::AutocompleteSyncableService::CreateForWebDataServiceAndBackend(
+ autofill_web_data.get(), autofill_backend);
+ autofill::AutocompleteSyncableService::FromWebDataService(
+ autofill_web_data.get())
+ ->InjectStartSyncFlare(sync_flare);
+ }
autofill::AutofillProfileSyncableService::CreateForWebDataServiceAndBackend(
autofill_web_data.get(), autofill_backend, app_locale);
diff --git a/chromium/components/webdata_services/web_data_service_wrapper.h b/chromium/components/webdata_services/web_data_service_wrapper.h
index 06d61ff6d47..3ae4af85a36 100644
--- a/chromium/components/webdata_services/web_data_service_wrapper.h
+++ b/chromium/components/webdata_services/web_data_service_wrapper.h
@@ -24,7 +24,6 @@ class PasswordWebDataService;
#endif
namespace autofill {
-class AutofillWebDataBackend;
class AutofillWebDataService;
} // namespace autofill